Home | History | Annotate | Download | only in users
      1 /*
      2  * Copyright (C) 2013 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.users;
     18 
     19 import android.app.Activity;
     20 import android.app.AppGlobals;
     21 import android.appwidget.AppWidgetManager;
     22 import android.content.BroadcastReceiver;
     23 import android.content.Context;
     24 import android.content.Intent;
     25 import android.content.IntentFilter;
     26 import android.content.RestrictionEntry;
     27 import android.content.pm.ApplicationInfo;
     28 import android.content.pm.IPackageManager;
     29 import android.content.pm.PackageInfo;
     30 import android.content.pm.PackageManager;
     31 import android.content.pm.PackageManager.NameNotFoundException;
     32 import android.content.pm.ResolveInfo;
     33 import android.content.res.Resources;
     34 import android.graphics.Bitmap;
     35 import android.graphics.ColorFilter;
     36 import android.graphics.ColorMatrix;
     37 import android.graphics.ColorMatrixColorFilter;
     38 import android.graphics.drawable.Drawable;
     39 import android.os.AsyncTask;
     40 import android.os.Bundle;
     41 import android.os.RemoteException;
     42 import android.os.ServiceManager;
     43 import android.os.UserHandle;
     44 import android.os.UserManager;
     45 import android.preference.CheckBoxPreference;
     46 import android.preference.ListPreference;
     47 import android.preference.MultiSelectListPreference;
     48 import android.preference.Preference;
     49 import android.preference.Preference.OnPreferenceChangeListener;
     50 import android.preference.Preference.OnPreferenceClickListener;
     51 import android.preference.PreferenceGroup;
     52 import android.preference.SwitchPreference;
     53 import android.text.TextUtils;
     54 import android.util.Log;
     55 import android.view.View;
     56 import android.view.View.OnClickListener;
     57 import android.view.inputmethod.InputMethodInfo;
     58 import android.view.inputmethod.InputMethodManager;
     59 import android.view.ViewGroup;
     60 import android.widget.CheckBox;
     61 import android.widget.CompoundButton;
     62 import android.widget.CompoundButton.OnCheckedChangeListener;
     63 import android.widget.Switch;
     64 
     65 import com.android.settings.R;
     66 import com.android.settings.SettingsPreferenceFragment;
     67 
     68 import java.util.ArrayList;
     69 import java.util.Collections;
     70 import java.util.Comparator;
     71 import java.util.HashMap;
     72 import java.util.HashSet;
     73 import java.util.List;
     74 import java.util.Map;
     75 import java.util.Set;
     76 import java.util.StringTokenizer;
     77 
     78 public class AppRestrictionsFragment extends SettingsPreferenceFragment implements
     79         OnPreferenceChangeListener, OnClickListener, OnPreferenceClickListener {
     80 
     81     private static final String TAG = AppRestrictionsFragment.class.getSimpleName();
     82 
     83     private static final boolean DEBUG = false;
     84 
     85     private static final String PKG_PREFIX = "pkg_";
     86 
     87     protected PackageManager mPackageManager;
     88     protected UserManager mUserManager;
     89     protected IPackageManager mIPm;
     90     protected UserHandle mUser;
     91     private PackageInfo mSysPackageInfo;
     92 
     93     private PreferenceGroup mAppList;
     94 
     95     private static final int MAX_APP_RESTRICTIONS = 100;
     96 
     97     private static final String DELIMITER = ";";
     98 
     99     /** Key for extra passed in from calling fragment for the userId of the user being edited */
    100     public static final String EXTRA_USER_ID = "user_id";
    101 
    102     /** Key for extra passed in from calling fragment to indicate if this is a newly created user */
    103     public static final String EXTRA_NEW_USER = "new_user";
    104 
    105     HashMap<String,Boolean> mSelectedPackages = new HashMap<String,Boolean>();
    106     private boolean mFirstTime = true;
    107     private boolean mNewUser;
    108     private boolean mAppListChanged;
    109     protected boolean mRestrictedProfile;
    110 
    111     private static final int CUSTOM_REQUEST_CODE_START = 1000;
    112     private int mCustomRequestCode = CUSTOM_REQUEST_CODE_START;
    113 
    114     private HashMap<Integer, AppRestrictionsPreference> mCustomRequestMap =
    115             new HashMap<Integer,AppRestrictionsPreference>();
    116 
    117     private List<SelectableAppInfo> mVisibleApps;
    118     private List<ApplicationInfo> mUserApps;
    119     private AsyncTask mAppLoadingTask;
    120 
    121     private BroadcastReceiver mUserBackgrounding = new BroadcastReceiver() {
    122         @Override
    123         public void onReceive(Context context, Intent intent) {
    124             // Update the user's app selection right away without waiting for a pause
    125             // onPause() might come in too late, causing apps to disappear after broadcasts
    126             // have been scheduled during user startup.
    127             if (mAppListChanged) {
    128                 if (DEBUG) Log.d(TAG, "User backgrounding, update app list");
    129                 applyUserAppsStates();
    130                 if (DEBUG) Log.d(TAG, "User backgrounding, done updating app list");
    131             }
    132         }
    133     };
    134 
    135     private BroadcastReceiver mPackageObserver = new BroadcastReceiver() {
    136         @Override
    137         public void onReceive(Context context, Intent intent) {
    138             onPackageChanged(intent);
    139         }
    140     };
    141 
    142     static class SelectableAppInfo {
    143         String packageName;
    144         CharSequence appName;
    145         CharSequence activityName;
    146         Drawable icon;
    147         SelectableAppInfo masterEntry;
    148 
    149         @Override
    150         public String toString() {
    151             return packageName + ": appName=" + appName + "; activityName=" + activityName
    152                     + "; icon=" + icon + "; masterEntry=" + masterEntry;
    153         }
    154     }
    155 
    156     static class AppRestrictionsPreference extends SwitchPreference {
    157         private boolean hasSettings;
    158         private OnClickListener listener;
    159         private ArrayList<RestrictionEntry> restrictions;
    160         private boolean panelOpen;
    161         private boolean immutable;
    162         private List<Preference> mChildren = new ArrayList<Preference>();
    163         private final ColorFilter grayscaleFilter;
    164 
    165         AppRestrictionsPreference(Context context, OnClickListener listener) {
    166             super(context);
    167             setLayoutResource(R.layout.preference_app_restrictions);
    168             this.listener = listener;
    169 
    170             ColorMatrix colorMatrix = new ColorMatrix();
    171             colorMatrix.setSaturation(0f);
    172             float[] matrix = colorMatrix.getArray();
    173             matrix[18] = 0.5f;
    174             grayscaleFilter = new ColorMatrixColorFilter(colorMatrix);
    175         }
    176 
    177         private void setSettingsEnabled(boolean enable) {
    178             hasSettings = enable;
    179         }
    180 
    181         @Override
    182         public void setChecked(boolean checked) {
    183             if (checked) {
    184                 getIcon().setColorFilter(null);
    185             } else {
    186                 getIcon().setColorFilter(grayscaleFilter);
    187             }
    188             super.setChecked(checked);
    189         }
    190 
    191         void setRestrictions(ArrayList<RestrictionEntry> restrictions) {
    192             this.restrictions = restrictions;
    193         }
    194 
    195         void setImmutable(boolean immutable) {
    196             this.immutable = immutable;
    197         }
    198 
    199         boolean isImmutable() {
    200             return immutable;
    201         }
    202 
    203         RestrictionEntry getRestriction(String key) {
    204             if (restrictions == null) return null;
    205             for (RestrictionEntry entry : restrictions) {
    206                 if (entry.getKey().equals(key)) {
    207                     return entry;
    208                 }
    209             }
    210             return null;
    211         }
    212 
    213         ArrayList<RestrictionEntry> getRestrictions() {
    214             return restrictions;
    215         }
    216 
    217         boolean isPanelOpen() {
    218             return panelOpen;
    219         }
    220 
    221         void setPanelOpen(boolean open) {
    222             panelOpen = open;
    223         }
    224 
    225         List<Preference> getChildren() {
    226             return mChildren;
    227         }
    228 
    229         @Override
    230         protected void onBindView(View view) {
    231             super.onBindView(view);
    232 
    233             View appRestrictionsSettings = view.findViewById(R.id.app_restrictions_settings);
    234             appRestrictionsSettings.setVisibility(hasSettings ? View.VISIBLE : View.GONE);
    235             view.findViewById(R.id.settings_divider).setVisibility(
    236                     hasSettings ? View.VISIBLE : View.GONE);
    237             appRestrictionsSettings.setOnClickListener(listener);
    238             appRestrictionsSettings.setTag(this);
    239 
    240             View appRestrictionsPref = view.findViewById(R.id.app_restrictions_pref);
    241             appRestrictionsPref.setOnClickListener(listener);
    242             appRestrictionsPref.setTag(this);
    243 
    244             ViewGroup widget = (ViewGroup) view.findViewById(android.R.id.widget_frame);
    245             widget.setEnabled(!isImmutable());
    246             if (widget.getChildCount() > 0) {
    247                 final Switch toggle = (Switch) widget.getChildAt(0);
    248                 toggle.setEnabled(!isImmutable());
    249                 toggle.setTag(this);
    250                 toggle.setOnCheckedChangeListener(new OnCheckedChangeListener() {
    251                     @Override
    252                     public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
    253                         listener.onClick(toggle);
    254                     }
    255                 });
    256             }
    257         }
    258     }
    259 
    260     protected void init(Bundle icicle) {
    261         if (icicle != null) {
    262             mUser = new UserHandle(icicle.getInt(EXTRA_USER_ID));
    263         } else {
    264             Bundle args = getArguments();
    265             if (args != null) {
    266                 if (args.containsKey(EXTRA_USER_ID)) {
    267                     mUser = new UserHandle(args.getInt(EXTRA_USER_ID));
    268                 }
    269                 mNewUser = args.getBoolean(EXTRA_NEW_USER, false);
    270             }
    271         }
    272 
    273         if (mUser == null) {
    274             mUser = android.os.Process.myUserHandle();
    275         }
    276 
    277         mPackageManager = getActivity().getPackageManager();
    278         mIPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    279         mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
    280         mRestrictedProfile = mUserManager.getUserInfo(mUser.getIdentifier()).isRestricted();
    281         try {
    282             mSysPackageInfo = mPackageManager.getPackageInfo("android",
    283                 PackageManager.GET_SIGNATURES);
    284         } catch (NameNotFoundException nnfe) {
    285             // ?
    286         }
    287         addPreferencesFromResource(R.xml.app_restrictions);
    288         mAppList = getAppPreferenceGroup();
    289     }
    290 
    291     @Override
    292     public void onSaveInstanceState(Bundle outState) {
    293         super.onSaveInstanceState(outState);
    294         outState.putInt(EXTRA_USER_ID, mUser.getIdentifier());
    295     }
    296 
    297     @Override
    298     public void onResume() {
    299         super.onResume();
    300 
    301         getActivity().registerReceiver(mUserBackgrounding,
    302                 new IntentFilter(Intent.ACTION_USER_BACKGROUND));
    303         IntentFilter packageFilter = new IntentFilter();
    304         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    305         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    306         packageFilter.addDataScheme("package");
    307         getActivity().registerReceiver(mPackageObserver, packageFilter);
    308 
    309         mAppListChanged = false;
    310         if (mAppLoadingTask == null || mAppLoadingTask.getStatus() == AsyncTask.Status.FINISHED) {
    311             mAppLoadingTask = new AppLoadingTask().execute((Void[]) null);
    312         }
    313     }
    314 
    315     @Override
    316     public void onPause() {
    317         super.onPause();
    318         mNewUser = false;
    319         getActivity().unregisterReceiver(mUserBackgrounding);
    320         getActivity().unregisterReceiver(mPackageObserver);
    321         if (mAppListChanged) {
    322             new Thread() {
    323                 public void run() {
    324                     applyUserAppsStates();
    325                 }
    326             }.start();
    327         }
    328     }
    329 
    330     private void onPackageChanged(Intent intent) {
    331         String action = intent.getAction();
    332         String packageName = intent.getData().getSchemeSpecificPart();
    333         // Package added, check if the preference needs to be enabled
    334         AppRestrictionsPreference pref = (AppRestrictionsPreference)
    335                 findPreference(getKeyForPackage(packageName));
    336         if (pref == null) return;
    337 
    338         if ((Intent.ACTION_PACKAGE_ADDED.equals(action) && pref.isChecked())
    339                 || (Intent.ACTION_PACKAGE_REMOVED.equals(action) && !pref.isChecked())) {
    340             pref.setEnabled(true);
    341         }
    342     }
    343 
    344     protected PreferenceGroup getAppPreferenceGroup() {
    345         return getPreferenceScreen();
    346     }
    347 
    348     protected Drawable getCircularUserIcon() {
    349         Bitmap userIcon = mUserManager.getUserIcon(mUser.getIdentifier());
    350         if (userIcon == null) {
    351             return null;
    352         }
    353         CircleFramedDrawable circularIcon =
    354                 CircleFramedDrawable.getInstance(this.getActivity(), userIcon);
    355         return circularIcon;
    356     }
    357 
    358     protected void clearSelectedApps() {
    359         mSelectedPackages.clear();
    360     }
    361 
    362     private void applyUserAppsStates() {
    363         final int userId = mUser.getIdentifier();
    364         if (!mUserManager.getUserInfo(userId).isRestricted() && userId != UserHandle.myUserId()) {
    365             Log.e(TAG, "Cannot apply application restrictions on another user!");
    366             return;
    367         }
    368         for (Map.Entry<String,Boolean> entry : mSelectedPackages.entrySet()) {
    369             String packageName = entry.getKey();
    370             boolean enabled = entry.getValue();
    371             applyUserAppState(packageName, enabled);
    372         }
    373     }
    374 
    375     private void applyUserAppState(String packageName, boolean enabled) {
    376         final int userId = mUser.getIdentifier();
    377         if (enabled) {
    378             // Enable selected apps
    379             try {
    380                 ApplicationInfo info = mIPm.getApplicationInfo(packageName,
    381                         PackageManager.GET_UNINSTALLED_PACKAGES, userId);
    382                 if (info == null || info.enabled == false
    383                         || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
    384                     mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier());
    385                     if (DEBUG) {
    386                         Log.d(TAG, "Installing " + packageName);
    387                     }
    388                 }
    389                 if (info != null && (info.flags&ApplicationInfo.FLAG_BLOCKED) != 0
    390                         && (info.flags&ApplicationInfo.FLAG_INSTALLED) != 0) {
    391                     disableUiForPackage(packageName);
    392                     mIPm.setApplicationBlockedSettingAsUser(packageName, false, userId);
    393                     if (DEBUG) {
    394                         Log.d(TAG, "Unblocking " + packageName);
    395                     }
    396                 }
    397             } catch (RemoteException re) {
    398             }
    399         } else {
    400             // Blacklist all other apps, system or downloaded
    401             try {
    402                 ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId);
    403                 if (info != null) {
    404                     if (mRestrictedProfile) {
    405                         mIPm.deletePackageAsUser(packageName, null, mUser.getIdentifier(),
    406                                 PackageManager.DELETE_SYSTEM_APP);
    407                         if (DEBUG) {
    408                             Log.d(TAG, "Uninstalling " + packageName);
    409                         }
    410                     } else {
    411                         disableUiForPackage(packageName);
    412                         mIPm.setApplicationBlockedSettingAsUser(packageName, true, userId);
    413                         if (DEBUG) {
    414                             Log.d(TAG, "Blocking " + packageName);
    415                         }
    416                     }
    417                 }
    418             } catch (RemoteException re) {
    419             }
    420         }
    421     }
    422 
    423     private void disableUiForPackage(String packageName) {
    424         AppRestrictionsPreference pref = (AppRestrictionsPreference) findPreference(
    425                 getKeyForPackage(packageName));
    426         if (pref != null) {
    427             pref.setEnabled(false);
    428         }
    429     }
    430 
    431     private boolean isSystemPackage(String packageName) {
    432         try {
    433             final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
    434             if (pi.applicationInfo == null) return false;
    435             final int flags = pi.applicationInfo.flags;
    436             if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
    437                     || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
    438                 return true;
    439             }
    440         } catch (NameNotFoundException nnfe) {
    441             // Missing package?
    442         }
    443         return false;
    444     }
    445 
    446     /**
    447      * Find all pre-installed input methods that are marked as default
    448      * and add them to an exclusion list so that they aren't
    449      * presented to the user for toggling.
    450      * Don't add non-default ones, as they may include other stuff that we
    451      * don't need to auto-include.
    452      * @param excludePackages the set of package names to append to
    453      */
    454     private void addSystemImes(Set<String> excludePackages) {
    455         final Context context = getActivity();
    456         if (context == null) return;
    457         InputMethodManager imm = (InputMethodManager)
    458                 context.getSystemService(Context.INPUT_METHOD_SERVICE);
    459         List<InputMethodInfo> imis = imm.getInputMethodList();
    460         for (InputMethodInfo imi : imis) {
    461             try {
    462                 if (imi.isDefault(context) && isSystemPackage(imi.getPackageName())) {
    463                     excludePackages.add(imi.getPackageName());
    464                 }
    465             } catch (Resources.NotFoundException rnfe) {
    466                 // Not default
    467             }
    468         }
    469     }
    470 
    471     /**
    472      * Add system apps that match an intent to the list, excluding any packages in the exclude list.
    473      * @param visibleApps list of apps to append the new list to
    474      * @param intent the intent to match
    475      * @param excludePackages the set of package names to be excluded, since they're required
    476      */
    477     private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent,
    478             Set<String> excludePackages) {
    479         if (getActivity() == null) return;
    480         final PackageManager pm = mPackageManager;
    481         List<ResolveInfo> launchableApps = pm.queryIntentActivities(intent,
    482                 PackageManager.GET_DISABLED_COMPONENTS | PackageManager.GET_UNINSTALLED_PACKAGES);
    483         for (ResolveInfo app : launchableApps) {
    484             if (app.activityInfo != null && app.activityInfo.applicationInfo != null) {
    485                 final String packageName = app.activityInfo.packageName;
    486                 int flags = app.activityInfo.applicationInfo.flags;
    487                 if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
    488                         || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
    489                     // System app
    490                     // Skip excluded packages
    491                     if (excludePackages.contains(packageName)) continue;
    492                     int enabled = pm.getApplicationEnabledSetting(packageName);
    493                     if (enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
    494                             || enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
    495                         // Check if the app is already enabled for the target user
    496                         ApplicationInfo targetUserAppInfo = getAppInfoForUser(packageName,
    497                                 0, mUser);
    498                         if (targetUserAppInfo == null
    499                                 || (targetUserAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
    500                             continue;
    501                         }
    502                     }
    503                     SelectableAppInfo info = new SelectableAppInfo();
    504                     info.packageName = app.activityInfo.packageName;
    505                     info.appName = app.activityInfo.applicationInfo.loadLabel(pm);
    506                     info.icon = app.activityInfo.loadIcon(pm);
    507                     info.activityName = app.activityInfo.loadLabel(pm);
    508                     if (info.activityName == null) info.activityName = info.appName;
    509 
    510                     visibleApps.add(info);
    511                 }
    512             }
    513         }
    514     }
    515 
    516     private ApplicationInfo getAppInfoForUser(String packageName, int flags, UserHandle user) {
    517         try {
    518             ApplicationInfo targetUserAppInfo = mIPm.getApplicationInfo(packageName, flags,
    519                     user.getIdentifier());
    520             return targetUserAppInfo;
    521         } catch (RemoteException re) {
    522             return null;
    523         }
    524     }
    525 
    526     private class AppLoadingTask extends AsyncTask<Void, Void, Void> {
    527 
    528         @Override
    529         protected Void doInBackground(Void... params) {
    530             fetchAndMergeApps();
    531             return null;
    532         }
    533 
    534         @Override
    535         protected void onPostExecute(Void result) {
    536             populateApps();
    537         }
    538 
    539         @Override
    540         protected void onPreExecute() {
    541         }
    542     }
    543 
    544     private void fetchAndMergeApps() {
    545         mAppList.setOrderingAsAdded(false);
    546         mVisibleApps = new ArrayList<SelectableAppInfo>();
    547         final Context context = getActivity();
    548         if (context == null) return;
    549         final PackageManager pm = mPackageManager;
    550         final IPackageManager ipm = mIPm;
    551 
    552         final HashSet<String> excludePackages = new HashSet<String>();
    553         addSystemImes(excludePackages);
    554 
    555         // Add launchers
    556         Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
    557         launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    558         addSystemApps(mVisibleApps, launcherIntent, excludePackages);
    559 
    560         // Add widgets
    561         Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    562         addSystemApps(mVisibleApps, widgetIntent, excludePackages);
    563 
    564         List<ApplicationInfo> installedApps = pm.getInstalledApplications(
    565                 PackageManager.GET_UNINSTALLED_PACKAGES);
    566         for (ApplicationInfo app : installedApps) {
    567             // If it's not installed, skip
    568             if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
    569 
    570             if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
    571                     && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
    572                 // Downloaded app
    573                 SelectableAppInfo info = new SelectableAppInfo();
    574                 info.packageName = app.packageName;
    575                 info.appName = app.loadLabel(pm);
    576                 info.activityName = info.appName;
    577                 info.icon = app.loadIcon(pm);
    578                 mVisibleApps.add(info);
    579             } else {
    580                 try {
    581                     PackageInfo pi = pm.getPackageInfo(app.packageName, 0);
    582                     // If it's a system app that requires an account and doesn't see restricted
    583                     // accounts, mark for removal. It might get shown in the UI if it has an icon
    584                     // but will still be marked as false and immutable.
    585                     if (mRestrictedProfile
    586                             && pi.requiredAccountType != null && pi.restrictedAccountType == null) {
    587                         mSelectedPackages.put(app.packageName, false);
    588                     }
    589                 } catch (NameNotFoundException re) {
    590                 }
    591             }
    592         }
    593 
    594         // Get the list of apps already installed for the user
    595         mUserApps = null;
    596         try {
    597             mUserApps = ipm.getInstalledApplications(
    598                     PackageManager.GET_UNINSTALLED_PACKAGES, mUser.getIdentifier()).getList();
    599         } catch (RemoteException re) {
    600         }
    601 
    602         if (mUserApps != null) {
    603             for (ApplicationInfo app : mUserApps) {
    604                 if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
    605 
    606                 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
    607                         && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
    608                     // Downloaded app
    609                     SelectableAppInfo info = new SelectableAppInfo();
    610                     info.packageName = app.packageName;
    611                     info.appName = app.loadLabel(pm);
    612                     info.activityName = info.appName;
    613                     info.icon = app.loadIcon(pm);
    614                     mVisibleApps.add(info);
    615                 }
    616             }
    617         }
    618 
    619         // Sort the list of visible apps
    620         Collections.sort(mVisibleApps, new AppLabelComparator());
    621 
    622         // Remove dupes
    623         Set<String> dedupPackageSet = new HashSet<String>();
    624         for (int i = mVisibleApps.size() - 1; i >= 0; i--) {
    625             SelectableAppInfo info = mVisibleApps.get(i);
    626             if (DEBUG) Log.i(TAG, info.toString());
    627             String both = info.packageName + "+" + info.activityName;
    628             if (!TextUtils.isEmpty(info.packageName)
    629                     && !TextUtils.isEmpty(info.activityName)
    630                     && dedupPackageSet.contains(both)) {
    631                 mVisibleApps.remove(i);
    632             } else {
    633                 dedupPackageSet.add(both);
    634             }
    635         }
    636 
    637         // Establish master/slave relationship for entries that share a package name
    638         HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>();
    639         for (SelectableAppInfo info : mVisibleApps) {
    640             if (packageMap.containsKey(info.packageName)) {
    641                 info.masterEntry = packageMap.get(info.packageName);
    642             } else {
    643                 packageMap.put(info.packageName, info);
    644             }
    645         }
    646     }
    647 
    648     private boolean isPlatformSigned(PackageInfo pi) {
    649         return (pi != null && pi.signatures != null &&
    650                     mSysPackageInfo.signatures[0].equals(pi.signatures[0]));
    651     }
    652 
    653     private boolean isAppEnabledForUser(PackageInfo pi) {
    654         if (pi == null) return false;
    655         final int flags = pi.applicationInfo.flags;
    656         // Return true if it is installed and not blocked
    657         return ((flags&ApplicationInfo.FLAG_INSTALLED) != 0
    658                 && (flags&ApplicationInfo.FLAG_BLOCKED) == 0);
    659     }
    660 
    661     private void populateApps() {
    662         final Context context = getActivity();
    663         if (context == null) return;
    664         final PackageManager pm = mPackageManager;
    665         final IPackageManager ipm = mIPm;
    666 
    667         mAppList.removeAll();
    668         Intent restrictionsIntent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES);
    669         final List<ResolveInfo> receivers = pm.queryBroadcastReceivers(restrictionsIntent, 0);
    670         int i = 0;
    671         if (mVisibleApps.size() > 0) {
    672             for (SelectableAppInfo app : mVisibleApps) {
    673                 String packageName = app.packageName;
    674                 if (packageName == null) continue;
    675                 final boolean isSettingsApp = packageName.equals(context.getPackageName());
    676                 AppRestrictionsPreference p = new AppRestrictionsPreference(context, this);
    677                 final boolean hasSettings = resolveInfoListHasPackage(receivers, packageName);
    678                 p.setIcon(app.icon != null ? app.icon.mutate() : null);
    679                 p.setChecked(false);
    680                 p.setTitle(app.activityName);
    681                 if (app.masterEntry != null) {
    682                     p.setSummary(context.getString(R.string.user_restrictions_controlled_by,
    683                             app.masterEntry.activityName));
    684                 }
    685                 p.setKey(getKeyForPackage(packageName));
    686                 p.setSettingsEnabled(hasSettings || isSettingsApp);
    687                 p.setPersistent(false);
    688                 p.setOnPreferenceChangeListener(this);
    689                 p.setOnPreferenceClickListener(this);
    690                 PackageInfo pi = null;
    691                 try {
    692                     pi = ipm.getPackageInfo(packageName,
    693                             PackageManager.GET_UNINSTALLED_PACKAGES
    694                             | PackageManager.GET_SIGNATURES, mUser.getIdentifier());
    695                 } catch (RemoteException e) {
    696                 }
    697                 if (pi != null && (pi.requiredForAllUsers || isPlatformSigned(pi))) {
    698                     p.setChecked(true);
    699                     p.setImmutable(true);
    700                     // If the app is required and has no restrictions, skip showing it
    701                     if (!hasSettings && !isSettingsApp) continue;
    702                     // Get and populate the defaults, since the user is not going to be
    703                     // able to toggle this app ON (it's ON by default and immutable).
    704                     // Only do this for restricted profiles, not single-user restrictions
    705                     if (hasSettings) {
    706                         requestRestrictionsForApp(packageName, p, false);
    707                     }
    708                 } else if (!mNewUser && isAppEnabledForUser(pi)) {
    709                     p.setChecked(true);
    710                 }
    711                 if (mRestrictedProfile
    712                         && pi.requiredAccountType != null && pi.restrictedAccountType == null) {
    713                     p.setChecked(false);
    714                     p.setImmutable(true);
    715                     p.setSummary(R.string.app_not_supported_in_limited);
    716                 }
    717                 if (mRestrictedProfile && pi.restrictedAccountType != null) {
    718                     p.setSummary(R.string.app_sees_restricted_accounts);
    719                 }
    720                 if (app.masterEntry != null) {
    721                     p.setImmutable(true);
    722                     p.setChecked(mSelectedPackages.get(packageName));
    723                 }
    724                 mAppList.addPreference(p);
    725                 if (isSettingsApp) {
    726                     p.setOrder(MAX_APP_RESTRICTIONS * 1);
    727                 } else {
    728                     p.setOrder(MAX_APP_RESTRICTIONS * (i + 2));
    729                 }
    730                 mSelectedPackages.put(packageName, p.isChecked());
    731                 mAppListChanged = true;
    732                 i++;
    733             }
    734         }
    735         // If this is the first time for a new profile, install/uninstall default apps for profile
    736         // to avoid taking the hit in onPause(), which can cause race conditions on user switch.
    737         if (mNewUser && mFirstTime) {
    738             mFirstTime = false;
    739             applyUserAppsStates();
    740         }
    741     }
    742 
    743     private String getKeyForPackage(String packageName) {
    744         return PKG_PREFIX + packageName;
    745     }
    746 
    747     private class AppLabelComparator implements Comparator<SelectableAppInfo> {
    748 
    749         @Override
    750         public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) {
    751             String lhsLabel = lhs.activityName.toString();
    752             String rhsLabel = rhs.activityName.toString();
    753             return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase());
    754         }
    755     }
    756 
    757     private boolean resolveInfoListHasPackage(List<ResolveInfo> receivers, String packageName) {
    758         for (ResolveInfo info : receivers) {
    759             if (info.activityInfo.packageName.equals(packageName)) {
    760                 return true;
    761             }
    762         }
    763         return false;
    764     }
    765 
    766     private void updateAllEntries(String prefKey, boolean checked) {
    767         for (int i = 0; i < mAppList.getPreferenceCount(); i++) {
    768             Preference pref = mAppList.getPreference(i);
    769             if (pref instanceof AppRestrictionsPreference) {
    770                 if (prefKey.equals(pref.getKey())) {
    771                     ((AppRestrictionsPreference) pref).setChecked(checked);
    772                 }
    773             }
    774         }
    775     }
    776 
    777     @Override
    778     public void onClick(View v) {
    779         if (v.getTag() instanceof AppRestrictionsPreference) {
    780             AppRestrictionsPreference pref = (AppRestrictionsPreference) v.getTag();
    781             if (v.getId() == R.id.app_restrictions_settings) {
    782                 onAppSettingsIconClicked(pref);
    783             } else if (!pref.isImmutable()) {
    784                 pref.setChecked(!pref.isChecked());
    785                 final String packageName = pref.getKey().substring(PKG_PREFIX.length());
    786                 mSelectedPackages.put(packageName, pref.isChecked());
    787                 if (pref.isChecked() && pref.hasSettings
    788                         && pref.restrictions == null) {
    789                     // The restrictions have not been initialized, get and save them
    790                     requestRestrictionsForApp(packageName, pref, false);
    791                 }
    792                 mAppListChanged = true;
    793                 // If it's not a restricted profile, apply the changes immediately
    794                 if (!mRestrictedProfile) {
    795                     applyUserAppState(packageName, pref.isChecked());
    796                 }
    797                 updateAllEntries(pref.getKey(), pref.isChecked());
    798             }
    799         }
    800     }
    801 
    802     @Override
    803     public boolean onPreferenceChange(Preference preference, Object newValue) {
    804         String key = preference.getKey();
    805         if (key != null && key.contains(DELIMITER)) {
    806             StringTokenizer st = new StringTokenizer(key, DELIMITER);
    807             final String packageName = st.nextToken();
    808             final String restrictionKey = st.nextToken();
    809             AppRestrictionsPreference appPref = (AppRestrictionsPreference)
    810                     mAppList.findPreference(PKG_PREFIX+packageName);
    811             ArrayList<RestrictionEntry> restrictions = appPref.getRestrictions();
    812             if (restrictions != null) {
    813                 for (RestrictionEntry entry : restrictions) {
    814                     if (entry.getKey().equals(restrictionKey)) {
    815                         switch (entry.getType()) {
    816                         case RestrictionEntry.TYPE_BOOLEAN:
    817                             entry.setSelectedState((Boolean) newValue);
    818                             break;
    819                         case RestrictionEntry.TYPE_CHOICE:
    820                         case RestrictionEntry.TYPE_CHOICE_LEVEL:
    821                             ListPreference listPref = (ListPreference) preference;
    822                             entry.setSelectedString((String) newValue);
    823                             String readable = findInArray(entry.getChoiceEntries(),
    824                                     entry.getChoiceValues(), (String) newValue);
    825                             listPref.setSummary(readable);
    826                             break;
    827                         case RestrictionEntry.TYPE_MULTI_SELECT:
    828                             Set<String> set = (Set<String>) newValue;
    829                             String [] selectedValues = new String[set.size()];
    830                             set.toArray(selectedValues);
    831                             entry.setAllSelectedStrings(selectedValues);
    832                             break;
    833                         default:
    834                             continue;
    835                         }
    836                         if (packageName.equals(getActivity().getPackageName())) {
    837                             RestrictionUtils.setRestrictions(getActivity(), restrictions, mUser);
    838                         } else {
    839                             mUserManager.setApplicationRestrictions(packageName,
    840                                     RestrictionUtils.restrictionsToBundle(restrictions),
    841                                     mUser);
    842                         }
    843                         break;
    844                     }
    845                 }
    846             }
    847         }
    848         return true;
    849     }
    850 
    851     private void removeRestrictionsForApp(AppRestrictionsPreference preference) {
    852         for (Preference p : preference.mChildren) {
    853             mAppList.removePreference(p);
    854         }
    855         preference.mChildren.clear();
    856     }
    857 
    858     private void onAppSettingsIconClicked(AppRestrictionsPreference preference) {
    859         if (preference.getKey().startsWith(PKG_PREFIX)) {
    860             if (preference.isPanelOpen()) {
    861                 removeRestrictionsForApp(preference);
    862             } else {
    863                 String packageName = preference.getKey().substring(PKG_PREFIX.length());
    864                 if (packageName.equals(getActivity().getPackageName())) {
    865                     // Settings, fake it by using user restrictions
    866                     ArrayList<RestrictionEntry> restrictions = RestrictionUtils.getRestrictions(
    867                             getActivity(), mUser);
    868                     onRestrictionsReceived(preference, packageName, restrictions);
    869                 } else {
    870                     requestRestrictionsForApp(packageName, preference, true /*invoke if custom*/);
    871                 }
    872             }
    873             preference.setPanelOpen(!preference.isPanelOpen());
    874         }
    875     }
    876 
    877     /**
    878      * Send a broadcast to the app to query its restrictions
    879      * @param packageName package name of the app with restrictions
    880      * @param preference the preference item for the app toggle
    881      * @param invokeIfCustom whether to directly launch any custom activity that is returned
    882      *        for the app.
    883      */
    884     private void requestRestrictionsForApp(String packageName,
    885             AppRestrictionsPreference preference, boolean invokeIfCustom) {
    886         Bundle oldEntries =
    887                 mUserManager.getApplicationRestrictions(packageName, mUser);
    888         Intent intent = new Intent(Intent.ACTION_GET_RESTRICTION_ENTRIES);
    889         intent.setPackage(packageName);
    890         intent.putExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE, oldEntries);
    891         intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
    892         getActivity().sendOrderedBroadcast(intent, null,
    893                 new RestrictionsResultReceiver(packageName, preference, invokeIfCustom),
    894                 null, Activity.RESULT_OK, null, null);
    895     }
    896 
    897     class RestrictionsResultReceiver extends BroadcastReceiver {
    898 
    899         private static final String CUSTOM_RESTRICTIONS_INTENT = Intent.EXTRA_RESTRICTIONS_INTENT;
    900         String packageName;
    901         AppRestrictionsPreference preference;
    902         boolean invokeIfCustom;
    903 
    904         RestrictionsResultReceiver(String packageName, AppRestrictionsPreference preference,
    905                 boolean invokeIfCustom) {
    906             super();
    907             this.packageName = packageName;
    908             this.preference = preference;
    909             this.invokeIfCustom = invokeIfCustom;
    910         }
    911 
    912         @Override
    913         public void onReceive(Context context, Intent intent) {
    914             Bundle results = getResultExtras(true);
    915             final ArrayList<RestrictionEntry> restrictions = results.getParcelableArrayList(
    916                     Intent.EXTRA_RESTRICTIONS_LIST);
    917             Intent restrictionsIntent = (Intent) results.getParcelable(CUSTOM_RESTRICTIONS_INTENT);
    918             if (restrictions != null && restrictionsIntent == null) {
    919                 onRestrictionsReceived(preference, packageName, restrictions);
    920                 if (mRestrictedProfile) {
    921                     mUserManager.setApplicationRestrictions(packageName,
    922                             RestrictionUtils.restrictionsToBundle(restrictions), mUser);
    923                 }
    924             } else if (restrictionsIntent != null) {
    925                 preference.setRestrictions(restrictions);
    926                 if (invokeIfCustom && AppRestrictionsFragment.this.isResumed()) {
    927                     int requestCode = generateCustomActivityRequestCode(
    928                             RestrictionsResultReceiver.this.preference);
    929                     AppRestrictionsFragment.this.startActivityForResult(
    930                             restrictionsIntent, requestCode);
    931                 }
    932             }
    933         }
    934     }
    935 
    936     private void onRestrictionsReceived(AppRestrictionsPreference preference, String packageName,
    937             ArrayList<RestrictionEntry> restrictions) {
    938         // Remove any earlier restrictions
    939         removeRestrictionsForApp(preference);
    940         // Non-custom-activity case - expand the restrictions in-place
    941         final Context context = preference.getContext();
    942         int count = 1;
    943         for (RestrictionEntry entry : restrictions) {
    944             Preference p = null;
    945             switch (entry.getType()) {
    946             case RestrictionEntry.TYPE_BOOLEAN:
    947                 p = new CheckBoxPreference(context);
    948                 p.setTitle(entry.getTitle());
    949                 p.setSummary(entry.getDescription());
    950                 ((CheckBoxPreference)p).setChecked(entry.getSelectedState());
    951                 break;
    952             case RestrictionEntry.TYPE_CHOICE:
    953             case RestrictionEntry.TYPE_CHOICE_LEVEL:
    954                 p = new ListPreference(context);
    955                 p.setTitle(entry.getTitle());
    956                 String value = entry.getSelectedString();
    957                 if (value == null) {
    958                     value = entry.getDescription();
    959                 }
    960                 p.setSummary(findInArray(entry.getChoiceEntries(), entry.getChoiceValues(),
    961                         value));
    962                 ((ListPreference)p).setEntryValues(entry.getChoiceValues());
    963                 ((ListPreference)p).setEntries(entry.getChoiceEntries());
    964                 ((ListPreference)p).setValue(value);
    965                 ((ListPreference)p).setDialogTitle(entry.getTitle());
    966                 break;
    967             case RestrictionEntry.TYPE_MULTI_SELECT:
    968                 p = new MultiSelectListPreference(context);
    969                 p.setTitle(entry.getTitle());
    970                 ((MultiSelectListPreference)p).setEntryValues(entry.getChoiceValues());
    971                 ((MultiSelectListPreference)p).setEntries(entry.getChoiceEntries());
    972                 HashSet<String> set = new HashSet<String>();
    973                 for (String s : entry.getAllSelectedStrings()) {
    974                     set.add(s);
    975                 }
    976                 ((MultiSelectListPreference)p).setValues(set);
    977                 ((MultiSelectListPreference)p).setDialogTitle(entry.getTitle());
    978                 break;
    979             case RestrictionEntry.TYPE_NULL:
    980             default:
    981             }
    982             if (p != null) {
    983                 p.setPersistent(false);
    984                 p.setOrder(preference.getOrder() + count);
    985                 // Store the restrictions key string as a key for the preference
    986                 p.setKey(preference.getKey().substring(PKG_PREFIX.length()) + DELIMITER
    987                         + entry.getKey());
    988                 mAppList.addPreference(p);
    989                 p.setOnPreferenceChangeListener(AppRestrictionsFragment.this);
    990                 preference.mChildren.add(p);
    991                 count++;
    992             }
    993         }
    994         preference.setRestrictions(restrictions);
    995         if (count == 1 // No visible restrictions
    996                 && preference.isImmutable()
    997                 && preference.isChecked()) {
    998             // Special case of required app with no visible restrictions. Remove it
    999             mAppList.removePreference(preference);
   1000         }
   1001     }
   1002 
   1003     /**
   1004      * Generates a request code that is stored in a map to retrieve the associated
   1005      * AppRestrictionsPreference.
   1006      * @param preference
   1007      * @return
   1008      */
   1009     private int generateCustomActivityRequestCode(AppRestrictionsPreference preference) {
   1010         mCustomRequestCode++;
   1011         mCustomRequestMap.put(mCustomRequestCode, preference);
   1012         return mCustomRequestCode;
   1013     }
   1014 
   1015     @Override
   1016     public void onActivityResult(int requestCode, int resultCode, Intent data) {
   1017         super.onActivityResult(requestCode, resultCode, data);
   1018 
   1019         AppRestrictionsPreference pref = mCustomRequestMap.get(requestCode);
   1020         if (pref == null) {
   1021             Log.w(TAG, "Unknown requestCode " + requestCode);
   1022             return;
   1023         }
   1024 
   1025         if (resultCode == Activity.RESULT_OK) {
   1026             String packageName = pref.getKey().substring(PKG_PREFIX.length());
   1027             ArrayList<RestrictionEntry> list =
   1028                     data.getParcelableArrayListExtra(Intent.EXTRA_RESTRICTIONS_LIST);
   1029             Bundle bundle = data.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE);
   1030             if (list != null) {
   1031                 // If there's a valid result, persist it to the user manager.
   1032                 pref.setRestrictions(list);
   1033                 mUserManager.setApplicationRestrictions(packageName,
   1034                         RestrictionUtils.restrictionsToBundle(list), mUser);
   1035             } else if (bundle != null) {
   1036                 // If there's a valid result, persist it to the user manager.
   1037                 mUserManager.setApplicationRestrictions(packageName, bundle, mUser);
   1038             }
   1039         }
   1040         // Remove request from the map
   1041         mCustomRequestMap.remove(requestCode);
   1042     }
   1043 
   1044     private String findInArray(String[] choiceEntries, String[] choiceValues,
   1045             String selectedString) {
   1046         for (int i = 0; i < choiceValues.length; i++) {
   1047             if (choiceValues[i].equals(selectedString)) {
   1048                 return choiceEntries[i];
   1049             }
   1050         }
   1051         return selectedString;
   1052     }
   1053 
   1054     @Override
   1055     public boolean onPreferenceClick(Preference preference) {
   1056         if (preference.getKey().startsWith(PKG_PREFIX)) {
   1057             AppRestrictionsPreference arp = (AppRestrictionsPreference) preference;
   1058             if (!arp.isImmutable()) {
   1059                 final String packageName = arp.getKey().substring(PKG_PREFIX.length());
   1060                 final boolean newEnabledState = !arp.isChecked();
   1061                 arp.setChecked(newEnabledState);
   1062                 mSelectedPackages.put(packageName, newEnabledState);
   1063                 updateAllEntries(arp.getKey(), newEnabledState);
   1064                 mAppListChanged = true;
   1065                 applyUserAppState(packageName, newEnabledState);
   1066             }
   1067             return true;
   1068         }
   1069         return false;
   1070     }
   1071 
   1072 }
   1073