• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.accounts;
18 
19 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_CLONE;
20 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_PERSONAL;
21 import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_WORK;
22 import static android.app.admin.DevicePolicyResources.Strings.Settings.CLONE_CATEGORY_HEADER;
23 import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_BY;
24 import static android.app.admin.DevicePolicyResources.Strings.Settings.MANAGED_PROFILE_SETTINGS_TITLE;
25 import static android.app.admin.DevicePolicyResources.Strings.Settings.PERSONAL_CATEGORY_HEADER;
26 import static android.app.admin.DevicePolicyResources.Strings.Settings.REMOVE_WORK_PROFILE;
27 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_CATEGORY_HEADER;
28 import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_NOT_AVAILABLE;
29 import static android.content.Intent.EXTRA_USER;
30 import static android.os.UserManager.DISALLOW_MODIFY_ACCOUNTS;
31 import static android.os.UserManager.DISALLOW_REMOVE_MANAGED_PROFILE;
32 import static android.provider.Settings.ACTION_ADD_ACCOUNT;
33 import static android.provider.Settings.EXTRA_AUTHORITIES;
34 
35 import android.accounts.Account;
36 import android.accounts.AccountManager;
37 import android.app.admin.DevicePolicyManager;
38 import android.content.BroadcastReceiver;
39 import android.content.Context;
40 import android.content.Intent;
41 import android.content.IntentFilter;
42 import android.content.pm.ApplicationInfo;
43 import android.content.pm.PackageManager;
44 import android.content.pm.UserInfo;
45 import android.content.res.Resources;
46 import android.graphics.drawable.Drawable;
47 import android.os.Bundle;
48 import android.os.UserHandle;
49 import android.os.UserManager;
50 import android.text.BidiFormatter;
51 import android.util.ArrayMap;
52 import android.util.Log;
53 import android.util.SparseArray;
54 
55 import androidx.annotation.VisibleForTesting;
56 import androidx.preference.Preference;
57 import androidx.preference.Preference.OnPreferenceClickListener;
58 import androidx.preference.PreferenceGroup;
59 import androidx.preference.PreferenceScreen;
60 
61 import com.android.settings.AccessiblePreferenceCategory;
62 import com.android.settings.R;
63 import com.android.settings.Utils;
64 import com.android.settings.core.PreferenceControllerMixin;
65 import com.android.settings.core.SubSettingLauncher;
66 import com.android.settings.dashboard.DashboardFragment;
67 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
68 import com.android.settings.overlay.FeatureFactory;
69 import com.android.settingslib.RestrictedPreference;
70 import com.android.settingslib.accounts.AuthenticatorHelper;
71 import com.android.settingslib.core.AbstractPreferenceController;
72 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
73 import com.android.settingslib.core.lifecycle.LifecycleObserver;
74 import com.android.settingslib.core.lifecycle.events.OnPause;
75 import com.android.settingslib.core.lifecycle.events.OnResume;
76 import com.android.settingslib.search.SearchIndexableRaw;
77 
78 import java.util.ArrayList;
79 import java.util.Collections;
80 import java.util.Comparator;
81 import java.util.List;
82 
83 public class AccountPreferenceController extends AbstractPreferenceController
84         implements PreferenceControllerMixin, AuthenticatorHelper.OnAccountsUpdateListener,
85         OnPreferenceClickListener, LifecycleObserver, OnPause, OnResume {
86 
87     private static final String TAG = "AccountPrefController";
88 
89     private static final int ORDER_ACCOUNT_PROFILES = 101;
90     private static final int ORDER_LAST = 1002;
91     private static final int ORDER_NEXT_TO_LAST = 1001;
92     private static final int ORDER_NEXT_TO_NEXT_TO_LAST = 1000;
93 
94     private static final String PREF_KEY_ADD_ACCOUNT = "add_account";
95     private static final String PREF_KEY_REMOVE_PROFILE = "remove_profile";
96     private static final String PREF_KEY_WORK_PROFILE_SETTING = "work_profile_setting";
97 
98     private UserManager mUm;
99     private DevicePolicyManager mDpm;
100     private SparseArray<ProfileData> mProfiles = new SparseArray<ProfileData>();
101     private ManagedProfileBroadcastReceiver mManagedProfileBroadcastReceiver =
102             new ManagedProfileBroadcastReceiver();
103     private Preference mProfileNotAvailablePreference;
104     private String[] mAuthorities;
105     private int mAuthoritiesCount = 0;
106     private DashboardFragment mFragment;
107     private int mAccountProfileOrder = ORDER_ACCOUNT_PROFILES;
108     private AccountRestrictionHelper mHelper;
109     private MetricsFeatureProvider mMetricsFeatureProvider;
110     private @ProfileSelectFragment.ProfileType int mType;
111 
112     /**
113      * Holds data related to the accounts belonging to one profile.
114      */
115     public static class ProfileData {
116         /**
117          * The preference that displays the accounts.
118          */
119         public PreferenceGroup preferenceGroup;
120         /**
121          * The preference that displays the add account button.
122          */
123         public RestrictedPreference addAccountPreference;
124         /**
125          * The preference that displays the button to remove the managed profile
126          */
127         public RestrictedPreference removeWorkProfilePreference;
128         /**
129          * The preference that displays managed profile settings.
130          */
131         public Preference managedProfilePreference;
132         /**
133          * The {@link AuthenticatorHelper} that holds accounts data for this profile.
134          */
135         public AuthenticatorHelper authenticatorHelper;
136         /**
137          * The {@link UserInfo} of the profile.
138          */
139         public UserInfo userInfo;
140         /**
141          * The {@link UserInfo} of the profile.
142          */
143         public boolean pendingRemoval;
144         /**
145          * The map from account key to account preference
146          */
147         public ArrayMap<String, AccountTypePreference> accountPreferences = new ArrayMap<>();
148     }
149 
AccountPreferenceController(Context context, DashboardFragment parent, String[] authorities, @ProfileSelectFragment.ProfileType int type)150     public AccountPreferenceController(Context context, DashboardFragment parent,
151             String[] authorities, @ProfileSelectFragment.ProfileType int type) {
152         this(context, parent, authorities, new AccountRestrictionHelper(context), type);
153     }
154 
155     @VisibleForTesting
AccountPreferenceController(Context context, DashboardFragment parent, String[] authorities, AccountRestrictionHelper helper, @ProfileSelectFragment.ProfileType int type)156     AccountPreferenceController(Context context, DashboardFragment parent,
157             String[] authorities, AccountRestrictionHelper helper,
158             @ProfileSelectFragment.ProfileType int type) {
159         super(context);
160         mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
161         mDpm = context.getSystemService(DevicePolicyManager.class);
162         mAuthorities = authorities;
163         mFragment = parent;
164         if (mAuthorities != null) {
165             mAuthoritiesCount = mAuthorities.length;
166         }
167         final FeatureFactory featureFactory = FeatureFactory.getFactory(mContext);
168         mMetricsFeatureProvider = featureFactory.getMetricsFeatureProvider();
169         mHelper = helper;
170         mType = type;
171     }
172 
173     @Override
isAvailable()174     public boolean isAvailable() {
175         return !mUm.isManagedProfile();
176     }
177 
178     @Override
getPreferenceKey()179     public String getPreferenceKey() {
180         return null;
181     }
182 
183     @Override
displayPreference(PreferenceScreen screen)184     public void displayPreference(PreferenceScreen screen) {
185         super.displayPreference(screen);
186         updateUi();
187     }
188 
189     @Override
updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData)190     public void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) {
191         if (!isAvailable()) {
192             return;
193         }
194         final Resources res = mContext.getResources();
195         final String screenTitle = res.getString(R.string.account_settings_title);
196 
197         List<UserInfo> profiles = mUm.getProfiles(UserHandle.myUserId());
198         for (final UserInfo userInfo : profiles) {
199             if (userInfo.isEnabled() && userInfo.isManagedProfile()) {
200                 if (!mHelper.hasBaseUserRestriction(DISALLOW_REMOVE_MANAGED_PROFILE,
201                         UserHandle.myUserId())) {
202                     final SearchIndexableRaw data = new SearchIndexableRaw(mContext);
203                     data.key = PREF_KEY_REMOVE_PROFILE;
204                     data.title = mDpm.getResources().getString(
205                             REMOVE_WORK_PROFILE,
206                             () -> res.getString(R.string.remove_managed_profile_label));
207                     data.screenTitle = screenTitle;
208                     rawData.add(data);
209                 }
210                 final SearchIndexableRaw data = new SearchIndexableRaw(mContext);
211                 data.key = PREF_KEY_WORK_PROFILE_SETTING;
212                 data.title = mDpm.getResources().getString(MANAGED_PROFILE_SETTINGS_TITLE,
213                         () -> res.getString(R.string.managed_profile_settings_title));
214                 data.screenTitle = screenTitle;
215                 rawData.add(data);
216             }
217         }
218     }
219 
220     @Override
onResume()221     public void onResume() {
222         updateUi();
223         mManagedProfileBroadcastReceiver.register(mContext);
224         listenToAccountUpdates();
225     }
226 
227     @Override
onPause()228     public void onPause() {
229         stopListeningToAccountUpdates();
230         mManagedProfileBroadcastReceiver.unregister(mContext);
231     }
232 
233     @Override
onAccountsUpdate(UserHandle userHandle)234     public void onAccountsUpdate(UserHandle userHandle) {
235         final ProfileData profileData = mProfiles.get(userHandle.getIdentifier());
236         if (profileData != null) {
237             updateAccountTypes(profileData);
238         } else {
239             Log.w(TAG, "Missing Settings screen for: " + userHandle.getIdentifier());
240         }
241     }
242 
243     @Override
onPreferenceClick(Preference preference)244     public boolean onPreferenceClick(Preference preference) {
245         final int metricsCategory = mFragment.getMetricsCategory();
246         // Check the preference
247         final int count = mProfiles.size();
248         for (int i = 0; i < count; i++) {
249             ProfileData profileData = mProfiles.valueAt(i);
250             if (preference == profileData.addAccountPreference) {
251                 mMetricsFeatureProvider.logClickedPreference(preference, metricsCategory);
252                 Intent intent = new Intent(ACTION_ADD_ACCOUNT);
253                 intent.putExtra(EXTRA_USER, profileData.userInfo.getUserHandle());
254                 intent.putExtra(EXTRA_AUTHORITIES, mAuthorities);
255                 mContext.startActivity(intent);
256                 return true;
257             }
258             if (preference == profileData.removeWorkProfilePreference) {
259                 mMetricsFeatureProvider.logClickedPreference(preference, metricsCategory);
260                 final int userId = profileData.userInfo.id;
261                 RemoveUserFragment.newInstance(userId).show(mFragment.getFragmentManager(),
262                         "removeUser");
263                 return true;
264             }
265             if (preference == profileData.managedProfilePreference) {
266                 mMetricsFeatureProvider.logClickedPreference(preference, metricsCategory);
267                 Bundle arguments = new Bundle();
268                 arguments.putParcelable(Intent.EXTRA_USER, profileData.userInfo.getUserHandle());
269                 new SubSettingLauncher(mContext)
270                         .setSourceMetricsCategory(metricsCategory)
271                         .setDestination(ManagedProfileSettings.class.getName())
272                         .setTitleText(mDpm.getResources().getString(MANAGED_PROFILE_SETTINGS_TITLE,
273                                 () -> mContext.getString(R.string.managed_profile_settings_title)))
274                         .setArguments(arguments)
275                         .launch();
276 
277                 return true;
278             }
279         }
280         return false;
281     }
282 
updateUi()283     private void updateUi() {
284         if (!isAvailable()) {
285             // This should not happen
286             Log.e(TAG, "We should not be showing settings for a managed profile");
287             return;
288         }
289 
290         for (int i = 0, size = mProfiles.size(); i < size; i++) {
291             mProfiles.valueAt(i).pendingRemoval = true;
292         }
293         if (mUm.isRestrictedProfile()) {
294             // Restricted user or similar
295             UserInfo userInfo = mUm.getUserInfo(UserHandle.myUserId());
296             updateProfileUi(userInfo);
297         } else {
298             List<UserInfo> profiles = mUm.getProfiles(UserHandle.myUserId());
299             final int profilesCount = profiles.size();
300             for (int i = 0; i < profilesCount; i++) {
301                 if (profiles.get(i).isManagedProfile()
302                         && (mType & ProfileSelectFragment.ProfileType.WORK) != 0) {
303                     updateProfileUi(profiles.get(i));
304                 } else if (!profiles.get(i).isManagedProfile()
305                         && (mType & ProfileSelectFragment.ProfileType.PERSONAL) != 0) {
306                     updateProfileUi(profiles.get(i));
307                 }
308             }
309         }
310         cleanUpPreferences();
311 
312         // Add all preferences, starting with one for the primary profile.
313         // Note that we're relying on the ordering given by the SparseArray keys, and on the
314         // value of UserHandle.USER_OWNER being smaller than all the rest.
315         final int profilesCount = mProfiles.size();
316         for (int i = 0; i < profilesCount; i++) {
317             updateAccountTypes(mProfiles.valueAt(i));
318         }
319 
320         // Refresh for the auto-sync preferences
321         mFragment.forceUpdatePreferences();
322     }
323 
updateProfileUi(final UserInfo userInfo)324     private void updateProfileUi(final UserInfo userInfo) {
325         if (mFragment.getPreferenceManager() == null) {
326             return;
327         }
328         final ProfileData data = mProfiles.get(userInfo.id);
329         if (data != null) {
330             data.pendingRemoval = false;
331             data.userInfo = userInfo;
332             if (userInfo.isEnabled()) {
333                 // recreate the authentication helper to refresh the list of enabled accounts
334                 data.authenticatorHelper =
335                         new AuthenticatorHelper(mContext, userInfo.getUserHandle(), this);
336             }
337             return;
338         }
339         final Context context = mContext;
340         final ProfileData profileData = new ProfileData();
341         profileData.userInfo = userInfo;
342         AccessiblePreferenceCategory preferenceGroup =
343                 mHelper.createAccessiblePreferenceCategory(
344                         mFragment.getPreferenceManager().getContext());
345         preferenceGroup.setOrder(mAccountProfileOrder++);
346         preferenceGroup.setTitle(R.string.account_settings); // default title; may be modified below
347         if (isSingleProfile()) {
348             final String title = context.getString(R.string.account_for_section_header,
349                     BidiFormatter.getInstance().unicodeWrap(userInfo.name));
350             preferenceGroup.setTitle(title);
351             preferenceGroup.setContentDescription(title);
352         } else if (userInfo.isManagedProfile()) {
353             if (mType == ProfileSelectFragment.ProfileType.ALL) {
354                 setCategoryTitleFromDevicePolicyResource(preferenceGroup, WORK_CATEGORY_HEADER,
355                         R.string.category_work);
356                 final String workGroupSummary = getWorkGroupSummary(context, userInfo);
357                 preferenceGroup.setSummary(workGroupSummary);
358                 setContentDescriptionFromDevicePolicyResource(preferenceGroup,
359                         ACCESSIBILITY_CATEGORY_WORK, R.string.accessibility_category_work,
360                         workGroupSummary);
361             }
362             profileData.removeWorkProfilePreference = newRemoveWorkProfilePreference();
363             mHelper.enforceRestrictionOnPreference(profileData.removeWorkProfilePreference,
364                     DISALLOW_REMOVE_MANAGED_PROFILE, UserHandle.myUserId());
365             profileData.managedProfilePreference = newManagedProfileSettings();
366         } else if (userInfo.isCloneProfile()) {
367             if (mType == ProfileSelectFragment.ProfileType.ALL) {
368                 setCategoryTitleFromDevicePolicyResource(preferenceGroup, CLONE_CATEGORY_HEADER,
369                         R.string.category_clone);
370                 setContentDescriptionFromDevicePolicyResource(preferenceGroup,
371                         ACCESSIBILITY_CATEGORY_CLONE, R.string.accessibility_category_clone,
372                         null);
373             }
374         } else {
375             // Primary Profile
376             if (mType == ProfileSelectFragment.ProfileType.ALL) {
377                 setCategoryTitleFromDevicePolicyResource(preferenceGroup, PERSONAL_CATEGORY_HEADER,
378                         R.string.category_personal);
379                 setContentDescriptionFromDevicePolicyResource(preferenceGroup,
380                         ACCESSIBILITY_CATEGORY_PERSONAL, R.string.accessibility_category_personal,
381                         null);
382             }
383         }
384         final PreferenceScreen screen = mFragment.getPreferenceScreen();
385         if (screen != null) {
386             screen.addPreference(preferenceGroup);
387         }
388         profileData.preferenceGroup = preferenceGroup;
389         if (userInfo.isEnabled()) {
390             profileData.authenticatorHelper = new AuthenticatorHelper(context,
391                     userInfo.getUserHandle(), this);
392             if (!userInfo.isCloneProfile()) {
393                 profileData.addAccountPreference = newAddAccountPreference();
394                 mHelper.enforceRestrictionOnPreference(profileData.addAccountPreference,
395                         DISALLOW_MODIFY_ACCOUNTS, userInfo.id);
396             }
397         }
398         mProfiles.put(userInfo.id, profileData);
399     }
400 
setCategoryTitleFromDevicePolicyResource( AccessiblePreferenceCategory preferenceGroup, String stringId, int resourceIdentifier)401     private void setCategoryTitleFromDevicePolicyResource(
402             AccessiblePreferenceCategory preferenceGroup, String stringId, int resourceIdentifier) {
403         preferenceGroup.setTitle(
404                 mDpm.getResources().getString(stringId,
405                         () -> mContext.getString(resourceIdentifier)));
406     }
407 
setContentDescriptionFromDevicePolicyResource( AccessiblePreferenceCategory preferenceGroup, String stringId, int resourceIdentifier, String formatArgs)408     private void setContentDescriptionFromDevicePolicyResource(
409             AccessiblePreferenceCategory preferenceGroup, String stringId, int resourceIdentifier,
410             String formatArgs) {
411         preferenceGroup.setContentDescription(mDpm.getResources().getString(stringId, () -> {
412             if (formatArgs != null) {
413                 return mContext.getString(resourceIdentifier, formatArgs);
414             }
415             return mContext.getString(resourceIdentifier);
416         }));
417     }
418 
newAddAccountPreference()419     private RestrictedPreference newAddAccountPreference() {
420         RestrictedPreference preference =
421                 new RestrictedPreference(mFragment.getPreferenceManager().getContext());
422         preference.setKey(PREF_KEY_ADD_ACCOUNT);
423         preference.setTitle(R.string.add_account_label);
424         preference.setIcon(R.drawable.ic_add_24dp);
425         preference.setOnPreferenceClickListener(this);
426         preference.setOrder(ORDER_NEXT_TO_NEXT_TO_LAST);
427         return preference;
428     }
429 
newRemoveWorkProfilePreference()430     private RestrictedPreference newRemoveWorkProfilePreference() {
431         RestrictedPreference preference = new RestrictedPreference(
432                 mFragment.getPreferenceManager().getContext());
433         preference.setKey(PREF_KEY_REMOVE_PROFILE);
434         preference.setTitle(
435                 mDpm.getResources().getString(REMOVE_WORK_PROFILE,
436                         () -> mContext.getString(R.string.remove_managed_profile_label)));
437         preference.setIcon(R.drawable.ic_delete);
438         preference.setOnPreferenceClickListener(this);
439         preference.setOrder(ORDER_LAST);
440         return preference;
441     }
442 
newManagedProfileSettings()443     private Preference newManagedProfileSettings() {
444         Preference preference = new Preference(mFragment.getPreferenceManager().getContext());
445         preference.setKey(PREF_KEY_WORK_PROFILE_SETTING);
446         preference.setTitle(mDpm.getResources().getString(MANAGED_PROFILE_SETTINGS_TITLE,
447                 () -> mContext.getString(R.string.managed_profile_settings_title)));
448         preference.setIcon(R.drawable.ic_settings_24dp);
449         preference.setOnPreferenceClickListener(this);
450         preference.setOrder(ORDER_NEXT_TO_LAST);
451         return preference;
452     }
453 
getWorkGroupSummary(Context context, UserInfo userInfo)454     private String getWorkGroupSummary(Context context, UserInfo userInfo) {
455         PackageManager packageManager = context.getPackageManager();
456         ApplicationInfo adminApplicationInfo = Utils.getAdminApplicationInfo(context, userInfo.id);
457         if (adminApplicationInfo == null) {
458             return null;
459         }
460         CharSequence appLabel = packageManager.getApplicationLabel(adminApplicationInfo);
461         return mDpm.getResources().getString(MANAGED_BY,
462                 () -> mContext.getString(R.string.managing_admin, appLabel), appLabel);
463     }
464 
cleanUpPreferences()465     void cleanUpPreferences() {
466         PreferenceScreen screen = mFragment.getPreferenceScreen();
467         if (screen == null) {
468             return;
469         }
470         final int count = mProfiles.size();
471         for (int i = count - 1; i >= 0; i--) {
472             final ProfileData data = mProfiles.valueAt(i);
473             if (data.pendingRemoval) {
474                 screen.removePreference(data.preferenceGroup);
475                 mProfiles.removeAt(i);
476             }
477         }
478     }
479 
listenToAccountUpdates()480     private void listenToAccountUpdates() {
481         final int count = mProfiles.size();
482         for (int i = 0; i < count; i++) {
483             AuthenticatorHelper authenticatorHelper = mProfiles.valueAt(i).authenticatorHelper;
484             if (authenticatorHelper != null) {
485                 authenticatorHelper.listenToAccountUpdates();
486             }
487         }
488     }
489 
stopListeningToAccountUpdates()490     private void stopListeningToAccountUpdates() {
491         final int count = mProfiles.size();
492         for (int i = 0; i < count; i++) {
493             AuthenticatorHelper authenticatorHelper = mProfiles.valueAt(i).authenticatorHelper;
494             if (authenticatorHelper != null) {
495                 authenticatorHelper.stopListeningToAccountUpdates();
496             }
497         }
498     }
499 
updateAccountTypes(ProfileData profileData)500     private void updateAccountTypes(ProfileData profileData) {
501         if (mFragment.getPreferenceManager() == null
502                 || profileData.preferenceGroup.getPreferenceManager() == null) {
503             // This could happen if activity is finishing
504             return;
505         }
506         if (profileData.userInfo.isEnabled()) {
507             final ArrayMap<String, AccountTypePreference> preferenceToRemove =
508                     new ArrayMap<>(profileData.accountPreferences);
509             final ArrayList<AccountTypePreference> preferences = getAccountTypePreferences(
510                     profileData.authenticatorHelper, profileData.userInfo.getUserHandle(),
511                     preferenceToRemove);
512             final int count = preferences.size();
513             for (int i = 0; i < count; i++) {
514                 final AccountTypePreference preference = preferences.get(i);
515                 preference.setOrder(i);
516                 final String key = preference.getKey();
517                 if (!profileData.accountPreferences.containsKey(key)) {
518                     profileData.preferenceGroup.addPreference(preference);
519                     profileData.accountPreferences.put(key, preference);
520                 }
521             }
522             if (profileData.addAccountPreference != null) {
523                 profileData.preferenceGroup.addPreference(profileData.addAccountPreference);
524             }
525             for (String key : preferenceToRemove.keySet()) {
526                 profileData.preferenceGroup.removePreference(
527                         profileData.accountPreferences.get(key));
528                 profileData.accountPreferences.remove(key);
529             }
530         } else {
531             profileData.preferenceGroup.removeAll();
532             // Put a label instead of the accounts list
533             if (mProfileNotAvailablePreference == null) {
534                 mProfileNotAvailablePreference =
535                         new Preference(mFragment.getPreferenceManager().getContext());
536             }
537             mProfileNotAvailablePreference.setEnabled(false);
538             mProfileNotAvailablePreference.setIcon(R.drawable.empty_icon);
539             mProfileNotAvailablePreference.setTitle(null);
540             mProfileNotAvailablePreference.setSummary(
541                     mDpm.getResources().getString(
542                             WORK_PROFILE_NOT_AVAILABLE, () -> mContext.getString(
543                     R.string.managed_profile_not_available_label)));
544             profileData.preferenceGroup.addPreference(mProfileNotAvailablePreference);
545         }
546         if (profileData.removeWorkProfilePreference != null) {
547             profileData.preferenceGroup.addPreference(profileData.removeWorkProfilePreference);
548         }
549         if (profileData.managedProfilePreference != null) {
550             profileData.preferenceGroup.addPreference(profileData.managedProfilePreference);
551         }
552     }
553 
getAccountTypePreferences(AuthenticatorHelper helper, UserHandle userHandle, ArrayMap<String, AccountTypePreference> preferenceToRemove)554     private ArrayList<AccountTypePreference> getAccountTypePreferences(AuthenticatorHelper helper,
555             UserHandle userHandle, ArrayMap<String, AccountTypePreference> preferenceToRemove) {
556         final String[] accountTypes = helper.getEnabledAccountTypes();
557         final ArrayList<AccountTypePreference> accountTypePreferences =
558                 new ArrayList<>(accountTypes.length);
559 
560         for (int i = 0; i < accountTypes.length; i++) {
561             final String accountType = accountTypes[i];
562             // Skip showing any account that does not have any of the requested authorities
563             if (!accountTypeHasAnyRequestedAuthorities(helper, accountType)) {
564                 continue;
565             }
566             final CharSequence label = helper.getLabelForType(mContext, accountType);
567             if (label == null) {
568                 continue;
569             }
570             final String titleResPackageName = helper.getPackageForType(accountType);
571             final int titleResId = helper.getLabelIdForType(accountType);
572 
573             final Account[] accounts = AccountManager.get(mContext)
574                     .getAccountsByTypeAsUser(accountType, userHandle);
575             final Drawable icon = helper.getDrawableForType(mContext, accountType);
576             final Context prefContext = mFragment.getPreferenceManager().getContext();
577 
578             // Add a preference row for each individual account
579             for (Account account : accounts) {
580                 final AccountTypePreference preference =
581                         preferenceToRemove.remove(AccountTypePreference.buildKey(account));
582                 if (preference != null) {
583                     accountTypePreferences.add(preference);
584                     continue;
585                 }
586                 final ArrayList<String> auths =
587                         helper.getAuthoritiesForAccountType(account.type);
588                 if (!AccountRestrictionHelper.showAccount(mAuthorities, auths)) {
589                     continue;
590                 }
591                 final Bundle fragmentArguments = new Bundle();
592                 fragmentArguments.putParcelable(AccountDetailDashboardFragment.KEY_ACCOUNT,
593                         account);
594                 fragmentArguments.putParcelable(AccountDetailDashboardFragment.KEY_USER_HANDLE,
595                         userHandle);
596                 fragmentArguments.putString(AccountDetailDashboardFragment.KEY_ACCOUNT_TYPE,
597                         accountType);
598                 fragmentArguments.putString(AccountDetailDashboardFragment.KEY_ACCOUNT_LABEL,
599                         label.toString());
600                 fragmentArguments.putInt(AccountDetailDashboardFragment.KEY_ACCOUNT_TITLE_RES,
601                         titleResId);
602                 fragmentArguments.putParcelable(EXTRA_USER, userHandle);
603                 accountTypePreferences.add(new AccountTypePreference(
604                         prefContext, mMetricsFeatureProvider.getMetricsCategory(mFragment),
605                         account, titleResPackageName, titleResId, label,
606                         AccountDetailDashboardFragment.class.getName(), fragmentArguments, icon));
607             }
608             helper.preloadDrawableForType(mContext, accountType);
609         }
610         // Sort by label
611         Collections.sort(accountTypePreferences, new Comparator<AccountTypePreference>() {
612             @Override
613             public int compare(AccountTypePreference t1, AccountTypePreference t2) {
614                 int result = t1.getSummary().toString().compareTo(t2.getSummary().toString());
615                 return result != 0
616                         ? result : t1.getTitle().toString().compareTo(t2.getTitle().toString());
617             }
618         });
619         return accountTypePreferences;
620     }
621 
accountTypeHasAnyRequestedAuthorities(AuthenticatorHelper helper, String accountType)622     private boolean accountTypeHasAnyRequestedAuthorities(AuthenticatorHelper helper,
623             String accountType) {
624         if (mAuthoritiesCount == 0) {
625             // No authorities required
626             return true;
627         }
628         final ArrayList<String> authoritiesForType = helper.getAuthoritiesForAccountType(
629                 accountType);
630         if (authoritiesForType == null) {
631             Log.d(TAG, "No sync authorities for account type: " + accountType);
632             return false;
633         }
634         for (int j = 0; j < mAuthoritiesCount; j++) {
635             if (authoritiesForType.contains(mAuthorities[j])) {
636                 return true;
637             }
638         }
639         return false;
640     }
641 
isSingleProfile()642     private boolean isSingleProfile() {
643         return mUm.isLinkedUser() || mUm.getProfiles(UserHandle.myUserId()).size() == 1;
644     }
645 
646     private class ManagedProfileBroadcastReceiver extends BroadcastReceiver {
647         private boolean mListeningToManagedProfileEvents;
648 
649         @Override
onReceive(Context context, Intent intent)650         public void onReceive(Context context, Intent intent) {
651             final String action = intent.getAction();
652             Log.v(TAG, "Received broadcast: " + action);
653             if (action.equals(Intent.ACTION_MANAGED_PROFILE_REMOVED)
654                     || action.equals(Intent.ACTION_MANAGED_PROFILE_ADDED)) {
655                 if (mFragment instanceof AccountWorkProfileDashboardFragment) {
656                     new SubSettingLauncher(context)
657                             .setDestination(AccountDashboardFragment.class.getName())
658                             .setSourceMetricsCategory(mFragment.getMetricsCategory())
659                             .setTitleRes(-1)
660                             .setIsSecondLayerPage(true)
661                             .launch();
662                     mFragment.getActivity().finish();
663                 } else {
664                     // Clean old state
665                     stopListeningToAccountUpdates();
666                     // Build new state
667                     updateUi();
668                     listenToAccountUpdates();
669                 }
670                 return;
671             }
672             Log.w(TAG, "Cannot handle received broadcast: " + intent.getAction());
673         }
674 
register(Context context)675         public void register(Context context) {
676             if (!mListeningToManagedProfileEvents) {
677                 IntentFilter intentFilter = new IntentFilter();
678                 intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
679                 intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
680                 context.registerReceiver(this, intentFilter);
681                 mListeningToManagedProfileEvents = true;
682             }
683         }
684 
unregister(Context context)685         public void unregister(Context context) {
686             if (mListeningToManagedProfileEvents) {
687                 context.unregisterReceiver(this);
688                 mListeningToManagedProfileEvents = false;
689             }
690         }
691     }
692 }
693