Change packagename to telephony

Also move it under package network, so all of them belong to
com.android.settings.network.telephony.

Bug: 114749736
Test: RunSettingsRoboTests
Change-Id: I35dbdcd5cc9dab939b0a3efccc8897b9a92338d1
This commit is contained in:
jackqdyulei
2018-10-11 11:13:26 -07:00
parent 695638ce35
commit 8eec6ff917
31 changed files with 37 additions and 42 deletions

View File

@@ -36,7 +36,7 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.core.FeatureFlags;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.mobilenetwork.MobileSettingsActivity;
import com.android.settings.network.telephony.MobileSettingsActivity;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
import com.android.settingslib.Utils;

View File

@@ -0,0 +1,204 @@
/*
* 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.network.telephony;
import android.content.Intent;
import android.os.PersistableBundle;
import android.os.SystemProperties;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
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.settings.R;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
/**
* 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 PreferenceFragmentCompat mPrefFragment;
private PreferenceScreen mPrefScreen;
private int mSubId;
public CdmaOptions(PreferenceFragmentCompat 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(
MobileNetworkUtils.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);
}
}

View File

@@ -0,0 +1,115 @@
/*
* 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.network.telephony;
import android.content.Context;
import android.os.Bundle;
import androidx.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 = "CdmaSubListPref";
// 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);
}
//TODO(b/114749736): move this logic to preference controller
protected void showDialog(Bundle state) {
setCurrentCdmaSubscriptionModeValue();
}
//TODO(b/114749736): move this logic to preference controller
protected void onDialogClosed(boolean 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");
}
});
}
}

View File

@@ -0,0 +1,186 @@
/*
* 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.network.telephony;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
import com.android.settingslib.utils.ThreadUtils;
import androidx.preference.ListPreference;
public class CdmaSystemSelectListPreference extends ListPreference {
private static final String LOG_TAG = "CdmaRoamingListPref";
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();
}
//TODO(b/114749736): Move this to preference controller
protected void showDialog(Bundle state) {
if (!mTelephonyManager.getEmergencyCallbackMode()) {
// show Dialog
}
}
//TODO(b/114749736): Move this to preference controller
protected void onDialogClosed(boolean 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();
});
}
}

View File

@@ -0,0 +1,164 @@
/*
* 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.network.telephony;
import android.telephony.CellIdentity;
import android.telephony.CellIdentityCdma;
import android.telephony.CellIdentityGsm;
import android.telephony.CellIdentityLte;
import android.telephony.CellIdentityWcdma;
import android.telephony.CellInfo;
import android.telephony.CellInfoCdma;
import android.telephony.CellInfoGsm;
import android.telephony.CellInfoLte;
import android.telephony.CellInfoWcdma;
import android.text.BidiFormatter;
import android.text.TextDirectionHeuristics;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.telephony.OperatorInfo;
import java.util.List;
/**
* Add static Utility functions to get information from the CellInfo object.
* TODO: Modify {@link CellInfo} for simplify those functions
*/
public final class CellInfoUtil {
private static final String TAG = "NetworkSelectSetting";
private CellInfoUtil() {
}
/**
* Wrap a CellIdentity into a CellInfo.
*/
public static CellInfo wrapCellInfoWithCellIdentity(CellIdentity cellIdentity) {
if (cellIdentity instanceof CellIdentityLte) {
CellInfoLte cellInfo = new CellInfoLte();
cellInfo.setCellIdentity((CellIdentityLte) cellIdentity);
return cellInfo;
} else if (cellIdentity instanceof CellIdentityCdma) {
CellInfoCdma cellInfo = new CellInfoCdma();
cellInfo.setCellIdentity((CellIdentityCdma) cellIdentity);
return cellInfo;
} else if (cellIdentity instanceof CellIdentityWcdma) {
CellInfoWcdma cellInfo = new CellInfoWcdma();
cellInfo.setCellIdentity((CellIdentityWcdma) cellIdentity);
return cellInfo;
} else if (cellIdentity instanceof CellIdentityGsm) {
CellInfoGsm cellInfo = new CellInfoGsm();
cellInfo.setCellIdentity((CellIdentityGsm) cellIdentity);
return cellInfo;
} else {
Log.e(TAG, "Invalid CellInfo type");
return null;
}
}
/**
* 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.
*/
public static String getNetworkTitle(CellInfo cellInfo) {
OperatorInfo oi = getOperatorInfoFromCellInfo(cellInfo);
if (!TextUtils.isEmpty(oi.getOperatorAlphaLong())) {
return oi.getOperatorAlphaLong();
} else if (!TextUtils.isEmpty(oi.getOperatorAlphaShort())) {
return oi.getOperatorAlphaShort();
} else {
BidiFormatter bidiFormatter = BidiFormatter.getInstance();
return bidiFormatter.unicodeWrap(oi.getOperatorNumeric(), TextDirectionHeuristics.LTR);
}
}
/**
* Wrap a cell info into an operator info.
*/
public static 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 {
Log.e(TAG, "Invalid CellInfo type");
oi = new OperatorInfo("", "", "");
}
return oi;
}
/**
* Creates a CellInfo object from OperatorInfo. GsmCellInfo is used here only because
* operatorInfo does not contain technology type while CellInfo is an abstract object that
* requires to specify technology type. It doesn't matter which CellInfo type to use here, since
* we only want to wrap the operator info and PLMN to a CellInfo object.
*/
public static CellInfo convertOperatorInfoToCellInfo(OperatorInfo operatorInfo) {
String operatorNumeric = operatorInfo.getOperatorNumeric();
String mcc = null;
String mnc = null;
if (operatorNumeric != null && operatorNumeric.matches("^[0-9]{5,6}$")) {
mcc = operatorNumeric.substring(0, 3);
mnc = operatorNumeric.substring(3);
}
CellIdentityGsm cig = new CellIdentityGsm(
Integer.MAX_VALUE /* lac */,
Integer.MAX_VALUE /* cid */,
Integer.MAX_VALUE /* arfcn */,
Integer.MAX_VALUE /* bsic */,
mcc,
mnc,
operatorInfo.getOperatorAlphaLong(),
operatorInfo.getOperatorAlphaShort());
CellInfoGsm ci = new CellInfoGsm();
ci.setCellIdentity(cig);
return ci;
}
/** Checks whether the network operator is forbidden. */
public static boolean isForbidden(CellInfo cellInfo, List<String> forbiddenPlmns) {
String plmn = CellInfoUtil.getOperatorInfoFromCellInfo(cellInfo).getOperatorNumeric();
return forbiddenPlmns != null && forbiddenPlmns.contains(plmn);
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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.network.telephony;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkTemplate;
import androidx.preference.Preference;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.format.Formatter;
import android.util.AttributeSet;
import com.android.settings.R;
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) {
final Context context = getContext();
mSubId = subId;
mTemplate = getNetworkTemplate(context, subId);
DataUsageController controller = new DataUsageController(context);
DataUsageController.DataUsageInfo usageInfo = controller.getDataUsageInfo(mTemplate);
setSummary(context.getString(R.string.data_usage_template,
Formatter.formatFileSize(context, 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(Context context, int subId) {
TelephonyManager tm = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
NetworkTemplate mobileAll = NetworkTemplate.buildTemplateMobileAll(
tm.getSubscriberId(subId));
return NetworkTemplate.normalize(mobileAll,
tm.getMergedSubscriberIds());
}
}

View File

@@ -0,0 +1,296 @@
/*
* 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.network.telephony;
import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionManager;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.IconCompat;
import androidx.slice.Slice;
import androidx.slice.builders.ListBuilder;
import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SliceAction;
import com.android.ims.ImsManager;
import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.slices.SettingsSliceProvider;
import com.android.settings.slices.SliceBroadcastReceiver;
import com.android.settings.slices.SliceBuilderUtils;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Helper class to control slices for enhanced 4g LTE settings.
*/
public class Enhanced4gLteSliceHelper {
private static final String TAG = "Enhanced4gLteSliceHelper";
/**
* Settings slice path to enhanced 4g LTE setting.
*/
public static final String PATH_ENHANCED_4G_LTE = "enhanced_4g_lte";
/**
* Action passed for changes to enhanced 4g LTE slice (toggle).
*/
public static final String ACTION_ENHANCED_4G_LTE_CHANGED =
"com.android.settings.mobilenetwork.action.ENHANCED_4G_LTE_CHANGED";
/**
* Slice Uri for Enhanced 4G slice
*/
public static final Uri SLICE_URI = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(SettingsSliceProvider.SLICE_AUTHORITY)
.appendPath(PATH_ENHANCED_4G_LTE)
.build();
/**
* Action for mobile network settings activity which
* allows setting configuration for Enhanced 4G LTE
* related settings
*/
public static final String ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY =
"android.settings.NETWORK_OPERATOR_SETTINGS";
/**
* Timeout for querying enhanced 4g lte setting from ims manager.
*/
private static final int TIMEOUT_MILLIS = 2000;
private final Context mContext;
/**
* Phone package name
*/
private static final String PACKAGE_PHONE = "com.android.phone";
/**
* String resource type
*/
private static final String RESOURCE_TYPE_STRING = "string";
/**
* Enhanced 4g lte mode title variant resource name
*/
private static final String RESOURCE_ENHANCED_4G_LTE_MODE_TITLE_VARIANT =
"enhanced_4g_lte_mode_title_variant";
@VisibleForTesting
public Enhanced4gLteSliceHelper(Context context) {
mContext = context;
}
/**
* Returns Slice object for enhanced_4g_lte settings.
*
* If enhanced 4g LTE is not supported for the current carrier, this method will return slice
* with not supported message.
*
* If enhanced 4g LTE is not editable for the current carrier, this method will return slice
* with not editable message.
*
* If enhanced 4g LTE setting can be changed, this method will return the slice to toggle
* enhanced 4g LTE option with ACTION_ENHANCED_4G_LTE_CHANGED as endItem.
*/
public Slice createEnhanced4gLteSlice(Uri sliceUri) {
final int subId = getDefaultVoiceSubId();
if (subId <= SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
Log.d(TAG, "Invalid subscription Id");
return null;
}
final ImsManager imsManager = getImsManager(subId);
if (!imsManager.isVolteEnabledByPlatform()
|| !imsManager.isVolteProvisionedOnDevice()) {
Log.d(TAG, "Setting is either not provisioned or not enabled by Platform");
return null;
}
if (isCarrierConfigManagerKeyEnabled(
CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL, subId, false)
|| !isCarrierConfigManagerKeyEnabled(
CarrierConfigManager.KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, subId,
true)) {
Log.d(TAG, "Setting is either hidden or not editable");
return null;
}
try {
return getEnhanced4gLteSlice(sliceUri,
isEnhanced4gLteModeEnabled(imsManager), subId);
} catch (InterruptedException | TimeoutException | ExecutionException e) {
Log.e(TAG, "Unable to read the current Enhanced 4g LTE status", e);
return null;
}
}
private boolean isEnhanced4gLteModeEnabled(ImsManager imsManager)
throws InterruptedException, ExecutionException, TimeoutException {
final FutureTask<Boolean> isEnhanced4gLteOnTask = new FutureTask<>(new Callable<Boolean>() {
@Override
public Boolean call() {
return imsManager.isEnhanced4gLteModeSettingEnabledByUser();
}
});
final ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(isEnhanced4gLteOnTask);
return isEnhanced4gLteOnTask.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}
/**
* Builds a toggle slice where the intent takes you to the Enhanced 4G LTE page and the toggle
* enables/disables Enhanced 4G LTE mode setting.
*/
private Slice getEnhanced4gLteSlice(Uri sliceUri, boolean isEnhanced4gLteEnabled, int subId) {
final IconCompat icon = IconCompat.createWithResource(mContext,
R.drawable.ic_launcher_settings);
return new ListBuilder(mContext, sliceUri, ListBuilder.INFINITY)
.setAccentColor(Utils.getColorAccentDefaultColor(mContext))
.addRow(new RowBuilder()
.setTitle(getEnhanced4glteModeTitle(subId))
.addEndItem(
new SliceAction(
getBroadcastIntent(ACTION_ENHANCED_4G_LTE_CHANGED),
null /* actionTitle */, isEnhanced4gLteEnabled))
.setPrimaryAction(new SliceAction(
getActivityIntent(ACTION_MOBILE_NETWORK_SETTINGS_ACTIVITY),
icon,
getEnhanced4glteModeTitle(subId))))
.build();
}
protected ImsManager getImsManager(int subId) {
return ImsManager.getInstance(mContext, SubscriptionManager.getPhoneId(subId));
}
/**
* Handles Enhanced 4G LTE mode setting change from Enhanced 4G LTE slice and posts
* notification. Should be called when intent action is ACTION_ENHANCED_4G_LTE_CHANGED
*
* @param intent action performed
*/
public void handleEnhanced4gLteChanged(Intent intent) {
final int subId = getDefaultVoiceSubId();
if (subId > SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
final ImsManager imsManager = getImsManager(subId);
if (imsManager.isVolteEnabledByPlatform()
&& imsManager.isVolteProvisionedOnDevice()) {
final boolean currentValue = imsManager.isEnhanced4gLteModeSettingEnabledByUser()
&& imsManager.isNonTtyOrTtyOnVolteEnabled();
final boolean newValue = intent.getBooleanExtra(EXTRA_TOGGLE_STATE,
currentValue);
if (newValue != currentValue) {
imsManager.setEnhanced4gLteModeSetting(newValue);
}
}
}
// notify change in slice in any case to get re-queried. This would result in displaying
// appropriate message with the updated setting.
final Uri uri = SliceBuilderUtils.getUri(PATH_ENHANCED_4G_LTE, false /*isPlatformSlice*/);
mContext.getContentResolver().notifyChange(uri, null);
}
private CharSequence getEnhanced4glteModeTitle(int subId) {
CharSequence ret = mContext.getText(R.string.enhanced_4g_lte_mode_title);
try {
if (isCarrierConfigManagerKeyEnabled(
CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL,
subId,
false)) {
final PackageManager manager = mContext.getPackageManager();
final Resources resources = manager.getResourcesForApplication(
PACKAGE_PHONE);
final int resId = resources.getIdentifier(
RESOURCE_ENHANCED_4G_LTE_MODE_TITLE_VARIANT,
RESOURCE_TYPE_STRING, PACKAGE_PHONE);
ret = resources.getText(resId);
}
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "package name not found");
}
return ret;
}
/**
* Returns {@code true} when the key is enabled for the carrier, and {@code false} otherwise.
*/
private boolean isCarrierConfigManagerKeyEnabled(String key,
int subId, boolean defaultValue) {
final CarrierConfigManager configManager = getCarrierConfigManager();
boolean ret = defaultValue;
if (configManager != null) {
final PersistableBundle bundle = configManager.getConfigForSubId(subId);
if (bundle != null) {
ret = bundle.getBoolean(key, defaultValue);
}
}
return ret;
}
protected CarrierConfigManager getCarrierConfigManager() {
return mContext.getSystemService(CarrierConfigManager.class);
}
private PendingIntent getBroadcastIntent(String action) {
final Intent intent = new Intent(action);
intent.setClass(mContext, SliceBroadcastReceiver.class);
return PendingIntent.getBroadcast(mContext, 0 /* requestCode */, intent,
PendingIntent.FLAG_CANCEL_CURRENT);
}
/**
* Returns the current default voice subId obtained from SubscriptionManager
*/
protected int getDefaultVoiceSubId() {
return SubscriptionManager.getDefaultVoiceSubscriptionId();
}
/**
* Returns PendingIntent to start activity specified by action
*/
private PendingIntent getActivityIntent(String action) {
final Intent intent = new Intent(action);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return PendingIntent.getActivity(mContext, 0 /* requestCode */, intent, 0 /* flags */);
}
}

View File

@@ -0,0 +1,180 @@
/*
* 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.network.telephony;
import android.content.Context;
import android.content.Intent;
import android.os.PersistableBundle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.telephony.TelephonyManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceScreen;
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.settings.R;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedPreference;
/**
* 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 PreferenceFragmentCompat mPrefFragment;
private PreferenceScreen mPrefScreen;
public GsmUmtsOptions(PreferenceFragmentCompat prefFragment, PreferenceScreen prefScreen,
final int subId) {
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);
}
// Unlike mPrefFragment or mPrefScreen, subId may change during lifecycle of GsmUmtsOptions.
// When that happens, we update GsmUmtsOptions with new parameters.
protected void update(final int subId) {
boolean addAPNExpand = true;
boolean addNetworkOperatorsCategory = true;
boolean addCarrierSettings = true;
final TelephonyManager telephonyManager = TelephonyManager.from(mPrefFragment.getContext())
.createForSubscriptionId(subId);
//TODO(b/115429509): Get phone from subId
Phone phone = null;
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(
MobileNetworkUtils.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);
} 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);
}
}

View File

@@ -0,0 +1,133 @@
/*
* 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.network.telephony;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
/**
* Dialog Fragment to show dialog for "mobile data"
*
* 1. When user want to disable data in single sim case, show dialog to confirm
* 2. When user want to enable data in multiple sim case, show dialog to confirm to disable other
* sim
*/
public class MobileDataDialogFragment extends InstrumentedDialogFragment implements
DialogInterface.OnClickListener {
public static final int TYPE_DISABLE_DIALOG = 0;
public static final int TYPE_MULTI_SIM_DIALOG = 1;
private static final String ARG_DIALOG_TYPE = "dialog_type";
private static final String ARG_SUB_ID = "subId";
private SubscriptionManager mSubscriptionManager;
private int mType;
private int mSubId;
public static MobileDataDialogFragment newInstance(int type, int subId) {
final MobileDataDialogFragment dialogFragment = new MobileDataDialogFragment();
Bundle args = new Bundle();
args.putInt(ARG_DIALOG_TYPE, type);
args.putInt(ARG_SUB_ID, subId);
dialogFragment.setArguments(args);
return dialogFragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSubscriptionManager = getContext().getSystemService(SubscriptionManager.class);
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Bundle bundle = getArguments();
final Context context = getContext();
mType = bundle.getInt(ARG_DIALOG_TYPE);
mSubId = bundle.getInt(ARG_SUB_ID);
switch (mType) {
case TYPE_DISABLE_DIALOG:
return new AlertDialog.Builder(context)
.setMessage(R.string.data_usage_disable_mobile)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, null)
.create();
case TYPE_MULTI_SIM_DIALOG:
final SubscriptionInfo currentSubInfo =
mSubscriptionManager.getActiveSubscriptionInfo(mSubId);
final SubscriptionInfo nextSubInfo =
mSubscriptionManager.getDefaultDataSubscriptionInfo();
final String previousName = (nextSubInfo == null)
? getContext().getResources().getString(
R.string.sim_selection_required_pref)
: nextSubInfo.getDisplayName().toString();
return new AlertDialog.Builder(context)
.setTitle(R.string.sim_change_data_title)
.setMessage(context.getString(R.string.sim_change_data_message,
currentSubInfo != null
? currentSubInfo.getDisplayName()
: "",
previousName))
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(R.string.cancel, null)
.create();
default:
throw new IllegalArgumentException("unknown type " + mType);
}
}
@Override
public int getMetricsCategory() {
//TODO(b/114749736): add metric id for this fragment
return 0;
}
@Override
public void onClick(DialogInterface dialog, int which) {
switch (mType) {
case TYPE_DISABLE_DIALOG:
MobileNetworkUtils.setMobileDataEnabled(getContext(), mSubId, false /* enabled */,
false /* disableOtherSubscriptions */);
break;
case TYPE_MULTI_SIM_DIALOG:
mSubscriptionManager.setDefaultDataSubId(mSubId);
MobileNetworkUtils.setMobileDataEnabled(getContext(), mSubId, true /* enabled */,
true /* disableOtherSubscriptions */);
break;
default:
throw new IllegalArgumentException("unknown type " + mType);
}
}
}

View File

@@ -0,0 +1,189 @@
/*
* 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.network.telephony;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.FragmentManager;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.preference.SwitchPreference;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
/**
* Preference controller for "Mobile data"
*/
public class MobileDataPreferenceController extends TogglePreferenceController
implements LifecycleObserver, OnStart, OnStop {
private static final String DIALOG_TAG = "MobileDataDialog";
private SwitchPreference mPreference;
private TelephonyManager mTelephonyManager;
private SubscriptionManager mSubscriptionManager;
private DataContentObserver mDataContentObserver;
private FragmentManager mFragmentManager;
private int mSubId;
@VisibleForTesting
int mDialogType;
@VisibleForTesting
boolean mNeedDialog;
public MobileDataPreferenceController(Context context, String key) {
super(context, key);
mSubscriptionManager = context.getSystemService(SubscriptionManager.class);
mDataContentObserver = new DataContentObserver(new Handler(Looper.getMainLooper()));
}
@Override
public int getAvailabilityStatus() {
return mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
? AVAILABLE
: CONDITIONALLY_UNAVAILABLE;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = (SwitchPreference) screen.findPreference(getPreferenceKey());
}
@Override
public void onStart() {
if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
mDataContentObserver.register(mContext, mSubId);
}
}
@Override
public void onStop() {
if (mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
mDataContentObserver.unRegister(mContext);
}
}
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (TextUtils.equals(preference.getKey(), getPreferenceKey())) {
if (mNeedDialog) {
showDialog(mDialogType);
}
return true;
}
return false;
}
@Override
public boolean setChecked(boolean isChecked) {
mNeedDialog = isDialogNeeded();
if (!mNeedDialog) {
// Update data directly if we don't need dialog
MobileNetworkUtils.setMobileDataEnabled(mContext, mSubId, isChecked, false);
return true;
}
return false;
}
@Override
public boolean isChecked() {
return mTelephonyManager.isDataEnabled();
}
public void init(FragmentManager fragmentManager, int subId) {
mFragmentManager = fragmentManager;
mSubId = subId;
mTelephonyManager = TelephonyManager.from(mContext).createForSubscriptionId(mSubId);
}
@VisibleForTesting
boolean isDialogNeeded() {
final boolean enableData = !mTelephonyManager.isDataEnabled();
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 (enableData) {
if (isMultiSim && !isMultipleDataOnCapable && !isDefaultDataSubscription) {
mDialogType = MobileDataDialogFragment.TYPE_MULTI_SIM_DIALOG;
return true;
}
} else {
if (!isMultiSim) {
mDialogType = MobileDataDialogFragment.TYPE_DISABLE_DIALOG;
return true;
}
}
return false;
}
private void showDialog(int type) {
final MobileDataDialogFragment dialogFragment = MobileDataDialogFragment.newInstance(type,
mSubId);
dialogFragment.show(mFragmentManager, DIALOG_TAG);
}
/**
* Listener that listens mobile data state change.
*/
public class DataContentObserver extends ContentObserver {
public DataContentObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
updateState(mPreference);
}
public void register(Context context, int subId) {
Uri uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA);
if (TelephonyManager.getDefault().getSimCount() != 1) {
uri = Settings.Global.getUriFor(Settings.Global.MOBILE_DATA + subId);
}
context.getContentResolver().registerContentObserver(uri, false, this);
}
public void unRegister(Context context) {
context.getContentResolver().unregisterContentObserver(this);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,241 @@
/*
* 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.network.telephony;
import static android.provider.Telephony.Carriers.ENFORCE_MANAGED_URI;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.os.PersistableBundle;
import android.os.SystemProperties;
import android.provider.Settings;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.telephony.euicc.EuiccManager;
import android.telephony.ims.feature.ImsFeature;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import com.android.ims.ImsException;
import com.android.ims.ImsManager;
import java.util.Arrays;
import java.util.List;
public class MobileNetworkUtils {
private static final String TAG = "MobileNetworkUtils";
// CID of the device.
private static final String KEY_CID = "ro.boot.cid";
// CIDs of devices which should not show anything related to eSIM.
private static final String KEY_ESIM_CID_IGNORE = "ro.setupwizard.esim_cid_ignore";
// System Property which is used to decide whether the default eSIM UI will be shown,
// the default value is false.
private static final String KEY_ENABLE_ESIM_UI_BY_DEFAULT =
"esim.enable_esim_system_ui_by_default";
private static final String LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT =
"android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
/**
* Returns if DPC APNs are enforced.
*/
public static boolean isDpcApnEnforced(Context context) {
try (Cursor enforceCursor = context.getContentResolver().query(ENFORCE_MANAGED_URI,
null, null, null, null)) {
if (enforceCursor == null || enforceCursor.getCount() != 1) {
return false;
}
enforceCursor.moveToFirst();
return enforceCursor.getInt(0) > 0;
}
}
/**
* Returns true if Wifi calling is enabled for at least one phone.
*/
public static boolean isWifiCallingEnabled(Context context) {
int phoneCount = TelephonyManager.from(context).getPhoneCount();
for (int i = 0; i < phoneCount; i++) {
if (isWifiCallingEnabled(context, i)) {
return true;
}
}
return false;
}
/**
* Returns true if Wifi calling is enabled for the specific phone with id {@code phoneId}.
*/
public static boolean isWifiCallingEnabled(Context context, int phoneId) {
final PhoneAccountHandle simCallManager =
TelecomManager.from(context).getSimCallManager();
boolean isWifiCallingEnabled;
if (simCallManager != null) {
Intent intent = buildPhoneAccountConfigureIntent(
context, simCallManager);
isWifiCallingEnabled = intent != null;
} else {
ImsManager imsMgr = ImsManager.getInstance(context, phoneId);
isWifiCallingEnabled = imsMgr != null
&& imsMgr.isWfcEnabledByPlatform()
&& imsMgr.isWfcProvisionedOnDevice()
&& isImsServiceStateReady(imsMgr);
}
return isWifiCallingEnabled;
}
@VisibleForTesting
static Intent buildPhoneAccountConfigureIntent(
Context context, PhoneAccountHandle accountHandle) {
Intent intent = buildConfigureIntent(
context, accountHandle, TelecomManager.ACTION_CONFIGURE_PHONE_ACCOUNT);
if (intent == null) {
// If the new configuration didn't work, try the old configuration intent.
intent = buildConfigureIntent(context, accountHandle,
LEGACY_ACTION_CONFIGURE_PHONE_ACCOUNT);
}
return intent;
}
private static Intent buildConfigureIntent(
Context context, PhoneAccountHandle accountHandle, String actionStr) {
if (accountHandle == null || accountHandle.getComponentName() == null
|| TextUtils.isEmpty(accountHandle.getComponentName().getPackageName())) {
return null;
}
// Build the settings intent.
Intent intent = new Intent(actionStr);
intent.setPackage(accountHandle.getComponentName().getPackageName());
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle);
// Check to see that the phone account package can handle the setting intent.
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
if (resolutions.size() == 0) {
intent = null; // set no intent if the package cannot handle it.
}
return intent;
}
public static boolean isImsServiceStateReady(ImsManager imsMgr) {
boolean isImsServiceStateReady = false;
try {
if (imsMgr != null && imsMgr.getImsServiceState() == ImsFeature.STATE_READY) {
isImsServiceStateReady = true;
}
} catch (ImsException ex) {
Log.e(TAG, "Exception when trying to get ImsServiceStatus: " + ex);
}
Log.d(TAG, "isImsServiceStateReady=" + isImsServiceStateReady);
return isImsServiceStateReady;
}
/**
* Whether to show the entry point to eUICC settings.
*
* <p>We show the entry point on any device which supports eUICC as long as either the eUICC
* was ever provisioned (that is, at least one profile was ever downloaded onto it), or if
* the user has enabled development mode.
*/
public static boolean showEuiccSettings(Context context) {
EuiccManager euiccManager =
(EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
if (!euiccManager.isEnabled()) {
return false;
}
final ContentResolver cr = context.getContentResolver();
TelephonyManager tm =
(TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String currentCountry = tm.getNetworkCountryIso().toLowerCase();
String supportedCountries =
Settings.Global.getString(cr, Settings.Global.EUICC_SUPPORTED_COUNTRIES);
boolean inEsimSupportedCountries = false;
if (TextUtils.isEmpty(currentCountry)) {
inEsimSupportedCountries = true;
} else if (!TextUtils.isEmpty(supportedCountries)) {
List<String> supportedCountryList =
Arrays.asList(TextUtils.split(supportedCountries.toLowerCase(), ","));
if (supportedCountryList.contains(currentCountry)) {
inEsimSupportedCountries = true;
}
}
final boolean esimIgnoredDevice =
Arrays.asList(TextUtils.split(SystemProperties.get(KEY_ESIM_CID_IGNORE, ""), ","))
.contains(SystemProperties.get(KEY_CID, null));
final boolean enabledEsimUiByDefault =
SystemProperties.getBoolean(KEY_ENABLE_ESIM_UI_BY_DEFAULT, true);
final boolean euiccProvisioned =
Settings.Global.getInt(cr, Settings.Global.EUICC_PROVISIONED, 0) != 0;
final boolean inDeveloperMode =
Settings.Global.getInt(cr, Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
return (inDeveloperMode || euiccProvisioned
|| (!esimIgnoredDevice && enabledEsimUiByDefault && inEsimSupportedCountries));
}
public static PersistableBundle getCarrierConfigBySubId(int mSubId) {
//TODO(b/114749736): get carrier config from subId
return new PersistableBundle();
}
/**
* Set whether to enable data for {@code subId}, also whether to disable data for other
* subscription
*/
public static void setMobileDataEnabled(Context context, int subId, boolean enabled,
boolean disableOtherSubscriptions) {
final TelephonyManager telephonyManager = TelephonyManager.from(context)
.createForSubscriptionId(subId);
final SubscriptionManager subscriptionManager = context.getSystemService(
SubscriptionManager.class);
telephonyManager.setDataEnabled(enabled);
if (disableOtherSubscriptions) {
List<SubscriptionInfo> subInfoList =
subscriptionManager.getActiveSubscriptionInfoList();
if (subInfoList != null) {
for (SubscriptionInfo subInfo : subInfoList) {
if (subInfo.getSubscriptionId() != subId) {
TelephonyManager.from(context).createForSubscriptionId(
subInfo.getSubscriptionId()).setDataEnabled(false);
}
}
}
}
}
}

View File

@@ -0,0 +1,165 @@
/*
* 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.network.telephony;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.view.Menu;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.android.internal.util.CollectionUtils;
import com.android.settings.R;
import com.android.settings.core.SettingsBaseActivity;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.util.List;
public class MobileSettingsActivity extends SettingsBaseActivity {
@VisibleForTesting
static final String MOBILE_SETTINGS_TAG = "mobile_settings:";
public static final String KEY_SUBSCRIPTION_ID = "key_subscription_id";
public static final String KEY_CUR_SUBSCRIPTION_ID = "key_cur_subscription_id";
private SubscriptionManager mSubscriptionManager;
@VisibleForTesting
Integer mCurSubscriptionId;
@VisibleForTesting
List<SubscriptionInfo> mSubscriptionInfos;
private final SubscriptionManager.OnSubscriptionsChangedListener
mOnSubscriptionsChangeListener
= new SubscriptionManager.OnSubscriptionsChangedListener() {
@Override
public void onSubscriptionsChanged() {
updateSubscriptions(null);
}
};
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//TODO(b/114749736): update fragment by new intent, or at least make sure this page shows
// current tab for sim card
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mobile_settings_container);
mSubscriptionManager = getSystemService(SubscriptionManager.class);
mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
mCurSubscriptionId = savedInstanceState != null
? savedInstanceState.getInt(KEY_CUR_SUBSCRIPTION_ID)
: null;
mSubscriptionManager.addOnSubscriptionsChangedListener(mOnSubscriptionsChangeListener);
updateSubscriptions(savedInstanceState);
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
saveInstanceState(outState);
}
@VisibleForTesting
void saveInstanceState(@NonNull Bundle outState) {
outState.putInt(KEY_CUR_SUBSCRIPTION_ID, mCurSubscriptionId);
}
@VisibleForTesting
void updateSubscriptions(Bundle savedInstanceState) {
//TODO(b/114749736): Sort it by phoneId
mSubscriptionInfos = mSubscriptionManager.getActiveSubscriptionInfoList();
final int subId = CollectionUtils.isEmpty(mSubscriptionInfos)
? SubscriptionManager.INVALID_SUBSCRIPTION_ID
: mSubscriptionInfos.get(0).getSubscriptionId();
updateBottomNavigationView();
if (savedInstanceState == null) {
switchFragment(new MobileNetworkFragment(), subId);
}
}
@VisibleForTesting
void updateBottomNavigationView() {
final BottomNavigationView navigation = findViewById(R.id.bottom_nav);
if (CollectionUtils.size(mSubscriptionInfos) <= 1) {
navigation.setVisibility(View.GONE);
} else {
final Menu menu = navigation.getMenu();
menu.clear();
for (int i = 0, size = mSubscriptionInfos.size(); i < size; i++) {
final SubscriptionInfo subscriptionInfo = mSubscriptionInfos.get(i);
menu.add(0, subscriptionInfo.getSubscriptionId(), i,
subscriptionInfo.getDisplayName());
}
navigation.setOnNavigationItemSelectedListener(item -> {
switchFragment(new MobileNetworkFragment(), item.getItemId());
return true;
});
}
}
@VisibleForTesting
void switchFragment(Fragment fragment, int subscriptionId) {
if (mCurSubscriptionId != null && subscriptionId == mCurSubscriptionId) {
return;
}
final FragmentManager fragmentManager = getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
final Bundle bundle = new Bundle();
bundle.putInt(KEY_SUBSCRIPTION_ID, subscriptionId);
if (mCurSubscriptionId != null) {
final Fragment hideFragment = fragmentManager.findFragmentByTag(
buildFragmentTag(mCurSubscriptionId));
if (hideFragment != null) {
fragmentTransaction.hide(hideFragment);
}
}
Fragment showFragment = fragmentManager.findFragmentByTag(buildFragmentTag(subscriptionId));
if (showFragment == null) {
fragment.setArguments(bundle);
fragmentTransaction.add(R.id.main_content, fragment, buildFragmentTag(subscriptionId));
} else {
showFragment.setArguments(bundle);
fragmentTransaction.show(showFragment);
}
fragmentTransaction.commit();
mCurSubscriptionId = subscriptionId;
}
private String buildFragmentTag(int subscriptionId) {
return MOBILE_SETTINGS_TAG + subscriptionId;
}
}

View File

@@ -0,0 +1,135 @@
/*
* 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.network.telephony;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.telephony.CellInfo;
import android.telephony.SignalStrength;
import android.util.Log;
import android.view.Gravity;
import com.android.settings.R;
import com.android.settingslib.graph.SignalDrawable;
import java.util.List;
import androidx.preference.Preference;
/**
* A Preference represents a network operator in the NetworkSelectSetting fragment.
*/
public class NetworkOperatorPreference extends Preference {
private static final String TAG = "NetworkOperatorPref";
private static final boolean DBG = false;
// number of signal strength level
public static final int NUMBER_OF_LEVELS = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
private CellInfo mCellInfo;
private List<String> mForbiddenPlmns;
private int mLevel = -1;
// The following constants are used to draw signal icon.
private static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
private static final int NO_CELL_DATA_CONNECTED_ICON = 0;
public NetworkOperatorPreference(
CellInfo cellinfo, Context context, List<String> forbiddenPlmns) {
super(context);
mCellInfo = cellinfo;
mForbiddenPlmns = forbiddenPlmns;
refresh();
}
public CellInfo getCellInfo() {
return mCellInfo;
}
/**
* Refresh the NetworkOperatorPreference by updating the title and the icon.
*/
public void refresh() {
if (DBG) Log.d(TAG, "refresh the network: " + CellInfoUtil.getNetworkTitle(mCellInfo));
String networkTitle = CellInfoUtil.getNetworkTitle(mCellInfo);
if (CellInfoUtil.isForbidden(mCellInfo, mForbiddenPlmns)) {
networkTitle += " " + getContext().getResources().getString(R.string.forbidden_network);
}
setTitle(networkTitle);
int level = mCellInfo.getCellSignalStrength().getLevel();
if (DBG) Log.d(TAG, "refresh level: " + String.valueOf(level));
if (mLevel != level) {
mLevel = level;
updateIcon(mLevel);
}
}
/**
* Update the icon according to the input signal strength level.
*/
public void setIcon(int level) {
updateIcon(level);
}
private static int getIconIdForCell(CellInfo ci) {
final int type = ci.getCellIdentity().getType();
switch (type) {
case CellInfo.TYPE_GSM: return R.drawable.signal_strength_g;
case CellInfo.TYPE_WCDMA: // fall through
case CellInfo.TYPE_TDSCDMA: return R.drawable.signal_strength_3g;
case CellInfo.TYPE_LTE: return R.drawable.signal_strength_lte;
case CellInfo.TYPE_CDMA: return R.drawable.signal_strength_1x;
default: return 0;
}
}
private void updateIcon(int level) {
if (level < 0 || level >= NUMBER_OF_LEVELS) return;
Context context = getContext();
// Make the signal strength drawable
int iconId = 0;
if (DBG) Log.d(TAG, "updateIcon level: " + String.valueOf(level));
iconId = SignalDrawable.getState(level, NUMBER_OF_LEVELS, false /* cutOut */);
SignalDrawable signalDrawable = new SignalDrawable(getContext());
signalDrawable.setLevel(iconId);
signalDrawable.setDarkIntensity(0);
// Make the network type drawable
int iconType = getIconIdForCell(mCellInfo);
Drawable networkDrawable =
iconType == NO_CELL_DATA_CONNECTED_ICON
? EMPTY_DRAWABLE
: getContext()
.getResources().getDrawable(iconType, getContext().getTheme());
// Overlay the two drawables
Drawable[] layers = {networkDrawable, signalDrawable};
final int iconSize =
context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
LayerDrawable icons = new LayerDrawable(layers);
// Set the network type icon at the top left
icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
// Set the signal strength icon at the bottom right
icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
setIcon(icons);
}
}

View File

@@ -0,0 +1,301 @@
/*
* 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.network.telephony;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
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 androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.TwoStatePreference;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
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() {
//TODO(b/114749736): Build intent without calling static method
Intent intent = new Intent();
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);
}
}

View File

@@ -0,0 +1,286 @@
/*
* 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.network.telephony;
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.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.
*
* <p> 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<CellInfo> 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.
*
* <p> There is no more result returned from {@link TelephonyManager} if an error occurred.
*
* <p> {@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.
*
* <p> 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)}.
*
* <p> 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<List<CellInfo>> 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<List<CellInfo>>() {
@Override
public void onSuccess(List<CellInfo> 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<CellInfo> 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<CellInfo> 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<List<CellInfo>> mCallback;
private final TelephonyManager mTelephonyManager;
NetworkScanSyncTask(
TelephonyManager telephonyManager, SettableFuture<List<CellInfo>> 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<CellInfo> 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()))));
}
}
}
}

View File

@@ -0,0 +1,506 @@
/*
* 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.network.telephony;
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.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.settings.R;
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;
import androidx.preference.ListPreference;
import androidx.preference.Preference;
/**
* "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<CellInfo> mCellInfoList;
private CellInfo mCellInfo;
private int mSubId;
private TelephonyManager mTelephonyManager;
private NetworkScanHelper mNetworkScanHelper;
private NetworkOperators mNetworkOperators;
private List<String> 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<Void, Void, List<String>>() {
@Override
protected List<String> doInBackground(Void... voids) {
String[] forbiddenPlmns = telephonyManager.getForbiddenPlmns();
return forbiddenPlmns != null ? Arrays.asList(forbiddenPlmns) : null;
}
@Override
protected void onPostExecute(List<String> 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<CellInfo> results = (List<CellInfo>) 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 NetworkScanHelper.NetworkScanCallback() {
public void onResults(List<CellInfo> 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();
}
//TODO(b/114749736): move this logic to preference controller
protected void onDialogClosed(boolean 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<CharSequence> networkEntriesList = new ArrayList<>();
List<CharSequence> 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<CellInfo> 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<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
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);
}
}

View File

@@ -0,0 +1,557 @@
/*
* 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.network.telephony;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.SearchIndexableResource;
import android.telephony.AccessNetworkConstants;
import android.telephony.CellIdentity;
import android.telephony.CellInfo;
import android.telephony.NetworkRegistrationState;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.View;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.telephony.OperatorInfo;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.utils.ThreadUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* "Choose network" settings UI for the Phone app.
*/
//TODO(b/115429509): Add test for this file once b/115429509 is not blocked anymore
@SearchIndexable(forTarget = SearchIndexable.ALL & ~SearchIndexable.ARC)
public class NetworkSelectSettings extends DashboardFragment {
private static final String TAG = "NetworkSelectSetting";
private static final boolean DBG = true;
public static final String KEY_SUBSCRIPTION_ID = "subscription_id";
private static final int EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE = 1;
private static final int EVENT_NETWORK_SCAN_RESULTS = 2;
private static final int EVENT_NETWORK_SCAN_ERROR = 3;
private static final int EVENT_NETWORK_SCAN_COMPLETED = 4;
private static final String PREF_KEY_CONNECTED_NETWORK_OPERATOR =
"connected_network_operator_preference";
private static final String PREF_KEY_NETWORK_OPERATORS = "network_operators_preference";
// used to add/remove NetworkOperatorsPreference.
private PreferenceCategory mNetworkOperatorsPreferences;
// used to add/remove connected NetworkOperatorPreference.
private PreferenceCategory mConnectedNetworkOperatorsPreference;
// manage the progress bar on the top of the page.
private View mProgressHeader;
private Preference mStatusMessagePreference;
private List<CellInfo> mCellInfoList;
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private NetworkOperatorPreference mSelectedNetworkOperatorPreference;
private TelephonyManager mTelephonyManager;
private List<String> mForbiddenPlmns;
//Flag indicating whether we have called bind on the service.
private boolean mShouldUnbind;
private final Runnable mUpdateNetworkOperatorsRunnable = () -> {
updateNetworkOperatorsPreferenceCategory();
};
/**
* Create a new instance of this fragment.
*/
public static NetworkSelectSettings newInstance(int subId) {
Bundle args = new Bundle();
args.putInt(KEY_SUBSCRIPTION_ID, subId);
NetworkSelectSettings
fragment = new NetworkSelectSettings();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle icicle) {
if (DBG) logd("onCreate");
super.onCreate(icicle);
mSubId = getArguments().getInt(KEY_SUBSCRIPTION_ID);
mConnectedNetworkOperatorsPreference =
(PreferenceCategory) findPreference(PREF_KEY_CONNECTED_NETWORK_OPERATOR);
mNetworkOperatorsPreferences =
(PreferenceCategory) findPreference(PREF_KEY_NETWORK_OPERATORS);
mStatusMessagePreference = new Preference(getContext());
mSelectedNetworkOperatorPreference = null;
mTelephonyManager = TelephonyManager.from(getContext()).createForSubscriptionId(mSubId);
setRetainInstance(true);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mProgressHeader = setPinnedHeaderView(R.layout.wifi_progress_header)
.findViewById(R.id.progress_bar_animation);
setProgressBarVisible(false);
forceConfigConnectedNetworkOperatorsPreferenceCategory();
}
@Override
public void onStart() {
super.onStart();
new AsyncTask<Void, Void, List<String>>() {
@Override
protected List<String> doInBackground(Void... voids) {
String[] forbiddenPlmns = mTelephonyManager.getForbiddenPlmns();
return forbiddenPlmns != null ? Arrays.asList(forbiddenPlmns) : null;
}
@Override
protected void onPostExecute(List<String> result) {
mForbiddenPlmns = result;
bindNetworkQueryService();
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
/**
* Invoked on each preference click in this hierarchy, overrides
* PreferenceActivity's implementation. Used to make sure we track the
* preference click events.
* Since the connected network operator is either faked (when no data connection) or already
* connected, we do not allow user to click the connected network operator.
*/
@Override
public boolean onPreferenceTreeClick(Preference preference) {
if (DBG) logd("User clicked the screen");
stopNetworkQuery();
setProgressBarVisible(false);
if (preference instanceof NetworkOperatorPreference) {
// Refresh the last selected item in case users reselect network.
if (mSelectedNetworkOperatorPreference != null) {
mSelectedNetworkOperatorPreference.setSummary("");
}
mSelectedNetworkOperatorPreference = (NetworkOperatorPreference) preference;
CellInfo cellInfo = mSelectedNetworkOperatorPreference.getCellInfo();
if (DBG) logd("User click a NetworkOperatorPreference: " + cellInfo.toString());
// Send metrics event
MetricsLogger.action(getContext(),
MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK);
// Connect to the network
if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
if (DBG) {
logd("Connect to the network: " + CellInfoUtil.getNetworkTitle(cellInfo));
}
// Set summary as "Connecting" to the selected network.
mSelectedNetworkOperatorPreference.setSummary(R.string.network_connecting);
// Set summary as "Disconnected" to the previously connected network
if (mConnectedNetworkOperatorsPreference.getPreferenceCount() > 0) {
NetworkOperatorPreference connectedNetworkOperator = (NetworkOperatorPreference)
(mConnectedNetworkOperatorsPreference.getPreference(0));
if (!CellInfoUtil.getNetworkTitle(cellInfo).equals(
CellInfoUtil.getNetworkTitle(connectedNetworkOperator.getCellInfo()))) {
connectedNetworkOperator.setSummary(R.string.network_disconnected);
}
}
OperatorInfo operatorInfo = CellInfoUtil.getOperatorInfoFromCellInfo(cellInfo);
if (DBG) logd("manually selected network operator: " + operatorInfo.toString());
ThreadUtils.postOnBackgroundThread(() -> {
Message msg = mHandler.obtainMessage(EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE);
msg.obj = mTelephonyManager.setNetworkSelectionModeManual(
operatorInfo.getOperatorNumeric(), true /* persistSelection */);
msg.sendToTarget();
});
setProgressBarVisible(true);
return true;
} else {
loge("Error selecting network. Subscription Id is invalid.");
mSelectedNetworkOperatorPreference = null;
return false;
}
} else {
getPreferenceScreen().setEnabled(false);
return false;
}
}
@Override
public void onStop() {
super.onStop();
if (DBG) logd("onStop");
getView().removeCallbacks(mUpdateNetworkOperatorsRunnable);
stopNetworkQuery();
// Unbind the NetworkQueryService
unbindNetworkQueryService();
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.choose_network;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public int getMetricsCategory() {
//TODO(b/114749736): add metrics id for this page
return 0;
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE:
if (DBG) logd("network selection done: hide the progress header");
setProgressBarVisible(false);
boolean isSuccessed = (boolean) msg.obj;
if (isSuccessed) {
if (DBG) logd("manual network selection: succeeded! ");
// Set summary as "Connected" to the selected network.
mSelectedNetworkOperatorPreference.setSummary(R.string.network_connected);
} else {
if (DBG) logd("manual network selection: failed! ");
updateNetworkSelection();
// Set summary as "Couldn't connect" to the selected network.
mSelectedNetworkOperatorPreference.setSummary(
R.string.network_could_not_connect);
}
break;
case EVENT_NETWORK_SCAN_RESULTS:
List<CellInfo> results = aggregateCellInfoList((List<CellInfo>) msg.obj);
mCellInfoList = new ArrayList<>(results);
if (DBG) logd("after aggregate: " + mCellInfoList.toString());
if (mCellInfoList != null && mCellInfoList.size() != 0) {
updateNetworkOperators();
} else {
addMessagePreference(R.string.empty_networks_list);
}
break;
case EVENT_NETWORK_SCAN_ERROR:
int error = msg.arg1;
if (DBG) logd("error while querying available networks " + error);
stopNetworkQuery();
addMessagePreference(R.string.network_query_error);
break;
case EVENT_NETWORK_SCAN_COMPLETED:
stopNetworkQuery();
if (DBG) logd("scan complete");
if (mCellInfoList == null) {
// In case the scan timeout before getting any results
addMessagePreference(R.string.empty_networks_list);
}
break;
}
return;
}
};
private void updateNetworkOperators() {
if (DBG) logd("updateNetworkOperators");
if (getActivity() != null) {
final View view = getView();
final Handler handler = view.getHandler();
if (handler != null && handler.hasCallbacks(mUpdateNetworkOperatorsRunnable)) {
return;
}
view.post(mUpdateNetworkOperatorsRunnable);
}
}
/**
* Update the currently available network operators list, which only contains the unregistered
* network operators. So if the device has no data and the network operator in the connected
* network operator category shows "Disconnected", it will also exist in the available network
* operator category for user to select. On the other hand, if the device has data and the
* network operator in the connected network operator category shows "Connected", it will not
* exist in the available network category.
*/
private void updateNetworkOperatorsPreferenceCategory() {
mNetworkOperatorsPreferences.removeAll();
configConnectedNetworkOperatorsPreferenceCategory();
for (int index = 0; index < mCellInfoList.size(); index++) {
if (!mCellInfoList.get(index).isRegistered()) {
NetworkOperatorPreference pref = new NetworkOperatorPreference(
mCellInfoList.get(index), getContext(), mForbiddenPlmns);
pref.setKey(CellInfoUtil.getNetworkTitle(mCellInfoList.get(index)));
pref.setOrder(index);
mNetworkOperatorsPreferences.addPreference(pref);
}
}
}
/**
* Config the connected network operator preference when the page was created. When user get
* into this page, the device might or might not have data connection.
* - If the device has data:
* 1. use {@code ServiceState#getNetworkRegistrationStates()} to get the currently
* registered cellIdentity, wrap it into a CellInfo;
* 2. set the signal strength level as strong;
* 3. use {@link TelephonyManager#getNetworkOperatorName()} to get the title of the
* previously connected network operator, since the CellIdentity got from step 1 only has
* PLMN.
* - If the device has no data, we will remove the connected network operators list from the
* screen.
*/
private void forceConfigConnectedNetworkOperatorsPreferenceCategory() {
if (DBG) logd("Force config ConnectedNetworkOperatorsPreferenceCategory");
if (mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED) {
// Try to get the network registration states
ServiceState ss = mTelephonyManager.getServiceState();
List<NetworkRegistrationState> networkList =
ss.getNetworkRegistrationStates(AccessNetworkConstants.TransportType.WWAN);
if (networkList == null || networkList.size() == 0) {
loge("getNetworkRegistrationStates return null");
// Remove the connected network operators category
removeConnectedNetworkOperatorPreference();
return;
}
CellIdentity cellIdentity = networkList.get(0).getCellIdentity();
CellInfo cellInfo = CellInfoUtil.wrapCellInfoWithCellIdentity(cellIdentity);
if (cellInfo != null) {
if (DBG) logd("Currently registered cell: " + cellInfo.toString());
NetworkOperatorPreference pref =
new NetworkOperatorPreference(cellInfo, getContext(), mForbiddenPlmns);
pref.setTitle(mTelephonyManager.getNetworkOperatorName());
pref.setSummary(R.string.network_connected);
// Update the signal strength icon, since the default signalStrength value would be
// zero (it would be quite confusing why the connected network has no signal)
pref.setIcon(NetworkOperatorPreference.NUMBER_OF_LEVELS - 1);
mConnectedNetworkOperatorsPreference.addPreference(pref);
} else {
loge("Invalid CellIfno: " + cellInfo.toString());
// Remove the connected network operators category
removeConnectedNetworkOperatorPreference();
}
} else {
if (DBG) logd("No currently registered cell");
// Remove the connected network operators category
removeConnectedNetworkOperatorPreference();
}
}
/**
* Configure the ConnectedNetworkOperatorsPreferenceCategory. The category only need to be
* configured if the category is currently empty or the operator network title of the previous
* connected network is different from the new one.
*/
private void configConnectedNetworkOperatorsPreferenceCategory() {
if (DBG) logd("config ConnectedNetworkOperatorsPreferenceCategory");
// Remove the category if the CellInfo list is empty or does not have registered cell.
if (mCellInfoList.size() == 0) {
if (DBG) logd("empty cellinfo list");
removeConnectedNetworkOperatorPreference();
}
CellInfo connectedNetworkOperator = null;
for (CellInfo cellInfo : mCellInfoList) {
if (cellInfo.isRegistered()) {
connectedNetworkOperator = cellInfo;
break;
}
}
if (connectedNetworkOperator == null) {
if (DBG) logd("no registered network");
removeConnectedNetworkOperatorPreference();
return;
}
// config the category if it is empty.
if (mConnectedNetworkOperatorsPreference.getPreferenceCount() == 0) {
if (DBG) logd("ConnectedNetworkSelectList is empty, add one");
addConnectedNetworkOperatorPreference(connectedNetworkOperator);
return;
}
NetworkOperatorPreference previousConnectedNetworkOperator = (NetworkOperatorPreference)
(mConnectedNetworkOperatorsPreference.getPreference(0));
// config the category if the network title of the previous connected network is different
// from the new one.
String cTitle = CellInfoUtil.getNetworkTitle(connectedNetworkOperator);
String pTitle = CellInfoUtil.getNetworkTitle(
previousConnectedNetworkOperator.getCellInfo());
if (!cTitle.equals(pTitle)) {
if (DBG) logd("reconfig the category: connected network changed");
addConnectedNetworkOperatorPreference(connectedNetworkOperator);
return;
}
if (DBG) logd("same network operator is connected, only refresh the connected network");
// Otherwise same network operator is connected, only refresh the connected network
// operator preference (first and the only one in this category).
((NetworkOperatorPreference) mConnectedNetworkOperatorsPreference.getPreference(0))
.refresh();
return;
}
/**
* Creates a Preference for the given {@link CellInfo} and adds it to the
* {@link #mConnectedNetworkOperatorsPreference}.
*/
private void addConnectedNetworkOperatorPreference(CellInfo cellInfo) {
if (DBG) logd("addConnectedNetworkOperatorPreference");
// Remove the current ConnectedNetworkOperatorsPreference
removeConnectedNetworkOperatorPreference();
final NetworkOperatorPreference pref =
new NetworkOperatorPreference(cellInfo, getContext(), mForbiddenPlmns);
pref.setSummary(R.string.network_connected);
mConnectedNetworkOperatorsPreference.addPreference(pref);
PreferenceScreen preferenceScreen = getPreferenceScreen();
preferenceScreen.addPreference(mConnectedNetworkOperatorsPreference);
}
/** Removes all preferences and hide the {@link #mConnectedNetworkOperatorsPreference}. */
private void removeConnectedNetworkOperatorPreference() {
mConnectedNetworkOperatorsPreference.removeAll();
PreferenceScreen preferenceScreen = getPreferenceScreen();
preferenceScreen.removePreference(mConnectedNetworkOperatorsPreference);
}
protected void setProgressBarVisible(boolean visible) {
if (mProgressHeader != null) {
mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
private void addMessagePreference(int messageId) {
if (DBG) logd("remove callback");
getView().removeCallbacks(mUpdateNetworkOperatorsRunnable);
setProgressBarVisible(false);
if (DBG) logd("addMessagePreference");
mStatusMessagePreference.setTitle(messageId);
removeConnectedNetworkOperatorPreference();
mNetworkOperatorsPreferences.removeAll();
mNetworkOperatorsPreferences.addPreference(mStatusMessagePreference);
}
/**
* The Scan results may contains several cell infos with different radio technologies and signal
* strength for one network operator. Aggregate the CellInfoList by retaining only the cell info
* with the strongest signal strength.
*/
private List<CellInfo> aggregateCellInfoList(List<CellInfo> cellInfoList) {
if (DBG) logd("before aggregate: " + cellInfoList.toString());
Map<String, CellInfo> map = new HashMap<>();
for (CellInfo cellInfo : cellInfoList) {
String plmn = CellInfoUtil.getOperatorInfoFromCellInfo(cellInfo).getOperatorNumeric();
if (cellInfo.isRegistered() || !map.containsKey(plmn)) {
map.put(plmn, cellInfo);
} else {
if (map.get(plmn).isRegistered()
|| map.get(plmn).getCellSignalStrength().getLevel()
> cellInfo.getCellSignalStrength().getLevel()) {
// Skip if the stored cellInfo is registered or has higher signal strength level
continue;
}
// Otherwise replace it with the new CellInfo
map.put(plmn, cellInfo);
}
}
return new ArrayList<>(map.values());
}
private void loadNetworksList() {
if (DBG) logd("load networks list...");
setProgressBarVisible(true);
//TODO(b/114749736): load network list once b/115401728 is done
}
private void bindNetworkQueryService() {
if (DBG) logd("bindNetworkQueryService");
//TODO(b/114749736): bind service/manager once b/115401728 is done
mShouldUnbind = true;
}
private void unbindNetworkQueryService() {
if (DBG) logd("unbindNetworkQueryService");
if (mShouldUnbind) {
if (DBG) logd("mShouldUnbind is true");
// unbind the service.
//TODO(b/114749736): unbind service/manager once b/115401728 is done
mShouldUnbind = false;
}
}
private void updateNetworkSelection() {
if (DBG) logd("Update notification about no service of user selected operator");
//TODO(b/114749736): update network selection once b/115429509 is done
}
private void stopNetworkQuery() {
// Stop the network query process
//TODO(b/114749736): stop service/manager query once b/115401728 is done
}
private void logd(String msg) {
Log.d(TAG, msg);
}
private void loge(String msg) {
Log.e(TAG, msg);
}
public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
new BaseSearchIndexProvider() {
@Override
public List<SearchIndexableResource> getXmlResourcesToIndex(Context context,
boolean enabled) {
final ArrayList<SearchIndexableResource> result = new ArrayList<>();
final SearchIndexableResource sir = new SearchIndexableResource(context);
sir.xmlResId = R.xml.choose_network;
result.add(sir);
return result;
}
};
}

View File

@@ -0,0 +1,101 @@
/*
* 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.network.telephony;
import android.app.AlertDialog;
import android.app.Dialog;
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;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import com.android.settings.R;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
/**
* 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 InstrumentedDialogFragment 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);
Fragment fragment = getTargetFragment();
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(getContext());
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 int getMetricsCategory() {
//TODO(b/114749736): add category for roaming dialog
return 0;
}
@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);
}
}
}