Loading src/java/com/android/internal/telephony/euicc/EuiccCardController.java +27 −9 Original line number Diff line number Diff line Loading @@ -39,7 +39,9 @@ import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.UiccCard; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccPort; Loading @@ -51,6 +53,7 @@ import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; /** Backing implementation of {@link EuiccCardManager}. */ public class EuiccCardController extends IEuiccCardController.Stub { Loading Loading @@ -649,9 +652,14 @@ public class EuiccCardController extends IEuiccCardController.Stub { @Override public void onResult(Void result) { Log.i(TAG, "Request subscription info list refresh after delete."); if (PhoneFactory.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( List.of(mUiccController.convertToPublicCardId(cardId)), null); } else { SubscriptionController.getInstance() .requestEmbeddedSubscriptionInfoListRefresh( mUiccController.convertToPublicCardId(cardId)); } try { callback.onComplete(EuiccCardManager.RESULT_OK); } catch (RemoteException exception) { Loading Loading @@ -701,9 +709,14 @@ public class EuiccCardController extends IEuiccCardController.Stub { @Override public void onResult(Void result) { Log.i(TAG, "Request subscription info list refresh after reset memory."); if (PhoneFactory.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( List.of(mUiccController.convertToPublicCardId(cardId)), null); } else { SubscriptionController.getInstance() .requestEmbeddedSubscriptionInfoListRefresh( mUiccController.convertToPublicCardId(cardId)); } try { callback.onComplete(EuiccCardManager.RESULT_OK); } catch (RemoteException exception) { Loading Loading @@ -1190,9 +1203,14 @@ public class EuiccCardController extends IEuiccCardController.Stub { @Override public void onResult(byte[] result) { Log.i(TAG, "Request subscription info list refresh after install."); if (PhoneFactory.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( List.of(mUiccController.convertToPublicCardId(cardId)), null); } else { SubscriptionController.getInstance() .requestEmbeddedSubscriptionInfoListRefresh( mUiccController.convertToPublicCardId(cardId)); } try { callback.onComplete(EuiccCardManager.RESULT_OK, result); } catch (RemoteException exception) { Loading src/java/com/android/internal/telephony/euicc/EuiccController.java +10 −3 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback; import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccPort; Loading Loading @@ -1585,10 +1586,16 @@ public class EuiccController extends IEuiccController.Stub { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public void refreshSubscriptionsAndSendResult( PendingIntent callbackIntent, int resultCode, Intent extrasIntent) { if (PhoneFactory.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( List.of(mTelephonyManager.getCardIdForDefaultEuicc()), () -> sendResult(callbackIntent, resultCode, extrasIntent)); } else { SubscriptionController.getInstance() .requestEmbeddedSubscriptionInfoListRefresh( () -> sendResult(callbackIntent, resultCode, extrasIntent)); } } /** Dispatch the given callback intent with the given result code and data. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) Loading src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java +18 −0 Original line number Diff line number Diff line Loading @@ -1764,6 +1764,24 @@ public class SubscriptionInfoInternal { return this; } /** * Set the native access rules for this subscription, if it is embedded and defines any. * This does not include access rules for non-embedded subscriptions. * * @param nativeAccessRules The native access rules for this subscription. * * @return The builder. */ @NonNull public Builder setNativeAccessRules(@NonNull List<UiccAccessRule> nativeAccessRules) { Objects.requireNonNull(nativeAccessRules); if (!nativeAccessRules.isEmpty()) { mNativeAccessRules = UiccAccessRule.encodeRules( nativeAccessRules.toArray(new UiccAccessRule[0])); } return this; } /** * Set the carrier certificates for this subscription that are saved in carrier configs. * This does not include access rules from the Uicc, whether embedded or non-embedded. Loading src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java +182 −2 Original line number Diff line number Diff line Loading @@ -37,15 +37,21 @@ import android.os.ParcelUuid; import android.os.TelephonyServiceManager; import android.os.UserHandle; import android.provider.Settings; import android.service.carrier.CarrierIdentifier; import android.service.euicc.EuiccProfileInfo; import android.service.euicc.EuiccService; import android.service.euicc.GetEuiccProfileInfoListResult; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.PhoneNumberSource; import android.telephony.SubscriptionManager.SimDisplayNameSource; import android.telephony.SubscriptionManager.SubscriptionType; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyManager; import android.telephony.TelephonyRegistryManager; import android.telephony.UiccAccessRule; import android.telephony.euicc.EuiccManager; import android.text.TextUtils; import android.util.ArraySet; Loading @@ -54,11 +60,13 @@ import android.util.IndentingPrintWriter; import android.util.LocalLog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CarrierResolver; import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.MultiSimSettingController; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyPermissions; import com.android.internal.telephony.euicc.EuiccController; import com.android.internal.telephony.subscription.SubscriptionDatabaseManager.SubscriptionDatabaseManagerCallback; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccController; Loading Loading @@ -107,17 +115,31 @@ public class SubscriptionManagerService extends ISub.Stub { private final Context mContext; /** Telephony manager instance. */ @NonNull private final TelephonyManager mTelephonyManager; /** Subscription manager instance. */ @NonNull private final SubscriptionManager mSubscriptionManager; /** Euicc manager instance. */ /** * Euicc manager instance. Will be null if the device does not support * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. */ @Nullable private final EuiccManager mEuiccManager; /** Uicc controller instance. */ @NonNull private final UiccController mUiccController; /** * Euicc controller instance. Will be null if the device does not support * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. */ @Nullable private EuiccController mEuiccController; /** The main handler of subscription manager service. */ @NonNull private final Handler mHandler; Loading Loading @@ -288,6 +310,7 @@ public class SubscriptionManagerService extends ISub.Stub { mTelephonyManager = context.getSystemService(TelephonyManager.class); mSubscriptionManager = context.getSystemService(SubscriptionManager.class); mEuiccManager = context.getSystemService(EuiccManager.class); mUiccController = UiccController.getInstance(); mHandler = new Handler(looper); TelephonyServiceManager.ServiceRegisterer subscriptionServiceRegisterer = Loading Loading @@ -402,6 +425,15 @@ public class SubscriptionManagerService extends ISub.Stub { }); updateDefaultSubId(); mHandler.post(() -> { // EuiccController is created after SubscriptionManagerService. So we need to get // the instance later in the handler. if (mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY_EUICC)) { mEuiccController = EuiccController.get(); } }); } /** Loading Loading @@ -669,6 +701,153 @@ public class SubscriptionManagerService extends ISub.Stub { subInfo.getSubscriptionId(), SubscriptionManager.INVALID_SIM_SLOT_INDEX)); } /** * This is only for internal use and the returned priority is arbitrary. The idea is to give a * higher value to name source that has higher priority to override other name sources. * * @param nameSource Source of display name. * * @return The priority. Higher value means higher priority. */ private static int getNameSourcePriority(@SimDisplayNameSource int nameSource) { int index = Arrays.asList( SubscriptionManager.NAME_SOURCE_UNKNOWN, SubscriptionManager.NAME_SOURCE_CARRIER_ID, SubscriptionManager.NAME_SOURCE_SIM_PNN, SubscriptionManager.NAME_SOURCE_SIM_SPN, SubscriptionManager.NAME_SOURCE_CARRIER, SubscriptionManager.NAME_SOURCE_USER_INPUT // user has highest priority. ).indexOf(nameSource); return Math.max(0, index); } /** * Get the embedded profile port index by ICCID. * * @param iccId The ICCID. * @return The port index. */ private int getEmbeddedProfilePortIndex(String iccId) { UiccSlot[] slots = UiccController.getInstance().getUiccSlots(); for (UiccSlot slot : slots) { if (slot != null && slot.isEuicc() && slot.getPortIndexFromIccId(iccId) != TelephonyManager.INVALID_PORT_INDEX) { return slot.getPortIndexFromIccId(iccId); } } return TelephonyManager.INVALID_PORT_INDEX; } /** * Pull the embedded subscription from {@link EuiccController} for the eUICC with the given list * of card IDs {@code cardIds}. * * @param cardIds The card ids of the embedded subscriptions. * @param callback Callback to be called upon completion. */ public void updateEmbeddedSubscriptions(@NonNull List<Integer> cardIds, @Nullable Runnable callback) { mHandler.post(() -> { // Do nothing if eUICCs are disabled. (Previous entries may remain in the cache, but // they are filtered out of list calls as long as EuiccManager.isEnabled returns false). if (mEuiccManager == null || !mEuiccManager.isEnabled()) { loge("updateEmbeddedSubscriptions: eUICC not enabled"); if (callback != null) { callback.run(); } return; } log("updateEmbeddedSubscriptions: start to get euicc profiles."); for (int cardId : cardIds) { GetEuiccProfileInfoListResult result = mEuiccController .blockingGetEuiccProfileInfoList(cardId); log("updateEmbeddedSubscriptions: cardId=" + cardId + ", result=" + result); if (result.getResult() != EuiccService.RESULT_OK) { loge("Failed to get euicc profile info. result=" + EuiccService.resultToString(result.getResult())); continue; } if (result.getProfiles() == null || result.getProfiles().isEmpty()) { loge("No profiles returned."); continue; } final boolean isRemovable = result.getIsRemovable(); for (EuiccProfileInfo embeddedProfile : result.getProfiles()) { SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternalByIccId(embeddedProfile.getIccid()); // The subscription does not exist in the database. Insert a new one here. if (subInfo == null) { subInfo = new SubscriptionInfoInternal.Builder() .setIccId(embeddedProfile.getIccid()) .build(); int subId = mSubscriptionDatabaseManager.insertSubscriptionInfo(subInfo); subInfo = new SubscriptionInfoInternal.Builder(subInfo) .setId(subId).build(); } int nameSource = subInfo.getDisplayNameSource(); int carrierId = subInfo.getCarrierId(); SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal .Builder(subInfo); builder.setEmbedded(1); List<UiccAccessRule> ruleList = embeddedProfile.getUiccAccessRules(); if (ruleList != null && !ruleList.isEmpty()) { builder.setNativeAccessRules(embeddedProfile.getUiccAccessRules()); } builder.setRemovableEmbedded(isRemovable ? 1 : 0); // override DISPLAY_NAME if the priority of existing nameSource is <= carrier if (getNameSourcePriority(nameSource) <= getNameSourcePriority( SubscriptionManager.NAME_SOURCE_CARRIER)) { builder.setDisplayName(embeddedProfile.getNickname()); builder.setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER); } builder.setProfileClass(embeddedProfile.getProfileClass()); builder.setPortIndex(getEmbeddedProfilePortIndex(embeddedProfile.getIccid())); CarrierIdentifier cid = embeddedProfile.getCarrierIdentifier(); if (cid != null) { // Due to the limited subscription information, carrier id identified here // might not be accurate compared with CarrierResolver. Only update carrier // id if there is no valid carrier id present. if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { builder.setCarrierId(CarrierResolver .getCarrierIdFromIdentifier(mContext, cid)); } String mcc = cid.getMcc(); String mnc = cid.getMnc(); builder.setMcc(mcc); builder.setMnc(mnc); } // If cardId = unsupported or un-initialized, we have no reason to update DB. // Additionally, if the device does not support cardId for default eUICC, the // CARD_ID field should not contain the EID if (cardId >= 0 && mUiccController.getCardIdForDefaultEuicc() != TelephonyManager.UNSUPPORTED_CARD_ID) { builder.setCardString(mUiccController.convertToCardString(cardId)); } subInfo = builder.build(); log("updateEmbeddedSubscriptions: update subscription " + subInfo); mSubscriptionDatabaseManager.updateSubscription(subInfo); } } }); log("updateEmbeddedSubscriptions: Finished embedded subscription update."); if (callback != null) { callback.run(); } } /** * Get all subscription info records from SIMs that are inserted now or were inserted before. * Loading Loading @@ -905,7 +1084,8 @@ public class SubscriptionManagerService extends ISub.Stub { return mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(subInfo -> subInfo.isActive() || iccIds.contains(subInfo.getIccId()) || (mEuiccManager.isEnabled() && subInfo.isEmbedded())) || (mEuiccManager != null && mEuiccManager.isEnabled() && subInfo.isEmbedded())) .map(SubscriptionInfoInternal::toSubscriptionInfo) .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex) .thenComparing(SubscriptionInfo::getSubscriptionId)) Loading tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java +82 −0 Original line number Diff line number Diff line Loading @@ -17,13 +17,19 @@ package com.android.internal.telephony.subscription; import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_ID1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_ID2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_COUNTRY_CODE2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_ICCID1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_ICCID2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MCC1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MCC2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MNC1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MNC2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_NATIVE_ACCESS_RULES1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_NATIVE_ACCESS_RULES2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SUBSCRIPTION_INFO1; Loading Loading @@ -59,8 +65,13 @@ import android.os.ParcelUuid; import android.os.UserHandle; import android.provider.Settings; import android.provider.Telephony; import android.service.carrier.CarrierIdentifier; import android.service.euicc.EuiccProfileInfo; import android.service.euicc.EuiccService; import android.service.euicc.GetEuiccProfileInfoListResult; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.UiccAccessRule; import android.test.mock.MockContentResolver; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; Loading @@ -68,6 +79,7 @@ import android.testing.TestableLooper; import com.android.internal.telephony.ContextFixture; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.euicc.EuiccController; import com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.SubscriptionProvider; import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback; Loading @@ -84,6 +96,7 @@ import org.mockito.Mockito; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; @RunWith(AndroidTestingRunner.class) Loading @@ -100,6 +113,7 @@ public class SubscriptionManagerServiceTest extends TelephonyTest { // mocked private SubscriptionManagerServiceCallback mMockedSubscriptionManagerServiceCallback; private EuiccController mEuiccController; @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); Loading @@ -108,10 +122,13 @@ public class SubscriptionManagerServiceTest extends TelephonyTest { public void setUp() throws Exception { logd("SubscriptionManagerServiceTest +Setup!"); super.setUp(getClass().getSimpleName()); mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_EUICC); setupMocksForTelephonyPermissions(Build.VERSION_CODES.UPSIDE_DOWN_CAKE); PropertyInvalidatedCache.disableForCurrentProcess("cache_key.is_compat_change_enabled"); doReturn(true).when(mTelephonyManager).isVoiceCapable(); mEuiccController = Mockito.mock(EuiccController.class); replaceInstance(EuiccController.class, "sInstance", null, mEuiccController); mMockedSubscriptionManagerServiceCallback = Mockito.mock( SubscriptionManagerServiceCallback.class); ((MockContentResolver) mContext.getContentResolver()).addProvider( Loading Loading @@ -681,4 +698,69 @@ public class SubscriptionManagerServiceTest extends TelephonyTest { 0, CALLING_PACKAGE, CALLING_FEATURE); assertThat(subInfo).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo()); } @Test public void testUpdateEmbeddedSubscriptions() { EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1) .setIccid(FAKE_ICCID1) .setNickname(FAKE_CARRIER_NAME1) .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL) .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC1, FAKE_MNC1, null, null, null, null, FAKE_CARRIER_ID1, FAKE_CARRIER_ID1)) .setUiccAccessRule(Arrays.asList(UiccAccessRule.decodeRules( FAKE_NATIVE_ACCESS_RULES1))) .build(); EuiccProfileInfo profileInfo2 = new EuiccProfileInfo.Builder(FAKE_ICCID2) .setIccid(FAKE_ICCID2) .setNickname(FAKE_CARRIER_NAME2) .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL) .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC2, FAKE_MNC2, null, null, null, null, FAKE_CARRIER_ID2, FAKE_CARRIER_ID2)) .setUiccAccessRule(Arrays.asList(UiccAccessRule.decodeRules( FAKE_NATIVE_ACCESS_RULES2))) .build(); GetEuiccProfileInfoListResult result = new GetEuiccProfileInfoListResult( EuiccService.RESULT_OK, new EuiccProfileInfo[]{profileInfo1}, false); doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(1)); result = new GetEuiccProfileInfoListResult(EuiccService.RESULT_OK, new EuiccProfileInfo[]{profileInfo2}, false); doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(2)); doReturn(FAKE_ICCID1).when(mUiccController).convertToCardString(eq(1)); doReturn(FAKE_ICCID2).when(mUiccController).convertToCardString(eq(2)); mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1, 2), null); processAllMessages(); SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT .getSubscriptionInfoInternal(1); assertThat(subInfo.getSubscriptionId()).isEqualTo(1); assertThat(subInfo.getSimSlotIndex()).isEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX); assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID1); assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME1); assertThat(subInfo.getDisplayNameSource()).isEqualTo( SubscriptionManager.NAME_SOURCE_CARRIER); assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC1); assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC1); assertThat(subInfo.getProfileClass()).isEqualTo( SubscriptionManager.PROFILE_CLASS_OPERATIONAL); assertThat(subInfo.isEmbedded()).isTrue(); assertThat(subInfo.isRemovableEmbedded()).isFalse(); assertThat(subInfo.getNativeAccessRules()).isEqualTo(FAKE_NATIVE_ACCESS_RULES1); subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2); assertThat(subInfo.getSubscriptionId()).isEqualTo(2); assertThat(subInfo.getSimSlotIndex()).isEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX); assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID2); assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME2); assertThat(subInfo.getDisplayNameSource()).isEqualTo( SubscriptionManager.NAME_SOURCE_CARRIER); assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC2); assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC2); assertThat(subInfo.getProfileClass()).isEqualTo( SubscriptionManager.PROFILE_CLASS_OPERATIONAL); assertThat(subInfo.isEmbedded()).isTrue(); assertThat(subInfo.isRemovableEmbedded()).isFalse(); assertThat(subInfo.getNativeAccessRules()).isEqualTo(FAKE_NATIVE_ACCESS_RULES2); } } Loading
src/java/com/android/internal/telephony/euicc/EuiccCardController.java +27 −9 Original line number Diff line number Diff line Loading @@ -39,7 +39,9 @@ import android.text.TextUtils; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.UiccCard; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccPort; Loading @@ -51,6 +53,7 @@ import com.android.internal.telephony.uicc.euicc.async.AsyncResultCallback; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.List; /** Backing implementation of {@link EuiccCardManager}. */ public class EuiccCardController extends IEuiccCardController.Stub { Loading Loading @@ -649,9 +652,14 @@ public class EuiccCardController extends IEuiccCardController.Stub { @Override public void onResult(Void result) { Log.i(TAG, "Request subscription info list refresh after delete."); if (PhoneFactory.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( List.of(mUiccController.convertToPublicCardId(cardId)), null); } else { SubscriptionController.getInstance() .requestEmbeddedSubscriptionInfoListRefresh( mUiccController.convertToPublicCardId(cardId)); } try { callback.onComplete(EuiccCardManager.RESULT_OK); } catch (RemoteException exception) { Loading Loading @@ -701,9 +709,14 @@ public class EuiccCardController extends IEuiccCardController.Stub { @Override public void onResult(Void result) { Log.i(TAG, "Request subscription info list refresh after reset memory."); if (PhoneFactory.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( List.of(mUiccController.convertToPublicCardId(cardId)), null); } else { SubscriptionController.getInstance() .requestEmbeddedSubscriptionInfoListRefresh( mUiccController.convertToPublicCardId(cardId)); } try { callback.onComplete(EuiccCardManager.RESULT_OK); } catch (RemoteException exception) { Loading Loading @@ -1190,9 +1203,14 @@ public class EuiccCardController extends IEuiccCardController.Stub { @Override public void onResult(byte[] result) { Log.i(TAG, "Request subscription info list refresh after install."); if (PhoneFactory.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( List.of(mUiccController.convertToPublicCardId(cardId)), null); } else { SubscriptionController.getInstance() .requestEmbeddedSubscriptionInfoListRefresh( mUiccController.convertToPublicCardId(cardId)); } try { callback.onComplete(EuiccCardManager.RESULT_OK, result); } catch (RemoteException exception) { Loading
src/java/com/android/internal/telephony/euicc/EuiccController.java +10 −3 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ import com.android.internal.telephony.Phone; import com.android.internal.telephony.PhoneFactory; import com.android.internal.telephony.SubscriptionController; import com.android.internal.telephony.euicc.EuiccConnector.OtaStatusChangedCallback; import com.android.internal.telephony.subscription.SubscriptionManagerService; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccController; import com.android.internal.telephony.uicc.UiccPort; Loading Loading @@ -1585,10 +1586,16 @@ public class EuiccController extends IEuiccController.Stub { @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) public void refreshSubscriptionsAndSendResult( PendingIntent callbackIntent, int resultCode, Intent extrasIntent) { if (PhoneFactory.isSubscriptionManagerServiceEnabled()) { SubscriptionManagerService.getInstance().updateEmbeddedSubscriptions( List.of(mTelephonyManager.getCardIdForDefaultEuicc()), () -> sendResult(callbackIntent, resultCode, extrasIntent)); } else { SubscriptionController.getInstance() .requestEmbeddedSubscriptionInfoListRefresh( () -> sendResult(callbackIntent, resultCode, extrasIntent)); } } /** Dispatch the given callback intent with the given result code and data. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) Loading
src/java/com/android/internal/telephony/subscription/SubscriptionInfoInternal.java +18 −0 Original line number Diff line number Diff line Loading @@ -1764,6 +1764,24 @@ public class SubscriptionInfoInternal { return this; } /** * Set the native access rules for this subscription, if it is embedded and defines any. * This does not include access rules for non-embedded subscriptions. * * @param nativeAccessRules The native access rules for this subscription. * * @return The builder. */ @NonNull public Builder setNativeAccessRules(@NonNull List<UiccAccessRule> nativeAccessRules) { Objects.requireNonNull(nativeAccessRules); if (!nativeAccessRules.isEmpty()) { mNativeAccessRules = UiccAccessRule.encodeRules( nativeAccessRules.toArray(new UiccAccessRule[0])); } return this; } /** * Set the carrier certificates for this subscription that are saved in carrier configs. * This does not include access rules from the Uicc, whether embedded or non-embedded. Loading
src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java +182 −2 Original line number Diff line number Diff line Loading @@ -37,15 +37,21 @@ import android.os.ParcelUuid; import android.os.TelephonyServiceManager; import android.os.UserHandle; import android.provider.Settings; import android.service.carrier.CarrierIdentifier; import android.service.euicc.EuiccProfileInfo; import android.service.euicc.EuiccService; import android.service.euicc.GetEuiccProfileInfoListResult; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.SubscriptionManager.PhoneNumberSource; import android.telephony.SubscriptionManager.SimDisplayNameSource; import android.telephony.SubscriptionManager.SubscriptionType; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.TelephonyManager; import android.telephony.TelephonyRegistryManager; import android.telephony.UiccAccessRule; import android.telephony.euicc.EuiccManager; import android.text.TextUtils; import android.util.ArraySet; Loading @@ -54,11 +60,13 @@ import android.util.IndentingPrintWriter; import android.util.LocalLog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.CarrierResolver; import com.android.internal.telephony.ISetOpportunisticDataCallback; import com.android.internal.telephony.ISub; import com.android.internal.telephony.MultiSimSettingController; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyPermissions; import com.android.internal.telephony.euicc.EuiccController; import com.android.internal.telephony.subscription.SubscriptionDatabaseManager.SubscriptionDatabaseManagerCallback; import com.android.internal.telephony.uicc.IccUtils; import com.android.internal.telephony.uicc.UiccController; Loading Loading @@ -107,17 +115,31 @@ public class SubscriptionManagerService extends ISub.Stub { private final Context mContext; /** Telephony manager instance. */ @NonNull private final TelephonyManager mTelephonyManager; /** Subscription manager instance. */ @NonNull private final SubscriptionManager mSubscriptionManager; /** Euicc manager instance. */ /** * Euicc manager instance. Will be null if the device does not support * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. */ @Nullable private final EuiccManager mEuiccManager; /** Uicc controller instance. */ @NonNull private final UiccController mUiccController; /** * Euicc controller instance. Will be null if the device does not support * {@link PackageManager#FEATURE_TELEPHONY_EUICC}. */ @Nullable private EuiccController mEuiccController; /** The main handler of subscription manager service. */ @NonNull private final Handler mHandler; Loading Loading @@ -288,6 +310,7 @@ public class SubscriptionManagerService extends ISub.Stub { mTelephonyManager = context.getSystemService(TelephonyManager.class); mSubscriptionManager = context.getSystemService(SubscriptionManager.class); mEuiccManager = context.getSystemService(EuiccManager.class); mUiccController = UiccController.getInstance(); mHandler = new Handler(looper); TelephonyServiceManager.ServiceRegisterer subscriptionServiceRegisterer = Loading Loading @@ -402,6 +425,15 @@ public class SubscriptionManagerService extends ISub.Stub { }); updateDefaultSubId(); mHandler.post(() -> { // EuiccController is created after SubscriptionManagerService. So we need to get // the instance later in the handler. if (mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEPHONY_EUICC)) { mEuiccController = EuiccController.get(); } }); } /** Loading Loading @@ -669,6 +701,153 @@ public class SubscriptionManagerService extends ISub.Stub { subInfo.getSubscriptionId(), SubscriptionManager.INVALID_SIM_SLOT_INDEX)); } /** * This is only for internal use and the returned priority is arbitrary. The idea is to give a * higher value to name source that has higher priority to override other name sources. * * @param nameSource Source of display name. * * @return The priority. Higher value means higher priority. */ private static int getNameSourcePriority(@SimDisplayNameSource int nameSource) { int index = Arrays.asList( SubscriptionManager.NAME_SOURCE_UNKNOWN, SubscriptionManager.NAME_SOURCE_CARRIER_ID, SubscriptionManager.NAME_SOURCE_SIM_PNN, SubscriptionManager.NAME_SOURCE_SIM_SPN, SubscriptionManager.NAME_SOURCE_CARRIER, SubscriptionManager.NAME_SOURCE_USER_INPUT // user has highest priority. ).indexOf(nameSource); return Math.max(0, index); } /** * Get the embedded profile port index by ICCID. * * @param iccId The ICCID. * @return The port index. */ private int getEmbeddedProfilePortIndex(String iccId) { UiccSlot[] slots = UiccController.getInstance().getUiccSlots(); for (UiccSlot slot : slots) { if (slot != null && slot.isEuicc() && slot.getPortIndexFromIccId(iccId) != TelephonyManager.INVALID_PORT_INDEX) { return slot.getPortIndexFromIccId(iccId); } } return TelephonyManager.INVALID_PORT_INDEX; } /** * Pull the embedded subscription from {@link EuiccController} for the eUICC with the given list * of card IDs {@code cardIds}. * * @param cardIds The card ids of the embedded subscriptions. * @param callback Callback to be called upon completion. */ public void updateEmbeddedSubscriptions(@NonNull List<Integer> cardIds, @Nullable Runnable callback) { mHandler.post(() -> { // Do nothing if eUICCs are disabled. (Previous entries may remain in the cache, but // they are filtered out of list calls as long as EuiccManager.isEnabled returns false). if (mEuiccManager == null || !mEuiccManager.isEnabled()) { loge("updateEmbeddedSubscriptions: eUICC not enabled"); if (callback != null) { callback.run(); } return; } log("updateEmbeddedSubscriptions: start to get euicc profiles."); for (int cardId : cardIds) { GetEuiccProfileInfoListResult result = mEuiccController .blockingGetEuiccProfileInfoList(cardId); log("updateEmbeddedSubscriptions: cardId=" + cardId + ", result=" + result); if (result.getResult() != EuiccService.RESULT_OK) { loge("Failed to get euicc profile info. result=" + EuiccService.resultToString(result.getResult())); continue; } if (result.getProfiles() == null || result.getProfiles().isEmpty()) { loge("No profiles returned."); continue; } final boolean isRemovable = result.getIsRemovable(); for (EuiccProfileInfo embeddedProfile : result.getProfiles()) { SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternalByIccId(embeddedProfile.getIccid()); // The subscription does not exist in the database. Insert a new one here. if (subInfo == null) { subInfo = new SubscriptionInfoInternal.Builder() .setIccId(embeddedProfile.getIccid()) .build(); int subId = mSubscriptionDatabaseManager.insertSubscriptionInfo(subInfo); subInfo = new SubscriptionInfoInternal.Builder(subInfo) .setId(subId).build(); } int nameSource = subInfo.getDisplayNameSource(); int carrierId = subInfo.getCarrierId(); SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal .Builder(subInfo); builder.setEmbedded(1); List<UiccAccessRule> ruleList = embeddedProfile.getUiccAccessRules(); if (ruleList != null && !ruleList.isEmpty()) { builder.setNativeAccessRules(embeddedProfile.getUiccAccessRules()); } builder.setRemovableEmbedded(isRemovable ? 1 : 0); // override DISPLAY_NAME if the priority of existing nameSource is <= carrier if (getNameSourcePriority(nameSource) <= getNameSourcePriority( SubscriptionManager.NAME_SOURCE_CARRIER)) { builder.setDisplayName(embeddedProfile.getNickname()); builder.setDisplayNameSource(SubscriptionManager.NAME_SOURCE_CARRIER); } builder.setProfileClass(embeddedProfile.getProfileClass()); builder.setPortIndex(getEmbeddedProfilePortIndex(embeddedProfile.getIccid())); CarrierIdentifier cid = embeddedProfile.getCarrierIdentifier(); if (cid != null) { // Due to the limited subscription information, carrier id identified here // might not be accurate compared with CarrierResolver. Only update carrier // id if there is no valid carrier id present. if (carrierId == TelephonyManager.UNKNOWN_CARRIER_ID) { builder.setCarrierId(CarrierResolver .getCarrierIdFromIdentifier(mContext, cid)); } String mcc = cid.getMcc(); String mnc = cid.getMnc(); builder.setMcc(mcc); builder.setMnc(mnc); } // If cardId = unsupported or un-initialized, we have no reason to update DB. // Additionally, if the device does not support cardId for default eUICC, the // CARD_ID field should not contain the EID if (cardId >= 0 && mUiccController.getCardIdForDefaultEuicc() != TelephonyManager.UNSUPPORTED_CARD_ID) { builder.setCardString(mUiccController.convertToCardString(cardId)); } subInfo = builder.build(); log("updateEmbeddedSubscriptions: update subscription " + subInfo); mSubscriptionDatabaseManager.updateSubscription(subInfo); } } }); log("updateEmbeddedSubscriptions: Finished embedded subscription update."); if (callback != null) { callback.run(); } } /** * Get all subscription info records from SIMs that are inserted now or were inserted before. * Loading Loading @@ -905,7 +1084,8 @@ public class SubscriptionManagerService extends ISub.Stub { return mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(subInfo -> subInfo.isActive() || iccIds.contains(subInfo.getIccId()) || (mEuiccManager.isEnabled() && subInfo.isEmbedded())) || (mEuiccManager != null && mEuiccManager.isEnabled() && subInfo.isEmbedded())) .map(SubscriptionInfoInternal::toSubscriptionInfo) .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex) .thenComparing(SubscriptionInfo::getSubscriptionId)) Loading
tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java +82 −0 Original line number Diff line number Diff line Loading @@ -17,13 +17,19 @@ package com.android.internal.telephony.subscription; import static com.android.internal.telephony.TelephonyTestUtils.waitForMs; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_ID1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_ID2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_CARRIER_NAME2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_COUNTRY_CODE2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_ICCID1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_ICCID2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MCC1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MCC2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MNC1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_MNC2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_NATIVE_ACCESS_RULES1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_NATIVE_ACCESS_RULES2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER1; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_PHONE_NUMBER2; import static com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.FAKE_SUBSCRIPTION_INFO1; Loading Loading @@ -59,8 +65,13 @@ import android.os.ParcelUuid; import android.os.UserHandle; import android.provider.Settings; import android.provider.Telephony; import android.service.carrier.CarrierIdentifier; import android.service.euicc.EuiccProfileInfo; import android.service.euicc.EuiccService; import android.service.euicc.GetEuiccProfileInfoListResult; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.UiccAccessRule; import android.test.mock.MockContentResolver; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; Loading @@ -68,6 +79,7 @@ import android.testing.TestableLooper; import com.android.internal.telephony.ContextFixture; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.TelephonyTest; import com.android.internal.telephony.euicc.EuiccController; import com.android.internal.telephony.subscription.SubscriptionDatabaseManagerTest.SubscriptionProvider; import com.android.internal.telephony.subscription.SubscriptionManagerService.SubscriptionManagerServiceCallback; Loading @@ -84,6 +96,7 @@ import org.mockito.Mockito; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; @RunWith(AndroidTestingRunner.class) Loading @@ -100,6 +113,7 @@ public class SubscriptionManagerServiceTest extends TelephonyTest { // mocked private SubscriptionManagerServiceCallback mMockedSubscriptionManagerServiceCallback; private EuiccController mEuiccController; @Rule public TestRule compatChangeRule = new PlatformCompatChangeRule(); Loading @@ -108,10 +122,13 @@ public class SubscriptionManagerServiceTest extends TelephonyTest { public void setUp() throws Exception { logd("SubscriptionManagerServiceTest +Setup!"); super.setUp(getClass().getSimpleName()); mContextFixture.addSystemFeature(PackageManager.FEATURE_TELEPHONY_EUICC); setupMocksForTelephonyPermissions(Build.VERSION_CODES.UPSIDE_DOWN_CAKE); PropertyInvalidatedCache.disableForCurrentProcess("cache_key.is_compat_change_enabled"); doReturn(true).when(mTelephonyManager).isVoiceCapable(); mEuiccController = Mockito.mock(EuiccController.class); replaceInstance(EuiccController.class, "sInstance", null, mEuiccController); mMockedSubscriptionManagerServiceCallback = Mockito.mock( SubscriptionManagerServiceCallback.class); ((MockContentResolver) mContext.getContentResolver()).addProvider( Loading Loading @@ -681,4 +698,69 @@ public class SubscriptionManagerServiceTest extends TelephonyTest { 0, CALLING_PACKAGE, CALLING_FEATURE); assertThat(subInfo).isEqualTo(FAKE_SUBSCRIPTION_INFO1.toSubscriptionInfo()); } @Test public void testUpdateEmbeddedSubscriptions() { EuiccProfileInfo profileInfo1 = new EuiccProfileInfo.Builder(FAKE_ICCID1) .setIccid(FAKE_ICCID1) .setNickname(FAKE_CARRIER_NAME1) .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL) .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC1, FAKE_MNC1, null, null, null, null, FAKE_CARRIER_ID1, FAKE_CARRIER_ID1)) .setUiccAccessRule(Arrays.asList(UiccAccessRule.decodeRules( FAKE_NATIVE_ACCESS_RULES1))) .build(); EuiccProfileInfo profileInfo2 = new EuiccProfileInfo.Builder(FAKE_ICCID2) .setIccid(FAKE_ICCID2) .setNickname(FAKE_CARRIER_NAME2) .setProfileClass(SubscriptionManager.PROFILE_CLASS_OPERATIONAL) .setCarrierIdentifier(new CarrierIdentifier(FAKE_MCC2, FAKE_MNC2, null, null, null, null, FAKE_CARRIER_ID2, FAKE_CARRIER_ID2)) .setUiccAccessRule(Arrays.asList(UiccAccessRule.decodeRules( FAKE_NATIVE_ACCESS_RULES2))) .build(); GetEuiccProfileInfoListResult result = new GetEuiccProfileInfoListResult( EuiccService.RESULT_OK, new EuiccProfileInfo[]{profileInfo1}, false); doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(1)); result = new GetEuiccProfileInfoListResult(EuiccService.RESULT_OK, new EuiccProfileInfo[]{profileInfo2}, false); doReturn(result).when(mEuiccController).blockingGetEuiccProfileInfoList(eq(2)); doReturn(FAKE_ICCID1).when(mUiccController).convertToCardString(eq(1)); doReturn(FAKE_ICCID2).when(mUiccController).convertToCardString(eq(2)); mSubscriptionManagerServiceUT.updateEmbeddedSubscriptions(List.of(1, 2), null); processAllMessages(); SubscriptionInfoInternal subInfo = mSubscriptionManagerServiceUT .getSubscriptionInfoInternal(1); assertThat(subInfo.getSubscriptionId()).isEqualTo(1); assertThat(subInfo.getSimSlotIndex()).isEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX); assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID1); assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME1); assertThat(subInfo.getDisplayNameSource()).isEqualTo( SubscriptionManager.NAME_SOURCE_CARRIER); assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC1); assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC1); assertThat(subInfo.getProfileClass()).isEqualTo( SubscriptionManager.PROFILE_CLASS_OPERATIONAL); assertThat(subInfo.isEmbedded()).isTrue(); assertThat(subInfo.isRemovableEmbedded()).isFalse(); assertThat(subInfo.getNativeAccessRules()).isEqualTo(FAKE_NATIVE_ACCESS_RULES1); subInfo = mSubscriptionManagerServiceUT.getSubscriptionInfoInternal(2); assertThat(subInfo.getSubscriptionId()).isEqualTo(2); assertThat(subInfo.getSimSlotIndex()).isEqualTo(SubscriptionManager.INVALID_SIM_SLOT_INDEX); assertThat(subInfo.getIccId()).isEqualTo(FAKE_ICCID2); assertThat(subInfo.getDisplayName()).isEqualTo(FAKE_CARRIER_NAME2); assertThat(subInfo.getDisplayNameSource()).isEqualTo( SubscriptionManager.NAME_SOURCE_CARRIER); assertThat(subInfo.getMcc()).isEqualTo(FAKE_MCC2); assertThat(subInfo.getMnc()).isEqualTo(FAKE_MNC2); assertThat(subInfo.getProfileClass()).isEqualTo( SubscriptionManager.PROFILE_CLASS_OPERATIONAL); assertThat(subInfo.isEmbedded()).isTrue(); assertThat(subInfo.isRemovableEmbedded()).isFalse(); assertThat(subInfo.getNativeAccessRules()).isEqualTo(FAKE_NATIVE_ACCESS_RULES2); } }