Merge "Improve the performance to avoid the potential ANR" into main
diff --git a/src/com/android/server/telecom/DefaultDialerCache.java b/src/com/android/server/telecom/DefaultDialerCache.java
index 44b426a..98289ed 100644
--- a/src/com/android/server/telecom/DefaultDialerCache.java
+++ b/src/com/android/server/telecom/DefaultDialerCache.java
@@ -31,76 +31,58 @@
 import android.provider.Settings;
 import android.telecom.DefaultDialerManager;
 import android.telecom.Log;
-import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
 import java.util.function.IntConsumer;
 
 public class DefaultDialerCache {
-    public interface DefaultDialerManagerAdapter {
-        String getDefaultDialerApplication(Context context);
-        String getDefaultDialerApplication(Context context, int userId);
-        boolean setDefaultDialerApplication(Context context, String packageName, int userId);
-    }
-
-    static class DefaultDialerManagerAdapterImpl implements DefaultDialerManagerAdapter {
-        @Override
-        public String getDefaultDialerApplication(Context context) {
-            return DefaultDialerManager.getDefaultDialerApplication(context);
-        }
-
-        @Override
-        public String getDefaultDialerApplication(Context context, int userId) {
-            return DefaultDialerManager.getDefaultDialerApplication(context, userId);
-        }
-
-        @Override
-        public boolean setDefaultDialerApplication(Context context, String packageName,
-                int userId) {
-            return DefaultDialerManager.setDefaultDialerApplication(context, packageName, userId);
-        }
-    }
-
     private static final String LOG_TAG = "DefaultDialerCache";
+    @VisibleForTesting
+    public final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final Context mContext;
+    private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
+    private final ComponentName mSystemDialerComponentName;
+    private final RoleManagerAdapter mRoleManagerAdapter;
+    private final ConcurrentHashMap<Integer, String> mCurrentDefaultDialerPerUser =
+            new ConcurrentHashMap<>();
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            Log.startSession("DDC.oR");
-            try {
-                String packageName;
-                if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
-                    packageName = null;
-                } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
-                        && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                    packageName = intent.getData().getSchemeSpecificPart();
-                } else if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
-                    packageName = null;
-                } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
-                    packageName = null;
-                } else {
-                    return;
-                }
+            mHandler.post(() -> {
+                Log.startSession("DDC.oR");
+                try {
+                    String packageName;
+                    if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
+                        packageName = null;
+                    } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
+                            && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                        packageName = intent.getData().getSchemeSpecificPart();
+                    } else if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
+                        packageName = null;
+                    } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+                        packageName = null;
+                    } else {
+                        return;
+                    }
 
-                synchronized (mLock) {
                     refreshCachesForUsersWithPackage(packageName);
+                } finally {
+                    Log.endSession();
                 }
-
-            } finally {
-                Log.endSession();
-            }
+            });
         }
     };
-
     private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
                 int removedUser = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                    UserHandle.USER_NULL);
+                        UserHandle.USER_NULL);
                 if (removedUser == UserHandle.USER_NULL) {
                     Log.w(LOG_TAG, "Expected EXTRA_USER_HANDLE with ACTION_USER_REMOVED");
                 } else {
@@ -110,8 +92,6 @@
             }
         }
     };
-
-    private final Handler mHandler = new Handler(Looper.getMainLooper());
     private final ContentObserver mDefaultDialerObserver = new ContentObserver(mHandler) {
         @Override
         public void onChange(boolean selfChange) {
@@ -119,9 +99,7 @@
             try {
                 // We don't get the user ID of the user that changed here, so we'll have to
                 // refresh all of the users.
-                synchronized (mLock) {
-                    refreshCachesForUsersWithPackage(null);
-                }
+                refreshCachesForUsersWithPackage(null);
             } finally {
                 Log.endSession();
             }
@@ -132,29 +110,21 @@
             return true;
         }
     };
