Add work account settings.

Bug: 15467756
Change-Id: I13b5a0bb3967611d0d24b575bfc15d9bfaad4cfa
diff --git a/src/com/android/settings/SettingsActivity.java b/src/com/android/settings/SettingsActivity.java
index a5bc8bb..da7cb98 100644
--- a/src/com/android/settings/SettingsActivity.java
+++ b/src/com/android/settings/SettingsActivity.java
@@ -127,7 +127,8 @@
         PreferenceFragment.OnPreferenceStartFragmentCallback,
         ButtonBarHandler, OnAccountsUpdateListener, FragmentManager.OnBackStackChangedListener,
         SearchView.OnQueryTextListener, SearchView.OnCloseListener,
-        MenuItem.OnActionExpandListener {
+        MenuItem.OnActionExpandListener,
+        AuthenticatorHelper.OnAccountsUpdateListener {
 
     private static final String LOG_TAG = "Settings";
 
@@ -466,10 +467,13 @@
         if (intent.hasExtra(EXTRA_UI_OPTIONS)) {
             getWindow().setUiOptions(intent.getIntExtra(EXTRA_UI_OPTIONS, 0));
         }
-
-        mAuthenticatorHelper = new AuthenticatorHelper();
+        // TODO: Delete accounts tile once we have the new screen working
+        // See: http://b/15815948
+        final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
+        mAuthenticatorHelper = new AuthenticatorHelper(
+                this, UserHandle.getCallingUserHandle(), um, this);
         mAuthenticatorHelper.updateAuthDescriptions(this);
-        mAuthenticatorHelper.onAccountsUpdated(this, null);
+        mAuthenticatorHelper.onAccountsUpdated(null);
 
         mDevelopmentPreferences = getSharedPreferences(DevelopmentSettings.PREF_FILE,
                 Context.MODE_PRIVATE);
@@ -1263,7 +1267,11 @@
     public void onAccountsUpdated(Account[] accounts) {
         // TODO: watch for package upgrades to invalidate cache; see 7206643
         mAuthenticatorHelper.updateAuthDescriptions(this);
-        mAuthenticatorHelper.onAccountsUpdated(this, accounts);
+        invalidateCategories();
+    }
+
+    @Override
+    public void onAccountsUpdate(UserHandle userHandle) {
         invalidateCategories();
     }
 
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index 5f90442..26750e7 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -56,12 +56,14 @@
 import android.view.ViewGroup;
 import android.widget.ListView;
 import android.widget.TabWidget;
+
 import com.android.settings.dashboard.DashboardCategory;
 import com.android.settings.dashboard.DashboardTile;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.InetAddress;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
@@ -579,4 +581,31 @@
         intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_AS_SHORTCUT, isShortcut);
         return intent;
     }
