Android Mobile Network Settings | APN 菜单加载异常

问题

从log看是有创建APN对应的Controller(功能逻辑是ok的),但是Mobile Network Settings无法显示(UI异常)。

相关术语:

  • GSM(Global System for Mobile Communications) 全球移动通信系统,GSM 使用时分多址(TDMA)技术
  • CDMA (Code Division Multiple Access)码分多址

日志分析

看似APN 菜单已经创建了,实际上并没有显示。

11-12 07:01:28.150  8773  8773 D PrefCtrlListHelper: Could not find Context-only controller for pref: com.android.settings.network.telephony.ApnPreferenceController

11-12 07:01:28.164  8773  8773 D ApnPreferenceController: init: subId = 1

Debug 1:表面原因 isGsmApn

debug打印log的时候会发现,使用平板时preference是不可见的,无法确认是不是getAvailabilityStatus影响了displayPreference。

  • Tablet Log:

11-13 17:10:46.692  7796  9288 D ApnPreferenceController: getAvailabilityStatus hideCarrierNetwork = false, isCdmaApn = false, isGsmApn = false

11-13 17:10:46.705  7796  7796 I ApnPreferenceController: displayPreference: isShow = false, isVisible = false, isEnable = true

  • Phone Log:

11-13 03:25:47.285 13222 13222 D ApnPreferenceController: init: subId = 3

11-13 03:25:47.323 13222 13401 D ApnPreferenceController: getAvailabilityStatus hideCarrierNetwork = false, isCdmaApn = false, isGsmApn = true

11-13 03:25:47.372 13222 13222 I ApnPreferenceController: displayPreference: isShow = true, isVisible = true, isEnable = true

Debug 2:根因 isGsmOptions

在手机设备上,会判断为isGsmBasicOptions,直接返回true,而平板设备的isGsmBasicOptions以及isWorldMode都是false,没有任何一个类型适合,isGsmOptions直接返回了false。

isGsmOptions
MobileNetworkUtils - isGsmOptions

手机设备能满足如下的phoneType

isGsmBasicOptions
MobileNetworkUtils - isGsmBasicOptions
  • Phone Log:

11-13 05:14:30.305 15022 15022 D SatelliteSettingPreferenceController: init(), subId=3
11-13 05:14:30.305 15022 15022 D ApnPreferenceController: init: subId = 3

11-13 05:14:30.354 15022 15233 D ApnPreferenceController: isGsmOption = true
11-13 05:14:30.354 15022 15233 D ApnPreferenceController: KEY_APN_EXPAND_BOOL = true

11-13 05:14:30.355 15022 15233 D ApnPreferenceController: getAvailabilityStatus hideCarrierNetwork = false, isCdmaApn = false, isGsmApn = true

11-13 05:14:30.448 15022 15022 I ApnPreferenceController: displayPreference: isShow = true, isVisible = true, isEnable = true

11-13 05:14:31.218 15022 15022 D ApnPreferenceController: updateState: preferenceKey = telephony_apn_key

11-13 05:14:32.350 15022 15022 D ApnPreferenceController: handlePreferenceTreeClick

  • Tablet Log:

11-13 18:13:24.359 11793 11793 D ApnPreferenceController: isGsmOption = false

11-13 18:13:24.359 11793 11793 D ApnPreferenceController: KEY_APN_EXPAND_BOOL = true

11-13 18:13:24.359 11793 11793 D ApnPreferenceController: getAvailabilityStatus hideCarrierNetwork = false, isCdmaApn = false, isGsmApn = false

11-13 18:13:24.360 11793 11793 I ApnPreferenceController: displayPreference: isShow = false, isVisible = false, isEnable = true

代码解读

移动网络界面加入APN的菜单

mobile_network_settings.xml 界面

先来看看界面设计逻辑

