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

Commit 48f5f301 authored by Ling Ma's avatar Ling Ma
Browse files

Split subscriptionManager

With feature flag ON, known issues:
1. work messenger crash when receive
message.
2. unassigned SIM is accessible to telecom, so accessible to
work dialer - it's expected behavior is till in discussion.

Bug: 296076674
Test: Make call and send messages from both profiles in split mode, all
personal mode, and work only mode

Change-Id: Ic6b6beacf0ee1bc84aadcfff55ae6fb2945044f6
parent a46c701c
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.