• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package com.android.packageinstaller.permission.model;
17 
18 import android.content.Context;
19 import android.content.pm.ApplicationInfo;
20 import android.content.pm.PackageInfo;
21 import android.content.pm.PackageItemInfo;
22 import android.content.pm.PackageManager;
23 import android.content.pm.PackageManager.NameNotFoundException;
24 import android.content.pm.PermissionInfo;
25 import android.graphics.drawable.Drawable;
26 import android.os.AsyncTask;
27 import android.os.UserHandle;
28 import android.os.UserManager;
29 import android.util.ArrayMap;
30 import android.util.ArraySet;
31 import android.util.IconDrawableFactory;
32 import android.util.Log;
33 import android.util.SparseArray;
34 
35 import com.android.packageinstaller.R;
36 import com.android.packageinstaller.permission.utils.Utils;
37 
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.List;
41 
42 public class PermissionApps {
43     private static final String LOG_TAG = "PermissionApps";
44 
45     private final Context mContext;
46     private final String mGroupName;
47     private final PackageManager mPm;
48     private final Callback mCallback;
49 
50     private final PmCache mCache;
51 
52     private CharSequence mLabel;
53     private Drawable mIcon;
54     private List<PermissionApp> mPermApps;
55     // Map (pkg|uid) -> AppPermission
56     private ArrayMap<String, PermissionApp> mAppLookup;
57 
58     private boolean mSkipUi;
59     private boolean mRefreshing;
60 
PermissionApps(Context context, String groupName, Callback callback)61     public PermissionApps(Context context, String groupName, Callback callback) {
62         this(context, groupName, callback, null);
63     }
64 
PermissionApps(Context context, String groupName, Callback callback, PmCache cache)65     public PermissionApps(Context context, String groupName, Callback callback, PmCache cache) {
66         mCache = cache;
67         mContext = context;
68         mPm = mContext.getPackageManager();
69         mGroupName = groupName;
70         mCallback = callback;
71         loadGroupInfo();
72     }
73 
getGroupName()74     public String getGroupName() {
75         return mGroupName;
76     }
77 
loadNowWithoutUi()78     public void loadNowWithoutUi() {
79         mSkipUi = true;
80         createMap(loadPermissionApps());
81     }
82 
83     /**
84      * Start an async refresh and call back the registered call back once done.
85      *
86      * @param getUiInfo If the UI info should be updated
87      */
refresh(boolean getUiInfo)88     public void refresh(boolean getUiInfo) {
89         if (mCallback == null) {
90             throw new IllegalStateException("callback needs to be set");
91         }
92 
93         if (!mRefreshing) {
94             mRefreshing = true;
95             mSkipUi = !getUiInfo;
96             new PermissionAppsLoader().execute();
97         }
98     }
99 
100     /**
101      * Refresh the state and do not return until it finishes. Should not be called while an {@link
102      * #refresh async referesh} is in progress.
103      */
refreshSync()104     public void refreshSync() {
105         mSkipUi = true;
106         createMap(loadPermissionApps());
107     }
108 
getGrantedCount(ArraySet<String> launcherPkgs)109     public int getGrantedCount(ArraySet<String> launcherPkgs) {
110         int count = 0;
111         for (PermissionApp app : mPermApps) {
112             if (!Utils.shouldShowPermission(app)) {
113                 continue;
114             }
115             if (Utils.isSystem(app, launcherPkgs)) {
116                 // We default to not showing system apps, so hide them from count.
117                 continue;
118             }
119             if (app.areRuntimePermissionsGranted()) {
120                 count++;
121             }
122         }
123         return count;
124     }
125 
getTotalCount(ArraySet<String> launcherPkgs)126     public int getTotalCount(ArraySet<String> launcherPkgs) {
127         int count = 0;
128         for (PermissionApp app : mPermApps) {
129             if (!Utils.shouldShowPermission(app)) {
130                 continue;
131             }
132             if (Utils.isSystem(app, launcherPkgs)) {
133                 // We default to not showing system apps, so hide them from count.
134                 continue;
135             }
136             count++;
137         }
138         return count;
139     }
140 
getApps()141     public List<PermissionApp> getApps() {
142         return mPermApps;
143     }
144 
getApp(String key)145     public PermissionApp getApp(String key) {
146         return mAppLookup.get(key);
147     }
148 
getLabel()149     public CharSequence getLabel() {
150         return mLabel;
151     }
152 
getIcon()153     public Drawable getIcon() {
154         return mIcon;
155     }
156 
loadPermissionApps()157     private List<PermissionApp> loadPermissionApps() {
158         PackageItemInfo groupInfo = getGroupInfo(mGroupName);
159         if (groupInfo == null) {
160             return Collections.emptyList();
161         }
162 
163         List<PermissionInfo> groupPermInfos = getGroupPermissionInfos(mGroupName);
164         if (groupPermInfos == null) {
165             return Collections.emptyList();
166         }
167 
168         ArrayList<PermissionApp> permApps = new ArrayList<>();
169         IconDrawableFactory iconFactory = IconDrawableFactory.newInstance(mContext);
170 
171         UserManager userManager = mContext.getSystemService(UserManager.class);
172         for (UserHandle user : userManager.getUserProfiles()) {
173             List<PackageInfo> apps = mCache != null ? mCache.getPackages(user.getIdentifier())
174                     : mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS,
175                             user.getIdentifier());
176 
177             final int N = apps.size();
178             for (int i = 0; i < N; i++) {
179                 PackageInfo app = apps.get(i);
180                 if (app.requestedPermissions == null) {
181                     continue;
182                 }
183 
184                 for (int j = 0; j < app.requestedPermissions.length; j++) {
185                     String requestedPerm = app.requestedPermissions[j];
186 
187                     PermissionInfo requestedPermissionInfo = null;
188 
189                     for (PermissionInfo groupPermInfo : groupPermInfos) {
190                         if (requestedPerm.equals(groupPermInfo.name)) {
191                             requestedPermissionInfo = groupPermInfo;
192                             break;
193                         }
194                     }
195 
196                     if (requestedPermissionInfo == null) {
197                         continue;
198                     }
199 
200                     if ((requestedPermissionInfo.protectionLevel
201                                 & PermissionInfo.PROTECTION_MASK_BASE)
202                                     != PermissionInfo.PROTECTION_DANGEROUS
203                             || (requestedPermissionInfo.flags
204                                 & PermissionInfo.FLAG_INSTALLED) == 0
205                             || (requestedPermissionInfo.flags
206                                 & PermissionInfo.FLAG_REMOVED) != 0) {
207                         continue;
208                     }
209 
210                     AppPermissionGroup group = AppPermissionGroup.create(mContext,
211                             app, groupInfo, groupPermInfos, user);
212 
213                     if (group == null) {
214                         continue;
215                     }
216 
217                     String label = mSkipUi ? app.packageName
218                             : app.applicationInfo.loadLabel(mPm).toString();
219 
220                     Drawable icon = null;
221                     if (!mSkipUi) {
222                         icon = iconFactory.getBadgedIcon(app.applicationInfo,
223                                 UserHandle.getUserId(group.getApp().applicationInfo.uid));
224                     }
225 
226                     PermissionApp permApp = new PermissionApp(app.packageName, group, label, icon,
227                             app.applicationInfo);
228 
229                     permApps.add(permApp);
230                     break; // move to the next app.
231                 }
232             }
233         }
234 
235         Collections.sort(permApps);
236 
237         return permApps;
238     }
239 
createMap(List<PermissionApp> result)240     private void createMap(List<PermissionApp> result) {
241         mAppLookup = new ArrayMap<>();
242         for (PermissionApp app : result) {
243             mAppLookup.put(app.getKey(), app);
244         }
245         mPermApps = result;
246     }
247 
getGroupInfo(String groupName)248     private PackageItemInfo getGroupInfo(String groupName) {
249         try {
250             return mContext.getPackageManager().getPermissionGroupInfo(groupName, 0);
251         } catch (NameNotFoundException e) {
252             /* ignore */
253         }
254         try {
255             return mContext.getPackageManager().getPermissionInfo(groupName, 0);
256         } catch (NameNotFoundException e2) {
257             /* ignore */
258         }
259         return null;
260     }
261 
getGroupPermissionInfos(String groupName)262     private List<PermissionInfo> getGroupPermissionInfos(String groupName) {
263         try {
264             return mContext.getPackageManager().queryPermissionsByGroup(groupName, 0);
265         } catch (NameNotFoundException e) {
266             /* ignore */
267         }
268         try {
269             PermissionInfo permissionInfo = mContext.getPackageManager()
270                     .getPermissionInfo(groupName, 0);
271             List<PermissionInfo> permissions = new ArrayList<>();
272             permissions.add(permissionInfo);
273             return permissions;
274         } catch (NameNotFoundException e2) {
275             /* ignore */
276         }
277         return null;
278     }
279 
loadGroupInfo()280     private void loadGroupInfo() {
281         PackageItemInfo info;
282         try {
283             info = mPm.getPermissionGroupInfo(mGroupName, 0);
284         } catch (PackageManager.NameNotFoundException e) {
285             try {
286                 PermissionInfo permInfo = mPm.getPermissionInfo(mGroupName, 0);
287                 if ((permInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
288                         != PermissionInfo.PROTECTION_DANGEROUS) {
289                     Log.w(LOG_TAG, mGroupName + " is not a runtime permission");
290                     return;
291                 }
292                 info = permInfo;
293             } catch (NameNotFoundException reallyNotFound) {
294                 Log.w(LOG_TAG, "Can't find permission: " + mGroupName, reallyNotFound);
295                 return;
296             }
297         }
298         mLabel = info.loadLabel(mPm);
299         if (info.icon != 0) {
300             mIcon = info.loadUnbadgedIcon(mPm);
301         } else {
302             mIcon = mContext.getDrawable(R.drawable.ic_perm_device_info);
303         }
304         mIcon = Utils.applyTint(mContext, mIcon, android.R.attr.colorControlNormal);
305     }
306 
307     public static class PermissionApp implements Comparable<PermissionApp> {
308         private final String mPackageName;
309         private final AppPermissionGroup mAppPermissionGroup;
310         private final String mLabel;
311         private final Drawable mIcon;
312         private final ApplicationInfo mInfo;
313 
PermissionApp(String packageName, AppPermissionGroup appPermissionGroup, String label, Drawable icon, ApplicationInfo info)314         public PermissionApp(String packageName, AppPermissionGroup appPermissionGroup,
315                 String label, Drawable icon, ApplicationInfo info) {
316             mPackageName = packageName;
317             mAppPermissionGroup = appPermissionGroup;
318             mLabel = label;
319             mIcon = icon;
320             mInfo = info;
321         }
322 
getAppInfo()323         public ApplicationInfo getAppInfo() {
324             return mInfo;
325         }
326 
getKey()327         public String getKey() {
328             return mPackageName + getUid();
329         }
330 
getLabel()331         public String getLabel() {
332             return mLabel;
333         }
334 
getIcon()335         public Drawable getIcon() {
336             return mIcon;
337         }
338 
areRuntimePermissionsGranted()339         public boolean areRuntimePermissionsGranted() {
340             return mAppPermissionGroup.areRuntimePermissionsGranted();
341         }
342 
isReviewRequired()343         public boolean isReviewRequired() {
344             return mAppPermissionGroup.isReviewRequired();
345         }
346 
grantRuntimePermissions()347         public void grantRuntimePermissions() {
348             mAppPermissionGroup.grantRuntimePermissions(false);
349         }
350 
revokeRuntimePermissions()351         public void revokeRuntimePermissions() {
352             mAppPermissionGroup.revokeRuntimePermissions(false);
353         }
354 
isPolicyFixed()355         public boolean isPolicyFixed() {
356             return mAppPermissionGroup.isPolicyFixed();
357         }
358 
isSystemFixed()359         public boolean isSystemFixed() {
360             return mAppPermissionGroup.isSystemFixed();
361         }
362 
hasGrantedByDefaultPermissions()363         public boolean hasGrantedByDefaultPermissions() {
364             return mAppPermissionGroup.hasGrantedByDefaultPermission();
365         }
366 
doesSupportRuntimePermissions()367         public boolean doesSupportRuntimePermissions() {
368             return mAppPermissionGroup.doesSupportRuntimePermissions();
369         }
370 
getUserId()371         public int getUserId() {
372             return mAppPermissionGroup.getUserId();
373         }
374 
getPackageName()375         public String getPackageName() {
376             return mPackageName;
377         }
378 
getPermissionGroup()379         public AppPermissionGroup getPermissionGroup() {
380             return mAppPermissionGroup;
381         }
382 
383         @Override
compareTo(PermissionApp another)384         public int compareTo(PermissionApp another) {
385             final int result = mLabel.compareTo(another.mLabel);
386             if (result == 0) {
387                 // Unbadged before badged.
388                 return getKey().compareTo(another.getKey());
389             }
390             return result;
391         }
392 
getUid()393         public int getUid() {
394             return mAppPermissionGroup.getApp().applicationInfo.uid;
395         }
396     }
397 
398     private class PermissionAppsLoader extends AsyncTask<Void, Void, List<PermissionApp>> {
399 
400         @Override
doInBackground(Void... args)401         protected List<PermissionApp> doInBackground(Void... args) {
402             return loadPermissionApps();
403         }
404 
405         @Override
onPostExecute(List<PermissionApp> result)406         protected void onPostExecute(List<PermissionApp> result) {
407             mRefreshing = false;
408             createMap(result);
409             if (mCallback != null) {
410                 mCallback.onPermissionsLoaded(PermissionApps.this);
411             }
412         }
413     }
414 
415     /**
416      * Class used to reduce the number of calls to the package manager.
417      * This caches app information so it should only be used across parallel PermissionApps
418      * instances, and should not be retained across UI refresh.
419      */
420     public static class PmCache {
421         private final SparseArray<List<PackageInfo>> mPackageInfoCache = new SparseArray<>();
422         private final PackageManager mPm;
423 
PmCache(PackageManager pm)424         public PmCache(PackageManager pm) {
425             mPm = pm;
426         }
427 
getPackages(int userId)428         public synchronized List<PackageInfo> getPackages(int userId) {
429             List<PackageInfo> ret = mPackageInfoCache.get(userId);
430             if (ret == null) {
431                 ret = mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, userId);
432                 mPackageInfoCache.put(userId, ret);
433             }
434             return ret;
435         }
436     }
437 
438     public interface Callback {
onPermissionsLoaded(PermissionApps permissionApps)439         void onPermissionsLoaded(PermissionApps permissionApps);
440     }
441 }
442