• 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 
17 package com.android.packageinstaller.permission.model;
18 
19 import android.app.LoaderManager;
20 import android.app.LoaderManager.LoaderCallbacks;
21 import android.content.AsyncTaskLoader;
22 import android.content.Context;
23 import android.content.Loader;
24 import android.content.pm.PackageInfo;
25 import android.content.pm.PackageItemInfo;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PermissionGroupInfo;
28 import android.content.pm.PermissionInfo;
29 import android.graphics.drawable.Drawable;
30 import android.os.Bundle;
31 import android.util.ArraySet;
32 
33 import com.android.packageinstaller.R;
34 import com.android.packageinstaller.permission.utils.Utils;
35 
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.List;
39 import java.util.Set;
40 
41 public final class PermissionGroups implements LoaderCallbacks<List<PermissionGroup>> {
42     private final ArrayList<PermissionGroup> mGroups = new ArrayList<>();
43     private final Context mContext;
44     private final LoaderManager mLoaderManager;
45     private final PermissionsGroupsChangeCallback mCallback;
46 
47     public interface PermissionsGroupsChangeCallback {
onPermissionGroupsChanged()48         public void onPermissionGroupsChanged();
49     }
50 
PermissionGroups(Context context, LoaderManager loaderManager, PermissionsGroupsChangeCallback callback)51     public PermissionGroups(Context context, LoaderManager loaderManager,
52             PermissionsGroupsChangeCallback callback) {
53         mContext = context;
54         mLoaderManager = loaderManager;
55         mCallback = callback;
56         mLoaderManager.initLoader(0, null, this);
57     }
58 
59     @Override
onCreateLoader(int id, Bundle args)60     public Loader<List<PermissionGroup>> onCreateLoader(int id, Bundle args) {
61         return new PermissionsLoader(mContext);
62     }
63 
64     @Override
onLoadFinished(Loader<List<PermissionGroup>> loader, List<PermissionGroup> groups)65     public void onLoadFinished(Loader<List<PermissionGroup>> loader,
66             List<PermissionGroup> groups) {
67         if (mGroups.equals(groups)) {
68             return;
69         }
70         mGroups.clear();
71         mGroups.addAll(groups);
72         mCallback.onPermissionGroupsChanged();
73     }
74 
75     @Override
onLoaderReset(Loader<List<PermissionGroup>> loader)76     public void onLoaderReset(Loader<List<PermissionGroup>> loader) {
77         mGroups.clear();
78         mCallback.onPermissionGroupsChanged();
79     }
80 
getGroups()81     public List<PermissionGroup> getGroups() {
82         return mGroups;
83     }
84 
getGroup(String name)85     public PermissionGroup getGroup(String name) {
86         for (PermissionGroup group : mGroups) {
87             if (group.getName().equals(name)) {
88                 return group;
89             }
90         }
91         return null;
92     }
93 
94     private static final class PermissionsLoader extends AsyncTaskLoader<List<PermissionGroup>>
95             implements PackageManager.OnPermissionsChangedListener {
96 
PermissionsLoader(Context context)97         public PermissionsLoader(Context context) {
98             super(context);
99         }
100 
101         @Override
onStartLoading()102         protected void onStartLoading() {
103             getContext().getPackageManager().addOnPermissionsChangeListener(this);
104             forceLoad();
105         }
106 
107         @Override
onStopLoading()108         protected void onStopLoading() {
109             getContext().getPackageManager().removeOnPermissionsChangeListener(this);
110         }
111 
112         @Override
loadInBackground()113         public List<PermissionGroup> loadInBackground() {
114             ArraySet<String> launcherPkgs = Utils.getLauncherPackages(getContext());
115             PermissionApps.PmCache pmCache = new PermissionApps.PmCache(
116                     getContext().getPackageManager());
117 
118             List<PermissionGroup> groups = new ArrayList<>();
119             Set<String> seenPermissions = new ArraySet<>();
120 
121             PackageManager packageManager = getContext().getPackageManager();
122             List<PermissionGroupInfo> groupInfos = packageManager.getAllPermissionGroups(0);
123 
124             for (PermissionGroupInfo groupInfo : groupInfos) {
125                 // Mare sure we respond to cancellation.
126                 if (isLoadInBackgroundCanceled()) {
127                     return Collections.emptyList();
128                 }
129 
130                 // Get the permissions in this group.
131                 final List<PermissionInfo> groupPermissions;
132                 try {
133                     groupPermissions = packageManager.queryPermissionsByGroup(groupInfo.name, 0);
134                 } catch (PackageManager.NameNotFoundException e) {
135                     continue;
136                 }
137 
138                 boolean hasRuntimePermissions = false;
139 
140                 // Cache seen permissions and see if group has runtime permissions.
141                 for (PermissionInfo groupPermission : groupPermissions) {
142                     seenPermissions.add(groupPermission.name);
143                     if ((groupPermission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
144                             == PermissionInfo.PROTECTION_DANGEROUS
145                             && (groupPermission.flags & PermissionInfo.FLAG_INSTALLED) != 0
146                             && (groupPermission.flags & PermissionInfo.FLAG_REMOVED) == 0) {
147                         hasRuntimePermissions = true;
148                     }
149                 }
150 
151                 // No runtime permissions - not interesting for us.
152                 if (!hasRuntimePermissions) {
153                     continue;
154                 }
155 
156                 CharSequence label = loadItemInfoLabel(groupInfo);
157                 Drawable icon = loadItemInfoIcon(groupInfo);
158 
159                 PermissionApps permApps = new PermissionApps(getContext(), groupInfo.name, null,
160                         pmCache);
161                 permApps.refreshSync();
162 
163                 // Create the group and add to the list.
164                 PermissionGroup group = new PermissionGroup(groupInfo.name,
165                         groupInfo.packageName, label, icon, permApps.getTotalCount(launcherPkgs),
166                         permApps.getGrantedCount(launcherPkgs));
167                 groups.add(group);
168             }
169 
170 
171             // Make sure we add groups for lone runtime permissions.
172             List<PackageInfo> installedPackages = getContext().getPackageManager()
173                     .getInstalledPackages(PackageManager.GET_PERMISSIONS);
174 
175 
176             // We will filter out permissions that no package requests.
177             Set<String> requestedPermissions = new ArraySet<>();
178             for (PackageInfo installedPackage : installedPackages) {
179                 if (installedPackage.requestedPermissions == null) {
180                     continue;
181                 }
182                 for (String requestedPermission : installedPackage.requestedPermissions) {
183                     requestedPermissions.add(requestedPermission);
184                 }
185             }
186 
187             for (PackageInfo installedPackage : installedPackages) {
188                 if (installedPackage.permissions == null) {
189                     continue;
190                 }
191 
192                 for (PermissionInfo permissionInfo : installedPackage.permissions) {
193                     // If we have handled this permission, no more work to do.
194                     if (!seenPermissions.add(permissionInfo.name)) {
195                         continue;
196                     }
197 
198                     // We care only about installed runtime permissions.
199                     if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
200                             != PermissionInfo.PROTECTION_DANGEROUS
201                             || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0) {
202                         continue;
203                     }
204 
205                     // If no app uses this permission,
206                     if (!requestedPermissions.contains(permissionInfo.name)) {
207                         continue;
208                     }
209 
210                     CharSequence label = loadItemInfoLabel(permissionInfo);
211                     Drawable icon = loadItemInfoIcon(permissionInfo);
212 
213                     PermissionApps permApps = new PermissionApps(getContext(), permissionInfo.name,
214                             null, pmCache);
215                     permApps.refreshSync();
216 
217                     // Create the group and add to the list.
218                     PermissionGroup group = new PermissionGroup(permissionInfo.name,
219                             permissionInfo.packageName, label, icon,
220                             permApps.getTotalCount(launcherPkgs),
221                             permApps.getGrantedCount(launcherPkgs));
222                     groups.add(group);
223                 }
224             }
225 
226             Collections.sort(groups);
227             return groups;
228         }
229 
loadItemInfoLabel(PackageItemInfo itemInfo)230         private CharSequence loadItemInfoLabel(PackageItemInfo itemInfo) {
231             CharSequence label = itemInfo.loadLabel(getContext().getPackageManager());
232             if (label == null) {
233                 label = itemInfo.name;
234             }
235             return label;
236         }
237 
loadItemInfoIcon(PackageItemInfo itemInfo)238         private Drawable loadItemInfoIcon(PackageItemInfo itemInfo) {
239             Drawable icon = null;
240             if (itemInfo.icon > 0) {
241                 icon = Utils.loadDrawable(getContext().getPackageManager(),
242                         itemInfo.packageName, itemInfo.icon);
243             }
244             if (icon == null) {
245                 icon = getContext().getDrawable(R.drawable.ic_perm_device_info);
246             }
247             return icon;
248         }
249 
250         @Override
onPermissionsChanged(int uid)251         public void onPermissionsChanged(int uid) {
252             forceLoad();
253         }
254     }
255 }
256