Merge "Do NOT disable IMS settings for non-call SIM during a call"
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 1d0138b..874c412 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -141,6 +141,8 @@
private static final int EVENT_FETCH_CARRIER_TIMEOUT = 15;
// SubscriptionInfoUpdater has finished updating the sub for the carrier config.
private static final int EVENT_SUBSCRIPTION_INFO_UPDATED = 16;
+ // Multi-SIM config changed.
+ private static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 17;
private static final int BIND_TIMEOUT_MILLIS = 30000;
@@ -174,28 +176,21 @@
public void handleMessage(Message msg) {
final int phoneId = msg.arg1;
logWithLocalLog("mHandler: " + msg.what + " phoneId: " + phoneId);
+ if (!SubscriptionManager.isValidPhoneId(phoneId)
+ && msg.what != EVENT_MULTI_SIM_CONFIG_CHANGED) {
+ return;
+ }
switch (msg.what) {
case EVENT_CLEAR_CONFIG:
{
- /* Ignore clear configuration request if device is being shutdown. */
- Phone phone = PhoneFactory.getPhone(phoneId);
- if (phone != null) {
- if (phone.isShuttingDown()) {
- break;
- }
- }
-
- mConfigFromDefaultApp[phoneId] = null;
- mConfigFromCarrierApp[phoneId] = null;
- mServiceConnection[phoneId] = null;
- broadcastConfigChangedIntent(phoneId, false);
+ clearConfigForPhone(phoneId, true);
break;
}
case EVENT_SYSTEM_UNLOCKED:
{
- for (int i = 0; i < TelephonyManager.from(mContext)
- .getSupportedModemCount(); ++i) {
+ for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount();
+ ++i) {
// When user unlock device, we should only try to send broadcast again if we
// have sent it before unlock. This will avoid we try to load carrier config
// when SIM is still loading when unlock happens.
@@ -212,8 +207,7 @@
// Only update if there are cached config removed to avoid updating config for
// unrelated packages.
if (clearCachedConfigForPackage(carrierPackageName)) {
- int numPhones = TelephonyManager.from(mContext)
- .getSupportedModemCount();
+ int numPhones = TelephonyManager.from(mContext).getActiveModemCount();
for (int i = 0; i < numPhones; ++i) {
updateConfigForPhoneId(i);
}
@@ -496,6 +490,9 @@
case EVENT_SUBSCRIPTION_INFO_UPDATED:
broadcastConfigChangedIntent(phoneId);
break;
+ case EVENT_MULTI_SIM_CONFIG_CHANGED:
+ onMultiSimConfigChanged();
+ break;
}
}
}
@@ -555,6 +552,23 @@
}
}
+ private void clearConfigForPhone(int phoneId, boolean sendBroadcast) {
+ /* Ignore clear configuration request if device is being shutdown. */
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null) {
+ if (phone.isShuttingDown()) {
+ return;
+ }
+ }
+
+ mConfigFromDefaultApp[phoneId] = null;
+ mConfigFromCarrierApp[phoneId] = null;
+ mServiceConnection[phoneId] = null;
+ mHasSentConfigChange[phoneId] = false;
+
+ if (sendBroadcast) broadcastConfigChangedIntent(phoneId, false);
+ }
+
private void notifySubscriptionInfoUpdater(int phoneId) {
String configPackagename;
PersistableBundle configToSend;
@@ -572,6 +586,7 @@
// mOverrideConfigs is for testing. And it will override current configs.
PersistableBundle config = mOverrideConfigs[phoneId];
if (config != null) {
+ configToSend = new PersistableBundle(configToSend);
configToSend.putAll(config);
}
@@ -919,6 +934,13 @@
mHandler.sendMessage(mHandler.obtainMessage(EVENT_DO_FETCH_DEFAULT, phoneId, -1));
}
+ private void onMultiSimConfigChanged() {
+ for (int i = TelephonyManager.from(mContext).getActiveModemCount();
+ i < mConfigFromDefaultApp.length; i++) {
+ clearConfigForPhone(i, false);
+ }
+ }
+
@Override
public @NonNull PersistableBundle getConfigForSubId(int subId, String callingPackage) {
if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
diff --git a/src/com/android/phone/PhoneDisplayMessage.java b/src/com/android/phone/PhoneDisplayMessage.java
index 2b86a61..199fbb8 100644
--- a/src/com/android/phone/PhoneDisplayMessage.java
+++ b/src/com/android/phone/PhoneDisplayMessage.java
@@ -78,10 +78,11 @@
sDisplayMessageDialog.getWindow().setType(
WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
sDisplayMessageDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
sDisplayMessageDialog.show();
- PhoneGlobals.getInstance().wakeUpScreen();
}
/**
@@ -91,6 +92,9 @@
if (DBG) log("Dissmissing Display Info Record...");
if (sDisplayMessageDialog != null) {
+ sDisplayMessageDialog.getWindow().clearFlags(
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
sDisplayMessageDialog.dismiss();
sDisplayMessageDialog = null;
}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index de71373..06ae81c 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -37,12 +37,11 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
-import android.os.SystemClock;
import android.os.SystemProperties;
-import android.os.UpdateLock;
import android.os.UserManager;
import android.preference.PreferenceManager;
import android.provider.Settings;
+import android.sysprop.TelephonyProperties;
import android.telecom.TelecomManager;
import android.telephony.AnomalyReporter;
import android.telephony.CarrierConfigManager;
@@ -187,8 +186,6 @@
private PowerManager.WakeLock mPartialWakeLock;
private KeyguardManager mKeyguardManager;
- private UpdateLock mUpdateLock;
-
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private final LocalLog mDataRoamingNotifLog = new LocalLog(50);
@@ -348,12 +345,6 @@
mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
- // Get UpdateLock to suppress system-update related events (e.g. dialog show-up)
- // during phone calls.
- mUpdateLock = new UpdateLock("phone");
-
- if (DBG) Log.d(LOG_TAG, "onCreate: mUpdateLock: " + mUpdateLock);
-
// Create the CallerInfoCache singleton, which remembers custom ring tone and
// send-to-voicemail settings.
//
@@ -506,19 +497,6 @@
mPUKEntryProgressDialog = dialog;
}
- /**
- * If we are not currently keeping the screen on, then poke the power
- * manager to wake up the screen for the user activity timeout duration.
- */
- /* package */ void wakeUpScreen() {
- synchronized (this) {
- if (mWakeState == WakeState.SLEEP) {
- if (DBG) Log.d(LOG_TAG, "pulse screen lock");
- mPowerManager.wakeUp(SystemClock.uptimeMillis(), "android.phone:WAKE");
- }
- }
- }
-
KeyguardManager getKeyguardManager() {
return mKeyguardManager;
}
@@ -569,7 +547,7 @@
Log.i(LOG_TAG, "Turning radio off - airplane");
Settings.Global.putInt(context.getContentResolver(), Settings.Global.CELL_ON,
PhoneConstants.CELL_OFF_DUE_TO_AIRPLANE_MODE_FLAG);
- SystemProperties.set("persist.radio.airplane_mode_on", "1");
+ TelephonyProperties.airplane_mode_on(true); // true means int value 1
Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT, 0);
PhoneUtils.setRadioPower(false);
}
@@ -580,7 +558,7 @@
PhoneConstants.CELL_ON_FLAG);
Settings.Global.putInt(getContentResolver(), Settings.Global.ENABLE_CELLULAR_ON_BOOT,
1);
- SystemProperties.set("persist.radio.airplane_mode_on", "0");
+ TelephonyProperties.airplane_mode_on(false); // false means int value 0
PhoneUtils.setRadioPower(true);
}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 571be34..0940954 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -62,6 +62,7 @@
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
+import android.telephony.Annotation.ApnType;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellIdentity;
@@ -94,7 +95,6 @@
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.data.ApnSetting;
-import android.telephony.data.ApnSetting.ApnType;
import android.telephony.emergency.EmergencyNumber;
import android.telephony.gsm.GsmCellLocation;
import android.telephony.ims.ImsException;
@@ -1077,11 +1077,13 @@
try {
if (ar.exception != null) {
Log.e(LOG_TAG, "Exception retrieving CellInfo=" + ar.exception);
- cb.onError(TelephonyManager.CellInfoCallback.ERROR_MODEM_ERROR,
- new android.os.ParcelableException(ar.exception));
+ cb.onError(
+ TelephonyManager.CellInfoCallback.ERROR_MODEM_ERROR,
+ ar.exception.getClass().getName(),
+ ar.exception.toString());
} else if (ar.result == null) {
Log.w(LOG_TAG, "Timeout Waiting for CellInfo!");
- cb.onError(TelephonyManager.CellInfoCallback.ERROR_TIMEOUT, null);
+ cb.onError(TelephonyManager.CellInfoCallback.ERROR_TIMEOUT, null, null);
} else {
// use the result as returned
cb.onCellInfo((List<CellInfo>) ar.result);
@@ -6421,7 +6423,8 @@
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver resultReceiver)
throws RemoteException {
- (new TelephonyShellCommand(this)).exec(this, in, out, err, args, callback, resultReceiver);
+ (new TelephonyShellCommand(this, getDefaultPhone().getContext()))
+ .exec(this, in, out, err, args, callback, resultReceiver);
}
/**
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index d8d1717..428c006 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -16,10 +16,15 @@
package com.android.phone;
+import android.content.Context;
import android.os.Binder;
+import android.os.Build;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.ShellCommand;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.emergency.EmergencyNumber;
import android.util.Log;
@@ -29,6 +34,9 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeSet;
/**
* Takes actions based on the adb commands given by "adb shell cmd phone ...". Be careful, no
@@ -46,6 +54,7 @@
private static final String IMS_SUBCOMMAND = "ims";
private static final String NUMBER_VERIFICATION_SUBCOMMAND = "numverify";
private static final String EMERGENCY_NUMBER_TEST_MODE = "emergency-number-test-mode";
+ private static final String CARRIER_CONFIG_SUBCOMMAND = "cc";
private static final String IMS_SET_CARRIER_SERVICE = "set-ims-service";
private static final String IMS_GET_CARRIER_SERVICE = "get-ims-service";
@@ -59,11 +68,60 @@
private static final String NUMBER_VERIFICATION_OVERRIDE_PACKAGE = "override-package";
private static final String NUMBER_VERIFICATION_FAKE_CALL = "fake-call";
+ private static final String CC_GET_VALUE = "get-value";
+ private static final String CC_SET_VALUE = "set-value";
+ private static final String CC_CLEAR_VALUES = "clear-values";
+
// Take advantage of existing methods that already contain permissions checks when possible.
private final ITelephony mInterface;
- public TelephonyShellCommand(ITelephony binder) {
+ private SubscriptionManager mSubscriptionManager;
+ private CarrierConfigManager mCarrierConfigManager;
+
+ private enum CcType {
+ BOOLEAN, DOUBLE, DOUBLE_ARRAY, INT, INT_ARRAY, LONG, LONG_ARRAY, STRING,
+ STRING_ARRAY, UNKNOWN
+ }
+
+ // Maps carrier config keys to type. It is possible to infer the type for most carrier config
+ // keys by looking at the end of the string which usually tells the type.
+ // For instance: "xxxx_string", "xxxx_string_array", etc.
+ // The carrier config keys in this map does not follow this convention. It is therefore not
+ // possible to infer the type for these keys by looking at the string.
+ private static final Map<String, CcType> CC_TYPE_MAP = new HashMap<String, CcType>() {{
+ put(CarrierConfigManager.Gps.KEY_A_GLONASS_POS_PROTOCOL_SELECT_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_GPS_LOCK_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_LPP_PROFILE_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_NFW_PROXY_APPS_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_ES_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_HOST_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_MODE_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_PORT_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_SUPL_VER_STRING, CcType.STRING);
+ put(CarrierConfigManager.Gps.KEY_USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL_STRING,
+ CcType.STRING);
+ put(CarrierConfigManager.KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+ CcType.STRING_ARRAY);
+ put(CarrierConfigManager.KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+ CcType.STRING_ARRAY);
+ put(CarrierConfigManager.KEY_CARRIER_CALL_SCREENING_APP_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_EMAIL_GATEWAY_NUMBER_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_HTTP_PARAMS_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_NAI_SUFFIX_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_UA_PROF_TAG_NAME_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_UA_PROF_URL_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_MMS_USER_AGENT_STRING, CcType.STRING);
+ put(CarrierConfigManager.KEY_RATCHET_RAT_FAMILIES, CcType.STRING_ARRAY);
+ }
+ };
+
+ public TelephonyShellCommand(ITelephony binder, Context context) {
mInterface = binder;
+ mCarrierConfigManager =
+ (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ mSubscriptionManager = (SubscriptionManager)
+ context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
}
@Override
@@ -80,6 +138,9 @@
return handleNumberVerificationCommand();
case EMERGENCY_NUMBER_TEST_MODE:
return handleEmergencyNumberTestModeCommand();
+ case CARRIER_CONFIG_SUBCOMMAND: {
+ return handleCcCommand();
+ }
default: {
return handleDefaultCommands(cmd);
}
@@ -96,8 +157,11 @@
pw.println(" IMS Commands.");
pw.println(" emergency-number-test-mode");
pw.println(" Emergency Number Test Mode Commands.");
+ pw.println(" cc");
+ pw.println(" Carrier Config Commands.");
onHelpIms();
onHelpEmergencyNumber();
+ onHelpCc();
}
private void onHelpIms() {
@@ -152,6 +216,33 @@
pw.println(" -p: get the full emergency number list in the test mode.");
}
+ private void onHelpCc() {
+ PrintWriter pw = getOutPrintWriter();
+ pw.println("Carrier Config Commands:");
+ pw.println(" cc get-value [-s SLOT_ID] [KEY]");
+ pw.println(" Print carrier config values.");
+ pw.println(" Options are:");
+ pw.println(" -s: The SIM slot ID to read carrier config value for. If no option");
+ pw.println(" is specified, it will choose the default voice SIM slot.");
+ pw.println(" KEY: The key to the carrier config value to print. All values are printed");
+ pw.println(" if KEY is not specified.");
+ pw.println(" cc set-value [-s SLOT_ID] KEY [NEW_VALUE]");
+ pw.println(" Set carrier config KEY to NEW_VALUE.");
+ pw.println(" Options are:");
+ pw.println(" -s: The SIM slot ID to set carrier config value for. If no option");
+ pw.println(" is specified, it will choose the default voice SIM slot.");
+ pw.println(" NEW_VALUE specifies the new value for carrier config KEY. Null will be");
+ pw.println(" used if NEW_VALUE is not set. Strings should be encapsulated with");
+ pw.println(" quotation marks. Spaces needs to be escaped. Example: \"Hello\\ World\"");
+ pw.println(" Separate items in arrays with space . Example: \"item1\" \"item2\"");
+ pw.println(" cc clear-values [-s SLOT_ID]");
+ pw.println(" Clear all carrier override values that has previously been set");
+ pw.println(" with set-value");
+ pw.println(" Options are:");
+ pw.println(" -s: The SIM slot ID to clear carrier config values for. If no option");
+ pw.println(" is specified, it will choose the default voice SIM slot.");
+ }
+
private int handleImsCommand() {
String arg = getNextArg();
if (arg == null) {
@@ -477,9 +568,486 @@
return slotId;
}
+ // Get the subId from argument SLOT_ID if it was provided. Otherwise use the default
+ // subscription.
+ private int getSubIdFromArgumentSlotId(String tag) {
+ PrintWriter errPw = getErrPrintWriter();
+ int subId = SubscriptionManager.getDefaultSubscriptionId();
+ String opt;
+
+ while ((opt = getNextOption()) != null) {
+ switch (opt) {
+ case "-s": {
+ try {
+ subId = slotStringToSubId(tag, getNextArgRequired());
+ } catch (IllegalArgumentException e) {
+ // Missing slot id
+ errPw.println(tag + "SLOT_ID expected after -s.");
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ break;
+ }
+ default: {
+ errPw.println(tag + "Unknown option " + opt);
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ }
+ }
+ return subId;
+ }
+
+ private int slotStringToSubId(String tag, String slotString) {
+ int slotId = -1;
+ try {
+ slotId = Integer.parseInt(slotString);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println(tag + slotString + " is not a valid SLOT_ID.");
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+
+ SubscriptionInfo subInfo =
+ mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(slotId);
+ if (subInfo == null) {
+ getErrPrintWriter().println(tag + "No subscription found in slot " + slotId + ".");
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ }
+ return subInfo.getSubscriptionId();
+ }
+
private boolean checkShellUid() {
// adb can run as root or as shell, depending on whether the device is rooted.
return Binder.getCallingUid() == Process.SHELL_UID
|| Binder.getCallingUid() == Process.ROOT_UID;
}
+
+ private int handleCcCommand() {
+ // Verify that the user is allowed to run the command. Only allowed in rooted device in a
+ // non user build.
+ if (Binder.getCallingUid() != Process.ROOT_UID || Build.IS_USER) {
+ getErrPrintWriter().println("cc: Permission denied.");
+ return -1;
+ }
+
+ String arg = getNextArg();
+ if (arg == null) {
+ onHelpCc();
+ return 0;
+ }
+
+ switch (arg) {
+ case CC_GET_VALUE: {
+ return handleCcGetValue();
+ }
+ case CC_SET_VALUE: {
+ return handleCcSetValue();
+ }
+ case CC_CLEAR_VALUES: {
+ return handleCcClearValues();
+ }
+ default: {
+ getErrPrintWriter().println("cc: Unknown argument: " + arg);
+ }
+ }
+ return -1;
+ }
+
+ // cc get-value
+ private int handleCcGetValue() {
+ PrintWriter errPw = getErrPrintWriter();
+ String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_GET_VALUE + ": ";
+ String key = null;
+
+ // Get the subId from the SLOT_ID-argument.
+ int subId = getSubIdFromArgumentSlotId(tag);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ errPw.println(tag + "No valid subscription found.");
+ return -1;
+ }
+
+ // Get bundle containing all carrier configuration values.
+ PersistableBundle bundle = mCarrierConfigManager.getConfigForSubId(subId);
+ if (bundle == null) {
+ errPw.println(tag + "No carrier config values found for subId " + subId + ".");
+ return -1;
+ }
+
+ // Get the key.
+ key = getNextArg();
+ if (key != null) {
+ // A key was provided. Verify if it is a valid key
+ if (!bundle.containsKey(key)) {
+ errPw.println(tag + key + " is not a valid key.");
+ return -1;
+ }
+
+ // Print the carrier config value for key.
+ getOutPrintWriter().println(ccValueToString(key, getType(tag, key, bundle), bundle));
+ } else {
+ // No key provided. Show all values.
+ // Iterate over a sorted list of all carrier config keys and print them.
+ TreeSet<String> sortedSet = new TreeSet<String>(bundle.keySet());
+ for (String k : sortedSet) {
+ getOutPrintWriter().println(ccValueToString(k, getType(tag, k, bundle), bundle));
+ }
+ }
+ return 0;
+ }
+
+ // cc set-value
+ private int handleCcSetValue() {
+ PrintWriter errPw = getErrPrintWriter();
+ String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_SET_VALUE + ": ";
+
+ // Get the subId from the SLOT_ID-argument.
+ int subId = getSubIdFromArgumentSlotId(tag);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ errPw.println(tag + "No valid subscription found.");
+ return -1;
+ }
+
+ // Get bundle containing all current carrier configuration values.
+ PersistableBundle originalValues = mCarrierConfigManager.getConfigForSubId(subId);
+ if (originalValues == null) {
+ errPw.println(tag + "No carrier config values found for subId " + subId + ".");
+ return -1;
+ }
+
+ // Get the key.
+ String key = getNextArg();
+ if (key == null || key.equals("")) {
+ errPw.println(tag + "KEY is missing");
+ return -1;
+ }
+
+ // Verify if the key is valid
+ if (!originalValues.containsKey(key)) {
+ errPw.println(tag + key + " is not a valid key.");
+ return -1;
+ }
+
+ // Remaining arguments is a list of new values. Add them all into an ArrayList.
+ ArrayList<String> valueList = new ArrayList<String>();
+ while (peekNextArg() != null) {
+ valueList.add(getNextArg());
+ }
+
+ // Find the type of the carrier config value
+ CcType type = getType(tag, key, originalValues);
+ if (type == CcType.UNKNOWN) {
+ errPw.println(tag + "ERROR: Not possible to override key with unknown type.");
+ return -1;
+ }
+
+ // Create an override bundle containing the key and value that should be overriden.
+ PersistableBundle overrideBundle = getOverrideBundle(tag, type, key, valueList);
+ if (overrideBundle == null) {
+ return -1;
+ }
+
+ // Override the value
+ mCarrierConfigManager.overrideConfig(subId, overrideBundle);
+
+ // Find bundle containing all new carrier configuration values after the override.
+ PersistableBundle newValues = mCarrierConfigManager.getConfigForSubId(subId);
+ if (newValues == null) {
+ errPw.println(tag + "No carrier config values found for subId " + subId + ".");
+ return -1;
+ }
+
+ // Print the original and new value.
+ String originalValueString = ccValueToString(key, type, originalValues);
+ String newValueString = ccValueToString(key, type, newValues);
+ getOutPrintWriter().println("Previous value: \n" + originalValueString);
+ getOutPrintWriter().println("New value: \n" + newValueString);
+
+ return 0;
+ }
+
+ // cc clear-values
+ private int handleCcClearValues() {
+ PrintWriter errPw = getErrPrintWriter();
+ String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_CLEAR_VALUES + ": ";
+
+ // Get the subId from the SLOT_ID-argument.
+ int subId = getSubIdFromArgumentSlotId(tag);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ errPw.println(tag + "No valid subscription found.");
+ return -1;
+ }
+
+ // Clear all values that has previously been set.
+ mCarrierConfigManager.overrideConfig(subId, null);
+ getOutPrintWriter()
+ .println("All previously set carrier config override values has been cleared");
+ return 0;
+ }
+
+ private CcType getType(String tag, String key, PersistableBundle bundle) {
+ // Find the type by checking the type of the current value stored in the bundle.
+ Object value = bundle.get(key);
+
+ if (CC_TYPE_MAP.containsKey(key)) {
+ return CC_TYPE_MAP.get(key);
+ } else if (value != null) {
+ if (value instanceof Boolean) {
+ return CcType.BOOLEAN;
+ } else if (value instanceof Double) {
+ return CcType.DOUBLE;
+ } else if (value instanceof double[]) {
+ return CcType.DOUBLE_ARRAY;
+ } else if (value instanceof Integer) {
+ return CcType.INT;
+ } else if (value instanceof int[]) {
+ return CcType.INT_ARRAY;
+ } else if (value instanceof Long) {
+ return CcType.LONG;
+ } else if (value instanceof long[]) {
+ return CcType.LONG_ARRAY;
+ } else if (value instanceof String) {
+ return CcType.STRING;
+ } else if (value instanceof String[]) {
+ return CcType.STRING_ARRAY;
+ }
+ } else {
+ // Current value was null and can therefore not be used in order to find the type.
+ // Check the name of the key to infer the type. This check is not needed for primitive
+ // data types (boolean, double, int and long), since they can not be null.
+ if (key.endsWith("double_array")) {
+ return CcType.DOUBLE_ARRAY;
+ }
+ if (key.endsWith("int_array")) {
+ return CcType.INT_ARRAY;
+ }
+ if (key.endsWith("long_array")) {
+ return CcType.LONG_ARRAY;
+ }
+ if (key.endsWith("string")) {
+ return CcType.STRING;
+ }
+ if (key.endsWith("string_array") || key.endsWith("strings")) {
+ return CcType.STRING_ARRAY;
+ }
+ }
+
+ // Not possible to infer the type by looking at the current value or the key.
+ PrintWriter errPw = getErrPrintWriter();
+ errPw.println(tag + "ERROR: " + key + " has unknown type.");
+ return CcType.UNKNOWN;
+ }
+
+ private String ccValueToString(String key, CcType type, PersistableBundle bundle) {
+ String result;
+ StringBuilder valueString = new StringBuilder();
+ String typeString = type.toString();
+ Object value = bundle.get(key);
+
+ if (value == null) {
+ valueString.append("null");
+ } else {
+ switch (type) {
+ case DOUBLE_ARRAY: {
+ // Format the string representation of the int array as value1 value2......
+ double[] valueArray = (double[]) value;
+ for (int i = 0; i < valueArray.length; i++) {
+ if (i != 0) {
+ valueString.append(" ");
+ }
+ valueString.append(valueArray[i]);
+ }
+ break;
+ }
+ case INT_ARRAY: {
+ // Format the string representation of the int array as value1 value2......
+ int[] valueArray = (int[]) value;
+ for (int i = 0; i < valueArray.length; i++) {
+ if (i != 0) {
+ valueString.append(" ");
+ }
+ valueString.append(valueArray[i]);
+ }
+ break;
+ }
+ case LONG_ARRAY: {
+ // Format the string representation of the int array as value1 value2......
+ long[] valueArray = (long[]) value;
+ for (int i = 0; i < valueArray.length; i++) {
+ if (i != 0) {
+ valueString.append(" ");
+ }
+ valueString.append(valueArray[i]);
+ }
+ break;
+ }
+ case STRING: {
+ valueString.append("\"" + value.toString() + "\"");
+ break;
+ }
+ case STRING_ARRAY: {
+ // Format the string representation of the string array as "value1" "value2"....
+ String[] valueArray = (String[]) value;
+ for (int i = 0; i < valueArray.length; i++) {
+ if (i != 0) {
+ valueString.append(" ");
+ }
+ if (valueArray[i] != null) {
+ valueString.append("\"" + valueArray[i] + "\"");
+ } else {
+ valueString.append("null");
+ }
+ }
+ break;
+ }
+ default: {
+ valueString.append(value.toString());
+ }
+ }
+ }
+ return String.format("%-70s %-15s %s", key, typeString, valueString);
+ }
+
+ private PersistableBundle getOverrideBundle(String tag, CcType type, String key,
+ ArrayList<String> valueList) {
+ PrintWriter errPw = getErrPrintWriter();
+ PersistableBundle bundle = new PersistableBundle();
+
+ // First verify that a valid number of values has been provided for the type.
+ switch (type) {
+ case BOOLEAN:
+ case DOUBLE:
+ case INT:
+ case LONG: {
+ if (valueList.size() != 1) {
+ errPw.println(tag + "Expected 1 value for type " + type
+ + ". Found: " + valueList.size());
+ return null;
+ }
+ break;
+ }
+ case STRING: {
+ if (valueList.size() > 1) {
+ errPw.println(tag + "Expected 0 or 1 values for type " + type
+ + ". Found: " + valueList.size());
+ return null;
+ }
+ break;
+ }
+ }
+
+ // Parse the value according to type and add it to the Bundle.
+ switch (type) {
+ case BOOLEAN: {
+ if ("true".equalsIgnoreCase(valueList.get(0))) {
+ bundle.putBoolean(key, true);
+ } else if ("false".equalsIgnoreCase(valueList.get(0))) {
+ bundle.putBoolean(key, false);
+ } else {
+ errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
+ return null;
+ }
+ break;
+ }
+ case DOUBLE: {
+ try {
+ bundle.putDouble(key, Double.parseDouble(valueList.get(0)));
+ } catch (NumberFormatException nfe) {
+ // Not a valid double
+ errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
+ return null;
+ }
+ break;
+ }
+ case DOUBLE_ARRAY: {
+ double[] valueDoubleArray = null;
+ if (valueList.size() > 0) {
+ valueDoubleArray = new double[valueList.size()];
+ for (int i = 0; i < valueList.size(); i++) {
+ try {
+ valueDoubleArray[i] = Double.parseDouble(valueList.get(i));
+ } catch (NumberFormatException nfe) {
+ // Not a valid double
+ errPw.println(
+ tag + "Unable to parse " + valueList.get(i) + " as a double.");
+ return null;
+ }
+ }
+ }
+ bundle.putDoubleArray(key, valueDoubleArray);
+ break;
+ }
+ case INT: {
+ try {
+ bundle.putInt(key, Integer.parseInt(valueList.get(0)));
+ } catch (NumberFormatException nfe) {
+ // Not a valid integer
+ errPw.println(tag + "Unable to parse " + valueList.get(0) + " as an " + type);
+ return null;
+ }
+ break;
+ }
+ case INT_ARRAY: {
+ int[] valueIntArray = null;
+ if (valueList.size() > 0) {
+ valueIntArray = new int[valueList.size()];
+ for (int i = 0; i < valueList.size(); i++) {
+ try {
+ valueIntArray[i] = Integer.parseInt(valueList.get(i));
+ } catch (NumberFormatException nfe) {
+ // Not a valid integer
+ errPw.println(tag
+ + "Unable to parse " + valueList.get(i) + " as an integer.");
+ return null;
+ }
+ }
+ }
+ bundle.putIntArray(key, valueIntArray);
+ break;
+ }
+ case LONG: {
+ try {
+ bundle.putLong(key, Long.parseLong(valueList.get(0)));
+ } catch (NumberFormatException nfe) {
+ // Not a valid long
+ errPw.println(tag + "Unable to parse " + valueList.get(0) + " as a " + type);
+ return null;
+ }
+ break;
+ }
+ case LONG_ARRAY: {
+ long[] valueLongArray = null;
+ if (valueList.size() > 0) {
+ valueLongArray = new long[valueList.size()];
+ for (int i = 0; i < valueList.size(); i++) {
+ try {
+ valueLongArray[i] = Long.parseLong(valueList.get(i));
+ } catch (NumberFormatException nfe) {
+ // Not a valid long
+ errPw.println(
+ tag + "Unable to parse " + valueList.get(i) + " as a long");
+ return null;
+ }
+ }
+ }
+ bundle.putLongArray(key, valueLongArray);
+ break;
+ }
+ case STRING: {
+ String value = null;
+ if (valueList.size() > 0) {
+ value = valueList.get(0);
+ }
+ bundle.putString(key, value);
+ break;
+ }
+ case STRING_ARRAY: {
+ String[] valueStringArray = null;
+ if (valueList.size() > 0) {
+ valueStringArray = new String[valueList.size()];
+ valueList.toArray(valueStringArray);
+ }
+ bundle.putStringArray(key, valueStringArray);
+ break;
+ }
+ }
+ return bundle;
+ }
}
diff --git a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
index 7c7b75d..4c85818 100644
--- a/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
+++ b/src/com/android/phone/euicc/EuiccUiDispatcherActivity.java
@@ -153,7 +153,7 @@
mPackageManager.revokeDefaultPermissionsFromLuiApps(luiAppsArray, getUserId());
} catch (RemoteException e) {
Log.e(TAG, "Failed to revoke LUI app permissions.");
- throw e.rethrowAsRuntimeException();
+ throw new RuntimeException(e);
}
}
diff --git a/src/com/android/services/telephony/RadioOnHelper.java b/src/com/android/services/telephony/RadioOnHelper.java
index 741a6c5..981dc96 100644
--- a/src/com/android/services/telephony/RadioOnHelper.java
+++ b/src/com/android/services/telephony/RadioOnHelper.java
@@ -49,13 +49,19 @@
}
private void setupListeners() {
- if (mListeners != null) {
- return;
+ if (mListeners == null) {
+ mListeners = new ArrayList<>(2);
}
- mListeners = new ArrayList<>(2);
- for (int i = 0; i < TelephonyManager.getDefault().getSupportedModemCount(); i++) {
+ int activeModems = TelephonyManager.from(mContext).getActiveModemCount();
+ // Add new listeners if active modem count increased.
+ while (mListeners.size() < activeModems) {
mListeners.add(new RadioOnStateListener());
}
+ // Clean up listeners if active modem count decreased.
+ while (mListeners.size() > activeModems) {
+ mListeners.get(mListeners.size() - 1).cleanup();
+ mListeners.remove(mListeners.size() - 1);
+ }
}
/**
* Starts the "turn on radio" sequence. This is the (single) external API of the
@@ -76,7 +82,7 @@
mCallback = callback;
mInProgressListeners.clear();
mIsRadioOnCallingEnabled = false;
- for (int i = 0; i < TelephonyManager.getDefault().getSupportedModemCount(); i++) {
+ for (int i = 0; i < TelephonyManager.from(mContext).getActiveModemCount(); i++) {
Phone phone = PhoneFactory.getPhone(i);
if (phone == null) {
continue;
diff --git a/src/com/android/services/telephony/RadioOnStateListener.java b/src/com/android/services/telephony/RadioOnStateListener.java
index 729f6a9..52bd9cf 100644
--- a/src/com/android/services/telephony/RadioOnStateListener.java
+++ b/src/com/android/services/telephony/RadioOnStateListener.java
@@ -231,7 +231,7 @@
* Note we don't call this method simply after a successful call to placeCall(), since it's
* still possible the call will disconnect very quickly with an OUT_OF_SERVICE error.
*/
- private void cleanup() {
+ public void cleanup() {
Log.d(this, "cleanup()");
// This will send a failure call back if callback has yet to be invoked. If the callback