From d3f373072112f3d7f3771ecd2a4c695435917c83 Mon Sep 17 00:00:00 2001 From: SongFerngWang Date: Thu, 27 Jan 2022 17:31:40 +0800 Subject: [PATCH] [MEP]The Esim's PhysicalSlotIndex is wrong Using the getUiccSlotInfo API to get the PhysicalSlotIndex of esim Bug: 215302360 Test: atest UiccSlotUtilTest Change-Id: Ic43d6c4a3209d24673769b71b9a148e780ee81ab Merged-In: Ic43d6c4a3209d24673769b71b9a148e780ee81ab --- .../SwitchToEuiccSubscriptionSidecar.java | 29 ++- .../settings/network/UiccSlotUtil.java | 23 +++ .../settings/network/UiccSlotUtilTest.java | 191 ++++++++++++++++++ 3 files changed, 234 insertions(+), 9 deletions(-) create mode 100644 tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java diff --git a/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java b/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java index 888e5b29e9e..9524164bc33 100644 --- a/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java +++ b/src/com/android/settings/network/SwitchToEuiccSubscriptionSidecar.java @@ -37,7 +37,6 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar { private static final String TAG = "SwitchToEuiccSidecar"; private static final String ACTION_SWITCH_TO_SUBSCRIPTION = "com.android.settings.network.SWITCH_TO_SUBSCRIPTION"; - private static final int ESIM_SLOT_ID = 1; private PendingIntent mCallbackIntent; private int mSubId; @@ -92,11 +91,20 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar { setState(State.RUNNING, Substate.UNUSED); mCallbackIntent = createCallbackIntent(); mSubId = subscriptionId; + int targetSlot = getTargetSlot(); + if (targetSlot < 0) { + Log.d(TAG, "There is no esim, the TargetSlot is " + targetSlot); + setState(State.ERROR, Substate.UNUSED); + return; + } + // To check whether the esim slot's port is active. If yes, skip setSlotMapping. If no, // set this slot+port into setSimSlotMapping. - mPort = (port < 0) ? getTargetPortId(removedSubInfo) : port; + mPort = (port < 0) ? getTargetPortId(removedSubInfo, targetSlot) : port; mRemovedSubInfo = removedSubInfo; - Log.i(TAG, "The SubId is " + mSubId + ". The port is " + mPort); + Log.d(TAG, + String.format("set esim into the Slot%d SubId%d:Port%d", + targetSlot, mSubId, mPort)); if (mTelephonyManager.isMultiSimEnabled() && removedSubInfo != null && removedSubInfo.isEmbedded()) { @@ -108,11 +116,11 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar { mEuiccManager.switchToSubscription(SubscriptionManager.INVALID_SUBSCRIPTION_ID, mPort, mCallbackIntent); } else { - mSwitchSlotSidecar.runSwitchToEuiccSlot(getTargetSlot(), mPort, removedSubInfo); + mSwitchSlotSidecar.runSwitchToEuiccSlot(targetSlot, mPort, removedSubInfo); } } - private int getTargetPortId(SubscriptionInfo removedSubInfo) { + private int getTargetPortId(SubscriptionInfo removedSubInfo, int targetSlot) { if (!mTelephonyManager.isMultiSimEnabled() || !isMultipleEnabledProfilesSupported()) { // In the 'SS mode' or 'DSDS+no MEP', the port is 0. return 0; @@ -124,16 +132,19 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar { return removedSubInfo.getPortIndex(); } - // In DSDS+MEP mode, the removedSubInfo is psim or is null, it means the this esim need + // In DSDS+MEP mode, the removedSubInfo is psim or is null, it means this esim needs // another port in the esim slot. - // To find another esim's port and value is from 0; + // To find another esim's port and value is from 0. + // For example: + // 1) If there is no enabled esim and the user add new esim. This new esim's port is 0. + // 2) If there is one enabled esim and the user add new esim. This new esim's port is 1. int port = 0; Collection uiccSlotMappings = mTelephonyManager.getSimSlotMapping(); for (UiccSlotMapping uiccSlotMapping : uiccSlotMappings.stream() .filter( uiccSlotMapping -> uiccSlotMapping.getPhysicalSlotIndex() - == getTargetSlot()) + == targetSlot) .collect(Collectors.toList())) { if (uiccSlotMapping.getPortIndex() == port) { port++; @@ -143,7 +154,7 @@ public class SwitchToEuiccSubscriptionSidecar extends EuiccOperationSidecar { } private int getTargetSlot() { - return ESIM_SLOT_ID; + return UiccSlotUtil.getEsimSlotId(getContext()); } private void onSwitchSlotSidecarStateChange() { diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java index 8938cdbb2e8..2765b3eb6bd 100644 --- a/src/com/android/settings/network/UiccSlotUtil.java +++ b/src/com/android/settings/network/UiccSlotUtil.java @@ -36,6 +36,7 @@ import java.util.Collection; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; +import java.util.stream.IntStream; // ToDo: to do the refactor for renaming public class UiccSlotUtil { @@ -174,6 +175,28 @@ public class UiccSlotUtil { performSwitchToSlot(telMgr, newUiccSlotMappings, context); } + /** + * @param context the application context. + * @return the esim slot. If the value is -1, there is not the esim. + */ + public static int getEsimSlotId(Context context) { + TelephonyManager telMgr = context.getSystemService(TelephonyManager.class); + ImmutableList slotInfos = UiccSlotUtil.getSlotInfos(telMgr); + int firstEsimSlot = IntStream.range(0, slotInfos.size()) + .filter( + index -> { + UiccSlotInfo slotInfo = slotInfos.get(index); + if (slotInfo == null) { + return false; + } + return !slotInfo.isRemovable(); + }) + .findFirst().orElse(-1); + + Log.i(TAG, "firstEsimSlot: " + firstEsimSlot); + return firstEsimSlot; + } + private static boolean isTargetSlotActive(Collection uiccSlotMappings, int slotId, int port) { return uiccSlotMappings.stream() diff --git a/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java new file mode 100644 index 00000000000..1a6879dea0b --- /dev/null +++ b/tests/unit/src/com/android/settings/network/UiccSlotUtilTest.java @@ -0,0 +1,191 @@ +/* + * 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.settings.network; + +import static android.telephony.UiccSlotInfo.CARD_STATE_INFO_PRESENT; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.telephony.TelephonyManager; +import android.telephony.UiccPortInfo; +import android.telephony.UiccSlotInfo; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.google.common.collect.ImmutableList; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.Collections; + +@RunWith(AndroidJUnit4.class) +public class UiccSlotUtilTest { + private Context mContext; + @Mock + private TelephonyManager mTelephonyManager; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = spy(ApplicationProvider.getApplicationContext()); + when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager); + } + + @Test + public void getSlotInfos_oneSimSlotDevice_returnTheCorrectSlotInfoList() { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn(oneSimSlotDevice_ActivePsim()); + ImmutableList testUiccSlotInfos = + UiccSlotUtil.getSlotInfos(mTelephonyManager); + + assertThat(testUiccSlotInfos.size()).isEqualTo(1); + } + + @Test + public void getSlotInfos_twoSimSlotsDevice_returnTheCorrectSlotInfoList() { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn( + twoSimSlotsDevice_ActivePsimActiveEsim()); + ImmutableList testUiccSlotInfos = + UiccSlotUtil.getSlotInfos(mTelephonyManager); + + assertThat(testUiccSlotInfos.size()).isEqualTo(2); + } + + @Test + public void getEsimSlotId_twoSimSlotsDeviceAndEsimIsSlot0_returnTheCorrectEsimSlot() { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn( + twoSimSlotsDevice_ActiveEsimActivePsim()); + int testSlot = UiccSlotUtil.getEsimSlotId(mContext); + + assertThat(testSlot).isEqualTo(0); + } + + @Test + public void getEsimSlotId_twoSimSlotsDeviceAndEsimIsSlot1_returnTheCorrectEsimSlot() { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn( + twoSimSlotsDevice_ActivePsimActiveEsim()); + int testSlot = UiccSlotUtil.getEsimSlotId(mContext); + + assertThat(testSlot).isEqualTo(1); + } + + @Test + public void getEsimSlotId_noEimSlotDevice_returnTheCorrectEsimSlot() { + when(mTelephonyManager.getUiccSlotsInfo()).thenReturn( + oneSimSlotDevice_ActivePsim()); + int testSlot = UiccSlotUtil.getEsimSlotId(mContext); + + assertThat(testSlot).isEqualTo(-1); + } + + /** + * The "oneSimSlotDevice" has below cases + * 1) The device is one psim slot and no esim slot + * 2) The device is no psim slot and one esim slot. like the watch. + * + * The "twoSimsSlotDevice" has below cases + * 1) The device is one psim slot and one esim slot + * 2) The device is two psim slots + */ + + private UiccSlotInfo[] oneSimSlotDevice_ActivePsim() { + return new UiccSlotInfo[]{createUiccSlotInfo(false, true, 0, true)}; + } + + private UiccSlotInfo[] oneSimSlotDevice_ActiveEsim() { + return new UiccSlotInfo[]{createUiccSlotInfo(true, false, 1, true)}; + } + + private UiccSlotInfo[] twoSimSlotsDevice_ActivePsimActiveEsim() { + return new UiccSlotInfo[]{ + createUiccSlotInfo(false, true, 0, true), + createUiccSlotInfo(true, false, 1, true)}; + } + + private UiccSlotInfo[] twoSimSlotsDevice_ActiveEsimActivePsim() { + return new UiccSlotInfo[]{ + createUiccSlotInfo(true, false, 0, true), + createUiccSlotInfo(false, true, 1, true)}; + } + + private UiccSlotInfo[] twoSimSlotsDevice_twoActiveEsims() { + // device supports MEP, so device can enable two esims. + // If device has psim slot, the UiccSlotInfo of psim always be in UiccSlotInfo[]. + return new UiccSlotInfo[]{ + createUiccSlotInfo(false, true, -1, true), + createUiccSlotInfoForEsimMep(0, true, 1, true)}; + } + + private UiccSlotInfo[] twoSimSlotsDevice_ActivePsimInactiveEsim() { + return new UiccSlotInfo[]{ + createUiccSlotInfo(false, true, 0, true), + createUiccSlotInfo(true, false, -1, false)}; + } + + private UiccSlotInfo[] twoSimSlotsDevice_InactivePsimActiveEsim() { + return new UiccSlotInfo[]{ + createUiccSlotInfo(false, true, 0, false), + createUiccSlotInfo(true, false, 1, true)}; + } + + private UiccSlotInfo[] twoSimSlotsDevice_NoInsertPsimActiveEsim() { + return new UiccSlotInfo[]{ + createUiccSlotInfo(false, true, -1, false), + createUiccSlotInfo(true, false, 1, true)}; + } + //ToDo: add more cases. + + private UiccSlotInfo createUiccSlotInfo(boolean isEuicc, boolean isRemovable, + int logicalSlotIdx, boolean isActive) { + return new UiccSlotInfo( + isEuicc, /* isEuicc */ + "123", /* cardId */ + CARD_STATE_INFO_PRESENT, /* cardStateInfo */ + true, /* isExtendApduSupported */ + isRemovable, /* isRemovable */ + Collections.singletonList( + new UiccPortInfo("" /* iccId */, 0 /* portIdx */, + logicalSlotIdx /* logicalSlotIdx */, isActive /* isActive */)) + ); + } + + private UiccSlotInfo createUiccSlotInfoForEsimMep(int logicalSlotIdx1, boolean isActiveEsim1, + int logicalSlotIdx2, boolean isActiveEsim2) { + return new UiccSlotInfo( + true, /* isEuicc */ + "123", /* cardId */ + CARD_STATE_INFO_PRESENT, /* cardStateInfo */ + true, /* isExtendApduSupported */ + false, /* isRemovable */ + Arrays.asList( + new UiccPortInfo("" /* iccId */, 0 /* portIdx */, + logicalSlotIdx1 /* logicalSlotIdx */, isActiveEsim1 /* isActive */), + new UiccPortInfo("" /* iccId */, 1 /* portIdx */, + logicalSlotIdx2 /* logicalSlotIdx */, + isActiveEsim2 /* isActive */))); + } +}