Revert "Properly protect cell location."
This reverts commit d9cc11475bf9033090db4476a589459e1576a130.
bug:38489777
Change-Id: Ib34bfa095fe0ace0deded3ded0ee92d5713147f2
diff --git a/src/com/android/phone/LocationAccessPolicy.java b/src/com/android/phone/LocationAccessPolicy.java
deleted file mode 100644
index 9f863a3..0000000
--- a/src/com/android/phone/LocationAccessPolicy.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (C) 2017 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.phone;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.Build;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import java.util.List;
-
-/**
- * Helper for performing location access checks.
- */
-final class LocationAccessPolicy {
-
- private LocationAccessPolicy() {
- /* do nothing - hide ctor */
- }
-
- /**
- * API to determine if the caller has permissions to get cell location.
- *
- * @param pkgName Package name of the application requesting access
- * @param uid The uid of the package
- * @return boolean true or false if permissions is granted
- */
- static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
- int uid) throws SecurityException {
- context.getSystemService(AppOpsManager.class).checkPackage(uid, pkgName);
- // We always require the location permission and also require the
- // location mode to be on for non-legacy apps. Legacy apps are
- // required to be in the foreground to at least mitigate the case
- // where a legacy app the user is not using tracks their location.
- if (!hasUidLocationPermission(context, pkgName, uid)
- || (!isLocationModeEnabled(context, UserHandle.getUserId(uid)))
- && !isLegacyForeground(context, pkgName)) {
- return false;
- }
- // If the user or profile is current, permission is granted.
- // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
- return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
- }
-
- private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
- return Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId)
- != Settings.Secure.LOCATION_MODE_OFF;
- }
-
- private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName) {
- return isLegacyVersion(context, pkgName) && isForegroundApp(context, pkgName);
- }
-
- private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
- try {
- if (context.getPackageManager().getApplicationInfo(pkgName, 0)
- .targetSdkVersion <= Build.VERSION_CODES.O) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- // In case of exception, assume known app (more strict checking)
- // Note: This case will never happen since checkPackage is
- // called to verify validity before checking app's version.
- }
- return false;
- }
-
- private static boolean isForegroundApp(@NonNull Context context, @NonNull String pkgName) {
- final ActivityManager am = context.getSystemService(ActivityManager.class);
- final List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
- if (!tasks.isEmpty()) {
- return pkgName.equals(tasks.get(0).topActivity.getPackageName());
- }
- return false;
- }
-
- private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
- return context.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- private static boolean hasUidLocationPermission(@NonNull Context context,
- @NonNull String pkgName, int uid) {
- // Grating ACCESS_FINE_LOCATION to an app automatically grants it ACCESS_COARSE_LOCATION.
- if ((context.checkCallingOrSelfPermission(
- Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED)) {
- final int opCode = AppOpsManager.permissionToOpCode(
- Manifest.permission.ACCESS_COARSE_LOCATION);
- if (opCode != AppOpsManager.OP_NONE) {
- return context.getSystemService(AppOpsManager.class).noteOp(opCode, uid, pkgName)
- == AppOpsManager.MODE_ALLOWED;
- }
- }
- return false;
- }
-
- private static boolean isCurrentProfile(@NonNull Context context, int uid) {
- final int currentUser = ActivityManager.getCurrentUser();
- final int callingUserId = UserHandle.getUserId(uid);
- if (callingUserId == currentUser) {
- return true;
- } else {
- List<UserInfo> userProfiles = context.getSystemService(
- UserManager.class).getProfiles(currentUser);
- for (UserInfo user: userProfiles) {
- if (user.id == callingUserId) {
- return true;
- }
- }
- }
- return false;
- }
-}
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 9752502..92da36f 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -19,6 +19,7 @@
import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
import android.Manifest.permission;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -34,6 +35,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.Process;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -1627,23 +1629,47 @@
@Override
public Bundle getCellLocation(String callingPackage) {
- if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
- callingPackage, Binder.getCallingUid())) {
+ enforceFineOrCoarseLocationPermission("getCellLocation");
+
+ // OP_COARSE_LOCATION controls both fine and coarse location.
+ if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ log("getCellLocation: returning null; mode != allowed");
return null;
}
- if (DBG_LOC) log("getCellLocation: is active user");
- Bundle data = new Bundle();
- Phone phone = getPhone(mSubscriptionController.getDefaultDataSubId());
- if (phone == null) {
+ if (checkIfCallerIsSelfOrForegroundUser() ||
+ checkCallerInteractAcrossUsersFull()) {
+ if (DBG_LOC) log("getCellLocation: is active user");
+ Bundle data = new Bundle();
+ Phone phone = getPhone(mSubscriptionController.getDefaultDataSubId());
+ if (phone == null) {
+ return null;
+ }
+
+ WorkSource workSource = getWorkSource(null, Binder.getCallingUid());
+ phone.getCellLocation(workSource).fillInNotifierBundle(data);
+ return data;
+ } else {
+ log("getCellLocation: suppress non-active user");
return null;
}
-
- WorkSource workSource = getWorkSource(null, Binder.getCallingUid());
- phone.getCellLocation(workSource).fillInNotifierBundle(data);
- return data;
}
+ private void enforceFineOrCoarseLocationPermission(String message) {
+ try {
+ mApp.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_FINE_LOCATION, null);
+ } catch (SecurityException e) {
+ // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
+ // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
+ // is the weaker precondition
+ mApp.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_COARSE_LOCATION, message);
+ }
+ }
+
+
@Override
public void enableLocationUpdates() {
enableLocationUpdatesForSubscriber(getDefaultSubscription());
@@ -1677,8 +1703,11 @@
@Override
@SuppressWarnings("unchecked")
public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
- if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
- callingPackage, Binder.getCallingUid())) {
+ enforceFineOrCoarseLocationPermission("getNeighboringCellInfo");
+
+ // OP_COARSE_LOCATION controls both fine and coarse location.
+ if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
return null;
}
@@ -1687,37 +1716,52 @@
return null;
}
- if (DBG_LOC) log("getNeighboringCellInfo: is active user");
+ if (checkIfCallerIsSelfOrForegroundUser() ||
+ checkCallerInteractAcrossUsersFull()) {
+ if (DBG_LOC) log("getNeighboringCellInfo: is active user");
- ArrayList<NeighboringCellInfo> cells = null;
+ ArrayList<NeighboringCellInfo> cells = null;
- WorkSource workSource = getWorkSource(null, Binder.getCallingUid());
- try {
- cells = (ArrayList<NeighboringCellInfo>) sendRequest(
- CMD_HANDLE_NEIGHBORING_CELL, workSource,
- SubscriptionManager.INVALID_SUBSCRIPTION_ID);
- } catch (RuntimeException e) {
- Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
+ WorkSource workSource = getWorkSource(null, Binder.getCallingUid());
+ try {
+ cells = (ArrayList<NeighboringCellInfo>) sendRequest(
+ CMD_HANDLE_NEIGHBORING_CELL, workSource,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
+ }
+ return cells;
+ } else {
+ if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
+ return null;
}
- return cells;
}
@Override
public List<CellInfo> getAllCellInfo(String callingPackage) {
- if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
- callingPackage, Binder.getCallingUid())) {
+ enforceFineOrCoarseLocationPermission("getAllCellInfo");
+
+ // OP_COARSE_LOCATION controls both fine and coarse location.
+ if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(),
+ callingPackage) != AppOpsManager.MODE_ALLOWED) {
return null;
}
- if (DBG_LOC) log("getAllCellInfo: is active user");
- WorkSource workSource = getWorkSource(null, Binder.getCallingUid());
- List<CellInfo> cellInfos = new ArrayList<CellInfo>();
- for (Phone phone : PhoneFactory.getPhones()) {
- final List<CellInfo> info = phone.getAllCellInfo(workSource);
- if (info != null) cellInfos.addAll(info);
+ if (checkIfCallerIsSelfOrForegroundUser() ||
+ checkCallerInteractAcrossUsersFull()) {
+ if (DBG_LOC) log("getAllCellInfo: is active user");
+ WorkSource workSource = getWorkSource(null, Binder.getCallingUid());
+ List<CellInfo> cellInfos = new ArrayList<CellInfo>();
+ for (Phone phone : PhoneFactory.getPhones()) {
+ final List<CellInfo> info = phone.getAllCellInfo(workSource);
+ if (info != null) cellInfos.addAll(info);
+ }
+ return cellInfos;
+ } else {
+ if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
+ return null;
}
- return cellInfos;
}
@Override
@@ -1759,6 +1803,47 @@
//
/**
+ * Returns true if the caller holds INTERACT_ACROSS_USERS_FULL.
+ */
+ private boolean checkCallerInteractAcrossUsersFull() {
+ return mPhone.getContext().checkCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private static boolean checkIfCallerIsSelfOrForegroundUser() {
+ boolean ok;
+
+ boolean self = Binder.getCallingUid() == Process.myUid();
+ if (!self) {
+ // Get the caller's user id then clear the calling identity
+ // which will be restored in the finally clause.
+ int callingUser = UserHandle.getCallingUserId();
+ long ident = Binder.clearCallingIdentity();
+
+ try {
+ // With calling identity cleared the current user is the foreground user.
+ int foregroundUser = ActivityManager.getCurrentUser();
+ ok = (foregroundUser == callingUser);
+ if (DBG_LOC) {
+ log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
+ + " callingUser=" + callingUser + " ok=" + ok);
+ }
+ } catch (Exception ex) {
+ if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
+ ok = false;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } else {
+ if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
+ ok = true;
+ }
+ if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
+ return ok;
+ }
+
+ /**
* Make sure the caller has the MODIFY_PHONE_STATE permission.
*
* @throws SecurityException if the caller does not have the required permission