Merge "Move LocationPermissionChecker to libs/net" am: a89a2ae676 am: eb7e0ad2c1
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1618840 Change-Id: I01d73266a4b4923ab098bfb6f50155d90b1effec
This commit is contained in:
@@ -1,303 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 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.internal.util;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.annotation.IntDef;
|
|
||||||
import android.annotation.Nullable;
|
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.app.AppOpsManager;
|
|
||||||
import android.content.Context;
|
|
||||||
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.util.Log;
|
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The methods used for location permission and location mode checking.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public class LocationPermissionChecker {
|
|
||||||
|
|
||||||
private static final String TAG = "LocationPermissionChecker";
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
@IntDef(prefix = {"LOCATION_PERMISSION_CHECK_STATUS_"}, value = {
|
|
||||||
SUCCEEDED,
|
|
||||||
ERROR_LOCATION_MODE_OFF,
|
|
||||||
ERROR_LOCATION_PERMISSION_MISSING,
|
|
||||||
})
|
|
||||||
public @interface LocationPermissionCheckStatus{}
|
|
||||||
|
|
||||||
// The location permission check succeeded.
|
|
||||||
public static final int SUCCEEDED = 0;
|
|
||||||
// The location mode turns off for the caller.
|
|
||||||
public static final int ERROR_LOCATION_MODE_OFF = 1;
|
|
||||||
// The location permission isn't granted for the caller.
|
|
||||||
public static final int ERROR_LOCATION_PERMISSION_MISSING = 2;
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final AppOpsManager mAppOpsManager;
|
|
||||||
|
|
||||||
public LocationPermissionChecker(Context context) {
|
|
||||||
mContext = context;
|
|
||||||
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check location permission granted by the caller.
|
|
||||||
*
|
|
||||||
* This API check if the location mode enabled for the caller and the caller has
|
|
||||||
* ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*
|
|
||||||
* @return {@code true} returns if the caller has location permission and the location mode is
|
|
||||||
* enabled.
|
|
||||||
*/
|
|
||||||
public boolean checkLocationPermission(String pkgName, @Nullable String featureId,
|
|
||||||
int uid, @Nullable String message) {
|
|
||||||
return checkLocationPermissionInternal(pkgName, featureId, uid, message) == SUCCEEDED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check location permission granted by the caller.
|
|
||||||
*
|
|
||||||
* This API check if the location mode enabled for the caller and the caller has
|
|
||||||
* ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
|
|
||||||
* Compared with {@link #checkLocationPermission(String, String, int, String)}, this API returns
|
|
||||||
* the detail information about the checking result, including the reason why it's failed and
|
|
||||||
* logs the error for the caller.
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*
|
|
||||||
* @return {@link LocationPermissionCheckStatus} the result of the location permission check.
|
|
||||||
*/
|
|
||||||
public @LocationPermissionCheckStatus int checkLocationPermissionWithDetailInfo(
|
|
||||||
String pkgName, @Nullable String featureId, int uid, @Nullable String message) {
|
|
||||||
final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
|
|
||||||
switch (result) {
|
|
||||||
case ERROR_LOCATION_MODE_OFF:
|
|
||||||
Log.e(TAG, "Location mode is disabled for the device");
|
|
||||||
break;
|
|
||||||
case ERROR_LOCATION_PERMISSION_MISSING:
|
|
||||||
Log.e(TAG, "UID " + uid + " has no location permission");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enforce the caller has location permission.
|
|
||||||
*
|
|
||||||
* This API determines if the location mode enabled for the caller and the caller has
|
|
||||||
* ACCESS_COARSE_LOCATION permission is targetSDK<29, otherwise, has ACCESS_FINE_LOCATION.
|
|
||||||
* SecurityException is thrown if the caller has no permission or the location mode is disabled.
|
|
||||||
*
|
|
||||||
* @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 enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
|
|
||||||
@Nullable String message) throws SecurityException {
|
|
||||||
final int result = checkLocationPermissionInternal(pkgName, featureId, uid, message);
|
|
||||||
|
|
||||||
switch (result) {
|
|
||||||
case ERROR_LOCATION_MODE_OFF:
|
|
||||||
throw new SecurityException("Location mode is disabled for the device");
|
|
||||||
case ERROR_LOCATION_PERMISSION_MISSING:
|
|
||||||
throw new SecurityException("UID " + uid + " has no location permission");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int checkLocationPermissionInternal(String pkgName, @Nullable String featureId,
|
|
||||||
int uid, @Nullable String message) {
|
|
||||||
checkPackage(uid, pkgName);
|
|
||||||
|
|
||||||
// Apps with NETWORK_SETTINGS, NETWORK_SETUP_WIZARD, NETWORK_STACK & MAINLINE_NETWORK_STACK
|
|
||||||
// are granted a bypass.
|
|
||||||
if (checkNetworkSettingsPermission(uid) || checkNetworkSetupWizardPermission(uid)
|
|
||||||
|| checkNetworkStackPermission(uid) || checkMainlineNetworkStackPermission(uid)) {
|
|
||||||
return SUCCEEDED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Location mode must be enabled
|
|
||||||
if (!isLocationModeEnabled()) {
|
|
||||||
return ERROR_LOCATION_MODE_OFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
|
|
||||||
// location information.
|
|
||||||
if (!checkCallersLocationPermission(pkgName, featureId, uid,
|
|
||||||
true /* coarseForTargetSdkLessThanQ */, message)) {
|
|
||||||
return ERROR_LOCATION_PERMISSION_MISSING;
|
|
||||||
}
|
|
||||||
return SUCCEEDED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks that calling process has android.Manifest.permission.ACCESS_FINE_LOCATION or
|
|
||||||
* android.Manifest.permission.ACCESS_COARSE_LOCATION (depending on config/targetSDK level)
|
|
||||||
* 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 (getUidPermission(permissionType, uid) == PackageManager.PERMISSION_DENIED) {
|
|
||||||
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) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (coarseForTargetSdkLessThanQ && isTargetSdkLessThanQ) {
|
|
||||||
return noteAppOpAllowed(AppOpsManager.OPSTR_COARSE_LOCATION, pkgName, featureId, uid,
|
|
||||||
message);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
|
|
||||||
*/
|
|
||||||
public boolean isLocationModeEnabled() {
|
|
||||||
final LocationManager LocationManager =
|
|
||||||
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
|
|
||||||
try {
|
|
||||||
return LocationManager.isLocationEnabledForUser(UserHandle.of(
|
|
||||||
getCurrentUser()));
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isTargetSdkLessThan(String packageName, int versionCode, int callingUid) {
|
|
||||||
final long ident = Binder.clearCallingIdentity();
|
|
||||||
try {
|
|
||||||
if (mContext.getPackageManager().getApplicationInfoAsUser(
|
|
||||||
packageName, 0,
|
|
||||||
UserHandle.getUserHandleForUid(callingUid)).targetSdkVersion
|
|
||||||
< versionCode) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean noteAppOpAllowed(String op, String pkgName, @Nullable String featureId,
|
|
||||||
int uid, @Nullable String message) {
|
|
||||||
return mAppOpsManager.noteOp(op, uid, pkgName, featureId, message)
|
|
||||||
== AppOpsManager.MODE_ALLOWED;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void checkPackage(int uid, String pkgName)
|
|
||||||
throws SecurityException {
|
|
||||||
if (pkgName == null) {
|
|
||||||
throw new SecurityException("Checking UID " + uid + " but Package Name is Null");
|
|
||||||
}
|
|
||||||
mAppOpsManager.checkPackage(uid, pkgName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@VisibleForTesting
|
|
||||||
protected int getCurrentUser() {
|
|
||||||
return ActivityManager.getCurrentUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getUidPermission(String permissionType, int uid) {
|
|
||||||
// We don't care about pid, pass in -1
|
|
||||||
return mContext.checkPermission(permissionType, -1, uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the |uid| holds NETWORK_SETTINGS permission.
|
|
||||||
*/
|
|
||||||
public boolean checkNetworkSettingsPermission(int uid) {
|
|
||||||
return getUidPermission(android.Manifest.permission.NETWORK_SETTINGS, uid)
|
|
||||||
== PackageManager.PERMISSION_GRANTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the |uid| holds NETWORK_SETUP_WIZARD permission.
|
|
||||||
*/
|
|
||||||
public boolean checkNetworkSetupWizardPermission(int uid) {
|
|
||||||
return 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 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 getUidPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, uid)
|
|
||||||
== PackageManager.PERMISSION_GRANTED;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,296 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2020 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.internal.util;
|
|
||||||
|
|
||||||
import static android.Manifest.permission.NETWORK_SETTINGS;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
|
||||||
import static org.mockito.Mockito.doAnswer;
|
|
||||||
import static org.mockito.Mockito.doThrow;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.ActivityManager;
|
|
||||||
import android.app.AppOpsManager;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.pm.ApplicationInfo;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.location.LocationManager;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.os.UserManager;
|
|
||||||
|
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
/** Unit tests for {@link LocationPermissionChecker}. */
|
|
||||||
public class LocationPermissionCheckerTest {
|
|
||||||
|
|
||||||
public static final String TAG = "ConnectivityUtilTest";
|
|
||||||
|
|
||||||
// Mock objects for testing
|
|
||||||
@Mock private Context mMockContext;
|
|
||||||
@Mock private PackageManager mMockPkgMgr;
|
|
||||||
@Mock private ApplicationInfo mMockApplInfo;
|
|
||||||
@Mock private AppOpsManager mMockAppOps;
|
|
||||||
@Mock private UserManager mMockUserManager;
|
|
||||||
@Mock private LocationManager mLocationManager;
|
|
||||||
|
|
||||||
private static final String TEST_PKG_NAME = "com.google.somePackage";
|
|
||||||
private static final String TEST_FEATURE_ID = "com.google.someFeature";
|
|
||||||
private static final int MANAGED_PROFILE_UID = 1100000;
|
|
||||||
private static final int OTHER_USER_UID = 1200000;
|
|
||||||
|
|
||||||
private final String mInteractAcrossUsersFullPermission =
|
|
||||||
"android.permission.INTERACT_ACROSS_USERS_FULL";
|
|
||||||
private final String mManifestStringCoarse =
|
|
||||||
Manifest.permission.ACCESS_COARSE_LOCATION;
|
|
||||||
private final String mManifestStringFine =
|
|
||||||
Manifest.permission.ACCESS_FINE_LOCATION;
|
|
||||||
|
|
||||||
// Test variables
|
|
||||||
private int mWifiScanAllowApps;
|
|
||||||
private int mUid;
|
|
||||||
private int mCoarseLocationPermission;
|
|
||||||
private int mAllowCoarseLocationApps;
|
|
||||||
private int mFineLocationPermission;
|
|
||||||
private int mAllowFineLocationApps;
|
|
||||||
private int mNetworkSettingsPermission;
|
|
||||||
private int mCurrentUser;
|
|
||||||
private boolean mIsLocationEnabled;
|
|
||||||
private boolean mThrowSecurityException;
|
|
||||||
private Answer<Integer> mReturnPermission;
|
|
||||||
private HashMap<String, Integer> mPermissionsList = new HashMap<String, Integer>();
|
|
||||||
private LocationPermissionChecker mChecker;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
MockitoAnnotations.initMocks(this);
|
|
||||||
initTestVars();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupMocks() throws Exception {
|
|
||||||
when(mMockPkgMgr.getApplicationInfoAsUser(eq(TEST_PKG_NAME), eq(0), any()))
|
|
||||||
.thenReturn(mMockApplInfo);
|
|
||||||
when(mMockContext.getPackageManager()).thenReturn(mMockPkgMgr);
|
|
||||||
when(mMockAppOps.noteOp(AppOpsManager.OPSTR_WIFI_SCAN, mUid, TEST_PKG_NAME,
|
|
||||||
TEST_FEATURE_ID, null)).thenReturn(mWifiScanAllowApps);
|
|
||||||
when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_COARSE_LOCATION), eq(mUid),
|
|
||||||
eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
|
|
||||||
.thenReturn(mAllowCoarseLocationApps);
|
|
||||||
when(mMockAppOps.noteOp(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(mUid),
|
|
||||||
eq(TEST_PKG_NAME), eq(TEST_FEATURE_ID), nullable(String.class)))
|
|
||||||
.thenReturn(mAllowFineLocationApps);
|
|
||||||
if (mThrowSecurityException) {
|
|
||||||
doThrow(new SecurityException("Package " + TEST_PKG_NAME + " doesn't belong"
|
|
||||||
+ " to application bound to user " + mUid))
|
|
||||||
.when(mMockAppOps).checkPackage(mUid, TEST_PKG_NAME);
|
|
||||||
}
|
|
||||||
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE))
|
|
||||||
.thenReturn(mMockAppOps);
|
|
||||||
when(mMockContext.getSystemService(Context.USER_SERVICE))
|
|
||||||
.thenReturn(mMockUserManager);
|
|
||||||
when(mMockContext.getSystemService(Context.LOCATION_SERVICE)).thenReturn(mLocationManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupTestCase() throws Exception {
|
|
||||||
setupMocks();
|
|
||||||
setupMockInterface();
|
|
||||||
mChecker = new LocationPermissionChecker(mMockContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initTestVars() {
|
|
||||||
mPermissionsList.clear();
|
|
||||||
mReturnPermission = createPermissionAnswer();
|
|
||||||
mWifiScanAllowApps = AppOpsManager.MODE_ERRORED;
|
|
||||||
mUid = OTHER_USER_UID;
|
|
||||||
mThrowSecurityException = true;
|
|
||||||
mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.M;
|
|
||||||
mIsLocationEnabled = false;
|
|
||||||
mCurrentUser = ActivityManager.getCurrentUser();
|
|
||||||
mCoarseLocationPermission = PackageManager.PERMISSION_DENIED;
|
|
||||||
mFineLocationPermission = PackageManager.PERMISSION_DENIED;
|
|
||||||
mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
|
|
||||||
mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
|
|
||||||
mNetworkSettingsPermission = PackageManager.PERMISSION_DENIED;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupMockInterface() {
|
|
||||||
Binder.restoreCallingIdentity((((long) mUid) << 32) | Binder.getCallingPid());
|
|
||||||
doAnswer(mReturnPermission).when(mMockContext).checkPermission(
|
|
||||||
anyString(), anyInt(), anyInt());
|
|
||||||
when(mMockUserManager.isSameProfileGroup(UserHandle.SYSTEM,
|
|
||||||
UserHandle.getUserHandleForUid(MANAGED_PROFILE_UID)))
|
|
||||||
.thenReturn(true);
|
|
||||||
when(mMockContext.checkPermission(mManifestStringCoarse, -1, mUid))
|
|
||||||
.thenReturn(mCoarseLocationPermission);
|
|
||||||
when(mMockContext.checkPermission(mManifestStringFine, -1, mUid))
|
|
||||||
.thenReturn(mFineLocationPermission);
|
|
||||||
when(mMockContext.checkPermission(NETWORK_SETTINGS, -1, mUid))
|
|
||||||
.thenReturn(mNetworkSettingsPermission);
|
|
||||||
when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(mIsLocationEnabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Answer<Integer> createPermissionAnswer() {
|
|
||||||
return new Answer<Integer>() {
|
|
||||||
@Override
|
|
||||||
public Integer answer(InvocationOnMock invocation) {
|
|
||||||
int myUid = (int) invocation.getArguments()[1];
|
|
||||||
String myPermission = (String) invocation.getArguments()[0];
|
|
||||||
mPermissionsList.get(myPermission);
|
|
||||||
if (mPermissionsList.containsKey(myPermission)) {
|
|
||||||
int uid = mPermissionsList.get(myPermission);
|
|
||||||
if (myUid == uid) {
|
|
||||||
return PackageManager.PERMISSION_GRANTED;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return PackageManager.PERMISSION_DENIED;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEnforceLocationPermission_HasAllPermissions_BeforeQ() throws Exception {
|
|
||||||
mIsLocationEnabled = true;
|
|
||||||
mThrowSecurityException = false;
|
|
||||||
mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
|
|
||||||
mAllowCoarseLocationApps = AppOpsManager.MODE_ALLOWED;
|
|
||||||
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
|
|
||||||
mUid = mCurrentUser;
|
|
||||||
setupTestCase();
|
|
||||||
|
|
||||||
final int result =
|
|
||||||
mChecker.checkLocationPermissionWithDetailInfo(
|
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
|
||||||
assertEquals(LocationPermissionChecker.SUCCEEDED, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEnforceLocationPermission_HasAllPermissions_AfterQ() throws Exception {
|
|
||||||
mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
|
|
||||||
mIsLocationEnabled = true;
|
|
||||||
mThrowSecurityException = false;
|
|
||||||
mUid = mCurrentUser;
|
|
||||||
mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
|
|
||||||
mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
|
|
||||||
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
|
|
||||||
setupTestCase();
|
|
||||||
|
|
||||||
final int result =
|
|
||||||
mChecker.checkLocationPermissionWithDetailInfo(
|
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
|
||||||
assertEquals(LocationPermissionChecker.SUCCEEDED, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testEnforceLocationPermission_PkgNameAndUidMismatch() throws Exception {
|
|
||||||
mThrowSecurityException = true;
|
|
||||||
mIsLocationEnabled = true;
|
|
||||||
mFineLocationPermission = PackageManager.PERMISSION_GRANTED;
|
|
||||||
mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
|
|
||||||
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
|
|
||||||
setupTestCase();
|
|
||||||
|
|
||||||
assertThrows(SecurityException.class,
|
|
||||||
() -> mChecker.checkLocationPermissionWithDetailInfo(
|
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testenforceCanAccessScanResults_NoCoarseLocationPermission() throws Exception {
|
|
||||||
mThrowSecurityException = false;
|
|
||||||
mIsLocationEnabled = true;
|
|
||||||
setupTestCase();
|
|
||||||
|
|
||||||
final int result =
|
|
||||||
mChecker.checkLocationPermissionWithDetailInfo(
|
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
|
||||||
assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testenforceCanAccessScanResults_NoFineLocationPermission() throws Exception {
|
|
||||||
mThrowSecurityException = false;
|
|
||||||
mMockApplInfo.targetSdkVersion = Build.VERSION_CODES.Q;
|
|
||||||
mIsLocationEnabled = true;
|
|
||||||
mCoarseLocationPermission = PackageManager.PERMISSION_GRANTED;
|
|
||||||
mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
|
|
||||||
mUid = MANAGED_PROFILE_UID;
|
|
||||||
setupTestCase();
|
|
||||||
|
|
||||||
final int result =
|
|
||||||
mChecker.checkLocationPermissionWithDetailInfo(
|
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
|
||||||
assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
|
|
||||||
verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testenforceCanAccessScanResults_LocationModeDisabled() throws Exception {
|
|
||||||
mThrowSecurityException = false;
|
|
||||||
mUid = MANAGED_PROFILE_UID;
|
|
||||||
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
|
|
||||||
mPermissionsList.put(mInteractAcrossUsersFullPermission, mUid);
|
|
||||||
mIsLocationEnabled = false;
|
|
||||||
|
|
||||||
setupTestCase();
|
|
||||||
|
|
||||||
final int result =
|
|
||||||
mChecker.checkLocationPermissionWithDetailInfo(
|
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
|
||||||
assertEquals(LocationPermissionChecker.ERROR_LOCATION_MODE_OFF, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testenforceCanAccessScanResults_LocationModeDisabledHasNetworkSettings()
|
|
||||||
throws Exception {
|
|
||||||
mThrowSecurityException = false;
|
|
||||||
mIsLocationEnabled = false;
|
|
||||||
mNetworkSettingsPermission = PackageManager.PERMISSION_GRANTED;
|
|
||||||
setupTestCase();
|
|
||||||
|
|
||||||
final int result =
|
|
||||||
mChecker.checkLocationPermissionWithDetailInfo(
|
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
|
||||||
assertEquals(LocationPermissionChecker.SUCCEEDED, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
|
|
||||||
try {
|
|
||||||
r.run();
|
|
||||||
Assert.fail("Expected " + exceptionClass + " to be thrown.");
|
|
||||||
} catch (Exception exception) {
|
|
||||||
assertTrue(exceptionClass.isInstance(exception));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -193,13 +193,13 @@ import com.android.connectivity.aidl.INetworkAgent;
|
|||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
import com.android.internal.util.IndentingPrintWriter;
|
||||||
import com.android.internal.util.LocationPermissionChecker;
|
|
||||||
import com.android.internal.util.MessageUtils;
|
import com.android.internal.util.MessageUtils;
|
||||||
import com.android.modules.utils.BasicShellCommandHandler;
|
import com.android.modules.utils.BasicShellCommandHandler;
|
||||||
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
|
import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
|
||||||
import com.android.net.module.util.CollectionUtils;
|
import com.android.net.module.util.CollectionUtils;
|
||||||
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
|
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
|
||||||
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
|
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
|
||||||
|
import com.android.net.module.util.LocationPermissionChecker;
|
||||||
import com.android.net.module.util.NetworkCapabilitiesUtils;
|
import com.android.net.module.util.NetworkCapabilitiesUtils;
|
||||||
import com.android.net.module.util.PermissionUtils;
|
import com.android.net.module.util.PermissionUtils;
|
||||||
import com.android.server.connectivity.AutodestructReference;
|
import com.android.server.connectivity.AutodestructReference;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ import android.util.Slog;
|
|||||||
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.annotations.GuardedBy;
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
import com.android.internal.annotations.VisibleForTesting.Visibility;
|
||||||
import com.android.internal.util.LocationPermissionChecker;
|
import com.android.net.module.util.LocationPermissionChecker;
|
||||||
import com.android.server.vcn.TelephonySubscriptionTracker;
|
import com.android.server.vcn.TelephonySubscriptionTracker;
|
||||||
import com.android.server.vcn.Vcn;
|
import com.android.server.vcn.Vcn;
|
||||||
import com.android.server.vcn.VcnContext;
|
import com.android.server.vcn.VcnContext;
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ import android.telephony.TelephonyManager;
|
|||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
import com.android.internal.util.LocationPermissionChecker;
|
import com.android.net.module.util.LocationPermissionChecker;
|
||||||
import com.android.server.VcnManagementService.VcnCallback;
|
import com.android.server.VcnManagementService.VcnCallback;
|
||||||
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
|
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
|
||||||
import com.android.server.vcn.TelephonySubscriptionTracker;
|
import com.android.server.vcn.TelephonySubscriptionTracker;
|
||||||
|
|||||||
Reference in New Issue
Block a user