Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c9f677b authored by Hui Wang's avatar Hui Wang
Browse files

Improve the performance to avoid the potential ANR

The operation of onReceive may take a longer time than expected,
which can be posted to the handler thread to prevent the potential
ANR. The synchronized block is not necessary and can be replaced
by the concurrent collection.

Flag: EXEMPT bugfix
Bug: 369750121
Test: atest TelecomUnitTests:DefaultDialerCacheTest
Change-Id: Id30792c9bf3d32a813c4d2eb27dda350139bb93e
parent 6b2d00fc
Loading
Loading
Loading
Loading
+74 −88
Original line number Diff line number Diff line
@@ -31,44 +31,29 @@ import android.os.UserHandle;
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) {
            mHandler.post(() -> {
                Log.startSession("DDC.oR");
                try {
                    String packageName;
@@ -85,16 +70,13 @@ public class DefaultDialerCache {
                        return;
                    }

                synchronized (mLock) {
                    refreshCachesForUsersWithPackage(packageName);
                }

                } finally {
                    Log.endSession();
                }
            });
        }
    };

    private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
@@ -110,8 +92,6 @@ public class DefaultDialerCache {
            }
        }
    };

    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 @@ public class DefaultDialerCache {
            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);
                }
            } finally {
                Log.endSession();
            }
@@ -132,13 +110,6 @@ public class DefaultDialerCache {
            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,
@@ -148,13 +119,12 @@ public class DefaultDialerCache {
        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 @@ public class DefaultDialerCache {
        //
        //synchronized (mLock) {
        //    String defaultDialer = mCurrentDefaultDialerPerUser.get(userId);
        //    if (defaultDialer != null) {
        //    if (!TextUtils.isEmpty(defaultDialer)) {
        //        return defaultDialer;
        //    }
        //}
@@ -242,10 +212,8 @@ public class DefaultDialerCache {
        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);
            }
            mCurrentDefaultDialerPerUser.put(userId, packageName == null ? "" : packageName);
        }
        return isChanged;
    }
@@ -253,47 +221,39 @@ public class DefaultDialerCache {
    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);
    }
    }

    /**
     * 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 class DefaultDialerCache {
    public RoleManagerAdapter getRoleManagerAdapter() {
        return mRoleManagerAdapter;
    }

    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);
        }
    }
}
+23 −6
Original line number Diff line number Diff line
@@ -56,14 +56,17 @@ public class DefaultDialerCacheTest extends TelecomTestCase {
    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,7 +79,8 @@ public class DefaultDialerCacheTest extends TelecomTestCase {

        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),
@@ -140,7 +144,10 @@ public class DefaultDialerCacheTest extends TelecomTestCase {
        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 @@ public class DefaultDialerCacheTest extends TelecomTestCase {
        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 @@ public class DefaultDialerCacheTest extends TelecomTestCase {
        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 @@ public class DefaultDialerCacheTest extends TelecomTestCase {
                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 @@ public class DefaultDialerCacheTest extends TelecomTestCase {
        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 @@ public class DefaultDialerCacheTest extends TelecomTestCase {
        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));