From bff33e170cff5eb1a2038c059e6aa8e5d766d132 Mon Sep 17 00:00:00 2001 From: jackqdyulei Date: Wed, 3 Oct 2018 13:54:53 -0700 Subject: [PATCH] Copy MobileNetworkSettings to settings app This is a CL to copy most components from telephony to settings app with following changes to pass the preupload check: 1. Add [CHAR LIMIT]/comments for strings that miss them This CL cannot build and future CL will fix the broken part. Bug: 114749736 Test: Build Change-Id: I744d537610eeeb7f2fb801defdd0ce47ef6088b6 --- res/values/arrays.xml | 182 ++ res/values/config.xml | 4 + res/values/strings.xml | 147 ++ res/xml/cdma_options.xml | 54 + res/xml/gsm_umts_options.xml | 60 + res/xml/network_setting_fragment.xml | 91 + .../AdvancedOptionsPreference.java | 42 + .../settings/mobilenetwork/CdmaOptions.java | 203 ++ .../CdmaSubscriptionListPreference.java | 119 + .../CdmaSystemSelectListPreference.java | 187 ++ .../mobilenetwork/DataUsagePreference.java | 80 + .../mobilenetwork/GsmUmtsOptions.java | 179 ++ .../mobilenetwork/MobileDataPreference.java | 320 +++ .../mobilenetwork/MobileNetworkFragment.java | 2003 +++++++++++++++++ .../mobilenetwork/NetworkOperators.java | 299 +++ .../mobilenetwork/NetworkScanHelper.java | 287 +++ .../NetworkSelectListPreference.java | 505 +++++ .../mobilenetwork/RoamingDialogFragment.java | 93 + 18 files changed, 4855 insertions(+) create mode 100644 res/xml/cdma_options.xml create mode 100644 res/xml/gsm_umts_options.xml create mode 100644 res/xml/network_setting_fragment.xml create mode 100644 src/com/android/settings/mobilenetwork/AdvancedOptionsPreference.java create mode 100644 src/com/android/settings/mobilenetwork/CdmaOptions.java create mode 100644 src/com/android/settings/mobilenetwork/CdmaSubscriptionListPreference.java create mode 100644 src/com/android/settings/mobilenetwork/CdmaSystemSelectListPreference.java create mode 100644 src/com/android/settings/mobilenetwork/DataUsagePreference.java create mode 100644 src/com/android/settings/mobilenetwork/GsmUmtsOptions.java create mode 100644 src/com/android/settings/mobilenetwork/MobileDataPreference.java create mode 100644 src/com/android/settings/mobilenetwork/MobileNetworkFragment.java create mode 100644 src/com/android/settings/mobilenetwork/NetworkOperators.java create mode 100644 src/com/android/settings/mobilenetwork/NetworkScanHelper.java create mode 100644 src/com/android/settings/mobilenetwork/NetworkSelectListPreference.java create mode 100644 src/com/android/settings/mobilenetwork/RoamingDialogFragment.java diff --git a/res/values/arrays.xml b/res/values/arrays.xml index e57cbfa00f2..6cc10fe7882 100644 --- a/res/values/arrays.xml +++ b/res/values/arrays.xml @@ -1133,4 +1133,186 @@ 4 + + @string/network_lte + @string/network_3G + @string/network_2G + + + @string/network_4G + @string/network_3G + @string/network_2G + + + "9" + "0" + "1" + + + + + Home only + + + + Automatic + + + + "0" + + + + "2" + + + + + GSM/WCDMA preferred + GSM only + WCDMA only + GSM/WCDMA auto + CDMA/EvDo auto + CDMA w/o EvDo + EvDo only + CDMA/EvDo/GSM/WCDMA + CDMA + LTE/EvDo + GSM/WCDMA/LTE + Global + LTE + LTE / WCDMA + TDSCDMA only + TDSCDMA/WCDMA + LTE/TDSCDMA + TDSCDMA/GSM + LTE/TDSCDMA/GSM + TDSCDMA/GSM/WCDMA + LTE/TDSCDMA/WCDMA + LTE/TDSCDMA/GSM/WCDMA + TDSCDMA/CDMA/EVDO/GSM/WCDMA + LTE/TDSCDMA/CDMA/EVDO/GSM/WCDMA + + + + "0" + "1" + "2" + "3" + "4" + "5" + "6" + "7" + "8" + "9" + "10" + "11" + "12" + "13" + "14" + "15" + "16" + "17" + "18" + "19" + "20" + "21" + "22" + + + + + RUIM/SIM + NV + + + + "0" + "1" + + + + @string/network_lte + @string/network_3G + + + @string/network_4G + @string/network_3G + + + "9" + "0" + + + + @string/network_3G + @string/network_2G + + + "0" + "1" + + + + @string/network_3G + + + "0" + + + + @string/network_lte + @string/network_3G + @string/network_1x + @string/network_global + + + "8" + "4" + "5" + "10" + + + + @string/network_3G + @string/network_1x + + + "4" + "5" + + + + @string/network_lte + @string/network_global + + + "8" + "10" + + + + @string/network_lte + @string/network_3G + @string/network_2G + + + "22" + "18" + "1" + + + + + Global + LTE / CDMA + LTE / GSM / UMTS + + + + "10" + "8" + "9" + + diff --git a/res/values/config.xml b/res/values/config.xml index b9a69f105e8..0129768d60c 100755 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -136,4 +136,8 @@ true + + + + false diff --git a/res/values/strings.xml b/res/values/strings.xml index d78b7b01af6..0060202f5cb 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -10112,4 +10112,151 @@ No SIM card + + + Advanced Calling + + + Preferred network mode: WCDMA preferred + + Preferred network mode: GSM only + + Preferred network mode: WCDMA only + + Preferred network mode: GSM / WCDMA + + Preferred network mode: CDMA + + Preferred network mode: CDMA / EvDo + + Preferred network mode: CDMA only + + Preferred network mode: EvDo only + + Preferred network mode: CDMA/EvDo/GSM/WCDMA + + Preferred network mode: LTE + + Preferred network mode: GSM/WCDMA/LTE + + Preferred network mode: CDMA+LTE/EVDO + + Preferred network mode: Global + + Preferred network mode: LTE / WCDMA + + Preferred network mode: LTE / GSM / UMTS + + Preferred network mode: LTE / CDMA + + Preferred network mode: TDSCDMA + + Preferred network mode: TDSCDMA / WCDMA + + Preferred network mode: LTE / TDSCDMA + + Preferred network mode: TDSCDMA / GSM + + Preferred network mode: LTE/GSM/TDSCDMA + + Preferred network mode: TDSCDMA/GSM/WCDMA + + Preferred network mode: LTE/TDSCDMA/WCDMA + + Preferred network mode: LTE/TDSCDMA/GSM/WCDMA + + Preferred network mode: TDSCDMA/CDMA/EvDo/GSM/WCDMA + + Preferred network mode: LTE/TDSCDMA/CDMA/EvDo/GSM/WCDMA + + + LTE (recommended) + + 4G (recommended) + + 3G + + 2G + + 1x + + Global + + + + + + Disconnected + + Connected + + Connecting\u2026 + + Couldn’t connect + + + Available networks + + Searching\u2026 + + No networks found. + + Couldn\u2019t find networks. Try again. + + Registering on %s\u2026 + + Your SIM card doesn\u2019t allow a connection to this network. + + Can\u2019t connect to this network right now. Try again later. + + Registered on network. + + + Automatically select network + + + Choose network + + Carrier settings + + Set up data service + + Mobile data + + Access data using mobile network + + + Preferred network type + + Change the network operating mode + + Preferred network type + + Carrier + + + Calling + + Carrier video calling + + + System select + + Change the CDMA roaming mode + + System select + + + Network + + Network + + + CDMA subscription + + Change between RUIM/SIM and NV + + subscription diff --git a/res/xml/cdma_options.xml b/res/xml/cdma_options.xml new file mode 100644 index 00000000000..f6ef97a7508 --- /dev/null +++ b/res/xml/cdma_options.xml @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/gsm_umts_options.xml b/res/xml/gsm_umts_options.xml new file mode 100644 index 00000000000..81b2debb83c --- /dev/null +++ b/res/xml/gsm_umts_options.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/network_setting_fragment.xml b/res/xml/network_setting_fragment.xml new file mode 100644 index 00000000000..d334861929e --- /dev/null +++ b/res/xml/network_setting_fragment.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/com/android/settings/mobilenetwork/AdvancedOptionsPreference.java b/src/com/android/settings/mobilenetwork/AdvancedOptionsPreference.java new file mode 100644 index 00000000000..23e3cd4d1aa --- /dev/null +++ b/src/com/android/settings/mobilenetwork/AdvancedOptionsPreference.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.mobilenetwork; + +import android.content.Context; +import android.preference.Preference; +import android.util.AttributeSet; +import android.view.View; +import android.widget.TextView; + +/** + * Customized preference class representing the "Advanced" button that expands to fields that + * are hidden by default. + */ +public class AdvancedOptionsPreference extends Preference { + public AdvancedOptionsPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + + setIcon(R.drawable.ic_expand_more); + setTitle(R.string.advanced_options_title); + TextView summary = view.findViewById(android.R.id.summary); + summary.setMaxLines(1); + } +} diff --git a/src/com/android/settings/mobilenetwork/CdmaOptions.java b/src/com/android/settings/mobilenetwork/CdmaOptions.java new file mode 100644 index 00000000000..38c6ca3dd5a --- /dev/null +++ b/src/com/android/settings/mobilenetwork/CdmaOptions.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.mobilenetwork; + +import android.content.Intent; +import android.os.PersistableBundle; +import android.os.SystemProperties; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.telephony.PhoneConstants; +import com.android.phone.MobileNetworkSettings; +import com.android.phone.RestrictedPreference; +import com.android.settingslib.RestrictedLockUtilsInternal; + +/** + * List of Phone-specific settings screens. + */ +public class CdmaOptions { + private static final String LOG_TAG = "CdmaOptions"; + + private CarrierConfigManager mCarrierConfigManager; + private CdmaSystemSelectListPreference mButtonCdmaSystemSelect; + private CdmaSubscriptionListPreference mButtonCdmaSubscription; + private RestrictedPreference mButtonAPNExpand; + private Preference mCategoryAPNExpand; + private Preference mButtonCarrierSettings; + + private static final String BUTTON_CDMA_SYSTEM_SELECT_KEY = "cdma_system_select_key"; + private static final String BUTTON_CDMA_SUBSCRIPTION_KEY = "cdma_subscription_key"; + private static final String BUTTON_CARRIER_SETTINGS_KEY = "carrier_settings_key"; + private static final String BUTTON_APN_EXPAND_KEY = "button_cdma_apn_key"; + private static final String CATEGORY_APN_EXPAND_KEY = "category_cdma_apn_key"; + + private PreferenceFragment mPrefFragment; + private PreferenceScreen mPrefScreen; + private int mSubId; + + public CdmaOptions(PreferenceFragment prefFragment, PreferenceScreen prefScreen, int subId) { + mPrefFragment = prefFragment; + mPrefScreen = prefScreen; + mPrefFragment.addPreferencesFromResource(R.xml.cdma_options); + mCarrierConfigManager = new CarrierConfigManager(prefFragment.getContext()); + + // Initialize preferences. + mButtonCdmaSystemSelect = (CdmaSystemSelectListPreference) mPrefScreen + .findPreference(BUTTON_CDMA_SYSTEM_SELECT_KEY); + mButtonCdmaSubscription = (CdmaSubscriptionListPreference) mPrefScreen + .findPreference(BUTTON_CDMA_SUBSCRIPTION_KEY); + mButtonCarrierSettings = mPrefScreen.findPreference(BUTTON_CARRIER_SETTINGS_KEY); + mButtonAPNExpand = (RestrictedPreference) mPrefScreen.findPreference(BUTTON_APN_EXPAND_KEY); + mCategoryAPNExpand = mPrefScreen.findPreference(CATEGORY_APN_EXPAND_KEY); + + updateSubscriptionId(subId); + } + + protected void updateSubscriptionId(int subId) { + mSubId = subId; + int phoneType = TelephonyManager.from(mPrefFragment.getContext()) + .createForSubscriptionId(mSubId).getPhoneType(); + + PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); + // Some CDMA carriers want the APN settings. + boolean addAPNExpand = shouldAddApnExpandPreference(phoneType, carrierConfig); + boolean addCdmaSubscription = + deviceSupportsNvAndRuim(); + // Read platform settings for carrier settings + boolean addCarrierSettings = + carrierConfig.getBoolean(CarrierConfigManager.KEY_CARRIER_SETTINGS_ENABLE_BOOL); + + mPrefScreen.addPreference(mButtonCdmaSystemSelect); + mButtonCdmaSystemSelect.setEnabled(true); + + // Making no assumptions of whether they are added or removed at this point. + // Calling add or remove explicitly to make sure they are updated. + + if (addAPNExpand) { + log("update: addAPNExpand"); + mButtonAPNExpand.setDisabledByAdmin( + MobileNetworkSettings.isDpcApnEnforced(mButtonAPNExpand.getContext()) + ? RestrictedLockUtilsInternal.getDeviceOwner( + mButtonAPNExpand.getContext()) + : null); + mButtonAPNExpand.setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + MetricsLogger.action(mButtonAPNExpand.getContext(), + MetricsEvent.ACTION_MOBILE_NETWORK_APN_SETTINGS); + // We need to build the Intent by hand as the Preference Framework + // does not allow to add an Intent with some extras into a Preference + // XML file + final Intent intent = new Intent(Settings.ACTION_APN_SETTINGS); + // This will setup the Home and Search affordance + intent.putExtra(":settings:show_fragment_as_subsetting", true); + intent.putExtra("sub_id", mSubId); + mPrefFragment.startActivity(intent); + return true; + } + }); + mPrefScreen.addPreference(mCategoryAPNExpand); + } else { + mPrefScreen.removePreference(mCategoryAPNExpand); + } + + if (addCdmaSubscription) { + log("Both NV and Ruim supported, ENABLE subscription type selection"); + mPrefScreen.addPreference(mButtonCdmaSubscription); + mButtonCdmaSubscription.setEnabled(true); + } else { + log("Both NV and Ruim NOT supported, REMOVE subscription type selection"); + mPrefScreen.removePreference(mButtonCdmaSubscription); + } + + if (addCarrierSettings) { + mPrefScreen.addPreference(mButtonCarrierSettings); + } else { + mPrefScreen.removePreference(mButtonCarrierSettings); + } + } + + /** + * Return whether we should add the APN expandable preference based on the phone type and + * carrier config + */ + @VisibleForTesting + public static boolean shouldAddApnExpandPreference(int phoneType, PersistableBundle config) { + return phoneType == PhoneConstants.PHONE_TYPE_CDMA + && config.getBoolean(CarrierConfigManager.KEY_SHOW_APN_SETTING_CDMA_BOOL); + } + + private boolean deviceSupportsNvAndRuim() { + // retrieve the list of subscription types supported by device. + String subscriptionsSupported = SystemProperties.get("ril.subscription.types"); + boolean nvSupported = false; + boolean ruimSupported = false; + + log("deviceSupportsnvAnRum: prop=" + subscriptionsSupported); + if (!TextUtils.isEmpty(subscriptionsSupported)) { + // Searches through the comma-separated list for a match for "NV" + // and "RUIM" to update nvSupported and ruimSupported. + for (String subscriptionType : subscriptionsSupported.split(",")) { + subscriptionType = subscriptionType.trim(); + if (subscriptionType.equalsIgnoreCase("NV")) { + nvSupported = true; + } + if (subscriptionType.equalsIgnoreCase("RUIM")) { + ruimSupported = true; + } + } + } + + log("deviceSupportsnvAnRum: nvSupported=" + nvSupported + + " ruimSupported=" + ruimSupported); + return (nvSupported && ruimSupported); + } + + public boolean preferenceTreeClick(Preference preference) { + if (preference.getKey().equals(BUTTON_CDMA_SYSTEM_SELECT_KEY)) { + log("preferenceTreeClick: return BUTTON_CDMA_ROAMING_KEY true"); + return true; + } + if (preference.getKey().equals(BUTTON_CDMA_SUBSCRIPTION_KEY)) { + log("preferenceTreeClick: return CDMA_SUBSCRIPTION_KEY true"); + return true; + } + return false; + } + + public void showDialog(Preference preference) { + if (preference.getKey().equals(BUTTON_CDMA_SYSTEM_SELECT_KEY)) { + mButtonCdmaSystemSelect.showDialog(null); + } else if (preference.getKey().equals(BUTTON_CDMA_SUBSCRIPTION_KEY)) { + mButtonCdmaSubscription.showDialog(null); + } + } + + protected void log(String s) { + android.util.Log.d(LOG_TAG, s); + } +} diff --git a/src/com/android/settings/mobilenetwork/CdmaSubscriptionListPreference.java b/src/com/android/settings/mobilenetwork/CdmaSubscriptionListPreference.java new file mode 100644 index 00000000000..9752e9936b1 --- /dev/null +++ b/src/com/android/settings/mobilenetwork/CdmaSubscriptionListPreference.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.mobilenetwork; + +import android.content.Context; +import android.os.Bundle; +import android.preference.ListPreference; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.util.AttributeSet; +import android.util.Log; + +import com.android.internal.telephony.Phone; +import com.android.settingslib.utils.ThreadUtils; + +public class CdmaSubscriptionListPreference extends ListPreference { + + private static final String LOG_TAG = "CdmaSubscriptionListPreference"; + + // Used for CDMA subscription mode + private static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; + private static final int CDMA_SUBSCRIPTION_NV = 1; + + //preferredSubscriptionMode 0 - RUIM/SIM, preferred + // 1 - NV + static final int preferredSubscriptionMode = Phone.PREFERRED_CDMA_SUBSCRIPTION; + + private TelephonyManager mTelephonyManager; + + public CdmaSubscriptionListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + mTelephonyManager = TelephonyManager.from(context); + setCurrentCdmaSubscriptionModeValue(); + } + + private void setCurrentCdmaSubscriptionModeValue() { + int cdmaSubscriptionMode = Settings.Global.getInt(getContext().getContentResolver(), + Settings.Global.CDMA_SUBSCRIPTION_MODE, preferredSubscriptionMode); + setValue(Integer.toString(cdmaSubscriptionMode)); + } + + public CdmaSubscriptionListPreference(Context context) { + this(context, null); + } + + /** + * Sets the subscription id associated with this preference. + * + * @param subId the subscription id. + */ + public void setSubscriptionId(int subId) { + mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(subId); + } + + @Override + protected void showDialog(Bundle state) { + setCurrentCdmaSubscriptionModeValue(); + + super.showDialog(state); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + super.onDialogClosed(positiveResult); + + if (!positiveResult) { + //The button was dismissed - no need to set new value + return; + } + + int buttonCdmaSubscriptionMode = Integer.parseInt(getValue()); + Log.d(LOG_TAG, "Setting new value " + buttonCdmaSubscriptionMode); + int statusCdmaSubscriptionMode; + switch(buttonCdmaSubscriptionMode) { + case CDMA_SUBSCRIPTION_NV: + statusCdmaSubscriptionMode = Phone.CDMA_SUBSCRIPTION_NV; + break; + case CDMA_SUBSCRIPTION_RUIM_SIM: + statusCdmaSubscriptionMode = Phone.CDMA_SUBSCRIPTION_RUIM_SIM; + break; + default: + statusCdmaSubscriptionMode = Phone.PREFERRED_CDMA_SUBSCRIPTION; + } + + // Set the CDMA subscription mode, when mode has been successfully changed, update the + // mode to the global setting. + ThreadUtils.postOnBackgroundThread(() -> { + // The subscription mode selected by user. + int cdmaSubscriptionMode = Integer.parseInt(getValue()); + + boolean isSuccessed = mTelephonyManager.setCdmaSubscriptionMode( + statusCdmaSubscriptionMode); + + // Update the global settings if successed. + if (isSuccessed) { + Settings.Global.putInt(getContext().getContentResolver(), + Settings.Global.CDMA_SUBSCRIPTION_MODE, + cdmaSubscriptionMode); + } else { + Log.e(LOG_TAG, "Setting Cdma subscription source failed"); + } + }); + } +} diff --git a/src/com/android/settings/mobilenetwork/CdmaSystemSelectListPreference.java b/src/com/android/settings/mobilenetwork/CdmaSystemSelectListPreference.java new file mode 100644 index 00000000000..ab62f8800b2 --- /dev/null +++ b/src/com/android/settings/mobilenetwork/CdmaSystemSelectListPreference.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.mobilenetwork; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.preference.ListPreference; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.util.AttributeSet; +import android.util.Log; + +import com.android.settingslib.utils.ThreadUtils; + +public class CdmaSystemSelectListPreference extends ListPreference { + + private static final String LOG_TAG = "CdmaRoamingListPreference"; + private static final boolean DBG = false; + + private TelephonyManager mTelephonyManager; + private MyHandler mHandler = new MyHandler(); + + public CdmaSystemSelectListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + + mHandler = new MyHandler(); + mTelephonyManager = TelephonyManager.from(context); + } + + public CdmaSystemSelectListPreference(Context context) { + this(context, null); + } + + /** + * Sets the subscription id associated with this preference. + * + * @param subId the subscription id. + */ + public void setSubscriptionId(int subId) { + mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(subId); + queryCdmaRoamingMode(); + } + + @Override + protected void showDialog(Bundle state) { + if (!mTelephonyManager.getEmergencyCallbackMode()) { + super.showDialog(state); + } + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + super.onDialogClosed(positiveResult); + + if (positiveResult && (getValue() != null)) { + int buttonCdmaRoamingMode = Integer.parseInt(getValue()); + int settingsCdmaRoamingMode = Settings.Global.getInt( + getContext().getContentResolver(), + Settings.Global.CDMA_ROAMING_MODE, + TelephonyManager.CDMA_ROAMING_MODE_HOME); + if (buttonCdmaRoamingMode != settingsCdmaRoamingMode) { + int cdmaRoamingMode = TelephonyManager.CDMA_ROAMING_MODE_ANY; + if (buttonCdmaRoamingMode != TelephonyManager.CDMA_ROAMING_MODE_ANY) { + cdmaRoamingMode = TelephonyManager.CDMA_ROAMING_MODE_HOME; + } + //Set the Settings.Secure network mode + Settings.Global.putInt( + getContext().getContentResolver(), + Settings.Global.CDMA_ROAMING_MODE, + buttonCdmaRoamingMode); + //Set the roaming preference mode + setCdmaRoamingMode(cdmaRoamingMode); + } + } else { + Log.d(LOG_TAG, String.format("onDialogClosed: positiveResult=%b value=%s -- do nothing", + positiveResult, getValue())); + } + } + + private class MyHandler extends Handler { + + static final int MESSAGE_GET_ROAMING_PREFERENCE = 0; + static final int MESSAGE_SET_ROAMING_PREFERENCE = 1; + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_GET_ROAMING_PREFERENCE: + handleQueryCdmaRoamingPreference(msg); + break; + + case MESSAGE_SET_ROAMING_PREFERENCE: + handleSetCdmaRoamingPreference(msg); + break; + } + } + + private void handleQueryCdmaRoamingPreference(Message msg) { + int cdmaRoamingMode = msg.arg1; + + if (cdmaRoamingMode != TelephonyManager.CDMA_ROAMING_MODE_RADIO_DEFAULT) { + int settingsRoamingMode = Settings.Global.getInt( + getContext().getContentResolver(), + Settings.Global.CDMA_ROAMING_MODE, + TelephonyManager.CDMA_ROAMING_MODE_HOME); + + //check that statusCdmaRoamingMode is from an accepted value + if (cdmaRoamingMode == TelephonyManager.CDMA_ROAMING_MODE_HOME + || cdmaRoamingMode == TelephonyManager.CDMA_ROAMING_MODE_ANY) { + //check changes in statusCdmaRoamingMode and updates settingsRoamingMode + if (cdmaRoamingMode != settingsRoamingMode) { + settingsRoamingMode = cdmaRoamingMode; + //changes the Settings.Secure accordingly to statusCdmaRoamingMode + Settings.Global.putInt( + getContext().getContentResolver(), + Settings.Global.CDMA_ROAMING_MODE, + settingsRoamingMode); + } + //changes the mButtonPreferredNetworkMode accordingly to modemNetworkMode + setValue(Integer.toString(cdmaRoamingMode)); + } + else { + if(DBG) Log.i(LOG_TAG, "reset cdma roaming mode to default" ); + resetCdmaRoamingModeToDefault(); + } + } + } + + private void handleSetCdmaRoamingPreference(Message msg) { + boolean isSuccessed = (boolean) msg.obj; + + if (isSuccessed && (getValue() != null)) { + int cdmaRoamingMode = Integer.parseInt(getValue()); + Settings.Global.putInt( + getContext().getContentResolver(), + Settings.Global.CDMA_ROAMING_MODE, + cdmaRoamingMode ); + } else { + queryCdmaRoamingMode(); + } + } + + private void resetCdmaRoamingModeToDefault() { + //set the mButtonCdmaRoam + setValue(Integer.toString(TelephonyManager.CDMA_ROAMING_MODE_ANY)); + //set the Settings.System + Settings.Global.putInt( + getContext().getContentResolver(), + Settings.Global.CDMA_ROAMING_MODE, + TelephonyManager.CDMA_ROAMING_MODE_ANY); + //Set the Status + setCdmaRoamingMode(TelephonyManager.CDMA_ROAMING_MODE_ANY); + } + } + + private void queryCdmaRoamingMode() { + ThreadUtils.postOnBackgroundThread(() -> { + Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_GET_ROAMING_PREFERENCE); + msg.arg1 = mTelephonyManager.getCdmaRoamingMode(); + msg.sendToTarget(); + }); + } + + private void setCdmaRoamingMode(int mode) { + ThreadUtils.postOnBackgroundThread(() -> { + Message msg = mHandler.obtainMessage(MyHandler.MESSAGE_SET_ROAMING_PREFERENCE); + msg.obj = mTelephonyManager.setCdmaRoamingMode(mode); + msg.sendToTarget(); + }); + } +} diff --git a/src/com/android/settings/mobilenetwork/DataUsagePreference.java b/src/com/android/settings/mobilenetwork/DataUsagePreference.java new file mode 100644 index 00000000000..594f83bb27a --- /dev/null +++ b/src/com/android/settings/mobilenetwork/DataUsagePreference.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.mobilenetwork; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.net.NetworkTemplate; +import android.preference.Preference; +import android.provider.Settings; +import android.telephony.TelephonyManager; +import android.text.format.Formatter; +import android.util.AttributeSet; + +import com.android.settingslib.net.DataUsageController; + +/** + * The preference that shows mobile data usage summary and + * leads to mobile data usage list page. + */ +public class DataUsagePreference extends Preference { + + private NetworkTemplate mTemplate; + private int mSubId; + + public DataUsagePreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + /** + * After creating this preference, this functions needs to be called to + * initialize which subID it connects to. + */ + public void initialize(int subId) { + Activity activity = (Activity) getContext(); + + mSubId = subId; + mTemplate = getNetworkTemplate(activity, subId); + + DataUsageController controller = new DataUsageController(activity); + + DataUsageController.DataUsageInfo usageInfo = controller.getDataUsageInfo(mTemplate); + setSummary(activity.getString(R.string.data_usage_template, + Formatter.formatFileSize(activity, usageInfo.usageLevel), usageInfo.period)); + setIntent(getIntent()); + } + + @Override + public Intent getIntent() { + Intent intent = new Intent(Settings.ACTION_MOBILE_DATA_USAGE); + + intent.putExtra(Settings.EXTRA_NETWORK_TEMPLATE, mTemplate); + intent.putExtra(Settings.EXTRA_SUB_ID, mSubId); + + return intent; + } + + private NetworkTemplate getNetworkTemplate(Activity activity, int subId) { + TelephonyManager tm = (TelephonyManager) activity + .getSystemService(Context.TELEPHONY_SERVICE); + NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll( + tm.getSubscriberId(subId)); + return NetworkTemplate.normalize(mobileAll, + tm.getMergedSubscriberIds()); + } +} diff --git a/src/com/android/settings/mobilenetwork/GsmUmtsOptions.java b/src/com/android/settings/mobilenetwork/GsmUmtsOptions.java new file mode 100644 index 00000000000..8400ec37d5f --- /dev/null +++ b/src/com/android/settings/mobilenetwork/GsmUmtsOptions.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.mobilenetwork; + +import android.content.Context; +import android.content.Intent; +import android.os.PersistableBundle; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.provider.Settings; +import android.telephony.CarrierConfigManager; +import android.telephony.TelephonyManager; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneConstants; +import com.android.phone.INetworkQueryService; +import com.android.phone.MobileNetworkSettings; +import com.android.phone.PhoneGlobals; +import com.android.phone.RestrictedPreference; +import com.android.settingslib.RestrictedLockUtilsInternal; + +/** + * List of Network-specific settings screens. + */ +public class GsmUmtsOptions { + private static final String LOG_TAG = "GsmUmtsOptions"; + + private CarrierConfigManager mCarrierConfigManager; + private RestrictedPreference mButtonAPNExpand; + private Preference mCategoryAPNExpand; + Preference mCarrierSettingPref; + + private NetworkOperators mNetworkOperator; + + private static final String BUTTON_APN_EXPAND_KEY = "button_gsm_apn_key"; + private static final String CATEGORY_APN_EXPAND_KEY = "category_gsm_apn_key"; + private static final String BUTTON_CARRIER_SETTINGS_KEY = "carrier_settings_key"; + + public static final String EXTRA_SUB_ID = "sub_id"; + private PreferenceFragment mPrefFragment; + private PreferenceScreen mPrefScreen; + + public GsmUmtsOptions(PreferenceFragment prefFragment, PreferenceScreen prefScreen, + final int subId, INetworkQueryService queryService) { + final Context context = prefFragment.getContext(); + mPrefFragment = prefFragment; + mPrefScreen = prefScreen; + mCarrierConfigManager = new CarrierConfigManager(context); + mPrefFragment.addPreferencesFromResource(R.xml.gsm_umts_options); + mButtonAPNExpand = (RestrictedPreference) mPrefScreen.findPreference(BUTTON_APN_EXPAND_KEY); + mCategoryAPNExpand = mPrefScreen.findPreference(CATEGORY_APN_EXPAND_KEY); + mNetworkOperator = (NetworkOperators) mPrefScreen + .findPreference(NetworkOperators.CATEGORY_NETWORK_OPERATORS_KEY); + mCarrierSettingPref = mPrefScreen.findPreference(BUTTON_CARRIER_SETTINGS_KEY); + + mNetworkOperator.initialize(); + + update(subId, queryService); + } + + // Unlike mPrefFragment or mPrefScreen, subId or queryService may change during lifecycle of + // GsmUmtsOptions. When that happens, we update GsmUmtsOptions with new parameters. + protected void update(final int subId, INetworkQueryService queryService) { + boolean addAPNExpand = true; + boolean addNetworkOperatorsCategory = true; + boolean addCarrierSettings = true; + final TelephonyManager telephonyManager = TelephonyManager.from(mPrefFragment.getContext()) + .createForSubscriptionId(subId); + Phone phone = PhoneGlobals.getPhone(subId); + if (phone == null) return; + if (telephonyManager.getPhoneType() != PhoneConstants.PHONE_TYPE_GSM) { + log("Not a GSM phone"); + addAPNExpand = false; + mNetworkOperator.setEnabled(false); + } else { + log("Not a CDMA phone"); + PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(subId); + + // Determine which options to display. For GSM these are defaulted to true in + // CarrierConfigManager, but they maybe overriden by DefaultCarrierConfigService or a + // carrier app. + // Note: these settings used to be controlled with overlays in + // Telephony/res/values/config.xml + if (!carrierConfig.getBoolean(CarrierConfigManager.KEY_APN_EXPAND_BOOL) + && mCategoryAPNExpand != null) { + addAPNExpand = false; + } + if (!carrierConfig.getBoolean( + CarrierConfigManager.KEY_OPERATOR_SELECTION_EXPAND_BOOL)) { + addNetworkOperatorsCategory = false; + } + + if (carrierConfig.getBoolean(CarrierConfigManager.KEY_CSP_ENABLED_BOOL)) { + if (phone.isCspPlmnEnabled()) { + log("[CSP] Enabling Operator Selection menu."); + mNetworkOperator.setEnabled(true); + } else { + log("[CSP] Disabling Operator Selection menu."); + addNetworkOperatorsCategory = false; + } + } + + // Read platform settings for carrier settings + addCarrierSettings = carrierConfig.getBoolean( + CarrierConfigManager.KEY_CARRIER_SETTINGS_ENABLE_BOOL); + } + + // Making no assumptions of whether they are added or removed at this point. + // Calling add or remove explicitly to make sure they are updated. + + if (addAPNExpand) { + log("update: addAPNExpand"); + mButtonAPNExpand.setDisabledByAdmin( + MobileNetworkSettings.isDpcApnEnforced(mButtonAPNExpand.getContext()) + ? RestrictedLockUtilsInternal.getDeviceOwner( + mButtonAPNExpand.getContext()) + : null); + mButtonAPNExpand.setOnPreferenceClickListener( + new Preference.OnPreferenceClickListener() { + @Override + public boolean onPreferenceClick(Preference preference) { + MetricsLogger.action(mButtonAPNExpand.getContext(), + MetricsEvent.ACTION_MOBILE_NETWORK_APN_SETTINGS); + // We need to build the Intent by hand as the Preference Framework + // does not allow to add an Intent with some extras into a Preference + // XML file + final Intent intent = new Intent(Settings.ACTION_APN_SETTINGS); + // This will setup the Home and Search affordance + intent.putExtra(":settings:show_fragment_as_subsetting", true); + intent.putExtra(EXTRA_SUB_ID, subId); + mPrefFragment.startActivity(intent); + return true; + } + }); + mPrefScreen.addPreference(mCategoryAPNExpand); + } else { + mPrefScreen.removePreference(mCategoryAPNExpand); + } + + if (addNetworkOperatorsCategory) { + mPrefScreen.addPreference(mNetworkOperator); + mNetworkOperator.update(subId, queryService); + } else { + mPrefScreen.removePreference(mNetworkOperator); + } + + if (addCarrierSettings) { + mPrefScreen.addPreference(mCarrierSettingPref); + } else { + mPrefScreen.removePreference(mCarrierSettingPref); + } + + } + + protected boolean preferenceTreeClick(Preference preference) { + return mNetworkOperator.preferenceTreeClick(preference); + } + + protected void log(String s) { + android.util.Log.d(LOG_TAG, s); + } +} diff --git a/src/com/android/settings/mobilenetwork/MobileDataPreference.java b/src/com/android/settings/mobilenetwork/MobileDataPreference.java new file mode 100644 index 00000000000..39f4d2cccc3 --- /dev/null +++ b/src/com/android/settings/mobilenetwork/MobileDataPreference.java @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.mobilenetwork; + +import android.app.AlertDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.DialogPreference; +import android.preference.PreferenceScreen; +import android.provider.Settings.Global; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.Checkable; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; + +import java.util.List; + +/** + * Customized Preference to enable / disable mobile data. + * Basically copy of with com.android.settings.CellDataPreference. + */ +public class MobileDataPreference extends DialogPreference { + + private static final boolean DBG = false; + private static final String TAG = "MobileDataPreference"; + + public int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + public boolean mChecked; + // Whether to show the dialog to ask switching default data subscription. + // Should be true only when a multi-sim phone only supports data connection on a single phone, + // and user is enabling data on the non-default phone. + public boolean mMultiSimDialog; + private TelephonyManager mTelephonyManager; + private SubscriptionManager mSubscriptionManager; + + public MobileDataPreference(Context context, AttributeSet attrs) { + super(context, attrs, com.android.internal.R.attr.switchPreferenceStyle); + } + + // Must be called to avoid binder leakage. + void dispose() { + mListener.setListener(false, mSubId, getContext()); + } + + @Override + protected void onRestoreInstanceState(Parcelable s) { + CellDataState state = (CellDataState) s; + super.onRestoreInstanceState(state.getSuperState()); + mTelephonyManager = TelephonyManager.from(getContext()); + mChecked = state.mChecked; + mMultiSimDialog = state.mMultiSimDialog; + if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + mSubId = state.mSubId; + setKey(getKey() + mSubId); + } + notifyChanged(); + } + + @Override + protected Parcelable onSaveInstanceState() { + CellDataState state = new CellDataState(super.onSaveInstanceState()); + state.mChecked = mChecked; + state.mMultiSimDialog = mMultiSimDialog; + state.mSubId = mSubId; + return state; + } + + @Override + protected void onAttachedToActivity() { + super.onAttachedToActivity(); + mListener.setListener(true, mSubId, getContext()); + } + + @Override + protected void onPrepareForRemoval() { + mListener.setListener(false, mSubId, getContext()); + super.onPrepareForRemoval(); + } + + /** + * Initialize this preference with subId. + */ + public void initialize(int subId) { + if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { + throw new IllegalArgumentException("MobileDataPreference needs a SubscriptionInfo"); + } + mSubscriptionManager = SubscriptionManager.from(getContext()); + mTelephonyManager = TelephonyManager.from(getContext()); + if (mSubId != subId) { + mSubId = subId; + setKey(getKey() + subId); + } + updateChecked(); + } + + private void updateChecked() { + setChecked(mTelephonyManager.getDataEnabled(mSubId)); + } + + @Override + public void performClick(PreferenceScreen preferenceScreen) { + if (!isEnabled() || !SubscriptionManager.isValidSubscriptionId(mSubId)) { + return; + } + final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo( + mSubId); + final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo(); + final boolean isMultiSim = (mTelephonyManager.getSimCount() > 1); + final boolean isMultipleDataOnCapable = + (mTelephonyManager.getNumberOfModemsWithSimultaneousDataConnections() > 1); + final boolean isDefaultDataSubscription = (nextSir != null && currentSir != null + && currentSir.getSubscriptionId() == nextSir.getSubscriptionId()); + if (mChecked) { + if (!isMultiSim) { + // disabling data; show confirmation dialog which eventually + // calls setMobileDataEnabled() once user confirms. + mMultiSimDialog = false; + super.performClick(preferenceScreen); + } else { + // Don't show any dialog. + setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */); + } + } else { + if (isMultiSim && !isMultipleDataOnCapable && !isDefaultDataSubscription) { + // enabling data and setting to default; show confirmation dialog which eventually + // calls setMobileDataEnabled() once user confirms. + mMultiSimDialog = true; + super.performClick(preferenceScreen); + } else { + // Don't show any dialog. + setMobileDataEnabled(true /* enabled */, false /* disableOtherSubscriptions */); + } + } + } + + private void setMobileDataEnabled(boolean enabled, boolean disableOtherSubscriptions) { + if (DBG) Log.d(TAG, "setMobileDataEnabled(" + enabled + "," + mSubId + ")"); + + MetricsLogger.action(getContext(), MetricsEvent.ACTION_MOBILE_NETWORK_MOBILE_DATA_TOGGLE, + enabled); + + mTelephonyManager.setDataEnabled(mSubId, enabled); + + if (disableOtherSubscriptions) { + disableDataForOtherSubscriptions(mSubId); + } + + setChecked(enabled); + } + + private void setChecked(boolean checked) { + if (mChecked == checked) return; + mChecked = checked; + notifyChanged(); + } + + @Override + protected void onBindView(View view) { + super.onBindView(view); + View checkableView = view.findViewById(com.android.internal.R.id.switch_widget); + checkableView.setClickable(false); + ((Checkable) checkableView).setChecked(mChecked); + } + + @Override + protected void onPrepareDialogBuilder(AlertDialog.Builder builder) { + if (mMultiSimDialog) { + showMultiSimDialog(builder); + } else { + showDisableDialog(builder); + } + } + + private void showDisableDialog(AlertDialog.Builder builder) { + builder.setTitle(null) + .setMessage(R.string.data_usage_disable_mobile) + .setPositiveButton(android.R.string.ok, this) + .setNegativeButton(android.R.string.cancel, null); + } + + private void showMultiSimDialog(AlertDialog.Builder builder) { + final SubscriptionInfo currentSir = mSubscriptionManager.getActiveSubscriptionInfo(mSubId); + final SubscriptionInfo nextSir = mSubscriptionManager.getDefaultDataSubscriptionInfo(); + + final String previousName = (nextSir == null) + ? getContext().getResources().getString(R.string.sim_selection_required_pref) + : nextSir.getDisplayName().toString(); + + builder.setTitle(R.string.sim_change_data_title); + builder.setMessage(getContext().getString(R.string.sim_change_data_message, + String.valueOf(currentSir != null ? currentSir.getDisplayName() : null), + previousName)); + + builder.setPositiveButton(R.string.ok, this); + builder.setNegativeButton(R.string.cancel, null); + } + + private void disableDataForOtherSubscriptions(int subId) { + List subInfoList = mSubscriptionManager.getActiveSubscriptionInfoList(); + if (subInfoList != null) { + for (SubscriptionInfo subInfo : subInfoList) { + if (subInfo.getSubscriptionId() != subId) { + mTelephonyManager.setDataEnabled(subInfo.getSubscriptionId(), false); + } + } + } + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (which != DialogInterface.BUTTON_POSITIVE) { + return; + } + if (mMultiSimDialog) { + mSubscriptionManager.setDefaultDataSubId(mSubId); + setMobileDataEnabled(true /* enabled */, true /* disableOtherSubscriptions */); + } else { + // TODO: extend to modify policy enabled flag. + setMobileDataEnabled(false /* enabled */, false /* disableOtherSubscriptions */); + } + } + + private final DataStateListener mListener = new DataStateListener() { + @Override + public void onChange(boolean selfChange) { + updateChecked(); + } + }; + + /** + * Listener that listens mobile data state change. + */ + public abstract static class DataStateListener extends ContentObserver { + public DataStateListener() { + super(new Handler(Looper.getMainLooper())); + } + + /** + * Set / Unset data state listening, specifying subId. + */ + public void setListener(boolean listening, int subId, Context context) { + if (listening) { + Uri uri = Global.getUriFor(Global.MOBILE_DATA); + if (TelephonyManager.getDefault().getSimCount() != 1) { + uri = Global.getUriFor(Global.MOBILE_DATA + subId); + } + context.getContentResolver().registerContentObserver(uri, false, this); + } else { + context.getContentResolver().unregisterContentObserver(this); + } + } + } + + /** + * Class that represents state of mobile data state. + * Used by onSaveInstanceState and onRestoreInstanceState. + */ + public static class CellDataState extends BaseSavedState { + public int mSubId; + public boolean mChecked; + public boolean mMultiSimDialog; + + public CellDataState(Parcelable base) { + super(base); + } + + public CellDataState(Parcel source) { + super(source); + mChecked = source.readByte() != 0; + mMultiSimDialog = source.readByte() != 0; + mSubId = source.readInt(); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeByte((byte) (mChecked ? 1 : 0)); + dest.writeByte((byte) (mMultiSimDialog ? 1 : 0)); + dest.writeInt(mSubId); + } + + public static final Creator CREATOR = new Creator() { + @Override + public CellDataState createFromParcel(Parcel source) { + return new CellDataState(source); + } + + @Override + public CellDataState[] newArray(int size) { + return new CellDataState[size]; + } + }; + } +} diff --git a/src/com/android/settings/mobilenetwork/MobileNetworkFragment.java b/src/com/android/settings/mobilenetwork/MobileNetworkFragment.java new file mode 100644 index 00000000000..03e7d793ec1 --- /dev/null +++ b/src/com/android/settings/mobilenetwork/MobileNetworkFragment.java @@ -0,0 +1,2003 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.mobilenetwork; + +import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI; + +import android.app.ActionBar; +import android.app.Activity; +import android.app.DialogFragment; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.database.ContentObserver; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.os.PersistableBundle; +import android.os.UserHandle; +import android.os.UserManager; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.preference.SwitchPreference; +import android.provider.Settings; +import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; +import android.telephony.CarrierConfigManager; +import android.telephony.PhoneStateListener; +import android.telephony.ServiceState; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.telephony.euicc.EuiccManager; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TabHost; + +import com.android.ims.ImsConfig; +import com.android.ims.ImsManager; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneConstants; +import com.android.internal.telephony.TelephonyIntents; +import com.android.phone.AdvancedOptionsPreference; +import com.android.phone.CdmaOptions; +import com.android.phone.CdmaSystemSelectListPreference; +import com.android.phone.DataUsagePreference; +import com.android.phone.EmergencyCallbackModeExitDialog; +import com.android.phone.GsmUmtsOptions; +import com.android.phone.MobileDataPreference; +import com.android.phone.MobileNetworkSettings; +import com.android.phone.NetworkOperators; +import com.android.phone.NetworkSelectListPreference; +import com.android.phone.RestrictedSwitchPreference; +import com.android.phone.RoamingDialogFragment; +import com.android.settingslib.RestrictedLockUtilsInternal; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public static class MobileNetworkFragment extends PreferenceFragment implements + Preference.OnPreferenceChangeListener, com.android.phone.RoamingDialogFragment.RoamingDialogListener { + + // debug data + private static final String LOG_TAG = "NetworkSettings"; + private static final boolean DBG = true; + public static final int REQUEST_CODE_EXIT_ECM = 17; + + // Number of active Subscriptions to show tabs + private static final int TAB_THRESHOLD = 2; + + // Number of last phone number digits shown in Euicc Setting tab + private static final int NUM_LAST_PHONE_DIGITS = 4; + + // fragment tag for roaming data dialog + private static final String ROAMING_TAG = "RoamingDialogFragment"; + + //String keys for preference lookup + private static final String BUTTON_PREFERED_NETWORK_MODE = "preferred_network_mode_key"; + private static final String BUTTON_ROAMING_KEY = "button_roaming_key"; + private static final String BUTTON_CDMA_LTE_DATA_SERVICE_KEY = "cdma_lte_data_service_key"; + private static final String BUTTON_ENABLED_NETWORKS_KEY = "enabled_networks_key"; + private static final String BUTTON_4G_LTE_KEY = "enhanced_4g_lte"; + private static final String BUTTON_CELL_BROADCAST_SETTINGS = "cell_broadcast_settings"; + private static final String BUTTON_CARRIER_SETTINGS_KEY = "carrier_settings_key"; + private static final String BUTTON_CDMA_SYSTEM_SELECT_KEY = "cdma_system_select_key"; + private static final String BUTTON_CDMA_SUBSCRIPTION_KEY = "cdma_subscription_key"; + private static final String BUTTON_CARRIER_SETTINGS_EUICC_KEY = + "carrier_settings_euicc_key"; + private static final String BUTTON_WIFI_CALLING_KEY = "wifi_calling_key"; + private static final String BUTTON_VIDEO_CALLING_KEY = "video_calling_key"; + private static final String BUTTON_MOBILE_DATA_ENABLE_KEY = "mobile_data_enable"; + private static final String BUTTON_DATA_USAGE_KEY = "data_usage_summary"; + private static final String BUTTON_ADVANCED_OPTIONS_KEY = "advanced_options"; + private static final String CATEGORY_CALLING_KEY = "calling"; + private static final String CATEGORY_GSM_APN_EXPAND_KEY = "category_gsm_apn_key"; + private static final String CATEGORY_CDMA_APN_EXPAND_KEY = "category_cdma_apn_key"; + private static final String BUTTON_GSM_APN_EXPAND_KEY = "button_gsm_apn_key"; + private static final String BUTTON_CDMA_APN_EXPAND_KEY = "button_cdma_apn_key"; + + private final BroadcastReceiver + mPhoneChangeReceiver = new MobileNetworkSettings.MobileNetworkFragment.PhoneChangeReceiver(); + private final ContentObserver + mDpcEnforcedContentObserver = new MobileNetworkSettings.MobileNetworkFragment.DpcApnEnforcedObserver(); + + static final int preferredNetworkMode = Phone.PREFERRED_NT_MODE; + + //Information about logical "up" Activity + private static final String UP_ACTIVITY_PACKAGE = "com.android.settings"; + private static final String UP_ACTIVITY_CLASS = + "com.android.settings.Settings$WirelessSettingsActivity"; + + //Information that needs to save into Bundle. + private static final String EXPAND_ADVANCED_FIELDS = "expand_advanced_fields"; + //Intent extra to indicate expand all fields. + private static final String EXPAND_EXTRA = "expandable"; + + private SubscriptionManager mSubscriptionManager; + private TelephonyManager mTelephonyManager; + private CarrierConfigManager mCarrierConfigManager; + private int mSubId; + + //UI objects + private com.android.phone.AdvancedOptionsPreference mAdvancedOptions; + private ListPreference mButtonPreferredNetworkMode; + private ListPreference mButtonEnabledNetworks; + private RestrictedSwitchPreference mButtonDataRoam; + private SwitchPreference mButton4glte; + private Preference mLteDataServicePref; + private Preference mEuiccSettingsPref; + private PreferenceCategory mCallingCategory; + private Preference mWiFiCallingPref; + private SwitchPreference mVideoCallingPref; + private NetworkSelectListPreference mButtonNetworkSelect; + private com.android.phone.MobileDataPreference mMobileDataPref; + private com.android.phone.DataUsagePreference mDataUsagePref; + + private static final String iface = "rmnet0"; //TODO: this will go away + private List mActiveSubInfos; + + private UserManager mUm; + private ImsManager mImsMgr; + private MobileNetworkSettings.MobileNetworkFragment.MyHandler mHandler; + private boolean mOkClicked; + private boolean mExpandAdvancedFields; + + // We assume the the value returned by mTabHost.getCurrentTab() == slotId + private TabHost mTabHost; + + //GsmUmts options and Cdma options + com.android.phone.GsmUmtsOptions mGsmUmtsOptions; + com.android.phone.CdmaOptions mCdmaOptions; + + private Preference mClickedPreference; + private boolean mShow4GForLTE; + private boolean mIsGlobalCdma; + private boolean mOnlyAutoSelectInHomeNW; + private boolean mUnavailable; + + private class PhoneCallStateListener extends PhoneStateListener { + /* + * Enable/disable the 'Enhanced 4G LTE Mode' when in/out of a call + * and depending on TTY mode and TTY support over VoLTE. + * @see android.telephony.PhoneStateListener#onCallStateChanged(int, + * java.lang.String) + */ + @Override + public void onCallStateChanged(int state, String incomingNumber) { + if (DBG) log("PhoneStateListener.onCallStateChanged: state=" + state); + + updateEnhanced4gLteState(); + updateWiFiCallState(); + updateVideoCallState(); + updatePreferredNetworkType(); + } + + /** + * Listen to different subId if it's changed. + */ + protected void updateSubscriptionId(Integer subId) { + if (subId.equals(MobileNetworkSettings.MobileNetworkFragment.PhoneCallStateListener.this.mSubId)) { + return; + } + + MobileNetworkSettings.MobileNetworkFragment.PhoneCallStateListener.this.mSubId = subId; + + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE); + + // Now, listen to new subId if it's valid. + if (SubscriptionManager.isValidSubscriptionId(subId)) { + mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE); + } + } + } + + private final MobileNetworkSettings.MobileNetworkFragment.PhoneCallStateListener + mPhoneStateListener = new MobileNetworkSettings.MobileNetworkFragment.PhoneCallStateListener(); + + @Override + public void onPositiveButtonClick(DialogFragment dialog) { + mTelephonyManager.setDataRoamingEnabled(true); + mButtonDataRoam.setChecked(true); + MetricsLogger.action(getContext(), + getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam), + true); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + if (getListView() != null) { + getListView().setDivider(null); + } + } + + public void onIntentUpdate(Intent intent) { + if (!mUnavailable) { + updateCurrentTab(intent); + } + } + + /** + * Invoked on each preference click in this hierarchy, overrides + * PreferenceActivity's implementation. Used to make sure we track the + * preference click events. + */ + @Override + public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, + Preference preference) { + sendMetricsEventPreferenceClicked(preferenceScreen, preference); + + /** TODO: Refactor and get rid of the if's using subclasses */ + if (preference.getKey().equals(BUTTON_4G_LTE_KEY)) { + return true; + } else if (mGsmUmtsOptions != null && + mGsmUmtsOptions.preferenceTreeClick(preference) == true) { + return true; + } else if (mCdmaOptions != null && + mCdmaOptions.preferenceTreeClick(preference) == true) { + if (mTelephonyManager.getEmergencyCallbackMode()) { + + mClickedPreference = preference; + + // In ECM mode launch ECM app dialog + startActivityForResult( + new Intent(TelephonyIntents.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null), + REQUEST_CODE_EXIT_ECM); + } + return true; + } else if (preference == mButtonPreferredNetworkMode) { + //displays the value taken from the Settings.System + int settingsNetworkMode = android.provider.Settings.Global.getInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + mSubId, + preferredNetworkMode); + mButtonPreferredNetworkMode.setValue(Integer.toString(settingsNetworkMode)); + return true; + } else if (preference == mLteDataServicePref) { + String tmpl = android.provider.Settings.Global.getString( + getActivity().getContentResolver(), + android.provider.Settings.Global.SETUP_PREPAID_DATA_SERVICE_URL); + if (!TextUtils.isEmpty(tmpl)) { + String imsi = mTelephonyManager.getSubscriberId(); + if (imsi == null) { + imsi = ""; + } + final String url = TextUtils.isEmpty(tmpl) ? null + : TextUtils.expandTemplate(tmpl, imsi).toString(); + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + startActivity(intent); + } else { + android.util.Log.e(LOG_TAG, "Missing SETUP_PREPAID_DATA_SERVICE_URL"); + } + return true; + } else if (preference == mButtonEnabledNetworks) { + int settingsNetworkMode = android.provider.Settings.Global.getInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + mSubId, + preferredNetworkMode); + mButtonEnabledNetworks.setValue(Integer.toString(settingsNetworkMode)); + return true; + } else if (preference == mButtonDataRoam) { + // Do not disable the preference screen if the user clicks Data roaming. + return true; + } else if (preference == mEuiccSettingsPref) { + Intent intent = new Intent(EuiccManager.ACTION_MANAGE_EMBEDDED_SUBSCRIPTIONS); + startActivity(intent); + return true; + } else if (preference == mWiFiCallingPref || preference == mVideoCallingPref + || preference == mMobileDataPref || preference == mDataUsagePref) { + return false; + } else if (preference == mAdvancedOptions) { + mExpandAdvancedFields = true; + updateBody(); + return true; + } else { + // if the button is anything but the simple toggle preference, + // we'll need to disable all preferences to reject all click + // events until the sub-activity's UI comes up. + preferenceScreen.setEnabled(false); + // Let the intents be launched by the Preference manager + return false; + } + } + + private final SubscriptionManager.OnSubscriptionsChangedListener + mOnSubscriptionsChangeListener + = new SubscriptionManager.OnSubscriptionsChangedListener() { + @Override + public void onSubscriptionsChanged() { + if (DBG) log("onSubscriptionsChanged:"); + initializeSubscriptions(); + } + }; + + private int getSlotIdFromIntent(Intent intent) { + Bundle data = intent.getExtras(); + int subId = -1; + if (data != null) { + subId = data.getInt(Settings.EXTRA_SUB_ID, -1); + } + return SubscriptionManager.getSlotIndex(subId); + } + + private void initializeSubscriptions() { + final Activity activity = getActivity(); + if (activity == null || activity.isDestroyed()) { + // Process preferences in activity only if its not destroyed + return; + } + int currentTab = 0; + if (DBG) log("initializeSubscriptions:+"); + + // Before updating the the active subscription list check + // if tab updating is needed as the list is changing. + List sil = mSubscriptionManager.getActiveSubscriptionInfoList(); + MobileNetworkSettings.TabState state = isUpdateTabsNeeded(sil); + + // Update to the active subscription list + mActiveSubInfos.clear(); + if (sil != null) { + mActiveSubInfos.addAll(sil); + // If there is only 1 sim then currenTab should represent slot no. of the sim. + if (sil.size() == 1) { + currentTab = sil.get(0).getSimSlotIndex(); + } + } + + switch (state) { + case UPDATE: { + if (DBG) log("initializeSubscriptions: UPDATE"); + currentTab = mTabHost != null ? mTabHost.getCurrentTab() : 0; + + mTabHost = (TabHost) getActivity().findViewById(android.R.id.tabhost); + mTabHost.setup(); + + // Update the tabName. Since the mActiveSubInfos are in slot order + // we can iterate though the tabs and subscription info in one loop. But + // we need to handle the case where a slot may be empty. + + Iterator siIterator = mActiveSubInfos.listIterator(); + SubscriptionInfo si = siIterator.hasNext() ? siIterator.next() : null; + for (int simSlotIndex = 0; simSlotIndex < mActiveSubInfos.size(); + simSlotIndex++) { + String tabName; + if (si != null && si.getSimSlotIndex() == simSlotIndex) { + // Slot is not empty and we match + tabName = String.valueOf(si.getDisplayName()); + si = siIterator.hasNext() ? siIterator.next() : null; + } else { + // Slot is empty, set name to unknown + tabName = getResources().getString(R.string.unknown); + } + if (DBG) { + log("initializeSubscriptions:tab=" + simSlotIndex + " name=" + tabName); + } + + mTabHost.addTab(buildTabSpec(String.valueOf(simSlotIndex), tabName)); + } + + mTabHost.setOnTabChangedListener(mTabListener); + mTabHost.setCurrentTab(currentTab); + break; + } + case NO_TABS: { + if (DBG) log("initializeSubscriptions: NO_TABS"); + + if (mTabHost != null) { + mTabHost.clearAllTabs(); + mTabHost = null; + } + break; + } + case DO_NOTHING: { + if (DBG) log("initializeSubscriptions: DO_NOTHING"); + if (mTabHost != null) { + currentTab = mTabHost.getCurrentTab(); + } + break; + } + } + updatePhone(currentTab); + updateBody(); + if (DBG) log("initializeSubscriptions:-"); + } + + private MobileNetworkSettings.TabState isUpdateTabsNeeded(List newSil) { + MobileNetworkSettings.TabState state = MobileNetworkSettings.TabState.DO_NOTHING; + if (newSil == null) { + if (mActiveSubInfos.size() >= TAB_THRESHOLD) { + if (DBG) log("isUpdateTabsNeeded: NO_TABS, size unknown and was tabbed"); + state = MobileNetworkSettings.TabState.NO_TABS; + } + } else if (newSil.size() < TAB_THRESHOLD && mActiveSubInfos.size() >= TAB_THRESHOLD) { + if (DBG) log("isUpdateTabsNeeded: NO_TABS, size went to small"); + state = MobileNetworkSettings.TabState.NO_TABS; + } else if (newSil.size() >= TAB_THRESHOLD && mActiveSubInfos.size() < TAB_THRESHOLD) { + if (DBG) log("isUpdateTabsNeeded: UPDATE, size changed"); + state = MobileNetworkSettings.TabState.UPDATE; + } else if (newSil.size() >= TAB_THRESHOLD) { + Iterator siIterator = mActiveSubInfos.iterator(); + for(SubscriptionInfo newSi : newSil) { + SubscriptionInfo curSi = siIterator.next(); + if (!newSi.getDisplayName().equals(curSi.getDisplayName())) { + if (DBG) log("isUpdateTabsNeeded: UPDATE, new name=" + + newSi.getDisplayName()); + state = MobileNetworkSettings.TabState.UPDATE; + break; + } + } + } + if (DBG) { + Log.i(LOG_TAG, "isUpdateTabsNeeded:- " + state + + " newSil.size()=" + ((newSil != null) ? newSil.size() : 0) + + " mActiveSubInfos.size()=" + mActiveSubInfos.size()); + } + return state; + } + + private TabHost.OnTabChangeListener mTabListener = new TabHost.OnTabChangeListener() { + @Override + public void onTabChanged(String tabId) { + if (DBG) log("onTabChanged:"); + // The User has changed tab; update the body. + updatePhone(Integer.parseInt(tabId)); + updateBody(); + } + }; + + private void updatePhone(int slotId) { + final SubscriptionInfo sir = mSubscriptionManager + .getActiveSubscriptionInfoForSimSlotIndex(slotId); + + if (sir != null) { + mSubId = sir.getSubscriptionId(); + + Log.i(LOG_TAG, "updatePhone:- slotId=" + slotId + " sir=" + sir); + + mImsMgr = ImsManager.getInstance(getContext(), + SubscriptionManager.getPhoneId(mSubId)); + mTelephonyManager = new TelephonyManager(getContext(), mSubId); + if (mImsMgr == null) { + log("updatePhone :: Could not get ImsManager instance!"); + } else if (DBG) { + log("updatePhone :: mImsMgr=" + mImsMgr); + } + } else { + // There is no active subscription in the given slot. + mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + } + + mPhoneStateListener.updateSubscriptionId(mSubId); + } + + private TabHost.TabContentFactory mEmptyTabContent = new TabHost.TabContentFactory() { + @Override + public View createTabContent(String tag) { + return new View(mTabHost.getContext()); + } + }; + + private TabHost.TabSpec buildTabSpec(String tag, String title) { + return mTabHost.newTabSpec(tag).setIndicator(title).setContent( + mEmptyTabContent); + } + + private void updateCurrentTab(Intent intent) { + int slotId = getSlotIdFromIntent(intent); + if (slotId >= 0 && mTabHost != null && mTabHost.getCurrentTab() != slotId) { + mTabHost.setCurrentTab(slotId); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + // If advanced fields are already expanded, we save it and expand it + // when it's re-created. + outState.putBoolean(EXPAND_ADVANCED_FIELDS, mExpandAdvancedFields); + } + + @Override + public void onCreate(Bundle icicle) { + Log.i(LOG_TAG, "onCreate:+"); + super.onCreate(icicle); + + final Activity activity = getActivity(); + if (activity == null || activity.isDestroyed()) { + Log.e(LOG_TAG, "onCreate:- with no valid activity."); + return; + } + + mHandler = new MobileNetworkSettings.MobileNetworkFragment.MyHandler(); + mUm = (UserManager) activity.getSystemService(Context.USER_SERVICE); + mSubscriptionManager = SubscriptionManager.from(activity); + mTelephonyManager = (TelephonyManager) activity.getSystemService( + Context.TELEPHONY_SERVICE); + mCarrierConfigManager = new CarrierConfigManager(getContext()); + + if (icicle != null) { + mExpandAdvancedFields = icicle.getBoolean(EXPAND_ADVANCED_FIELDS, false); + } else if (getActivity().getIntent().getBooleanExtra(EXPAND_EXTRA, false)) { + mExpandAdvancedFields = true; + } + + addPreferencesFromResource(R.xml.network_setting_fragment); + + mButton4glte = (SwitchPreference)findPreference(BUTTON_4G_LTE_KEY); + mButton4glte.setOnPreferenceChangeListener(this); + + mCallingCategory = (PreferenceCategory) findPreference(CATEGORY_CALLING_KEY); + mWiFiCallingPref = findPreference(BUTTON_WIFI_CALLING_KEY); + mVideoCallingPref = (SwitchPreference) findPreference(BUTTON_VIDEO_CALLING_KEY); + mMobileDataPref = (MobileDataPreference) findPreference(BUTTON_MOBILE_DATA_ENABLE_KEY); + mDataUsagePref = (DataUsagePreference) findPreference(BUTTON_DATA_USAGE_KEY); + + try { + Context con = activity.createPackageContext("com.android.systemui", 0); + int id = con.getResources().getIdentifier("config_show4GForLTE", + "bool", "com.android.systemui"); + mShow4GForLTE = con.getResources().getBoolean(id); + } catch (PackageManager.NameNotFoundException e) { + Log.e(LOG_TAG, "NameNotFoundException for show4GFotLTE"); + mShow4GForLTE = false; + } + + //get UI object references + PreferenceScreen prefSet = getPreferenceScreen(); + + mButtonDataRoam = (RestrictedSwitchPreference) prefSet.findPreference( + BUTTON_ROAMING_KEY); + mButtonPreferredNetworkMode = (ListPreference) prefSet.findPreference( + BUTTON_PREFERED_NETWORK_MODE); + mButtonEnabledNetworks = (ListPreference) prefSet.findPreference( + BUTTON_ENABLED_NETWORKS_KEY); + mAdvancedOptions = (AdvancedOptionsPreference) prefSet.findPreference( + BUTTON_ADVANCED_OPTIONS_KEY); + mButtonDataRoam.setOnPreferenceChangeListener(this); + + mLteDataServicePref = prefSet.findPreference(BUTTON_CDMA_LTE_DATA_SERVICE_KEY); + + mEuiccSettingsPref = prefSet.findPreference(BUTTON_CARRIER_SETTINGS_EUICC_KEY); + mEuiccSettingsPref.setOnPreferenceChangeListener(this); + + // Initialize mActiveSubInfo + int max = mSubscriptionManager.getActiveSubscriptionInfoCountMax(); + mActiveSubInfos = new ArrayList(max); + + int currentTab = mTabHost != null ? mTabHost.getCurrentTab() : 0; + updatePhone(currentTab); + if (hasActiveSubscriptions()) { + updateEnabledNetworksEntries(); + } + Log.i(LOG_TAG, "onCreate:-"); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(com.android.internal.R.layout.common_tab_settings, + container, false); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS) + || !mUm.isSystemUser()) { + mUnavailable = true; + getActivity().setContentView(R.layout.telephony_disallowed_preference_screen); + } else { + initializeSubscriptions(); + updateCurrentTab(getActivity().getIntent()); + } + } + + private class PhoneChangeReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + Log.i(LOG_TAG, "onReceive:"); + if (getActivity() == null || getContext() == null) { + // Received broadcast and activity is in the process of being torn down. + return; + } + // When the radio changes (ex: CDMA->GSM), refresh all options. + updateBody(); + } + } + + private class DpcApnEnforcedObserver extends ContentObserver { + DpcApnEnforcedObserver() { + super(null); + } + + @Override + public void onChange(boolean selfChange) { + Log.i(LOG_TAG, "DPC enforced onChange:"); + if (getActivity() == null || getContext() == null) { + // Received content change and activity is in the process of being torn down. + return; + } + updateBody(); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mMobileDataPref != null) { + mMobileDataPref.dispose(); + } + } + + @Override + public void onResume() { + super.onResume(); + Log.i(LOG_TAG, "onResume:+"); + + if (mUnavailable) { + Log.i(LOG_TAG, "onResume:- ignore mUnavailable == false"); + return; + } + + // upon resumption from the sub-activity, make sure we re-enable the + // preferences. + getPreferenceScreen().setEnabled(true); + + // Set UI state in onResume because a user could go home, launch some + // app to change this setting's backend, and re-launch this settings app + // and the UI state would be inconsistent with actual state + mButtonDataRoam.setChecked(mTelephonyManager.isDataRoamingEnabled()); + + if (getPreferenceScreen().findPreference(BUTTON_PREFERED_NETWORK_MODE) != null + || getPreferenceScreen().findPreference(BUTTON_ENABLED_NETWORKS_KEY) != null) { + updatePreferredNetworkUIFromDb(); + } + + mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + + // NOTE: Buttons will be enabled/disabled in mPhoneStateListener + updateEnhanced4gLteState(); + + // Video calling and WiFi calling state might have changed. + updateCallingCategory(); + + mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); + + final Context context = getActivity(); + IntentFilter intentFilter = new IntentFilter( + TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); + context.registerReceiver(mPhoneChangeReceiver, intentFilter); + context.getContentResolver().registerContentObserver(ENFORCE_MANAGED_URI, false, + mDpcEnforcedContentObserver); + + Log.i(LOG_TAG, "onResume:-"); + + } + + private boolean hasActiveSubscriptions() { + return mActiveSubInfos.size() > 0; + } + + private void updateBodyBasicFields(Activity activity, PreferenceScreen prefSet, + int phoneSubId, boolean hasActiveSubscriptions) { + Context context = activity.getApplicationContext(); + + ActionBar actionBar = activity.getActionBar(); + if (actionBar != null) { + // android.R.id.home will be triggered in onOptionsItemSelected() + actionBar.setDisplayHomeAsUpEnabled(true); + } + + prefSet.addPreference(mMobileDataPref); + prefSet.addPreference(mButtonDataRoam); + prefSet.addPreference(mDataUsagePref); + + mMobileDataPref.setEnabled(hasActiveSubscriptions); + mButtonDataRoam.setEnabled(hasActiveSubscriptions); + mDataUsagePref.setEnabled(hasActiveSubscriptions); + + if (hasActiveSubscriptions) { + // Customized preferences needs to be initialized with subId. + mMobileDataPref.initialize(phoneSubId); + mDataUsagePref.initialize(phoneSubId); + + // Initialize states of mButtonDataRoam. + mButtonDataRoam.setChecked(mTelephonyManager.isDataRoamingEnabled()); + mButtonDataRoam.setDisabledByAdmin(false); + if (mButtonDataRoam.isEnabled()) { + if (RestrictedLockUtilsInternal.hasBaseUserRestriction(context, + UserManager.DISALLOW_DATA_ROAMING, UserHandle.myUserId())) { + mButtonDataRoam.setEnabled(false); + } else { + mButtonDataRoam.checkRestrictionAndSetDisabled( + UserManager.DISALLOW_DATA_ROAMING); + } + } + } + } + + private void updateBody() { + final Activity activity = getActivity(); + final PreferenceScreen prefSet = getPreferenceScreen(); + final boolean hasActiveSubscriptions = hasActiveSubscriptions(); + + if (activity == null || activity.isDestroyed()) { + Log.e(LOG_TAG, "updateBody with no valid activity."); + return; + } + + if (prefSet == null) { + Log.e(LOG_TAG, "updateBody with no null prefSet."); + return; + } + + prefSet.removeAll(); + + updateBodyBasicFields(activity, prefSet, mSubId, hasActiveSubscriptions); + + if (hasActiveSubscriptions) { + if (mExpandAdvancedFields) { + updateBodyAdvancedFields(activity, prefSet, mSubId, hasActiveSubscriptions); + } else { + prefSet.addPreference(mAdvancedOptions); + } + } else { + // Shows the "Carrier" preference that allows user to add a e-sim profile. + if (showEuiccSettings(getContext())) { + mEuiccSettingsPref.setSummary(null /* summary */); + prefSet.addPreference(mEuiccSettingsPref); + } + } + } + + private void updateBodyAdvancedFields(Activity activity, PreferenceScreen prefSet, + int phoneSubId, boolean hasActiveSubscriptions) { + boolean isLteOnCdma = mTelephonyManager.getLteOnCdmaMode() + == PhoneConstants.LTE_ON_CDMA_TRUE; + + if (DBG) { + log("updateBody: isLteOnCdma=" + isLteOnCdma + " phoneSubId=" + phoneSubId); + } + + prefSet.addPreference(mButtonPreferredNetworkMode); + prefSet.addPreference(mButtonEnabledNetworks); + prefSet.addPreference(mButton4glte); + + if (showEuiccSettings(getActivity())) { + prefSet.addPreference(mEuiccSettingsPref); + String spn = mTelephonyManager.getSimOperatorName(); + if (TextUtils.isEmpty(spn)) { + mEuiccSettingsPref.setSummary(null); + } else { + mEuiccSettingsPref.setSummary(spn); + } + } + + int settingsNetworkMode = android.provider.Settings.Global.getInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId, + preferredNetworkMode); + + PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); + mIsGlobalCdma = isLteOnCdma + && carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_CDMA_CHOICES_BOOL); + if (carrierConfig.getBoolean( + CarrierConfigManager.KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL)) { + prefSet.removePreference(mButtonPreferredNetworkMode); + prefSet.removePreference(mButtonEnabledNetworks); + prefSet.removePreference(mLteDataServicePref); + } else if (carrierConfig.getBoolean(CarrierConfigManager + .KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL) + && !mTelephonyManager.getServiceState().getRoaming() + && mTelephonyManager.getServiceState().getDataRegState() + == ServiceState.STATE_IN_SERVICE) { + prefSet.removePreference(mButtonPreferredNetworkMode); + prefSet.removePreference(mButtonEnabledNetworks); + + final int phoneType = mTelephonyManager.getPhoneType(); + if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { + updateCdmaOptions(this, prefSet, mSubId); + } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) { + updateGsmUmtsOptions(this, prefSet, phoneSubId); + } else { + throw new IllegalStateException("Unexpected phone type: " + phoneType); + } + // Since pref is being hidden from user, set network mode to default + // in case it is currently something else. That is possible if user + // changed the setting while roaming and is now back to home network. + settingsNetworkMode = preferredNetworkMode; + } else if (carrierConfig.getBoolean( + CarrierConfigManager.KEY_WORLD_PHONE_BOOL) == true) { + prefSet.removePreference(mButtonEnabledNetworks); + // set the listener for the mButtonPreferredNetworkMode list preference so we can issue + // change Preferred Network Mode. + mButtonPreferredNetworkMode.setOnPreferenceChangeListener(this); + + updateCdmaOptions(this, prefSet, mSubId); + updateGsmUmtsOptions(this, prefSet, phoneSubId); + } else { + prefSet.removePreference(mButtonPreferredNetworkMode); + updateEnabledNetworksEntries(); + mButtonEnabledNetworks.setOnPreferenceChangeListener(this); + if (DBG) log("settingsNetworkMode: " + settingsNetworkMode); + } + + final boolean missingDataServiceUrl = TextUtils.isEmpty( + android.provider.Settings.Global.getString(activity.getContentResolver(), + android.provider.Settings.Global.SETUP_PREPAID_DATA_SERVICE_URL)); + if (!isLteOnCdma || missingDataServiceUrl) { + prefSet.removePreference(mLteDataServicePref); + } else { + android.util.Log.d(LOG_TAG, "keep ltePref"); + } + + updateEnhanced4gLteState(); + updatePreferredNetworkType(); + updateCallingCategory(); + + // Enable link to CMAS app settings depending on the value in config.xml. + final boolean isCellBroadcastAppLinkEnabled = activity.getResources().getBoolean( + com.android.internal.R.bool.config_cellBroadcastAppLinks); + if (!mUm.isAdminUser() || !isCellBroadcastAppLinkEnabled + || mUm.hasUserRestriction(UserManager.DISALLOW_CONFIG_CELL_BROADCASTS)) { + PreferenceScreen root = getPreferenceScreen(); + Preference ps = findPreference(BUTTON_CELL_BROADCAST_SETTINGS); + if (ps != null) { + root.removePreference(ps); + } + } + + /** + * Listen to extra preference changes that need as Metrics events logging. + */ + if (prefSet.findPreference(BUTTON_CDMA_SYSTEM_SELECT_KEY) != null) { + prefSet.findPreference(BUTTON_CDMA_SYSTEM_SELECT_KEY) + .setOnPreferenceChangeListener(this); + } + + if (prefSet.findPreference(BUTTON_CDMA_SUBSCRIPTION_KEY) != null) { + prefSet.findPreference(BUTTON_CDMA_SUBSCRIPTION_KEY) + .setOnPreferenceChangeListener(this); + } + + // Get the networkMode from Settings.System and displays it + mButtonEnabledNetworks.setValue(Integer.toString(settingsNetworkMode)); + mButtonPreferredNetworkMode.setValue(Integer.toString(settingsNetworkMode)); + UpdatePreferredNetworkModeSummary(settingsNetworkMode); + UpdateEnabledNetworksValueAndSummary(settingsNetworkMode); + // Display preferred network type based on what modem returns b/18676277 + new MobileNetworkSettings.SetPreferredNetworkAsyncTask( + mTelephonyManager, + mSubId, + settingsNetworkMode, + mHandler.obtainMessage(MobileNetworkSettings.MobileNetworkFragment.MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE)) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + /** + * Enable/disable depending upon if there are any active subscriptions. + * + * I've decided to put this enable/disable code at the bottom as the + * code above works even when there are no active subscriptions, thus + * putting it afterwards is a smaller change. This can be refined later, + * but you do need to remember that this all needs to work when subscriptions + * change dynamically such as when hot swapping sims. + */ + boolean useVariant4glteTitle = carrierConfig.getBoolean( + CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL); + int enhanced4glteModeTitleId = useVariant4glteTitle ? + R.string.enhanced_4g_lte_mode_title_variant : + R.string.enhanced_4g_lte_mode_title; + + mOnlyAutoSelectInHomeNW = carrierConfig.getBoolean( + CarrierConfigManager.KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL); + mButton4glte.setTitle(enhanced4glteModeTitleId); + mLteDataServicePref.setEnabled(hasActiveSubscriptions); + Preference ps; + ps = findPreference(BUTTON_CELL_BROADCAST_SETTINGS); + if (ps != null) { + ps.setEnabled(hasActiveSubscriptions); + } + ps = findPreference(CATEGORY_GSM_APN_EXPAND_KEY); + if (ps != null) { + ps.setEnabled(hasActiveSubscriptions); + } + ps = findPreference(CATEGORY_CDMA_APN_EXPAND_KEY); + if (ps != null) { + ps.setEnabled(hasActiveSubscriptions); + } + ps = findPreference(com.android.phone.NetworkOperators.CATEGORY_NETWORK_OPERATORS_KEY); + if (ps != null) { + ps.setEnabled(hasActiveSubscriptions); + } + ps = findPreference(BUTTON_CARRIER_SETTINGS_KEY); + if (ps != null) { + ps.setEnabled(hasActiveSubscriptions); + } + ps = findPreference(BUTTON_CDMA_SYSTEM_SELECT_KEY); + if (ps != null) { + ps.setEnabled(hasActiveSubscriptions); + } + ps = findPreference(CATEGORY_CALLING_KEY); + if (ps != null) { + ps.setEnabled(hasActiveSubscriptions); + } + ps = findPreference(com.android.phone.NetworkOperators.BUTTON_AUTO_SELECT_KEY); + if (ps != null) { + ps.setSummary(null); + if (mTelephonyManager.getServiceState().getRoaming()) { + ps.setEnabled(true); + } else { + ps.setEnabled(!mOnlyAutoSelectInHomeNW); + if (mOnlyAutoSelectInHomeNW) { + ps.setSummary(getResources().getString( + R.string.manual_mode_disallowed_summary, + mTelephonyManager.getSimOperatorName())); + } + } + } + } + + // Requires that mSubId is up to date + void updateEnabledNetworksEntries() { + final int phoneType = mTelephonyManager.getPhoneType(); + final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); + if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { + final int lteForced = android.provider.Settings.Global.getInt( + getContext().getContentResolver(), + android.provider.Settings.Global.LTE_SERVICE_FORCED + mSubId, + 0); + final boolean isLteOnCdma = mTelephonyManager.getLteOnCdmaMode() + == PhoneConstants.LTE_ON_CDMA_TRUE; + final int settingsNetworkMode = android.provider.Settings.Global.getInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + mSubId, + preferredNetworkMode); + if (isLteOnCdma) { + if (lteForced == 0) { + mButtonEnabledNetworks.setEntries( + R.array.enabled_networks_cdma_choices); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_cdma_values); + } else { + switch (settingsNetworkMode) { + case TelephonyManager.NETWORK_MODE_CDMA_EVDO: + case TelephonyManager.NETWORK_MODE_CDMA_NO_EVDO: + case TelephonyManager.NETWORK_MODE_EVDO_NO_CDMA: + mButtonEnabledNetworks.setEntries( + R.array.enabled_networks_cdma_no_lte_choices); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_cdma_no_lte_values); + break; + case TelephonyManager.NETWORK_MODE_GLOBAL: + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO: + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_ONLY: + mButtonEnabledNetworks.setEntries( + R.array.enabled_networks_cdma_only_lte_choices); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_cdma_only_lte_values); + break; + default: + mButtonEnabledNetworks.setEntries( + R.array.enabled_networks_cdma_choices); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_cdma_values); + break; + } + } + } + updateCdmaOptions(this, getPreferenceScreen(), mSubId); + + } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) { + if (isSupportTdscdma()) { + mButtonEnabledNetworks.setEntries( + R.array.enabled_networks_tdscdma_choices); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_tdscdma_values); + } else if (!carrierConfig.getBoolean(CarrierConfigManager.KEY_PREFER_2G_BOOL) + && !getResources().getBoolean(R.bool.config_enabled_lte)) { + mButtonEnabledNetworks.setEntries( + R.array.enabled_networks_except_gsm_lte_choices); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_except_gsm_lte_values); + } else if (!carrierConfig.getBoolean(CarrierConfigManager.KEY_PREFER_2G_BOOL)) { + int select = mShow4GForLTE + ? R.array.enabled_networks_except_gsm_4g_choices + : R.array.enabled_networks_except_gsm_choices; + mButtonEnabledNetworks.setEntries(select); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_except_gsm_values); + } else if (!getResources().getBoolean(R.bool.config_enabled_lte)) { + mButtonEnabledNetworks.setEntries( + R.array.enabled_networks_except_lte_choices); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_except_lte_values); + } else if (mIsGlobalCdma) { + mButtonEnabledNetworks.setEntries( + R.array.enabled_networks_cdma_choices); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_cdma_values); + } else { + int select = mShow4GForLTE ? R.array.enabled_networks_4g_choices + : R.array.enabled_networks_choices; + mButtonEnabledNetworks.setEntries(select); + mButtonEnabledNetworks.setEntryValues( + R.array.enabled_networks_values); + } + updateGsmUmtsOptions(this, getPreferenceScreen(), mSubId); + } else { + throw new IllegalStateException("Unexpected phone type: " + phoneType); + } + if (isWorldMode()) { + mButtonEnabledNetworks.setEntries( + R.array.preferred_network_mode_choices_world_mode); + mButtonEnabledNetworks.setEntryValues( + R.array.preferred_network_mode_values_world_mode); + } + } + + @Override + public void onPause() { + super.onPause(); + if (DBG) log("onPause:+"); + + mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); + + mSubscriptionManager + .removeOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener); + + final Context context = getActivity(); + context.unregisterReceiver(mPhoneChangeReceiver); + context.getContentResolver().unregisterContentObserver(mDpcEnforcedContentObserver); + if (DBG) log("onPause:-"); + } + + /** + * Implemented to support onPreferenceChangeListener to look for preference + * changes specifically on CLIR. + * + * @param preference is the preference to be changed, should be mButtonCLIR. + * @param objValue should be the value of the selection, NOT its localized + * display value. + */ + public boolean onPreferenceChange(Preference preference, Object objValue) { + sendMetricsEventPreferenceChanged(getPreferenceScreen(), preference, objValue); + + final int phoneSubId = mSubId; + if (preference == mButtonPreferredNetworkMode) { + //NOTE onPreferenceChange seems to be called even if there is no change + //Check if the button value is changed from the System.Setting + mButtonPreferredNetworkMode.setValue((String) objValue); + int buttonNetworkMode; + buttonNetworkMode = Integer.parseInt((String) objValue); + int settingsNetworkMode = android.provider.Settings.Global.getInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId, + preferredNetworkMode); + if (buttonNetworkMode != settingsNetworkMode) { + int modemNetworkMode; + // if new mode is invalid ignore it + switch (buttonNetworkMode) { + case TelephonyManager.NETWORK_MODE_WCDMA_PREF: + case TelephonyManager.NETWORK_MODE_GSM_ONLY: + case TelephonyManager.NETWORK_MODE_WCDMA_ONLY: + case TelephonyManager.NETWORK_MODE_GSM_UMTS: + case TelephonyManager.NETWORK_MODE_CDMA_EVDO: + case TelephonyManager.NETWORK_MODE_CDMA_NO_EVDO: + case TelephonyManager.NETWORK_MODE_EVDO_NO_CDMA: + case TelephonyManager.NETWORK_MODE_GLOBAL: + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO: + case TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_ONLY: + case TelephonyManager.NETWORK_MODE_LTE_WCDMA: + case TelephonyManager.NETWORK_MODE_TDSCDMA_ONLY: + case TelephonyManager.NETWORK_MODE_TDSCDMA_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA: + case TelephonyManager.NETWORK_MODE_TDSCDMA_GSM: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM: + case TelephonyManager.NETWORK_MODE_TDSCDMA_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + // This is one of the modes we recognize + modemNetworkMode = buttonNetworkMode; + break; + default: + loge("Invalid Network Mode (" +buttonNetworkMode+ ") chosen. Ignore."); + return true; + } + + android.provider.Settings.Global.putInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId, + buttonNetworkMode ); + //Set the modem network mode + new MobileNetworkSettings.SetPreferredNetworkAsyncTask( + mTelephonyManager, + mSubId, + modemNetworkMode, + mHandler.obtainMessage(MobileNetworkSettings.MobileNetworkFragment.MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE)) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } else if (preference == mButtonEnabledNetworks) { + mButtonEnabledNetworks.setValue((String) objValue); + int buttonNetworkMode; + buttonNetworkMode = Integer.parseInt((String) objValue); + if (DBG) log("buttonNetworkMode: " + buttonNetworkMode); + int settingsNetworkMode = android.provider.Settings.Global.getInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId, + preferredNetworkMode); + if (buttonNetworkMode != settingsNetworkMode) { + int modemNetworkMode; + // if new mode is invalid ignore it + switch (buttonNetworkMode) { + case TelephonyManager.NETWORK_MODE_WCDMA_PREF: + case TelephonyManager.NETWORK_MODE_GSM_ONLY: + case TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_CDMA_EVDO: + case TelephonyManager.NETWORK_MODE_CDMA_NO_EVDO: + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO: + case TelephonyManager.NETWORK_MODE_TDSCDMA_ONLY: + case TelephonyManager.NETWORK_MODE_TDSCDMA_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA: + case TelephonyManager.NETWORK_MODE_TDSCDMA_GSM: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM: + case TelephonyManager.NETWORK_MODE_TDSCDMA_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + // This is one of the modes we recognize + modemNetworkMode = buttonNetworkMode; + break; + default: + loge("Invalid Network Mode (" +buttonNetworkMode+ ") chosen. Ignore."); + return true; + } + + UpdateEnabledNetworksValueAndSummary(buttonNetworkMode); + + android.provider.Settings.Global.putInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + phoneSubId, + buttonNetworkMode ); + //Set the modem network mode + new MobileNetworkSettings.SetPreferredNetworkAsyncTask( + mTelephonyManager, + mSubId, + modemNetworkMode, + mHandler.obtainMessage(MobileNetworkSettings.MobileNetworkFragment.MyHandler.MESSAGE_SET_PREFERRED_NETWORK_TYPE)) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } else if (preference == mButton4glte) { + boolean enhanced4gMode = !mButton4glte.isChecked(); + mButton4glte.setChecked(enhanced4gMode); + mImsMgr.setEnhanced4gLteModeSetting(mButton4glte.isChecked()); + } else if (preference == mButtonDataRoam) { + if (DBG) log("onPreferenceTreeClick: preference == mButtonDataRoam."); + + //normally called on the toggle click + if (!mButtonDataRoam.isChecked()) { + PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId( + mSubId); + if (carrierConfig != null && carrierConfig.getBoolean( + CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL)) { + mTelephonyManager.setDataRoamingEnabled(true); + MetricsLogger.action(getContext(), + getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam), + true); + } else { + // MetricsEvent with no value update. + MetricsLogger.action(getContext(), + getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam)); + // First confirm with a warning dialog about charges + mOkClicked = false; + com.android.phone.RoamingDialogFragment + fragment = new com.android.phone.RoamingDialogFragment(); + Bundle b = new Bundle(); + b.putInt(RoamingDialogFragment.SUB_ID_KEY, mSubId); + fragment.setArguments(b); + fragment.show(getFragmentManager(), ROAMING_TAG); + // Don't update the toggle unless the confirm button is actually pressed. + return false; + } + } else { + mTelephonyManager.setDataRoamingEnabled(false); + MetricsLogger.action(getContext(), + getMetricsEventCategory(getPreferenceScreen(), mButtonDataRoam), + false); + return true; + } + } else if (preference == mVideoCallingPref) { + // If mButton4glte is not checked, mVideoCallingPref should be disabled. + // So it only makes sense to call phoneMgr.enableVideoCalling if it's checked. + if (mButton4glte.isChecked()) { + mImsMgr.setVtSetting((boolean) objValue); + return true; + } else { + loge("mVideoCallingPref should be disabled if mButton4glte is not checked."); + mVideoCallingPref.setEnabled(false); + return false; + } + } else if (preference == getPreferenceScreen() + .findPreference(BUTTON_CDMA_SYSTEM_SELECT_KEY) + || preference == getPreferenceScreen() + .findPreference(BUTTON_CDMA_SUBSCRIPTION_KEY)) { + return true; + } + + updateBody(); + // always let the preference setting proceed. + return true; + } + + private boolean is4gLtePrefEnabled(PersistableBundle carrierConfig) { + return (mTelephonyManager.getCallState(mSubId) + == TelephonyManager.CALL_STATE_IDLE) + && mImsMgr != null + && mImsMgr.isNonTtyOrTtyOnVolteEnabled() + && carrierConfig.getBoolean( + CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL); + } + + private class MyHandler extends Handler { + + static final int MESSAGE_SET_PREFERRED_NETWORK_TYPE = 0; + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_SET_PREFERRED_NETWORK_TYPE: + handleSetPreferredNetworkTypeResponse(msg); + break; + } + } + + private void handleSetPreferredNetworkTypeResponse(Message msg) { + final Activity activity = getActivity(); + if (activity == null || activity.isDestroyed()) { + // Access preferences of activity only if it is not destroyed + // or if fragment is not attached to an activity. + return; + } + + boolean success = (boolean) msg.obj; + + if (success) { + int networkMode; + if (getPreferenceScreen().findPreference( + BUTTON_PREFERED_NETWORK_MODE) != null) { + networkMode = Integer.parseInt(mButtonPreferredNetworkMode.getValue()); + android.provider.Settings.Global.putInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + + mSubId, + networkMode ); + } + if (getPreferenceScreen().findPreference(BUTTON_ENABLED_NETWORKS_KEY) != null) { + networkMode = Integer.parseInt(mButtonEnabledNetworks.getValue()); + android.provider.Settings.Global.putInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + + mSubId, + networkMode ); + } + } else { + Log.i(LOG_TAG, "handleSetPreferredNetworkTypeResponse:" + + "exception in setting network mode."); + updatePreferredNetworkUIFromDb(); + } + } + } + + private void updatePreferredNetworkUIFromDb() { + int settingsNetworkMode = android.provider.Settings.Global.getInt( + getContext().getContentResolver(), + android.provider.Settings.Global.PREFERRED_NETWORK_MODE + mSubId, + preferredNetworkMode); + + if (DBG) { + log("updatePreferredNetworkUIFromDb: settingsNetworkMode = " + + settingsNetworkMode); + } + + UpdatePreferredNetworkModeSummary(settingsNetworkMode); + UpdateEnabledNetworksValueAndSummary(settingsNetworkMode); + // changes the mButtonPreferredNetworkMode accordingly to settingsNetworkMode + mButtonPreferredNetworkMode.setValue(Integer.toString(settingsNetworkMode)); + } + + private void UpdatePreferredNetworkModeSummary(int NetworkMode) { + switch(NetworkMode) { + case TelephonyManager.NETWORK_MODE_TDSCDMA_GSM_WCDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_tdscdma_gsm_wcdma_summary); + break; + case TelephonyManager.NETWORK_MODE_TDSCDMA_GSM: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_tdscdma_gsm_summary); + break; + case TelephonyManager.NETWORK_MODE_WCDMA_PREF: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_wcdma_perf_summary); + break; + case TelephonyManager.NETWORK_MODE_GSM_ONLY: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_gsm_only_summary); + break; + case TelephonyManager.NETWORK_MODE_TDSCDMA_WCDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_tdscdma_wcdma_summary); + break; + case TelephonyManager.NETWORK_MODE_WCDMA_ONLY: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_wcdma_only_summary); + break; + case TelephonyManager.NETWORK_MODE_GSM_UMTS: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_gsm_wcdma_summary); + break; + case TelephonyManager.NETWORK_MODE_CDMA_EVDO: + switch (mTelephonyManager.getLteOnCdmaMode()) { + case PhoneConstants.LTE_ON_CDMA_TRUE: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_cdma_summary); + break; + case PhoneConstants.LTE_ON_CDMA_FALSE: + default: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_cdma_evdo_summary); + break; + } + break; + case TelephonyManager.NETWORK_MODE_CDMA_NO_EVDO: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_cdma_only_summary); + break; + case TelephonyManager.NETWORK_MODE_EVDO_NO_CDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_evdo_only_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_tdscdma_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_ONLY: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_tdscdma_gsm_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_tdscdma_gsm_wcdma_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_gsm_wcdma_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_cdma_evdo_summary); + break; + case TelephonyManager.NETWORK_MODE_TDSCDMA_ONLY: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_tdscdma_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_tdscdma_cdma_evdo_gsm_wcdma_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA: + if (mTelephonyManager.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA + || mIsGlobalCdma + || isWorldMode()) { + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_global_summary); + } else { + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_summary); + } + break; + case TelephonyManager.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_tdscdma_cdma_evdo_gsm_wcdma_summary); + break; + case TelephonyManager.NETWORK_MODE_GLOBAL: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_cdma_evdo_gsm_wcdma_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_WCDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_tdscdma_wcdma_summary); + break; + case TelephonyManager.NETWORK_MODE_LTE_WCDMA: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_lte_wcdma_summary); + break; + default: + mButtonPreferredNetworkMode.setSummary( + R.string.preferred_network_mode_global_summary); + } + } + + private void UpdateEnabledNetworksValueAndSummary(int NetworkMode) { + switch (NetworkMode) { + case TelephonyManager.NETWORK_MODE_TDSCDMA_WCDMA: + case TelephonyManager.NETWORK_MODE_TDSCDMA_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_TDSCDMA_GSM: + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager.NETWORK_MODE_TDSCDMA_GSM_WCDMA)); + mButtonEnabledNetworks.setSummary(R.string.network_3G); + break; + case TelephonyManager.NETWORK_MODE_WCDMA_ONLY: + case TelephonyManager.NETWORK_MODE_GSM_UMTS: + case TelephonyManager.NETWORK_MODE_WCDMA_PREF: + if (!mIsGlobalCdma) { + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager.NETWORK_MODE_WCDMA_PREF)); + mButtonEnabledNetworks.setSummary(R.string.network_3G); + } else { + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager + .NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)); + mButtonEnabledNetworks.setSummary(R.string.network_global); + } + break; + case TelephonyManager.NETWORK_MODE_GSM_ONLY: + if (!mIsGlobalCdma) { + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager.NETWORK_MODE_GSM_ONLY)); + mButtonEnabledNetworks.setSummary(R.string.network_2G); + } else { + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager + .NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)); + mButtonEnabledNetworks.setSummary(R.string.network_global); + } + break; + case TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA: + if (isWorldMode()) { + mButtonEnabledNetworks.setSummary( + R.string.preferred_network_mode_lte_gsm_umts_summary); + controlCdmaOptions(false); + controlGsmOptions(true); + break; + } + case TelephonyManager.NETWORK_MODE_LTE_ONLY: + case TelephonyManager.NETWORK_MODE_LTE_WCDMA: + if (!mIsGlobalCdma) { + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager.NETWORK_MODE_LTE_GSM_WCDMA)); + mButtonEnabledNetworks.setSummary((mShow4GForLTE == true) + ? R.string.network_4G : R.string.network_lte); + } else { + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager + .NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)); + mButtonEnabledNetworks.setSummary(R.string.network_global); + } + break; + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO: + if (isWorldMode()) { + mButtonEnabledNetworks.setSummary( + R.string.preferred_network_mode_lte_cdma_summary); + controlCdmaOptions(true); + controlGsmOptions(false); + } else { + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO)); + mButtonEnabledNetworks.setSummary(R.string.network_lte); + } + break; + case TelephonyManager.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager + .NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA)); + mButtonEnabledNetworks.setSummary(R.string.network_3G); + break; + case TelephonyManager.NETWORK_MODE_CDMA_EVDO: + case TelephonyManager.NETWORK_MODE_EVDO_NO_CDMA: + case TelephonyManager.NETWORK_MODE_GLOBAL: + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager.NETWORK_MODE_CDMA_EVDO)); + mButtonEnabledNetworks.setSummary(R.string.network_3G); + break; + case TelephonyManager.NETWORK_MODE_CDMA_NO_EVDO: + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager.NETWORK_MODE_CDMA_NO_EVDO)); + mButtonEnabledNetworks.setSummary(R.string.network_1x); + break; + case TelephonyManager.NETWORK_MODE_TDSCDMA_ONLY: + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager.NETWORK_MODE_TDSCDMA_ONLY)); + mButtonEnabledNetworks.setSummary(R.string.network_3G); + break; + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA: + case TelephonyManager.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA: + if (isSupportTdscdma()) { + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager + .NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA)); + mButtonEnabledNetworks.setSummary(R.string.network_lte); + } else { + if (isWorldMode()) { + controlCdmaOptions(true); + controlGsmOptions(false); + } + mButtonEnabledNetworks.setValue( + Integer.toString(TelephonyManager + .NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA)); + if (mTelephonyManager.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA + || mIsGlobalCdma + || isWorldMode()) { + mButtonEnabledNetworks.setSummary(R.string.network_global); + } else { + mButtonEnabledNetworks.setSummary((mShow4GForLTE == true) + ? R.string.network_4G : R.string.network_lte); + } + } + break; + default: + String errMsg = "Invalid Network Mode (" + NetworkMode + "). Ignore."; + loge(errMsg); + mButtonEnabledNetworks.setSummary(errMsg); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + switch(requestCode) { + case REQUEST_CODE_EXIT_ECM: + Boolean isChoiceYes = data.getBooleanExtra( + EmergencyCallbackModeExitDialog.EXTRA_EXIT_ECM_RESULT, false); + if (isChoiceYes) { + // If the phone exits from ECM mode, show the CDMA Options + mCdmaOptions.showDialog(mClickedPreference); + } else { + // do nothing + } + break; + + default: + break; + } + } + + private void updateWiFiCallState() { + if (mWiFiCallingPref == null || mCallingCategory == null) { + return; + } + + // Removes the preference if the wifi calling is disabled. + if (!isWifiCallingEnabled(getContext(), SubscriptionManager.getPhoneId(mSubId))) { + mCallingCategory.removePreference(mWiFiCallingPref); + return; + } + + final PhoneAccountHandle simCallManager = + TelecomManager.from(getContext()).getSimCallManager(); + + if (simCallManager != null) { + Intent intent = MobileNetworkSettings.buildPhoneAccountConfigureIntent( + getContext(), simCallManager); + PackageManager pm = getContext().getPackageManager(); + List resolutions = pm.queryIntentActivities(intent, 0); + mWiFiCallingPref.setTitle(resolutions.get(0).loadLabel(pm)); + mWiFiCallingPref.setSummary(null); + mWiFiCallingPref.setIntent(intent); + } else { + int resId = com.android.internal.R.string.wifi_calling_off_summary; + if (mImsMgr.isWfcEnabledByUser()) { + boolean isRoaming = mTelephonyManager.isNetworkRoaming(); + int wfcMode = mImsMgr.getWfcMode(isRoaming); + + switch (wfcMode) { + case ImsConfig.WfcModeFeatureValueConstants.WIFI_ONLY: + resId = com.android.internal.R.string.wfc_mode_wifi_only_summary; + break; + case ImsConfig.WfcModeFeatureValueConstants.CELLULAR_PREFERRED: + resId = com.android.internal.R.string + .wfc_mode_cellular_preferred_summary; + break; + case ImsConfig.WfcModeFeatureValueConstants.WIFI_PREFERRED: + resId = com.android.internal.R.string.wfc_mode_wifi_preferred_summary; + break; + default: + if (DBG) log("Unexpected WFC mode value: " + wfcMode); + } + } + mWiFiCallingPref.setSummary(resId); + } + + mCallingCategory.addPreference(mWiFiCallingPref); + mWiFiCallingPref.setEnabled(mTelephonyManager.getCallState(mSubId) + == TelephonyManager.CALL_STATE_IDLE && hasActiveSubscriptions()); + } + + private void updateEnhanced4gLteState() { + if (mButton4glte == null) { + return; + } + + PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); + + if ((mImsMgr == null + || !mImsMgr.isVolteEnabledByPlatform() + || !mImsMgr.isVolteProvisionedOnDevice() + || !isImsServiceStateReady(mImsMgr) + || carrierConfig.getBoolean( + CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL))) { + getPreferenceScreen().removePreference(mButton4glte); + } else { + mButton4glte.setEnabled(is4gLtePrefEnabled(carrierConfig) + && hasActiveSubscriptions()); + boolean enh4glteMode = mImsMgr.isEnhanced4gLteModeSettingEnabledByUser() + && mImsMgr.isNonTtyOrTtyOnVolteEnabled(); + mButton4glte.setChecked(enh4glteMode); + } + } + + private void updateVideoCallState() { + if (mVideoCallingPref == null || mCallingCategory == null) { + return; + } + + PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); + + if (mImsMgr != null + && mImsMgr.isVtEnabledByPlatform() + && mImsMgr.isVtProvisionedOnDevice() + && isImsServiceStateReady(mImsMgr) + && (carrierConfig.getBoolean( + CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS) + || mTelephonyManager.isDataEnabled())) { + mCallingCategory.addPreference(mVideoCallingPref); + if (!mButton4glte.isChecked()) { + mVideoCallingPref.setEnabled(false); + mVideoCallingPref.setChecked(false); + } else { + mVideoCallingPref.setEnabled(mTelephonyManager.getCallState(mSubId) + == TelephonyManager.CALL_STATE_IDLE && hasActiveSubscriptions()); + mVideoCallingPref.setChecked(mImsMgr.isVtEnabledByUser()); + mVideoCallingPref.setOnPreferenceChangeListener(this); + } + } else { + mCallingCategory.removePreference(mVideoCallingPref); + } + } + + private void updatePreferredNetworkType() { + boolean enabled = mTelephonyManager.getCallState( + mSubId) == TelephonyManager.CALL_STATE_IDLE + && hasActiveSubscriptions(); + Log.i(LOG_TAG, "updatePreferredNetworkType: " + enabled); + // TODO: Disentangle enabled networks vs preferred network mode, it looks like + // both buttons are shown to the user as "Preferred network type" and the options change + // based on what looks like World mode. + if (mButtonEnabledNetworks != null) { + mButtonEnabledNetworks.setEnabled(enabled); + } + if (mButtonPreferredNetworkMode != null) { + mButtonPreferredNetworkMode.setEnabled(enabled); + } + } + + private void updateCallingCategory() { + if (mCallingCategory == null) { + return; + } + + updateWiFiCallState(); + updateVideoCallState(); + + // If all items in calling category is removed, we remove it from + // the screen. Otherwise we'll see title of the category but nothing + // is in there. + if (mCallingCategory.getPreferenceCount() == 0) { + getPreferenceScreen().removePreference(mCallingCategory); + } else { + getPreferenceScreen().addPreference(mCallingCategory); + } + } + + private static void log(String msg) { + Log.d(LOG_TAG, msg); + } + + private static void loge(String msg) { + Log.e(LOG_TAG, msg); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + final int itemId = item.getItemId(); + if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled() + // Commenting out "logical up" capability. This is a workaround for issue 5278083. + // + // Settings app may not launch this activity via UP_ACTIVITY_CLASS but the other + // Activity that looks exactly same as UP_ACTIVITY_CLASS ("SubSettings" Activity). + // At that moment, this Activity launches UP_ACTIVITY_CLASS on top of the Activity. + // which confuses users. + // TODO: introduce better mechanism for "up" capability here. + /*Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(UP_ACTIVITY_PACKAGE, UP_ACTIVITY_CLASS); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent);*/ + getActivity().finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private boolean isWorldMode() { + boolean worldModeOn = false; + final String configString = getResources().getString(R.string.config_world_mode); + + if (!TextUtils.isEmpty(configString)) { + String[] configArray = configString.split(";"); + // Check if we have World mode configuration set to True only or config is set to True + // and SIM GID value is also set and matches to the current SIM GID. + if (configArray != null && + ((configArray.length == 1 && configArray[0].equalsIgnoreCase("true")) + || (configArray.length == 2 && !TextUtils.isEmpty(configArray[1]) + && mTelephonyManager != null + && configArray[1].equalsIgnoreCase( + mTelephonyManager.getGroupIdLevel1())))) { + worldModeOn = true; + } + } + + Log.d(LOG_TAG, "isWorldMode=" + worldModeOn); + + return worldModeOn; + } + + private void controlGsmOptions(boolean enable) { + PreferenceScreen prefSet = getPreferenceScreen(); + if (prefSet == null) { + return; + } + + updateGsmUmtsOptions(this, prefSet, mSubId); + + PreferenceCategory networkOperatorCategory = + (PreferenceCategory) prefSet.findPreference( + com.android.phone.NetworkOperators.CATEGORY_NETWORK_OPERATORS_KEY); + Preference carrierSettings = prefSet.findPreference(BUTTON_CARRIER_SETTINGS_KEY); + if (networkOperatorCategory != null) { + if (enable) { + networkOperatorCategory.setEnabled(true); + } else { + prefSet.removePreference(networkOperatorCategory); + } + } + if (carrierSettings != null) { + prefSet.removePreference(carrierSettings); + } + } + + private void controlCdmaOptions(boolean enable) { + PreferenceScreen prefSet = getPreferenceScreen(); + if (prefSet == null) { + return; + } + updateCdmaOptions(this, prefSet, mSubId); + com.android.phone.CdmaSystemSelectListPreference systemSelect = + (CdmaSystemSelectListPreference)prefSet.findPreference + (BUTTON_CDMA_SYSTEM_SELECT_KEY); + systemSelect.setSubscriptionId(mSubId); + if (systemSelect != null) { + systemSelect.setEnabled(enable); + } + } + + private boolean isSupportTdscdma() { + if (getResources().getBoolean(R.bool.config_support_tdscdma)) { + return true; + } + + String operatorNumeric = mTelephonyManager.getServiceState().getOperatorNumeric(); + String[] numericArray = getResources().getStringArray( + R.array.config_support_tdscdma_roaming_on_networks); + if (numericArray.length == 0 || operatorNumeric == null) { + return false; + } + for (String numeric : numericArray) { + if (operatorNumeric.equals(numeric)) { + return true; + } + } + return false; + } + + /** + * Metrics events related methods. it takes care of all preferences possible in this + * fragment(except a few that log on their own). It doesn't only include preferences in + * network_setting_fragment.xml, but also those defined in GsmUmtsOptions and CdmaOptions. + */ + private void sendMetricsEventPreferenceClicked( + PreferenceScreen preferenceScreen, Preference preference) { + final int category = getMetricsEventCategory(preferenceScreen, preference); + if (category == MetricsProto.MetricsEvent.VIEW_UNKNOWN) { + return; + } + + // Send MetricsEvent on click. It includes preferences other than SwitchPreferences, + // which send MetricsEvent in onPreferenceChange. + // For ListPreferences, we log it here without a value, only indicating it's clicked to + // open the list dialog. When a value is chosen, another MetricsEvent is logged with + // new value in onPreferenceChange. + if (preference == mLteDataServicePref || preference == mDataUsagePref + || preference == mEuiccSettingsPref || preference == mAdvancedOptions + || preference == mWiFiCallingPref || preference == mButtonPreferredNetworkMode + || preference == mButtonEnabledNetworks + || preference == preferenceScreen.findPreference(BUTTON_CDMA_SYSTEM_SELECT_KEY) + || preference == preferenceScreen.findPreference(BUTTON_CDMA_SUBSCRIPTION_KEY) + || preference == preferenceScreen.findPreference(BUTTON_GSM_APN_EXPAND_KEY) + || preference == preferenceScreen.findPreference(BUTTON_CDMA_APN_EXPAND_KEY) + || preference == preferenceScreen.findPreference(BUTTON_CARRIER_SETTINGS_KEY)) { + MetricsLogger.action(getContext(), category); + } + } + + private void sendMetricsEventPreferenceChanged( + PreferenceScreen preferenceScreen, Preference preference, Object newValue) { + final int category = getMetricsEventCategory(preferenceScreen, preference); + if (category == MetricsProto.MetricsEvent.VIEW_UNKNOWN) { + return; + } + + // MetricsEvent logging with new value, for SwitchPreferences and ListPreferences. + if (preference == mButton4glte || preference == mVideoCallingPref) { + MetricsLogger.action(getContext(), category, (Boolean) newValue); + } else if (preference == mButtonPreferredNetworkMode + || preference == mButtonEnabledNetworks + || preference == preferenceScreen + .findPreference(BUTTON_CDMA_SYSTEM_SELECT_KEY) + || preference == preferenceScreen + .findPreference(BUTTON_CDMA_SUBSCRIPTION_KEY)) { + // Network select preference sends metrics event in its own listener. + MetricsLogger.action(getContext(), category, Integer.valueOf((String) newValue)); + } + } + + private int getMetricsEventCategory( + PreferenceScreen preferenceScreen, Preference preference) { + + if (preference == null) { + return MetricsProto.MetricsEvent.VIEW_UNKNOWN; + } else if (preference == mMobileDataPref) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_MOBILE_DATA_TOGGLE; + } else if (preference == mButtonDataRoam) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_DATA_ROAMING_TOGGLE; + } else if (preference == mDataUsagePref) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_DATA_USAGE; + } else if (preference == mLteDataServicePref) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_SET_UP_DATA_SERVICE; + } else if (preference == mAdvancedOptions) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_EXPAND_ADVANCED_FIELDS; + } else if (preference == mButton4glte) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_ENHANCED_4G_LTE_MODE_TOGGLE; + } else if (preference == mButtonPreferredNetworkMode) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_SELECT_PREFERRED_NETWORK; + } else if (preference == mButtonEnabledNetworks) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_SELECT_ENABLED_NETWORK; + } else if (preference == mEuiccSettingsPref) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_EUICC_SETTING; + } else if (preference == mWiFiCallingPref) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_WIFI_CALLING; + } else if (preference == mVideoCallingPref) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_VIDEO_CALLING_TOGGLE; + } else if (preference == preferenceScreen + .findPreference(com.android.phone.NetworkOperators.BUTTON_AUTO_SELECT_KEY)) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE; + } else if (preference == preferenceScreen + .findPreference(NetworkOperators.BUTTON_NETWORK_SELECT_KEY)) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK; + } else if (preference == preferenceScreen + .findPreference(BUTTON_CDMA_SYSTEM_SELECT_KEY)) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_CDMA_SYSTEM_SELECT; + } else if (preference == preferenceScreen + .findPreference(BUTTON_CDMA_SUBSCRIPTION_KEY)) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_CDMA_SUBSCRIPTION_SELECT; + } else if (preference == preferenceScreen.findPreference(BUTTON_GSM_APN_EXPAND_KEY) + || preference == preferenceScreen.findPreference(BUTTON_CDMA_APN_EXPAND_KEY)) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_APN_SETTINGS; + } else if (preference == preferenceScreen.findPreference(BUTTON_CARRIER_SETTINGS_KEY)) { + return MetricsProto.MetricsEvent.ACTION_MOBILE_NETWORK_CARRIER_SETTINGS; + } else { + return MetricsProto.MetricsEvent.VIEW_UNKNOWN; + } + } + + private void updateGsmUmtsOptions(PreferenceFragment prefFragment, + PreferenceScreen prefScreen, final int subId) { + // We don't want to re-create GsmUmtsOptions if already exists. Otherwise, the + // preferences inside it will also be re-created which causes unexpected behavior. + // For example, the open dialog gets dismissed or detached after pause / resume. + if (mGsmUmtsOptions == null) { + mGsmUmtsOptions = new GsmUmtsOptions(prefFragment, prefScreen, subId); + } else { + mGsmUmtsOptions.update(subId); + } + } + + private void updateCdmaOptions(PreferenceFragment prefFragment, PreferenceScreen prefScreen, + int subId) { + // We don't want to re-create CdmaOptions if already exists. Otherwise, the preferences + // inside it will also be re-created which causes unexpected behavior. For example, + // the open dialog gets dismissed or detached after pause / resume. + if (mCdmaOptions == null) { + mCdmaOptions = new CdmaOptions(prefFragment, prefScreen, subId); + } else { + mCdmaOptions.updateSubscriptionId(subId); + } + } +} + +private static final class SetPreferredNetworkAsyncTask extends AsyncTask { + + private final TelephonyManager mTelephonyManager; + private final int mSubId; + private final int mNetworkType; + private final Message mCallback; + + SetPreferredNetworkAsyncTask( + TelephonyManager tm, int subId, int networkType, Message callback) { + mTelephonyManager = tm; + mSubId = subId; + mNetworkType = networkType; + mCallback = callback; + } + + @Override + protected Boolean doInBackground(Void... voids) { + return mTelephonyManager.setPreferredNetworkType(mSubId, mNetworkType); + } + + @Override + protected void onPostExecute(Boolean isSuccessed) { + mCallback.obj = isSuccessed; + mCallback.sendToTarget(); + } +} \ No newline at end of file diff --git a/src/com/android/settings/mobilenetwork/NetworkOperators.java b/src/com/android/settings/mobilenetwork/NetworkOperators.java new file mode 100644 index 00000000000..b07a17059ba --- /dev/null +++ b/src/com/android/settings/mobilenetwork/NetworkOperators.java @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.mobilenetwork; + +import android.app.ProgressDialog; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Message; +import android.preference.Preference; +import android.preference.PreferenceCategory; +import android.preference.TwoStatePreference; +import android.telephony.ServiceState; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.Toast; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.phone.NetworkSelectSettingActivity; +import com.android.settingslib.utils.ThreadUtils; + +/** + * "Networks" settings UI for the Phone app. + */ +public class NetworkOperators extends PreferenceCategory + implements Preference.OnPreferenceChangeListener { + + private static final String LOG_TAG = "NetworkOperators"; + private static final boolean DBG = true; + + private static final int EVENT_AUTO_SELECT_DONE = 100; + private static final int EVENT_GET_NETWORK_SELECTION_MODE_DONE = 200; + + //String keys for preference lookup + public static final String BUTTON_NETWORK_SELECT_KEY = "button_network_select_key"; + public static final String BUTTON_AUTO_SELECT_KEY = "button_auto_select_key"; + public static final String BUTTON_CHOOSE_NETWORK_KEY = "button_choose_network_key"; + public static final String CATEGORY_NETWORK_OPERATORS_KEY = "network_operators_category_key"; + + //preference objects + private NetworkSelectListPreference mNetworkSelect; + private TwoStatePreference mAutoSelect; + private Preference mChooseNetwork; + private ProgressDialog mProgressDialog; + + private int mSubId; + private TelephonyManager mTelephonyManager; + + // There's two sets of Auto-Select UI in this class. + // If {@code com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI} set as true + // {@link mChooseNetwork} will be used, otherwise {@link mNetworkSelect} will be used. + boolean mEnableNewManualSelectNetworkUI; + + public NetworkOperators(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NetworkOperators(Context context) { + super(context); + } + + /** + * Initialize NetworkOperators instance. + */ + public void initialize() { + mEnableNewManualSelectNetworkUI = getContext().getResources().getBoolean( + com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI); + mAutoSelect = (TwoStatePreference) findPreference(BUTTON_AUTO_SELECT_KEY); + mChooseNetwork = findPreference(BUTTON_CHOOSE_NETWORK_KEY); + mNetworkSelect = (NetworkSelectListPreference) findPreference(BUTTON_NETWORK_SELECT_KEY); + if (mEnableNewManualSelectNetworkUI) { + removePreference(mNetworkSelect); + } else { + removePreference(mChooseNetwork); + } + mProgressDialog = new ProgressDialog(getContext()); + mTelephonyManager = TelephonyManager.from(getContext()); + } + + /** + * Update NetworkOperators instance if like subId is updated. + * + * @param subId Corresponding subscription ID of this network. + */ + protected void update(final int subId) { + mSubId = subId; + mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId); + + if (mAutoSelect != null) { + mAutoSelect.setOnPreferenceChangeListener(this); + } + + if (mEnableNewManualSelectNetworkUI) { + if (mChooseNetwork != null) { + ServiceState ss = mTelephonyManager.getServiceState(); + if (ss != null && ss.getState() == ServiceState.STATE_IN_SERVICE) { + mChooseNetwork.setSummary(mTelephonyManager.getNetworkOperatorName()); + } else { + mChooseNetwork.setSummary(R.string.network_disconnected); + } + } + } else { + if (mNetworkSelect != null) { + mNetworkSelect.initialize(mSubId, this, mProgressDialog); + } + } + getNetworkSelectionMode(); + } + + /** + * Implemented to support onPreferenceChangeListener to look for preference + * changes specifically on auto select button. + * + * @param preference is the preference to be changed, should be auto select button. + * @param newValue should be the value of whether autoSelect is checked. + */ + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (preference == mAutoSelect) { + boolean autoSelect = (Boolean) newValue; + if (DBG) logd("onPreferenceChange autoSelect: " + String.valueOf(autoSelect)); + selectNetworkAutomatic(autoSelect); + MetricsLogger.action(getContext(), + MetricsEvent.ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE, autoSelect); + return true; + } + return false; + } + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_AUTO_SELECT_DONE: + mAutoSelect.setEnabled(true); + dismissProgressBar(); + + boolean isSuccessed = (boolean) msg.obj; + + if (isSuccessed) { + if (DBG) logd("automatic network selection: succeeded!"); + displayNetworkSelectionSucceeded(); + } else { + if (DBG) logd("automatic network selection: failed!"); + displayNetworkSelectionFailed(); + } + + break; + case EVENT_GET_NETWORK_SELECTION_MODE_DONE: + int networkSelectionMode = msg.arg1; + if (networkSelectionMode == TelephonyManager.NETWORK_SELECTION_MODE_UNKNOWN) { + if (DBG) logd("get network selection mode: failed!"); + } else { + boolean autoSelect = networkSelectionMode + == TelephonyManager.NETWORK_SELECTION_MODE_AUTO; + if (DBG) { + logd("get network selection mode: " + + (autoSelect ? "auto" : "manual") + " selection"); + } + if (mAutoSelect != null) { + mAutoSelect.setChecked(autoSelect); + } + if (mEnableNewManualSelectNetworkUI) { + if (mChooseNetwork != null) { + mChooseNetwork.setEnabled(!autoSelect); + } + } else { + if (mNetworkSelect != null) { + mNetworkSelect.setEnabled(!autoSelect); + } + } + } + } + return; + } + }; + + // Used by both mAutoSelect and mNetworkSelect buttons. + protected void displayNetworkSelectionFailed() { + Toast.makeText(getContext(), R.string.connect_later, Toast.LENGTH_LONG).show(); + } + + // Used by both mAutoSelect and mNetworkSelect buttons. + protected void displayNetworkSelectionSucceeded() { + Toast.makeText(getContext(), R.string.registration_done, Toast.LENGTH_LONG).show(); + } + + private void selectNetworkAutomatic(boolean autoSelect) { + if (DBG) logd("selectNetworkAutomatic: " + String.valueOf(autoSelect)); + + if (autoSelect) { + if (mEnableNewManualSelectNetworkUI) { + if (mChooseNetwork != null) { + mChooseNetwork.setEnabled(!autoSelect); + } + } else { + if (mNetworkSelect != null) { + mNetworkSelect.setEnabled(!autoSelect); + } + } + if (DBG) logd("select network automatically..."); + showAutoSelectProgressBar(); + mAutoSelect.setEnabled(false); + if (SubscriptionManager.isValidSubscriptionId(mSubId)) { + ThreadUtils.postOnBackgroundThread(() -> { + mTelephonyManager.setNetworkSelectionModeAutomatic(); + // Because TelephonyManager#setNetworkSelectionModeAutomatic doesn't have a + // return value, we query the current network selection mode to tell if the + // TelephonyManager#setNetworkSelectionModeAutomatic is successed. + int networkSelectionMode = mTelephonyManager.getNetworkSelectionMode(); + Message msg = mHandler.obtainMessage(EVENT_AUTO_SELECT_DONE); + msg.obj = networkSelectionMode == TelephonyManager.NETWORK_SELECTION_MODE_AUTO; + msg.sendToTarget(); + }); + } + } else { + if (mEnableNewManualSelectNetworkUI) { + if (mChooseNetwork != null) { + // Open the choose Network page automatically when user turn off the auto-select + openChooseNetworkPage(); + } + } else { + if (mNetworkSelect != null) { + mNetworkSelect.onClick(); + } + } + } + } + + protected void getNetworkSelectionMode() { + if (DBG) logd("getting network selection mode..."); + ThreadUtils.postOnBackgroundThread(() -> { + int networkSelectionMode = mTelephonyManager.getNetworkSelectionMode(); + Message msg = mHandler.obtainMessage(EVENT_GET_NETWORK_SELECTION_MODE_DONE); + msg.arg1 = networkSelectionMode; + msg.sendToTarget(); + }); + } + + private void dismissProgressBar() { + if (mProgressDialog != null && mProgressDialog.isShowing()) { + mProgressDialog.dismiss(); + } + } + + private void showAutoSelectProgressBar() { + mProgressDialog.setMessage( + getContext().getResources().getString(R.string.register_automatically)); + mProgressDialog.setCanceledOnTouchOutside(false); + mProgressDialog.setCancelable(false); + mProgressDialog.setIndeterminate(true); + mProgressDialog.show(); + } + + /** + * Open the Choose network page via {@alink NetworkSelectSettingActivity} + */ + public void openChooseNetworkPage() { + Intent intent = NetworkSelectSettingActivity.getIntent(getContext(), mSubId); + getContext().startActivity(intent); + } + + protected boolean preferenceTreeClick(Preference preference) { + if (mEnableNewManualSelectNetworkUI) { + if (DBG) logd("enable New AutoSelectNetwork UI"); + if (preference == mChooseNetwork) { + openChooseNetworkPage(); + } + return (preference == mAutoSelect || preference == mChooseNetwork); + } else { + return (preference == mAutoSelect || preference == mNetworkSelect); + } + } + + private void logd(String msg) { + Log.d(LOG_TAG, "[NetworksList] " + msg); + } + + private void loge(String msg) { + Log.e(LOG_TAG, "[NetworksList] " + msg); + } +} diff --git a/src/com/android/settings/mobilenetwork/NetworkScanHelper.java b/src/com/android/settings/mobilenetwork/NetworkScanHelper.java new file mode 100644 index 00000000000..59908bccfb1 --- /dev/null +++ b/src/com/android/settings/mobilenetwork/NetworkScanHelper.java @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.mobilenetwork; + +import android.annotation.IntDef; +import android.telephony.AccessNetworkConstants.AccessNetworkType; +import android.telephony.CellInfo; +import android.telephony.NetworkScan; +import android.telephony.NetworkScanRequest; +import android.telephony.RadioAccessSpecifier; +import android.telephony.TelephonyManager; +import android.telephony.TelephonyScanManager; +import android.util.Log; + +import com.android.internal.telephony.CellNetworkScanResult; +import com.android.phone.CellInfoUtil; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.SettableFuture; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.List; +import java.util.concurrent.Executor; +import java.util.stream.Collectors; + +/** + * A helper class that builds the common interface and performs the network scan for two different + * network scan APIs. + */ +public class NetworkScanHelper { + public static final String TAG = "NetworkScanHelper"; + private static final boolean DBG = true; + + /** + * Callbacks interface to inform the network scan results. + */ + public interface NetworkScanCallback { + /** + * Called when the results is returned from {@link TelephonyManager}. This method will be + * called at least one time if there is no error occurred during the network scan. + * + *

This method can be called multiple times in one network scan, until + * {@link #onComplete()} or {@link #onError(int)} is called. + * + * @param results + */ + void onResults(List results); + + /** + * Called when the current network scan process is finished. No more + * {@link #onResults(List)} will be called for the current network scan after this method is + * called. + */ + void onComplete(); + + /** + * Called when an error occurred during the network scan process. + * + *

There is no more result returned from {@link TelephonyManager} if an error occurred. + * + *

{@link #onComplete()} will not be called if an error occurred. + * + * @see {@link NetworkScan.ScanErrorCode} + */ + void onError(int errorCode); + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef({NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS, NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS}) + public @interface NetworkQueryType {} + + /** + * Performs the network scan using {@link TelephonyManager#getAvailableNetworks()}. The network + * scan results won't be returned to the caller until the network scan is completed. + * + *

This is typically used when the modem doesn't support the new network scan api + * {@link TelephonyManager#requestNetworkScan( + * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}. + */ + public static final int NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS = 1; + + /** + * Performs the network scan using {@link TelephonyManager#requestNetworkScan( + * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)} The network scan + * results will be returned to the caller periodically in a small time window until the network + * scan is completed. The complete results should be returned in the last called of + * {@link NetworkScanCallback#onResults(List)}. + * + *

This is recommended to be used if modem supports the new network scan api + * {@link TelephonyManager#requestNetworkScan( + * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)} + */ + public static final int NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS = 2; + + /** The constants below are used in the async network scan. */ + private static final boolean INCREMENTAL_RESULTS = true; + private static final int SEARCH_PERIODICITY_SEC = 5; + private static final int MAX_SEARCH_TIME_SEC = 300; + private static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3; + + private static final NetworkScanRequest NETWORK_SCAN_REQUEST = + new NetworkScanRequest( + NetworkScanRequest.SCAN_TYPE_ONE_SHOT, + new RadioAccessSpecifier[]{ + // GSM + new RadioAccessSpecifier( + AccessNetworkType.GERAN, + null /* bands */, + null /* channels */), + // LTE + new RadioAccessSpecifier( + AccessNetworkType.EUTRAN, + null /* bands */, + null /* channels */), + // WCDMA + new RadioAccessSpecifier( + AccessNetworkType.UTRAN, + null /* bands */, + null /* channels */) + }, + SEARCH_PERIODICITY_SEC, + MAX_SEARCH_TIME_SEC, + INCREMENTAL_RESULTS, + INCREMENTAL_RESULTS_PERIODICITY_SEC, + null /* List of PLMN ids (MCC-MNC) */); + + private final NetworkScanCallback mNetworkScanCallback; + private final TelephonyManager mTelephonyManager; + private final TelephonyScanManager.NetworkScanCallback mInternalNetworkScanCallback; + private final Executor mExecutor; + + private NetworkScan mNetworkScanRequester; + + /** Callbacks for sync network scan */ + private ListenableFuture> mNetworkScanFuture; + + public NetworkScanHelper(TelephonyManager tm, NetworkScanCallback callback, Executor executor) { + mTelephonyManager = tm; + mNetworkScanCallback = callback; + mInternalNetworkScanCallback = new NetworkScanCallbackImpl(); + mExecutor = executor; + } + + /** + * Performs a network scan for the given type {@code type}. + * {@link #NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS} is recommended if modem supports + * {@link TelephonyManager#requestNetworkScan( + * NetworkScanRequest, Executor, TelephonyScanManager.NetworkScanCallback)}. + * + * @param type used to tell which network scan API should be used. + */ + public void startNetworkScan(@NetworkQueryType int type) { + if (type == NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS) { + mNetworkScanFuture = SettableFuture.create(); + Futures.addCallback(mNetworkScanFuture, new FutureCallback>() { + @Override + public void onSuccess(List result) { + onResults(result); + onComplete(); + } + + @Override + public void onFailure(Throwable t) { + int errCode = Integer.parseInt(t.getMessage()); + onError(errCode); + } + }); + mExecutor.execute(new NetworkScanSyncTask( + mTelephonyManager, (SettableFuture) mNetworkScanFuture)); + } else if (type == NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS) { + if (DBG) Log.d(TAG, "start network scan async"); + mNetworkScanRequester = mTelephonyManager.requestNetworkScan( + NETWORK_SCAN_REQUEST, + mExecutor, + mInternalNetworkScanCallback); + } + } + + /** + * The network scan of type {@link #NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS} can't be stopped, + * however, the result of the current network scan won't be returned to the callback after + * calling this method. + */ + public void stopNetworkQuery() { + if (mNetworkScanRequester != null) { + mNetworkScanRequester.stopScan(); + mNetworkScanFuture = null; + } + + if (mNetworkScanFuture != null) { + mNetworkScanFuture.cancel(true /* mayInterruptIfRunning */); + mNetworkScanFuture = null; + } + } + + private void onResults(List cellInfos) { + mNetworkScanCallback.onResults(cellInfos); + } + + private void onComplete() { + mNetworkScanCallback.onComplete(); + } + + private void onError(int errCode) { + mNetworkScanCallback.onError(errCode); + } + + /** + * Converts the status code of {@link CellNetworkScanResult} to one of the + * {@link NetworkScan.ScanErrorCode}. + * @param errCode status code from {@link CellNetworkScanResult}. + * + * @return one of the scan error code from {@link NetworkScan.ScanErrorCode}. + */ + private static int convertToScanErrorCode(int errCode) { + switch (errCode) { + case CellNetworkScanResult.STATUS_RADIO_NOT_AVAILABLE: + return NetworkScan.ERROR_RADIO_INTERFACE_ERROR; + case CellNetworkScanResult.STATUS_RADIO_GENERIC_FAILURE: + default: + return NetworkScan.ERROR_MODEM_ERROR; + } + } + + private final class NetworkScanCallbackImpl extends TelephonyScanManager.NetworkScanCallback { + public void onResults(List results) { + if (DBG) Log.d(TAG, "async scan onResults() results = " + results); + NetworkScanHelper.this.onResults(results); + } + + public void onComplete() { + if (DBG) Log.d(TAG, "async scan onComplete()"); + NetworkScanHelper.this.onComplete(); + } + + public void onError(@NetworkScan.ScanErrorCode int errCode) { + if (DBG) Log.d(TAG, "async scan onError() errorCode = " + errCode); + NetworkScanHelper.this.onError(errCode); + } + } + + private static final class NetworkScanSyncTask implements Runnable { + private final SettableFuture> mCallback; + private final TelephonyManager mTelephonyManager; + + NetworkScanSyncTask( + TelephonyManager telephonyManager, SettableFuture> callback) { + mTelephonyManager = telephonyManager; + mCallback = callback; + } + + @Override + public void run() { + if (DBG) Log.d(TAG, "sync scan start"); + CellNetworkScanResult result = mTelephonyManager.getAvailableNetworks(); + if (result.getStatus() == CellNetworkScanResult.STATUS_SUCCESS) { + List cellInfos = result.getOperators() + .stream() + .map(operatorInfo + -> CellInfoUtil.convertOperatorInfoToCellInfo(operatorInfo)) + .collect(Collectors.toList()); + if (DBG) Log.d(TAG, "sync scan complete"); + mCallback.set(cellInfos); + } else { + mCallback.setException(new Throwable( + Integer.toString(convertToScanErrorCode(result.getStatus())))); + } + } + } +} diff --git a/src/com/android/settings/mobilenetwork/NetworkSelectListPreference.java b/src/com/android/settings/mobilenetwork/NetworkSelectListPreference.java new file mode 100644 index 00000000000..95283dcc74b --- /dev/null +++ b/src/com/android/settings/mobilenetwork/NetworkSelectListPreference.java @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.phone; + +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; +import android.preference.ListPreference; +import android.preference.Preference; +import android.telephony.CellInfo; +import android.telephony.CellInfoCdma; +import android.telephony.CellInfoGsm; +import android.telephony.CellInfoLte; +import android.telephony.CellInfoWcdma; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.text.BidiFormatter; +import android.text.TextDirectionHeuristics; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.Toast; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.telephony.OperatorInfo; +import com.android.phone.NetworkScanHelper.NetworkScanCallback; +import com.android.settingslib.utils.ThreadUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + + +/** + * "Networks" preference in "Mobile network" settings UI for the Phone app. + * It's used to manually search and choose mobile network. Enabled only when + * autoSelect preference is turned off. + */ +public class NetworkSelectListPreference extends ListPreference + implements DialogInterface.OnCancelListener, + Preference.OnPreferenceChangeListener{ + + private static final String LOG_TAG = "networkSelect"; + private static final boolean DBG = true; + + private static final int EVENT_MANUALLY_NETWORK_SELECTION_DONE = 1; + private static final int EVENT_NETWORK_SCAN_RESULTS = 2; + private static final int EVENT_NETWORK_SCAN_COMPLETED = 3; + private static final int EVENT_NETWORK_SCAN_ERROR = 4; + + //dialog ids + private static final int DIALOG_NETWORK_SELECTION = 100; + private static final int DIALOG_NETWORK_LIST_LOAD = 200; + + private final ExecutorService mNetworkScanExecutor = Executors.newFixedThreadPool(1); + + private List mCellInfoList; + private CellInfo mCellInfo; + + private int mSubId; + private TelephonyManager mTelephonyManager; + private NetworkScanHelper mNetworkScanHelper; + private NetworkOperators mNetworkOperators; + private List mForbiddenPlmns; + + private ProgressDialog mProgressDialog; + public NetworkSelectListPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public NetworkSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onClick() { + showProgressDialog(DIALOG_NETWORK_LIST_LOAD); + TelephonyManager telephonyManager = (TelephonyManager) + getContext().getSystemService(Context.TELEPHONY_SERVICE); + new AsyncTask>() { + @Override + protected List doInBackground(Void... voids) { + String[] forbiddenPlmns = telephonyManager.getForbiddenPlmns(); + return forbiddenPlmns != null ? Arrays.asList(forbiddenPlmns) : null; + } + + @Override + protected void onPostExecute(List result) { + mForbiddenPlmns = result; + loadNetworksList(); + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_MANUALLY_NETWORK_SELECTION_DONE: + if (DBG) logd("hideProgressPanel"); + dismissProgressDialog(); + + boolean isSuccessed = (boolean) msg.obj; + if (isSuccessed) { + if (DBG) { + logd("manual network selection: succeeded! " + + getNetworkTitle(mCellInfo)); + } + mNetworkOperators.displayNetworkSelectionSucceeded(); + } else { + if (DBG) logd("manual network selection: failed!"); + mNetworkOperators.displayNetworkSelectionFailed(); + } + mNetworkOperators.getNetworkSelectionMode(); + break; + + case EVENT_NETWORK_SCAN_RESULTS: + List results = (List) msg.obj; + results.removeIf(cellInfo -> cellInfo == null); + mCellInfoList = new ArrayList<>(results); + if (DBG) logd("CALLBACK_SCAN_RESULTS" + mCellInfoList.toString()); + break; + + case EVENT_NETWORK_SCAN_COMPLETED: + if (DBG) logd("scan complete, load the cellInfosList"); + dismissProgressDialog(); + networksListLoaded(); + break; + case EVENT_NETWORK_SCAN_ERROR: + dismissProgressDialog(); + displayNetworkQueryFailed(); + mNetworkOperators.getNetworkSelectionMode(); + break; + } + return; + } + }; + + private final NetworkScanHelper.NetworkScanCallback mCallback = new NetworkScanCallback() { + public void onResults(List results) { + if (DBG) logd("get scan results: " + results.toString()); + Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results); + msg.sendToTarget(); + } + + public void onComplete() { + if (DBG) logd("network scan completed."); + Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED); + msg.sendToTarget(); + } + + public void onError(int error) { + if (DBG) logd("network scan error."); + Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR); + msg.sendToTarget(); + } + }; + + @Override + //implemented for DialogInterface.OnCancelListener + public void onCancel(DialogInterface dialog) { + if (DBG) logd("user manually close the dialog"); + mNetworkScanHelper.stopNetworkQuery(); + + // If cancelled, we query NetworkSelectMode and update states of AutoSelect button. + mNetworkOperators.getNetworkSelectionMode(); + } + + @Override + protected void onDialogClosed(boolean positiveResult) { + super.onDialogClosed(positiveResult); + // If dismissed, we query NetworkSelectMode and update states of AutoSelect button. + if (!positiveResult) { + mNetworkOperators.getNetworkSelectionMode(); + } + } + + // This initialize method needs to be called for this preference to work properly. + protected void initialize(int subId, NetworkOperators networkOperators, + ProgressDialog progressDialog) { + mSubId = subId; + mNetworkOperators = networkOperators; + // This preference should share the same progressDialog with networkOperators category. + mProgressDialog = progressDialog; + + mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId); + mNetworkScanHelper = new NetworkScanHelper( + mTelephonyManager, mCallback, mNetworkScanExecutor); + + setSummary(mTelephonyManager.getNetworkOperatorName()); + + setOnPreferenceChangeListener(this); + } + + @Override + protected void onPrepareForRemoval() { + destroy(); + super.onPrepareForRemoval(); + } + + private void destroy() { + dismissProgressDialog(); + + if (mNetworkScanHelper != null) { + mNetworkScanHelper.stopNetworkQuery(); + } + + mNetworkScanExecutor.shutdown(); + } + + private void displayEmptyNetworkList() { + Toast.makeText(getContext(), R.string.empty_networks_list, Toast.LENGTH_LONG).show(); + } + + private void displayNetworkQueryFailed() { + Toast.makeText(getContext(), R.string.network_query_error, Toast.LENGTH_LONG).show(); + } + + private void loadNetworksList() { + if (DBG) logd("load networks list..."); + mNetworkScanHelper.startNetworkScan( + NetworkScanHelper.NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS); + } + + private void networksListLoaded() { + if (DBG) logd("networks list loaded"); + + mNetworkOperators.getNetworkSelectionMode(); + if (mCellInfoList != null) { + // create a preference for each item in the list. + // just use the operator name instead of the mildly + // confusing mcc/mnc. + List networkEntriesList = new ArrayList<>(); + List networkEntryValuesList = new ArrayList<>(); + for (CellInfo cellInfo: mCellInfoList) { + // Display each operator name only once. + String networkTitle = getNetworkTitle(cellInfo); + if (CellInfoUtil.isForbidden(cellInfo, mForbiddenPlmns)) { + networkTitle += " " + + getContext().getResources().getString(R.string.forbidden_network); + } + networkEntriesList.add(networkTitle); + networkEntryValuesList.add(getOperatorNumeric(cellInfo)); + } + setEntries(networkEntriesList.toArray(new CharSequence[networkEntriesList.size()])); + setEntryValues(networkEntryValuesList.toArray( + new CharSequence[networkEntryValuesList.size()])); + + super.onClick(); + } else { + displayEmptyNetworkList(); + } + } + + private void dismissProgressDialog() { + if (mProgressDialog != null && mProgressDialog.isShowing()) { + try { + mProgressDialog.dismiss(); + } catch (IllegalArgumentException ex) { + loge("Can't close the progress dialog " + ex); + } + } + } + + private void showProgressDialog(int id) { + if (mProgressDialog == null) { + mProgressDialog = new ProgressDialog(getContext()); + } else { + // Dismiss progress bar if it's showing now. + dismissProgressDialog(); + } + + switch (id) { + case DIALOG_NETWORK_SELECTION: + final String networkSelectMsg = getContext().getResources() + .getString(R.string.register_on_network, + getNetworkTitle(mCellInfo)); + mProgressDialog.setMessage(networkSelectMsg); + mProgressDialog.setCanceledOnTouchOutside(false); + mProgressDialog.setCancelable(false); + mProgressDialog.setIndeterminate(true); + break; + case DIALOG_NETWORK_LIST_LOAD: + mProgressDialog.setMessage( + getContext().getResources().getString(R.string.load_networks_progress)); + mProgressDialog.setCanceledOnTouchOutside(false); + mProgressDialog.setCancelable(true); + mProgressDialog.setIndeterminate(false); + mProgressDialog.setOnCancelListener(this); + break; + default: + } + mProgressDialog.show(); + } + + /** + * Implemented to support onPreferenceChangeListener to look for preference + * changes specifically on this button. + * + * @param preference is the preference to be changed, should be network select button. + * @param newValue should be the value of the selection as index of operators. + */ + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + int operatorIndex = findIndexOfValue((String) newValue); + mCellInfo = mCellInfoList.get(operatorIndex); + if (DBG) logd("selected network: " + mCellInfo.toString()); + + MetricsLogger.action(getContext(), + MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK); + + if (SubscriptionManager.isValidSubscriptionId(mSubId)) { + ThreadUtils.postOnBackgroundThread(() -> { + final OperatorInfo operatorInfo = getOperatorInfoFromCellInfo(mCellInfo); + if (DBG) logd("manually selected network: " + operatorInfo.toString()); + boolean isSuccessed = mTelephonyManager.setNetworkSelectionModeManual( + operatorInfo, true /* persistSelection */); + Message msg = mHandler.obtainMessage(EVENT_MANUALLY_NETWORK_SELECTION_DONE); + msg.obj = isSuccessed; + msg.sendToTarget(); + }); + } else { + loge("Error selecting network, subscription Id is invalid " + mSubId); + } + + return true; + } + + /** + * Returns the title of the network obtained in the manual search. + * + * @param cellInfo contains the information of the network. + * @return Long Name if not null/empty, otherwise Short Name if not null/empty, + * else MCCMNC string. + */ + private String getNetworkTitle(CellInfo cellInfo) { + OperatorInfo ni = getOperatorInfoFromCellInfo(cellInfo); + + if (!TextUtils.isEmpty(ni.getOperatorAlphaLong())) { + return ni.getOperatorAlphaLong(); + } else if (!TextUtils.isEmpty(ni.getOperatorAlphaShort())) { + return ni.getOperatorAlphaShort(); + } else { + BidiFormatter bidiFormatter = BidiFormatter.getInstance(); + return bidiFormatter.unicodeWrap(ni.getOperatorNumeric(), TextDirectionHeuristics.LTR); + } + } + + /** + * Returns the operator numeric (MCCMNC) obtained in the manual search. + * + * @param cellInfo contains the information of the network. + * @return MCCMNC string. + */ + private String getOperatorNumeric(CellInfo cellInfo) { + return getOperatorInfoFromCellInfo(cellInfo).getOperatorNumeric(); + } + + /** + * Wrap a cell info into an operator info. + */ + private OperatorInfo getOperatorInfoFromCellInfo(CellInfo cellInfo) { + OperatorInfo oi; + if (cellInfo instanceof CellInfoLte) { + CellInfoLte lte = (CellInfoLte) cellInfo; + oi = new OperatorInfo( + (String) lte.getCellIdentity().getOperatorAlphaLong(), + (String) lte.getCellIdentity().getOperatorAlphaShort(), + lte.getCellIdentity().getMobileNetworkOperator()); + } else if (cellInfo instanceof CellInfoWcdma) { + CellInfoWcdma wcdma = (CellInfoWcdma) cellInfo; + oi = new OperatorInfo( + (String) wcdma.getCellIdentity().getOperatorAlphaLong(), + (String) wcdma.getCellIdentity().getOperatorAlphaShort(), + wcdma.getCellIdentity().getMobileNetworkOperator()); + } else if (cellInfo instanceof CellInfoGsm) { + CellInfoGsm gsm = (CellInfoGsm) cellInfo; + oi = new OperatorInfo( + (String) gsm.getCellIdentity().getOperatorAlphaLong(), + (String) gsm.getCellIdentity().getOperatorAlphaShort(), + gsm.getCellIdentity().getMobileNetworkOperator()); + } else if (cellInfo instanceof CellInfoCdma) { + CellInfoCdma cdma = (CellInfoCdma) cellInfo; + oi = new OperatorInfo( + (String) cdma.getCellIdentity().getOperatorAlphaLong(), + (String) cdma.getCellIdentity().getOperatorAlphaShort(), + "" /* operator numeric */); + } else { + oi = new OperatorInfo("", "", ""); + } + return oi; + } + + @Override + protected Parcelable onSaveInstanceState() { + final Parcelable superState = super.onSaveInstanceState(); + if (isPersistent()) { + // No need to save instance state since it's persistent + return superState; + } + + final SavedState myState = new SavedState(superState); + myState.mDialogListEntries = getEntries(); + myState.mDialogListEntryValues = getEntryValues(); + myState.mCellInfoList = mCellInfoList; + return myState; + } + + @Override + protected void onRestoreInstanceState(Parcelable state) { + if (state == null || !state.getClass().equals(SavedState.class)) { + // Didn't save state for us in onSaveInstanceState + super.onRestoreInstanceState(state); + return; + } + + SavedState myState = (SavedState) state; + + if (getEntries() == null && myState.mDialogListEntries != null) { + setEntries(myState.mDialogListEntries); + } + if (getEntryValues() == null && myState.mDialogListEntryValues != null) { + setEntryValues(myState.mDialogListEntryValues); + } + if (mCellInfoList == null && myState.mCellInfoList != null) { + mCellInfoList = myState.mCellInfoList; + } + + super.onRestoreInstanceState(myState.getSuperState()); + } + + /** + * We save entries, entryValues and operatorInfoList into bundle. + * At onCreate of fragment, dialog will be restored if it was open. In this case, + * we need to restore entries, entryValues and operatorInfoList. Without those information, + * onPreferenceChange will fail if user select network from the dialog. + */ + private static class SavedState extends BaseSavedState { + CharSequence[] mDialogListEntries; + CharSequence[] mDialogListEntryValues; + List mCellInfoList; + + SavedState(Parcel source) { + super(source); + final ClassLoader boot = Object.class.getClassLoader(); + mDialogListEntries = source.readCharSequenceArray(); + mDialogListEntryValues = source.readCharSequenceArray(); + mCellInfoList = source.readParcelableList(mCellInfoList, boot); + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeCharSequenceArray(mDialogListEntries); + dest.writeCharSequenceArray(mDialogListEntryValues); + dest.writeParcelableList(mCellInfoList, flags); + } + + SavedState(Parcelable superState) { + super(superState); + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + } + + private void logd(String msg) { + Log.d(LOG_TAG, "[NetworksList] " + msg); + } + + private void loge(String msg) { + Log.e(LOG_TAG, "[NetworksList] " + msg); + } +} \ No newline at end of file diff --git a/src/com/android/settings/mobilenetwork/RoamingDialogFragment.java b/src/com/android/settings/mobilenetwork/RoamingDialogFragment.java new file mode 100644 index 00000000000..1b1091bcc8b --- /dev/null +++ b/src/com/android/settings/mobilenetwork/RoamingDialogFragment.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.mobilenetwork; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.Fragment; +import android.app.FragmentManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.os.Bundle; +import android.os.PersistableBundle; +import android.telephony.CarrierConfigManager; + +/** + * A dialog fragment that asks the user if they are sure they want to turn on data roaming + * to avoid accidental charges. + */ +public class RoamingDialogFragment extends DialogFragment implements OnClickListener { + + public static final String SUB_ID_KEY = "sub_id_key"; + + private CarrierConfigManager mCarrierConfigManager; + private int mSubId; + + /** + * The interface we expect a host activity to implement. + */ + public interface RoamingDialogListener { + void onPositiveButtonClick(DialogFragment dialog); + } + + // the host activity which implements the listening interface + private RoamingDialogListener mListener; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + Bundle args = getArguments(); + mSubId = args.getInt(SUB_ID_KEY); + mCarrierConfigManager = new CarrierConfigManager(context); + + // Verify host activity implemented callback interface + FragmentManager fragmentManager = getFragmentManager(); + Fragment fragment = fragmentManager.findFragmentById(R.id.network_setting_content); + try { + mListener = (RoamingDialogListener) fragment; + } catch (ClassCastException e) { + throw new ClassCastException(fragment.toString() + + "must implement RoamingDialogListener"); + } + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + int title = R.string.roaming_alert_title; + PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); + if (carrierConfig != null && carrierConfig.getBoolean( + CarrierConfigManager.KEY_CHECK_PRICING_WITH_CARRIER_FOR_DATA_ROAMING_BOOL)) { + title = R.string.roaming_check_price_warning; + } + builder.setMessage(getResources().getString(R.string.roaming_warning)) + .setTitle(title) + .setIconAttribute(android.R.attr.alertDialogIcon) + .setPositiveButton(android.R.string.yes, this) + .setNegativeButton(android.R.string.no, this); + return builder.create(); + } + + @Override + public void onClick(DialogInterface dialog, int which) { + // let the host know that the positive button has been clicked + if (which == dialog.BUTTON_POSITIVE) { + mListener.onPositiveButtonClick(this); + } + } +}