enforcePhoneAccounts should unregister unresolvable accts
Accounts that cannot be resolved anymore should be unregistered to avoid
keeping the unusable account in memory.
Fixes: 281061708
Test: 1 new unit test + manual:
(1) flash apk that registers accts on diff CS's
(2) register MAX accts and disable CS
(3) app should repeat step 2 mutliple times
expect: accts unregistered after disabling CS and
registering another acct
Change-Id: Iddd205d4e9c4013fdd6baaeb78e7b299410d2327
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index 11b0f4d..9e3a37f 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -900,13 +900,15 @@
* @throws IllegalArgumentException if MAX_PHONE_ACCOUNT_REGISTRATIONS are reached
*/
private void enforceMaxPhoneAccountLimit(@NonNull PhoneAccount account) {
- final PhoneAccountHandle accountHandle = account.getAccountHandle();
- final UserHandle user = accountHandle.getUserHandle();
- final ComponentName componentName = accountHandle.getComponentName();
-
- if (getPhoneAccountHandles(0, null, componentName.getPackageName(),
- true /* includeDisabled */, user, false /* crossUserAccess */).size()
- >= MAX_PHONE_ACCOUNT_REGISTRATIONS) {
+ List<PhoneAccount> unverifiedAccounts = getAccountsForPackage_BypassResolveComp(
+ account.getAccountHandle().getComponentName().getPackageName(),
+ account.getAccountHandle().getUserHandle());
+ // verify each phone account is backed by a valid ConnectionService. If the
+ // ConnectionService has been disabled or cannot be resolved, unregister the accounts.
+ List<PhoneAccount> verifiedAccounts =
+ cleanupUnresolvableConnectionServiceAccounts(unverifiedAccounts);
+ // enforce the max phone account limit for the application registering accounts
+ if (verifiedAccounts.size() >= MAX_PHONE_ACCOUNT_REGISTRATIONS) {
EventLog.writeEvent(0x534e4554, "259064622", Binder.getCallingUid(),
"enforceMaxPhoneAccountLimit");
throw new IllegalArgumentException(
@@ -1553,6 +1555,51 @@
}
/**
+ * This getter should be used when you want to bypass the {@link
+ * PhoneAccountRegistrar#resolveComponent(PhoneAccountHandle)} check when fetching accounts
+ */
+ @VisibleForTesting
+ public List<PhoneAccount> getAccountsForPackage_BypassResolveComp(String packageName,
+ UserHandle userHandle) {
+ List<PhoneAccount> accounts = new ArrayList<>(mState.accounts.size());
+ for (PhoneAccount m : mState.accounts) {
+ PhoneAccountHandle handle = m.getAccountHandle();
+
+ if (packageName != null && !packageName.equals(
+ handle.getComponentName().getPackageName())) {
+ // Not the right package name; skip this one.
+ continue;
+ }
+
+ if (!isVisibleForUser(m, userHandle, false)) {
+ // Account is not visible for the current user; skip this one.
+ continue;
+ }
+ accounts.add(m);
+ }
+ return accounts;
+ }
+
+ @VisibleForTesting
+ public List<PhoneAccount> cleanupUnresolvableConnectionServiceAccounts(
+ List<PhoneAccount> accounts) {
+ ArrayList<PhoneAccount> verifiedAccounts = new ArrayList<>();
+ for (PhoneAccount account : accounts) {
+ PhoneAccountHandle handle = account.getAccountHandle();
+ // if the ConnectionService has been disabled or can longer be found, remove the handle
+ if (resolveComponent(handle).isEmpty()) {
+ Log.i(this,
+ "Cannot resolve the ConnectionService for handle=[%s]; unregistering"
+ + " account", handle);
+ unregisterPhoneAccount(handle);
+ } else {
+ verifiedAccounts.add(account);
+ }
+ }
+ return verifiedAccounts;
+ }
+
+ /**
* Clean up the orphan {@code PhoneAccount}. An orphan {@code PhoneAccount} is a phone
* account that does not have a {@code UserHandle} or belongs to a deleted package.
*
diff --git a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
index cc22de2..df855e9 100644
--- a/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
+++ b/tests/src/com/android/server/telecom/tests/ComponentContextFixture.java
@@ -735,6 +735,14 @@
mServiceInfoByComponentName.put(componentName, serviceInfo);
}
+ public void removeConnectionService(
+ ComponentName componentName,
+ IConnectionService service)
+ throws Exception {
+ removeService(ConnectionService.SERVICE_INTERFACE, componentName, service);
+ mServiceInfoByComponentName.remove(componentName);
+ }
+
public void addInCallService(
ComponentName componentName,
IInCallService service,
@@ -828,6 +836,12 @@
mComponentNameByService.put(service, name);
}
+ private void removeService(String action, ComponentName name, IInterface service) {
+ mComponentNamesByAction.remove(action, name);
+ mServiceByComponentName.remove(name);
+ mComponentNameByService.remove(service);
+ }
+
private List<ResolveInfo> doQueryIntentServices(Intent intent, int flags) {
List<ResolveInfo> result = new ArrayList<>();
for (ComponentName componentName : mComponentNamesByAction.get(intent.getAction())) {
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index e573bb8..9fcb87a 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -353,6 +353,40 @@
PhoneAccount.SCHEME_TEL));
}
+ /**
+ * Verify when a {@link android.telecom.ConnectionService} is disabled or cannot be resolved,
+ * all phone accounts are unregistered when calling
+ * {@link PhoneAccountRegistrar#getAccountsForPackage_BypassResolveComp(String, UserHandle)}.
+ */
+ @Test
+ public void testCannotResolveServiceUnregistersAccounts() throws Exception {
+ ComponentName componentName = makeQuickConnectionServiceComponentName();
+ PhoneAccount account = makeQuickAccountBuilder("0", 0, USER_HANDLE_10)
+ .setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER
+ | PhoneAccount.CAPABILITY_CALL_PROVIDER).build();
+ // add the ConnectionService and register a single phone account for it
+ mComponentContextFixture.addConnectionService(componentName,
+ Mockito.mock(IConnectionService.class));
+ registerAndEnableAccount(account);
+ // verify the start state
+ assertEquals(1,
+ mRegistrar.getAccountsForPackage_BypassResolveComp(componentName.getPackageName(),
+ USER_HANDLE_10).size());
+ // remove the ConnectionService so that the account cannot be resolved anymore
+ mComponentContextFixture.removeConnectionService(componentName,
+ Mockito.mock(IConnectionService.class));
+ // verify the account is unregistered when fetching the phone accounts for the package
+ assertEquals(1,
+ mRegistrar.getAccountsForPackage_BypassResolveComp(componentName.getPackageName(),
+ USER_HANDLE_10).size());
+ assertEquals(0,mRegistrar.cleanupUnresolvableConnectionServiceAccounts(
+ mRegistrar.getAccountsForPackage_BypassResolveComp(componentName.getPackageName(),
+ USER_HANDLE_10)).size());
+ assertEquals(0,
+ mRegistrar.getAccountsForPackage_BypassResolveComp(componentName.getPackageName(),
+ USER_HANDLE_10).size());
+ }
+
@MediumTest
@Test
public void testSimCallManager() throws Exception {