Loading src/com/android/server/telecom/PhoneAccountRegistrar.java +99 −9 Original line number Diff line number Diff line Loading @@ -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)); Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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); } /** Loading Loading @@ -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); } /** Loading Loading @@ -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); } /** Loading @@ -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; Loading @@ -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); } /** Loading @@ -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. Loading Loading @@ -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; } Loading src/com/android/server/telecom/TelecomServiceImpl.java +48 −3 Original line number Diff line number Diff line Loading @@ -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")) { Loading @@ -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<>( Loading Loading @@ -638,6 +661,7 @@ public class TelecomServiceImpl { public ParceledListSlice<PhoneAccountHandle> getAllPhoneAccountHandles() { try { Log.startSession("TSI.gAPAH"); try { enforceModifyPermission( "getAllPhoneAccountHandles requires MODIFY_PHONE_STATE permission."); Loading @@ -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); Loading Loading @@ -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, Loading @@ -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); Loading @@ -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; Loading Loading @@ -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) { Loading src/com/android/server/telecom/TelecomSystem.java +2 −1 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -485,6 +485,7 @@ public class TelecomSystem { new TelecomServiceImpl.SubscriptionManagerAdapterImpl(), new TelecomServiceImpl.SettingsSecureAdapterImpl(), featureFlags, null, mLock); } finally { Log.endSession(); Loading tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java +55 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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); Loading tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +60 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -251,6 +252,7 @@ public class TelecomServiceImplTest extends TelecomTestCase { mSubscriptionManagerAdapter, mSettingsSecureAdapter, mFeatureFlags, mTelephonyFeatureFlags, mLock); telecomServiceImpl.setTransactionManager(mTransactionManager); telecomServiceImpl.setAnomalyReporterAdapter(mAnomalyReporterAdapter); Loading @@ -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 Loading Loading @@ -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 Loading @@ -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 Loading Loading
src/com/android/server/telecom/PhoneAccountRegistrar.java +99 −9 Original line number Diff line number Diff line Loading @@ -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)); Loading @@ -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); Loading Loading @@ -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) { Loading Loading @@ -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); } /** Loading Loading @@ -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); } /** Loading Loading @@ -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); } /** Loading @@ -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; Loading @@ -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); } /** Loading @@ -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. Loading Loading @@ -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; } Loading
src/com/android/server/telecom/TelecomServiceImpl.java +48 −3 Original line number Diff line number Diff line Loading @@ -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")) { Loading @@ -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<>( Loading Loading @@ -638,6 +661,7 @@ public class TelecomServiceImpl { public ParceledListSlice<PhoneAccountHandle> getAllPhoneAccountHandles() { try { Log.startSession("TSI.gAPAH"); try { enforceModifyPermission( "getAllPhoneAccountHandles requires MODIFY_PHONE_STATE permission."); Loading @@ -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); Loading Loading @@ -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, Loading @@ -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); Loading @@ -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; Loading Loading @@ -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) { Loading
src/com/android/server/telecom/TelecomSystem.java +2 −1 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading @@ -485,6 +485,7 @@ public class TelecomSystem { new TelecomServiceImpl.SubscriptionManagerAdapterImpl(), new TelecomServiceImpl.SettingsSecureAdapterImpl(), featureFlags, null, mLock); } finally { Log.endSession(); Loading
tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java +55 −2 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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); Loading
tests/src/com/android/server/telecom/tests/TelecomServiceImplTest.java +60 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -251,6 +252,7 @@ public class TelecomServiceImplTest extends TelecomTestCase { mSubscriptionManagerAdapter, mSettingsSecureAdapter, mFeatureFlags, mTelephonyFeatureFlags, mLock); telecomServiceImpl.setTransactionManager(mTransactionManager); telecomServiceImpl.setAnomalyReporterAdapter(mAnomalyReporterAdapter); Loading @@ -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 Loading Loading @@ -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 Loading @@ -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 Loading