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