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

Commit 257ba894 authored by Dmitry Dementyev's avatar Dmitry Dementyev Committed by Android (Google) Code Review
Browse files

Merge "Update PACKAGE_REMOVED broadcast." into oc-dev

parents efb859f2 b6a7dc03
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -345,7 +345,13 @@ public class AccountManager {
        "android.accounts.LOGIN_ACCOUNTS_CHANGED";

    /**
     * Action sent as a broadcast Intent by the AccountsService when any account is removed.
     * Action sent as a broadcast Intent by the AccountsService when any account is removed
     * or renamed. Only applications which were able to see the account will receive the intent.
     * Intent extra will include the following fields:
     * <ul>
     * <li> {@link #KEY_ACCOUNT_NAME} - the name of the removed account
     * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account
     * </ul>
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    @BroadcastBehavior(includeBackground = true)
+89 −15
Original line number Diff line number Diff line
@@ -112,6 +112,7 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
@@ -752,10 +753,12 @@ public class AccountManagerService
        synchronized (accounts.dbLock) {
            synchronized (accounts.cacheLock) {
                Map<String, Integer> packagesToVisibility;
                List<String> accountRemovedReceivers;
                if (notify) {
                    if (isSpecialPackageKey(packageName)) {
                        packagesToVisibility =
                                getRequestingPackages(account, accounts);
                        accountRemovedReceivers = getAccountRemovedReceivers(account, accounts);
                    } else {
                        if (!packageExistsForUser(packageName, accounts.userId)) {
                            return false; // package is not installed.
@@ -763,15 +766,20 @@ public class AccountManagerService
                        packagesToVisibility = new HashMap<>();
                        packagesToVisibility.put(packageName,
                                resolveAccountVisibility(account, packageName, accounts));
                        accountRemovedReceivers = new ArrayList<>();
                        if (shouldNotifyPackageOnAccountRemoval(account, packageName, accounts)) {
                            accountRemovedReceivers.add(packageName);
                        }
                    }
                } else {
                    // Notifications will not be send.
                    // Notifications will not be send - only used during add account.
                    if (!isSpecialPackageKey(packageName) &&
                            !packageExistsForUser(packageName, accounts.userId)) {
                        // package is not installed and not meta value.
                        return false;
                    }
                    packagesToVisibility = new HashMap<>();
                    packagesToVisibility = Collections.emptyMap();
                    accountRemovedReceivers = Collections.emptyList();
                }

                if (!updateAccountVisibilityLocked(account, packageName, newVisibility, accounts)) {
@@ -781,11 +789,14 @@ public class AccountManagerService
                if (notify) {
                    for (Entry<String, Integer> packageToVisibility : packagesToVisibility
                            .entrySet()) {
                        if (packageToVisibility.getValue()
                                != AccountManager.VISIBILITY_NOT_VISIBLE) {
                        if (shouldNotifyOnVisibilityChange(packageToVisibility.getValue(),
                                resolveAccountVisibility(account, packageName, accounts))) {
                            notifyPackage(packageToVisibility.getKey(), accounts);
                        }
                    }
                    for (String packageNameToNotify : accountRemovedReceivers) {
                        sendAccountRemovedBroadcast(account, packageNameToNotify, accounts.userId);
                    }
                    sendAccountsChangedBroadcast(accounts.userId);
                }
                return true;
@@ -889,10 +900,11 @@ public class AccountManagerService
    // Send notification to all packages which can potentially see the account
    private void sendNotificationAccountUpdated(Account account, UserAccounts accounts) {
        Map<String, Integer> packagesToVisibility = getRequestingPackages(account, accounts);
        // packages with VISIBILITY_USER_MANAGED_NOT_VISIBL still get notification.
        // Should we notify VISIBILITY_NOT_VISIBLE packages when account is added?

        for (Entry<String, Integer> packageToVisibility : packagesToVisibility.entrySet()) {
            if (packageToVisibility.getValue() != AccountManager.VISIBILITY_NOT_VISIBLE) {
            if ((packageToVisibility.getValue() != AccountManager.VISIBILITY_NOT_VISIBLE)
                    && (packageToVisibility.getValue()
                        != AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE)) {
                notifyPackage(packageToVisibility.getKey(), accounts);
            }
        }
@@ -931,6 +943,44 @@ public class AccountManagerService
        return result;
    }

    // Returns a list of packages listening to ACTION_ACCOUNT_REMOVED able to see the account.
    private List<String> getAccountRemovedReceivers(Account account, UserAccounts accounts) {
        Intent intent = new Intent(AccountManager.ACTION_ACCOUNT_REMOVED);
        intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        List<ResolveInfo> receivers =
            mPackageManager.queryBroadcastReceiversAsUser(intent, 0, accounts.userId);
        List<String> result = new ArrayList<>();
        if (receivers == null) {
            return result;
        }
        for (ResolveInfo resolveInfo: receivers) {
            String packageName = resolveInfo.activityInfo.applicationInfo.packageName;
            int visibility = resolveAccountVisibility(account, packageName, accounts);
            if (visibility == AccountManager.VISIBILITY_VISIBLE
                || visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE) {
                result.add(packageName);
            }
        }
        return result;
    }

    // Returns true if given package is listening to ACTION_ACCOUNT_REMOVED and can see the account.
    private boolean shouldNotifyPackageOnAccountRemoval(Account account,
            String packageName, UserAccounts accounts) {
        int visibility = resolveAccountVisibility(account, packageName, accounts);
        if (visibility != AccountManager.VISIBILITY_VISIBLE
            && visibility != AccountManager.VISIBILITY_USER_MANAGED_VISIBLE) {
            return false;
        }

        Intent intent = new Intent(AccountManager.ACTION_ACCOUNT_REMOVED);
        intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        intent.setPackage(packageName);
        List<ResolveInfo> receivers =
            mPackageManager.queryBroadcastReceiversAsUser(intent, 0, accounts.userId);
        return (receivers != null && receivers.size() > 0);
    }

    private boolean packageExistsForUser(String packageName, int userId) {
        try {
            long identityToken = clearCallingIdentity();
@@ -959,9 +1009,12 @@ public class AccountManagerService
        mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId));
    }

    private void sendAccountRemovedBroadcast(int userId) {
    private void sendAccountRemovedBroadcast(Account account, String packageName, int userId) {
        Intent intent = new Intent(AccountManager.ACTION_ACCOUNT_REMOVED);
        intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        intent.setPackage(packageName);
        intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
        intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, account.type);
        mContext.sendBroadcastAsUser(intent, new UserHandle(userId));
    }

@@ -1087,6 +1140,8 @@ public class AccountManagerService
                                    + "'s registered authenticator no longer exist.");
                            Map<String, Integer> packagesToVisibility =
                                    getRequestingPackages(account, accounts);
                            List<String> accountRemovedReceivers =
                                getAccountRemovedReceivers(account, accounts);
                            accountsDb.beginTransaction();
                            try {
                                accountsDb.deleteDeAccount(accountId);
@@ -1112,12 +1167,14 @@ public class AccountManagerService

                            for (Entry<String, Integer> packageToVisibility :
                                    packagesToVisibility.entrySet()) {
                                if (packageToVisibility.getValue()
                                        != AccountManager.VISIBILITY_NOT_VISIBLE) {
                                if (shouldNotifyOnVisibilityChange(packageToVisibility.getValue(),
                                        AccountManager.VISIBILITY_NOT_VISIBLE)) {
                                    notifyPackage(packageToVisibility.getKey(), accounts);
                                }
                            }
                            sendAccountRemovedBroadcast(accounts.userId);
                            for (String packageName : accountRemovedReceivers) {
                                sendAccountRemovedBroadcast(account, packageName, accounts.userId);
                            }
                        } else {
                            ArrayList<String> accountNames = accountNamesByType.get(account.type);
                            if (accountNames == null) {
@@ -1147,6 +1204,14 @@ public class AccountManagerService
        }
    }

    private boolean shouldNotifyOnVisibilityChange(int oldVisibility, int newVisibility) {
        boolean oldVisible = (oldVisibility == AccountManager.VISIBILITY_VISIBLE) ||
            (oldVisibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
        boolean newVisible = (newVisibility == AccountManager.VISIBILITY_VISIBLE) ||
            (newVisibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
        return oldVisible == newVisible;
    }

    private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) {
        // Get the UIDs of all apps that might have data on the device. We want
        // to preserve user data if the app might otherwise be storing data.
@@ -1911,6 +1976,8 @@ public class AccountManagerService
        }
        synchronized (accounts.dbLock) {
            synchronized (accounts.cacheLock) {
                List<String> accountRemovedReceivers =
                    getAccountRemovedReceivers(accountToRename, accounts);
                accounts.accountsDb.beginTransaction();
                Account renamedAccount = new Account(newName, accountToRename.type);
                if ((accounts.accountsDb.findCeAccountId(renamedAccount) >= 0)) {
@@ -1978,7 +2045,9 @@ public class AccountManagerService

                sendNotificationAccountUpdated(resultAccount, accounts);
                sendAccountsChangedBroadcast(accounts.userId);
                sendAccountRemovedBroadcast(accounts.userId);
                for (String packageName : accountRemovedReceivers) {
                    sendAccountRemovedBroadcast(accountToRename, packageName, accounts.userId);
                }
            }
        }
        return resultAccount;
@@ -2181,6 +2250,8 @@ public class AccountManagerService
            synchronized (accounts.cacheLock) {
                Map<String, Integer> packagesToVisibility = getRequestingPackages(account,
                        accounts);
                List<String> accountRemovedReceivers =
                    getAccountRemovedReceivers(account, accounts);
                accounts.accountsDb.beginTransaction();
                // Set to a dummy value, this will only be used if the database
                // transaction succeeds.
@@ -2206,15 +2277,18 @@ public class AccountManagerService
                    removeAccountFromCacheLocked(accounts, account);
                    for (Entry<String, Integer> packageToVisibility : packagesToVisibility
                            .entrySet()) {
                        if (packageToVisibility.getValue()
                                != AccountManager.VISIBILITY_NOT_VISIBLE) {
                        if ((packageToVisibility.getValue() == AccountManager.VISIBILITY_VISIBLE)
                                || (packageToVisibility.getValue()
                                    == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE)) {
                            notifyPackage(packageToVisibility.getKey(), accounts);
                        }
                    }

                    // Only broadcast LOGIN_ACCOUNTS_CHANGED if a change occurred.
                    sendAccountsChangedBroadcast(accounts.userId);
                    sendAccountRemovedBroadcast(accounts.userId);
                    for (String packageName : accountRemovedReceivers) {
                        sendAccountRemovedBroadcast(account, packageName, accounts.userId);
                    }
                    String action = userUnlocked ? AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE
                            : AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE_DE;
                    logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts);
+77 −15
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.nullable;
import static org.mockito.Mockito.times;
@@ -80,6 +81,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
@@ -2443,7 +2445,6 @@ public class AccountManagerServiceTest extends AndroidTestCase {
    @SmallTest
    public void testGetAccountsByFeaturesError() throws Exception {
        unlockSystemUser();

        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_ERROR, "p12", null);

@@ -2511,28 +2512,37 @@ public class AccountManagerServiceTest extends AndroidTestCase {
        updateBroadcastCounters(2);
        assertEquals(mVisibleAccountsChangedBroadcasts, 0); // broadcast was not sent
        assertEquals(mLoginAccountsChangedBroadcasts, 2);
        assertEquals(mAccountRemovedBroadcasts, 0);
    }

    @SmallTest
    public void testRegisterAccountListenerWithAddingTwoAccounts() throws Exception {
        unlockSystemUser();

        HashMap<String, Integer> visibility = new HashMap<>();
        visibility.put(AccountManagerServiceTestFixtures.CALLER_PACKAGE,
            AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);

        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage"); // opPackageName
        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
            AccountManagerServiceTestFixtures.CALLER_PACKAGE);
        mAms.addAccountExplicitlyWithVisibility(
            AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null, visibility);
        mAms.unregisterAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage"); // opPackageName
        mAms.addAccountExplicitly(
            AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p11", null);
            AccountManagerServiceTestFixtures.CALLER_PACKAGE);

        addAccountRemovedReceiver(AccountManagerServiceTestFixtures.CALLER_PACKAGE);
        mAms.addAccountExplicitlyWithVisibility(
            AccountManagerServiceTestFixtures.ACCOUNT_INTERVENE, "p11", null, visibility);

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

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

        updateBroadcastCounters(8);
@@ -2544,6 +2554,13 @@ public class AccountManagerServiceTest extends AndroidTestCase {
    @SmallTest
    public void testRegisterAccountListenerForThreePackages() throws Exception {
        unlockSystemUser();

        addAccountRemovedReceiver("testpackage1");
        HashMap<String, Integer> visibility = new HashMap<>();
        visibility.put("testpackage1", AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
        visibility.put("testpackage2", AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);
        visibility.put("testpackage3", AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);

        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage1"); // opPackageName
@@ -2553,7 +2570,8 @@ public class AccountManagerServiceTest extends AndroidTestCase {
        mAms.registerAccountListener(
            new String [] {AccountManagerServiceTestFixtures.ACCOUNT_TYPE_1},
            "testpackage3"); // opPackageName
        mAms.addAccountExplicitly(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null);
        mAms.addAccountExplicitlyWithVisibility(
            AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null, visibility);
        updateBroadcastCounters(4);
        assertEquals(mVisibleAccountsChangedBroadcasts, 3);
        assertEquals(mLoginAccountsChangedBroadcasts, 1);
@@ -2572,12 +2590,46 @@ public class AccountManagerServiceTest extends AndroidTestCase {
        mAms.addAccountExplicitly(
            AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS_TYPE_2, "p11", null);

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

    @SmallTest
    public void testRegisterAccountListenerForAddingAccountWithVisibility() throws Exception {
        unlockSystemUser();

        HashMap<String, Integer> visibility = new HashMap<>();
        visibility.put("testpackage1", AccountManager.VISIBILITY_NOT_VISIBLE);
        visibility.put("testpackage2", AccountManager.VISIBILITY_USER_MANAGED_NOT_VISIBLE);
        visibility.put("testpackage3", AccountManager.VISIBILITY_USER_MANAGED_VISIBLE);

        addAccountRemovedReceiver("testpackage1");
        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.addAccountExplicitlyWithVisibility(
            AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS, "p11", null, visibility);

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

        mAms.removeAccountInternal(AccountManagerServiceTestFixtures.ACCOUNT_SUCCESS);

        updateBroadcastCounters(4);
        assertEquals(mVisibleAccountsChangedBroadcasts, 2);
        assertEquals(mLoginAccountsChangedBroadcasts, 2);
        assertEquals(mAccountRemovedBroadcasts, 0); // account was never visible.
    }

    @SmallTest
    public void testRegisterAccountListenerCredentialsUpdate() throws Exception {
        unlockSystemUser();
@@ -2609,21 +2661,31 @@ public class AccountManagerServiceTest extends AndroidTestCase {
        mLoginAccountsChangedBroadcasts = 0;
        mAccountRemovedBroadcasts = 0;
        ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class);
        verify(mMockContext, times(expectedBroadcasts)).sendBroadcastAsUser(captor.capture(),
        verify(mMockContext, atLeast(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())) {
            } else if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(intent.getAction())) {
                mLoginAccountsChangedBroadcasts++;
            }
            if (AccountManager.ACTION_ACCOUNT_REMOVED.equals(intent.getAction())) {
            } else if (AccountManager.ACTION_ACCOUNT_REMOVED.equals(intent.getAction())) {
                mAccountRemovedBroadcasts++;
            }
        }
    }

    private void addAccountRemovedReceiver(String packageName) {
        ResolveInfo resolveInfo = new ResolveInfo();
        resolveInfo.activityInfo = new ActivityInfo();
        resolveInfo.activityInfo.applicationInfo =  new ApplicationInfo();
        resolveInfo.activityInfo.applicationInfo.packageName = packageName;

        List<ResolveInfo> accountRemovedReceivers = new ArrayList<>();
        accountRemovedReceivers.add(resolveInfo);
        when(mMockPackageManager.queryBroadcastReceiversAsUser(any(Intent.class), anyInt(),
            anyInt())).thenReturn(accountRemovedReceivers);
    }

    @SmallTest
    public void testConcurrencyReadWrite() throws Exception {
        // Test 2 threads calling getAccounts and 1 thread setAuthToken