blob: 5845f74809b132ee26028e5157ec039ecf27e490 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.wifi.util;
import android.Manifest;
import android.annotation.Nullable;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.location.LocationManager;
import android.net.NetworkStack;
import android.os.Binder;
import android.os.Build;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.modules.utils.build.SdkLevel;
import com.android.server.wifi.FrameworkFacade;
import com.android.server.wifi.WifiInjector;
import com.android.server.wifi.WifiLog;
import java.util.Arrays;
/**
* A wifi permissions utility assessing permissions
* for getting scan results by a package.
*/
public class WifiPermissionsUtil {
private static final String TAG = "WifiPermissionsUtil";
private static final int APP_INFO_FLAGS_SYSTEM_APP =
ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
private final WifiPermissionsWrapper mWifiPermissionsWrapper;
private final Context mContext;
private final FrameworkFacade mFrameworkFacade;
private final AppOpsManager mAppOps;
private final UserManager mUserManager;
private final Object mLock = new Object();
@GuardedBy("mLock")
private LocationManager mLocationManager;
private WifiLog mLog;
private boolean mVerboseLoggingEnabled;
public WifiPermissionsUtil(WifiPermissionsWrapper wifiPermissionsWrapper,
Context context, UserManager userManager, WifiInjector wifiInjector) {
mWifiPermissionsWrapper = wifiPermissionsWrapper;
mContext = context;
mFrameworkFacade = wifiInjector.getFrameworkFacade();
mUserManager = userManager;
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mLog = wifiInjector.makeLog(TAG);
}
/**
* Checks if the app has the permission to override Wi-Fi network configuration or not.
*
* @param uid uid of the app.
* @return true if the app does have the permission, false otherwise.
*/
public boolean checkConfigOverridePermission(int uid) {
int permission = mWifiPermissionsWrapper.getOverrideWifiConfigPermission(uid);
return permission == PackageManager.PERMISSION_GRANTED;
}
/**
* Check and enforce Coarse or Fine Location permission (depending on target SDK).
*
* @param pkgName PackageName of the application requesting access
* @param featureId The feature in the package
* @param uid The uid of the package
*/
public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid) {
if (!checkCallersLocationPermission(pkgName, featureId,
uid, /* coarseForTargetSdkLessThanQ */ true, null)) {
throw new SecurityException(
"UID " + uid + " does not have Coarse/Fine Location permission");
}
}
/**
* Checks whether than the target SDK of the package is less than the specified version code.
*/
public boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
long ident = Binder.clearCallingIdentity();
try {
final int targetSdkVersion;
if (SdkLevel.isAtLeastS()) {
// >= S, use the lightweight API to just get the target SDK version.
Context userContext = createPackageContextAsUser(callingUid);
if (userContext == null) return false;
targetSdkVersion = userContext.getPackageManager().getTargetSdkVersion(packageName);
} else {
// < S, use the heavyweight API to get all package info.
targetSdkVersion = mContext.getPackageManager().getApplicationInfoAsUser(
packageName, 0,
UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion;
}
return targetSdkVersion < versionCode;
} catch (PackageManager.NameNotFoundException e) {
// In case of exception, assume unknown app (more strict checking)
// Note: This case will never happen since checkPackage is
// called to verify validity before checking App's version.
return false;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
/**
* Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
* android.Manifest.permission.ACCESS_FINE_LOCATION (depending on config/targetSDK leve)
* and a corresponding app op is allowed for this package and uid.
*
* @param pkgName PackageName of the application requesting access
* @param featureId The feature in the package
* @param uid The uid of the package
* @param coarseForTargetSdkLessThanQ If true and the targetSDK < Q then will check for COARSE
* else (false or targetSDK >= Q) then will check for FINE
* @param message A message describing why the permission was checked. Only needed if this is
* not inside of a two-way binder call from the data receiver
*/
public boolean checkCallersLocationPermission(String pkgName, @Nullable String featureId,
int uid, boolean coarseForTargetSdkLessThanQ, @Nullable String message) {
boolean isTargetSdkLessThanQ = isTargetSdkLessThan(pkgName, Build.VERSION_CODES.Q, uid);
String permissionType = Manifest.permission.ACCESS_FINE_LOCATION;
if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
// Having FINE permission implies having COARSE permission (but not the reverse)
permissionType = Manifest.permission.ACCESS_COARSE_LOCATION;
}
if (mWifiPermissionsWrapper.getUidPermission(permissionType, uid)
== PackageManager.PERMISSION_DENIED) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): uid " + uid
+ " doesn't have permission " + permissionType);
}
return false;
}
// Always checking FINE - even if will not enforce. This will record the request for FINE
// so that a location request by the app is surfaced to the user.
boolean isFineLocationAllowed = noteAppOpAllowed(
AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid, message);
if (isFineLocationAllowed) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): ok because uid " + uid
+ " has app-op " + AppOpsManager.OPSTR_FINE_LOCATION);
}
return true;
}
if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
boolean allowed = noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName,
featureId, uid, message);
if (mVerboseLoggingEnabled) {
Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): returning " + allowed
+ " because uid " + uid + (allowed ? "has" : "doesn't have") + " app-op "
+ AppOpsManager.OPSTR_COARSE_LOCATION);
}
return allowed;
}
if (mVerboseLoggingEnabled) {
Log.v(TAG, "checkCallersLocationPermission(" + pkgName + "): returning false for " + uid
+ ": coarseForTargetSdkLessThanQ=" + coarseForTargetSdkLessThanQ
+ ", isTargetSdkLessThanQ=" + isTargetSdkLessThanQ);
}
return false;
}
/**
* Check and enforce Fine Location permission.
*
* @param pkgName PackageName of the application requesting access
* @param featureId The feature in the package
* @param uid The uid of the package
*/
public void enforceFineLocationPermission(String pkgName, @Nullable String featureId,
int uid) {
if (!checkCallersFineLocationPermission(pkgName, featureId, uid, false)) {
throw new SecurityException("UID " + uid + " does not have Fine Location permission");
}
}
/**
* Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION
* and a corresponding app op is allowed for this package and uid.
*
* @param pkgName PackageName of the application requesting access
* @param featureId The feature in the package
* @param uid The uid of the package
* @param hideFromAppOps True to invoke {@link AppOpsManager#checkOp(int, int, String)}, false
* to invoke {@link AppOpsManager#noteOp(String, int, String, String,
* String)}.
*/
private boolean checkCallersFineLocationPermission(String pkgName, @Nullable String featureId,
int uid, boolean hideFromAppOps) {
// Having FINE permission implies having COARSE permission (but not the reverse)
if (mWifiPermissionsWrapper.getUidPermission(
Manifest.permission.ACCESS_FINE_LOCATION, uid)
== PackageManager.PERMISSION_DENIED) {
return false;
}
if (hideFromAppOps) {
// Don't note the operation, just check if the app is allowed to perform the operation.
return checkAppOpAllowed(AppOpsManager.OPSTR_FINE_LOCATION, pkgName, uid);
} else {
return noteAppOpAllowed(AppOpsManager.OPSTR_FINE_LOCATION, pkgName, featureId, uid,
null);
}
}
/**
* Checks that calling process has android.Manifest.permission.LOCATION_HARDWARE.
*
* @param uid The uid of the package
*/
public boolean checkCallersHardwareLocationPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(Manifest.permission.LOCATION_HARDWARE, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* API to determine if the caller has permissions to get scan results. Throws SecurityException
* if the caller has no permission.
* @param pkgName package name of the application requesting access
* @param featureId The feature in the package
* @param uid The uid of the package
* @param message A message describing why the permission was checked. Only needed if this is
* not inside of a two-way binder call from the data receiver
*/
public void enforceCanAccessScanResults(String pkgName, @Nullable String featureId, int uid,
@Nullable String message)
throws SecurityException {
checkPackage(uid, pkgName);
// Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_MANAGED_PROVISIONING,
// NETWORK_STACK & MAINLINE_NETWORK_STACK, RADIO_SCAN_WITHOUT_LOCATION are granted a bypass.
if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
|| checkNetworkManagedProvisioningPermission(uid)
|| checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)
|| checkScanWithoutLocationPermission(uid)) {
return;
}
// Location mode must be enabled
if (!isLocationModeEnabled()) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
+ "location is disabled");
}
// Location mode is disabled, scan results cannot be returned
throw new SecurityException("Location mode is disabled for the device");
}
// Check if the calling Uid has CAN_READ_PEER_MAC_ADDRESS permission.
boolean canCallingUidAccessLocation = checkCallerHasPeersMacAddressPermission(uid);
// LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
// location information.
boolean canAppPackageUseLocation = checkCallersLocationPermission(pkgName, featureId,
uid, /* coarseForTargetSdkLessThanQ */ true, message);
// If neither caller or app has location access, there is no need to check
// any other permissions. Deny access to scan results.
if (!canCallingUidAccessLocation && !canAppPackageUseLocation) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
+ "canCallingUidAccessLocation=" + canCallingUidAccessLocation
+ ", canAppPackageUseLocation=" + canAppPackageUseLocation);
}
throw new SecurityException("UID " + uid + " has no location permission");
}
// Check if Wifi Scan request is an operation allowed for this App.
if (!isScanAllowedbyApps(pkgName, featureId, uid)) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
+ "doesn't have app-op " + AppOpsManager.OPSTR_WIFI_SCAN);
}
throw new SecurityException("UID " + uid + " has no wifi scan permission");
}
// If the User or profile is current, permission is granted
// Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
boolean isCurrentProfile = isCurrentProfile(uid);
if (!isCurrentProfile && !checkInteractAcrossUsersFull(uid)) {
if (mVerboseLoggingEnabled) {
Log.v(TAG, "enforceCanAccessScanResults(pkg=" + pkgName + ", uid=" + uid + "): "
+ "isCurrentProfile=" + isCurrentProfile
+ ", checkInteractAcrossUsersFull=" + checkInteractAcrossUsersFull(uid));
}
throw new SecurityException("UID " + uid + " profile not permitted");
}
}
/**
* API to determine if the caller has permissions to get scan results. Throws SecurityException
* if the caller has no permission.
* @param pkgName package name of the application requesting access
* @param featureId The feature in the package
* @param uid The uid of the package
* @param ignoreLocationSettings Whether this request can bypass location settings.
* @param hideFromAppOps Whether to note the request in app-ops logging or not.
*
* Note: This is to be used for checking permissions in the internal WifiScanner API surface
* for requests coming from system apps.
*/
public void enforceCanAccessScanResultsForWifiScanner(String pkgName,
@Nullable String featureId, int uid, boolean ignoreLocationSettings,
boolean hideFromAppOps) throws SecurityException {
checkPackage(uid, pkgName);
// Location mode must be enabled
if (!isLocationModeEnabled()) {
if (ignoreLocationSettings) {
mLog.w("Request from " + pkgName + " violated location settings");
} else {
// Location mode is disabled, scan results cannot be returned
throw new SecurityException("Location mode is disabled for the device");
}
}
// LocationAccess by App: caller must have fine & hardware Location permission to have
// access to location information.
if (!checkCallersFineLocationPermission(pkgName, featureId, uid, hideFromAppOps)
|| !checkCallersHardwareLocationPermission(uid)) {
throw new SecurityException("UID " + uid + " has no location permission");
}
// Check if Wifi Scan request is an operation allowed for this App.
if (!isScanAllowedbyApps(pkgName, featureId, uid)) {
throw new SecurityException("UID " + uid + " has no wifi scan permission");
}
}
/**
*
* Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION
* and a corresponding app op is allowed for this package and uid
*
* @param pkgName package name of the application requesting access
* @param featureId The feature in the package
* @param uid The uid of the package
* @param needLocationModeEnabled indicates location mode must be enabled.
*
* @return true if caller has permission, false otherwise
*/
public boolean checkCanAccessWifiDirect(String pkgName, @Nullable String featureId, int uid,
boolean needLocationModeEnabled) {
try {
checkPackage(uid, pkgName);
} catch (SecurityException se) {
Log.e(TAG, "Package check exception - " + se);
return false;
}
// Apps with NETWORK_SETTINGS are granted a bypass.
if (checkNetworkSettingsPermission(uid)) {
return true;
}
// Location mode must be enabled if needed.
if (needLocationModeEnabled && !isLocationModeEnabled()) {
Log.e(TAG, "Location mode is disabled for the device");
return false;
}
// LocationAccess by App: caller must have Fine Location permission to have access to
// location information.
if (!checkCallersLocationPermission(pkgName, featureId, uid,
/* coarseForTargetSdkLessThanQ */ false, null)) {
Log.e(TAG, "UID " + uid + " has no location permission");
return false;
}
return true;
}
/**
* API to validate if a package name belongs to a UID. Throws SecurityException
* if pkgName does not belongs to a UID
*
* @param pkgName package name of the application requesting access
* @param uid The uid of the package
*
*/
public void checkPackage(int uid, String pkgName) throws SecurityException {
if (pkgName == null) {
throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
}
mAppOps.checkPackage(uid, pkgName);
}
/**
* Returns true if the caller holds PEERS_MAC_ADDRESS permission.
*/
private boolean checkCallerHasPeersMacAddressPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.PEERS_MAC_ADDRESS, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if Wifi scan operation is allowed for this caller
* and package.
*/
private boolean isScanAllowedbyApps(String pkgName, @Nullable String featureId, int uid) {
return noteAppOpAllowed(AppOpsManager.OPSTR_WIFI_SCAN, pkgName, featureId, uid, null);
}
/**
* Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
*/
private boolean checkInteractAcrossUsersFull(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the calling user is the current one or a profile of the
* current user.
*/
private boolean isCurrentProfile(int uid) {
UserHandle currentUser = UserHandle.of(mWifiPermissionsWrapper.getCurrentUser());
UserHandle callingUser = UserHandle.getUserHandleForUid(uid);
return currentUser.equals(callingUser)
|| mUserManager.isSameProfileGroup(currentUser, callingUser);
}
private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
int uid, @Nullable String message) {
return mAppOps.noteOp(op, uid, pkgName, featureId, message) == AppOpsManager.MODE_ALLOWED;
}
private boolean checkAppOpAllowed(String op, String pkgName, int uid) {
return mAppOps.unsafeCheckOp(op, uid, pkgName) == AppOpsManager.MODE_ALLOWED;
}
private boolean retrieveLocationManagerIfNecessary() {
// This is going to be accessed by multiple threads.
synchronized (mLock) {
if (mLocationManager == null) {
mLocationManager =
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
}
}
return mLocationManager != null;
}
/**
* Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
*/
public boolean isLocationModeEnabled() {
if (!retrieveLocationManagerIfNecessary()) return false;
try {
return mLocationManager.isLocationEnabledForUser(UserHandle.of(
mWifiPermissionsWrapper.getCurrentUser()));
} catch (Exception e) {
Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
return mFrameworkFacade.getIntegerSetting(
mContext, Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF)
== Settings.Secure.LOCATION_MODE_ON;
}
}
/**
* Returns true if the |uid| holds NETWORK_SETTINGS permission.
*/
public boolean checkNetworkSettingsPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.NETWORK_SETTINGS, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the |uid| holds RADIO_SCAN_WITHOUT_LOCATION permission.
*/
public boolean checkScanWithoutLocationPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.RADIO_SCAN_WITHOUT_LOCATION, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the |uid| holds LOCAL_MAC_ADDRESS permission.
*/
public boolean checkLocalMacAddressPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.LOCAL_MAC_ADDRESS, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
*/
public boolean checkNetworkSetupWizardPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.NETWORK_SETUP_WIZARD, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the |uid| holds NETWORK_STACK permission.
*/
public boolean checkNetworkStackPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.NETWORK_STACK, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the |uid| holds MAINLINE_NETWORK_STACK permission.
*/
public boolean checkMainlineNetworkStackPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the |uid| holds NETWORK_MANAGED_PROVISIONING permission.
*/
public boolean checkNetworkManagedProvisioningPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.NETWORK_MANAGED_PROVISIONING, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the |uid| holds NETWORK_CARRIER_PROVISIONING permission.
*/
public boolean checkNetworkCarrierProvisioningPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.NETWORK_CARRIER_PROVISIONING, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the |uid| holds READ_WIFI_CREDENTIAL permission.
*/
public boolean checkReadWifiCredentialPermission(int uid) {
return mWifiPermissionsWrapper.getUidPermission(
android.Manifest.permission.READ_WIFI_CREDENTIAL, uid)
== PackageManager.PERMISSION_GRANTED;
}
/**
* Returns true if the |callingUid|/\callingPackage| holds SYSTEM_ALERT_WINDOW permission.
*/
public boolean checkSystemAlertWindowPermission(int callingUid, String callingPackage) {
final int mode = mAppOps.noteOp(AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW, callingUid,
callingPackage, null, null);
if (mode == AppOpsManager.MODE_DEFAULT) {
return mWifiPermissionsWrapper.getUidPermission(
Manifest.permission.SYSTEM_ALERT_WINDOW, callingUid)
== PackageManager.PERMISSION_GRANTED;
}
return mode == AppOpsManager.MODE_ALLOWED;
}
private static DevicePolicyManager retrieveDevicePolicyManagerFromContext(Context context) {
DevicePolicyManager devicePolicyManager =
context.getSystemService(DevicePolicyManager.class);
if (devicePolicyManager == null
&& context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_DEVICE_ADMIN)) {
Log.w(TAG, "Error retrieving DPM service");
}
return devicePolicyManager;
}
@Nullable
private Context createPackageContextAsUser(int uid) {
Context userContext = null;
try {
userContext = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,
UserHandle.getUserHandleForUid(uid));
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Unknown package name");
return null;
}
if (userContext == null) {
Log.e(TAG, "Unable to retrieve user context for " + uid);
return null;
}
return userContext;
}
private DevicePolicyManager retrieveDevicePolicyManagerFromUserContext(int uid) {
Context userContext = createPackageContextAsUser(uid);
if (userContext == null) return null;
return retrieveDevicePolicyManagerFromContext(userContext);
}
@Nullable
private Pair<UserHandle, ComponentName> getDeviceOwner() {
DevicePolicyManager devicePolicyManager =
retrieveDevicePolicyManagerFromContext(mContext);
if (devicePolicyManager == null) return null;
long ident = Binder.clearCallingIdentity();
UserHandle deviceOwnerUser = null;
ComponentName deviceOwnerComponent = null;
try {
deviceOwnerUser = devicePolicyManager.getDeviceOwnerUser();
deviceOwnerComponent = devicePolicyManager.getDeviceOwnerComponentOnAnyUser();
} finally {
Binder.restoreCallingIdentity(ident);
}
if (deviceOwnerUser == null || deviceOwnerComponent == null) return null;
if (deviceOwnerComponent.getPackageName() == null) {
// shouldn't happen
Log.wtf(TAG, "no package name on device owner component: " + deviceOwnerComponent);
return null;
}
return new Pair<>(deviceOwnerUser, deviceOwnerComponent);
}
/**
* Returns {@code true} if the calling {@code uid} and {@code packageName} is the device owner.
*/
public boolean isDeviceOwner(int uid, @Nullable String packageName) {
// Cannot determine if the app is DO/PO if packageName is null. So, will return false to be
// safe.
if (packageName == null) {
Log.e(TAG, "isDeviceOwner: packageName is null, returning false");
return false;
}
Pair<UserHandle, ComponentName> deviceOwner = getDeviceOwner();
if (mVerboseLoggingEnabled) Log.v(TAG, "deviceOwner:" + deviceOwner);
// no device owner
if (deviceOwner == null) return false;
return deviceOwner.first.equals(UserHandle.getUserHandleForUid(uid))
&& deviceOwner.second.getPackageName().equals(packageName);
}
/**
* Returns {@code true} if the calling {@code uid} is the device owner.
*/
public boolean isDeviceOwner(int uid) {
Pair<UserHandle, ComponentName> deviceOwner = getDeviceOwner();
// no device owner
if (deviceOwner == null) return false;
// device owner belowngs to wrong user
if (!deviceOwner.first.equals(UserHandle.getUserHandleForUid(uid))) return false;
// finally, check uid
String deviceOwnerPackageName = deviceOwner.second.getPackageName();
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
if (mVerboseLoggingEnabled) {
Log.v(TAG, "Packages for uid " + uid + ":" + Arrays.toString(packageNames));
}
if (packageNames == null) {
Log.w(TAG, "isDeviceOwner(): could not find packages for packageName="
+ deviceOwnerPackageName + " uid=" + uid);
return false;
}
for (String packageName : packageNames) {
if (deviceOwnerPackageName.equals(packageName)) return true;
}
return false;
}
/**
* Returns true if the |callingUid|/\callingPackage| is the profile owner.
*/
public boolean isProfileOwner(int uid, @Nullable String packageName) {
// Cannot determine if the app is DO/PO if packageName is null. So, will return false to be
// safe.
if (packageName == null) {
Log.e(TAG, "isProfileOwner: packageName is null, returning false");
return false;
}
DevicePolicyManager devicePolicyManager =
retrieveDevicePolicyManagerFromUserContext(uid);
if (devicePolicyManager == null) return false;
return devicePolicyManager.isProfileOwnerApp(packageName);
}
/** Helper method to check if the entity initiating the binder call is a system app. */
public boolean isSystem(String packageName, int uid) {
long ident = Binder.clearCallingIdentity();
try {
ApplicationInfo info = mContext.getPackageManager().getApplicationInfoAsUser(
packageName, 0, UserHandle.getUserHandleForUid(uid));
return (info.flags & APP_INFO_FLAGS_SYSTEM_APP) != 0;
} catch (PackageManager.NameNotFoundException e) {
// In case of exception, assume unknown app (more strict checking)
// Note: This case will never happen since checkPackage is
// called to verify validity before checking App's version.
} finally {
Binder.restoreCallingIdentity(ident);
}
return false;
}
/**
* Checks if the given UID belongs to the current foreground or device owner user. This is
* used to prevent apps running in background users from modifying network
* configurations.
* <p>
* UIDs belonging to system internals (such as SystemUI) are always allowed,
* since they always run as {@link UserHandle#USER_SYSTEM}.
*
* @param uid uid of the app.
* @return true if the given UID belongs to the current foreground user,
* otherwise false.
*/
public boolean doesUidBelongToCurrentUser(int uid) {
if (uid == android.os.Process.SYSTEM_UID
// UIDs with the NETWORK_SETTINGS permission are always allowed since they are
// acting on behalf of the user.
|| checkNetworkSettingsPermission(uid)) {
return true;
}
boolean isCurrentProfile = isCurrentProfile(uid);
if (!isCurrentProfile) {
// Fix for b/174749461
EventLog.writeEvent(0x534e4554, "174749461", -1,
"Non foreground user trying to modify wifi configuration");
}
return isCurrentProfile || isDeviceOwner(uid);
}
/**
* Sets the verbose logging level.
*/
public void enableVerboseLogging(boolean enabled) {
mVerboseLoggingEnabled = enabled;
}
}