+
+    /**
+     * Returns the managed profile of the current user or null if none found.
+     */
+    public static UserHandle getManagedProfile(UserManager userManager) {
+        List<UserHandle> userProfiles = userManager.getUserProfiles();
+        final int count = userProfiles.size();
+        for (int i = 0; i < count; i++) {
+            final UserHandle profile = userProfiles.get(i);
+            if (profile.getIdentifier() == userManager.getUserHandle()) {
+                continue;
+            }
+            final UserInfo userInfo = userManager.getUserInfo(profile.getIdentifier());
+            if (userInfo.isManagedProfile()) {
+                return profile;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns true if the current profile is a managed one.
+     */
+    public static boolean isManagedProfile(UserManager userManager) {
+        UserInfo currentUser = userManager.getUserInfo(userManager.getUserHandle());
+        return currentUser.isManagedProfile();
+    }
 }
diff --git a/src/com/android/settings/accounts/AccountPreferenceBase.java b/src/com/android/settings/accounts/AccountPreferenceBase.java
index 37dffca..c25831d 100644
--- a/src/com/android/settings/accounts/AccountPreferenceBase.java
+++ b/src/com/android/settings/accounts/AccountPreferenceBase.java
@@ -16,19 +16,9 @@
 
 package com.android.settings.accounts;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.android.settings.SettingsPreferenceFragment;
-
 import com.google.android.collect.Maps;
 
-import android.accounts.Account;
-import android.accounts.AccountManager;
 import android.accounts.AuthenticatorDescription;
-import android.accounts.OnAccountsUpdateListener;
 import android.app.Activity;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -40,28 +30,47 @@
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.preference.PreferenceScreen;
 import android.text.format.DateFormat;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
 
+import com.android.settings.SettingsPreferenceFragment;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+
 class AccountPreferenceBase extends SettingsPreferenceFragment
-        implements OnAccountsUpdateListener {
+        implements AuthenticatorHelper.OnAccountsUpdateListener {
 
     protected static final String TAG = "AccountSettings";
     public static final String AUTHORITIES_FILTER_KEY = "authorities";
     public static final String ACCOUNT_TYPES_FILTER_KEY = "account_types";
     private final Handler mHandler = new Handler();
+    private UserManager mUm;
     private Object mStatusChangeListenerHandle;
     private HashMap<String, ArrayList<String>> mAccountTypeToAuthorities = null;
-    private AuthenticatorHelper mAuthenticatorHelper = new AuthenticatorHelper();
+    protected AuthenticatorHelper mAuthenticatorHelper;
     private java.text.DateFormat mDateFormat;
     private java.text.DateFormat mTimeFormat;
 
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        // TODO: This needs to handle different users, get the user id from the intent
+        mUm = (UserManager) getSystemService(Context.USER_SERVICE);
+        mAuthenticatorHelper = new AuthenticatorHelper(
+                getActivity(), UserHandle.getCallingUserHandle(), mUm, this);
+    }
+
     /**
      * Overload to handle account updates.
      */
-    public void onAccountsUpdated(Account[] accounts) {
+    @Override
+    public void onAccountsUpdate(UserHandle userHandle) {
 
     }
 
diff --git a/src/com/android/settings/accounts/AccountSettings.java b/src/com/android/settings/accounts/AccountSettings.java
index bb06b2f..e60bed9 100644
--- a/src/com/android/settings/accounts/AccountSettings.java
+++ b/src/com/android/settings/accounts/AccountSettings.java
@@ -19,11 +19,17 @@
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
+import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.Log;
+import android.util.SparseArray;
 import android.preference.Preference;
 import android.preference.Preference.OnPreferenceClickListener;
 import android.preference.PreferenceGroup;
@@ -33,16 +39,17 @@
 import com.android.settings.Utils;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.List;
 
 /**
  * Settings screen for the account types on the device.
  * This shows all account types available for personal and work profiles.
  */
 public class AccountSettings extends SettingsPreferenceFragment
-        implements OnAccountsUpdateListener, OnPreferenceClickListener {
+        implements AuthenticatorHelper.OnAccountsUpdateListener,
+        OnPreferenceClickListener {
     public static final String TAG = "AccountSettings";
 
     private static final String KEY_ACCOUNT = "account";
@@ -51,128 +58,183 @@
     private static final String KEY_CATEGORY_PERSONAL = "account_personal";
     private static final String KEY_ADD_ACCOUNT_PERSONAL = "add_account_personal";
     private static final String KEY_CATEGORY_WORK = "account_work";
+    private static final String KEY_ADD_ACCOUNT_WORK = "add_account_work";
 
-    private AuthenticatorHelper mAuthenticatorHelper;
-    private boolean mListeningToAccountUpdates;
-
-    private PreferenceGroup mAccountTypesForUser;
-    private Preference mAddAccountForUser;
+    private static final String ADD_ACCOUNT_ACTION = "android.settings.ADD_ACCOUNT_SETTINGS";
 
     private UserManager mUm;
+    private SparseArray<ProfileData> mProfiles;
+    private ManagedProfileBroadcastReceiver mManagedProfileBroadcastReceiver;
+
+    /**
+     * Holds data related to the accounts belonging to one profile.
+     */
+    private static class ProfileData {
+        /**
+         * The preference that displays the accounts.
+         */
+        public PreferenceGroup preferenceGroup;
+        /**
+         * The preference that displays the add account button.
+         */
+        public Preference addAccountPreference;
+        /**
+         * The user handle of the user that these accounts belong to.
+         */
+        public UserHandle userHandle;
+        /**
+         * The {@link AuthenticatorHelper} that holds accounts data for this profile.
+         */
+        public AuthenticatorHelper authenticatorHelper;
+    }
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         mUm = (UserManager) getSystemService(Context.USER_SERVICE);
+        mProfiles = new SparseArray<ProfileData>(2);
+        updateUi();
+    }
 
-        mAuthenticatorHelper = new AuthenticatorHelper();
-        mAuthenticatorHelper.updateAuthDescriptions(getActivity());
-        mAuthenticatorHelper.onAccountsUpdated(getActivity(), null);
-
+    void updateUi() {
         // Load the preferences from an XML resource
         addPreferencesFromResource(R.xml.account_settings);
 
         if(mUm.isLinkedUser()) {
             // Restricted user or similar
-            // TODO: Do we disallow modifying accounts for restricted profiles?
-            mAccountTypesForUser = (PreferenceGroup) findPreference(KEY_ACCOUNT);
-            if (mUm.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
-                removePreference(KEY_ADD_ACCOUNT);
-            } else {
-                mAddAccountForUser = findPreference(KEY_ADD_ACCOUNT);
-                mAddAccountForUser.setOnPreferenceClickListener(this);
-            }
-            removePreference(KEY_CATEGORY_PERSONAL);
-            removePreference(KEY_CATEGORY_WORK);
+            updateSingleProfileUi();
         } else {
-            mAccountTypesForUser = (PreferenceGroup) findPreference(KEY_CATEGORY_PERSONAL);
-            mAddAccountForUser = findPreference(KEY_ADD_ACCOUNT_PERSONAL);
-            mAddAccountForUser.setOnPreferenceClickListener(this);
-
-            // TODO: Show the work accounts also
-            // TODO: Handle the case where there is only one account
-            removePreference(KEY_CATEGORY_WORK);
-            removePreference(KEY_ADD_ACCOUNT);
+            if (Utils.isManagedProfile(mUm)) {
+                // This should not happen
+                Log.w(TAG, "We should not be showing settings for a managed profile");
+                updateSingleProfileUi();
+            }
+            final UserHandle currentProfile = UserHandle.getCallingUserHandle();
+            final UserHandle managedProfile = Utils.getManagedProfile(mUm);
+            if (managedProfile == null) {
+                updateSingleProfileUi();
+            } else {
+                updateProfileUi(currentProfile,
+                        KEY_CATEGORY_PERSONAL, KEY_ADD_ACCOUNT_PERSONAL, new ArrayList<String>());
+                final ArrayList<String> unusedPreferences = new ArrayList<String>(1);
+                unusedPreferences.add(KEY_ADD_ACCOUNT);
+                updateProfileUi(managedProfile,
+                        KEY_CATEGORY_WORK, KEY_ADD_ACCOUNT_WORK, unusedPreferences);
+                mManagedProfileBroadcastReceiver = new ManagedProfileBroadcastReceiver();
+                mManagedProfileBroadcastReceiver.register(getActivity());
+            }
         }
-        updateAccountTypes(mAccountTypesForUser);
+        final int count = mProfiles.size();
+        for (int i = 0; i < count; i++) {
+            updateAccountTypes(mProfiles.valueAt(i));
+        }
+    }
+
+    private void updateSingleProfileUi() {
+        final ArrayList<String> unusedPreferences = new ArrayList<String>(2);
+        unusedPreferences.add(KEY_CATEGORY_PERSONAL);
+        unusedPreferences.add(KEY_CATEGORY_WORK);
+        updateProfileUi(UserHandle.getCallingUserHandle(), KEY_ACCOUNT, KEY_ADD_ACCOUNT,
+                unusedPreferences);
+    }
+
+    private void updateProfileUi(UserHandle userHandle, String categoryKey, String addAccountKey,
+            ArrayList<String> unusedPreferences) {
+        final int count = unusedPreferences.size();
+        for (int i = 0; i < count; i++) {
+            removePreference(unusedPreferences.get(i));
+        }
+        final ProfileData profileData = new ProfileData();
+        profileData.preferenceGroup = (PreferenceGroup) findPreference(categoryKey);
+        if (mUm.hasUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
+            removePreference(addAccountKey);
+        } else {
+            profileData.addAccountPreference = findPreference(addAccountKey);
+            profileData.addAccountPreference.setOnPreferenceClickListener(this);
+        }
+        profileData.userHandle = userHandle;
+        profileData.authenticatorHelper = new AuthenticatorHelper(
+                getActivity(), userHandle, mUm, this);
+        mProfiles.put(userHandle.getIdentifier(), profileData);
+
+        profileData.authenticatorHelper.listenToAccountUpdates();
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
-        stopListeningToAccountUpdates();
+        cleanUp();
+    }
+
+    void cleanUp() {
+        if (mManagedProfileBroadcastReceiver != null) {
+            mManagedProfileBroadcastReceiver.unregister(getActivity());
+        }
+        final int count = mProfiles.size();
+        for (int i = 0; i < count; i++) {
+            mProfiles.valueAt(i).authenticatorHelper.stopListeningToAccountUpdates();
+        }
     }
 
     @Override
-    public void onAccountsUpdated(Account[] accounts) {
-        // TODO: watch for package upgrades to invalidate cache; see 7206643
-        mAuthenticatorHelper.updateAuthDescriptions(getActivity());
-        mAuthenticatorHelper.onAccountsUpdated(getActivity(), accounts);
-        listenToAccountUpdates();
-        updateAccountTypes(mAccountTypesForUser);
-    }
-
-    @Override
-    public boolean onPreferenceClick(Preference preference) {
-        // Check the preference
-        if (preference == mAddAccountForUser) {
-            Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
-            startActivity(intent);
-            return true;
-        }
-        return false;
-    }
-
-    private void updateAccountTypes(PreferenceGroup preferenceGroup) {
-        preferenceGroup.removeAll();
-        preferenceGroup.setOrderingAsAdded(true);
-        for (AccountPreference preference : getAccountTypePreferences()) {
-            preferenceGroup.addPreference(preference);
-        }
-        if (mAddAccountForUser != null) {
-            preferenceGroup.addPreference(mAddAccountForUser);
+    public void onAccountsUpdate(UserHandle userHandle) {
+        final ProfileData profileData = mProfiles.get(userHandle.getIdentifier());
+        if (profileData != null) {
+            updateAccountTypes(profileData);
+        } else {
+            Log.w(TAG, "Missing Settings screen for: " + userHandle.getIdentifier());
         }
     }
 
-    private List<AccountPreference> getAccountTypePreferences() {
-        String[] accountTypes = mAuthenticatorHelper.getEnabledAccountTypes();
-        List<AccountPreference> accountTypePreferences =
+    private void updateAccountTypes(ProfileData profileData) {
+        profileData.preferenceGroup.removeAll();
+        final ArrayList<AccountPreference> preferences = getAccountTypePreferences(
+                profileData.authenticatorHelper);
+        final int count = preferences.size();
+        for (int i = 0; i < count; i++) {
+            profileData.preferenceGroup.addPreference(preferences.get(i));
+        }
+        if (profileData.addAccountPreference != null) {
+            profileData.preferenceGroup.addPreference(profileData.addAccountPreference);
+        }
+    }
+
+    private ArrayList<AccountPreference> getAccountTypePreferences(AuthenticatorHelper helper) {
+        final String[] accountTypes = helper.getEnabledAccountTypes();
+        final ArrayList<AccountPreference> accountTypePreferences =
                 new ArrayList<AccountPreference>(accountTypes.length);
-        for (String accountType : accountTypes) {
-            CharSequence label = mAuthenticatorHelper.getLabelForType(getActivity(), accountType);
+
+        for (int i = 0; i < accountTypes.length; i++) {
+            final String accountType = accountTypes[i];
+            final CharSequence label = helper.getLabelForType(getActivity(), accountType);
             if (label == null) {
                 continue;
             }
 
-            Account[] accounts = AccountManager.get(getActivity()).getAccountsByType(accountType);
-            boolean skipToAccount = accounts.length == 1
-                    && !mAuthenticatorHelper.hasAccountPreferences(accountType);
+            final Account[] accounts = AccountManager.get(getActivity())
+                    .getAccountsByType(accountType);
+            final boolean skipToAccount = accounts.length == 1
+                    && !helper.hasAccountPreferences(accountType);
 
             if (skipToAccount) {
-                Bundle fragmentArguments = new Bundle();
+                final Bundle fragmentArguments = new Bundle();
                 fragmentArguments.putParcelable(AccountSyncSettings.ACCOUNT_KEY,
                         accounts[0]);
 
-                accountTypePreferences.add(new AccountPreference(
-                        getActivity(),
-                        label,
-                        accountType,
-                        AccountSyncSettings.class.getName(),
-                        fragmentArguments));
+                accountTypePreferences.add(new AccountPreference(getActivity(), label,
+                        AccountSyncSettings.class.getName(), fragmentArguments,
+                        helper.getDrawableForType(getActivity(), accountType)));
             } else {
-                Bundle fragmentArguments = new Bundle();
+                final Bundle fragmentArguments = new Bundle();
                 fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_TYPE, accountType);
                 fragmentArguments.putString(ManageAccountsSettings.KEY_ACCOUNT_LABEL,
                         label.toString());
 
-                accountTypePreferences.add(new AccountPreference(
-                        getActivity(),
-                        label,
-                        accountType,
-                        ManageAccountsSettings.class.getName(),
-                        fragmentArguments));
+                accountTypePreferences.add(new AccountPreference(getActivity(), label,
+                        ManageAccountsSettings.class.getName(), fragmentArguments,
+                        helper.getDrawableForType(getActivity(), accountType)));
             }
-            mAuthenticatorHelper.preloadDrawableForType(getActivity(), accountType);
+            helper.preloadDrawableForType(getActivity(), accountType);
         }
         // Sort by label
         Collections.sort(accountTypePreferences, new Comparator<AccountPreference>() {
@@ -184,18 +246,20 @@
         return accountTypePreferences;
     }
 
-    private void listenToAccountUpdates() {
-        if (!mListeningToAccountUpdates) {
-            AccountManager.get(getActivity()).addOnAccountsUpdatedListener(this, null, true);
-            mListeningToAccountUpdates = true;
+    @Override
+    public boolean onPreferenceClick(Preference preference) {
+        // Check the preference
+        final int count = mProfiles.size();
+        for (int i = 0; i < count; i++) {
+            ProfileData profileData = mProfiles.valueAt(i);
+            if (preference == profileData.addAccountPreference) {
+                Intent intent = new Intent(ADD_ACCOUNT_ACTION);
+                intent.putExtra(Intent.EXTRA_USER_HANDLE, profileData.userHandle);
+                startActivity(intent);
+                return true;
+            }
         }
-    }
-
-    private void stopListeningToAccountUpdates() {
-        if (mListeningToAccountUpdates) {
-            AccountManager.get(getActivity()).removeOnAccountsUpdatedListener(this);
-            mListeningToAccountUpdates = false;
-        }
+        return false;
     }
 
     private class AccountPreference extends Preference implements OnPreferenceClickListener {
@@ -218,18 +282,16 @@
          */
         private final Bundle mFragmentArguments;
 
-
-        public AccountPreference(Context context, CharSequence title,
-                String accountType, String fragment, Bundle fragmentArguments) {
+        public AccountPreference(Context context, CharSequence title, String fragment,
+                Bundle fragmentArguments, Drawable icon) {
             super(context);
             mTitle = title;
             mFragment = fragment;
             mFragmentArguments = fragmentArguments;
             setWidgetLayoutResource(R.layout.account_type_preference);
 
-            Drawable drawable = mAuthenticatorHelper.getDrawableForType(context, accountType);
             setTitle(title);
-            setIcon(drawable);
+            setIcon(icon);
 
             setOnPreferenceClickListener(this);
         }
@@ -244,6 +306,39 @@
             return false;
         }
     }
+
+    private class ManagedProfileBroadcastReceiver extends BroadcastReceiver {
+        private boolean listeningToManagedProfileEvents;
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(Intent.ACTION_MANAGED_PROFILE_REMOVED)
+                    || intent.getAction().equals(Intent.ACTION_MANAGED_PROFILE_ADDED)) {
+                Log.v(TAG, "Received broadcast: " + intent.getAction());
+                cleanUp();
+                updateUi();
+                return;
+            }
+            Log.w(TAG, "Cannot handle received broadcast: " + intent.getAction());
+        }
+
+        public void register(Context context) {
+            if (!listeningToManagedProfileEvents) {
+                IntentFilter intentFilter = new IntentFilter();
+                intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
+                intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
+                context.registerReceiver(this, intentFilter);
+                listeningToManagedProfileEvents = true;
+            }
+        }
+
+        public void unregister(Context context) {
+            if (listeningToManagedProfileEvents) {
+                context.unregisterReceiver(this);
+                listeningToManagedProfileEvents = false;
+            }
+        }
+    }
     // TODO Implement a {@link SearchIndexProvider} to allow Indexing and Search of account types
     // See http://b/15403806
 }
diff --git a/src/com/android/settings/accounts/AccountSyncSettings.java b/src/com/android/settings/accounts/AccountSyncSettings.java
index c14dbbe..e33e37a 100644
--- a/src/com/android/settings/accounts/AccountSyncSettings.java
+++ b/src/com/android/settings/accounts/AccountSyncSettings.java
@@ -16,13 +16,15 @@
 
 package com.android.settings.accounts;
 
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerCallback;
 import android.accounts.AccountManagerFuture;
 import android.accounts.AuthenticatorException;
 import android.accounts.OperationCanceledException;
-import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.ContentResolver;
@@ -34,6 +36,7 @@
 import android.content.pm.ProviderInfo;
 import android.net.ConnectivityManager;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.os.UserManager;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
@@ -51,8 +54,6 @@
 
 import com.android.settings.R;
 import com.android.settings.Utils;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -193,10 +194,9 @@
 
     @Override
     public void onResume() {
-        final Activity activity = getActivity();
-        AccountManager.get(activity).addOnAccountsUpdatedListener(this, null, false);
+        mAuthenticatorHelper.listenToAccountUpdates();
         updateAuthDescriptions();
-        onAccountsUpdated(AccountManager.get(activity).getAccounts());
+        onAccountsUpdate(UserHandle.getCallingUserHandle());
 
         super.onResume();
     }
@@ -204,7 +204,7 @@
     @Override
     public void onPause() {
         super.onPause();
-        AccountManager.get(getActivity()).removeOnAccountsUpdatedListener(this);
+        mAuthenticatorHelper.stopListeningToAccountUpdates();
     }
 
     private void addSyncStateCheckBox(Account account, String authority) {
@@ -436,10 +436,10 @@
     }
 
     @Override
-    public void onAccountsUpdated(Account[] accounts) {
-        super.onAccountsUpdated(accounts);
-        mAccounts = accounts;
-        updateAccountCheckboxes(accounts);
+    public void onAccountsUpdate(final UserHandle userHandle) {
+        super.onAccountsUpdate(userHandle);
+        mAccounts = AccountManager.get(getActivity()).getAccounts();
+        updateAccountCheckboxes(mAccounts);
         onSyncStateUpdated();
     }
 
diff --git a/src/com/android/settings/accounts/AuthenticatorHelper.java b/src/com/android/settings/accounts/AuthenticatorHelper.java
index a164b8b..0ecf438 100644
--- a/src/com/android/settings/accounts/AuthenticatorHelper.java
+++ b/src/com/android/settings/accounts/AuthenticatorHelper.java
@@ -19,27 +19,58 @@
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AuthenticatorDescription;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Map;
 
-public class AuthenticatorHelper {
-
+/**
+ * Helper class for monitoring accounts on the device for a given user.
+ *
+ * Classes using this helper should implement {@link OnAccountsUpdateListener}.
+ * {@link OnAccountsUpdateListener#onAccountsUpdate(UserHandle)} will then be
+ * called once accounts get updated. For setting up listening for account
+ * updates, {@link #listenToAccountUpdates()} and
+ * {@link #stopListeningToAccountUpdates()} should be used.
+ */
+final public class AuthenticatorHelper extends BroadcastReceiver {
     private static final String TAG = "AuthenticatorHelper";
+
     private Map<String, AuthenticatorDescription> mTypeToAuthDescription
             = new HashMap<String, AuthenticatorDescription>();
     private AuthenticatorDescription[] mAuthDescs;
     private ArrayList<String> mEnabledAccountTypes = new ArrayList<String>();
     private Map<String, Drawable> mAccTypeIconCache = new HashMap<String, Drawable>();
 
-    public AuthenticatorHelper() {
+    private final UserHandle mUserHandle;
+    private final UserManager mUm;
+    private final Context mContext;
+    private final OnAccountsUpdateListener mListener;
+    private boolean mListeningToAccountUpdates;
+
+    public interface OnAccountsUpdateListener {
+        void onAccountsUpdate(UserHandle userHandle);
+    }
+
+    public AuthenticatorHelper(Context context, UserHandle userHandle, UserManager userManager,
+            OnAccountsUpdateListener listener) {
+        mContext = context;
+        mUm = userManager;
+        mUserHandle = userHandle;
+        mListener = listener;
+        // This guarantees that the helper is ready to use once constructed
+        onAccountsUpdated(null);
     }
 
     public String[] getEnabledAccountTypes() {
@@ -72,7 +103,8 @@
             try {
                 AuthenticatorDescription desc = mTypeToAuthDescription.get(accountType);
                 Context authContext = context.createPackageContext(desc.packageName, 0);
-                icon = authContext.getResources().getDrawable(desc.iconId);
+                icon = mUm.getBadgedDrawableForUser(
+                        authContext.getResources().getDrawable(desc.iconId), mUserHandle);
                 synchronized (mAccTypeIconCache) {
                     mAccTypeIconCache.put(accountType, icon);
                 }
@@ -112,25 +144,13 @@
      * and update any UI that depends on AuthenticatorDescriptions in onAuthDescriptionsUpdated().
      */
     public void updateAuthDescriptions(Context context) {
-        mAuthDescs = AccountManager.get(context).getAuthenticatorTypes();
+        mAuthDescs = AccountManager.get(context)
+                .getAuthenticatorTypesAsUser(mUserHandle.getIdentifier());
         for (int i = 0; i < mAuthDescs.length; i++) {
             mTypeToAuthDescription.put(mAuthDescs[i].type, mAuthDescs[i]);
         }
     }
 
-    public void onAccountsUpdated(Context context, Account[] accounts) {
-        if (accounts == null) {
-            accounts = AccountManager.get(context).getAccounts();
-        }
-        mEnabledAccountTypes.clear();
-        mAccTypeIconCache.clear();
-        for (Account account: accounts) {
-            if (!mEnabledAccountTypes.contains(account.type)) {
-                mEnabledAccountTypes.add(account.type);
-            }
-        }
-    }
-
     public boolean containsAccountType(String accountType) {
         return mTypeToAuthDescription.containsKey(accountType);
     }
@@ -148,4 +168,50 @@
         }
         return false;
     }
+
+    public void onAccountsUpdated(Account[] accounts) {
+        // TODO: Revert to non-public once no longer needed in SettingsActivity
+        // See http://b/15819268
+        updateAuthDescriptions(mContext);
+        if (accounts == null) {
+            accounts = AccountManager.get(mContext).getAccounts();
+        }
+        mEnabledAccountTypes.clear();
+        mAccTypeIconCache.clear();
+        for (int i = 0; i < accounts.length; i++) {
+            final Account account = accounts[i];
+            if (!mEnabledAccountTypes.contains(account.type)) {
+                mEnabledAccountTypes.add(account.type);
+            }
+        }
+        if (mListeningToAccountUpdates) {
+            mListener.onAccountsUpdate(mUserHandle);
+        }
+    }
+
+    @Override
+    public void onReceive(final Context context, final Intent intent) {
+        final Account[] accounts = AccountManager.get(mContext)
+                .getAccountsAsUser(mUserHandle.getIdentifier());
+        onAccountsUpdated(accounts);
+    }
+
+    public void listenToAccountUpdates() {
+        if (!mListeningToAccountUpdates) {
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION);
+            // At disk full, certain actions are blocked (such as writing the accounts to storage).
+            // It is useful to also listen for recovery from disk full to avoid bugs.
+            intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
+            mContext.registerReceiverAsUser(this, mUserHandle, intentFilter, null, null);
+            mListeningToAccountUpdates = true;
+        }
+    }
+
+    public void stopListeningToAccountUpdates() {
+        if (mListeningToAccountUpdates) {
+            mContext.unregisterReceiver(this);
+            mListeningToAccountUpdates = false;
+        }
+    }
 }