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.os.UserHandle;
26 import android.util.Log;
27 
28 import com.android.internal.util.ArrayUtils;
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, int userId)69     private boolean hasRequestedAppOpPermission(String permission, String packageName, int userId) {
70         try {
71             String[] packages = mIpm.getAppOpPermissionPackages(permission, userId);
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     public InstallAppsState createInstallAppsStateFor(String packageName, int uid) {
94         final InstallAppsState appState = new InstallAppsState();
95         final int userId = UserHandle.getUserId(uid);
96         appState.permissionRequested = hasRequestedAppOpPermission(
97                 Manifest.permission.REQUEST_INSTALL_PACKAGES, packageName, userId);
98         appState.appOpMode = getAppOpMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid,
99                 packageName);
100         return appState;
101     }
102 
103     /**
104      * Collection of information to be used as {@link AppEntry#extraInfo} objects
105      */
106     public static class InstallAppsState {
107         boolean permissionRequested;
108         int appOpMode;
109 
InstallAppsState()110         public InstallAppsState() {
111             this.appOpMode = AppOpsManager.MODE_DEFAULT;
112         }
113 
canInstallApps()114         public boolean canInstallApps() {
115             return appOpMode == AppOpsManager.MODE_ALLOWED;
116         }
117 
isPotentialAppSource()118         public boolean isPotentialAppSource() {
119             return appOpMode != AppOpsManager.MODE_DEFAULT || permissionRequested;
120         }
121 
122         @Override
toString()123         public String toString() {
124             StringBuilder sb = new StringBuilder();
125             sb.append("[permissionRequested: " + permissionRequested);
126             sb.append(", appOpMode: " + appOpMode);
127             sb.append("]");
128             return sb.toString();
129         }
130     }
131 
132     public static final AppFilter FILTER_APP_SOURCES = new AppFilter() {
133 
134         @Override
135         public void init() {
136         }
137 
138         @Override
139         public boolean filterApp(AppEntry info) {
140             if (info.extraInfo == null || !(info.extraInfo instanceof InstallAppsState)) {
141                 return false;
142             }
143             InstallAppsState state = (InstallAppsState) info.extraInfo;
144             return state.isPotentialAppSource();
145         }
146     };
147 }
148