Fix a race condition in SyncManager.onAccountsUpdated()

Get the full list of accounts from AccountManagerService in one shot
instead of getting them per user.

Bug: 6263091

Change-Id: I488f24749a96281ef1e2a620820399c97f471024
diff --git a/core/java/android/accounts/AccountAndUser.java b/core/java/android/accounts/AccountAndUser.java
new file mode 100644
index 0000000..04157cc
--- /dev/null
+++ b/core/java/android/accounts/AccountAndUser.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.accounts;
+
+/**
+ * Used to store the Account and the UserId this account is associated with.
+ *
+ * @hide
+ */
+public class AccountAndUser {
+    public Account account;
+    public int userId;
+
+    public AccountAndUser(Account account, int userId) {
+        this.account = account;
+        this.userId = userId;
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof AccountAndUser)) return false;
+        final AccountAndUser other = (AccountAndUser) o;
+        return this.account.equals(other.account)
+                && this.userId == other.userId;
+    }
+
+    @Override
+    public int hashCode() {
+        return account.hashCode() + userId;
+    }
+
+    public String toString() {
+        return account.toString() + " u" + userId;
+    }
+}
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index 197c1bd..241fecb 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -1488,6 +1488,31 @@
         }
     }
 
+    /**
+     * Returns all the accounts qualified by user.
+     * @hide
+     */
+    public AccountAndUser[] getAllAccounts() {
+        ArrayList<AccountAndUser> allAccounts = new ArrayList<AccountAndUser>();
+        List<UserInfo> users = getAllUsers();
+        if (users == null)  return new AccountAndUser[0];
+
+        synchronized(mUsers) {
+            for (UserInfo user : users) {
+                UserAccounts userAccounts = getUserAccounts(user.id);
+                if (userAccounts == null) continue;
+                synchronized (userAccounts.cacheLock) {
+                    Account[] accounts = getAccountsFromCacheLocked(userAccounts, null);
+                    for (int a = 0; a < accounts.length; a++) {
+                        allAccounts.add(new AccountAndUser(accounts[a], user.id));
+                    }
+                }
+            }
+        }
+        AccountAndUser[] accountsArray = new AccountAndUser[allAccounts.size()];
+        return allAccounts.toArray(accountsArray);
+    }
+
     public Account[] getAccounts(String type) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "getAccounts: accountType " + type
diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java
index 06dfe90..6219de7 100644
--- a/core/java/android/content/SyncManager.java
+++ b/core/java/android/content/SyncManager.java
@@ -22,6 +22,7 @@
 import com.google.android.collect.Maps;
 
 import android.accounts.Account;
+import android.accounts.AccountAndUser;
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerService;
 import android.accounts.OnAccountsUpdateListener;
@@ -237,22 +238,14 @@
 
         int count = 0;
 
-        // For all known users on the system, get their accounts and add them to the list
+        // Get accounts from AccountManager for all the users on the system
         // TODO: Limit this to active users, when such a concept exists.
+        AccountAndUser[] allAccounts = AccountManagerService.getSingleton().getAllAccounts();
         for (UserInfo user : users) {
-            accounts = AccountManagerService.getSingleton().getAccounts(user.id);
-            count += accounts.length;
-        }
-
-        AccountAndUser[] allAccounts = new AccountAndUser[count];
-        int index = 0;
-        for (UserInfo user : users) {
-            accounts = AccountManagerService.getSingleton().getAccounts(user.id);
-            for (Account account : accounts) {
-                allAccounts[index++] = new AccountAndUser(account, user.id);
-            }
             if (mBootCompleted) {
-                mSyncStorageEngine.doDatabaseCleanup(accounts, user.id);
+                Account[] accountsForUser =
+                        AccountManagerService.getSingleton().getAccounts(user.id);
+                mSyncStorageEngine.doDatabaseCleanup(accountsForUser, user.id);
             }
         }
 
@@ -338,33 +331,6 @@
 
     private volatile boolean mBootCompleted = false;
 
-    static class AccountAndUser {
-        Account account;
-        int userId;
-
-        AccountAndUser(Account account, int userId) {
-            this.account = account;
-            this.userId = userId;
-        }
-
-        public boolean equals(Object o) {
-            if (this == o) return true;
-            if (!(o instanceof AccountAndUser)) return false;
-            final AccountAndUser other = (AccountAndUser) o;
-            return this.account.equals(other.account)
-                    && this.userId == other.userId;
-        }
-
-        @Override
-        public int hashCode() {
-            return account.hashCode() + userId;
-        }
-
-        public String toString() {
-            return account.toString() + " u" + userId;
-        }
-    }
-
     private ConnectivityManager getConnectivityManager() {
         synchronized (this) {
             if (mConnManagerDoNotUseDirectly == null) {
diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java
index 9c81c9e..d3baf70 100644
--- a/core/java/android/content/SyncStorageEngine.java
+++ b/core/java/android/content/SyncStorageEngine.java
@@ -25,7 +25,7 @@
 import org.xmlpull.v1.XmlSerializer;
 
 import android.accounts.Account;
-import android.content.SyncManager.AccountAndUser;
+import android.accounts.AccountAndUser;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteException;