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