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