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

Commit 21c3e58a authored by Hui Wang's avatar Hui Wang Committed by Android (Google) Code Review
Browse files

Merge "Update phone account APIs for work profile split mode" into main

parents c72518c5 f69e7808
Loading
Loading
Loading
Loading
+99 −9
Original line number Diff line number Diff line
@@ -181,16 +181,19 @@ public class PhoneAccountRegistrar {
    private interface PhoneAccountRegistrarWriteLock {}
    private final PhoneAccountRegistrarWriteLock mWriteLock =
            new PhoneAccountRegistrarWriteLock() {};
    private final com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;

    @VisibleForTesting
    public PhoneAccountRegistrar(Context context, TelecomSystem.SyncRoot lock,
            DefaultDialerCache defaultDialerCache, AppLabelProxy appLabelProxy) {
        this(context, lock, FILE_NAME, defaultDialerCache, appLabelProxy);
            DefaultDialerCache defaultDialerCache, AppLabelProxy appLabelProxy,
            com.android.internal.telephony.flags.FeatureFlags telephonyFeatureFlags) {
        this(context, lock, FILE_NAME, defaultDialerCache, appLabelProxy, telephonyFeatureFlags);
    }

    @VisibleForTesting
    public PhoneAccountRegistrar(Context context, TelecomSystem.SyncRoot lock, String fileName,
            DefaultDialerCache defaultDialerCache, AppLabelProxy appLabelProxy) {
            DefaultDialerCache defaultDialerCache, AppLabelProxy appLabelProxy,
            com.android.internal.telephony.flags.FeatureFlags telephonyFeatureFlags) {

        mAtomicFile = new AtomicFile(new File(context.getFilesDir(), fileName));

@@ -204,6 +207,13 @@ public class PhoneAccountRegistrar {
        mAppLabelProxy = appLabelProxy;
        mCurrentUserHandle = Process.myUserHandle();

        if (telephonyFeatureFlags != null) {
            mTelephonyFeatureFlags = telephonyFeatureFlags;
        } else {
            mTelephonyFeatureFlags =
                    new com.android.internal.telephony.flags.FeatureFlagsImpl();
        }

        // register context based receiver to clean up orphan phone accounts
        IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MANAGED_PROFILE_REMOVED);
        intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -697,6 +707,24 @@ public class PhoneAccountRegistrar {
        }
    }

    private boolean isMatchedUser(PhoneAccount account, UserHandle userHandle) {
        if (account == null) {
            return false;
        }

        if (userHandle == null) {
            Log.w(this, "userHandle is null in isVisibleForUser");
            return false;
        }

        UserHandle phoneAccountUserHandle = account.getAccountHandle().getUserHandle();
        if (phoneAccountUserHandle == null) {
            return false;
        }

        return phoneAccountUserHandle.equals(userHandle);
    }

    private boolean isVisibleForUser(PhoneAccount account, UserHandle userHandle,
            boolean acrossProfiles) {
        if (account == null) {
@@ -763,11 +791,11 @@ public class PhoneAccountRegistrar {
     */
    public List<PhoneAccountHandle> getAllPhoneAccountHandles(UserHandle userHandle,
            boolean crossUserAccess) {
        return getPhoneAccountHandles(0, null, null, false, userHandle, crossUserAccess);
        return getPhoneAccountHandles(0, null, null, false, userHandle, crossUserAccess, true);
    }

    public List<PhoneAccount> getAllPhoneAccounts(UserHandle userHandle, boolean crossUserAccess) {
        return getPhoneAccounts(0, null, null, false, mCurrentUserHandle, crossUserAccess);
        return getPhoneAccounts(0, null, null, false, mCurrentUserHandle, crossUserAccess, true);
    }

    /**
@@ -858,7 +886,7 @@ public class PhoneAccountRegistrar {
    public List<PhoneAccountHandle> getAllPhoneAccountHandlesForPackage(UserHandle userHandle,
            String packageName) {
        return getPhoneAccountHandles(0, null, packageName, true /* includeDisabled */, userHandle,
                true /* crossUserAccess */);
                true /* crossUserAccess */, true);
    }

    /**
@@ -1490,7 +1518,19 @@ public class PhoneAccountRegistrar {
            UserHandle userHandle,
            boolean crossUserAccess) {
        return getPhoneAccountHandles(capabilities, 0 /*excludedCapabilities*/, uriScheme,
                packageName, includeDisabledAccounts, userHandle, crossUserAccess);
                packageName, includeDisabledAccounts, userHandle, crossUserAccess, false);
    }

    private List<PhoneAccountHandle> getPhoneAccountHandles(
            int capabilities,
            String uriScheme,
            String packageName,
            boolean includeDisabledAccounts,
            UserHandle userHandle,
            boolean crossUserAccess,
            boolean includeAll) {
        return getPhoneAccountHandles(capabilities, 0 /*excludedCapabilities*/, uriScheme,
                packageName, includeDisabledAccounts, userHandle, crossUserAccess, includeAll);
    }

    /**
@@ -1505,11 +1545,24 @@ public class PhoneAccountRegistrar {
            boolean includeDisabledAccounts,
            UserHandle userHandle,
            boolean crossUserAccess) {
        return getPhoneAccountHandles(capabilities, excludedCapabilities, uriScheme, packageName,
                includeDisabledAccounts, userHandle, crossUserAccess, false);
    }

    private List<PhoneAccountHandle> getPhoneAccountHandles(
            int capabilities,
            int excludedCapabilities,
            String uriScheme,
            String packageName,
            boolean includeDisabledAccounts,
            UserHandle userHandle,
            boolean crossUserAccess,
            boolean includeAll) {
        List<PhoneAccountHandle> handles = new ArrayList<>();

        for (PhoneAccount account : getPhoneAccounts(
                capabilities, excludedCapabilities, uriScheme, packageName,
                includeDisabledAccounts, userHandle, crossUserAccess)) {
                includeDisabledAccounts, userHandle, crossUserAccess, includeAll)) {
            handles.add(account.getAccountHandle());
        }
        return handles;
@@ -1523,7 +1576,19 @@ public class PhoneAccountRegistrar {
            UserHandle userHandle,
            boolean crossUserAccess) {
        return getPhoneAccounts(capabilities, 0 /*excludedCapabilities*/, uriScheme, packageName,
                includeDisabledAccounts, userHandle, crossUserAccess);
                includeDisabledAccounts, userHandle, crossUserAccess, false);
    }

    private List<PhoneAccount> getPhoneAccounts(
            int capabilities,
            String uriScheme,
            String packageName,
            boolean includeDisabledAccounts,
            UserHandle userHandle,
            boolean crossUserAccess,
            boolean includeAll) {
        return getPhoneAccounts(capabilities, 0 /*excludedCapabilities*/, uriScheme, packageName,
                includeDisabledAccounts, userHandle, crossUserAccess, includeAll);
    }

    /**
@@ -1545,7 +1610,22 @@ public class PhoneAccountRegistrar {
            boolean includeDisabledAccounts,
            UserHandle userHandle,
            boolean crossUserAccess) {
        return getPhoneAccounts(capabilities, excludedCapabilities, uriScheme, packageName,
                includeDisabledAccounts, userHandle, crossUserAccess, false);
    }

    @VisibleForTesting
    public List<PhoneAccount> getPhoneAccounts(
            int capabilities,
            int excludedCapabilities,
            String uriScheme,
            String packageName,
            boolean includeDisabledAccounts,
            UserHandle userHandle,
            boolean crossUserAccess,
            boolean includeAll) {
        List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size());
        List<PhoneAccount> matchedAccounts = new ArrayList<>(mState.accounts.size());
        for (PhoneAccount m : mState.accounts) {
            if (!(m.isEnabled() || includeDisabledAccounts)) {
                // Do not include disabled accounts.
@@ -1579,12 +1659,22 @@ public class PhoneAccountRegistrar {
                // Not the right package name; skip this one.
                continue;
            }
            if (isMatchedUser(m, userHandle)) {
                matchedAccounts.add(m);
            }
            if (!crossUserAccess && !isVisibleForUser(m, userHandle, false)) {
                // Account is not visible for the current user; skip this one.
                continue;
            }
            accounts.add(m);
        }

        // Return the account if it exactly matches. Otherwise, return any account that's visible
        if (mTelephonyFeatureFlags.workProfileApiSplit() && !crossUserAccess && !includeAll
                && !matchedAccounts.isEmpty()) {
            return matchedAccounts;
        }

        return accounts;
    }

+48 −3
Original line number Diff line number Diff line
@@ -356,9 +356,28 @@ public class TelecomServiceImpl {

        @Override
        public ParceledListSlice<PhoneAccountHandle> getCallCapablePhoneAccounts(
                boolean includeDisabledAccounts, String callingPackage, String callingFeatureId) {
                boolean includeDisabledAccounts, String callingPackage,
                String callingFeatureId, boolean acrossProfiles) {
            try {
                Log.startSession("TSI.gCCPA", Log.getPackageAbbreviation(callingPackage));

                if (mTelephonyFeatureFlags.workProfileApiSplit()) {
                    if (acrossProfiles) {
                        enforceInAppCrossProfilePermission();
                    }

                    if (includeDisabledAccounts && !canReadPrivilegedPhoneState(
                            callingPackage, "getCallCapablePhoneAccounts")) {
                        throw new SecurityException(
                                "Requires READ_PRIVILEGED_PHONE_STATE permission.");
                    }

                    if (!includeDisabledAccounts && !canReadPhoneState(callingPackage,
                            callingFeatureId, "Requires READ_PHONE_STATE permission.")) {
                        throw new SecurityException("Requires READ_PHONE_STATE permission.");
                    }
                }

                if (includeDisabledAccounts &&
                        !canReadPrivilegedPhoneState(
                                callingPackage, "getCallCapablePhoneAccounts")) {
@@ -370,7 +389,11 @@ public class TelecomServiceImpl {
                }
                synchronized (mLock) {
                    final UserHandle callingUserHandle = Binder.getCallingUserHandle();
                    boolean crossUserAccess = hasInAppCrossUserPermission();
                    boolean crossUserAccess = mTelephonyFeatureFlags.workProfileApiSplit()
                            && !acrossProfiles ? false
                            : (mTelephonyFeatureFlags.workProfileApiSplit()
                                    ? hasInAppCrossProfilePermission()
                                    : hasInAppCrossUserPermission());
                    long token = Binder.clearCallingIdentity();
                    try {
                        return new ParceledListSlice<>(
@@ -638,6 +661,7 @@ public class TelecomServiceImpl {
        public ParceledListSlice<PhoneAccountHandle> getAllPhoneAccountHandles() {
            try {
                Log.startSession("TSI.gAPAH");

                try {
                    enforceModifyPermission(
                            "getAllPhoneAccountHandles requires MODIFY_PHONE_STATE permission.");
@@ -656,7 +680,7 @@ public class TelecomServiceImpl {
                                .getAllPhoneAccountHandles(callingUserHandle,
                                        crossUserAccess));
                    } catch (Exception e) {
                        Log.e(this, e, "getAllPhoneAccounts");
                        Log.e(this, e, "getAllPhoneAccountsHandles");
                        throw e;
                    } finally {
                        Binder.restoreCallingIdentity(token);
@@ -2558,6 +2582,8 @@ public class TelecomServiceImpl {
    private TransactionManager mTransactionManager;
    private final TransactionalServiceRepository mTransactionalServiceRepository;
    private final FeatureFlags mFeatureFlags;
    private final com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;


    public TelecomServiceImpl(
            Context context,
@@ -2569,6 +2595,7 @@ public class TelecomServiceImpl {
            SubscriptionManagerAdapter subscriptionManagerAdapter,
            SettingsSecureAdapter settingsSecureAdapter,
            FeatureFlags featureFlags,
            com.android.internal.telephony.flags.FeatureFlags telephonyFeatureFlags,
            TelecomSystem.SyncRoot lock) {
        mContext = context;
        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -2577,6 +2604,12 @@ public class TelecomServiceImpl {

        mCallsManager = callsManager;
        mFeatureFlags = featureFlags;
        if (telephonyFeatureFlags != null) {
            mTelephonyFeatureFlags = telephonyFeatureFlags;
        } else {
            mTelephonyFeatureFlags =
                    new com.android.internal.telephony.flags.FeatureFlagsImpl();
        }
        mLock = lock;
        mPhoneAccountRegistrar = phoneAccountRegistrar;
        mUserCallIntentProcessorFactory = userCallIntentProcessorFactory;
@@ -2947,12 +2980,24 @@ public class TelecomServiceImpl {
                        + " INTERACT_ACROSS_USERS permission");
    }

    private void enforceInAppCrossProfilePermission() {
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.INTERACT_ACROSS_PROFILES, "Must be system or have"
                        + " INTERACT_ACROSS_PROFILES permission");
    }

    private boolean hasInAppCrossUserPermission() {
        return mContext.checkCallingOrSelfPermission(
                Manifest.permission.INTERACT_ACROSS_USERS)
                == PackageManager.PERMISSION_GRANTED;
    }

    private boolean hasInAppCrossProfilePermission() {
        return mContext.checkCallingOrSelfPermission(
                Manifest.permission.INTERACT_ACROSS_PROFILES)
                == PackageManager.PERMISSION_GRANTED;
    }

    // to be used for TestApi methods that can only be called with SHELL UID.
    private void enforceShellOnly(int callingUid, String message) {
        if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+2 −1
Original line number Diff line number Diff line
@@ -245,7 +245,7 @@ public class TelecomSystem {
        try {
            mPhoneAccountRegistrar = new PhoneAccountRegistrar(mContext, mLock, defaultDialerCache,
                    packageName -> AppLabelProxy.Util.getAppLabel(
                            mContext.getPackageManager(), packageName));
                            mContext.getPackageManager(), packageName), null);

            mContactsAsyncHelper = contactsAsyncHelperFactory.create(
                    new ContactsAsyncHelper.ContentResolverAdapter() {
@@ -485,6 +485,7 @@ public class TelecomSystem {
                    new TelecomServiceImpl.SubscriptionManagerAdapterImpl(),
                    new TelecomServiceImpl.SettingsSecureAdapterImpl(),
                    featureFlags,
                    null,
                    mLock);
        } finally {
            Log.endSession();
+55 −2
Original line number Diff line number Diff line
@@ -117,6 +117,7 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase {
    @Mock private TelecomManager mTelecomManager;
    @Mock private DefaultDialerCache mDefaultDialerCache;
    @Mock private AppLabelProxy mAppLabelProxy;
    @Mock private com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;

    @Override
    @Before
@@ -134,8 +135,10 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase {
        when(mAppLabelProxy.getAppLabel(anyString()))
                .thenReturn(TEST_LABEL);
        mRegistrar = new PhoneAccountRegistrar(
                mComponentContextFixture.getTestDouble().getApplicationContext(),
                mLock, FILE_NAME, mDefaultDialerCache, mAppLabelProxy);
                mComponentContextFixture.getTestDouble().getApplicationContext(), mLock, FILE_NAME,
                mDefaultDialerCache, mAppLabelProxy, mTelephonyFeatureFlags);
        when(mFeatureFlags.onlyUpdateTelephonyOnValidSubIds()).thenReturn(false);
        when(mTelephonyFeatureFlags.workProfileApiSplit()).thenReturn(false);
    }

    @Override
@@ -1734,6 +1737,56 @@ public class PhoneAccountRegistrarTest extends TelecomTestCase {
        }
    }

    @Test
    public void testGetPhoneAccountAcrossUsers() throws Exception {
        when(mTelephonyFeatureFlags.workProfileApiSplit()).thenReturn(true);
        mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
                Mockito.mock(IConnectionService.class));

        PhoneAccount accountForCurrent = makeQuickAccountBuilder("id_0", 0, UserHandle.CURRENT)
                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
                        | PhoneAccount.CAPABILITY_CALL_PROVIDER).build();
        PhoneAccount accountForAll = makeQuickAccountBuilder("id_0", 0, UserHandle.ALL)
                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
                        | PhoneAccount.CAPABILITY_CALL_PROVIDER
                        | PhoneAccount.CAPABILITY_MULTI_USER).build();
        PhoneAccount accountForWorkProfile = makeQuickAccountBuilder("id_1", 1, USER_HANDLE_10)
                .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
                        | PhoneAccount.CAPABILITY_CALL_PROVIDER).build();

        registerAndEnableAccount(accountForCurrent);
        registerAndEnableAccount(accountForAll);
        registerAndEnableAccount(accountForWorkProfile);

        List<PhoneAccount> accountsForUser = mRegistrar.getPhoneAccounts(0, 0,
                null, null, false, USER_HANDLE_10, false, false);
        List<PhoneAccount> accountsVisibleUser = mRegistrar.getPhoneAccounts(0, 0,
                null, null, false, USER_HANDLE_10, false, true);
        List<PhoneAccount> accountsAcrossUser = mRegistrar.getPhoneAccounts(0, 0,
                null, null, false, USER_HANDLE_10, true, false);

        // Return the account exactly matching the user if it exists
        assertEquals(1, accountsForUser.size());
        assertTrue(accountsForUser.contains(accountForWorkProfile));
        // The accounts visible to the user without across user permission
        assertEquals(2, accountsVisibleUser.size());
        assertTrue(accountsVisibleUser.containsAll(accountsForUser));
        assertTrue(accountsVisibleUser.contains(accountForAll));
        // The accounts visible to the user with across user permission
        assertEquals(3, accountsAcrossUser.size());
        assertTrue(accountsAcrossUser.containsAll(accountsVisibleUser));
        assertTrue(accountsAcrossUser.contains(accountForCurrent));

        mRegistrar.unregisterPhoneAccount(accountForWorkProfile.getAccountHandle());

        accountsForUser = mRegistrar.getPhoneAccounts(0, 0,
                null, null, false, USER_HANDLE_10, false, false);

        // Return the account visible for the user if no account exactly matches the user
        assertEquals(1, accountsForUser.size());
        assertTrue(accountsForUser.contains(accountForAll));
    }

    private static PhoneAccount.Builder makeBuilderWithBindCapabilities(PhoneAccountHandle handle) {
        return new PhoneAccount.Builder(handle, TEST_LABEL)
                .setCapabilities(PhoneAccount.CAPABILITY_SUPPORTS_TRANSACTIONAL_OPERATIONS);
+60 −3
Original line number Diff line number Diff line
@@ -197,6 +197,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
    @Mock private TransactionManager mTransactionManager;
    @Mock private AnomalyReporterAdapter mAnomalyReporterAdapter;
    @Mock private FeatureFlags mFeatureFlags;
    @Mock private com.android.internal.telephony.flags.FeatureFlags mTelephonyFeatureFlags;

    @Mock private InCallController mInCallController;

@@ -251,6 +252,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
                mSubscriptionManagerAdapter,
                mSettingsSecureAdapter,
                mFeatureFlags,
                mTelephonyFeatureFlags,
                mLock);
        telecomServiceImpl.setTransactionManager(mTransactionManager);
        telecomServiceImpl.setAnomalyReporterAdapter(mAnomalyReporterAdapter);
@@ -270,6 +272,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        mPackageManager = mContext.getPackageManager();
        when(mPackageManager.getPackageUid(anyString(), eq(0))).thenReturn(Binder.getCallingUid());
        when(mFeatureFlags.earlyBindingToIncallService()).thenReturn(true);
        when(mTelephonyFeatureFlags.workProfileApiSplit()).thenReturn(false);
    }

    @Override
@@ -535,10 +538,64 @@ public class TelecomServiceImplTest extends TelecomTestCase {

        assertEquals(fullPHList,
                mTSIBinder.getCallCapablePhoneAccounts(
                        true, DEFAULT_DIALER_PACKAGE, null).getList());
                        true, DEFAULT_DIALER_PACKAGE, null, false).getList());
        assertEquals(smallPHList,
                mTSIBinder.getCallCapablePhoneAccounts(
                        false, DEFAULT_DIALER_PACKAGE, null).getList());
                        false, DEFAULT_DIALER_PACKAGE, null, false).getList());
    }

    @SmallTest
    @Test
    public void testGetCallCapablePhoneAccountsAcrossProfiles() throws RemoteException {
        List<PhoneAccountHandle> fullPHList = List.of(TEL_PA_HANDLE_16, SIP_PA_HANDLE_17);
        List<PhoneAccountHandle> smallPHList = List.of(SIP_PA_HANDLE_17);

        // Returns all accounts when getCallCapablePhoneAccounts is called with across user.
        doReturn(fullPHList).when(mFakePhoneAccountRegistrar).getCallCapablePhoneAccounts(
                nullable(String.class), anyBoolean(), nullable(UserHandle.class), eq(true));
        // Returns one account when getCallCapablePhoneAccounts is called without across user.
        doReturn(smallPHList).when(mFakePhoneAccountRegistrar).getCallCapablePhoneAccounts(
                nullable(String.class), anyBoolean(), nullable(UserHandle.class), eq(false));
        // With across user permission
        doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
                eq(Manifest.permission.INTERACT_ACROSS_USERS));

        assertEquals(fullPHList,
                mTSIBinder.getCallCapablePhoneAccounts(
                        true, DEFAULT_DIALER_PACKAGE, null, false).getList());

        // Without across user permission
        doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(
                eq(Manifest.permission.INTERACT_ACROSS_USERS));

        assertEquals(smallPHList,
                mTSIBinder.getCallCapablePhoneAccounts(
                        true, DEFAULT_DIALER_PACKAGE, null, false).getList());

        // Enabled the feature flag of the work profile split mode
        when(mTelephonyFeatureFlags.workProfileApiSplit()).thenReturn(true);

        // With across user permission
        doReturn(PackageManager.PERMISSION_GRANTED).when(mContext).checkCallingOrSelfPermission(
                eq(Manifest.permission.INTERACT_ACROSS_PROFILES));

        assertEquals(fullPHList,
                mTSIBinder.getCallCapablePhoneAccounts(
                        true, DEFAULT_DIALER_PACKAGE, null, true).getList());
        assertEquals(smallPHList,
                mTSIBinder.getCallCapablePhoneAccounts(
                        true, DEFAULT_DIALER_PACKAGE, null, false).getList());

        // Without across user permission
        doReturn(PackageManager.PERMISSION_DENIED).when(mContext).checkCallingOrSelfPermission(
                eq(Manifest.permission.INTERACT_ACROSS_PROFILES));

        assertEquals(smallPHList,
                mTSIBinder.getCallCapablePhoneAccounts(
                        true, DEFAULT_DIALER_PACKAGE, null, true).getList());
        assertEquals(smallPHList,
                mTSIBinder.getCallCapablePhoneAccounts(
                        true, DEFAULT_DIALER_PACKAGE, null, false).getList());
    }

    @SmallTest
@@ -550,7 +607,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
                argThat(new AnyStringIn(enforcedPermissions)), anyString());

        assertThrows(SecurityException.class,
                () -> mTSIBinder.getCallCapablePhoneAccounts(true, "", null));
                () -> mTSIBinder.getCallCapablePhoneAccounts(true, "", null, false));
    }

    @SmallTest