diff options
| author | 2017-04-18 13:43:31 -0700 | |
|---|---|---|
| committer | 2017-04-24 23:57:30 +0000 | |
| commit | b6a7dc033ceb11df2d4e95dc1bb272362332e557 (patch) | |
| tree | 72796c2fb2ce0c4a834d291083d59b47e812713d | |
| parent | 9e55fcbdc254da098bb792f460296ff0649c3b00 (diff) | |
Update PACKAGE_REMOVED broadcast.
1) Include KEY_ACCOUNT_NAME and KEY_ACCOUNT_TYPE.
2) Only send the broadcast to packages which were able to see the
account.
Test: manual, APCT.
Bug:37280078
(cherry picked from commit cbbc99f76224cedb6d680c3cd7f0afc9cc912202)
Change-Id: I3c323e545628199903313096f93654687fa8f22b
3 files changed, 173 insertions, 31 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index b320d5d83cc7..06b09c041f1f 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -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) diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index adb55b91c0ae..018e41b7bef1 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -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); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 20839c5f7a18..36e9b3f8a9e4 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -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,13 +2590,47 @@ 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(); mAms.registerAccountListener( @@ -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 |