Loading src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java +241 −78 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; Loading Loading @@ -123,6 +124,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; /** * The subscription manager service is the backend service of {@link SubscriptionManager}. Loading Loading @@ -188,6 +190,18 @@ public class SubscriptionManagerService extends ISub.Stub { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID = 213902861L; /** * Apps targeting on Android V and beyond can only see subscriptions accessible by them * according to its user Id. */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) public static final long FILTER_ACCESSIBLE_SUBS_BY_USER = 296076674L; /** Wrap Binder methods for testing. */ @NonNull private static final BinderWrapper BINDER_WRAPPER = new BinderWrapper(); /** Instance of subscription manager service. */ @NonNull private static SubscriptionManagerService sInstance; Loading Loading @@ -283,6 +297,12 @@ public class SubscriptionManagerService extends ISub.Stub { @NonNull private final int[] mSimState; /** * {@code true} if a user profile can only see the SIMs associated with it, unless it possesses * no SIMs on the device. */ private Map<Integer, List<Integer>> mUserIdToAvailableSubs = new ConcurrentHashMap<>(); /** * Slot index/subscription map that automatically invalidate cache in * {@link SubscriptionManager}. Loading Loading @@ -356,6 +376,14 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** Binder Wrapper for test mocking. */ @VisibleForTesting public static class BinderWrapper { @NonNull public UserHandle getCallingUserHandle() { return Binder.getCallingUserHandle(); } } /** * This is the callback used for listening events from {@link SubscriptionManagerService}. */ Loading Loading @@ -510,6 +538,8 @@ public class SubscriptionManagerService extends ISub.Stub { */ @Override public void onSubscriptionChanged(int subId) { updateUserIdToAvailableSubs(); mSubscriptionManagerServiceCallbacks.forEach( callback -> callback.invokeFromExecutor( () -> callback.onSubscriptionChanged(subId))); Loading Loading @@ -1654,7 +1684,8 @@ public class SubscriptionManagerService extends ISub.Stub { } /** * Get all subscription info records from SIMs that are inserted now or previously inserted. * Get all subscription info records from SIMs visible to the calling user that are inserted now * or previously inserted. * * <p> * If the caller does not have {@link Manifest.permission#READ_PHONE_NUMBERS} permission, Loading Loading @@ -1694,8 +1725,7 @@ public class SubscriptionManagerService extends ISub.Stub { throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or " + "carrier privilege"); } return mSubscriptionDatabaseManager.getAllSubscriptions().stream() return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle()) // callers have READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE can get a full // list. Carrier apps can only get the subscriptions they have privileged. .filter(subInfo -> TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow( Loading Loading @@ -1824,8 +1854,8 @@ public class SubscriptionManagerService extends ISub.Stub { } /** * Get the SubscriptionInfo(s) of the active subscriptions. The records will be sorted * by {@link SubscriptionInfo#getSimSlotIndex} then by * Get the SubscriptionInfo(s) of the active subscriptions for calling user. The records will be * sorted by {@link SubscriptionInfo#getSimSlotIndex} then by * {@link SubscriptionInfo#getSubscriptionId}. * * @param callingPackage The package making the call. Loading @@ -1848,7 +1878,7 @@ public class SubscriptionManagerService extends ISub.Stub { // on the subs it has carrier privilege. if (!TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId, "getAllSubInfoList")) { "getActiveSubscriptionInfoList")) { // Ideally we should avoid silent failure, but since this API has already been used by // many apps and they do not expect the security exception, we return an empty list // here so it's consistent with pre-U behavior. Loading @@ -1856,14 +1886,13 @@ public class SubscriptionManagerService extends ISub.Stub { + "permission. Returning empty list here."); return Collections.emptyList(); } return mSubscriptionDatabaseManager.getAllSubscriptions().stream() return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle()) .filter(SubscriptionInfoInternal::isActive) // Remove the identifier if the caller does not have sufficient permission. // carrier apps will get full subscription info on the subscriptions associated // to them. .map(subInfo -> conditionallyRemoveIdentifiers(subInfo.toSubscriptionInfo(), callingPackage, callingFeatureId, "getAllSubInfoList")) callingPackage, callingFeatureId, "getActiveSubscriptionInfoList")) .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex) .thenComparing(SubscriptionInfo::getSubscriptionId)) .collect(Collectors.toList()); Loading Loading @@ -1893,13 +1922,7 @@ public class SubscriptionManagerService extends ISub.Stub { throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or " + "carrier privilege"); } final long token = Binder.clearCallingIdentity(); try { return getActiveSubIdList(false).length; } finally { Binder.restoreCallingIdentity(token); } return getActiveSubIdListAsUser(false, BINDER_WRAPPER.getCallingUserHandle()).length; } /** Loading Loading @@ -1930,10 +1953,18 @@ public class SubscriptionManagerService extends ISub.Stub { @Nullable String callingFeatureId) { enforcePermissions("getAvailableSubscriptionInfoList", Manifest.permission.READ_PRIVILEGED_PHONE_STATE); return getAvailableSubscriptionsInternalStream() .sorted(Comparator.comparing(SubscriptionInfoInternal::getSimSlotIndex) .thenComparing(SubscriptionInfoInternal::getSubscriptionId)) .map(SubscriptionInfoInternal::toSubscriptionInfo) .collect(Collectors.toList()); // Now that all security checks pass, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { } /** * @return all the subscriptions visible to user on the device. */ private Stream<SubscriptionInfoInternal> getAvailableSubscriptionsInternalStream() { // Available eSIM profiles are reported by EuiccManager. However for physical SIMs if // they are in inactive slot or programmatically disabled, they are still considered // available. In this case we get their iccid from slot info and include their Loading @@ -1943,14 +1974,21 @@ public class SubscriptionManagerService extends ISub.Stub { return mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(subInfo -> subInfo.isActive() || iccIds.contains(subInfo.getIccId()) || (mEuiccManager != null && mEuiccManager.isEnabled() && subInfo.isEmbedded())) .map(SubscriptionInfoInternal::toSubscriptionInfo) .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex) .thenComparing(SubscriptionInfo::getSubscriptionId)) .collect(Collectors.toList()); } finally { Binder.restoreCallingIdentity(identity); && subInfo.isEmbedded())); } /** * Tracks for each user Id, a list of subscriptions associated with it. * A profile is barred from seeing unassociated subscriptions if it has its own subscription * which is available to choose from the device. */ private void updateUserIdToAvailableSubs() { mUserIdToAvailableSubs = getAvailableSubscriptionsInternalStream() .collect(Collectors.groupingBy( SubscriptionInfoInternal::getUserId, Collectors.mapping(SubscriptionInfoInternal::getSubscriptionId, Collectors.toList()))); log("updateUserIdToAvailableSubs: " + mUserIdToAvailableSubs); } /** Loading Loading @@ -1985,8 +2023,7 @@ public class SubscriptionManagerService extends ISub.Stub { // Verify that the callingPackage belongs to the calling UID mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); return mSubscriptionDatabaseManager.getAllSubscriptions().stream() return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle()) .map(SubscriptionInfoInternal::toSubscriptionInfo) .filter(subInfo -> subInfo.isEmbedded() && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) Loading Loading @@ -2777,10 +2814,37 @@ public class SubscriptionManagerService extends ISub.Stub { /** * @return The default subscription id. * @deprecated Use {@link #getDefaultSubIdAsUser}. */ @Override public int getDefaultSubId() { return mDefaultSubId.get(); return getDefaultSubIdAsUser(BINDER_WRAPPER.getCallingUserHandle().getIdentifier()); } /** * @param userId The given user Id to check. * @return The default subscription id. */ @Override public int getDefaultSubIdAsUser(@UserIdInt int userId) { return getDefaultAsUser(userId, mDefaultSubId.get()); } /** * Get the default subscription visible to the caller. * @param userId The calling user Id. * @param defaultValue Useful if the user owns more than one subscription. * @return The subscription Id default to use. */ private int getDefaultAsUser(@UserIdInt int userId, int defaultValue) { List<SubscriptionInfoInternal> subInfos = getSubscriptionInfoStreamAsUser(UserHandle.of(userId)) .filter(SubscriptionInfoInternal::isActive) .toList(); if (subInfos.size() == 1) { return subInfos.get(0).getSubscriptionId(); } return defaultValue; } /** Loading Loading @@ -2872,10 +2936,20 @@ public class SubscriptionManagerService extends ISub.Stub { /** * @return The default subscription id for voice. * @deprecated Use {@link #getDefaultVoiceSubIdAsUser}. */ @Override public int getDefaultVoiceSubId() { return mDefaultVoiceSubId.get(); return getDefaultVoiceSubIdAsUser(BINDER_WRAPPER.getCallingUserHandle().getIdentifier()); } /** * @param userId The calling user Id. * @return The default voice subscription id. */ @Override public int getDefaultVoiceSubIdAsUser(@UserIdInt int userId) { return getDefaultAsUser(userId, mDefaultVoiceSubId.get()); } /** Loading Loading @@ -2918,10 +2992,24 @@ public class SubscriptionManagerService extends ISub.Stub { /** * @return The default subscription id for SMS. * @deprecated Use {@link #getDefaultSmsSubIdAsUser}. */ @Override public int getDefaultSmsSubId() { return mDefaultSmsSubId.get(); return getDefaultSmsSubIdAsUser(BINDER_WRAPPER.getCallingUserHandle().getIdentifier()); } /** * Get the default sms subscription id associated with the user. When a subscription is * associated with personal profile or work profile, the default sms subscription id will be * always the subscription it is associated with. * * @param userId The given user Id to check. * @return The default voice id. */ @Override public int getDefaultSmsSubIdAsUser(@UserIdInt int userId) { return getDefaultAsUser(userId, mDefaultSmsSubId.get()); } /** Loading Loading @@ -2978,19 +3066,30 @@ public class SubscriptionManagerService extends ISub.Stub { @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubIdList(boolean visibleOnly) { enforcePermissions("getActiveSubIdList", Manifest.permission.READ_PRIVILEGED_PHONE_STATE); // UserHandle.ALL because this API is exposed as system API. return getActiveSubIdListAsUser(visibleOnly, UserHandle.ALL); } final long token = Binder.clearCallingIdentity(); try { /** * Get the active subscription id list as user. * Must be used before clear Binder identity. * * @param visibleOnly {@code true} if only includes user visible subscription's sub id. * @param user If {@code null}, uses the calling user handle to judge which subscriptions are * accessible to the caller. * @return List of the active subscription id. */ private int[] getActiveSubIdListAsUser(boolean visibleOnly, @NonNull final UserHandle user) { return mSlotIndexToSubId.values().stream() .filter(subId -> { SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternal(subId); return subInfo != null && (!visibleOnly || subInfo.isVisible()); }) return subInfo != null && (!visibleOnly || subInfo.isVisible()) && isSubscriptionAssociatedWithUserInternal( subInfo, user.getIdentifier()); }) .mapToInt(x -> x) .toArray(); } finally { Binder.restoreCallingIdentity(token); } } /** Loading Loading @@ -3654,23 +3753,25 @@ public class SubscriptionManagerService extends ISub.Stub { @NonNull UserHandle userHandle) { enforcePermissions("isSubscriptionAssociatedWithUser", Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION); long token = Binder.clearCallingIdentity(); try { SubscriptionInfoInternal subInfoInternal = mSubscriptionDatabaseManager .getSubscriptionInfoInternal(subscriptionId); // Throw IAE if no record of the sub's association state. if (mSubscriptionDatabaseManager.getSubscriptionInfoInternal(subscriptionId) == null) { if (subInfoInternal == null) { throw new IllegalArgumentException( "[isSubscriptionAssociatedWithUser]: Subscription doesn't exist: " + subscriptionId); } if (mFeatureFlags.workProfileApiSplit()) { return isSubscriptionAssociatedWithUserInternal( subInfoInternal, userHandle.getIdentifier()); } long token = Binder.clearCallingIdentity(); try { // Get list of subscriptions associated with this user. List<SubscriptionInfo> associatedSubscriptionsList = getSubscriptionInfoListAssociatedWithUser(userHandle); if (associatedSubscriptionsList.isEmpty()) { return false; } // Return true if required subscription is present in associated subscriptions list. for (SubscriptionInfo subInfo: associatedSubscriptionsList) { if (subInfo.getSubscriptionId() == subscriptionId) { Loading @@ -3683,6 +3784,25 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * @param subInfo The subscription info to check. * @param userId The caller user Id. * @return {@code true} if the given user Id is allowed to access to the given subscription. */ private boolean isSubscriptionAssociatedWithUserInternal( @NonNull SubscriptionInfoInternal subInfo, @UserIdInt int userId) { if (!mFeatureFlags.workProfileApiSplit() || !CompatChanges.isChangeEnabled(FILTER_ACCESSIBLE_SUBS_BY_USER, Binder.getCallingUid())) { return true; } return subInfo.getUserId() == userId // Can access the unassociated sub if the user doesn't have its own. || (subInfo.getUserId() == UserHandle.USER_NULL && mUserIdToAvailableSubs.get(userId) == null) || userId == UserHandle.USER_ALL; } /** * Get list of subscriptions associated with user. * Loading @@ -3696,11 +3816,18 @@ public class SubscriptionManagerService extends ISub.Stub { * */ @Override public @NonNull List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser( @NonNull public List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser( @NonNull UserHandle userHandle) { enforcePermissions("getSubscriptionInfoListAssociatedWithUser", Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION); if (mFeatureFlags.workProfileApiSplit()) { return getSubscriptionInfoStreamAsUser(userHandle) .map(SubscriptionInfoInternal::toSubscriptionInfo) .collect(Collectors.toList()); } long token = Binder.clearCallingIdentity(); try { List<SubscriptionInfoInternal> subInfoList = mSubscriptionDatabaseManager Loading @@ -3724,19 +3851,44 @@ public class SubscriptionManagerService extends ISub.Stub { UserManager userManager = mContext.getSystemService(UserManager.class); if ((userManager != null) && (userManager.isManagedProfile(userHandle.getIdentifier()))) { // For work profile, return subscriptions associated only with work profile // For work profile, return subscriptions associated only with work profile even // if it's empty. return subscriptionsAssociatedWithUser; } // For all other profiles, if subscriptionsAssociatedWithUser is empty return all the // subscriptionsWithNoAssociation. return subscriptionsAssociatedWithUser.isEmpty() ? subscriptionsWithNoAssociation : subscriptionsAssociatedWithUser; // For all other profiles, if subscriptionsAssociatedWithUser is empty return all // the subscriptionsWithNoAssociation. return subscriptionsAssociatedWithUser.isEmpty() ? subscriptionsWithNoAssociation : subscriptionsAssociatedWithUser; } finally { Binder.restoreCallingIdentity(token); } } /** * @return All active subscriptions. */ @NonNull private List<SubscriptionInfoInternal> getActiveSubscriptionInfoListAllUser() { return mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(SubscriptionInfoInternal::isActive) .collect(Collectors.toList()); } /** * Get subscriptions accessible to the caller user. * * @param user The user to check. * @return a stream of accessible internal subscriptions. */ @NonNull private Stream<SubscriptionInfoInternal> getSubscriptionInfoStreamAsUser( @NonNull final UserHandle user) { return mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(info -> isSubscriptionAssociatedWithUserInternal( info, user.getIdentifier())); } /** * Called during setup wizard restore flow to attempt to restore the backed up sim-specific * configs to device for all existing SIMs in the subscription database {@link SimInfo}. Loading Loading @@ -3804,14 +3956,26 @@ public class SubscriptionManagerService extends ISub.Stub { * @throws SecurityException if the caller does not have any permissions. */ private void enforcePermissions(@Nullable String message, @NonNull String ...permissions) { if (!hasPermissions(permissions)) { throw new SecurityException( message + ". Does not have any of the following permissions. " + Arrays.toString(permissions)); } } /** * Check have any of the permissions * @param permissions The permissions to check. * @return {@code true} if the caller has one of the given permissions. */ private boolean hasPermissions(@NonNull String ...permissions) { for (String permission : permissions) { if (mContext.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) { return; return true; } } throw new SecurityException(message + ". Does not have any of the following permissions. " + Arrays.toString(permissions)); return false; } /** Loading @@ -3835,9 +3999,8 @@ public class SubscriptionManagerService extends ISub.Stub { */ @Nullable public SubscriptionInfo getSubscriptionInfo(int subId) { SubscriptionInfoInternal subscriptionInfoInternal = getSubscriptionInfoInternal(subId); return subscriptionInfoInternal != null ? subscriptionInfoInternal.toSubscriptionInfo() : null; SubscriptionInfoInternal infoInternal = getSubscriptionInfoInternal(subId); return infoInternal != null ? infoInternal.toSubscriptionInfo() : null; } /** Loading Loading @@ -3954,8 +4117,7 @@ public class SubscriptionManagerService extends ISub.Stub { */ @VisibleForTesting public void updateGroupDisabled() { List<SubscriptionInfo> activeSubscriptions = getActiveSubscriptionInfoList( mContext.getOpPackageName(), mContext.getFeatureId()); List<SubscriptionInfoInternal> activeSubscriptions = getActiveSubscriptionInfoListAllUser(); for (SubscriptionInfo oppSubInfo : getOpportunisticSubscriptions( mContext.getOpPackageName(), mContext.getFeatureId())) { boolean groupDisabled = activeSubscriptions.stream() Loading Loading @@ -4062,6 +4224,7 @@ public class SubscriptionManagerService extends ISub.Stub { pw.println("activeDataSubId=" + getActiveDataSubscriptionId()); pw.println("defaultSmsSubId=" + getDefaultSmsSubId()); pw.println("areAllSubscriptionsLoaded=" + areAllSubscriptionsLoaded()); pw.println("mUserIdToAvailableSubs=" + mUserIdToAvailableSubs); pw.println(); for (int i = 0; i < mSimState.length; i++) { pw.println("mSimState[" + i + "]=" Loading tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java +307 −7 File changed.Preview size limit exceeded, changes collapsed. Show changes Loading
src/java/com/android/internal/telephony/subscription/SubscriptionManagerService.java +241 −78 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.UserIdInt; import android.app.AppOpsManager; import android.app.PendingIntent; import android.app.compat.CompatChanges; Loading Loading @@ -123,6 +124,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; /** * The subscription manager service is the backend service of {@link SubscriptionManager}. Loading Loading @@ -188,6 +190,18 @@ public class SubscriptionManagerService extends ISub.Stub { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) public static final long REQUIRE_DEVICE_IDENTIFIERS_FOR_GROUP_UUID = 213902861L; /** * Apps targeting on Android V and beyond can only see subscriptions accessible by them * according to its user Id. */ @ChangeId @EnabledSince(targetSdkVersion = Build.VERSION_CODES.VANILLA_ICE_CREAM) public static final long FILTER_ACCESSIBLE_SUBS_BY_USER = 296076674L; /** Wrap Binder methods for testing. */ @NonNull private static final BinderWrapper BINDER_WRAPPER = new BinderWrapper(); /** Instance of subscription manager service. */ @NonNull private static SubscriptionManagerService sInstance; Loading Loading @@ -283,6 +297,12 @@ public class SubscriptionManagerService extends ISub.Stub { @NonNull private final int[] mSimState; /** * {@code true} if a user profile can only see the SIMs associated with it, unless it possesses * no SIMs on the device. */ private Map<Integer, List<Integer>> mUserIdToAvailableSubs = new ConcurrentHashMap<>(); /** * Slot index/subscription map that automatically invalidate cache in * {@link SubscriptionManager}. Loading Loading @@ -356,6 +376,14 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** Binder Wrapper for test mocking. */ @VisibleForTesting public static class BinderWrapper { @NonNull public UserHandle getCallingUserHandle() { return Binder.getCallingUserHandle(); } } /** * This is the callback used for listening events from {@link SubscriptionManagerService}. */ Loading Loading @@ -510,6 +538,8 @@ public class SubscriptionManagerService extends ISub.Stub { */ @Override public void onSubscriptionChanged(int subId) { updateUserIdToAvailableSubs(); mSubscriptionManagerServiceCallbacks.forEach( callback -> callback.invokeFromExecutor( () -> callback.onSubscriptionChanged(subId))); Loading Loading @@ -1654,7 +1684,8 @@ public class SubscriptionManagerService extends ISub.Stub { } /** * Get all subscription info records from SIMs that are inserted now or previously inserted. * Get all subscription info records from SIMs visible to the calling user that are inserted now * or previously inserted. * * <p> * If the caller does not have {@link Manifest.permission#READ_PHONE_NUMBERS} permission, Loading Loading @@ -1694,8 +1725,7 @@ public class SubscriptionManagerService extends ISub.Stub { throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or " + "carrier privilege"); } return mSubscriptionDatabaseManager.getAllSubscriptions().stream() return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle()) // callers have READ_PHONE_STATE or READ_PRIVILEGED_PHONE_STATE can get a full // list. Carrier apps can only get the subscriptions they have privileged. .filter(subInfo -> TelephonyPermissions.checkCallingOrSelfReadPhoneStateNoThrow( Loading Loading @@ -1824,8 +1854,8 @@ public class SubscriptionManagerService extends ISub.Stub { } /** * Get the SubscriptionInfo(s) of the active subscriptions. The records will be sorted * by {@link SubscriptionInfo#getSimSlotIndex} then by * Get the SubscriptionInfo(s) of the active subscriptions for calling user. The records will be * sorted by {@link SubscriptionInfo#getSimSlotIndex} then by * {@link SubscriptionInfo#getSubscriptionId}. * * @param callingPackage The package making the call. Loading @@ -1848,7 +1878,7 @@ public class SubscriptionManagerService extends ISub.Stub { // on the subs it has carrier privilege. if (!TelephonyPermissions.checkReadPhoneStateOnAnyActiveSub(mContext, Binder.getCallingPid(), Binder.getCallingUid(), callingPackage, callingFeatureId, "getAllSubInfoList")) { "getActiveSubscriptionInfoList")) { // Ideally we should avoid silent failure, but since this API has already been used by // many apps and they do not expect the security exception, we return an empty list // here so it's consistent with pre-U behavior. Loading @@ -1856,14 +1886,13 @@ public class SubscriptionManagerService extends ISub.Stub { + "permission. Returning empty list here."); return Collections.emptyList(); } return mSubscriptionDatabaseManager.getAllSubscriptions().stream() return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle()) .filter(SubscriptionInfoInternal::isActive) // Remove the identifier if the caller does not have sufficient permission. // carrier apps will get full subscription info on the subscriptions associated // to them. .map(subInfo -> conditionallyRemoveIdentifiers(subInfo.toSubscriptionInfo(), callingPackage, callingFeatureId, "getAllSubInfoList")) callingPackage, callingFeatureId, "getActiveSubscriptionInfoList")) .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex) .thenComparing(SubscriptionInfo::getSubscriptionId)) .collect(Collectors.toList()); Loading Loading @@ -1893,13 +1922,7 @@ public class SubscriptionManagerService extends ISub.Stub { throw new SecurityException("Need READ_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE, or " + "carrier privilege"); } final long token = Binder.clearCallingIdentity(); try { return getActiveSubIdList(false).length; } finally { Binder.restoreCallingIdentity(token); } return getActiveSubIdListAsUser(false, BINDER_WRAPPER.getCallingUserHandle()).length; } /** Loading Loading @@ -1930,10 +1953,18 @@ public class SubscriptionManagerService extends ISub.Stub { @Nullable String callingFeatureId) { enforcePermissions("getAvailableSubscriptionInfoList", Manifest.permission.READ_PRIVILEGED_PHONE_STATE); return getAvailableSubscriptionsInternalStream() .sorted(Comparator.comparing(SubscriptionInfoInternal::getSimSlotIndex) .thenComparing(SubscriptionInfoInternal::getSubscriptionId)) .map(SubscriptionInfoInternal::toSubscriptionInfo) .collect(Collectors.toList()); // Now that all security checks pass, perform the operation as ourselves. final long identity = Binder.clearCallingIdentity(); try { } /** * @return all the subscriptions visible to user on the device. */ private Stream<SubscriptionInfoInternal> getAvailableSubscriptionsInternalStream() { // Available eSIM profiles are reported by EuiccManager. However for physical SIMs if // they are in inactive slot or programmatically disabled, they are still considered // available. In this case we get their iccid from slot info and include their Loading @@ -1943,14 +1974,21 @@ public class SubscriptionManagerService extends ISub.Stub { return mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(subInfo -> subInfo.isActive() || iccIds.contains(subInfo.getIccId()) || (mEuiccManager != null && mEuiccManager.isEnabled() && subInfo.isEmbedded())) .map(SubscriptionInfoInternal::toSubscriptionInfo) .sorted(Comparator.comparing(SubscriptionInfo::getSimSlotIndex) .thenComparing(SubscriptionInfo::getSubscriptionId)) .collect(Collectors.toList()); } finally { Binder.restoreCallingIdentity(identity); && subInfo.isEmbedded())); } /** * Tracks for each user Id, a list of subscriptions associated with it. * A profile is barred from seeing unassociated subscriptions if it has its own subscription * which is available to choose from the device. */ private void updateUserIdToAvailableSubs() { mUserIdToAvailableSubs = getAvailableSubscriptionsInternalStream() .collect(Collectors.groupingBy( SubscriptionInfoInternal::getUserId, Collectors.mapping(SubscriptionInfoInternal::getSubscriptionId, Collectors.toList()))); log("updateUserIdToAvailableSubs: " + mUserIdToAvailableSubs); } /** Loading Loading @@ -1985,8 +2023,7 @@ public class SubscriptionManagerService extends ISub.Stub { // Verify that the callingPackage belongs to the calling UID mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackage); return mSubscriptionDatabaseManager.getAllSubscriptions().stream() return getSubscriptionInfoStreamAsUser(BINDER_WRAPPER.getCallingUserHandle()) .map(SubscriptionInfoInternal::toSubscriptionInfo) .filter(subInfo -> subInfo.isEmbedded() && mSubscriptionManager.canManageSubscription(subInfo, callingPackage)) Loading Loading @@ -2777,10 +2814,37 @@ public class SubscriptionManagerService extends ISub.Stub { /** * @return The default subscription id. * @deprecated Use {@link #getDefaultSubIdAsUser}. */ @Override public int getDefaultSubId() { return mDefaultSubId.get(); return getDefaultSubIdAsUser(BINDER_WRAPPER.getCallingUserHandle().getIdentifier()); } /** * @param userId The given user Id to check. * @return The default subscription id. */ @Override public int getDefaultSubIdAsUser(@UserIdInt int userId) { return getDefaultAsUser(userId, mDefaultSubId.get()); } /** * Get the default subscription visible to the caller. * @param userId The calling user Id. * @param defaultValue Useful if the user owns more than one subscription. * @return The subscription Id default to use. */ private int getDefaultAsUser(@UserIdInt int userId, int defaultValue) { List<SubscriptionInfoInternal> subInfos = getSubscriptionInfoStreamAsUser(UserHandle.of(userId)) .filter(SubscriptionInfoInternal::isActive) .toList(); if (subInfos.size() == 1) { return subInfos.get(0).getSubscriptionId(); } return defaultValue; } /** Loading Loading @@ -2872,10 +2936,20 @@ public class SubscriptionManagerService extends ISub.Stub { /** * @return The default subscription id for voice. * @deprecated Use {@link #getDefaultVoiceSubIdAsUser}. */ @Override public int getDefaultVoiceSubId() { return mDefaultVoiceSubId.get(); return getDefaultVoiceSubIdAsUser(BINDER_WRAPPER.getCallingUserHandle().getIdentifier()); } /** * @param userId The calling user Id. * @return The default voice subscription id. */ @Override public int getDefaultVoiceSubIdAsUser(@UserIdInt int userId) { return getDefaultAsUser(userId, mDefaultVoiceSubId.get()); } /** Loading Loading @@ -2918,10 +2992,24 @@ public class SubscriptionManagerService extends ISub.Stub { /** * @return The default subscription id for SMS. * @deprecated Use {@link #getDefaultSmsSubIdAsUser}. */ @Override public int getDefaultSmsSubId() { return mDefaultSmsSubId.get(); return getDefaultSmsSubIdAsUser(BINDER_WRAPPER.getCallingUserHandle().getIdentifier()); } /** * Get the default sms subscription id associated with the user. When a subscription is * associated with personal profile or work profile, the default sms subscription id will be * always the subscription it is associated with. * * @param userId The given user Id to check. * @return The default voice id. */ @Override public int getDefaultSmsSubIdAsUser(@UserIdInt int userId) { return getDefaultAsUser(userId, mDefaultSmsSubId.get()); } /** Loading Loading @@ -2978,19 +3066,30 @@ public class SubscriptionManagerService extends ISub.Stub { @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int[] getActiveSubIdList(boolean visibleOnly) { enforcePermissions("getActiveSubIdList", Manifest.permission.READ_PRIVILEGED_PHONE_STATE); // UserHandle.ALL because this API is exposed as system API. return getActiveSubIdListAsUser(visibleOnly, UserHandle.ALL); } final long token = Binder.clearCallingIdentity(); try { /** * Get the active subscription id list as user. * Must be used before clear Binder identity. * * @param visibleOnly {@code true} if only includes user visible subscription's sub id. * @param user If {@code null}, uses the calling user handle to judge which subscriptions are * accessible to the caller. * @return List of the active subscription id. */ private int[] getActiveSubIdListAsUser(boolean visibleOnly, @NonNull final UserHandle user) { return mSlotIndexToSubId.values().stream() .filter(subId -> { SubscriptionInfoInternal subInfo = mSubscriptionDatabaseManager .getSubscriptionInfoInternal(subId); return subInfo != null && (!visibleOnly || subInfo.isVisible()); }) return subInfo != null && (!visibleOnly || subInfo.isVisible()) && isSubscriptionAssociatedWithUserInternal( subInfo, user.getIdentifier()); }) .mapToInt(x -> x) .toArray(); } finally { Binder.restoreCallingIdentity(token); } } /** Loading Loading @@ -3654,23 +3753,25 @@ public class SubscriptionManagerService extends ISub.Stub { @NonNull UserHandle userHandle) { enforcePermissions("isSubscriptionAssociatedWithUser", Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION); long token = Binder.clearCallingIdentity(); try { SubscriptionInfoInternal subInfoInternal = mSubscriptionDatabaseManager .getSubscriptionInfoInternal(subscriptionId); // Throw IAE if no record of the sub's association state. if (mSubscriptionDatabaseManager.getSubscriptionInfoInternal(subscriptionId) == null) { if (subInfoInternal == null) { throw new IllegalArgumentException( "[isSubscriptionAssociatedWithUser]: Subscription doesn't exist: " + subscriptionId); } if (mFeatureFlags.workProfileApiSplit()) { return isSubscriptionAssociatedWithUserInternal( subInfoInternal, userHandle.getIdentifier()); } long token = Binder.clearCallingIdentity(); try { // Get list of subscriptions associated with this user. List<SubscriptionInfo> associatedSubscriptionsList = getSubscriptionInfoListAssociatedWithUser(userHandle); if (associatedSubscriptionsList.isEmpty()) { return false; } // Return true if required subscription is present in associated subscriptions list. for (SubscriptionInfo subInfo: associatedSubscriptionsList) { if (subInfo.getSubscriptionId() == subscriptionId) { Loading @@ -3683,6 +3784,25 @@ public class SubscriptionManagerService extends ISub.Stub { } } /** * @param subInfo The subscription info to check. * @param userId The caller user Id. * @return {@code true} if the given user Id is allowed to access to the given subscription. */ private boolean isSubscriptionAssociatedWithUserInternal( @NonNull SubscriptionInfoInternal subInfo, @UserIdInt int userId) { if (!mFeatureFlags.workProfileApiSplit() || !CompatChanges.isChangeEnabled(FILTER_ACCESSIBLE_SUBS_BY_USER, Binder.getCallingUid())) { return true; } return subInfo.getUserId() == userId // Can access the unassociated sub if the user doesn't have its own. || (subInfo.getUserId() == UserHandle.USER_NULL && mUserIdToAvailableSubs.get(userId) == null) || userId == UserHandle.USER_ALL; } /** * Get list of subscriptions associated with user. * Loading @@ -3696,11 +3816,18 @@ public class SubscriptionManagerService extends ISub.Stub { * */ @Override public @NonNull List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser( @NonNull public List<SubscriptionInfo> getSubscriptionInfoListAssociatedWithUser( @NonNull UserHandle userHandle) { enforcePermissions("getSubscriptionInfoListAssociatedWithUser", Manifest.permission.MANAGE_SUBSCRIPTION_USER_ASSOCIATION); if (mFeatureFlags.workProfileApiSplit()) { return getSubscriptionInfoStreamAsUser(userHandle) .map(SubscriptionInfoInternal::toSubscriptionInfo) .collect(Collectors.toList()); } long token = Binder.clearCallingIdentity(); try { List<SubscriptionInfoInternal> subInfoList = mSubscriptionDatabaseManager Loading @@ -3724,19 +3851,44 @@ public class SubscriptionManagerService extends ISub.Stub { UserManager userManager = mContext.getSystemService(UserManager.class); if ((userManager != null) && (userManager.isManagedProfile(userHandle.getIdentifier()))) { // For work profile, return subscriptions associated only with work profile // For work profile, return subscriptions associated only with work profile even // if it's empty. return subscriptionsAssociatedWithUser; } // For all other profiles, if subscriptionsAssociatedWithUser is empty return all the // subscriptionsWithNoAssociation. return subscriptionsAssociatedWithUser.isEmpty() ? subscriptionsWithNoAssociation : subscriptionsAssociatedWithUser; // For all other profiles, if subscriptionsAssociatedWithUser is empty return all // the subscriptionsWithNoAssociation. return subscriptionsAssociatedWithUser.isEmpty() ? subscriptionsWithNoAssociation : subscriptionsAssociatedWithUser; } finally { Binder.restoreCallingIdentity(token); } } /** * @return All active subscriptions. */ @NonNull private List<SubscriptionInfoInternal> getActiveSubscriptionInfoListAllUser() { return mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(SubscriptionInfoInternal::isActive) .collect(Collectors.toList()); } /** * Get subscriptions accessible to the caller user. * * @param user The user to check. * @return a stream of accessible internal subscriptions. */ @NonNull private Stream<SubscriptionInfoInternal> getSubscriptionInfoStreamAsUser( @NonNull final UserHandle user) { return mSubscriptionDatabaseManager.getAllSubscriptions().stream() .filter(info -> isSubscriptionAssociatedWithUserInternal( info, user.getIdentifier())); } /** * Called during setup wizard restore flow to attempt to restore the backed up sim-specific * configs to device for all existing SIMs in the subscription database {@link SimInfo}. Loading Loading @@ -3804,14 +3956,26 @@ public class SubscriptionManagerService extends ISub.Stub { * @throws SecurityException if the caller does not have any permissions. */ private void enforcePermissions(@Nullable String message, @NonNull String ...permissions) { if (!hasPermissions(permissions)) { throw new SecurityException( message + ". Does not have any of the following permissions. " + Arrays.toString(permissions)); } } /** * Check have any of the permissions * @param permissions The permissions to check. * @return {@code true} if the caller has one of the given permissions. */ private boolean hasPermissions(@NonNull String ...permissions) { for (String permission : permissions) { if (mContext.checkCallingOrSelfPermission(permission) == PackageManager.PERMISSION_GRANTED) { return; return true; } } throw new SecurityException(message + ". Does not have any of the following permissions. " + Arrays.toString(permissions)); return false; } /** Loading @@ -3835,9 +3999,8 @@ public class SubscriptionManagerService extends ISub.Stub { */ @Nullable public SubscriptionInfo getSubscriptionInfo(int subId) { SubscriptionInfoInternal subscriptionInfoInternal = getSubscriptionInfoInternal(subId); return subscriptionInfoInternal != null ? subscriptionInfoInternal.toSubscriptionInfo() : null; SubscriptionInfoInternal infoInternal = getSubscriptionInfoInternal(subId); return infoInternal != null ? infoInternal.toSubscriptionInfo() : null; } /** Loading Loading @@ -3954,8 +4117,7 @@ public class SubscriptionManagerService extends ISub.Stub { */ @VisibleForTesting public void updateGroupDisabled() { List<SubscriptionInfo> activeSubscriptions = getActiveSubscriptionInfoList( mContext.getOpPackageName(), mContext.getFeatureId()); List<SubscriptionInfoInternal> activeSubscriptions = getActiveSubscriptionInfoListAllUser(); for (SubscriptionInfo oppSubInfo : getOpportunisticSubscriptions( mContext.getOpPackageName(), mContext.getFeatureId())) { boolean groupDisabled = activeSubscriptions.stream() Loading Loading @@ -4062,6 +4224,7 @@ public class SubscriptionManagerService extends ISub.Stub { pw.println("activeDataSubId=" + getActiveDataSubscriptionId()); pw.println("defaultSmsSubId=" + getDefaultSmsSubId()); pw.println("areAllSubscriptionsLoaded=" + areAllSubscriptionsLoaded()); pw.println("mUserIdToAvailableSubs=" + mUserIdToAvailableSubs); pw.println(); for (int i = 0; i < mSimState.length; i++) { pw.println("mSimState[" + i + "]=" Loading
tests/telephonytests/src/com/android/internal/telephony/subscription/SubscriptionManagerServiceTest.java +307 −7 File changed.Preview size limit exceeded, changes collapsed. Show changes