WifiPermissionsUtil.java revision 868b692e6faa9ec3c8dd0cd42d4302082e28b992
1/* 2 * Copyright (C) 2016 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 17package com.android.server.wifi.util; 18 19import android.Manifest; 20import android.app.AppOpsManager; 21import android.content.Context; 22import android.content.pm.PackageManager; 23import android.content.pm.UserInfo; 24import android.os.UserManager; 25import android.provider.Settings; 26 27import com.android.server.wifi.WifiSettingsStore; 28 29import java.util.List; 30 31/** 32 * A wifi permissions utility assessing permissions 33 * for getting scan results by a package. 34 */ 35public class WifiPermissionsUtil { 36 private static final String TAG = "WifiPermissionsUtil"; 37 private final WifiPermissionsWrapper mWifiPermissionsWrapper; 38 private final Context mContext; 39 private final AppOpsManager mAppOps; 40 private final UserManager mUserManager; 41 private final WifiSettingsStore mSettingsStore; 42 43 public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper, 44 Context context, WifiSettingsStore settingsStore, UserManager userManager) { 45 mWifiPermissionsWrapper = wifiPermissionsWrapper; 46 mContext = context; 47 mUserManager = userManager; 48 mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 49 mSettingsStore = settingsStore; 50 } 51 52 /** 53 * API to determine if the caller has permissions to get 54 * scan results. 55 * @param pkgName Packagename of the application requesting access 56 * @param uid The uid of the package 57 * @param minVersion Minimum app API Version number to enforce location permission 58 * @return boolean true or false if permissions is granted 59 */ 60 public boolean canAccessScanResults(String pkgName, int uid, 61 int minVersion) throws SecurityException { 62 mAppOps.checkPackage(uid, pkgName); 63 // Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS 64 // permission or is an Active Nw scorer. 65 boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid) 66 || isCallerActiveNwScorer(uid); 67 // LocationAccess by App: For AppVersion older than minVersion, 68 // it is sufficient to check if the App is foreground. 69 // Otherwise, Location Mode must be enabled and caller must have 70 // Coarse Location permission to have access to location information. 71 boolean canAppPackageUseLocation = isLegacyForeground(pkgName, minVersion) 72 || (isLocationModeEnabled(pkgName) 73 && checkCallersLocationPermission(pkgName, uid)); 74 // If neither caller or app has location access, there is no need to check 75 // any other permissions. Deny access to scan results. 76 if (!canCallingUidAccessLocation && !canAppPackageUseLocation) { 77 return false; 78 } 79 // Check if Wifi Scan request is an operation allowed for this App. 80 if (!isScanAllowedbyApps(pkgName, uid)) { 81 return false; 82 } 83 // If the User or profile is current, permission is granted 84 // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission. 85 if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) { 86 return false; 87 } 88 return true; 89 } 90 91 /** 92 * Returns true if the caller holds PEERS_MAC_ADDRESS permission. 93 */ 94 private boolean checkCallerHasPeersMacAddressPermission(int uid) { 95 return mWifiPermissionsWrapper.getUidPermission( 96 android.Manifest.permission.PEERS_MAC_ADDRESS, uid) 97 == PackageManager.PERMISSION_GRANTED; 98 } 99 100 /** 101 * Returns true if the caller is an Active Network Scorer. 102 */ 103 private boolean isCallerActiveNwScorer(int uid) { 104 return mWifiPermissionsWrapper.isCallerActiveNwScorer(uid); 105 } 106 107 /** 108 * Returns true if Wifi scan operation is allowed for this caller 109 * and package. 110 */ 111 private boolean isScanAllowedbyApps(String pkgName, int uid) { 112 return checkAppOpAllowed(AppOpsManager.OP_WIFI_SCAN, pkgName, uid); 113 } 114 115 /** 116 * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL. 117 */ 118 private boolean checkInteractAcrossUsersFull(int uid) { 119 return mWifiPermissionsWrapper.getUidPermission( 120 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid) 121 == PackageManager.PERMISSION_GRANTED; 122 } 123 124 /** 125 * Returns true if the calling user is the current one or a profile of the 126 * current user. 127 */ 128 private boolean isCurrentProfile(int uid) { 129 int currentUser = mWifiPermissionsWrapper.getCurrentUser(); 130 int callingUserId = mWifiPermissionsWrapper.getCallingUserId(uid); 131 if (callingUserId == currentUser) { 132 return true; 133 } else { 134 List<UserInfo> userProfiles = mUserManager.getProfiles(currentUser); 135 for (UserInfo user: userProfiles) { 136 if (user.id == callingUserId) { 137 return true; 138 } 139 } 140 } 141 return false; 142 } 143 144 /** 145 * Returns true if the App version is older than minVersion. 146 */ 147 private boolean isLegacyVersion(String pkgName, int minVersion) { 148 try { 149 if (mContext.getPackageManager().getApplicationInfo(pkgName, 0) 150 .targetSdkVersion < minVersion) { 151 return true; 152 } 153 } catch (PackageManager.NameNotFoundException e) { 154 // In case of exception, assume known app (more strict checking) 155 // Note: This case will never happen since checkPackage is 156 // called to verify valididity before checking App's version. 157 } 158 return false; 159 } 160 161 private boolean checkAppOpAllowed(int op, String pkgName, int uid) { 162 return mAppOps.noteOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED; 163 } 164 165 private boolean isLegacyForeground(String pkgName, int version) { 166 return isLegacyVersion(pkgName, version) && isForegroundApp(pkgName); 167 } 168 169 private boolean isForegroundApp(String pkgName) { 170 return pkgName.equals(mWifiPermissionsWrapper.getTopPkgName()); 171 } 172 173 /** 174 * Checks that calling process has android.Manifest.permission.ACCESS_COARSE_LOCATION 175 * and a corresponding app op is allowed for this package and uid. 176 */ 177 private boolean checkCallersLocationPermission(String pkgName, int uid) { 178 // Coarse Permission implies Fine permission 179 if ((mWifiPermissionsWrapper.getUidPermission( 180 Manifest.permission.ACCESS_COARSE_LOCATION, uid) 181 == PackageManager.PERMISSION_GRANTED) 182 && checkAppOpAllowed(AppOpsManager.OP_COARSE_LOCATION, pkgName, uid)) { 183 return true; 184 } 185 return false; 186 } 187 private boolean isLocationModeEnabled(String pkgName) { 188 // Location mode check on applications that are later than version. 189 return (mSettingsStore.getLocationModeSetting(mContext) 190 != Settings.Secure.LOCATION_MODE_OFF); 191 } 192} 193