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