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 {