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.appOpMode = getAppOpMode(AppOpsManager.OP_REQUEST_INSTALL_PACKAGES, uid, 98 packageName); 99 return appState; 100 } 101 102 /** 103 * Collection of information to be used as {@link AppEntry#extraInfo} objects 104 */ 105 public static class InstallAppsState { 106 boolean permissionRequested; 107 int appOpMode; 108 InstallAppsState()109 public InstallAppsState() { 110 this.appOpMode = AppOpsManager.MODE_DEFAULT; 111 } 112 canInstallApps()113 public boolean canInstallApps() { 114 return appOpMode == AppOpsManager.MODE_ALLOWED; 115 } 116 isPotentialAppSource()117 public boolean isPotentialAppSource() { 118 return appOpMode != AppOpsManager.MODE_DEFAULT || permissionRequested; 119 } 120 121 @Override toString()122 public String toString() { 123 StringBuilder sb = new StringBuilder(); 124 sb.append("[permissionRequested: " + permissionRequested); 125 sb.append(", appOpMode: " + appOpMode); 126 sb.append("]"); 127 return sb.toString(); 128 } 129 } 130 131 static final AppFilter FILTER_APP_SOURCES = new AppFilter() { 132 133 @Override 134 public void init() { 135 } 136 137 @Override 138 public boolean filterApp(AppEntry info) { 139 if (info.extraInfo == null || !(info.extraInfo instanceof InstallAppsState)) { 140 return false; 141 } 142 InstallAppsState state = (InstallAppsState) info.extraInfo; 143 return state.isPotentialAppSource(); 144 } 145 }; 146 } 147