Home | History | Annotate | Download | only in users
      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.tv.settings.users;
     18 
     19 import android.app.Activity;
     20 import android.app.AppGlobals;
     21 import android.content.BroadcastReceiver;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.content.RestrictionEntry;
     26 import android.content.RestrictionsManager;
     27 import android.content.pm.ActivityInfo;
     28 import android.content.pm.ApplicationInfo;
     29 import android.content.pm.IPackageManager;
     30 import android.content.pm.PackageInfo;
     31 import android.content.pm.PackageManager;
     32 import android.content.pm.ResolveInfo;
     33 import android.content.pm.UserInfo;
     34 import android.graphics.Color;
     35 import android.graphics.drawable.ColorDrawable;
     36 import android.graphics.drawable.Drawable;
     37 import android.os.AsyncTask;
     38 import android.os.Bundle;
     39 import android.os.RemoteException;
     40 import android.os.UserHandle;
     41 import android.os.UserManager;
     42 import android.support.annotation.NonNull;
     43 import android.support.v14.preference.MultiSelectListPreference;
     44 import android.support.v14.preference.SwitchPreference;
     45 import android.support.v17.preference.LeanbackPreferenceFragment;
     46 import android.support.v4.util.ArrayMap;
     47 import android.support.v7.preference.ListPreference;
     48 import android.support.v7.preference.Preference;
     49 import android.support.v7.preference.PreferenceGroup;
     50 import android.support.v7.preference.PreferenceScreen;
     51 import android.support.v7.preference.PreferenceViewHolder;
     52 import android.util.Log;
     53 import android.view.View;
     54 import android.widget.Checkable;
     55 import android.widget.CompoundButton;
     56 import android.widget.Switch;
     57 
     58 import com.android.settingslib.users.AppRestrictionsHelper;
     59 import com.android.tv.settings.R;
     60 
     61 import java.util.ArrayList;
     62 import java.util.Collections;
     63 import java.util.HashSet;
     64 import java.util.List;
     65 import java.util.Map;
     66 import java.util.Set;
     67 import java.util.StringTokenizer;
     68 
     69 public class AppRestrictionsFragment extends LeanbackPreferenceFragment implements
     70         Preference.OnPreferenceChangeListener,
     71         AppRestrictionsHelper.OnDisableUiForPackageListener {
     72 
     73     private static final String TAG = AppRestrictionsFragment.class.getSimpleName();
     74 
     75     private static final boolean DEBUG = false;
     76 
     77     private static final String PKG_PREFIX = "pkg_";
     78     private static final String ACTIVITY_PREFIX = "activity_";
     79 
     80     private static final Drawable BLANK_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
     81 
     82     protected PackageManager mPackageManager;
     83     protected UserManager mUserManager;
     84     protected IPackageManager mIPm;
     85     protected UserHandle mUser;
     86     private PackageInfo mSysPackageInfo;
     87 
     88     private AppRestrictionsHelper mHelper;
     89 
     90     private PreferenceGroup mAppList;
     91 
     92     private static final int MAX_APP_RESTRICTIONS = 100;
     93 
     94     private static final String DELIMITER = ";";
     95 
     96     /** Key for extra passed in from calling fragment for the userId of the user being edited */
     97     public static final String EXTRA_USER_ID = "user_id";
     98 
     99     /** Key for extra passed in from calling fragment to indicate if this is a newly created user */
    100     public static final String EXTRA_NEW_USER = "new_user";
    101 
    102     private boolean mFirstTime = true;
    103     private boolean mNewUser;
    104     private boolean mAppListChanged;
    105     protected boolean mRestrictedProfile;
    106 
    107     private static final int CUSTOM_REQUEST_CODE_START = 1000;
    108     private int mCustomRequestCode = CUSTOM_REQUEST_CODE_START;
    109 
    110     private Map<Integer, String> mCustomRequestMap = new ArrayMap<>();
    111 
    112     private AsyncTask mAppLoadingTask;
    113 
    114     private BroadcastReceiver mUserBackgrounding = new BroadcastReceiver() {
    115         @Override
    116         public void onReceive(Context context, Intent intent) {
    117             // Update the user's app selection right away without waiting for a pause
    118             // onPause() might come in too late, causing apps to disappear after broadcasts
    119             // have been scheduled during user startup.
    120             if (mAppListChanged) {
    121                 if (DEBUG) Log.d(TAG, "User backgrounding, update app list");
    122                 mHelper.applyUserAppsStates(AppRestrictionsFragment.this);
    123                 if (DEBUG) Log.d(TAG, "User backgrounding, done updating app list");
    124             }
    125         }
    126     };
    127 
    128     private BroadcastReceiver mPackageObserver = new BroadcastReceiver() {
    129         @Override
    130         public void onReceive(Context context, Intent intent) {
    131             onPackageChanged(intent);
    132         }
    133     };
    134 
    135     static class AppRestrictionsPreference extends PreferenceGroup {
    136         private final Listener mListener = new Listener();
    137         private ArrayList<RestrictionEntry> mRestrictions;
    138         private boolean mImmutable;
    139         private boolean mChecked;
    140         private boolean mCheckedSet;
    141 
    142         AppRestrictionsPreference(Context context) {
    143             super(context, null, 0, R.style.LeanbackPreference_SwitchPreference);
    144         }
    145 
    146         void setRestrictions(ArrayList<RestrictionEntry> restrictions) {
    147             this.mRestrictions = restrictions;
    148         }
    149 
    150         void setImmutable(boolean immutable) {
    151             this.mImmutable = immutable;
    152         }
    153 
    154         boolean isImmutable() {
    155             return mImmutable;
    156         }
    157 
    158         ArrayList<RestrictionEntry> getRestrictions() {
    159             return mRestrictions;
    160         }
    161 
    162         public void setChecked(boolean checked) {
    163             // Always persist/notify the first time; don't assume the field's default of false.
    164             final boolean changed = mChecked != checked;
    165             if (changed || !mCheckedSet) {
    166                 mChecked = checked;
    167                 mCheckedSet = true;
    168                 persistBoolean(checked);
    169                 if (changed) {
    170                     notifyDependencyChange(shouldDisableDependents());
    171                     notifyChanged();
    172                     notifyHierarchyChanged();
    173                 }
    174             }
    175         }
    176 
    177         @Override
    178         public int getPreferenceCount() {
    179             if (isChecked()) {
    180                 return super.getPreferenceCount();
    181             } else {
    182                 return 0;
    183             }
    184         }
    185 
    186         public boolean isChecked() {
    187             return mChecked;
    188         }
    189 
    190         @Override
    191         public void onBindViewHolder(PreferenceViewHolder holder) {
    192             super.onBindViewHolder(holder);
    193             View switchView = holder.findViewById(android.R.id.switch_widget);
    194             syncSwitchView(switchView);
    195         }
    196 
    197         private void syncSwitchView(View view) {
    198             if (view instanceof Switch) {
    199                 final Switch switchView = (Switch) view;
    200                 switchView.setOnCheckedChangeListener(null);
    201             }
    202             if (view instanceof Checkable) {
    203                 ((Checkable) view).setChecked(mChecked);
    204             }
    205             if (view instanceof Switch) {
    206                 final Switch switchView = (Switch) view;
    207                 switchView.setOnCheckedChangeListener(mListener);
    208             }
    209         }
    210 
    211         private class Listener implements CompoundButton.OnCheckedChangeListener {
    212             @Override
    213             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    214                 if (!callChangeListener(isChecked)) {
    215                     // Listener didn't like it, change it back.
    216                     // CompoundButton will make sure we don't recurse.
    217                     buttonView.setChecked(!isChecked);
    218                     return;
    219                 }
    220 
    221                 AppRestrictionsPreference.this.setChecked(isChecked);
    222             }
    223         }
    224     }
    225 
    226     public static void prepareArgs(@NonNull Bundle bundle, int userId, boolean newUser) {
    227         bundle.putInt(EXTRA_USER_ID, userId);
    228         bundle.putBoolean(EXTRA_NEW_USER, newUser);
    229     }
    230 
    231     public static AppRestrictionsFragment newInstance(int userId, boolean newUser) {
    232         final Bundle args = new Bundle(2);
    233         prepareArgs(args, userId, newUser);
    234         AppRestrictionsFragment fragment = new AppRestrictionsFragment();
    235         fragment.setArguments(args);
    236         return fragment;
    237     }
    238 
    239     @Override
    240     public void onCreate(Bundle savedInstanceState) {
    241         super.onCreate(savedInstanceState);
    242         if (savedInstanceState != null) {
    243             mUser = new UserHandle(savedInstanceState.getInt(EXTRA_USER_ID));
    244         } else {
    245             Bundle args = getArguments();
    246             if (args != null) {
    247                 if (args.containsKey(EXTRA_USER_ID)) {
    248                     mUser = new UserHandle(args.getInt(EXTRA_USER_ID));
    249                 }
    250                 mNewUser = args.getBoolean(EXTRA_NEW_USER, false);
    251             }
    252         }
    253 
    254         if (mUser == null) {
    255             mUser = android.os.Process.myUserHandle();
    256         }
    257 
    258         mHelper = new AppRestrictionsHelper(getContext(), mUser);
    259         mHelper.setLeanback(true);
    260         mPackageManager = getActivity().getPackageManager();
    261         mIPm = AppGlobals.getPackageManager();
    262         mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
    263         mRestrictedProfile = mUserManager.getUserInfo(mUser.getIdentifier()).isRestricted();
    264         try {
    265             mSysPackageInfo = mPackageManager.getPackageInfo("android",
    266                     PackageManager.GET_SIGNATURES);
    267         } catch (PackageManager.NameNotFoundException nnfe) {
    268             Log.e(TAG, "Could not find system package signatures", nnfe);
    269         }
    270         mAppList = getAppPreferenceGroup();
    271         mAppList.setOrderingAsAdded(false);
    272     }
    273 
    274     @Override
    275     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
    276         final PreferenceScreen screen = getPreferenceManager()
    277                 .createPreferenceScreen(getPreferenceManager().getContext());
    278         screen.setTitle(R.string.restricted_profile_configure_apps_title);
    279         setPreferenceScreen(screen);
    280     }
    281 
    282     @Override
    283     public void onSaveInstanceState(Bundle outState) {
    284         super.onSaveInstanceState(outState);
    285         outState.putInt(EXTRA_USER_ID, mUser.getIdentifier());
    286     }
    287 
    288     @Override
    289     public void onResume() {
    290         super.onResume();
    291 
    292         getActivity().registerReceiver(mUserBackgrounding,
    293                 new IntentFilter(Intent.ACTION_USER_BACKGROUND));
    294         IntentFilter packageFilter = new IntentFilter();
    295         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    296         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    297         packageFilter.addDataScheme("package");
    298         getActivity().registerReceiver(mPackageObserver, packageFilter);
    299 
    300         mAppListChanged = false;
    301         if (mAppLoadingTask == null || mAppLoadingTask.getStatus() == AsyncTask.Status.FINISHED) {
    302             mAppLoadingTask = new AppLoadingTask().execute();
    303         }
    304     }
    305 
    306     @Override
    307     public void onPause() {
    308         super.onPause();
    309         mNewUser = false;
    310         getActivity().unregisterReceiver(mUserBackgrounding);
    311         getActivity().unregisterReceiver(mPackageObserver);
    312         if (mAppListChanged) {
    313             new AsyncTask<Void, Void, Void>() {
    314                 @Override
    315                 protected Void doInBackground(Void... params) {
    316                     mHelper.applyUserAppsStates(AppRestrictionsFragment.this);
    317                     return null;
    318                 }
    319             }.execute();
    320         }
    321     }
    322 
    323     private void onPackageChanged(Intent intent) {
    324         String action = intent.getAction();
    325         String packageName = intent.getData().getSchemeSpecificPart();
    326         // Package added, check if the preference needs to be enabled
    327         AppRestrictionsPreference pref = (AppRestrictionsPreference)
    328                 findPreference(getKeyForPackage(packageName));
    329         if (pref == null) return;
    330 
    331         if ((Intent.ACTION_PACKAGE_ADDED.equals(action) && pref.isChecked())
    332                 || (Intent.ACTION_PACKAGE_REMOVED.equals(action) && !pref.isChecked())) {
    333             pref.setEnabled(true);
    334         }
    335     }
    336 
    337     protected PreferenceGroup getAppPreferenceGroup() {
    338         return getPreferenceScreen();
    339     }
    340 
    341     @Override
    342     public void onDisableUiForPackage(String packageName) {
    343         AppRestrictionsPreference pref = (AppRestrictionsPreference) findPreference(
    344                 getKeyForPackage(packageName));
    345         if (pref != null) {
    346             pref.setEnabled(false);
    347         }
    348     }
    349 
    350     private class AppLoadingTask extends AsyncTask<Void, Void, Void> {
    351 
    352         @Override
    353         protected Void doInBackground(Void... params) {
    354             mHelper.fetchAndMergeApps();
    355             return null;
    356         }
    357 
    358         @Override
    359         protected void onPostExecute(Void result) {
    360             populateApps();
    361         }
    362     }
    363 
    364     private boolean isPlatformSigned(PackageInfo pi) {
    365         return (pi != null && pi.signatures != null &&
    366                 mSysPackageInfo.signatures[0].equals(pi.signatures[0]));
    367     }
    368 
    369     private boolean isAppEnabledForUser(PackageInfo pi) {
    370         if (pi == null) return false;
    371         final int flags = pi.applicationInfo.flags;
    372         final int privateFlags = pi.applicationInfo.privateFlags;
    373         // Return true if it is installed and not hidden
    374         return ((flags& ApplicationInfo.FLAG_INSTALLED) != 0
    375                 && (privateFlags&ApplicationInfo.PRIVATE_FLAG_HIDDEN) == 0);
    376     }
    377 
    378     private void populateApps() {
    379         final Context context = getActivity();
    380         if (context == null) return;
    381         final PackageManager pm = mPackageManager;
    382         final int userId = mUser.getIdentifier();
    383 
    384         // Check if the user was removed in the meantime.
    385         if (getExistingUser(mUserManager, mUser) == null) {
    386             return;
    387         }
    388         mAppList.removeAll();
    389         addLocationAppRestrictionsPreference();
    390         Intent restrictionsIntent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES);
    391         final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(restrictionsIntent, 0);
    392         for (AppRestrictionsHelper.SelectableAppInfo app : mHelper.getVisibleApps()) {
    393             String packageName = app.packageName;
    394             if (packageName == null) continue;
    395             final boolean isSettingsApp = packageName.equals(context.getPackageName());
    396             AppRestrictionsPreference p =
    397                     new AppRestrictionsPreference(getPreferenceManager().getContext());
    398             final boolean hasSettings = resolveInfoListHasPackage(receivers, packageName);
    399             if (isSettingsApp) {
    400                 // Settings app should be available to restricted user
    401                 mHelper.setPackageSelected(packageName, true);
    402                 continue;
    403             }
    404             PackageInfo pi = null;
    405             try {
    406                 pi = mIPm.getPackageInfo(packageName,
    407                         PackageManager.MATCH_UNINSTALLED_PACKAGES
    408                                 | PackageManager.GET_SIGNATURES, userId);
    409             } catch (RemoteException e) {
    410                 // Ignore
    411             }
    412             if (pi == null) {
    413                 continue;
    414             }
    415             if (mRestrictedProfile && isAppUnsupportedInRestrictedProfile(pi)) {
    416                 continue;
    417             }
    418             p.setIcon(app.icon != null ? app.icon.mutate() : null);
    419             p.setChecked(false);
    420             p.setTitle(app.activityName);
    421             p.setKey(getKeyForPackage(packageName));
    422             p.setPersistent(false);
    423             p.setOnPreferenceChangeListener(this);
    424             p.setSummary(getPackageSummary(pi, app));
    425             if (pi.requiredForAllUsers || isPlatformSigned(pi)) {
    426                 p.setChecked(true);
    427                 p.setImmutable(true);
    428                 // If the app is required and has no restrictions, skip showing it
    429                 if (!hasSettings) continue;
    430             } else if (!mNewUser && isAppEnabledForUser(pi)) {
    431                 p.setChecked(true);
    432             }
    433             if (app.masterEntry == null && hasSettings) {
    434                 requestRestrictionsForApp(packageName, p);
    435             }
    436             if (app.masterEntry != null) {
    437                 p.setImmutable(true);
    438                 p.setChecked(mHelper.isPackageSelected(packageName));
    439             }
    440             p.setOrder(MAX_APP_RESTRICTIONS * (mAppList.getPreferenceCount() + 2));
    441             mHelper.setPackageSelected(packageName, p.isChecked());
    442             mAppList.addPreference(p);
    443         }
    444         mAppListChanged = true;
    445         // If this is the first time for a new profile, install/uninstall default apps for profile
    446         // to avoid taking the hit in onPause(), which can cause race conditions on user switch.
    447         if (mNewUser && mFirstTime) {
    448             mFirstTime = false;
    449             mHelper.applyUserAppsStates(this);
    450         }
    451     }
    452 
    453     private String getPackageSummary(PackageInfo pi, AppRestrictionsHelper.SelectableAppInfo app) {
    454         // Check for 3 cases:
    455         // - Slave entry that can see primary user accounts
    456         // - Slave entry that cannot see primary user accounts
    457         // - Master entry that can see primary user accounts
    458         // Otherwise no summary is returned
    459         if (app.masterEntry != null) {
    460             if (mRestrictedProfile && pi.restrictedAccountType != null) {
    461                 return getString(R.string.app_sees_restricted_accounts_and_controlled_by,
    462                         app.masterEntry.activityName);
    463             }
    464             return getString(R.string.user_restrictions_controlled_by,
    465                     app.masterEntry.activityName);
    466         } else if (pi.restrictedAccountType != null) {
    467             return getString(R.string.app_sees_restricted_accounts);
    468         }
    469         return null;
    470     }
    471 
    472     private static boolean isAppUnsupportedInRestrictedProfile(PackageInfo pi) {
    473         return pi.requiredAccountType != null && pi.restrictedAccountType == null;
    474     }
    475 
    476     private void addLocationAppRestrictionsPreference() {
    477         AppRestrictionsPreference p =
    478                 new AppRestrictionsPreference(getPreferenceManager().getContext());
    479         String packageName = getContext().getPackageName();
    480         p.setIcon(R.drawable.ic_location_on);
    481         p.setKey(getKeyForPackage(packageName));
    482         ArrayList<RestrictionEntry> restrictions = RestrictionUtils.getRestrictions(
    483                 getActivity(), mUser);
    484         RestrictionEntry locationRestriction = restrictions.get(0);
    485         p.setTitle(locationRestriction.getTitle());
    486         p.setRestrictions(restrictions);
    487         p.setSummary(locationRestriction.getDescription());
    488         p.setChecked(locationRestriction.getSelectedState());
    489         p.setPersistent(false);
    490         p.setOrder(MAX_APP_RESTRICTIONS);
    491         mAppList.addPreference(p);
    492     }
    493 
    494     private String getKeyForPackage(String packageName) {
    495         return PKG_PREFIX + packageName;
    496     }
    497 
    498     private String getKeyForPackageActivity(String packageName) {
    499         return ACTIVITY_PREFIX + packageName;
    500     }
    501 
    502     private String getPackageFromKey(String key) {
    503         if (key.startsWith(PKG_PREFIX)) {
    504             return key.substring(PKG_PREFIX.length());
    505         } else if (key.startsWith(ACTIVITY_PREFIX)) {
    506             return key.substring(ACTIVITY_PREFIX.length());
    507         } else {
    508             throw new IllegalArgumentException("Tried to extract package from wrong key: " + key);
    509         }
    510     }
    511 
    512     private boolean resolveInfoListHasPackage(List<ResolveInfo> receivers, String packageName) {
    513         for (ResolveInfo info : receivers) {
    514             if (info.activityInfo.packageName.equals(packageName)) {
    515                 return true;
    516             }
    517         }
    518         return false;
    519     }
    520 
    521     private void updateAllEntries(String prefKey, boolean checked) {
    522         for (int i = 0; i < mAppList.getPreferenceCount(); i++) {
    523             Preference pref = mAppList.getPreference(i);
    524             if (pref instanceof AppRestrictionsPreference) {
    525                 if (prefKey.equals(pref.getKey())) {
    526                     ((AppRestrictionsPreference) pref).setChecked(checked);
    527                 }
    528             }
    529         }
    530     }
    531 
    532     private void assertSafeToStartCustomActivity(Intent intent, String packageName) {
    533         // Activity can be started if it belongs to the same app
    534         if (intent.getPackage() != null && intent.getPackage().equals(packageName)) {
    535             return;
    536         }
    537         // Activity can be started if intent resolves to multiple activities
    538         List<ResolveInfo> resolveInfos = AppRestrictionsFragment.this.mPackageManager
    539                 .queryIntentActivities(intent, 0 /* no flags */);
    540         if (resolveInfos.size() != 1) {
    541             return;
    542         }
    543         // Prevent potential privilege escalation
    544         ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
    545         if (!packageName.equals(activityInfo.packageName)) {
    546             throw new SecurityException("Application " + packageName
    547                     + " is not allowed to start activity " + intent);
    548         }
    549     }
    550 
    551     @Override
    552     public boolean onPreferenceTreeClick(Preference preference) {
    553         if (preference instanceof AppRestrictionsPreference) {
    554             AppRestrictionsPreference pref = (AppRestrictionsPreference) preference;
    555             if (!pref.isImmutable()) {
    556                 pref.setChecked(!pref.isChecked());
    557                 final String packageName = getPackageFromKey(pref.getKey());
    558                 // Settings/Location is handled as a top-level entry
    559                 if (packageName.equals(getActivity().getPackageName())) {
    560                     pref.getRestrictions().get(0).setSelectedState(pref.isChecked());
    561                     RestrictionUtils.setRestrictions(getActivity(), pref.getRestrictions(), mUser);
    562                     return true;
    563                 }
    564                 mHelper.setPackageSelected(packageName, pref.isChecked());
    565                 mAppListChanged = true;
    566                 // If it's not a restricted profile, apply the changes immediately
    567                 if (!mRestrictedProfile) {
    568                     mHelper.applyUserAppState(packageName, pref.isChecked(), this);
    569                 }
    570                 updateAllEntries(pref.getKey(), pref.isChecked());
    571             }
    572             return true;
    573         } else if (preference.getIntent() != null) {
    574             assertSafeToStartCustomActivity(preference.getIntent(),
    575                     getPackageFromKey(preference.getKey()));
    576             startActivityForResult(preference.getIntent(),
    577                     generateCustomActivityRequestCode(preference));
    578             return true;
    579         } else {
    580             return super.onPreferenceTreeClick(preference);
    581         }
    582     }
    583 
    584     @Override
    585     public boolean onPreferenceChange(Preference preference, Object newValue) {
    586         String key = preference.getKey();
    587         if (key != null && key.contains(DELIMITER)) {
    588             StringTokenizer st = new StringTokenizer(key, DELIMITER);
    589             final String packageName = st.nextToken();
    590             final String restrictionKey = st.nextToken();
    591             AppRestrictionsPreference appPref = (AppRestrictionsPreference)
    592                     mAppList.findPreference(getKeyForPackage(packageName));
    593             ArrayList<RestrictionEntry> restrictions = appPref.getRestrictions();
    594             if (restrictions != null) {
    595                 for (RestrictionEntry entry : restrictions) {
    596                     if (entry.getKey().equals(restrictionKey)) {
    597                         switch (entry.getType()) {
    598                             case RestrictionEntry.TYPE_BOOLEAN:
    599                                 entry.setSelectedState((Boolean) newValue);
    600                                 break;
    601                             case RestrictionEntry.TYPE_CHOICE:
    602                             case RestrictionEntry.TYPE_CHOICE_LEVEL:
    603                                 ListPreference listPref = (ListPreference) preference;
    604                                 entry.setSelectedString((String) newValue);
    605                                 String readable = findInArray(entry.getChoiceEntries(),
    606                                         entry.getChoiceValues(), (String) newValue);
    607                                 listPref.setSummary(readable);
    608                                 break;
    609                             case RestrictionEntry.TYPE_MULTI_SELECT:
    610                                 Set<String> set = (Set<String>) newValue;
    611                                 String [] selectedValues = new String[set.size()];
    612                                 set.toArray(selectedValues);
    613                                 entry.setAllSelectedStrings(selectedValues);
    614                                 break;
    615                             default:
    616                                 continue;
    617                         }
    618                         mUserManager.setApplicationRestrictions(packageName,
    619                                 RestrictionsManager.convertRestrictionsToBundle(restrictions),
    620                                 mUser);
    621                         break;
    622                     }
    623                 }
    624             }
    625         }
    626         return true;
    627     }
    628 
    629     /**
    630      * Send a broadcast to the app to query its restrictions
    631      * @param packageName package name of the app with restrictions
    632      * @param preference the preference item for the app toggle
    633      */
    634     private void requestRestrictionsForApp(String packageName,
    635             AppRestrictionsPreference preference) {
    636         Bundle oldEntries =
    637                 mUserManager.getApplicationRestrictions(packageName, mUser);
    638         Intent intent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES);
    639         intent.setPackage(packageName);
    640         intent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, oldEntries);
    641         intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
    642         getActivity().sendOrderedBroadcast(intent, null,
    643                 new RestrictionsResultReceiver(packageName, preference),
    644                 null, Activity.RESULT_OK, null, null);
    645     }
    646 
    647     class RestrictionsResultReceiver extends BroadcastReceiver {
    648 
    649         private static final String CUSTOM_RESTRICTIONS_INTENT = Intent.EXTRA_RESTRICTIONS_INTENT;
    650         private final String mPackageName;
    651         private final AppRestrictionsPreference mPreference;
    652 
    653         RestrictionsResultReceiver(String packageName, AppRestrictionsPreference preference) {
    654             super();
    655             mPackageName = packageName;
    656             mPreference = preference;
    657         }
    658 
    659         @Override
    660         public void onReceive(Context context, Intent intent) {
    661             Bundle results = getResultExtras(true);
    662             final ArrayList<RestrictionEntry> restrictions = results.getParcelableArrayList(
    663                     Intent.EXTRA_RESTRICTIONS_LIST);
    664             Intent restrictionsIntent = results.getParcelable(CUSTOM_RESTRICTIONS_INTENT);
    665             if (restrictions != null && restrictionsIntent == null) {
    666                 onRestrictionsReceived(mPreference, restrictions);
    667                 if (mRestrictedProfile) {
    668                     mUserManager.setApplicationRestrictions(mPackageName,
    669                             RestrictionsManager.convertRestrictionsToBundle(restrictions), mUser);
    670                 }
    671             } else if (restrictionsIntent != null) {
    672                 mPreference.setRestrictions(null);
    673                 mPreference.removeAll();
    674                 final Preference p = new Preference(mPreference.getContext());
    675                 p.setKey(getKeyForPackageActivity(mPackageName));
    676                 p.setIcon(BLANK_DRAWABLE);
    677                 p.setTitle(R.string.restricted_profile_customize_restrictions);
    678                 p.setIntent(restrictionsIntent);
    679                 mPreference.addPreference(p);
    680             }
    681         }
    682     }
    683 
    684     private void onRestrictionsReceived(AppRestrictionsPreference preference,
    685             ArrayList<RestrictionEntry> restrictions) {
    686         // Remove any earlier restrictions
    687         preference.removeAll();
    688         // Non-custom-activity case - expand the restrictions in-place
    689         int count = 1;
    690         final Context themedContext = getPreferenceManager().getContext();
    691         for (RestrictionEntry entry : restrictions) {
    692             Preference p = null;
    693             switch (entry.getType()) {
    694                 case RestrictionEntry.TYPE_BOOLEAN:
    695                     p = new SwitchPreference(themedContext);
    696                     p.setTitle(entry.getTitle());
    697                     p.setSummary(entry.getDescription());
    698                     ((SwitchPreference)p).setChecked(entry.getSelectedState());
    699                     break;
    700                 case RestrictionEntry.TYPE_CHOICE:
    701                 case RestrictionEntry.TYPE_CHOICE_LEVEL:
    702                     p = new ListPreference(themedContext);
    703                     p.setTitle(entry.getTitle());
    704                     String value = entry.getSelectedString();
    705                     if (value == null) {
    706                         value = entry.getDescription();
    707                     }
    708                     p.setSummary(findInArray(entry.getChoiceEntries(), entry.getChoiceValues(),
    709                             value));
    710                     ((ListPreference)p).setEntryValues(entry.getChoiceValues());
    711                     ((ListPreference)p).setEntries(entry.getChoiceEntries());
    712                     ((ListPreference)p).setValue(value);
    713                     ((ListPreference)p).setDialogTitle(entry.getTitle());
    714                     break;
    715                 case RestrictionEntry.TYPE_MULTI_SELECT:
    716                     p = new MultiSelectListPreference(themedContext);
    717                     p.setTitle(entry.getTitle());
    718                     ((MultiSelectListPreference)p).setEntryValues(entry.getChoiceValues());
    719                     ((MultiSelectListPreference)p).setEntries(entry.getChoiceEntries());
    720                     HashSet<String> set = new HashSet<>();
    721                     Collections.addAll(set, entry.getAllSelectedStrings());
    722                     ((MultiSelectListPreference)p).setValues(set);
    723                     ((MultiSelectListPreference)p).setDialogTitle(entry.getTitle());
    724                     break;
    725                 case RestrictionEntry.TYPE_NULL:
    726                 default:
    727             }
    728             if (p != null) {
    729                 p.setPersistent(false);
    730                 p.setOrder(preference.getOrder() + count);
    731                 // Store the restrictions key string as a key for the preference
    732                 p.setKey(getPackageFromKey(preference.getKey()) + DELIMITER + entry.getKey());
    733                 preference.addPreference(p);
    734                 p.setOnPreferenceChangeListener(AppRestrictionsFragment.this);
    735                 p.setIcon(BLANK_DRAWABLE);
    736                 count++;
    737             }
    738         }
    739         preference.setRestrictions(restrictions);
    740         if (count == 1 // No visible restrictions
    741                 && preference.isImmutable()
    742                 && preference.isChecked()) {
    743             // Special case of required app with no visible restrictions. Remove it
    744             mAppList.removePreference(preference);
    745         }
    746     }
    747 
    748     /**
    749      * Generates a request code that is stored in a map to retrieve the associated
    750      * AppRestrictionsPreference.
    751      */
    752     private int generateCustomActivityRequestCode(Preference preference) {
    753         mCustomRequestCode++;
    754         final String key = getKeyForPackage(getPackageFromKey(preference.getKey()));
    755         mCustomRequestMap.put(mCustomRequestCode, key);
    756         return mCustomRequestCode;
    757     }
    758 
    759     @Override
    760     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    761         super.onActivityResult(requestCode, resultCode, data);
    762 
    763         AppRestrictionsPreference pref =
    764                 (AppRestrictionsPreference) findPreference(mCustomRequestMap.get(requestCode));
    765         if (pref == null) {
    766             Log.w(TAG, "Unknown requestCode " + requestCode);
    767             return;
    768         }
    769 
    770         if (resultCode == Activity.RESULT_OK) {
    771             String packageName = getPackageFromKey(pref.getKey());
    772             ArrayList<RestrictionEntry> list =
    773                     data.getParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST);
    774             Bundle bundle = data.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE);
    775             if (list != null) {
    776                 // If there's a valid result, persist it to the user manager.
    777                 pref.setRestrictions(list);
    778                 mUserManager.setApplicationRestrictions(packageName,
    779                         RestrictionsManager.convertRestrictionsToBundle(list), mUser);
    780             } else if (bundle != null) {
    781                 // If there's a valid result, persist it to the user manager.
    782                 mUserManager.setApplicationRestrictions(packageName, bundle, mUser);
    783             }
    784         }
    785         // Remove request from the map
    786         mCustomRequestMap.remove(requestCode);
    787     }
    788 
    789     private String findInArray(String[] choiceEntries, String[] choiceValues,
    790             String selectedString) {
    791         for (int i = 0; i < choiceValues.length; i++) {
    792             if (choiceValues[i].equals(selectedString)) {
    793                 return choiceEntries[i];
    794             }
    795         }
    796         return selectedString;
    797     }
    798 
    799     /**
    800      * Queries for the UserInfo of a user. Returns null if the user doesn't exist (was removed).
    801      * @param userManager Instance of UserManager
    802      * @param checkUser The user to check the existence of.
    803      * @return UserInfo of the user or null for non-existent user.
    804      */
    805     public static UserInfo getExistingUser(UserManager userManager, UserHandle checkUser) {
    806         final List<UserInfo> users = userManager.getUsers(true /* excludeDying */);
    807         final int checkUserId = checkUser.getIdentifier();
    808         for (UserInfo user : users) {
    809             if (user.id == checkUserId) {
    810                 return user;
    811             }
    812         }
    813         return null;
    814     }
    815 }
    816