Merge "Add required flag to registerReceiver calls"
diff --git a/src/java/com/android/internal/telephony/BaseCommands.java b/src/java/com/android/internal/telephony/BaseCommands.java
index 02530da..972884a 100644
--- a/src/java/com/android/internal/telephony/BaseCommands.java
+++ b/src/java/com/android/internal/telephony/BaseCommands.java
@@ -55,6 +55,7 @@
protected RegistrantList mNetworkStateRegistrants = new RegistrantList();
protected RegistrantList mDataCallListChangedRegistrants = new RegistrantList();
protected RegistrantList mApnUnthrottledRegistrants = new RegistrantList();
+ protected RegistrantList mSlicingConfigChangedRegistrants = new RegistrantList();
@UnsupportedAppUsage
protected RegistrantList mVoiceRadioTechChangedRegistrants = new RegistrantList();
@UnsupportedAppUsage
@@ -319,6 +320,16 @@
}
@Override
+ public void registerForSlicingConfigChanged(Handler h, int what, Object obj) {
+ mSlicingConfigChangedRegistrants.addUnique(h, what, obj);
+ }
+
+ @Override
+ public void unregisterForSlicingConfigChanged(Handler h) {
+ mSlicingConfigChangedRegistrants.remove(h);
+ }
+
+ @Override
public void registerForVoiceRadioTechChanged(Handler h, int what, Object obj) {
mVoiceRadioTechChangedRegistrants.addUnique(h, what, obj);
}
diff --git a/src/java/com/android/internal/telephony/CommandsInterface.java b/src/java/com/android/internal/telephony/CommandsInterface.java
index b09123c..3d3fda4 100644
--- a/src/java/com/android/internal/telephony/CommandsInterface.java
+++ b/src/java/com/android/internal/telephony/CommandsInterface.java
@@ -228,6 +228,10 @@
void registerForApnUnthrottled(Handler h, int what, Object obj);
/** Unregister for apn unthrottled event */
void unregisterForApnUnthrottled(Handler h);
+ /** Register for the slicing config changed event */
+ void registerForSlicingConfigChanged(Handler h, int what, Object obj);
+ /** Unregister for slicing config changed event */
+ void unregisterForSlicingConfigChanged(Handler h);
/** InCall voice privacy notifications */
void registerForInCallVoicePrivacyOn(Handler h, int what, Object obj);
diff --git a/src/java/com/android/internal/telephony/DataIndication.java b/src/java/com/android/internal/telephony/DataIndication.java
index dd1fb8e..a87aa2a 100644
--- a/src/java/com/android/internal/telephony/DataIndication.java
+++ b/src/java/com/android/internal/telephony/DataIndication.java
@@ -19,6 +19,7 @@
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_DATA_CALL_LIST_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_KEEPALIVE_STATUS;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_PCO_DATA;
+import static com.android.internal.telephony.RILConstants.RIL_UNSOL_SLICING_CONFIG_CHANGED;
import static com.android.internal.telephony.RILConstants.RIL_UNSOL_UNTHROTTLE_APN;
import android.hardware.radio.data.IRadioDataIndication;
@@ -27,6 +28,7 @@
import android.telephony.PcoData;
import android.telephony.data.DataCallResponse;
import android.telephony.data.DataProfile;
+import android.telephony.data.NetworkSlicingConfig;
import com.android.internal.telephony.dataconnection.KeepaliveStatus;
@@ -107,4 +109,18 @@
mRil.mApnUnthrottledRegistrants.notifyRegistrants(new AsyncResult(null, response, null));
}
+
+ /**
+ * Current slicing configuration including URSP rules and NSSAIs
+ * (configured, allowed and rejected).
+ * @param indicationType Type of radio indication
+ * @param slicingConfig Current slicing configuration
+ */
+ public void slicingConfigChanged(int indicationType,
+ android.hardware.radio.data.SlicingConfig slicingConfig) throws RemoteException {
+ mRil.processIndication(RIL.DATA_SERVICE, indicationType);
+ if (RIL.RILJ_LOGD) mRil.unsljLogRet(RIL_UNSOL_SLICING_CONFIG_CHANGED, slicingConfig);
+ NetworkSlicingConfig ret = RILUtils.convertHalSlicingConfig(slicingConfig);
+ mRil.mApnUnthrottledRegistrants.notifyRegistrants(new AsyncResult(null, ret, null));
+ }
}
diff --git a/src/java/com/android/internal/telephony/SubscriptionController.java b/src/java/com/android/internal/telephony/SubscriptionController.java
index eeed088..be42c9c 100644
--- a/src/java/com/android/internal/telephony/SubscriptionController.java
+++ b/src/java/com/android/internal/telephony/SubscriptionController.java
@@ -905,6 +905,19 @@
@Override
public List<SubscriptionInfo> getAllSubInfoList(String callingPackage,
String callingFeatureId) {
+ return getAllSubInfoList(callingPackage, callingFeatureId, false);
+ }
+
+ /**
+ * @param callingPackage The package making the IPC.
+ * @param callingFeatureId The feature in the package
+ * @param skipConditionallyRemoveIdentifier if set, skip removing identifier conditionally
+ * @return List of all SubscriptionInfo records in database,
+ * include those that were inserted before, maybe empty but not null.
+ * @hide
+ */
+ public List<SubscriptionInfo> getAllSubInfoList(String callingPackage,
+ String callingFeatureId, boolean skipConditionallyRemoveIdentifier) {
if (VDBG) logd("[getAllSubInfoList]+");
// This API isn't public, so no need to provide a valid subscription ID - we're not worried
@@ -923,9 +936,9 @@
} finally {
Binder.restoreCallingIdentity(identity);
}
- if (subList != null) {
+ if (subList != null && !skipConditionallyRemoveIdentifier) {
if (VDBG) logd("[getAllSubInfoList]- " + subList.size() + " infos return");
- subList.stream().map(
+ subList = subList.stream().map(
subscriptionInfo -> conditionallyRemoveIdentifiers(subscriptionInfo,
callingPackage, callingFeatureId, "getAllSubInfoList"))
.collect(Collectors.toList());
@@ -3903,8 +3916,10 @@
List<SubscriptionInfo> subInfoList;
try {
+ // need to bypass removing identifier check because that will remove the subList without
+ // group id.
subInfoList = getAllSubInfoList(mContext.getOpPackageName(),
- mContext.getAttributionTag());
+ mContext.getAttributionTag(), true);
if (groupUuid == null || subInfoList == null || subInfoList.isEmpty()) {
return new ArrayList<>();
}
diff --git a/src/java/com/android/internal/telephony/TelephonyTester.java b/src/java/com/android/internal/telephony/TelephonyTester.java
index 364b18d..40b2713 100644
--- a/src/java/com/android/internal/telephony/TelephonyTester.java
+++ b/src/java/com/android/internal/telephony/TelephonyTester.java
@@ -249,7 +249,8 @@
filter.addAction(ACTION_TEST_CHANGE_NUMBER);
log("register for intent action=" + ACTION_TEST_CHANGE_NUMBER);
- phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler());
+ phone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone.getHandler(),
+ Context.RECEIVER_EXPORTED);
}
}
diff --git a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
index fd604a0..eb5f866 100644
--- a/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java
@@ -141,13 +141,15 @@
sTestBroadcastReceiver = new CdmaCbTestBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(TEST_ACTION);
- context.registerReceiver(sTestBroadcastReceiver, filter);
+ context.registerReceiver(sTestBroadcastReceiver, filter,
+ Context.RECEIVER_EXPORTED);
}
if (sTestScpBroadcastReceiver == null) {
sTestScpBroadcastReceiver = new CdmaScpTestBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(SCP_TEST_ACTION);
- context.registerReceiver(sTestScpBroadcastReceiver, filter);
+ context.registerReceiver(sTestScpBroadcastReceiver, filter,
+ Context.RECEIVER_EXPORTED);
}
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java b/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java
index f4b26b6..11a0ae6 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTesterDeactivateAll.java
@@ -78,7 +78,8 @@
filter.addAction(mPhone.getActionDetached());
log("register for intent action=" + mPhone.getActionDetached());
- phone.getContext().registerReceiver(sIntentReceiver, filter, null, handler);
+ phone.getContext().registerReceiver(sIntentReceiver, filter, null, handler,
+ Context.RECEIVER_EXPORTED);
}
}
diff --git a/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java b/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java
index ba07e12..788da29 100644
--- a/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java
+++ b/src/java/com/android/internal/telephony/dataconnection/DcTesterFailBringUpAll.java
@@ -89,7 +89,8 @@
filter.addAction(mPhone.getActionAttached());
log("register for intent action=" + mPhone.getActionAttached());
- phone.getContext().registerReceiver(mIntentReceiver, filter, null, handler);
+ phone.getContext().registerReceiver(mIntentReceiver, filter, null, handler,
+ Context.RECEIVER_EXPORTED);
}
}
diff --git a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
index 86e6463..0abd4ab 100644
--- a/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
+++ b/src/java/com/android/internal/telephony/gsm/GsmInboundSmsHandler.java
@@ -67,7 +67,8 @@
sTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(TEST_ACTION);
- context.registerReceiver(sTestBroadcastReceiver, filter);
+ context.registerReceiver(sTestBroadcastReceiver, filter,
+ Context.RECEIVER_EXPORTED);
}
}
}
diff --git a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
index 8ba390b..ebb4ffc 100644
--- a/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
+++ b/src/java/com/android/internal/telephony/ims/ImsServiceControllerCompat.java
@@ -18,6 +18,7 @@
import android.content.ComponentName;
import android.content.Context;
+import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
@@ -36,6 +37,7 @@
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.ims.internal.IImsMMTelFeature;
import com.android.ims.internal.IImsServiceController;
+import com.android.internal.annotations.VisibleForTesting;
/**
* Manages the Binding lifecycle of one ImsService as well as the relevant ImsFeatures that the
@@ -57,10 +59,33 @@
private final SparseArray<ImsRegistrationCompatAdapter> mRegCompatAdapters =
new SparseArray<>();
+ private final MmTelFeatureCompatFactory mMmTelFeatureFactory;
+
+ /**
+ * Used to inject test instances of MmTelFeatureCompatAdapter
+ */
+ @VisibleForTesting
+ public interface MmTelFeatureCompatFactory {
+ /**
+ * @return A new instance of {@link MmTelFeatureCompatAdapter}
+ */
+ MmTelFeatureCompatAdapter create(Context context, int slotId,
+ MmTelInterfaceAdapter compatFeature);
+ }
+
public ImsServiceControllerCompat(Context context, ComponentName componentName,
ImsServiceController.ImsServiceControllerCallbacks callbacks,
ImsFeatureBinderRepository repo) {
super(context, componentName, callbacks, repo);
+ mMmTelFeatureFactory = MmTelFeatureCompatAdapter::new;
+ }
+
+ @VisibleForTesting
+ public ImsServiceControllerCompat(Context context, ComponentName componentName,
+ ImsServiceControllerCallbacks callbacks, Handler handler, RebindRetry rebindRetry,
+ ImsFeatureBinderRepository repo, MmTelFeatureCompatFactory factory) {
+ super(context, componentName, callbacks, handler, rebindRetry, repo);
+ mMmTelFeatureFactory = factory;
}
@Override
@@ -186,6 +211,10 @@
protected final void removeImsFeature(int slotId, int featureType)
throws RemoteException {
if (featureType == ImsFeature.MMTEL) {
+ MmTelFeatureCompatAdapter adapter = mMmTelCompatAdapters.get(slotId, null);
+ // Need to manually call onFeatureRemoved here, since this is normally called by the
+ // ImsService itself.
+ if (adapter != null) adapter.onFeatureRemoved();
mMmTelCompatAdapters.remove(slotId);
mRegCompatAdapters.remove(slotId);
mConfigCompatAdapters.remove(slotId);
@@ -218,7 +247,7 @@
private IImsMmTelFeature createMMTelCompat(int slotId)
throws RemoteException {
MmTelInterfaceAdapter interfaceAdapter = getInterface(slotId);
- MmTelFeatureCompatAdapter mmTelAdapter = new MmTelFeatureCompatAdapter(mContext, slotId,
+ MmTelFeatureCompatAdapter mmTelAdapter = mMmTelFeatureFactory.create(mContext, slotId,
interfaceAdapter);
mMmTelCompatAdapters.put(slotId, mmTelAdapter);
ImsRegistrationCompatAdapter regAdapter = new ImsRegistrationCompatAdapter();
diff --git a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
old mode 100755
new mode 100644
index cff9e41..c76a649
--- a/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
+++ b/src/java/com/android/internal/telephony/imsphone/ImsPhoneCallTracker.java
@@ -1542,6 +1542,9 @@
mUssdMethod = carrierConfig.getInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT);
}
+ if (!mImsReasonCodeMap.isEmpty()) {
+ mImsReasonCodeMap.clear();
+ }
String[] mappings = carrierConfig
.getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY);
if (mappings != null && mappings.length > 0) {
@@ -1562,21 +1565,22 @@
if (message == null) {
message = "";
}
+ else if (message.equals("*")) {
+ message = null;
+ }
int toCode = Integer.parseInt(values[2]);
addReasonCodeRemapping(fromCode, message, toCode);
- log("Loaded ImsReasonInfo mapping : fromCode = " +
- fromCode == null ? "any" : fromCode + " ; message = " +
- message + " ; toCode = " + toCode);
+ log("Loaded ImsReasonInfo mapping :" +
+ " fromCode = " + (fromCode == null ? "any" : fromCode) +
+ " ; message = " + (message == null ? "any" : message) +
+ " ; toCode = " + toCode);
} catch (NumberFormatException nfe) {
loge("Invalid ImsReasonInfo mapping found: " + mapping);
}
}
} else {
log("No carrier ImsReasonInfo mappings defined.");
- if (!mImsReasonCodeMap.isEmpty()) {
- mImsReasonCodeMap.clear();
- }
}
}
@@ -2665,6 +2669,7 @@
+ reason);
Pair<Integer, String> toCheck = new Pair<>(code, reason);
Pair<Integer, String> wildcardToCheck = new Pair<>(null, reason);
+ Pair<Integer, String> wildcardMessageToCheck = new Pair<>(code, null);
if (mImsReasonCodeMap.containsKey(toCheck)) {
int toCode = mImsReasonCodeMap.get(toCheck);
@@ -2682,6 +2687,19 @@
" ; message = " + reason + " ; toCode = " + toCode);
return toCode;
}
+ else if (mImsReasonCodeMap.containsKey(wildcardMessageToCheck)) {
+ // Handle the case where a wildcard is specified for the reason.
+ // For example, we can set these two strings in
+ // CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY:
+ // - "1014|call completed elsewhere|1014"
+ // - "1014|*|510"
+ // to remap CODE_ANSWERED_ELSEWHERE to CODE_USER_TERMINATED_BY_REMOTE
+ // when reason is NOT "call completed elsewhere".
+ int toCode = mImsReasonCodeMap.get(wildcardMessageToCheck);
+ log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() +
+ " ; message(wildcard) = " + reason + " ; toCode = " + toCode);
+ return toCode;
+ }
return code;
}
@@ -3128,7 +3146,9 @@
getNetworkCountryIso(), emergencyNumberTracker != null
? emergencyNumberTracker.getEmergencyNumberDbVersion()
: TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION);
- mPhone.getVoiceCallSessionStats().onImsCallTerminated(conn, reasonInfo);
+ mPhone.getVoiceCallSessionStats().onImsCallTerminated(conn, new ImsReasonInfo(
+ maybeRemapReasonCode(reasonInfo),
+ reasonInfo.mExtraCode, reasonInfo.mExtraMessage));
// Remove info for the callId from the current calls and add it to the history
CallQualityMetrics lastCallMetrics = mCallQualityMetrics.remove(callId);
if (lastCallMetrics != null) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
index 14f5aa1..3137f74 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/ContextFixture.java
@@ -392,6 +392,17 @@
return registerReceiverFakeImpl(receiver, filter);
}
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter, int flags) {
+ return registerReceiverFakeImpl(receiver, filter);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
+ String broadcastPermission, Handler scheduler, int flags) {
+ return registerReceiverFakeImpl(receiver, filter);
+ }
+
private Intent registerReceiverFakeImpl(BroadcastReceiver receiver, IntentFilter filter) {
Intent result = null;
synchronized (mBroadcastReceiversByAction) {
diff --git a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
index e9c74ae..03d3459 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/SimulatedCommandsVerifier.java
@@ -1525,4 +1525,12 @@
@Override
public void unregisterForSimPhonebookRecordsReceived(Handler h){
}
+
+ @Override
+ public void registerForSlicingConfigChanged(Handler h, int what, Object obj) {
+ }
+
+ @Override
+ public void unregisterForSlicingConfigChanged(Handler h) {
+ }
}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
new file mode 100644
index 0000000..30fc477
--- /dev/null
+++ b/tests/telephonytests/src/com/android/internal/telephony/ims/ImsServiceControllerCompatTest.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2022 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.internal.telephony.ims;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.stub.ImsFeatureConfiguration;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.ims.ImsFeatureBinderRepository;
+import com.android.ims.ImsFeatureContainer;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsMMTelFeature;
+import com.android.ims.internal.IImsServiceController;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+
+import java.util.HashSet;
+
+@RunWith(AndroidJUnit4.class)
+public class ImsServiceControllerCompatTest extends ImsTestBase {
+
+ private static final int SLOT_0 = 0;
+
+ private static final ImsServiceController.RebindRetry REBIND_RETRY =
+ new ImsServiceController.RebindRetry() {
+ @Override
+ public long getStartDelay() {
+ return 50;
+ }
+
+ @Override
+ public long getMaximumDelay() {
+ return 1000;
+ }
+ };
+
+ @Mock MmTelInterfaceAdapter mMockMmTelInterfaceAdapter;
+ @Mock IImsMMTelFeature mMockRemoteMMTelFeature;
+ @Mock IBinder mMockMMTelBinder;
+ @Mock IImsServiceController mMockServiceControllerBinder;
+ @Mock ImsServiceController.ImsServiceControllerCallbacks mMockCallbacks;
+ @Mock IImsConfig mMockImsConfig;
+ @Mock Context mMockContext;
+ private final ComponentName mTestComponentName = new ComponentName("TestPkg",
+ "ImsServiceControllerTest");
+ private final Handler mHandler = new Handler(Looper.getMainLooper());
+ private ImsServiceController mTestImsServiceController;
+ private ImsFeatureBinderRepository mRepo;
+ private MmTelFeatureCompatAdapter mMmTelCompatAdapterSpy;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mRepo = new ImsFeatureBinderRepository();
+ // Can't mock MmTelFeatureCompatAdapter due to final getBinder() method.
+ mMmTelCompatAdapterSpy = spy(new MmTelFeatureCompatAdapter(mMockContext, SLOT_0,
+ mMockMmTelInterfaceAdapter));
+ mTestImsServiceController = new ImsServiceControllerCompat(mMockContext, mTestComponentName,
+ mMockCallbacks, mHandler, REBIND_RETRY, mRepo,
+ (a, b, c) -> mMmTelCompatAdapterSpy);
+ when(mMockContext.bindService(any(), any(), anyInt())).thenReturn(true);
+ when(mMockServiceControllerBinder.createMMTelFeature(anyInt()))
+ .thenReturn(mMockRemoteMMTelFeature);
+ when(mMockRemoteMMTelFeature.getConfigInterface()).thenReturn(mMockImsConfig);
+ when(mMockRemoteMMTelFeature.asBinder()).thenReturn(mMockMMTelBinder);
+ }
+
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ mTestImsServiceController.stopBackoffTimerForTesting();
+ mTestImsServiceController = null;
+ // Make sure the handler is empty before finishing the test.
+ waitForHandlerAction(mHandler, 1000);
+ super.tearDown();
+ }
+
+ /**
+ * Tests that the MmTelFeatureCompatAdapter is cleaned up properly and ImsServiceController
+ * callbacks are properly called when an ImsService is bound and then crashes.
+ */
+ @SmallTest
+ @Test
+ public void testBindServiceAndCrashCleanUp() throws RemoteException {
+ ServiceConnection conn = bindAndConnectService();
+ // add the MMTelFeature
+ verify(mMockServiceControllerBinder).createMMTelFeature(SLOT_0);
+ verify(mMockServiceControllerBinder).addFeatureStatusCallback(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL), any());
+ verify(mMockCallbacks).imsServiceFeatureCreated(eq(SLOT_0), eq(ImsFeature.FEATURE_MMTEL),
+ eq(mTestImsServiceController));
+ validateMmTelFeatureContainerExists(SLOT_0);
+ // Remove the feature
+ conn.onBindingDied(mTestComponentName);
+ verify(mMmTelCompatAdapterSpy).onFeatureRemoved();
+ verify(mMockServiceControllerBinder).removeImsFeature(eq(SLOT_0),
+ eq(ImsFeature.FEATURE_MMTEL));
+ validateMmTelFeatureContainerDoesntExist(SLOT_0);
+ }
+
+ private void validateMmTelFeatureContainerExists(int slotId) {
+ ImsFeatureContainer fc =
+ mRepo.getIfExists(slotId, ImsFeature.FEATURE_MMTEL).orElse(null);
+ assertNotNull("MMTEL FeatureContainer should not be null", fc);
+ assertEquals("ImsServiceController did not report MmTelFeature to service repo correctly",
+ mMmTelCompatAdapterSpy.getBinder(), fc.imsFeature);
+ assertEquals(0, (android.telephony.ims.ImsService.CAPABILITY_EMERGENCY_OVER_MMTEL
+ & fc.getCapabilities()));
+ }
+
+ private void validateMmTelFeatureContainerDoesntExist(int slotId) {
+ ImsFeatureContainer fc =
+ mRepo.getIfExists(slotId, ImsFeature.FEATURE_MMTEL).orElse(null);
+ assertNull("FeatureContainer should be null", fc);
+ }
+
+ private ServiceConnection bindAndConnectService() {
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures = new HashSet<>();
+ testFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(SLOT_0,
+ ImsFeature.FEATURE_MMTEL));
+ ServiceConnection connection = bindService(testFeatures);
+ IImsServiceController.Stub controllerStub = mock(IImsServiceController.Stub.class);
+ when(controllerStub.queryLocalInterface(any())).thenReturn(mMockServiceControllerBinder);
+ connection.onServiceConnected(mTestComponentName, controllerStub);
+ return connection;
+ }
+
+ private ServiceConnection bindService(
+ HashSet<ImsFeatureConfiguration.FeatureSlotPair> testFeatures) {
+ ArgumentCaptor<ServiceConnection> serviceCaptor =
+ ArgumentCaptor.forClass(ServiceConnection.class);
+ assertTrue(mTestImsServiceController.bind(testFeatures));
+ verify(mMockContext).bindService(any(), serviceCaptor.capture(), anyInt());
+ return serviceCaptor.getValue();
+ }
+}
diff --git a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
index d1826d5..d977a6f 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/imsphone/ImsPhoneCallTrackerTest.java
@@ -770,6 +770,71 @@
"Call answered elsewhere.")));
}
+ private void clearCarrierConfig() {
+ PersistableBundle bundle = new PersistableBundle();
+ mCTUT.updateCarrierConfigCache(bundle);
+ }
+
+ private void loadReasonCodeRemapCarrierConfig() {
+ PersistableBundle bundle = new PersistableBundle();
+ String[] mappings = new String[] {
+ // These shall be equivalent to the remappings added in setUp():
+ "*|Wifi signal lost.|1407",
+ "501|Call answered elsewhere.|1014",
+ "510|Call answered elsewhere.|1014",
+ "510||332",
+ "352|emergency calls over wifi not allowed in this location|1622",
+ "332|service not allowed in this location|1623",
+ };
+ bundle.putStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY,
+ mappings);
+ mCTUT.updateCarrierConfigCache(bundle);
+ }
+
+ @Test
+ @SmallTest
+ public void testReasonCodeRemapCarrierConfig() {
+ clearCarrierConfig();
+ // The map shall become empty now
+
+ assertEquals(510, // ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE
+ mCTUT.maybeRemapReasonCode(new ImsReasonInfo(510, 1, "Call answered elsewhere.")));
+
+ loadReasonCodeRemapCarrierConfig();
+ testReasonCodeRemap();
+ testNumericOnlyRemap();
+ testRemapEmergencyCallsOverWfc();
+ testRemapWfcNotAvailable();
+ }
+
+ private void loadReasonCodeRemapCarrierConfigWithWildcardMessage() {
+ PersistableBundle bundle = new PersistableBundle();
+ String[] mappings = new String[]{
+ "1014|call completed elsewhere|1014",
+ "1014|*|510",
+ };
+ bundle.putStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY,
+ mappings);
+ mCTUT.updateCarrierConfigCache(bundle);
+ }
+
+ @Test
+ @SmallTest
+ public void testReasonCodeRemapCarrierConfigWithWildcardMessage() {
+ clearCarrierConfig();
+ // The map shall become empty now
+
+ loadReasonCodeRemapCarrierConfigWithWildcardMessage();
+ assertEquals(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, mCTUT.maybeRemapReasonCode(
+ new ImsReasonInfo(1014, 200, "Call Rejected By User"))); // 1014 -> 510
+ assertEquals(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE, mCTUT.maybeRemapReasonCode(
+ new ImsReasonInfo(1014, 200, "Call completed elsewhere"))); // 1014 -> 1014
+
+ // Simulate that after SIM swap the new carrier config doesn't have the mapping for 1014
+ loadReasonCodeRemapCarrierConfig();
+ assertEquals(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE, mCTUT.maybeRemapReasonCode(
+ new ImsReasonInfo(1014, 200, "Call Rejected By User"))); // 1014 -> 1014
+ }
@Test
@SmallTest