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