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

Commit ac9e6f74 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement runtime account update notifications."

parents 07bf9630 8882d881
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