1 /*
2  * Copyright (C) 2017 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.content.Context;
22 import android.content.pm.IPackageManager;
23 import android.content.pm.PackageManager;
24 import android.os.RemoteException;
25 import android.util.Log;
26 
27 import com.android.internal.util.ArrayUtils;
28 import com.android.settings.R;
29 import com.android.settingslib.applications.ApplicationsState;
30 import com.android.settingslib.applications.ApplicationsState.AppEntry;
31 import com.android.settingslib.applications.ApplicationsState.AppFilter;
32 
33 import java.util.List;
34 
35 /**
36  * Connects app op info to the ApplicationsState. Wraps around the generic AppStateBaseBridge
37  * class to tailor to the semantics of {@link AppOpsManager#OP_REQUEST_INSTALL_PACKAGES}
38  * Also provides app filters that can use the info.
39  */
40 public class AppStateInstallAppsBridge extends AppStateBaseBridge {
41 
42     private static final String TAG = AppStateInstallAppsBridge.class.getSimpleName();
43 
44     private final IPackageManager mIpm;
45     private final AppOpsManager mAppOpsManager;
46 
AppStateInstallAppsBridge(Context context, ApplicationsState appState, Callback callback)47     public AppStateInstallAppsBridge(Context context, ApplicationsState appState,
48             Callback callback) {
49         super(appState, callback);
50         mIpm = AppGlobals.getPackageManager();
51         mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
52     }
53 
54     @Override
updateExtraInfo(AppEntry app, String packageName, int uid)55     protected void updateExtraInfo(AppEntry app, String packageName, int uid) {
56         app.extraInfo = createInstallAppsStateFor(packageName, uid);
57     }
58 
59     @Override
loadAllExtraInfo()60     protected void loadAllExtraInfo() {
61         // TODO: consider making this a batch operation with a single binder call
62         final List<AppEntry> allApps = mAppSession.getAllApps();
63         for (int i = 0; i < allApps.size(); i++) {
64             AppEntry currentEntry = allApps.get(i);
65             updateExtraInfo(currentEntry, currentEntry.info.packageName, currentEntry.info.uid);
66         }
67     }
68 
hasRequestedAppOpPermission(String permission, String packageName)69     private boolean hasRequestedAppOpPermission(String permission, String packageName) {
70         try {
71             String[] packages = mIpm.getAppOpPermissionPackages(permission);
72             return ArrayUtils.contains(packages, packageName);
73         } catch (RemoteException exc) {
74             Log.e(TAG, "PackageManager dead. Cannot get permission info");
75             return false;
76         }
77     }
78 
hasPermission(String permission, int uid)79     private boolean hasPermission(String permission, int uid) {
80         try {
81             int result = mIpm.checkUidPermission(permission, uid);
82             return result == PackageManager.PERMISSION_GRANTED;
83         } catch (RemoteException e) {
84             Log.e(TAG, "PackageManager dead. Cannot get permission info");
85             return false;
86         }
87     }
88 
getAppOpMode(int appOpCode, int uid, String packageName)89     private int getAppOpMode(int appOpCode, int uid, String packageName) {
90         return mAppOpsManager.checkOpNoThrow(appOpCode, uid, packageName);
91     }
92 
createInstallAppsStateFor(String packageName, int uid)93     InstallAppsState createInstallAppsStateFor(String packageName, int uid) {
94         final InstallAppsState appState = new InstallAppsState();
95         appState.permissionRequested = hasRequestedAppOpPermission(
96                 Manifest.permission.REQUEST_INSTALL_PACKAGES, packageName);
97         appState.permissionGranted = hasPermission(Manifest.permission.REQUEST_INSTALL_PACKAGES,
98                 uid);
99         appState.appOpMode = getAppOpMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid,
100                 packageName);
101         return appState;
102     }
103 
104     /**
105      * Collection of information to be used as {@link AppEntry#extraInfo} objects
106      */
107     public static class InstallAppsState {
108         boolean permissionRequested;
109         boolean permissionGranted;
110         int appOpMode;
111 
InstallAppsState()112         public InstallAppsState() {
113             this.appOpMode = AppOpsManager.MODE_DEFAULT;
114         }
115 
canInstallApps()116         public boolean canInstallApps() {
117             if (appOpMode == AppOpsManager.MODE_DEFAULT) {
118                 return permissionGranted;
119             } else {
120                 return appOpMode == AppOpsManager.MODE_ALLOWED;
121             }
122         }
123 
isPotentialAppSource()124         public boolean isPotentialAppSource() {
125             return appOpMode != AppOpsManager.MODE_DEFAULT || permissionRequested;
126         }
127 
128         @Override
toString()129         public String toString() {
130             StringBuilder sb = new StringBuilder("[permissionGranted: " + permissionGranted);
131             sb.append(", permissionRequested: " + permissionRequested);
132             sb.append(", appOpMode: " + appOpMode);
133             sb.append("]");
134             return sb.toString();
135         }
136     }
137 
138     static final AppFilter FILTER_APP_SOURCES = new AppFilter() {
139 
140         @Override
141         public void init() {
142         }
143 
144         @Override
145         public boolean filterApp(AppEntry info) {
146             if (info.extraInfo == null || !(info.extraInfo instanceof InstallAppsState)) {
147                 return false;
148             }
149             InstallAppsState state = (InstallAppsState) info.extraInfo;
150             return state.isPotentialAppSource();
151         }
152     };
153 }
154