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

Commit 8882d881 authored by Dmitry Dementyev's avatar Dmitry Dementyev
Browse files

Implement runtime account update notifications.

Test: manual, cts.
Change-Id: Iaf9fe4814c3d3c2ca2dca0a5bfa0f997e060bfeb
parent 1c10a61a
Loading
Loading
Loading
Loading
+42 −12
Original line number Diff line number Diff line
@@ -343,6 +343,17 @@ public class AccountManager {
    public static final String LOGIN_ACCOUNTS_CHANGED_ACTION =
        "android.accounts.LOGIN_ACCOUNTS_CHANGED";

    /**
     * Action sent as a broadcast Intent to specific package by the AccountsService
     * when account visibility or account's credentials (saved password, etc) are changed.
     *
     * @see #addOnAccountsUpdatedListener
     *
     * @hide
     */
    public static final String ACTION_VISIBLE_ACCOUNTS_CHANGED =
        "android.accounts.action.VISIBLE_ACCOUNTS_CHANGED";

    /**
     * Key to set default visibility for applications targeting API level
     * {@link android.os.Build.VERSION_CODES#O} or above and don't have the same signature as
@@ -1057,8 +1068,8 @@ public class AccountManager {

    /**
     * Gets the previous name associated with the account or {@code null}, if
     * none. This is intended so that clients of {@link
     * #LOGIN_ACCOUNTS_CHANGED_ACTION} broadcasts can determine if an
     * none. This is intended so that clients of
     * {@link OnAccountsUpdateListener} can determine if an
     * authenticator has renamed an account.
     *
     * <p>It is safe to call this method from the main thread.
@@ -1555,7 +1566,8 @@ public class AccountManager {
     * <p>In that case, you may need to wait until the user responds, which
     * could take hours or days or forever.  When the user does respond and
     * supply a new password, the account manager will broadcast the
     * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can
     * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent and
     * notify {@link OnAccountsUpdateListener} which applications can
     * use to try again.
     *
     * <p>If notifyAuthFailure is not set, it is the application's
@@ -1631,7 +1643,8 @@ public class AccountManager {
     * <p>In that case, you may need to wait until the user responds, which
     * could take hours or days or forever.  When the user does respond and
     * supply a new password, the account manager will broadcast the
     * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent, which applications can
     * {@link #LOGIN_ACCOUNTS_CHANGED_ACTION} Intent and
     * notify {@link OnAccountsUpdateListener} which applications can
     * use to try again.
     *
     * <p>If notifyAuthFailure is not set, it is the application's
@@ -2811,7 +2824,7 @@ public class AccountManager {
            Maps.newHashMap();

    /**
     * BroadcastReceiver that listens for the LOGIN_ACCOUNTS_CHANGED_ACTION intent
     * BroadcastReceiver that listens for the ACTION_VISIBLE_ACCOUNTS_CHANGED intent
     * so that it can read the updated list of accounts and send them to the listener
     * in mAccountsUpdatedListeners.
     */
@@ -2882,21 +2895,26 @@ public class AccountManager {
            if (accountTypes != null) {
                mAccountsUpdatedListenersTypes.put(listener,
                    new HashSet<String>(Arrays.asList(accountTypes)));
            } else {
                mAccountsUpdatedListenersTypes.put(listener, null);
            }

            if (wasEmpty) {
                // Register a broadcast receiver to monitor account changes
                IntentFilter intentFilter = new IntentFilter();
                // TODO get rid of the broadcast receiver
                // create android.os.ResultReceiver
                // send it to the service via aidl
                // handle onReceiveResult
                intentFilter.addAction(LOGIN_ACCOUNTS_CHANGED_ACTION);
                intentFilter.addAction(ACTION_VISIBLE_ACCOUNTS_CHANGED);
                // To recover from disk-full.
                intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
                // Register a broadcast receiver to monitor account changes
                mContext.registerReceiver(mAccountsChangedBroadcastReceiver, intentFilter);
            }

            try {
                // Notify AccountManagedService about new receiver.
                // The receiver must be unregistered later exactly one time
                mService.registerAccountListener(accountTypes, mContext.getOpPackageName());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
        if (updateImmediately) {
            postToHandler(handler, listener, getAccounts());
@@ -2923,11 +2941,23 @@ public class AccountManager {
                Log.e(TAG, "Listener was not previously added");
                return;
            }
            Set<String> accountTypes = mAccountsUpdatedListenersTypes.get(listener);
            String[] accountsArray;
            if (accountTypes != null) {
                accountsArray = accountTypes.toArray(new String[accountTypes.size()]);
            } else {
                accountsArray = null;
            }
            mAccountsUpdatedListeners.remove(listener);
            mAccountsUpdatedListenersTypes.remove(listener);
            if (mAccountsUpdatedListeners.isEmpty()) {
                mContext.unregisterReceiver(mAccountsChangedBroadcastReceiver);
            }
            try {
                mService.unregisterAccountListener(accountsArray, mContext.getOpPackageName());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }

+3 −0
Original line number Diff line number Diff line
@@ -117,6 +117,9 @@ interface IAccountManager {
    /* Type may be null returns Map <Account, Integer>*/
    Map getAccountsAndVisibilityForPackage(in String packageName, in String accountType);

    void registerAccountListener(in String[] accountTypes, String opPackageName);
    void unregisterAccountListener(in String[] accountTypes, String opPackageName);

    /* Check if the package in a user can access an account */
    boolean hasAccountAccess(in Account account, String packageName, in UserHandle userHandle);
    /* Crate an intent to request account access for package and a given user id */
+1 −0
Original line number Diff line number Diff line
@@ -486,6 +486,7 @@
    <protected-broadcast android:name="android.intent.action.ACTION_RADIO_OFF" />

    <protected-broadcast android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED" />
    <protected-broadcast android:name="android.accounts.action.VISIBLE_ACCOUNTS_CHANGED" />
    <protected-broadcast android:name="com.android.sync.SYNC_CONN_STATUS_CHANGED" />

    <protected-broadcast android:name="com.android.phone.SIP_INCOMING_CALL" />
+169 −121

File changed.

Preview size limit exceeded, changes collapsed.

+172 −2
Original line number Diff line number Diff line
@@ -24,6 +24,8 @@ import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.nullable;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@@ -87,6 +89,17 @@ import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;


/**
 * Tests for {@link AccountManagerService}.
 * <p>Run with:<pre>
 * mmma -j40 frameworks/base/services/tests/servicestests
 * adb install -r ${OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
 * adb shell am instrument -w -e class package com.android.server.accounts \
 * com.android.frameworks.servicestests\
 * /android.support.test.runner.AndroidJUnitRunner
 * </pre>
 */
public class AccountManagerServiceTest extends AndroidTestCase {
    private static final String TAG = AccountManagerServiceTest.class.getSimpleName();
    private static final long ONE_DAY_IN_MILLISECOND = 86400000;
@@ -103,6 +116,8 @@ public class AccountManagerServiceTest extends AndroidTestCase {

    @Captor private ArgumentCaptor<Intent> mIntentCaptor;
    @Captor private ArgumentCaptor<Bundle> mBundleCaptor;
    private int mVisibleAccountsChangedBroadcasts;
    private int mLoginAccountsChangedBroadcasts;

    private static final int LATCH_TIMEOUT_MS = 500;
    private static final String PREN_DB = "pren.db";
@@ -1042,7 +1057,7 @@ public class AccountManagerServiceTest extends AndroidTestCase {
        waitForLatch(latch);
        // Verify notification is cancelled
        verify(mMockNotificationManager).cancelNotificationWithTag(
                anyString(), anyString(), anyInt(), anyInt());
                anyString(), nullable(String.class), anyInt(), anyInt());

        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
        Bundle result = mBundleCaptor.getValue();
@@ -1889,7 +1904,7 @@ public class AccountManagerServiceTest extends AndroidTestCase {
        waitForLatch(latch);
        // Verify notification is cancelled
        verify(mMockNotificationManager).cancelNotificationWithTag(
                anyString(), anyString(), anyInt(), anyInt());
                anyString(), nullable(String.class), anyInt(), anyInt());

        verify(mMockAccountManagerResponse).onResult(mBundleCaptor.capture());
        Bundle result = mBundleCaptor.getValue();
@@ -2446,6 +2461,161 @@ public class AccountManagerServiceTest extends AndroidTestCase {
        verify(mMockAccountManagerResponse, never()).onResult(any(Bundle.class));
    }

    @SmallTest
    public void testRegisterAccountListener() throws Exception {
        unlockSystemUser();
        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage"); // opPackageName

        mAms.registerAccountListener(
            null, //accountTypes
            "testpackage"); // opPackageName

        // Check that two previously registered receivers can be unregistered successfully.
        mAms.unregisterAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage"); // opPackageName

        mAms.unregisterAccountListener(
             null, //accountTypes
            "testpackage"); // opPackageName
    }

    @SmallTest
    public void testRegisterAccountListenerAndAddAccount() throws Exception {
        unlockSystemUser();
        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage"); // opPackageName

        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
        // Notification about new account
        updateBroadcastCounters(2);
        assertEquals(mVisibleAccountsChangedBroadcasts, 1);
        assertEquals(mLoginAccountsChangedBroadcasts, 1);
    }

    @SmallTest
    public void testRegisterAccountListenerAndAddAccountOfDifferentType() throws Exception {
        unlockSystemUser();
        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_2},
            "testpackage"); // opPackageName

        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
        mAms.addAccountExplicitly(
            AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p11", null);
        // Notification about new account

        updateBroadcastCounters(2);
        assertEquals(mVisibleAccountsChangedBroadcasts, 0); // broadcast was not sent
        assertEquals(mLoginAccountsChangedBroadcasts, 2);
    }

    @SmallTest
    public void testRegisterAccountListenerWithAddingTwoAccounts() throws Exception {
        unlockSystemUser();
        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage"); // opPackageName
        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
        mAms.unregisterAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage"); // opPackageName
        mAms.addAccountExplicitly(
            AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p11", null);

        updateBroadcastCounters(3);
        assertEquals(mVisibleAccountsChangedBroadcasts, 1);
        assertEquals(mLoginAccountsChangedBroadcasts, 2);

        mAms.removeAccountInternal(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS);
        mAms.registerAccountListener( null /* accountTypes */, "testpackage");
        mAms.removeAccountInternal(AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE);

        updateBroadcastCounters(6);
        assertEquals(mVisibleAccountsChangedBroadcasts, 2);
        assertEquals(mLoginAccountsChangedBroadcasts, 4);
    }

    @SmallTest
    public void testRegisterAccountListenerForThreePackages() throws Exception {
        unlockSystemUser();
        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage1"); // opPackageName
        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage2"); // opPackageName
        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage3"); // opPackageName
        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
        updateBroadcastCounters(4);
        assertEquals(mVisibleAccountsChangedBroadcasts, 3);
        assertEquals(mLoginAccountsChangedBroadcasts, 1);

        mAms.unregisterAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage3"); // opPackageName
        // Remove account with 2 active listeners.
        mAms.removeAccountInternal(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS);
        updateBroadcastCounters(7);
        assertEquals(mVisibleAccountsChangedBroadcasts, 5);
        assertEquals(mLoginAccountsChangedBroadcasts, 2); // 3 add, 2 remove

        // Add account of another type.
        mAms.addAccountExplicitly(
            AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS_TYPE_2, "p11", null);

        updateBroadcastCounters(8);
        assertEquals(mVisibleAccountsChangedBroadcasts, 5);
        assertEquals(mLoginAccountsChangedBroadcasts, 3);
    }

    @SmallTest
    public void testRegisterAccountListenerCredentialsUpdate() throws Exception {
        unlockSystemUser();
        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage"); // opPackageName
        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
        mAms.setPassword(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "pwd");
        updateBroadcastCounters(4);
        assertEquals(mVisibleAccountsChangedBroadcasts, 2);
        assertEquals(mLoginAccountsChangedBroadcasts, 2);
    }

    @SmallTest
    public void testUnregisterAccountListenerNotRegistered() throws Exception {
        unlockSystemUser();
        try {
            mAms.unregisterAccountListener(
                new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
                "testpackage"); // opPackageName
            fail("IllegalArgumentException expected. But no exception was thrown.");
        } catch (IllegalArgumentException e) {
            // IllegalArgumentException is expected.
        }
    }

    private void updateBroadcastCounters (int expectedBroadcasts){
        mVisibleAccountsChangedBroadcasts = 0;
        mLoginAccountsChangedBroadcasts = 0;
        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mMockContext, times(expectedBroadcasts)).sendBroadcastAsUser(captor.capture(),
            any(UserHandle.class));
        for (Intent intent : captor.getAllValues()) {
            if (AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED. equals(intent.getAction())) {
                mVisibleAccountsChangedBroadcasts++;
            }
            if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION. equals(intent.getAction())) {
                mLoginAccountsChangedBroadcasts++;
            }
        }
    }

    private void waitForLatch(CountDownLatch latch) {
        try {
            latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
Loading