Merge "Update path for BasicShellCommandHandler"
diff --git a/src/com/android/phone/ImsRcsController.java b/src/com/android/phone/ImsRcsController.java
index e415122..58f3646 100644
--- a/src/com/android/phone/ImsRcsController.java
+++ b/src/com/android/phone/ImsRcsController.java
@@ -22,6 +22,7 @@
import android.os.ServiceSpecificException;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
+import android.telephony.ims.DelegateRequest;
import android.telephony.ims.ImsException;
import android.telephony.ims.RcsUceAdapter.PublishState;
import android.telephony.ims.RegistrationManager;
@@ -30,6 +31,9 @@
import android.telephony.ims.aidl.IImsRegistrationCallback;
import android.telephony.ims.aidl.IRcsUceControllerCallback;
import android.telephony.ims.aidl.IRcsUcePublishStateCallback;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
@@ -408,6 +412,40 @@
}
}
+ @Override
+ public void createSipDelegate(int subId, DelegateRequest request,
+ ISipDelegateConnectionStateCallback delegateState,
+ ISipDelegateMessageCallback delegateMessage) {
+ enforceModifyPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ SipTransportController transport = getRcsFeatureController(subId).getFeature(
+ SipTransportController.class);
+ if (transport == null) {
+ throw new ServiceSpecificException(ImsException.CODE_ERROR_UNSUPPORTED_OPERATION,
+ "This subscription does not support the creation of SIP delegates");
+ }
+ try {
+ transport.createSipDelegate(subId, request, delegateState, delegateMessage);
+ } catch (ImsException e) {
+ throw new ServiceSpecificException(e.getCode(), e.getMessage());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void destroySipDelegate(int subId, ISipDelegate connection, int reason) {
+ enforceModifyPermission();
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // Do nothing yet, we do not support this API yet.
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
/**
* Registers for updates to the RcsFeature connection through the IImsServiceFeatureCallback
* callback.
diff --git a/src/com/android/phone/ImsUtil.java b/src/com/android/phone/ImsUtil.java
index 38936ec..0825cfb 100644
--- a/src/com/android/phone/ImsUtil.java
+++ b/src/com/android/phone/ImsUtil.java
@@ -25,6 +25,8 @@
import com.android.ims.ImsConfig;
import com.android.ims.ImsManager;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.imsphone.ImsPhone;
public class ImsUtil {
private static final String LOG_TAG = ImsUtil.class.getSimpleName();
@@ -128,6 +130,13 @@
return false;
}
+ // Do not promote WFC if in roaming and WFC roaming not allowed.
+ // WFC roaming setting is not modifiable, so its value is decided by the default value
+ // chosen by the carrier, hence it really means if the carrier supports WFC roaming.
+ if (getLastKnownRoamingState(phoneId) && !imsManager.isWfcRoamingEnabledByUser()) {
+ return false;
+ }
+
ConnectivityManager cm =
(ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm != null) {
@@ -152,4 +161,13 @@
}
return subId;
}
+
+ private static boolean getLastKnownRoamingState(int phoneId) {
+ try {
+ ImsPhone imsPhone = (ImsPhone) (PhoneFactory.getPhone(phoneId).getImsPhone());
+ return imsPhone.getRoamingState();
+ } catch (NullPointerException | ClassCastException e) {
+ return false;
+ }
+ }
}
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index c2dece5..fe4a0ba 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -536,7 +536,11 @@
int slotId = SubscriptionManager.getSlotIndex(subId);
resId = (slotId == 0) ? R.drawable.stat_sys_phone_call_forward_sub1
: R.drawable.stat_sys_phone_call_forward_sub2;
- notificationTitle = subInfo.getDisplayName().toString();
+ if (subInfo.getDisplayName() != null) {
+ notificationTitle = subInfo.getDisplayName().toString();
+ } else {
+ notificationTitle = mContext.getString(R.string.labelCF);
+ }
} else {
notificationTitle = mContext.getString(R.string.labelCF);
}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 4ee4141..4316705 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -65,6 +65,7 @@
import android.telecom.TelecomManager;
import android.telephony.Annotation.ApnType;
import android.telephony.CallForwardingInfo;
+import android.telephony.CarrierBandwidth;
import android.telephony.CarrierConfigManager;
import android.telephony.CarrierRestrictionRules;
import android.telephony.CellIdentity;
@@ -293,6 +294,8 @@
private static final int EVENT_ENABLE_NR_DUAL_CONNECTIVITY_DONE = 92;
private static final int CMD_IS_NR_DUAL_CONNECTIVITY_ENABLED = 93;
private static final int EVENT_IS_NR_DUAL_CONNECTIVITY_ENABLED_DONE = 94;
+ private static final int CMD_GET_CDMA_SUBSCRIPTION_MODE = 95;
+ private static final int EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE = 96;
// Parameters of select command.
private static final int SELECT_COMMAND = 0xA4;
@@ -1382,11 +1385,27 @@
request.result = ar.exception == null;
notifyRequester(request);
break;
+ case CMD_GET_CDMA_SUBSCRIPTION_MODE:
+ request = (MainThreadRequest) msg.obj;
+ onCompleted = obtainMessage(EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE, request);
+ getPhoneFromRequest(request).queryCdmaSubscriptionMode(onCompleted);
+ break;
+ case EVENT_GET_CDMA_SUBSCRIPTION_MODE_DONE:
+ ar = (AsyncResult) msg.obj;
+ request = (MainThreadRequest) ar.userObj;
+ if (ar.exception != null) {
+ request.result = TelephonyManager.CDMA_SUBSCRIPTION_RUIM_SIM;
+ } else {
+ request.result = ((int[]) ar.result)[0];
+ }
+ notifyRequester(request);
+ break;
case CMD_SET_CDMA_SUBSCRIPTION_MODE:
request = (MainThreadRequest) msg.obj;
onCompleted = obtainMessage(EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE, request);
int subscriptionMode = (int) request.argument;
- getPhoneFromRequest(request).setCdmaSubscription(subscriptionMode, onCompleted);
+ getPhoneFromRequest(request).setCdmaSubscriptionMode(
+ subscriptionMode, onCompleted);
break;
case EVENT_SET_CDMA_SUBSCRIPTION_MODE_DONE:
ar = (AsyncResult) msg.obj;
@@ -5873,6 +5892,28 @@
}
/**
+ * get carrier bandwidth per primary and secondary carrier
+ * @param subId subscription id of the sim card
+ * @return CarrierBandwidth with bandwidth of both primary and secondary carrier..
+ */
+ @Override
+ public CarrierBandwidth getCarrierBandwidth(int subId) {
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "isNRDualConnectivityEnabled");
+ WorkSource workSource = getWorkSource(Binder.getCallingUid());
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ CarrierBandwidth carrierBandwidth =
+ getPhoneFromSubId(subId).getCarrierBandwidth();
+ if (DBG) log("getCarrierBandwidth: " + carrierBandwidth);
+ return carrierBandwidth;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ /**
* Get the effective allowed network types on the device.
* This API will return an intersection of allowed network types for all reasons,
* including the configuration done through setAllowedNetworkTypes
@@ -6314,12 +6355,10 @@
final Phone phone = getPhone(subId);
UiccCard card = phone == null ? null : phone.getUiccCard();
if (card == null) {
- loge("getIccId: No UICC");
return null;
}
String iccId = card.getIccId();
if (TextUtils.isEmpty(iccId)) {
- loge("getIccId: ICC ID is null or empty.");
return null;
}
return iccId;
@@ -8111,6 +8150,20 @@
}
@Override
+ public int getCdmaSubscriptionMode(int subId) {
+ TelephonyPermissions
+ .enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ mApp, subId, "getCdmaSubscriptionMode");
+
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ return (int) sendRequest(CMD_GET_CDMA_SUBSCRIPTION_MODE, null /* argument */, subId);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
public boolean setCdmaSubscriptionMode(int subId, int mode) {
TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
mApp, subId, "setCdmaSubscriptionMode");
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 1caa8e7..09c83c0 100755
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -96,6 +96,9 @@
private static final int MSG_CONFERENCE_MERGE_FAILED = 6;
private static final int MSG_SUPP_SERVICE_NOTIFY = 7;
+ // the threshold used to compare mAudioCodecBitrateKbps and mAudioCodecBandwidth.
+ private static final float THRESHOLD = 0.01f;
+
/**
* Mappings from {@link com.android.internal.telephony.Connection} extras keys to their
* equivalents defined in {@link android.telecom.Connection}.
@@ -1496,7 +1499,8 @@
}
}
- private void refreshCodecType() {
+ private void refreshCodec() {
+ boolean changed = false;
Bundle newExtras = getExtras();
if (newExtras == null) {
newExtras = new Bundle();
@@ -1512,6 +1516,31 @@
Connection.AUDIO_CODEC_NONE);
if (newCodecType != oldCodecType) {
newExtras.putInt(Connection.EXTRA_AUDIO_CODEC, newCodecType);
+ changed = true;
+ }
+ if (isImsConnection()) {
+ float newBitrate = getOriginalConnection().getAudioCodecBitrateKbps();
+ float oldBitrate = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, 0.0f);
+ if (Math.abs(newBitrate - oldBitrate) > THRESHOLD) {
+ newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS, newBitrate);
+ changed = true;
+ }
+
+ float newBandwidth = getOriginalConnection().getAudioCodecBandwidthKhz();
+ float oldBandwidth = newExtras.getFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ,
+ 0.0f);
+ if (Math.abs(newBandwidth - oldBandwidth) > THRESHOLD) {
+ newExtras.putFloat(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ, newBandwidth);
+ changed = true;
+ }
+ } else {
+ ArrayList<String> toRemove = new ArrayList<>();
+ toRemove.add(Connection.EXTRA_AUDIO_CODEC_BITRATE_KBPS);
+ toRemove.add(Connection.EXTRA_AUDIO_CODEC_BANDWIDTH_KHZ);
+ removeTelephonyExtras(toRemove);
+ }
+
+ if (changed) {
putTelephonyExtras(newExtras);
}
}
@@ -2195,7 +2224,7 @@
updateAddress();
updateMultiparty();
refreshDisableAddCall();
- refreshCodecType();
+ refreshCodec();
}
/**
diff --git a/src/com/android/services/telephony/rcs/SipTransportController.java b/src/com/android/services/telephony/rcs/SipTransportController.java
index da5374a..813834a 100644
--- a/src/com/android/services/telephony/rcs/SipTransportController.java
+++ b/src/com/android/services/telephony/rcs/SipTransportController.java
@@ -17,8 +17,15 @@
package com.android.services.telephony.rcs;
import android.content.Context;
+import android.telephony.ims.DelegateRequest;
import android.telephony.ims.ImsException;
import android.telephony.ims.ImsService;
+import android.telephony.ims.aidl.ISipDelegate;
+import android.telephony.ims.aidl.ISipDelegateConnectionStateCallback;
+import android.telephony.ims.aidl.ISipDelegateMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionMessageCallback;
+import android.telephony.ims.stub.DelegateConnectionStateCallback;
+import android.telephony.ims.stub.SipDelegate;
import android.util.Log;
import com.android.ims.RcsFeatureManager;
@@ -100,6 +107,37 @@
}
/**
+ * Optionally create a new {@link SipDelegate} based off of the {@link DelegateRequest} given
+ * based on the state of this controller and associate it with the given callbacks.
+ * <p>
+ * Once the {@link SipDelegate} has been created,
+ * {@link ISipDelegateConnectionStateCallback#onCreated(ISipDelegate)} must be called with
+ * the AIDL instance corresponding to the remote {@link SipDelegate}.
+ * @param subId the subId associated with the request.
+ * @param request The request parameters used to create the {@link SipDelegate}.
+ * @param delegateState The {@link DelegateConnectionStateCallback} Binder connection.
+ * @param delegateMessage The {@link DelegateConnectionMessageCallback} Binder Connection
+ * @throws ImsException if the request to create the {@link SipDelegate} did not complete.
+ */
+ public void createSipDelegate(int subId, DelegateRequest request,
+ ISipDelegateConnectionStateCallback delegateState,
+ ISipDelegateMessageCallback delegateMessage) throws ImsException {
+ // TODO implementation.
+ throw new ImsException("createSipDelegate is not supported yet",
+ ImsException.CODE_ERROR_UNSUPPORTED_OPERATION);
+ }
+
+ /**
+ * The remote IMS application has requested the destruction of an existing {@link SipDelegate}.
+ * @param subId The subId associated with the request.
+ * @param connection The internal Binder connection associated with the {@link SipDelegate}.
+ * @param reason The reason for why the {@link SipDelegate} was destroyed.
+ */
+ public void destroySipDelegate(int subId, ISipDelegate connection, int reason) {
+ // TODO implementation
+ }
+
+ /**
* @return Whether or not SipTransports are supported on the connected ImsService. This can
* change based on the capabilities of the ImsService.
* @throws ImsException if the ImsService connected to this controller is currently down.
diff --git a/tests/src/com/android/services/telephony/TelephonyManagerTest.java b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
new file mode 100644
index 0000000..e9cdc98
--- /dev/null
+++ b/tests/src/com/android/services/telephony/TelephonyManagerTest.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2020 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.services.telephony;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.telephony.emergency.EmergencyNumber;
+import android.test.mock.MockContext;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.telephony.ITelephony;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Unit tests for {@link TelephonyManager}. */
+@RunWith(AndroidJUnit4.class)
+public class TelephonyManagerTest {
+ private static final String PKG_NAME = "Unittest.TelephonyManagerTest";
+ private static final String TAG = "TelephonyManagerTest";
+
+ private ITelephony mMockITelephony;
+ private SubscriptionManager mMockSubscriptionManager;
+ private Context mMockContext;
+
+ private TelephonyManager mTelephonyManager;
+
+ private final MockContext mContext =
+ new MockContext() {
+ @Override
+ public String getOpPackageName() {
+ return PKG_NAME;
+ }
+ @Override
+ public String getAttributionTag() {
+ return TAG;
+ }
+ @Override
+ public Context getApplicationContext() {
+ return null;
+ }
+ @Override
+ public Object getSystemService(String name) {
+ switch (name) {
+ case (Context.TELEPHONY_SUBSCRIPTION_SERVICE) : {
+ return mMockSubscriptionManager;
+ }
+ }
+ return null;
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mMockITelephony = mock(ITelephony.class);
+ mMockSubscriptionManager = mock(SubscriptionManager.class);
+ mMockContext = mock(Context.class);
+ when(mMockContext.getSystemService(eq(Context.TELEPHONY_SUBSCRIPTION_SERVICE)))
+ .thenReturn(mMockSubscriptionManager);
+
+ mTelephonyManager = new TelephonyManager(mContext);
+ TelephonyManager.setupITelephonyForTest(mMockITelephony);
+ TelephonyManager.enableServiceHandleCaching();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ TelephonyManager.setupITelephonyForTest(null);
+ TelephonyManager.disableServiceHandleCaching();
+ }
+
+ @Test
+ public void testGetEmergencyNumberListForCategories() throws Exception {
+ Map<Integer, List<EmergencyNumber>> emergencyNumberLists = new HashMap<>();
+ List<EmergencyNumber> emergencyNumberList = new ArrayList<>();
+ EmergencyNumber number_police = new EmergencyNumber(
+ "911",
+ "us",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ EmergencyNumber number_fire = new EmergencyNumber(
+ "912",
+ "us",
+ "30",
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+ new ArrayList<String>(),
+ EmergencyNumber.EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+ EmergencyNumber.EMERGENCY_CALL_ROUTING_NORMAL);
+ emergencyNumberList.add(number_police);
+ emergencyNumberList.add(number_fire);
+ final int test_sub_id = 1;
+ emergencyNumberLists.put(test_sub_id, emergencyNumberList);
+ when(mMockITelephony.getEmergencyNumberList(eq(PKG_NAME), eq(TAG))).thenReturn(
+ emergencyNumberLists);
+
+ // Call TelephonyManager.getEmergencyNumberList(Category)
+ Map<Integer, List<EmergencyNumber>> returnedEmergencyNumberLists =
+ mTelephonyManager.getEmergencyNumberList(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE);
+
+ // Verify the ITelephony service is called
+ verify(mMockITelephony, times(1)).getEmergencyNumberList(eq(PKG_NAME), eq(TAG));
+
+ // Verify the returned number list contains only the police number(s)
+ List<EmergencyNumber> returnedEmergencyNumberList = returnedEmergencyNumberLists.get(
+ test_sub_id);
+ for (EmergencyNumber num : returnedEmergencyNumberList) {
+ assertTrue(num.isInEmergencyServiceCategories(
+ EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE));
+ }
+ }
+}