Snap for 6001391 from 624edfdec69eeac489e6d52e998575b965aab7c0 to qt-aml-networking-release
Change-Id: I4420c3575e655d2ac1cb5a204ae19d8430d0907a
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 29df8b8..17a1734 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -229,8 +229,8 @@
}
private void listenPhoneState(boolean listen) {
- TelephonyManager telephonyManager =
- (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+ TelephonyManager telephonyManager = getSystemService(TelephonyManager.class)
+ .createForSubscriptionId(mPhone.getSubId());
telephonyManager.listen(mPhoneStateListener, listen
? PhoneStateListener.LISTEN_CALL_STATE : PhoneStateListener.LISTEN_NONE);
}
@@ -239,10 +239,7 @@
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (DBG) log("PhoneStateListener onCallStateChanged: state is " + state);
- // Use TelecomManager#getCallStete instead of 'state' parameter because it needs
- // to check the current state of all phone calls.
- boolean isCallStateIdle =
- mTelecomManager.getCallState() == TelephonyManager.CALL_STATE_IDLE;
+ boolean isCallStateIdle = state == TelephonyManager.CALL_STATE_IDLE;
if (mEnableVideoCalling != null) {
mEnableVideoCalling.setEnabled(isCallStateIdle);
}
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/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
new file mode 100644
index 0000000..d1ff56f
--- /dev/null
+++ b/src/com/android/phone/ImsRcsController.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.phone;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.ServiceManager;
+import android.telephony.ims.aidl.IImsCapabilityCallback;
+import android.telephony.ims.aidl.IImsRcsController;
+import android.telephony.ims.aidl.IRcsUceControllerCallback;
+import android.telephony.ims.feature.RcsFeature;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Implementation of the IImsRcsController interface.
+ */
+public class ImsRcsController extends IImsRcsController.Stub {
+ private static final String TAG = "ImsRcsController";
+
+ /** The singleton instance. */
+ private static ImsRcsController sInstance;
+
+ private PhoneGlobals mApp;
+
+ /**
+ * Initialize the singleton ImsRcsController instance.
+ * This is only done once, at startup, from PhoneApp.onCreate().
+ */
+ static ImsRcsController init(PhoneGlobals app) {
+ synchronized (ImsRcsController.class) {
+ if (sInstance == null) {
+ sInstance = new ImsRcsController(app);
+ } else {
+ Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance);
+ }
+ return sInstance;
+ }
+ }
+
+ /** Private constructor; @see init() */
+ private ImsRcsController(PhoneGlobals app) {
+ Log.i(TAG, "ImsRcsController");
+ mApp = app;
+ ServiceManager.addService(Context.TELEPHONY_IMS_SERVICE, this);
+ }
+
+ @Override
+ public void registerRcsAvailabilityCallback(IImsCapabilityCallback c) {
+ enforceReadPrivilegedPermission("registerRcsAvailabilityCallback");
+ }
+
+ @Override
+ public void unregisterRcsAvailabilityCallback(IImsCapabilityCallback c) {
+ enforceReadPrivilegedPermission("unregisterRcsAvailabilityCallback");
+ }
+
+ @Override
+ public boolean isCapable(int subId,
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+ enforceReadPrivilegedPermission("isCapable");
+ return false;
+ }
+
+ @Override
+ public boolean isAvailable(int subId,
+ @RcsFeature.RcsImsCapabilities.RcsImsCapabilityFlag int capability) {
+ enforceReadPrivilegedPermission("isAvailable");
+ return false;
+ }
+
+ @Override
+ public void requestCapabilities(int subId, List<Uri> contactNumbers,
+ IRcsUceControllerCallback c) {
+ enforceReadPrivilegedPermission("requestCapabilities");
+ }
+
+ @Override
+ public int getUcePublishState(int subId) {
+ enforceReadPrivilegedPermission("getUcePublishState");
+ return -1;
+ }
+
+ @Override
+ public boolean isUceSettingEnabled(int subId) {
+ enforceReadPrivilegedPermission("isUceSettingEnabled");
+ return false;
+ }
+
+ @Override
+ public void setUceSettingEnabled(int subId, boolean isEnabled) {
+ enforceModifyPermission();
+ }
+
+ /**
+ * Make sure either called from same process as self (phone) or IPC caller has read privilege.
+ *
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ private void enforceReadPrivilegedPermission(String message) {
+ mApp.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
+ }
+
+ /**
+ * Make sure the caller has the MODIFY_PHONE_STATE permission.
+ *
+ * @throws SecurityException if the caller does not have the required permission
+ */
+ private void enforceModifyPermission() {
+ mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
+ }
+}
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 c46d09b..b9c6728 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -37,7 +37,6 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.PowerManager;
-import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserManager;
import android.preference.PreferenceManager;
@@ -148,6 +147,7 @@
CallerInfoCache callerInfoCache;
NotificationMgr notificationMgr;
public PhoneInterfaceManager phoneMgr;
+ public ImsRcsController imsRcsController;
CarrierConfigLoader configLoader;
private Phone phoneInEcm;
@@ -354,6 +354,8 @@
phoneMgr = PhoneInterfaceManager.init(this);
+ imsRcsController = ImsRcsController.init(this);
+
configLoader = CarrierConfigLoader.init(this);
// Create the CallNotifier singleton, which handles
@@ -498,19 +500,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;
}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 4f91eff..b5fca03 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -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);
@@ -6475,7 +6477,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/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 6c3f0bf..3625733 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -43,10 +43,7 @@
import android.widget.Toast;
import com.android.internal.telephony.Call;
-import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.CallStateException;
-import android.telephony.CallerInfo;
-import android.telephony.CallerInfoAsyncQuery;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.IccCard;
import com.android.internal.telephony.MmiCode;
@@ -54,10 +51,8 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyCapabilities;
-import com.android.phone.CallGatewayManager.RawGatewayInfo;
import com.android.phone.settings.SuppServicesUiUtil;
-import java.util.Arrays;
import java.util.List;
/**
@@ -131,79 +126,35 @@
}
/**
- * @see placeCall below
- */
- public static int placeCall(Context context, Phone phone, String number, Uri contactRef,
- boolean isEmergencyCall) {
- return placeCall(context, phone, number, contactRef, isEmergencyCall,
- CallGatewayManager.EMPTY_INFO, null);
- }
-
- /**
* Dial the number using the phone passed in.
*
- * If the connection is establised, this method issues a sync call
- * that may block to query the caller info.
- * TODO: Change the logic to use the async query.
- *
* @param context To perform the CallerInfo query.
* @param phone the Phone object.
* @param number to be dialed as requested by the user. This is
* NOT the phone number to connect to. It is used only to build the
* call card and to update the call log. See above for restrictions.
- * @param contactRef that triggered the call. Typically a 'tel:'
- * uri but can also be a 'content://contacts' one.
- * @param isEmergencyCall indicates that whether or not this is an
- * emergency call
- * @param gatewayUri Is the address used to setup the connection, null
- * if not using a gateway
- * @param callGateway Class for setting gateway data on a successful call.
*
* @return either CALL_STATUS_DIALED or CALL_STATUS_FAILED
*/
- public static int placeCall(Context context, Phone phone, String number, Uri contactRef,
- boolean isEmergencyCall, RawGatewayInfo gatewayInfo, CallGatewayManager callGateway) {
- final Uri gatewayUri = gatewayInfo.gatewayUri;
+ public static int placeOtaspCall(Context context, Phone phone, String number) {
+ final Uri gatewayUri = null;
if (VDBG) {
log("placeCall()... number: '" + number + "'"
- + ", GW:'" + gatewayUri + "'"
- + ", contactRef:" + contactRef
- + ", isEmergencyCall: " + isEmergencyCall);
+ + ", GW:'" + gatewayUri + "'");
} else {
log("placeCall()... number: " + toLogSafePhoneNumber(number)
- + ", GW: " + (gatewayUri != null ? "non-null" : "null")
- + ", emergency? " + isEmergencyCall);
+ + ", GW: " + (gatewayUri != null ? "non-null" : "null"));
}
final PhoneGlobals app = PhoneGlobals.getInstance();
boolean useGateway = false;
- if (null != gatewayUri &&
- !isEmergencyCall &&
- PhoneUtils.isRoutableViaGateway(number)) { // Filter out MMI, OTA and other codes.
- useGateway = true;
- }
+ Uri contactRef = null;
int status = CALL_STATUS_DIALED;
Connection connection;
String numberToDial;
- if (useGateway) {
- // TODO: 'tel' should be a constant defined in framework base
- // somewhere (it is in webkit.)
- if (null == gatewayUri || !PhoneAccount.SCHEME_TEL.equals(gatewayUri.getScheme())) {
- Log.e(LOG_TAG, "Unsupported URL:" + gatewayUri);
- return CALL_STATUS_FAILED;
- }
-
- // We can use getSchemeSpecificPart because we don't allow #
- // in the gateway numbers (treated a fragment delim.) However
- // if we allow more complex gateway numbers sequence (with
- // passwords or whatnot) that use #, this may break.
- // TODO: Need to support MMI codes.
- numberToDial = gatewayUri.getSchemeSpecificPart();
- } else {
- numberToDial = number;
- }
+ numberToDial = number;
try {
connection = app.mCM.dial(phone, numberToDial, VideoProfile.STATE_AUDIO_ONLY);
@@ -228,33 +179,6 @@
if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
updateCdmaCallStateOnNewOutgoingCall(app, connection);
}
-
- if (gatewayUri == null) {
- // phone.dial() succeeded: we're now in a normal phone call.
- // attach the URI to the CallerInfo Object if it is there,
- // otherwise just attach the Uri Reference.
- // if the uri does not have a "content" scheme, then we treat
- // it as if it does NOT have a unique reference.
- String content = context.getContentResolver().SCHEME_CONTENT;
- if ((contactRef != null) && (contactRef.getScheme().equals(content))) {
- Object userDataObject = connection.getUserData();
- if (userDataObject == null) {
- connection.setUserData(contactRef);
- } else {
- // TODO: This branch is dead code, we have
- // just created the connection which has
- // no user data (null) by default.
- if (userDataObject instanceof CallerInfo) {
- ((CallerInfo) userDataObject).contactRefUri = contactRef;
- } else {
- ((CallerInfoToken) userDataObject).currentInfo.contactRefUri =
- contactRef;
- }
- }
- }
- }
-
- startGetCallerInfo(context, connection, null, null, gatewayInfo);
}
return status;
@@ -662,533 +586,12 @@
return canceled;
}
- /**
- * Returns the caller-id info corresponding to the specified Connection.
- * (This is just a simple wrapper around CallerInfo.getCallerInfo(): we
- * extract a phone number from the specified Connection, and feed that
- * number into CallerInfo.getCallerInfo().)
- *
- * The returned CallerInfo may be null in certain error cases, like if the
- * specified Connection was null, or if we weren't able to get a valid
- * phone number from the Connection.
- *
- * Finally, if the getCallerInfo() call did succeed, we save the resulting
- * CallerInfo object in the "userData" field of the Connection.
- *
- * NOTE: This API should be avoided, with preference given to the
- * asynchronous startGetCallerInfo API.
- */
- static CallerInfo getCallerInfo(Context context, Connection c) {
- CallerInfo info = null;
-
- if (c != null) {
- //See if there is a URI attached. If there is, this means
- //that there is no CallerInfo queried yet, so we'll need to
- //replace the URI with a full CallerInfo object.
- Object userDataObject = c.getUserData();
- if (userDataObject instanceof Uri) {
- info = CallerInfo.getCallerInfo(context, (Uri) userDataObject);
- if (info != null) {
- c.setUserData(info);
- }
- } else {
- if (userDataObject instanceof CallerInfoToken) {
- //temporary result, while query is running
- info = ((CallerInfoToken) userDataObject).currentInfo;
- } else {
- //final query result
- info = (CallerInfo) userDataObject;
- }
- if (info == null) {
- // No URI, or Existing CallerInfo, so we'll have to make do with
- // querying a new CallerInfo using the connection's phone number.
- String number = c.getAddress();
-
- if (DBG) log("getCallerInfo: number = " + toLogSafePhoneNumber(number));
-
- if (!TextUtils.isEmpty(number)) {
- info = CallerInfo.getCallerInfo(context, number);
- if (info != null) {
- c.setUserData(info);
- }
- }
- }
- }
- }
- return info;
- }
-
- /**
- * Class returned by the startGetCallerInfo call to package a temporary
- * CallerInfo Object, to be superceded by the CallerInfo Object passed
- * into the listener when the query with token mAsyncQueryToken is complete.
- */
- public static class CallerInfoToken {
- /**indicates that there will no longer be updates to this request.*/
- public boolean isFinal;
-
- public CallerInfo currentInfo;
- public CallerInfoAsyncQuery asyncQuery;
- }
-
- /**
- * place a temporary callerinfo object in the hands of the caller and notify
- * caller when the actual query is done.
- */
- static CallerInfoToken startGetCallerInfo(Context context, Connection c,
- CallerInfoAsyncQuery.OnQueryCompleteListener listener, Object cookie,
- RawGatewayInfo info) {
- CallerInfoToken cit;
-
- if (c == null) {
- //TODO: perhaps throw an exception here.
- cit = new CallerInfoToken();
- cit.asyncQuery = null;
- return cit;
- }
-
- Object userDataObject = c.getUserData();
-
- // There are now 3 states for the Connection's userData object:
- //
- // (1) Uri - query has not been executed yet
- //
- // (2) CallerInfoToken - query is executing, but has not completed.
- //
- // (3) CallerInfo - query has executed.
- //
- // In each case we have slightly different behaviour:
- // 1. If the query has not been executed yet (Uri or null), we start
- // query execution asynchronously, and note it by attaching a
- // CallerInfoToken as the userData.
- // 2. If the query is executing (CallerInfoToken), we've essentially
- // reached a state where we've received multiple requests for the
- // same callerInfo. That means that once the query is complete,
- // we'll need to execute the additional listener requested.
- // 3. If the query has already been executed (CallerInfo), we just
- // return the CallerInfo object as expected.
- // 4. Regarding isFinal - there are cases where the CallerInfo object
- // will not be attached, like when the number is empty (caller id
- // blocking). This flag is used to indicate that the
- // CallerInfoToken object is going to be permanent since no
- // query results will be returned. In the case where a query
- // has been completed, this flag is used to indicate to the caller
- // that the data will not be updated since it is valid.
- //
- // Note: For the case where a number is NOT retrievable, we leave
- // the CallerInfo as null in the CallerInfoToken. This is
- // something of a departure from the original code, since the old
- // code manufactured a CallerInfo object regardless of the query
- // outcome. From now on, we will append an empty CallerInfo
- // object, to mirror previous behaviour, and to avoid Null Pointer
- // Exceptions.
-
- if (userDataObject instanceof Uri) {
- // State (1): query has not been executed yet
-
- //create a dummy callerinfo, populate with what we know from URI.
- cit = new CallerInfoToken();
- cit.currentInfo = new CallerInfo();
- cit.asyncQuery = CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context,
- (Uri) userDataObject, sCallerInfoQueryListener, c);
- cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
- cit.isFinal = false;
-
- c.setUserData(cit);
-
- if (DBG) log("startGetCallerInfo: query based on Uri: " + userDataObject);
-
- } else if (userDataObject == null) {
- // No URI, or Existing CallerInfo, so we'll have to make do with
- // querying a new CallerInfo using the connection's phone number.
- String number = c.getAddress();
-
- if (info != null && info != CallGatewayManager.EMPTY_INFO) {
- // Gateway number, the connection number is actually the gateway number.
- // need to lookup via dialed number.
- number = info.trueNumber;
- }
-
- if (DBG) {
- log("PhoneUtils.startGetCallerInfo: new query for phone number...");
- log("- number (address): " + toLogSafePhoneNumber(number));
- log("- c: " + c);
- log("- phone: " + c.getCall().getPhone());
- int phoneType = c.getCall().getPhone().getPhoneType();
- log("- phoneType: " + phoneType);
- switch (phoneType) {
- case PhoneConstants.PHONE_TYPE_NONE: log(" ==> PHONE_TYPE_NONE"); break;
- case PhoneConstants.PHONE_TYPE_GSM: log(" ==> PHONE_TYPE_GSM"); break;
- case PhoneConstants.PHONE_TYPE_IMS: log(" ==> PHONE_TYPE_IMS"); break;
- case PhoneConstants.PHONE_TYPE_CDMA: log(" ==> PHONE_TYPE_CDMA"); break;
- case PhoneConstants.PHONE_TYPE_SIP: log(" ==> PHONE_TYPE_SIP"); break;
- case PhoneConstants.PHONE_TYPE_THIRD_PARTY:
- log(" ==> PHONE_TYPE_THIRD_PARTY");
- break;
- default: log(" ==> Unknown phone type"); break;
- }
- }
-
- cit = new CallerInfoToken();
- cit.currentInfo = new CallerInfo();
-
- // Store CNAP information retrieved from the Connection (we want to do this
- // here regardless of whether the number is empty or not).
- cit.currentInfo.cnapName = c.getCnapName();
- cit.currentInfo.setName(cit.currentInfo.cnapName); // This can still get overwritten
- // by ContactInfo later
- cit.currentInfo.numberPresentation = c.getNumberPresentation();
- cit.currentInfo.namePresentation = c.getCnapNamePresentation();
-
- if (VDBG) {
- log("startGetCallerInfo: number = " + number);
- log("startGetCallerInfo: CNAP Info from FW(1): name="
- + cit.currentInfo.cnapName
- + ", Name/Number Pres=" + cit.currentInfo.numberPresentation);
- }
-
- // handling case where number is null (caller id hidden) as well.
- if (!TextUtils.isEmpty(number)) {
- // Check for special CNAP cases and modify the CallerInfo accordingly
- // to be sure we keep the right information to display/log later
- number = modifyForSpecialCnapCases(context, cit.currentInfo, number,
- cit.currentInfo.numberPresentation);
-
- cit.currentInfo.setPhoneNumber(number);
- // For scenarios where we may receive a valid number from the network but a
- // restricted/unavailable presentation, we do not want to perform a contact query
- // (see note on isFinal above). So we set isFinal to true here as well.
- if (cit.currentInfo.numberPresentation != PhoneConstants.PRESENTATION_ALLOWED) {
- cit.isFinal = true;
- } else {
- if (DBG) log("==> Actually starting CallerInfoAsyncQuery.startQuery()...");
- cit.asyncQuery = CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context,
- number, sCallerInfoQueryListener, c);
- cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
- cit.isFinal = false;
- }
- } else {
- // This is the case where we are querying on a number that
- // is null or empty, like a caller whose caller id is
- // blocked or empty (CLIR). The previous behaviour was to
- // throw a null CallerInfo object back to the user, but
- // this departure is somewhat cleaner.
- if (DBG) log("startGetCallerInfo: No query to start, send trivial reply.");
- cit.isFinal = true; // please see note on isFinal, above.
- }
-
- c.setUserData(cit);
-
- if (DBG) {
- log("startGetCallerInfo: query based on number: " + toLogSafePhoneNumber(number));
- }
-
- } else if (userDataObject instanceof CallerInfoToken) {
- // State (2): query is executing, but has not completed.
-
- // just tack on this listener to the queue.
- cit = (CallerInfoToken) userDataObject;
-
- // handling case where number is null (caller id hidden) as well.
- if (cit.asyncQuery != null) {
- cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
-
- if (DBG) log("startGetCallerInfo: query already running, adding listener: " +
- listener.getClass().toString());
- } else {
- // handling case where number/name gets updated later on by the network
- String updatedNumber = c.getAddress();
-
- if (info != null) {
- // Gateway number, the connection number is actually the gateway number.
- // need to lookup via dialed number.
- updatedNumber = info.trueNumber;
- }
-
- if (DBG) {
- log("startGetCallerInfo: updatedNumber initially = "
- + toLogSafePhoneNumber(updatedNumber));
- }
- if (!TextUtils.isEmpty(updatedNumber)) {
- // Store CNAP information retrieved from the Connection
- cit.currentInfo.cnapName = c.getCnapName();
- // This can still get overwritten by ContactInfo
- cit.currentInfo.setName(cit.currentInfo.cnapName);
- cit.currentInfo.numberPresentation = c.getNumberPresentation();
- cit.currentInfo.namePresentation = c.getCnapNamePresentation();
-
- updatedNumber = modifyForSpecialCnapCases(context, cit.currentInfo,
- updatedNumber, cit.currentInfo.numberPresentation);
-
- cit.currentInfo.setPhoneNumber(updatedNumber);
- if (DBG) {
- log("startGetCallerInfo: updatedNumber="
- + toLogSafePhoneNumber(updatedNumber));
- }
- if (VDBG) {
- log("startGetCallerInfo: CNAP Info from FW(2): name="
- + cit.currentInfo.cnapName
- + ", Name/Number Pres=" + cit.currentInfo.numberPresentation);
- } else if (DBG) {
- log("startGetCallerInfo: CNAP Info from FW(2)");
- }
- // For scenarios where we may receive a valid number from the network but a
- // restricted/unavailable presentation, we do not want to perform a contact query
- // (see note on isFinal above). So we set isFinal to true here as well.
- if (cit.currentInfo.numberPresentation != PhoneConstants.PRESENTATION_ALLOWED) {
- cit.isFinal = true;
- } else {
- cit.asyncQuery = CallerInfoAsyncQuery.startQuery(QUERY_TOKEN, context,
- updatedNumber, sCallerInfoQueryListener, c);
- cit.asyncQuery.addQueryListener(QUERY_TOKEN, listener, cookie);
- cit.isFinal = false;
- }
- } else {
- if (DBG) log("startGetCallerInfo: No query to attach to, send trivial reply.");
- if (cit.currentInfo == null) {
- cit.currentInfo = new CallerInfo();
- }
- // Store CNAP information retrieved from the Connection
- cit.currentInfo.cnapName = c.getCnapName(); // This can still get
- // overwritten by ContactInfo
- cit.currentInfo.setName(cit.currentInfo.cnapName);
- cit.currentInfo.numberPresentation = c.getNumberPresentation();
- cit.currentInfo.namePresentation = c.getCnapNamePresentation();
-
- if (VDBG) {
- log("startGetCallerInfo: CNAP Info from FW(3): name="
- + cit.currentInfo.cnapName
- + ", Name/Number Pres=" + cit.currentInfo.numberPresentation);
- } else if (DBG) {
- log("startGetCallerInfo: CNAP Info from FW(3)");
- }
- cit.isFinal = true; // please see note on isFinal, above.
- }
- }
- } else {
- // State (3): query is complete.
-
- // The connection's userDataObject is a full-fledged
- // CallerInfo instance. Wrap it in a CallerInfoToken and
- // return it to the user.
-
- cit = new CallerInfoToken();
- cit.currentInfo = (CallerInfo) userDataObject;
- cit.asyncQuery = null;
- cit.isFinal = true;
- // since the query is already done, call the listener.
- if (DBG) log("startGetCallerInfo: query already done, returning CallerInfo");
- if (DBG) log("==> cit.currentInfo = " + cit.currentInfo);
- }
- return cit;
- }
-
- /**
- * Static CallerInfoAsyncQuery.OnQueryCompleteListener instance that
- * we use with all our CallerInfoAsyncQuery.startQuery() requests.
- */
- private static final int QUERY_TOKEN = -1;
- static CallerInfoAsyncQuery.OnQueryCompleteListener sCallerInfoQueryListener =
- new CallerInfoAsyncQuery.OnQueryCompleteListener () {
- /**
- * When the query completes, we stash the resulting CallerInfo
- * object away in the Connection's "userData" (where it will
- * later be retrieved by the in-call UI.)
- */
- public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
- if (DBG) log("query complete, updating connection.userdata");
- Connection conn = (Connection) cookie;
-
- // Added a check if CallerInfo is coming from ContactInfo or from Connection.
- // If no ContactInfo, then we want to use CNAP information coming from network
- if (DBG) log("- onQueryComplete: CallerInfo:" + ci);
- if (ci.contactExists || ci.isEmergencyNumber() || ci.isVoiceMailNumber()) {
- // If the number presentation has not been set by
- // the ContactInfo, use the one from the
- // connection.
-
- // TODO: Need a new util method to merge the info
- // from the Connection in a CallerInfo object.
- // Here 'ci' is a new CallerInfo instance read
- // from the DB. It has lost all the connection
- // info preset before the query (see PhoneUtils
- // line 1334). We should have a method to merge
- // back into this new instance the info from the
- // connection object not set by the DB. If the
- // Connection already has a CallerInfo instance in
- // userData, then we could use this instance to
- // fill 'ci' in. The same routine could be used in
- // PhoneUtils.
- if (0 == ci.numberPresentation) {
- ci.numberPresentation = conn.getNumberPresentation();
- }
- } else {
- // No matching contact was found for this number.
- // Return a new CallerInfo based solely on the CNAP
- // information from the network.
-
- CallerInfo newCi = getCallerInfo(null, conn);
-
- // ...but copy over the (few) things we care about
- // from the original CallerInfo object:
- if (newCi != null) {
- newCi.setPhoneNumber(ci.getPhoneNumber()); // To get formatted phone number
- newCi.geoDescription = ci.geoDescription; // To get geo description string
- ci = newCi;
- }
- }
-
- if (DBG) log("==> Stashing CallerInfo " + ci + " into the connection...");
- conn.setUserData(ci);
- }
- };
-
-
- /**
- * Returns a single "name" for the specified given a CallerInfo object.
- * If the name is null, return defaultString as the default value, usually
- * context.getString(R.string.unknown).
- */
- static String getCompactNameFromCallerInfo(CallerInfo ci, Context context) {
- if (DBG) log("getCompactNameFromCallerInfo: info = " + ci);
-
- String compactName = null;
- if (ci != null) {
- if (TextUtils.isEmpty(ci.getName())) {
- // Perform any modifications for special CNAP cases to
- // the phone number being displayed, if applicable.
- compactName = modifyForSpecialCnapCases(context, ci, ci.getPhoneNumber(),
- ci.numberPresentation);
- } else {
- // Don't call modifyForSpecialCnapCases on regular name. See b/2160795.
- compactName = ci.getName();
- }
- }
-
- if ((compactName == null) || (TextUtils.isEmpty(compactName))) {
- // If we're still null/empty here, then check if we have a presentation
- // string that takes precedence that we could return, otherwise display
- // "unknown" string.
- if (ci != null && ci.numberPresentation == PhoneConstants.PRESENTATION_RESTRICTED) {
- compactName = context.getString(R.string.private_num);
- } else if (ci != null && ci.numberPresentation == PhoneConstants.PRESENTATION_PAYPHONE) {
- compactName = context.getString(R.string.payphone);
- } else {
- compactName = context.getString(R.string.unknown);
- }
- }
- if (VDBG) log("getCompactNameFromCallerInfo: compactName=" + compactName);
- return compactName;
- }
-
- static boolean isInEmergencyCall(CallManager cm) {
- Call fgCall = cm.getActiveFgCall();
- // isIdle includes checks for the DISCONNECTING/DISCONNECTED state.
- if(!fgCall.isIdle()) {
- for (Connection cn : fgCall.getConnections()) {
- if (PhoneNumberUtils.isLocalEmergencyNumber(PhoneGlobals.getInstance(),
- cn.getAddress())) {
- return true;
- }
- }
- }
- return false;
- }
//
// Misc UI policy helper functions
//
/**
- * Based on the input CNAP number string,
- * @return _RESTRICTED or _UNKNOWN for all the special CNAP strings.
- * Otherwise, return CNAP_SPECIAL_CASE_NO.
- */
- private static int checkCnapSpecialCases(String n) {
- if (n.equals("PRIVATE") ||
- n.equals("P") ||
- n.equals("RES")) {
- if (DBG) log("checkCnapSpecialCases, PRIVATE string: " + n);
- return PhoneConstants.PRESENTATION_RESTRICTED;
- } else if (n.equals("UNAVAILABLE") ||
- n.equals("UNKNOWN") ||
- n.equals("UNA") ||
- n.equals("U")) {
- if (DBG) log("checkCnapSpecialCases, UNKNOWN string: " + n);
- return PhoneConstants.PRESENTATION_UNKNOWN;
- } else {
- if (DBG) log("checkCnapSpecialCases, normal str. number: " + n);
- return CNAP_SPECIAL_CASE_NO;
- }
- }
-
- /**
- * Handles certain "corner cases" for CNAP. When we receive weird phone numbers
- * from the network to indicate different number presentations, convert them to
- * expected number and presentation values within the CallerInfo object.
- * @param number number we use to verify if we are in a corner case
- * @param presentation presentation value used to verify if we are in a corner case
- * @return the new String that should be used for the phone number
- */
- /* package */ static String modifyForSpecialCnapCases(Context context, CallerInfo ci,
- String number, int presentation) {
- // Obviously we return number if ci == null, but still return number if
- // number == null, because in these cases the correct string will still be
- // displayed/logged after this function returns based on the presentation value.
- if (ci == null || number == null) return number;
-
- if (DBG) {
- log("modifyForSpecialCnapCases: initially, number="
- + toLogSafePhoneNumber(number)
- + ", presentation=" + presentation + " ci " + ci);
- }
-
- // "ABSENT NUMBER" is a possible value we could get from the network as the
- // phone number, so if this happens, change it to "Unknown" in the CallerInfo
- // and fix the presentation to be the same.
- final String[] absentNumberValues =
- context.getResources().getStringArray(R.array.absent_num);
- if (Arrays.asList(absentNumberValues).contains(number)
- && presentation == PhoneConstants.PRESENTATION_ALLOWED) {
- number = context.getString(R.string.unknown);
- ci.numberPresentation = PhoneConstants.PRESENTATION_UNKNOWN;
- }
-
- // Check for other special "corner cases" for CNAP and fix them similarly. Corner
- // cases only apply if we received an allowed presentation from the network, so check
- // if we think we have an allowed presentation, or if the CallerInfo presentation doesn't
- // match the presentation passed in for verification (meaning we changed it previously
- // because it's a corner case and we're being called from a different entry point).
- if (ci.numberPresentation == PhoneConstants.PRESENTATION_ALLOWED
- || (ci.numberPresentation != presentation
- && presentation == PhoneConstants.PRESENTATION_ALLOWED)) {
- int cnapSpecialCase = checkCnapSpecialCases(number);
- if (cnapSpecialCase != CNAP_SPECIAL_CASE_NO) {
- // For all special strings, change number & numberPresentation.
- if (cnapSpecialCase == PhoneConstants.PRESENTATION_RESTRICTED) {
- number = context.getString(R.string.private_num);
- } else if (cnapSpecialCase == PhoneConstants.PRESENTATION_UNKNOWN) {
- number = context.getString(R.string.unknown);
- }
- if (DBG) {
- log("SpecialCnap: number=" + toLogSafePhoneNumber(number)
- + "; presentation now=" + cnapSpecialCase);
- }
- ci.numberPresentation = cnapSpecialCase;
- }
- }
- if (DBG) {
- log("modifyForSpecialCnapCases: returning number string="
- + toLogSafePhoneNumber(number));
- }
- return number;
- }
-
- //
- // Support for 3rd party phone service providers.
- //
-
- /**
* Check if a phone number can be route through a 3rd party
* gateway. The number must be a global phone number in numerical
* form (1-800-666-SEXY won't work).
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/otasp/OtaspActivationService.java b/src/com/android/phone/otasp/OtaspActivationService.java
index 7490880..6ed2ea8 100644
--- a/src/com/android/phone/otasp/OtaspActivationService.java
+++ b/src/com/android/phone/otasp/OtaspActivationService.java
@@ -150,11 +150,9 @@
mPhone.registerForCdmaOtaStatusChange(mHandler, EVENT_CDMA_PROVISION_STATUS_UPDATE, null);
mPhone.registerForPreciseCallStateChanged(mHandler, EVENT_CALL_STATE_CHANGED, null);
logd("startNonInteractiveOtasp: placing call to '" + OTASP_NUMBER + "'...");
- int callStatus = PhoneUtils.placeCall(this,
+ int callStatus = PhoneUtils.placeOtaspCall(this,
getPhone(),
- OTASP_NUMBER,
- null, // contactRef
- false); // isEmergencyCall
+ OTASP_NUMBER);
if (callStatus == PhoneUtils.CALL_STATUS_DIALED) {
if (DBG) logd(" ==> success return from placeCall(): callStatus = " + callStatus);
} else {
diff --git a/src/com/android/services/telephony/ImsConferenceController.java b/src/com/android/services/telephony/ImsConferenceController.java
index 315aaad..b7f7b92 100644
--- a/src/com/android/services/telephony/ImsConferenceController.java
+++ b/src/com/android/services/telephony/ImsConferenceController.java
@@ -147,6 +147,7 @@
mTelephonyConnections.add(connection);
connection.addTelephonyConnectionListener(mTelephonyConnectionListener);
recalculateConference();
+ recalculateConferenceable();
}
/**
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
diff --git a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
index 139f8ac..5c0416b 100644
--- a/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
+++ b/testapps/ImsTestService/src/com/android/phone/testapps/imstestapp/ImsRegistrationActivity.java
@@ -182,7 +182,7 @@
try {
mImsManager = ImsMmTelManager.createForSubscriptionId(
SubscriptionManager.getDefaultVoiceSubscriptionId());
- mImsManager.registerImsRegistrationCallback(mRegistrationCallback, getMainExecutor());
+ mImsManager.registerImsRegistrationCallback(getMainExecutor(), mRegistrationCallback);
} catch (IllegalArgumentException | ImsException e) {
Log.w("ImsCallingActivity", "illegal subscription ID.");
}
diff --git a/tests/src/com/android/phone/CnapTest.java b/tests/src/com/android/phone/CnapTest.java
deleted file mode 100644
index 534d02a..0000000
--- a/tests/src/com/android/phone/CnapTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2009 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.
- */
-
-// Need to be in this package to access package methods.
-package com.android.phone;
-import android.content.Context;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
-import android.telephony.CallerInfo;
-
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_ALLOWED;
-import static com.android.internal.telephony.PhoneConstants.PRESENTATION_UNKNOWN;
-
-// Test suite for the Caller Name Presentation (CNAP) handling.
-// See AndroidManifest.xml how to run these tests.
-public class CnapTest extends AndroidTestCase {
- private static final String TAG = "CnapTest";
- private Context mContext;
- private CallerInfo mCallerInfo;
- // TODO: This string should be loaded from the phone package and
- // not hardcoded.
- private String mUnknown = "Unknown";
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getContext();
- mCallerInfo = new CallerInfo();
- }
-
- // Checks the cnap 'ABSENT NUMBER' is mapped to the unknown presentation.
- @SmallTest
- public void testAbsentNumberIsMappedToUnknown() throws Exception {
- String num = modifyForSpecialCnapCases("ABSENT NUMBER", PRESENTATION_ALLOWED);
- assertIsUnknown(num);
- }
-
- // HELPERS
-
- /**
- * Checks the number and CallerInfo structure indicate the number
- * is unknown.
- */
- private void assertIsUnknown(String number) {
- assertEquals(mUnknown, number);
- assertEquals(PRESENTATION_UNKNOWN, mCallerInfo.numberPresentation);
- // TODO: cnapName and name presentation should be set to
- // unknown. At least I cannot see why it shouldn't be the case
- // assertEquals(mUnknown, mCallerInfo.cnapName);
- // assertEquals(PRESENTATION_UNKNOWN, mCallerInfo.namePresentation);
- }
-
- /**
- * Shorthand for PhoneUtils.modifyForSpecialCnapCases(mContext, mCallerInfo, ...)
- */
- private String modifyForSpecialCnapCases(String number, int presentation) {
- return PhoneUtils.modifyForSpecialCnapCases(
- mContext, mCallerInfo, number, presentation);
- }
-}