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