blob: 5c8849da38764cbcc969ea8956c9439cb814f3f8 [file] [log] [blame]
Tony Mantlere662ca62016-01-11 11:42:12 -08001/*
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;
Tony Mantlere662ca62016-01-11 11:42:12 -080032import android.os.UserHandle;
33import android.os.UserManager;
34import android.text.TextUtils;
35import android.util.Log;
36import android.view.inputmethod.InputMethodInfo;
37import android.view.inputmethod.InputMethodManager;
38
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.Comparator;
42import java.util.HashMap;
43import java.util.HashSet;
44import java.util.List;
45import java.util.Map;
46import java.util.Set;
47
48public 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;
Tony Mantlera1ae2892016-01-13 11:07:44 -080058 private boolean mLeanback;
Tony Mantlere662ca62016-01-11 11:42:12 -080059
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
Tony Mantlera1ae2892016-01-13 11:07:44 -080080 public void setLeanback(boolean isLeanback) {
81 mLeanback = isLeanback;
82 }
83
Tony Mantlere662ca62016-01-11 11:42:12 -080084 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);
Tony Mantlera1ae2892016-01-13 11:07:44 -0800162 if (mLeanback) {
163 launcherIntent.addCategory(Intent.CATEGORY_LEANBACK_LAUNCHER);
164 } else {
165 launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
166 }
Tony Mantlere662ca62016-01-11 11:42:12 -0800167 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}