summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dmitry Dementyev <dementyev@google.com> 2017-04-18 13:43:31 -0700
committer Dmitry Dementyev <dementyev@google.com> 2017-04-24 23:57:30 +0000
commitb6a7dc033ceb11df2d4e95dc1bb272362332e557 (patch)
tree72796c2fb2ce0c4a834d291083d59b47e812713d
parent9e55fcbdc254da098bb792f460296ff0649c3b00 (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
-rw-r--r--core/java/android/accounts/AccountManager.java8
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java104
-rw-r--r--services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java92
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