diff options
author | Remi NGUYEN VAN <reminv@google.com> | 2021-03-04 17:27:32 +0900 |
---|---|---|
committer | Remi NGUYEN VAN <reminv@google.com> | 2021-03-11 20:08:06 +0900 |
commit | fd4a502dd32827389117ac4033a95c98cf9c6d53 (patch) | |
tree | 1ac806fe3d6007b207c198a8af267e2e464fa7c3 /common | |
parent | e454573fad28da75621a3055ff1b4634edf54561 (diff) | |
download | net-fd4a502dd32827389117ac4033a95c98cf9c6d53.tar.gz |
Move LocationPermissionChecker to libs/net
LocationPermissionChecker was written to be used by multiple
connectivity modules, so it belongs in the frameworks/libs/net library.
The file is moved as-is with minor modifications in the test to avoid
usage of the privileged ActivityManager.getCurrentUser API.
Bug: 181837977
Test: atest NetworkStaticLibTests
Change-Id: I80cff14c06c3cab3e8f3bd1978c2951c4a3317c6
Diffstat (limited to 'common')
3 files changed, 614 insertions, 1 deletions
diff --git a/common/framework/com/android/net/module/util/LocationPermissionChecker.java b/common/framework/com/android/net/module/util/LocationPermissionChecker.java new file mode 100644 index 00000000..e4ce9e80 --- /dev/null +++ b/common/framework/com/android/net/module/util/LocationPermissionChecker.java @@ -0,0 +1,306 @@ +/* + * 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.net.module.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) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + throw new UnsupportedOperationException("This utility is not supported before R"); + } + + mContext = context; + mAppOpsManager = mContext.getSystemService(AppOpsManager.class); + } + + /** + * 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 = mContext.getSystemService(LocationManager.class); + 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; + } + +} diff --git a/common/tests/unit/Android.bp b/common/tests/unit/Android.bp index 4ce4b0ea..45a89b0b 100644 --- a/common/tests/unit/Android.bp +++ b/common/tests/unit/Android.bp @@ -15,7 +15,7 @@ android_library { "androidx.test.rules", "mockito-target-extended-minus-junit4", "net-utils-device-common", - "net-tests-utils-host-device-common", + "net-tests-utils", ], libs: [ "android.test.runner", diff --git a/common/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java b/common/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java new file mode 100644 index 00000000..78a21a6c --- /dev/null +++ b/common/tests/unit/src/com/android/net/module/util/LocationPermissionCheckerTest.java @@ -0,0 +1,307 @@ +/* + * 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.net.module.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.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.Process; +import android.os.UserHandle; +import android.os.UserManager; + +import com.android.testutils.DevSdkIgnoreRule; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +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 { + @Rule + public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule( + Build.VERSION_CODES.Q /* ignoreClassUpTo */); + + // 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 mCurrentUid; + 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) { + @Override + protected int getCurrentUser() { + // Get the user ID of the process running the test rather than the foreground user + // id: ActivityManager.getCurrentUser() requires privileged permissions. + return UserHandle.getUserHandleForUid(Process.myUid()).getIdentifier(); + } + }; + } + + 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; + mCurrentUid = Process.myUid(); + 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 = mCurrentUid; + 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 = mCurrentUid; + 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)); + } + } +} |