/* * 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 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 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 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. *

* 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; } }