cleanupOrphanPA's feature (1/2)*
Bug: 210134615
Test: Unit + manual
Change-Id: Ide6fc11a3ce8b2d1a911c4886f07f383db9424bc
Merged-In: Ide6fc11a3ce8b2d1a911c4886f07f383db9424bc
diff --git a/src/com/android/server/telecom/PhoneAccountRegistrar.java b/src/com/android/server/telecom/PhoneAccountRegistrar.java
index d8f2d22..4e8524e 100644
--- a/src/com/android/server/telecom/PhoneAccountRegistrar.java
+++ b/src/com/android/server/telecom/PhoneAccountRegistrar.java
@@ -76,6 +76,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -1187,6 +1188,53 @@
return accounts;
}
+ /**
+ * 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.
+ *
+ * @return the number of orphan {@code PhoneAccount} deleted.
+ */
+ public int cleanupOrphanedPhoneAccounts() {
+ ArrayList<PhoneAccount> badAccountsList = new ArrayList<>();
+ HashMap<String, Boolean> packageLookup = new HashMap<>();
+ HashMap<PhoneAccount, Boolean> userHandleLookup = new HashMap<>();
+
+ // iterate over all accounts in registrar
+ for (PhoneAccount pa : mState.accounts) {
+ String packageName = pa.getAccountHandle().getComponentName().getPackageName();
+
+ // check if the package for the PhoneAccount is uninstalled
+ if (packageLookup.computeIfAbsent(packageName,
+ pn -> isPackageUninstalled(pn))) {
+ badAccountsList.add(pa);
+ }
+ // check if PhoneAccount does not have a valid UserHandle (user was deleted)
+ else if (userHandleLookup.computeIfAbsent(pa,
+ a -> isUserHandleDeletedForPhoneAccount(a))) {
+ badAccountsList.add(pa);
+ }
+ }
+
+ mState.accounts.removeAll(badAccountsList);
+
+ return badAccountsList.size();
+ }
+
+ public Boolean isPackageUninstalled(String packageName) {
+ try {
+ mContext.getPackageManager().getPackageInfo(packageName, 0);
+ return false;
+ } catch (PackageManager.NameNotFoundException e) {
+ return true;
+ }
+ }
+
+ private Boolean isUserHandleDeletedForPhoneAccount(PhoneAccount phoneAccount) {
+ UserHandle userHandle = phoneAccount.getAccountHandle().getUserHandle();
+ return (userHandle == null) ||
+ (mUserManager.getSerialNumberForUser(userHandle) == -1L);
+ }
+
//
// State Implementation for PhoneAccountRegistrar
//
diff --git a/src/com/android/server/telecom/TelecomServiceImpl.java b/src/com/android/server/telecom/TelecomServiceImpl.java
index 80bcbe0..c765a6e 100644
--- a/src/com/android/server/telecom/TelecomServiceImpl.java
+++ b/src/com/android/server/telecom/TelecomServiceImpl.java
@@ -1875,6 +1875,31 @@
}
/**
+ * A method intended for test to clean up orphan {@link PhoneAccount}. An orphan
+ * {@link PhoneAccount} is a phone account belongs to an invalid {@link UserHandle} or a
+ * deleted package.
+ *
+ * @return the number of orphan {@code PhoneAccount} deleted.
+ */
+ @Override
+ public int cleanupOrphanPhoneAccounts() {
+ Log.startSession("TCI.cOPA");
+ try {
+ synchronized (mLock) {
+ enforceShellOnly(Binder.getCallingUid(), "cleanupOrphanPhoneAccounts");
+ long token = Binder.clearCallingIdentity();
+ try {
+ return mPhoneAccountRegistrar.cleanupOrphanedPhoneAccounts();
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ /**
* A method intended for use in testing to reset car mode at all priorities.
*
* Runs during setup to avoid cascading failures from failing car mode CTS.
diff --git a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
index a56036a..6232396 100644
--- a/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
+++ b/tests/src/com/android/server/telecom/tests/PhoneAccountRegistrarTest.java
@@ -28,6 +28,8 @@
import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.drawable.Icon;
@@ -82,6 +84,9 @@
private static final int MAX_VERSION = Integer.MAX_VALUE;
private static final String FILE_NAME = "phone-account-registrar-test-1223.xml";
private static final String TEST_LABEL = "right";
+ private final String PACKAGE_1 = "PACKAGE_1";
+ private final String PACKAGE_2 = "PACKAGE_2";
+ private final String COMPONENT_NAME = "com.android.server.telecom.tests.MockConnectionService";
private PhoneAccountRegistrar mRegistrar;
@Mock private TelecomManager mTelecomManager;
@Mock private DefaultDialerCache mDefaultDialerCache;
@@ -1015,6 +1020,52 @@
assertFalse(PhoneAccountHandle.areFromSamePackage(null, d));
}
+ /**
+ * Tests {@link PhoneAccountRegistrar#cleanupOrphanedPhoneAccounts } cleans up / deletes an
+ * orphan account.
+ */
+ @Test
+ public void testCleanUpOrphanAccounts() throws Exception {
+ // GIVEN
+ mComponentContextFixture.addConnectionService(makeQuickConnectionServiceComponentName(),
+ Mockito.mock(IConnectionService.class));
+
+ List<UserHandle> users = Arrays.asList(new UserHandle(0),
+ new UserHandle(1000));
+
+ PhoneAccount pa1 = new PhoneAccount.Builder(
+ new PhoneAccountHandle(new ComponentName(PACKAGE_1, COMPONENT_NAME), "1234",
+ users.get(0)), "l1").build();
+ PhoneAccount pa2 = new PhoneAccount.Builder(
+ new PhoneAccountHandle(new ComponentName(PACKAGE_2, COMPONENT_NAME), "5678",
+ users.get(1)), "l2").build();
+
+
+ registerAndEnableAccount(pa1);
+ registerAndEnableAccount(pa2);
+
+ assertEquals(1, mRegistrar.getAllPhoneAccounts(users.get(0)).size());
+ assertEquals(1, mRegistrar.getAllPhoneAccounts(users.get(1)).size());
+
+
+ // WHEN
+ when(mContext.getPackageManager().getPackageInfo(PACKAGE_1, 0))
+ .thenReturn(new PackageInfo());
+
+ when(mContext.getPackageManager().getPackageInfo(PACKAGE_2, 0))
+ .thenThrow(new PackageManager.NameNotFoundException());
+
+ when(UserManager.get(mContext).getSerialNumberForUser(users.get(0)))
+ .thenReturn(0L);
+
+ when(UserManager.get(mContext).getSerialNumberForUser(users.get(1)))
+ .thenReturn(-1L);
+
+ // THEN
+ int deletedAccounts = mRegistrar.cleanupOrphanedPhoneAccounts();
+ assertEquals(1, deletedAccounts);
+ }
+
private static ComponentName makeQuickConnectionServiceComponentName() {
return new ComponentName(
"com.android.server.telecom.tests",