Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d62a2cc7 authored by Ling Ma's avatar Ling Ma Committed by Android (Google) Code Review
Browse files

Merge "Split subscriptionManager" into main

parents c7b2a23e 48f5f301
Loading
Loading
Loading
Loading
+241 −78
Original line number Diff line number Diff line
@@ -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;
@@ -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}.
@@ -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;
@@ -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}.
@@ -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}.
     */
@@ -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)));
@@ -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,
@@ -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(
@@ -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.
@@ -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.
@@ -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());
@@ -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;
    }

    /**
@@ -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
@@ -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);
    }

    /**
@@ -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))
@@ -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;
    }

    /**
@@ -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());
    }

    /**
@@ -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());
    }

    /**
@@ -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);
        }
    }

    /**
@@ -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) {
@@ -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.
     *
@@ -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
@@ -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}.
@@ -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;
    }

    /**
@@ -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;
    }

    /**
@@ -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()
@@ -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 + "]="
+307 −7

File changed.

Preview size limit exceeded, changes collapsed.