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

Commit a670a25f authored by Hall Liu's avatar Hall Liu Committed by android-build-merger
Browse files

Cache default dialer in Telecom (part 1) am: eacd67b9 am: 1cb4a322

am: d4d3c316

Change-Id: I60adab9846815daabc02732e26fff8a3fd9e7936
parents 239bbd4a d4d3c316
Loading
Loading
Loading
Loading
+180 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.telecom;

import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.provider.Settings;
import android.telecom.Log;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;

import java.util.Objects;

public class DefaultDialerCache {
    private static final String LOG_TAG = "DefaultDialerCache";
    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 = intent.getData().getSchemeSpecificPart();
                } 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 {
                    return;
                }

                synchronized (mLock) {
                    refreshCachesForUsersWithPackage(packageName);
                }

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

    private final Handler mHandler = new Handler(Looper.getMainLooper());
    private final ContentObserver mDefaultDialerObserver = new ContentObserver(mHandler) {
        @Override
        public void onChange(boolean selfChange) {
            Log.startSession("DDC.oC");
            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();
            }
        }

        @Override
        public boolean deliverSelfNotifications() {
            return true;
        }
    };

    private final Context mContext;
    private final TelecomServiceImpl.DefaultDialerManagerAdapter mDefaultDialerManagerAdapter;
    private final TelecomSystem.SyncRoot mLock;
    private final String mSystemDialerName;
    private SparseArray<String> mCurrentDefaultDialerPerUser = new SparseArray<>();

    public DefaultDialerCache(Context context,
            TelecomServiceImpl.DefaultDialerManagerAdapter defaultDialerManagerAdapter,
            TelecomSystem.SyncRoot lock) {
        mContext = context;
        mDefaultDialerManagerAdapter = defaultDialerManagerAdapter;
        mLock = lock;
        mSystemDialerName = mContext.getResources().getString(R.string.ui_default_package);

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        intentFilter.addDataScheme("package");
        context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);

        Uri defaultDialerSetting =
                Settings.Secure.getUriFor(Settings.Secure.DIALER_DEFAULT_APPLICATION);
        context.getContentResolver()
                .registerContentObserver(defaultDialerSetting, false, mDefaultDialerObserver,
                        UserHandle.USER_ALL);
    }

    public String getDefaultDialerApplication(int userId) {
        if (userId == UserHandle.USER_CURRENT) {
            userId = ActivityManager.getCurrentUser();
        }

        if (userId < 0) {
            Log.w(LOG_TAG, "Attempting to get default dialer for a meta-user %d", userId);
            return null;
        }

        synchronized (mLock) {
            String defaultDialer = mCurrentDefaultDialerPerUser.get(userId);
            if (defaultDialer != null) {
                return defaultDialer;
            }
        }
        return refreshCacheForUser(userId);
    }

    public String getDefaultDialerApplication() {
        return getDefaultDialerApplication(mContext.getUserId());
    }

    public boolean isDefaultOrSystemDialer(Context context, String packageName) {
        String defaultDialer = getDefaultDialerApplication(context.getUserId());
        return Objects.equals(packageName, defaultDialer)
                || Objects.equals(packageName, mSystemDialerName);
    }

    private String refreshCacheForUser(int userId) {
        String currentDefaultDialer =
                mDefaultDialerManagerAdapter.getDefaultDialerApplication(mContext, userId);
        synchronized (mLock) {
            mCurrentDefaultDialerPerUser.put(userId, 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))) {
                String newDefaultDialer = refreshCacheForUser(userId);
                Log.i(LOG_TAG, "Refreshing default dialer for user %d: now %s",
                        userId, newDefaultDialer);
            }
        }
    }

    /**
     * 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
    public ContentObserver getContentObserver() {
        return mDefaultDialerObserver;
    }
}
 No newline at end of file
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@

    <!-- TODO: Needed because we call ActivityManager.getCurrentUser() statically. -->
    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
    <uses-permission android:name="android.permission.MANAGE_USERS" />

    <!-- Used to access TelephonyManager APIs -->
+6 −0
Original line number Diff line number Diff line
@@ -258,6 +258,12 @@ public class ComponentContextFixture implements TestFixture<Context> {
            return null;
        }

        @Override
        public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle handle,
                IntentFilter filter, String broadcastPermission, Handler scheduler) {
            return null;
        }

        @Override
        public void sendBroadcast(Intent intent) {
            // TODO -- need to ensure this is captured
+221 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.telecom.tests;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
import android.test.suitebuilder.annotation.SmallTest;

import com.android.server.telecom.DefaultDialerCache;
import com.android.server.telecom.TelecomServiceImpl;
import com.android.server.telecom.TelecomSystem;

import org.mockito.ArgumentCaptor;
import org.mockito.Mock;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class DefaultDialerCacheTest extends TelecomTestCase {

    private static final String DIALER1 = "com.android.dialer";
    private static final String DIALER2 = "xyz.abc.dialer";
    private static final String DIALER3 = "aaa.bbb.ccc.ddd";
    private static final int USER0 = 0;
    private static final int USER1 = 1;
    private static final int USER2 = 2;

    private DefaultDialerCache mDefaultDialerCache;
    private ContentObserver mDefaultDialerSettingObserver;
    private BroadcastReceiver mPackageChangeReceiver;

    @Mock private TelecomServiceImpl.DefaultDialerManagerAdapter mMockDefaultDialerManager;

    public void setUp() throws Exception {
        super.setUp();
        mContext = mComponentContextFixture.getTestDouble().getApplicationContext();

        ArgumentCaptor<BroadcastReceiver> receiverCaptor =
                ArgumentCaptor.forClass(BroadcastReceiver.class);

        mDefaultDialerCache = new DefaultDialerCache(
                mContext, mMockDefaultDialerManager, new TelecomSystem.SyncRoot() { });

        verify(mContext).registerReceiverAsUser(
                receiverCaptor.capture(), eq(UserHandle.ALL), any(IntentFilter.class),
                isNull(String.class), isNull(Handler.class));
        mPackageChangeReceiver = receiverCaptor.getValue();
        mDefaultDialerSettingObserver = mDefaultDialerCache.getContentObserver();

        when(mMockDefaultDialerManager.getDefaultDialerApplication(any(Context.class), eq(USER0)))
                .thenReturn(DIALER1);
        when(mMockDefaultDialerManager.getDefaultDialerApplication(any(Context.class), eq(USER1)))
                .thenReturn(DIALER2);
        when(mMockDefaultDialerManager.getDefaultDialerApplication(any(Context.class), eq(USER2)))
                .thenReturn(DIALER3);
    }

    @SmallTest
    public void testThreeUsers() {
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER2), DIALER3);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER2), DIALER3);

        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER0));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER1));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER2));
    }

    @SmallTest
    public void testDialer1PackageChanged() {
        // Populate the caches first
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER2), DIALER3);

        Intent packageChangeIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED,
                Uri.fromParts("package", DIALER1, null));
        when(mMockDefaultDialerManager.getDefaultDialerApplication(any(Context.class), eq(USER0)))
                .thenReturn(DIALER2);
        mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
        verify(mMockDefaultDialerManager, times(2))
                .getDefaultDialerApplication(any(Context.class), eq(USER0));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER1));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER2));

        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER2);
    }

    @SmallTest
    public void testRandomOtherPackageChanged() {
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER2), DIALER3);

        Intent packageChangeIntent = new Intent(Intent.ACTION_PACKAGE_CHANGED,
                Uri.fromParts("package", "red.orange.blue", null));
        mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER0));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER1));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER2));
    }

    @SmallTest
    public void testPackageRemovedWithoutReplace() {
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER2), DIALER3);

        Intent packageChangeIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
                Uri.fromParts("package", DIALER1, null));
        packageChangeIntent.putExtra(Intent.EXTRA_REPLACING, false);

        mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
        verify(mMockDefaultDialerManager, times(2))
                .getDefaultDialerApplication(any(Context.class), eq(USER0));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER1));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER2));
    }

    @SmallTest
    public void testPackageAdded() {
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER2), DIALER3);

        Intent packageChangeIntent = new Intent(Intent.ACTION_PACKAGE_ADDED,
                Uri.fromParts("package", "ppp.qqq.zzz", null));

        mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
        verify(mMockDefaultDialerManager, times(2))
                .getDefaultDialerApplication(any(Context.class), eq(USER0));
        verify(mMockDefaultDialerManager, times(2))
                .getDefaultDialerApplication(any(Context.class), eq(USER1));
        verify(mMockDefaultDialerManager, times(2))
                .getDefaultDialerApplication(any(Context.class), eq(USER2));
    }

    @SmallTest
    public void testPackageRemovedWithReplace() {
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER2), DIALER3);

        Intent packageChangeIntent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
                Uri.fromParts("package", DIALER1, null));
        packageChangeIntent.putExtra(Intent.EXTRA_REPLACING, true);

        mPackageChangeReceiver.onReceive(mContext, packageChangeIntent);
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER0));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER1));
        verify(mMockDefaultDialerManager, times(1))
                .getDefaultDialerApplication(any(Context.class), eq(USER2));
    }

    @SmallTest
    public void testDefaultDialerSettingChanged() {
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER1);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER2), DIALER3);

        when(mMockDefaultDialerManager.getDefaultDialerApplication(any(Context.class), eq(USER0)))
                .thenReturn(DIALER2);
        when(mMockDefaultDialerManager.getDefaultDialerApplication(any(Context.class), eq(USER1)))
                .thenReturn(DIALER2);
        when(mMockDefaultDialerManager.getDefaultDialerApplication(any(Context.class), eq(USER2)))
                .thenReturn(DIALER2);
        mDefaultDialerSettingObserver.onChange(false);

        verify(mMockDefaultDialerManager, times(2))
                .getDefaultDialerApplication(any(Context.class), eq(USER0));
        verify(mMockDefaultDialerManager, times(2))
                .getDefaultDialerApplication(any(Context.class), eq(USER2));
        verify(mMockDefaultDialerManager, times(2))
                .getDefaultDialerApplication(any(Context.class), eq(USER2));

        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER0), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER1), DIALER2);
        assertEquals(mDefaultDialerCache.getDefaultDialerApplication(USER2), DIALER2);
    }
}