问题

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

日志分析

看似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

  • 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 -->

<PreferenceScreen
    xmlns: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.ComposePreference
        android: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.RestrictedPreference
            android: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

    @Override
    public 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;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        Log.i(TAG, "displayPreference + ");
        mPreference = screen.findPreference(getPreferenceKey());

        //For debug as below
        if (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 implements
        LifecycleObserver, 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 BasePreferenceController
        implements 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

    /**
     * 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 status
            return callback.getAvailabilityStatus(defSubId);
        } else {
            // Otherwise, search whether there is one subId in device that support this preference
            final 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;
    }


}

解决方案

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

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部