Loading core/java/android/accounts/AccountManager.java +7 −1 Original line number Diff line number Diff line Loading @@ -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) Loading services/core/java/com/android/server/accounts/AccountManagerService.java +89 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading @@ -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)) { Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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(); Loading Loading @@ -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)); } Loading Loading @@ -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); Loading @@ -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) { Loading Loading @@ -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. Loading Loading @@ -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)) { Loading Loading @@ -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; Loading Loading @@ -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. Loading @@ -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); Loading services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +77 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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 Loading @@ -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); Loading @@ -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(); Loading Loading @@ -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 Loading Loading
core/java/android/accounts/AccountManager.java +7 −1 Original line number Diff line number Diff line Loading @@ -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) Loading
services/core/java/com/android/server/accounts/AccountManagerService.java +89 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading @@ -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)) { Loading @@ -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; Loading Loading @@ -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); } } Loading Loading @@ -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(); Loading Loading @@ -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)); } Loading Loading @@ -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); Loading @@ -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) { Loading Loading @@ -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. Loading Loading @@ -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)) { Loading Loading @@ -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; Loading Loading @@ -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. Loading @@ -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); Loading
services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +77 −15 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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); Loading Loading @@ -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); Loading @@ -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 Loading @@ -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); Loading @@ -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(); Loading Loading @@ -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 Loading