-
-    private final Context mContext;
-    private final DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
-    private final TelecomSystem.SyncRoot mLock;
-    private final ComponentName mSystemDialerComponentName;
-    private final RoleManagerAdapter mRoleManagerAdapter;
-    private SparseArray<String> mCurrentDefaultDialerPerUser = new SparseArray<>();
     private ComponentName mOverrideSystemDialerComponentName;
 
     public DefaultDialerCache(Context context,
-                              DefaultDialerManagerAdapter defaultDialerManagerAdapter,
-                              RoleManagerAdapter roleManagerAdapter,
-                              TelecomSystem.SyncRoot lock) {
+            DefaultDialerManagerAdapter defaultDialerManagerAdapter,
+            RoleManagerAdapter roleManagerAdapter,
+            TelecomSystem.SyncRoot lock) {
         mContext = context;
         mDefaultDialerManagerAdapter = defaultDialerManagerAdapter;
         mRoleManagerAdapter = roleManagerAdapter;
-        mLock = lock;
+
         Resources resources = mContext.getResources();
         mSystemDialerComponentName = new ComponentName(resources.getString(
                 com.android.internal.R.string.config_defaultDialer),
                 resources.getString(R.string.incall_default_class));
 
-
         IntentFilter packageIntentFilter = new IntentFilter();
         packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -195,7 +165,7 @@
         //
         //synchronized (mLock) {
         //    String defaultDialer = mCurrentDefaultDialerPerUser.get(userId);
-        //    if (defaultDialer != null) {
+        //    if (!TextUtils.isEmpty(defaultDialer)) {
         //        return defaultDialer;
         //    }
         //}
@@ -241,11 +211,9 @@
     public boolean setDefaultDialer(String packageName, int userId) {
         boolean isChanged = mDefaultDialerManagerAdapter.setDefaultDialerApplication(
                 mContext, packageName, userId);
-        if(isChanged) {
-            synchronized (mLock) {
-                // Update the cache synchronously so that there is no delay in cache update.
-                mCurrentDefaultDialerPerUser.put(userId, packageName);
-            }
+        if (isChanged) {
+            // Update the cache synchronously so that there is no delay in cache update.
+            mCurrentDefaultDialerPerUser.put(userId, packageName == null ? "" : packageName);
         }
         return isChanged;
     }
@@ -253,47 +221,39 @@
     private String refreshCacheForUser(int userId) {
         String currentDefaultDialer =
                 mRoleManagerAdapter.getDefaultDialerApp(userId);
-        synchronized (mLock) {
-            mCurrentDefaultDialerPerUser.put(userId, currentDefaultDialer);
-        }
+        mCurrentDefaultDialerPerUser.put(userId, currentDefaultDialer == null ? "" :
+                currentDefaultDialer);
         return currentDefaultDialer;
     }
 
     /**
      * Refreshes the cache for users that currently have packageName as their cached default dialer.
      * If packageName is null, refresh all caches.
+     *
      * @param packageName Name of the affected package.
      */
     private void refreshCachesForUsersWithPackage(String packageName) {
-        for (int i = 0; i < mCurrentDefaultDialerPerUser.size(); i++) {
-            int userId = mCurrentDefaultDialerPerUser.keyAt(i);
-            if (packageName == null ||
-                    Objects.equals(packageName, mCurrentDefaultDialerPerUser.get(userId))) {
+        mCurrentDefaultDialerPerUser.forEach((userId, currentName) -> {
+            if (packageName == null || Objects.equals(packageName, currentName)) {
                 String newDefaultDialer = refreshCacheForUser(userId);
                 Log.v(LOG_TAG, "Refreshing default dialer for user %d: now %s",
                         userId, newDefaultDialer);
             }
-        }
+        });
     }
 
     public void dumpCache(IndentingPrintWriter pw) {
-        synchronized (mLock) {
-            for (int i = 0; i < mCurrentDefaultDialerPerUser.size(); i++) {
-                pw.printf("User %d: %s\n", mCurrentDefaultDialerPerUser.keyAt(i),
-                        mCurrentDefaultDialerPerUser.valueAt(i));
-            }
-        }
+        mCurrentDefaultDialerPerUser.forEach((k, v) -> pw.printf("User %d: %s\n", k, v));
     }
 
     private void removeUserFromCache(int userId) {
-        synchronized (mLock) {
-            mCurrentDefaultDialerPerUser.remove(userId);
-        }
+        mCurrentDefaultDialerPerUser.remove(userId);
     }
 
     /**
      * registerContentObserver is really hard to mock out, so here is a getter method for the
      * content observer for testing instead.
+     *
      * @return The content observer
      */
     @VisibleForTesting
@@ -304,4 +264,30 @@
     public RoleManagerAdapter getRoleManagerAdapter() {
         return mRoleManagerAdapter;
     }
-}
\ No newline at end of file
+
+    public interface DefaultDialerManagerAdapter {
+        String getDefaultDialerApplication(Context context);
+
+        String getDefaultDialerApplication(Context context, int userId);
+
+        boolean setDefaultDialerApplication(Context context, String packageName, int userId);
+    }
+
+    static class DefaultDialerManagerAdapterImpl implements DefaultDialerManagerAdapter {
+        @Override
+        public String getDefaultDialerApplication(Context context) {
+            return DefaultDialerManager.getDefaultDialerApplication(context);
+        }
+
+        @Override
+        public String getDefaultDialerApplication(Context context, int userId) {
+            return DefaultDialerManager.getDefaultDialerApplication(context, userId);
+        }
+
+        @Override
+        public boolean setDefaultDialerApplication(Context context, String packageName,
+                int userId) {
+            return DefaultDialerManager.setDefaultDialerApplication(context, packageName, userId);
+        }
+    }
+}
diff --git a/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java b/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java
index 18f2eb0..3da9284 100644
--- a/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java
+++ b/tests/src/com/android/server/telecom/tests/DefaultDialerCacheTest.java
@@ -56,14 +56,17 @@
     private static final int USER0 = 0;
     private static final int USER1 = 1;
     private static final int USER2 = 2;
+    private static final int DELAY_TOLERANCE = 100;
 
     private DefaultDialerCache mDefaultDialerCache;
     private ContentObserver mDefaultDialerSettingObserver;
     private BroadcastReceiver mPackageChangeReceiver;
     private BroadcastReceiver mUserRemovedReceiver;
 
-    @Mock private DefaultDialerCache.DefaultDialerManagerAdapter mMockDefaultDialerManager;
-    @Mock private RoleManagerAdapter mRoleManagerAdapter;
+    @Mock
+    private DefaultDialerCache.DefaultDialerManagerAdapter mMockDefaultDialerManager;
+    @Mock
+    private RoleManagerAdapter mRoleManagerAdapter;
 
     @Override
     @Before
@@ -76,18 +79,19 @@
 
         mDefaultDialerCache = new DefaultDialerCache(
                 mContext, mMockDefaultDialerManager, mRoleManagerAdapter,
-                new TelecomSystem.SyncRoot() { });
+                new TelecomSystem.SyncRoot() {
+                });
 
         verify(mContext, times(2)).registerReceiverAsUser(
-            packageReceiverCaptor.capture(), eq(UserHandle.ALL), any(IntentFilter.class),
+                packageReceiverCaptor.capture(), eq(UserHandle.ALL), any(IntentFilter.class),
                 isNull(String.class), isNull(Handler.class));
         // Receive the first receiver that was captured, the package change receiver.
         mPackageChangeReceiver = packageReceiverCaptor.getAllValues().get(0);
 
         ArgumentCaptor<BroadcastReceiver> userRemovedReceiverCaptor =