<!-- Copyright (C) 2019 The Android Open Source Project --><PreferenceScreenxmlns:android="http://schemas.android.com/apk/res/android"xmlns:settings="http://schemas.android.com/apk/res-auto"android:key="mobile_network_pref_screen"><com.android.settings.spa.preference.ComposePreferenceandroid:key="use_sim_switch"settings:controller="com.android.settings.network.telephony.MobileNetworkSwitchController"/><!-- 省略移动网络其他controller --><!--We want separate APN setting from reset of settings because we want user to change it with caution--><com.android.settingslib.RestrictedPreferenceandroid:key="telephony_apn_key"android:persistent="false"android:title="@string/mobile_network_apn_title"settings:keywords="@string/keywords_access_point_names"settings:controller="com.android.settings.network.telephony.ApnPreferenceController"/></PreferenceScreen>

标题定义

packages\apps\Settings\res\values\strings.xml

    <!-- Title for Apn settings in mobile network settings [CHAR LIMIT=60] --><string name="mobile_network_apn_title">Access Point Names</string>

ApnPreferenceController

  • KEY_SHOW_APN_SETTING_CDMA_BOOL
  • KEY_APN_EXPAND_BOOL
  • KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL
    @Overridepublic int getAvailabilityStatus(int subId) {final PersistableBundle carrierConfig = mCarrierConfigCache.getConfigForSubId(subId);final boolean isCdmaApn = MobileNetworkUtils.isCdmaOptions(mContext, subId)&& carrierConfig != null&& carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL);final boolean isGsmApn = MobileNetworkUtils.isGsmOptions(mContext, subId)&& carrierConfig != null&& carrierConfig.getBoolean(CarrierConfigManager.KEY_APN_EXPAND_BOOL);final boolean hideCarrierNetwork = carrierConfig == null|| carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL);return !hideCarrierNetwork && (isCdmaApn || isGsmApn)? AVAILABLE: CONDITIONALLY_UNAVAILABLE;}@Overridepublic void displayPreference(PreferenceScreen screen) {super.displayPreference(screen);Log.i(TAG, "displayPreference + ");mPreference = screen.findPreference(getPreferenceKey());//For debug as belowif (mPreference != null) {Log.i(TAG, "displayPreference: isShow = " + mPreference.isShown() +", isVisible = " + mPreference.isVisible() + ", isEnable = " + mPreference.isEnabled());}mPreference.setEnabled(true);    //是否置灰mPreference.setVisible(true);    //是否显示Log.i(TAG, "displayPreference: enable and visible.");}

继续分析preference的逻辑,沿着 getAvailabilityStatus 可以看到,在 ApnPreferenceController  其父类实现的接口 TelephonyAvailabilityCallback 中会回调

packages/apps/Settings/src/com/android/settings/network/telephony/ApnPreferenceController.java

*** Preference controller for "Apn settings"*/
public class ApnPreferenceController extends TelephonyBasePreferenceController implementsLifecycleObserver, OnStart, OnStop {}
父类 TelephonyBasePreferenceController

packages/apps/Settings/src/com/android/settings/network/telephony/TelephonyBasePreferenceController.java

/*** {@link BasePreferenceController} that used by all preferences that requires subscription id.*/
public abstract class TelephonyBasePreferenceController extends BasePreferenceControllerimplements TelephonyAvailabilityCallback, TelephonyAvailabilityHandler {}

可用性接口TelephonyAvailabilityCallback

packages/apps/Settings/src/com/android/settings/network/telephony/TelephonyAvailabilityCallback.java

package com.android.settings.network.telephony;/*** Callback to decide whether preference is available based on subscription id*/
public interface TelephonyAvailabilityCallback {/*** Return availability status for a specific subId** @see TelephonyBasePreferenceController* @see TelephonyTogglePreferenceController*/int getAvailabilityStatus(int subId);
}
MobileNetworkUtils

packages/apps/Settings/src/com/android/settings/network/telephony/MobileNetworkUtils.java

  • isGsmBasicOptions 以下默认都是false,不管是手机还是平板
    • hide_carrier_network_settings_bool 
    • world_phone_bool

所以关键是确定设备是否支持GSM网络

final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class).createForSubscriptionId(subId);
if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {Log.d(TAG, "isGsmBasicOptions: PHONE_TYPE_GSM");return true;
}
    /*** Return availability for a default subscription id. If subId already been set, use it to* check, otherwise traverse all active subIds on device to check.* @param context context* @param defSubId Default subId get from telephony preference controller* @param callback Callback to check availability for a specific subId* @return Availability** @see BasePreferenceController#getAvailabilityStatus()*/public static int getAvailability(Context context, int defSubId,TelephonyAvailabilityCallback callback) {if (defSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {// If subId has been set, return the corresponding statusreturn callback.getAvailabilityStatus(defSubId);} else {// Otherwise, search whether there is one subId in device that support this preferencefinal int[] subIds = getActiveSubscriptionIdList(context);if (ArrayUtils.isEmpty(subIds)) {return callback.getAvailabilityStatus(SubscriptionManager.INVALID_SUBSCRIPTION_ID);} else {for (final int subId : subIds) {final int status = callback.getAvailabilityStatus(subId);if (status == BasePreferenceController.AVAILABLE) {return status;}}return callback.getAvailabilityStatus(subIds[0]);}}}/*** return {@code true} if we need show Gsm related settings*/public static boolean isGsmOptions(Context context, int subId) {if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {return false;}if (isGsmBasicOptions(context, subId)) {return true;}final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class).createForSubscriptionId(subId);final int networkMode = getNetworkTypeFromRaf((int) telephonyManager.getAllowedNetworkTypesForReason(TelephonyManager.ALLOWED_NETWORK_TYPES_REASON_USER));if (isWorldMode(context, subId)) {if (networkMode == NETWORK_MODE_LTE_CDMA_EVDO|| networkMode == NETWORK_MODE_LTE_GSM_WCDMA|| networkMode == NETWORK_MODE_NR_LTE_CDMA_EVDO|| networkMode == NETWORK_MODE_NR_LTE_GSM_WCDMA) {return true;} else if (shouldSpeciallyUpdateGsmCdma(context, subId)) {return true;}}return false;}private static boolean isGsmBasicOptions(Context context, int subId) {final PersistableBundle carrierConfig =CarrierConfigCache.getInstance(context).getConfigForSubId(subId);if (carrierConfig != null&& !carrierConfig.getBoolean(CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)&& carrierConfig.getBoolean(CarrierConfigManager.KEY_WORLD_PHONE_BOOL)) {return true;}final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class).createForSubscriptionId(subId);if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM) {return true;}return false;}}

Settings Manifest文件

在配置文件中绑定title,就不用再其他layout中定义

        <activity android:name="Settings$ApnSettingsActivity"android:label="@string/apn_settings"android:exported="true"android:configChanges="orientation|keyboardHidden|screenSize"><intent-filter android:priority="1"><action android:name="android.settings.APN_SETTINGS" /><category android:name="android.intent.category.DEFAULT" /></intent-filter><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.DEFAULT" /><category android:name="android.intent.category.VOICE_LAUNCH" /></intent-filter><meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"android:value="true" /><meta-data android:name="com.android.settings.FRAGMENT_CLASS"android:value="com.android.settings.network.apn.ApnSettings" /><meta-data android:name="com.android.settings.HIGHLIGHT_MENU_KEY"android:value="@string/menu_key_network"/></activity>

其他:

GsmUmtsOptions

解决方案

如何更改平板上面gsm的判断?

在Android 早期版本的mobilenetwork是没有使用controller管理preference的。

  • mobile_network_settings.xml

移除isGsmApn判断可以显示preference

final boolean isGsmApn = MobileNetworkUtils.isGsmOptions(mContext, subId)&& carrierConfig != null&& carrierConfig.getBoolean(CarrierConfigManager.KEY_APN_EXPAND_BOOL);

因为carrierconfig配置是默认true的,平板也是

public static final String KEY_APN_EXPAND_BOOL = "apn_expand_bool";

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/473204.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AndroidStudio-Activity的生命周期

一、Avtivity的启动和结束 从当前页面跳到新页面&#xff0c;跳转代码如下&#xff1a; startActivity(new Intent(源页面.this&#xff0c;目标页面.class))&#xff1b; 从当前页面回到上一个页面&#xff0c;相当于关闭当前页面&#xff0c;返回代码如下&#xff1a; finis…

python机器人Agent编程——多Agent框架的底层逻辑(上)

目录 一、前言二、两个核心概念2.1 Routines&#xff08;1&#xff09;清晰的Prompt&#xff08;2&#xff09;工具调用json schema自动生成&#xff08;3&#xff09;解析模型的toolcall指令&#xff08;4&#xff09;单Agent的循环决策与输出 PS.扩展阅读ps1.六自由度机器人相…

SOP搭建:企业标准化操作程序构建与实施指南

一、引言 在当今充满竞争的商业领域&#xff0c;实现企业运营的标准化、高效化和高质量化是提升企业市场竞争力的关键所在。标准操作程序&#xff08;SOP&#xff09;作为一种至关重要的管理工具&#xff0c;能够清晰地阐述业务流程&#xff0c;规范操作行为&#xff0c;并促进…

用 Python 从零开始创建神经网络(五):损失函数(Loss Functions)计算网络误差

用损失函数&#xff08;Loss Functions&#xff09;计算网络误差 引言1. 分类交叉熵损失&#xff08;Categorical Cross-Entropy Loss&#xff09;2. 分类交叉熵损失类&#xff08;The Categorical Cross-Entropy Loss Class&#xff09;展示到目前为止的所有代码3. 准确率计算…

ubuntu 安装kafka-eagle

上传压缩包 kafka-eagle-bin-2.0.8.tar.gz 到集群 /root/efak 目录 cd /root/efak tar -zxvf kafka-eagle-bin-2.0.8.tar.gz cd /root/efak/kafka-eagle-bin-2.0.8 mkdir /root/efakmodule tar -zxvf efak-web-2.0.8-bin.tar.gz -C /root/efakmodule/ mv /root/efakmodule/efak…

Zotero 7本地pdf文件名自适应中英文格式

问题 Zotero7默认语言是中文&#xff0c;发现本地pdf文献中均会出现“等”字&#xff0c;出现中英文不统一的不便。 &#xff08;注&#xff1a;存在et al.的pdf&#xff0c;是从外部直接拖进去的&#xff0c;不是自动产生的。&#xff09; 解决 zotero 7提供了丰富的文件后…

Redis性能优化——针对实习面试

目录 Redis性能优化什么是bigkey&#xff1f;bigkey的危害&#xff1f;如何处理bigkey?什么是hotkey&#xff1f;hotkey的危害&#xff1f;如何处理hotkey&#xff1f;如何处理大量key集中过期问题&#xff1f;什么是内存碎片&#xff1f;为什么会有Redis内存碎片&#xff1f;…

牛客挑战赛77

#include <iostream>// 函数 kXOR&#xff1a;计算两个数在 k 进制下的异或和 // 参数&#xff1a; // a: 第一个正整数 // b: 第二个正整数 // k: 进制基数 // 返回值&#xff1a; // 两数在 k 进制下的异或和&#xff08;十进制表示&#xff09; long long kXO…

开源共建 | 长安链开发常见问题及规避

长安链开源社区鼓励社区成员参与社区共建&#xff0c;参与形式包括不限于代码贡献、文章撰写、社区答疑等。腾讯云区块链王燕飞在参与长安链测试工作过程中&#xff0c;深入细致地总结了长安链实际开发应用中的常见问题及其有效的规避方法&#xff0c;相关内容多次解答社区成员…

EWM 打印

目录 1 简介 2 后台配置 3 主数据 4 业务操作 1 简介 打印即输出管理&#xff08;output management&#xff09;利用“条件表”那一套理论实现。而当打印跟 EWM 集成到一起时&#xff0c;也需要利用 PPF&#xff08;Post Processing Framework&#xff09;那一套理论。而…

LLaMA-Factory全流程训练模型

&#x1f917;本文主要讲述在docker下使用LLaMA-Factory训练推理模型。 &#x1fae1;拉取镜像 首先需要启动docker&#xff0c;然后在终端中输入&#xff1a; docker run -tid --gpus all -p 8000:8000 --name LLM -e NVIDIA_DRIVER_CAPABILITIEScompute,utility -e NVIDIA…

WebSocket简易聊天室实现(有详细解释)

完整代码 Arata08/online-chat-demo 服务端: 1.编写配置类&#xff0c;扫描有 ServerEndpoint 注解的 Bean import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.s…

Excel超级处理器:高效实现2种批量生成二维码方式

在Excel数据处理中&#xff0c;二维码的批量生成是一个常见且重要的需求。借助Excel超级处理器这一强大的插件&#xff0c;用户可以轻松实现二维码的两种主要批量生成方式&#xff1a;直接在单元格中显示二维码图片&#xff0c;以及直接生成二维码图片并保存在文件夹中。超级处…

Linux Android 正点原子RK3568替换开机Logo完整教程

0.这CSDN是有BUG吗?大家注意:表示路径的2个点号全都变成3个点号啦! 接下来的后文中,应该是2个点都被CSDN变成了3个点: 1.将这两个 bmp 图片文件720x1280_8bit拷贝到内核源码目录下,替换内核源码中默认的 logo 图片。注意:此时还缺少电量显示图片 2.编译内核 make d…

性能高于Transformer模型1.7-2倍,彩云科技发布基于DCFormer架构通用大模型云锦天章

2017年&#xff0c;谷歌发布《Attention Is All You Need》论文&#xff0c;首次提出Transformer架构&#xff0c;掀开了人工智能自然语言处理&#xff08;NLP&#xff09;领域发展的全新篇章。Transformer架构作为神经网络学习中最重要的架构&#xff0c;成为后来席卷全球的一…

函数指针示例

目录&#xff1a; 代码&#xff1a; main.c #include <stdio.h> #include <stdlib.h>int Max(int x, int y); int Min(int x, int y);int main(int argc, char**argv) {int x,y;scanf("%d",&x);scanf("%d",&y);int select;printf(&q…

【书生大模型实战营 闯关材料】入门岛:第4关 玩转HF/魔搭/魔乐社区

2.1.2-2.1.3 InternLM 模型下载 模型下载 使用Hugging Face平台、魔搭社区平台&#xff08;可选&#xff09;和魔乐社区平台&#xff08;可选&#xff09;下载文档中提到的模型&#xff08;至少需要下载config.json文件、model.safetensors.index.json文件&#xff09;&#x…

Android - Pixel 6a 手机OS 由 Android 15 降级到 Android 14 操作记录

Pixel 6a 手机由 Android 14 升级到 Android 15了&#xff0c;但是由于一些原因又想降级回 Android 14&#xff0c; 能降吗&#xff1f;该怎么降级呢&#xff1f;本篇文章来记述实际操作过程&#xff0c;希望能给想做相同操作的人一些帮助。 答案当然是能降&#xff0c;而且我…

python-文件内容操作

文章目录 文件的介绍文件的理解文件操作基本知识文件对象属性与常用方法文件的读取文件的写入**上下文管理语句 with****读CSV文件**二维数据的存储从CSV格式的文件中读取数据将数据写入CSV格式的文件 读取Excel格式数据文件(pandas库)读取Excel格式数据文件(pandas库) 文件的介…

《操作系统 - 清华大学》3 -3:连续内存分配:内存碎片与分区的动态分配

文章目录 0. 概述1. 内存碎片问题2. 动态分配3. 首次适配算法4. 最优适配算法5. 最差适配算法 0. 概述 内存分配是操作系统管理过程中很重要的环节&#xff0c;首先需要考虑的是一块连续区域分配的过程&#xff0c;这个过程中会有很多问题&#xff0c;首先比较关注的一个问题是…