Merge "Remove try-catch from LocationPermissionChecker" am: 497afcff0c
Change-Id: Iba17dc925db09447e71e766c5fac254128f820d1
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
package com.android.internal.util;
|
package com.android.internal.util;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
|
import android.annotation.IntDef;
|
||||||
import android.annotation.Nullable;
|
import android.annotation.Nullable;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.AppOpsManager;
|
import android.app.AppOpsManager;
|
||||||
@@ -26,11 +27,13 @@ import android.location.LocationManager;
|
|||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.os.UserManager;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
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.
|
* The methods used for location permission and location mode checking.
|
||||||
@@ -41,17 +44,27 @@ public class LocationPermissionChecker {
|
|||||||
|
|
||||||
private static final String TAG = "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 Context mContext;
|
||||||
private final AppOpsManager mAppOpsManager;
|
private final AppOpsManager mAppOpsManager;
|
||||||
private final UserManager mUserManager;
|
|
||||||
private final LocationManager mLocationManager;
|
|
||||||
|
|
||||||
public LocationPermissionChecker(Context context) {
|
public LocationPermissionChecker(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
|
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
|
||||||
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
|
||||||
mLocationManager =
|
|
||||||
(LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,12 +84,38 @@ public class LocationPermissionChecker {
|
|||||||
*/
|
*/
|
||||||
public boolean checkLocationPermission(String pkgName, @Nullable String featureId,
|
public boolean checkLocationPermission(String pkgName, @Nullable String featureId,
|
||||||
int uid, @Nullable String message) {
|
int uid, @Nullable String message) {
|
||||||
try {
|
return checkLocationPermissionInternal(pkgName, featureId, uid, message) == SUCCEEDED;
|
||||||
enforceLocationPermission(pkgName, featureId, uid, message);
|
}
|
||||||
return true;
|
|
||||||
} catch (SecurityException e) {
|
/**
|
||||||
return false;
|
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -94,20 +133,32 @@ public class LocationPermissionChecker {
|
|||||||
*/
|
*/
|
||||||
public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
|
public void enforceLocationPermission(String pkgName, @Nullable String featureId, int uid,
|
||||||
@Nullable String message) throws SecurityException {
|
@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);
|
checkPackage(uid, pkgName);
|
||||||
|
|
||||||
// Location mode must be enabled
|
// Location mode must be enabled
|
||||||
if (!isLocationModeEnabled()) {
|
if (!isLocationModeEnabled()) {
|
||||||
throw new SecurityException("Location mode is disabled for the device");
|
return ERROR_LOCATION_MODE_OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
|
// LocationAccess by App: caller must have Coarse/Fine Location permission to have access to
|
||||||
// location information.
|
// location information.
|
||||||
if (!checkCallersLocationPermission(pkgName, featureId,
|
if (!checkCallersLocationPermission(pkgName, featureId, uid,
|
||||||
uid, /* coarseForTargetSdkLessThanQ */ true, message)) {
|
true /* coarseForTargetSdkLessThanQ */, message)) {
|
||||||
throw new SecurityException("UID " + uid + " has no location permission");
|
return ERROR_LOCATION_PERMISSION_MISSING;
|
||||||
}
|
}
|
||||||
|
return SUCCEEDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,8 +206,10 @@ public class LocationPermissionChecker {
|
|||||||
* Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
|
* Retrieves a handle to LocationManager (if not already done) and check if location is enabled.
|
||||||
*/
|
*/
|
||||||
public boolean isLocationModeEnabled() {
|
public boolean isLocationModeEnabled() {
|
||||||
|
final LocationManager LocationManager =
|
||||||
|
(LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
|
||||||
try {
|
try {
|
||||||
return mLocationManager.isLocationEnabledForUser(UserHandle.of(
|
return LocationManager.isLocationEnabledForUser(UserHandle.of(
|
||||||
getCurrentUser()));
|
getCurrentUser()));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
|
Log.e(TAG, "Failure to get location mode via API, falling back to settings", e);
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package com.android.internal.util;
|
package com.android.internal.util;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
@@ -120,6 +121,7 @@ public class LocationPermissionCheckerTest {
|
|||||||
private void setupTestCase() throws Exception {
|
private void setupTestCase() throws Exception {
|
||||||
setupMocks();
|
setupMocks();
|
||||||
setupMockInterface();
|
setupMockInterface();
|
||||||
|
mChecker = new LocationPermissionChecker(mMockContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTestVars() {
|
private void initTestVars() {
|
||||||
@@ -135,7 +137,6 @@ public class LocationPermissionCheckerTest {
|
|||||||
mFineLocationPermission = PackageManager.PERMISSION_DENIED;
|
mFineLocationPermission = PackageManager.PERMISSION_DENIED;
|
||||||
mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
|
mAllowCoarseLocationApps = AppOpsManager.MODE_ERRORED;
|
||||||
mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
|
mAllowFineLocationApps = AppOpsManager.MODE_ERRORED;
|
||||||
mChecker = new LocationPermissionChecker(mMockContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupMockInterface() {
|
private void setupMockInterface() {
|
||||||
@@ -179,7 +180,11 @@ public class LocationPermissionCheckerTest {
|
|||||||
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
|
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
|
||||||
mUid = mCurrentUser;
|
mUid = mCurrentUser;
|
||||||
setupTestCase();
|
setupTestCase();
|
||||||
mChecker.enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
|
||||||
|
final int result =
|
||||||
|
mChecker.checkLocationPermissionWithDetailInfo(
|
||||||
|
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
||||||
|
assertEquals(LocationPermissionChecker.SUCCEEDED, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -192,7 +197,11 @@ public class LocationPermissionCheckerTest {
|
|||||||
mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
|
mAllowFineLocationApps = AppOpsManager.MODE_ALLOWED;
|
||||||
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
|
mWifiScanAllowApps = AppOpsManager.MODE_ALLOWED;
|
||||||
setupTestCase();
|
setupTestCase();
|
||||||
mChecker.enforceLocationPermission(TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
|
||||||
|
final int result =
|
||||||
|
mChecker.checkLocationPermissionWithDetailInfo(
|
||||||
|
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
||||||
|
assertEquals(LocationPermissionChecker.SUCCEEDED, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -205,7 +214,7 @@ public class LocationPermissionCheckerTest {
|
|||||||
setupTestCase();
|
setupTestCase();
|
||||||
|
|
||||||
assertThrows(SecurityException.class,
|
assertThrows(SecurityException.class,
|
||||||
() -> mChecker.enforceLocationPermission(
|
() -> mChecker.checkLocationPermissionWithDetailInfo(
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
|
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,9 +223,11 @@ public class LocationPermissionCheckerTest {
|
|||||||
mThrowSecurityException = false;
|
mThrowSecurityException = false;
|
||||||
mIsLocationEnabled = true;
|
mIsLocationEnabled = true;
|
||||||
setupTestCase();
|
setupTestCase();
|
||||||
assertThrows(SecurityException.class,
|
|
||||||
() -> mChecker.enforceLocationPermission(
|
final int result =
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
|
mChecker.checkLocationPermissionWithDetailInfo(
|
||||||
|
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
||||||
|
assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -229,9 +240,10 @@ public class LocationPermissionCheckerTest {
|
|||||||
mUid = MANAGED_PROFILE_UID;
|
mUid = MANAGED_PROFILE_UID;
|
||||||
setupTestCase();
|
setupTestCase();
|
||||||
|
|
||||||
assertThrows(SecurityException.class,
|
final int result =
|
||||||
() -> mChecker.enforceLocationPermission(
|
mChecker.checkLocationPermissionWithDetailInfo(
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
|
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
||||||
|
assertEquals(LocationPermissionChecker.ERROR_LOCATION_PERMISSION_MISSING, result);
|
||||||
verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
|
verify(mMockAppOps, never()).noteOp(anyInt(), anyInt(), anyString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,9 +257,10 @@ public class LocationPermissionCheckerTest {
|
|||||||
|
|
||||||
setupTestCase();
|
setupTestCase();
|
||||||
|
|
||||||
assertThrows(SecurityException.class,
|
final int result =
|
||||||
() -> mChecker.enforceLocationPermission(
|
mChecker.checkLocationPermissionWithDetailInfo(
|
||||||
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null));
|
TEST_PKG_NAME, TEST_FEATURE_ID, mUid, null);
|
||||||
|
assertEquals(LocationPermissionChecker.ERROR_LOCATION_MODE_OFF, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
|
private static void assertThrows(Class<? extends Exception> exceptionClass, Runnable r) {
|
||||||
|
|||||||
Reference in New Issue
Block a user