summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-01-16 01:14:53 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-01-16 01:14:53 +0000
commita7a2825aeb098462c0484efa8387092f85f4b110 (patch)
tree7ecb66c281a4b7cf117d18e3d30433c00860ab15 /core/java
parent10cf15da5bed8fe962ce8a9655699d946a62b367 (diff)
parentb896ed6e19c22c04b5b8130f1511b700f349c4d7 (diff)
Merge "Create an until function to check location permission" am: 948c4086af am: b896ed6e19
Change-Id: Ic868fbfa4d9b9199e754c5b6aacf97990245e95e
Diffstat (limited to 'core/java')
-rw-r--r--core/java/com/android/internal/util/ConnectivityUtil.java202
1 files changed, 202 insertions, 0 deletions
diff --git a/core/java/com/android/internal/util/ConnectivityUtil.java b/core/java/com/android/internal/util/ConnectivityUtil.java
new file mode 100644
index 000000000000..b1d4fa0d3fd3
--- /dev/null
+++ b/core/java/com/android/internal/util/ConnectivityUtil.java
@@ -0,0 +1,202 @@
+/*
+ * 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.Nullable;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+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 android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+
+/**
+ * Utility methods for common functionality using by different networks.
+ *
+ * @hide
+ */
+public class ConnectivityUtil {
+
+ private static final String TAG = "ConnectivityUtil";
+
+ private final Context mContext;
+ private final AppOpsManager mAppOps;
+ private final UserManager mUserManager;
+
+ public ConnectivityUtil(Context context) {
+ mContext = context;
+ mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ }
+
+ /**
+ * API to determine if the caller has fine/coarse location permission (depending on
+ * config/targetSDK level) and the location mode is enabled for the user. 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 {
+ checkPackage(uid, pkgName);
+
+ // Location mode must be enabled
+ if (!isLocationModeEnabled()) {
+ // 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 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 (!canAppPackageUseLocation) {
+ throw new SecurityException("UID " + uid + " has no location permission");
+ }
+ // If the User or profile is current, permission is granted
+ // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+ if (!isCurrentProfile(uid) && !checkInteractAcrossUsersFull(uid)) {
+ throw new SecurityException("UID " + uid + " profile not permitted");
+ }
+ }
+
+ /**
+ * 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() {
+ 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) {
+ 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 mAppOps.noteOp(op, uid, pkgName) == 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");
+ }
+ mAppOps.checkPackage(uid, pkgName);
+ }
+
+ private boolean isCurrentProfile(int uid) {
+ UserHandle currentUser = UserHandle.of(getCurrentUser());
+ UserHandle callingUser = UserHandle.getUserHandleForUid(uid);
+ return currentUser.equals(callingUser)
+ || mUserManager.isSameProfileGroup(
+ currentUser.getIdentifier(), callingUser.getIdentifier());
+ }
+
+ private boolean checkInteractAcrossUsersFull(int uid) {
+ return getUidPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ @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);
+ }
+}