-            ArgumentCaptor.forClass(BroadcastReceiver.class);
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
         verify(mContext).registerReceiver(
-            userRemovedReceiverCaptor.capture(), any(IntentFilter.class));
+                userRemovedReceiverCaptor.capture(), any(IntentFilter.class));
         mUserRemovedReceiver = userRemovedReceiverCaptor.getAllValues().get(0);
 
         mDefaultDialerSettingObserver = mDefaultDialerCache.getContentObserver();
@@ -140,7 +144,10 @@
         Intent packageChangeIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED,
                 Uri.fromParts("package", DIALER1, null));
         when(mRoleManagerAdapter.getDefaultDialerApp(eq(USER0))).thenReturn(DIALER2);
+
         mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
+        waitForHandlerAction(mDefaultDialerCache.mHandler, DELAY_TOLERANCE);
+
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER0));
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER1));
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER2));
@@ -158,6 +165,8 @@
         Intent packageChangeIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED,
                 Uri.fromParts("package", "red.orange.blue", null));
         mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
+        waitForHandlerAction(mDefaultDialerCache.mHandler, DELAY_TOLERANCE);
+
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER0));
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER1));
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER2));
@@ -192,6 +201,8 @@
         packageChangeIntent.putExtra(Intent.EXTRA_REPLACING, false);
 
         mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
+        waitForHandlerAction(mDefaultDialerCache.mHandler, DELAY_TOLERANCE);
+
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER0));
         verify(mRoleManagerAdapter, times(1)).getDefaultDialerApp(eq(USER1));
         verify(mRoleManagerAdapter, times(1)).getDefaultDialerApp(eq(USER2));
@@ -208,6 +219,8 @@
                 Uri.fromParts("package", "ppp.qqq.zzz", null));
 
         mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
+        waitForHandlerAction(mDefaultDialerCache.mHandler, DELAY_TOLERANCE);
+
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER0));
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER1));
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER2));
@@ -225,6 +238,8 @@
         packageChangeIntent.putExtra(Intent.EXTRA_REPLACING, true);
 
         mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
+        waitForHandlerAction(mDefaultDialerCache.mHandler, DELAY_TOLERANCE);
+
         verify(mRoleManagerAdapter, times(1)).getDefaultDialerApp(eq(USER0));
         verify(mRoleManagerAdapter, times(1)).getDefaultDialerApp(eq(USER1));
         verify(mRoleManagerAdapter, times(1)).getDefaultDialerApp(eq(USER2));
@@ -240,7 +255,9 @@
         when(mRoleManagerAdapter.getDefaultDialerApp(eq(USER0))).thenReturn(DIALER2);
         when(mRoleManagerAdapter.getDefaultDialerApp(eq(USER1))).thenReturn(DIALER2);
         when(mRoleManagerAdapter.getDefaultDialerApp(eq(USER2))).thenReturn(DIALER2);
+
         mDefaultDialerSettingObserver.onChange(false);
+        waitForHandlerAction(mDefaultDialerCache.mHandler, DELAY_TOLERANCE);
 
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER0));
         verify(mRoleManagerAdapter, times(2)).getDefaultDialerApp(eq(USER2));