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