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.settingslib.users;
     18 
     19 import android.app.AppGlobals;
     20 import android.appwidget.AppWidgetManager;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.pm.ApplicationInfo;
     24 import android.content.pm.IPackageManager;
     25 import android.content.pm.PackageInfo;
     26 import android.content.pm.PackageManager;
     27 import android.content.pm.ParceledListSlice;
     28 import android.content.pm.ResolveInfo;
     29 import android.content.res.Resources;
     30 import android.graphics.drawable.Drawable;
     31 import android.os.RemoteException;
     32 import android.os.UserHandle;
     33 import android.os.UserManager;
     34 import android.text.TextUtils;
     35 import android.util.Log;
     36 import android.view.inputmethod.InputMethodInfo;
     37 import android.view.inputmethod.InputMethodManager;
     38 
     39 import java.util.ArrayList;
     40 import java.util.Collections;
     41 import java.util.Comparator;
     42 import java.util.HashMap;
     43 import java.util.HashSet;
     44 import java.util.List;
     45 import java.util.Map;
     46 import java.util.Set;
     47 
     48 public class AppRestrictionsHelper {
     49     private static final boolean DEBUG = false;
     50     private static final String TAG = "AppRestrictionsHelper";
     51 
     52     private final Context mContext;
     53     private final PackageManager mPackageManager;
     54     private final IPackageManager mIPm;
     55     private final UserManager mUserManager;
     56     private final UserHandle mUser;
     57     private final boolean mRestrictedProfile;
     58     private boolean mLeanback;
     59 
     60     HashMap<String,Boolean> mSelectedPackages = new HashMap<>();
     61     private List<SelectableAppInfo> mVisibleApps;
     62 
     63     public AppRestrictionsHelper(Context context, UserHandle user) {
     64         mContext = context;
     65         mPackageManager = context.getPackageManager();
     66         mIPm = AppGlobals.getPackageManager();
     67         mUser = user;
     68         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
     69         mRestrictedProfile = mUserManager.getUserInfo(mUser.getIdentifier()).isRestricted();
     70     }
     71 
     72     public void setPackageSelected(String packageName, boolean selected) {
     73         mSelectedPackages.put(packageName, selected);
     74     }
     75 
     76     public boolean isPackageSelected(String packageName) {
     77         return mSelectedPackages.get(packageName);
     78     }
     79 
     80     public void setLeanback(boolean isLeanback) {
     81         mLeanback = isLeanback;
     82     }
     83 
     84     public List<SelectableAppInfo> getVisibleApps() {
     85         return mVisibleApps;
     86     }
     87 
     88     public void applyUserAppsStates(OnDisableUiForPackageListener listener) {
     89         final int userId = mUser.getIdentifier();
     90         if (!mUserManager.getUserInfo(userId).isRestricted() && userId != UserHandle.myUserId()) {
     91             Log.e(TAG, "Cannot apply application restrictions on another user!");
     92             return;
     93         }
     94         for (Map.Entry<String,Boolean> entry : mSelectedPackages.entrySet()) {
     95             String packageName = entry.getKey();
     96             boolean enabled = entry.getValue();
     97             applyUserAppState(packageName, enabled, listener);
     98         }
     99     }
    100 
    101     public void applyUserAppState(String packageName, boolean enabled,
    102             OnDisableUiForPackageListener listener) {
    103         final int userId = mUser.getIdentifier();
    104         if (enabled) {
    105             // Enable selected apps
    106             try {
    107                 ApplicationInfo info = mIPm.getApplicationInfo(packageName,
    108                         PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
    109                 if (info == null || !info.enabled
    110                         || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
    111                     mIPm.installExistingPackageAsUser(packageName, mUser.getIdentifier());
    112                     if (DEBUG) {
    113                         Log.d(TAG, "Installing " + packageName);
    114                     }
    115                 }
    116                 if (info != null && (info.privateFlags&ApplicationInfo.PRIVATE_FLAG_HIDDEN) != 0
    117                         && (info.flags&ApplicationInfo.FLAG_INSTALLED) != 0) {
    118                     listener.onDisableUiForPackage(packageName);
    119                     mIPm.setApplicationHiddenSettingAsUser(packageName, false, userId);
    120                     if (DEBUG) {
    121                         Log.d(TAG, "Unhiding " + packageName);
    122                     }
    123                 }
    124             } catch (RemoteException re) {
    125                 // Ignore
    126             }
    127         } else {
    128             // Blacklist all other apps, system or downloaded
    129             try {
    130                 ApplicationInfo info = mIPm.getApplicationInfo(packageName, 0, userId);
    131                 if (info != null) {
    132                     if (mRestrictedProfile) {
    133                         mIPm.deletePackageAsUser(packageName, null, mUser.getIdentifier(),
    134                                 PackageManager.DELETE_SYSTEM_APP);
    135                         if (DEBUG) {
    136                             Log.d(TAG, "Uninstalling " + packageName);
    137                         }
    138                     } else {
    139                         listener.onDisableUiForPackage(packageName);
    140                         mIPm.setApplicationHiddenSettingAsUser(packageName, true, userId);
    141                         if (DEBUG) {
    142                             Log.d(TAG, "Hiding " + packageName);
    143                         }
    144                     }
    145                 }
    146             } catch (RemoteException re) {
    147                 // Ignore
    148             }
    149         }
    150     }
    151 
    152     public void fetchAndMergeApps() {
    153         mVisibleApps = new ArrayList<>();
    154         final PackageManager pm = mPackageManager;
    155         final IPackageManager ipm = mIPm;
    156 
    157         final HashSet<String> excludePackages = new HashSet<>();
    158         addSystemImes(excludePackages);
    159 
    160         // Add launchers
    161         Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
    162         if (mLeanback) {
    163             launcherIntent.addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER);
    164         } else {
    165             launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
    166         }
    167         addSystemApps(mVisibleApps, launcherIntent, excludePackages);
    168 
    169         // Add widgets
    170         Intent widgetIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    171         addSystemApps(mVisibleApps, widgetIntent, excludePackages);
    172 
    173         List<ApplicationInfo> installedApps = pm.getInstalledApplications(
    174                 PackageManager.MATCH_UNINSTALLED_PACKAGES);
    175         for (ApplicationInfo app : installedApps) {
    176             // If it's not installed, skip
    177             if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
    178 
    179             if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
    180                     && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
    181                 // Downloaded app
    182                 SelectableAppInfo info = new SelectableAppInfo();
    183                 info.packageName = app.packageName;
    184                 info.appName = app.loadLabel(pm);
    185                 info.activityName = info.appName;
    186                 info.icon = app.loadIcon(pm);
    187                 mVisibleApps.add(info);
    188             } else {
    189                 try {
    190                     PackageInfo pi = pm.getPackageInfo(app.packageName, 0);
    191                     // If it's a system app that requires an account and doesn't see restricted
    192                     // accounts, mark for removal. It might get shown in the UI if it has an icon
    193                     // but will still be marked as false and immutable.
    194                     if (mRestrictedProfile
    195                             && pi.requiredAccountType != null && pi.restrictedAccountType == null) {
    196                         mSelectedPackages.put(app.packageName, false);
    197                     }
    198                 } catch (PackageManager.NameNotFoundException re) {
    199                     // Skip
    200                 }
    201             }
    202         }
    203 
    204         // Get the list of apps already installed for the user
    205         List<ApplicationInfo> userApps = null;
    206         try {
    207             ParceledListSlice<ApplicationInfo> listSlice = ipm.getInstalledApplications(
    208                     PackageManager.MATCH_UNINSTALLED_PACKAGES, mUser.getIdentifier());
    209             if (listSlice != null) {
    210                 userApps = listSlice.getList();
    211             }
    212         } catch (RemoteException re) {
    213             // Ignore
    214         }
    215 
    216         if (userApps != null) {
    217             for (ApplicationInfo app : userApps) {
    218                 if ((app.flags & ApplicationInfo.FLAG_INSTALLED) == 0) continue;
    219 
    220                 if ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0
    221                         && (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0) {
    222                     // Downloaded app
    223                     SelectableAppInfo info = new SelectableAppInfo();
    224                     info.packageName = app.packageName;
    225                     info.appName = app.loadLabel(pm);
    226                     info.activityName = info.appName;
    227                     info.icon = app.loadIcon(pm);
    228                     mVisibleApps.add(info);
    229                 }
    230             }
    231         }
    232 
    233         // Sort the list of visible apps
    234         Collections.sort(mVisibleApps, new AppLabelComparator());
    235 
    236         // Remove dupes
    237         Set<String> dedupPackageSet = new HashSet<String>();
    238         for (int i = mVisibleApps.size() - 1; i >= 0; i--) {
    239             SelectableAppInfo info = mVisibleApps.get(i);
    240             if (DEBUG) Log.i(TAG, info.toString());
    241             String both = info.packageName + "+" + info.activityName;
    242             if (!TextUtils.isEmpty(info.packageName)
    243                     && !TextUtils.isEmpty(info.activityName)
    244                     && dedupPackageSet.contains(both)) {
    245                 mVisibleApps.remove(i);
    246             } else {
    247                 dedupPackageSet.add(both);
    248             }
    249         }
    250 
    251         // Establish master/slave relationship for entries that share a package name
    252         HashMap<String,SelectableAppInfo> packageMap = new HashMap<String,SelectableAppInfo>();
    253         for (SelectableAppInfo info : mVisibleApps) {
    254             if (packageMap.containsKey(info.packageName)) {
    255                 info.masterEntry = packageMap.get(info.packageName);
    256             } else {
    257                 packageMap.put(info.packageName, info);
    258             }
    259         }
    260     }
    261 
    262     /**
    263      * Find all pre-installed input methods that are marked as default
    264      * and add them to an exclusion list so that they aren't
    265      * presented to the user for toggling.
    266      * Don't add non-default ones, as they may include other stuff that we
    267      * don't need to auto-include.
    268      * @param excludePackages the set of package names to append to
    269      */
    270     private void addSystemImes(Set<String> excludePackages) {
    271         InputMethodManager imm = (InputMethodManager)
    272                 mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
    273         List<InputMethodInfo> imis = imm.getInputMethodList();
    274         for (InputMethodInfo imi : imis) {
    275             try {
    276                 if (imi.isDefault(mContext) && isSystemPackage(imi.getPackageName())) {
    277                     excludePackages.add(imi.getPackageName());
    278                 }
    279             } catch (Resources.NotFoundException rnfe) {
    280                 // Not default
    281             }
    282         }
    283     }
    284 
    285     /**
    286      * Add system apps that match an intent to the list, excluding any packages in the exclude list.
    287      * @param visibleApps list of apps to append the new list to
    288      * @param intent the intent to match
    289      * @param excludePackages the set of package names to be excluded, since they're required
    290      */
    291     private void addSystemApps(List<SelectableAppInfo> visibleApps, Intent intent,
    292             Set<String> excludePackages) {
    293         final PackageManager pm = mPackageManager;
    294         List<ResolveInfo> launchableApps = pm.queryIntentActivities(intent,
    295                 PackageManager.MATCH_DISABLED_COMPONENTS | PackageManager.MATCH_UNINSTALLED_PACKAGES);
    296         for (ResolveInfo app : launchableApps) {
    297             if (app.activityInfo != null && app.activityInfo.applicationInfo != null) {
    298                 final String packageName = app.activityInfo.packageName;
    299                 int flags = app.activityInfo.applicationInfo.flags;
    300                 if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
    301                         || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
    302                     // System app
    303                     // Skip excluded packages
    304                     if (excludePackages.contains(packageName)) continue;
    305                     int enabled = pm.getApplicationEnabledSetting(packageName);
    306                     if (enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
    307                             || enabled == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
    308                         // Check if the app is already enabled for the target user
    309                         ApplicationInfo targetUserAppInfo = getAppInfoForUser(packageName,
    310                                 0, mUser);
    311                         if (targetUserAppInfo == null
    312                                 || (targetUserAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
    313                             continue;
    314                         }
    315                     }
    316                     SelectableAppInfo info = new SelectableAppInfo();
    317                     info.packageName = app.activityInfo.packageName;
    318                     info.appName = app.activityInfo.applicationInfo.loadLabel(pm);
    319                     info.icon = app.activityInfo.loadIcon(pm);
    320                     info.activityName = app.activityInfo.loadLabel(pm);
    321                     if (info.activityName == null) info.activityName = info.appName;
    322 
    323                     visibleApps.add(info);
    324                 }
    325             }
    326         }
    327     }
    328 
    329     private boolean isSystemPackage(String packageName) {
    330         try {
    331             final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
    332             if (pi.applicationInfo == null) return false;
    333             final int flags = pi.applicationInfo.flags;
    334             if ((flags & ApplicationInfo.FLAG_SYSTEM) != 0
    335                     || (flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
    336                 return true;
    337             }
    338         } catch (PackageManager.NameNotFoundException nnfe) {
    339             // Missing package?
    340         }
    341         return false;
    342     }
    343 
    344     private ApplicationInfo getAppInfoForUser(String packageName, int flags, UserHandle user) {
    345         try {
    346             return mIPm.getApplicationInfo(packageName, flags, user.getIdentifier());
    347         } catch (RemoteException re) {
    348             return null;
    349         }
    350     }
    351 
    352     public interface OnDisableUiForPackageListener {
    353         void onDisableUiForPackage(String packageName);
    354     }
    355 
    356     public static class SelectableAppInfo {
    357         public String packageName;
    358         public CharSequence appName;
    359         public CharSequence activityName;
    360         public Drawable icon;
    361         public SelectableAppInfo masterEntry;
    362 
    363         @Override
    364         public String toString() {
    365             return packageName + ": appName=" + appName + "; activityName=" + activityName
    366                     + "; icon=" + icon + "; masterEntry=" + masterEntry;
    367         }
    368     }
    369 
    370     private static class AppLabelComparator implements Comparator<SelectableAppInfo> {
    371 
    372         @Override
    373         public int compare(SelectableAppInfo lhs, SelectableAppInfo rhs) {
    374             String lhsLabel = lhs.activityName.toString();
    375             String rhsLabel = rhs.activityName.toString();
    376             return lhsLabel.toLowerCase().compareTo(rhsLabel.toLowerCase());
    377         }
    378     }
    379 }
    380