• 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.settings.applications;
17 
18 import android.app.AppGlobals;
19 import android.app.AppOpsManager;
20 import android.app.AppOpsManager.PackageOps;
21 import android.content.Context;
22 import android.content.pm.IPackageManager;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.os.RemoteException;
26 import android.os.UserHandle;
27 import android.os.UserManager;
28 import android.support.annotation.VisibleForTesting;
29 import android.util.ArrayMap;
30 import android.util.Log;
31 import android.util.SparseArray;
32 
33 import com.android.settingslib.applications.ApplicationsState;
34 import com.android.settingslib.applications.ApplicationsState.AppEntry;
35 
36 import java.util.Arrays;
37 import java.util.Collection;
38 import java.util.HashSet;
39 import java.util.List;
40 import java.util.Set;
41 
42 /*
43  * Connects app ops info to the ApplicationsState. Makes use of AppOpsManager to
44  * determine further permission level.
45  */
46 public abstract class AppStateAppOpsBridge extends AppStateBaseBridge {
47 
48     private static final String TAG = "AppStateAppOpsBridge";
49 
50     private final IPackageManager mIPackageManager;
51     private final UserManager mUserManager;
52     private final List<UserHandle> mProfiles;
53     private final AppOpsManager mAppOpsManager;
54     private final Context mContext;
55     private final int[] mAppOpsOpCodes;
56     private final String[] mPermissions;
57 
AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, int appOpsOpCode, String[] permissions)58     public AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback,
59             int appOpsOpCode, String[] permissions) {
60         this(context, appState, callback, appOpsOpCode, permissions,
61                 AppGlobals.getPackageManager());
62     }
63 
64     @VisibleForTesting
AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback, int appOpsOpCode, String[] permissions, IPackageManager packageManager)65     AppStateAppOpsBridge(Context context, ApplicationsState appState, Callback callback,
66             int appOpsOpCode, String[] permissions, IPackageManager packageManager) {
67         super(appState, callback);
68         mContext = context;
69         mIPackageManager = packageManager;
70         mUserManager = UserManager.get(context);
71         mProfiles = mUserManager.getUserProfiles();
72         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
73         mAppOpsOpCodes = new int[] {appOpsOpCode};
74         mPermissions = permissions;
75     }
76 
isThisUserAProfileOfCurrentUser(final int userId)77     private boolean isThisUserAProfileOfCurrentUser(final int userId) {
78         final int profilesMax = mProfiles.size();
79         for (int i = 0; i < profilesMax; i++) {
80             if (mProfiles.get(i).getIdentifier() == userId) {
81                 return true;
82             }
83         }
84         return false;
85     }
86 
updateExtraInfo(AppEntry app, String pkg, int uid)87     protected abstract void updateExtraInfo(AppEntry app, String pkg, int uid);
88 
doesAnyPermissionMatch(String permissionToMatch, String[] permissions)89     private boolean doesAnyPermissionMatch(String permissionToMatch, String[] permissions) {
90         for (String permission : permissions) {
91             if (permissionToMatch.equals(permission)) {
92                 return true;
93             }
94         }
95         return false;
96     }
97 
getPermissionInfo(String pkg, int uid)98     public PermissionState getPermissionInfo(String pkg, int uid) {
99         PermissionState permissionState = new PermissionState(pkg, new UserHandle(UserHandle
100                 .getUserId(uid)));
101         try {
102             permissionState.packageInfo = mIPackageManager.getPackageInfo(pkg,
103                     PackageManager.GET_PERMISSIONS | PackageManager.MATCH_ANY_USER,
104                     permissionState.userHandle.getIdentifier());
105             if (permissionState.packageInfo != null) {
106                 // Check static permission state (whatever that is declared in package manifest)
107                 String[] requestedPermissions = permissionState.packageInfo.requestedPermissions;
108                 int[] permissionFlags = permissionState.packageInfo.requestedPermissionsFlags;
109                 if (requestedPermissions != null) {
110                     for (int i = 0; i < requestedPermissions.length; i++) {
111                         if (doesAnyPermissionMatch(requestedPermissions[i], mPermissions)) {
112                             permissionState.permissionDeclared = true;
113                             if ((permissionFlags[i] & PackageInfo.REQUESTED_PERMISSION_GRANTED)
114                                     != 0) {
115                                 permissionState.staticPermissionGranted = true;
116                                 break;
117                             }
118                         }
119                     }
120                 }
121             }
122             // Check app op state.
123             List<PackageOps> ops = mAppOpsManager.getOpsForPackage(uid, pkg, mAppOpsOpCodes);
124             if (ops != null && ops.size() > 0 && ops.get(0).getOps().size() > 0) {
125                 permissionState.appOpMode = ops.get(0).getOps().get(0).getMode();
126             }
127         } catch (RemoteException e) {
128             Log.w(TAG, "PackageManager is dead. Can't get package info " + pkg, e);
129         }
130         return permissionState;
131     }
132 
133     @Override
loadAllExtraInfo()134     protected void loadAllExtraInfo() {
135         SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
136 
137         // Load state info.
138         loadPermissionsStates(entries);
139         loadAppOpsStates(entries);
140 
141         // Map states to application info.
142         List<AppEntry> apps = mAppSession.getAllApps();
143         final int N = apps.size();
144         for (int i = 0; i < N; i++) {
145             AppEntry app = apps.get(i);
146             int userId = UserHandle.getUserId(app.info.uid);
147             ArrayMap<String, PermissionState> userMap = entries.get(userId);
148             app.extraInfo = userMap != null ? userMap.get(app.info.packageName) : null;
149         }
150     }
151 
152     /*
153      * Gets a sparse array that describes every user on the device and all the associated packages
154      * of each user, together with the packages available for that user.
155      */
getEntries()156     private SparseArray<ArrayMap<String, PermissionState>> getEntries() {
157         try {
158             Set<String> packagesSet = new HashSet<>();
159             for (String permission : mPermissions) {
160                 String[] pkgs = mIPackageManager.getAppOpPermissionPackages(permission);
161                 if (pkgs != null) {
162                     packagesSet.addAll(Arrays.asList(pkgs));
163                 }
164             }
165 
166             if (packagesSet.isEmpty()) {
167                 // No packages are requesting permission as specified by mPermissions.
168                 return null;
169             }
170 
171             // Create a sparse array that maps profileIds to an ArrayMap that maps package names to
172             // an associated PermissionState object
173             SparseArray<ArrayMap<String, PermissionState>> entries = new SparseArray<>();
174             for (final UserHandle profile : mProfiles) {
175                 final ArrayMap<String, PermissionState> entriesForProfile = new ArrayMap<>();
176                 final int profileId = profile.getIdentifier();
177                 entries.put(profileId, entriesForProfile);
178                 for (final String packageName : packagesSet) {
179                     final boolean isAvailable = mIPackageManager.isPackageAvailable(packageName,
180                             profileId);
181                     if (!shouldIgnorePackage(packageName) && isAvailable) {
182                         final PermissionState newEntry = new PermissionState(packageName, profile);
183                         entriesForProfile.put(packageName, newEntry);
184                     }
185                 }
186             }
187 
188             return entries;
189         } catch (RemoteException e) {
190             Log.w(TAG, "PackageManager is dead. Can't get list of packages requesting "
191                     + mPermissions[0], e);
192             return null;
193         }
194     }
195 
196     /*
197      * This method will set the packageInfo and staticPermissionGranted field of the associated
198      * PermissionState, which describes a particular package.
199      */
loadPermissionsStates(SparseArray<ArrayMap<String, PermissionState>> entries)200     private void loadPermissionsStates(SparseArray<ArrayMap<String, PermissionState>> entries) {
201         // Load the packages that have been granted the permission specified in mPermission.
202         if (entries == null) {
203             return;
204         }
205 
206         try {
207             for (final UserHandle profile : mProfiles) {
208                 final int profileId = profile.getIdentifier();
209                 final ArrayMap<String, PermissionState> entriesForProfile = entries.get(profileId);
210                 if (entriesForProfile == null) {
211                     continue;
212                 }
213                 @SuppressWarnings("unchecked") final List<PackageInfo> packageInfos =
214                         mIPackageManager
215                                 .getPackagesHoldingPermissions(mPermissions, 0,
216                                         profileId).getList();
217                 final int packageInfoCount = packageInfos != null ? packageInfos.size() : 0;
218                 for (int i = 0; i < packageInfoCount; i++) {
219                     final PackageInfo packageInfo = packageInfos.get(i);
220                     final PermissionState pe = entriesForProfile.get(packageInfo.packageName);
221                     if (pe != null) {
222                         pe.packageInfo = packageInfo;
223                         pe.staticPermissionGranted = true;
224                     }
225                 }
226             }
227         } catch (RemoteException e) {
228             Log.w(TAG, "PackageManager is dead. Can't get list of packages granted "
229                     + mPermissions, e);
230             return;
231         }
232     }
233 
234     /*
235      * This method will set the appOpMode field of the associated PermissionState, which describes
236      * a particular package.
237      */
loadAppOpsStates(SparseArray<ArrayMap<String, PermissionState>> entries)238     private void loadAppOpsStates(SparseArray<ArrayMap<String, PermissionState>> entries) {
239         // Find out which packages have been granted permission from AppOps.
240         final List<AppOpsManager.PackageOps> packageOps = mAppOpsManager.getPackagesForOps(
241                 mAppOpsOpCodes);
242         final int packageOpsCount = packageOps != null ? packageOps.size() : 0;
243         for (int i = 0; i < packageOpsCount; i++) {
244             final AppOpsManager.PackageOps packageOp = packageOps.get(i);
245             final int userId = UserHandle.getUserId(packageOp.getUid());
246             if (!isThisUserAProfileOfCurrentUser(userId)) {
247                 // This AppOp does not belong to any of this user's profiles.
248                 continue;
249             }
250 
251             final ArrayMap<String, PermissionState> entriesForProfile = entries.get(userId);
252             if (entriesForProfile == null) {
253                 continue;
254             }
255             final PermissionState pe = entriesForProfile.get(packageOp.getPackageName());
256             if (pe == null) {
257                 Log.w(TAG, "AppOp permission exists for package " + packageOp.getPackageName()
258                         + " of user " + userId + " but package doesn't exist or did not request "
259                         + mPermissions + " access");
260                 continue;
261             }
262 
263             if (packageOp.getOps().size() < 1) {
264                 Log.w(TAG, "No AppOps permission exists for package " + packageOp.getPackageName());
265                 continue;
266             }
267             pe.appOpMode = packageOp.getOps().get(0).getMode();
268         }
269     }
270 
271     /*
272      * Check for packages that should be ignored for further processing
273      */
shouldIgnorePackage(String packageName)274     private boolean shouldIgnorePackage(String packageName) {
275         return packageName.equals("android") || packageName.equals(mContext.getPackageName());
276     }
277 
getNumPackagesDeclaredPermission()278     public int getNumPackagesDeclaredPermission() {
279         SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
280         if (entries == null) {
281             return 0;
282         }
283         final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager
284                 .getUserHandle());
285         if (entriesForProfile == null) {
286             return 0;
287         }
288         return entriesForProfile.size();
289     }
290 
getNumPackagesAllowedByAppOps()291     public int getNumPackagesAllowedByAppOps() {
292         SparseArray<ArrayMap<String, PermissionState>> entries = getEntries();
293         if (entries == null) {
294             return 0;
295         }
296         loadPermissionsStates(entries);
297         loadAppOpsStates(entries);
298         final ArrayMap<String, PermissionState> entriesForProfile = entries.get(mUserManager
299                 .getUserHandle());
300         if (entriesForProfile == null) {
301             return 0;
302         }
303         Collection<PermissionState> permStates = entriesForProfile.values();
304         int result = 0;
305         for (PermissionState permState : permStates) {
306             if (permState.isPermissible()) {
307                 result++;
308             }
309         }
310         return result;
311     }
312 
313     public static class PermissionState {
314         public final String packageName;
315         public final UserHandle userHandle;
316         public PackageInfo packageInfo;
317         public boolean staticPermissionGranted;
318         public boolean permissionDeclared;
319         public int appOpMode;
320 
PermissionState(String packageName, UserHandle userHandle)321         public PermissionState(String packageName, UserHandle userHandle) {
322             this.packageName = packageName;
323             this.appOpMode = AppOpsManager.MODE_DEFAULT;
324             this.userHandle = userHandle;
325         }
326 
isPermissible()327         public boolean isPermissible() {
328             // defining the default behavior as permissible as long as the package requested this
329             // permission (this means pre-M gets approval during install time; M apps gets approval
330             // during runtime.
331             if (appOpMode == AppOpsManager.MODE_DEFAULT) {
332                 return staticPermissionGranted;
333             }
334             return appOpMode == AppOpsManager.MODE_ALLOWED;
335         }
336     }
337 }
338