Loading location/java/android/location/LocationRequest.java +22 −0 Original line number Diff line number Diff line Loading @@ -150,6 +150,8 @@ public final class LocationRequest implements Parcelable { @UnsupportedAppUsage private String mProvider; // if true, client requests coarse location, if false, client requests fine location private boolean mCoarseLocation; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mQuality; @UnsupportedAppUsage Loading Loading @@ -255,6 +257,7 @@ public final class LocationRequest implements Parcelable { public LocationRequest() { this( /* provider= */ LocationManager.FUSED_PROVIDER, /* coarseLocation= */ false, /* quality= */ POWER_LOW, /* interval= */ DEFAULT_INTERVAL_MS, /* fastestInterval= */ (long) (DEFAULT_INTERVAL_MS / FASTEST_INTERVAL_FACTOR), Loading @@ -273,6 +276,7 @@ public final class LocationRequest implements Parcelable { public LocationRequest(LocationRequest src) { this( src.mProvider, src.mCoarseLocation, src.mQuality, src.mInterval, src.mFastestInterval, Loading @@ -289,6 +293,7 @@ public final class LocationRequest implements Parcelable { private LocationRequest( @NonNull String provider, boolean coarseLocation, int quality, long intervalMs, long fastestIntervalMs, Loading @@ -305,6 +310,7 @@ public final class LocationRequest implements Parcelable { checkQuality(quality); mProvider = provider; mCoarseLocation = coarseLocation; mQuality = quality; mInterval = intervalMs; mFastestInterval = fastestIntervalMs; Loading @@ -320,6 +326,20 @@ public final class LocationRequest implements Parcelable { mWorkSource = workSource; } /** * @hide */ public boolean isCoarse() { return mCoarseLocation; } /** * @hide */ public void setCoarse(boolean coarse) { mCoarseLocation = coarse; } /** * Set the quality of the request. * Loading Loading @@ -700,6 +720,7 @@ public final class LocationRequest implements Parcelable { public LocationRequest createFromParcel(Parcel in) { return new LocationRequest( /* provider= */ in.readString(), /* coarseLocation= */ in.readBoolean(), /* quality= */ in.readInt(), /* interval= */ in.readLong(), /* fastestInterval= */ in.readLong(), Loading Loading @@ -728,6 +749,7 @@ public final class LocationRequest implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(mProvider); parcel.writeBoolean(mCoarseLocation); parcel.writeInt(mQuality); parcel.writeLong(mInterval); parcel.writeLong(mFastestInterval); Loading location/java/android/location/util/identity/CallerIdentity.java +61 −143 Original line number Diff line number Diff line Loading @@ -16,24 +16,16 @@ package android.location.util.identity; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.IntDef; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.os.Binder; import android.os.Process; import android.os.UserHandle; import android.os.WorkSource; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** Loading @@ -43,60 +35,21 @@ import java.util.Objects; */ public final class CallerIdentity { public static final int PERMISSION_NONE = 0; public static final int PERMISSION_COARSE = 1; public static final int PERMISSION_FINE = 2; @IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE}) @Retention(RetentionPolicy.SOURCE) public @interface PermissionLevel {} /** * Converts the given permission level to the corresponding permission. */ public static String asPermission(@PermissionLevel int permissionLevel) { switch (permissionLevel) { case PERMISSION_COARSE: return ACCESS_COARSE_LOCATION; case PERMISSION_FINE: return ACCESS_FINE_LOCATION; default: throw new IllegalArgumentException(); } } /** * Converts the given permission level to the corresponding appop. */ public static int asAppOp(@PermissionLevel int permissionLevel) { switch (permissionLevel) { case PERMISSION_COARSE: return AppOpsManager.OP_COARSE_LOCATION; case PERMISSION_FINE: return AppOpsManager.OP_FINE_LOCATION; default: throw new IllegalArgumentException(); } } /** * Construct a CallerIdentity for test purposes. */ @VisibleForTesting public static CallerIdentity forTest(int uid, int pid, String packageName, @Nullable String attributionTag, @PermissionLevel int permissionLevel) { return new CallerIdentity(uid, pid, packageName, attributionTag, null, permissionLevel); @Nullable String attributionTag) { return new CallerIdentity(uid, pid, packageName, attributionTag, null); } /** * Creates a CallerIdentity for the current process and context. */ public static CallerIdentity fromContext(Context context) { return new CallerIdentity(Process.myUid(), Process.myPid(), context.getPackageName(), context.getAttributionTag(), null, getPermissionLevel(context, Binder.getCallingPid(), Binder.getCallingUid())); return new CallerIdentity(Process.myUid(), Process.myPid(), context.getPackageName(), context.getAttributionTag(), null); } /** Loading @@ -121,7 +74,7 @@ public final class CallerIdentity { throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid); } return fromBinderUnsafe(context, packageName, attributionTag, listenerId); return fromBinderUnsafe(packageName, attributionTag, listenerId); } /** Loading @@ -130,9 +83,9 @@ public final class CallerIdentity { * this method should only be used if the package will be validated by some other means, such as * an appops call. */ public static CallerIdentity fromBinderUnsafe(Context context, String packageName, public static CallerIdentity fromBinderUnsafe(String packageName, @Nullable String attributionTag) { return fromBinderUnsafe(context, packageName, attributionTag, null); return fromBinderUnsafe(packageName, attributionTag, null); } /** Loading @@ -141,124 +94,89 @@ public final class CallerIdentity { * calling uid - this method should only be used if the package will be validated by some other * means, such as an appops call. */ public static CallerIdentity fromBinderUnsafe(Context context, String packageName, public static CallerIdentity fromBinderUnsafe(String packageName, @Nullable String attributionTag, @Nullable String listenerId) { return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName, attributionTag, listenerId, getPermissionLevel(context, Binder.getCallingPid(), Binder.getCallingUid())); } /** * Throws a security exception if the caller does not hold a location permission. */ public static void enforceCallingOrSelfLocationPermission(Context context, @PermissionLevel int desiredPermissionLevel) { enforceLocationPermission(Binder.getCallingUid(), getPermissionLevel(context, Binder.getCallingPid(), Binder.getCallingUid()), desiredPermissionLevel); packageName, attributionTag, listenerId); } /** * Returns false if the caller does not hold a location permission, true otherwise. */ public static boolean checkCallingOrSelfLocationPermission(Context context, @PermissionLevel int desiredPermissionLevel) { return checkLocationPermission( getPermissionLevel(context, Binder.getCallingPid(), Binder.getCallingUid()), desiredPermissionLevel); } private final int mUid; private static void enforceLocationPermission(int uid, @PermissionLevel int permissionLevel, @PermissionLevel int desiredPermissionLevel) { if (checkLocationPermission(permissionLevel, desiredPermissionLevel)) { return; } private final int mPid; if (desiredPermissionLevel == PERMISSION_COARSE) { throw new SecurityException("uid " + uid + " does not have " + ACCESS_COARSE_LOCATION + " or " + ACCESS_FINE_LOCATION + "."); } else if (desiredPermissionLevel == PERMISSION_FINE) { throw new SecurityException("uid " + uid + " does not have " + ACCESS_FINE_LOCATION + "."); } } private final String mPackageName; private static boolean checkLocationPermission(@PermissionLevel int permissionLevel, @PermissionLevel int desiredPermissionLevel) { return permissionLevel >= desiredPermissionLevel; } private final @Nullable String mAttributionTag; private static @PermissionLevel int getPermissionLevel(Context context, int pid, int uid) { if (context.checkPermission(ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) { return PERMISSION_FINE; } if (context.checkPermission(ACCESS_COARSE_LOCATION, pid, uid) == PERMISSION_GRANTED) { return PERMISSION_COARSE; } private final @Nullable String mListenerId; return PERMISSION_NONE; private CallerIdentity(int uid, int pid, String packageName, @Nullable String attributionTag, @Nullable String listenerId) { this.mUid = uid; this.mPid = pid; this.mPackageName = Objects.requireNonNull(packageName); this.mAttributionTag = attributionTag; this.mListenerId = listenerId; } /** The calling UID. */ public final int uid; public int getUid() { return mUid; } /** The calling PID. */ public final int pid; public int getPid() { return mPid; } /** The calling user. */ public final int userId; public int getUserId() { return UserHandle.getUserId(mUid); } /** The calling package name. */ public final String packageName; public String getPackageName() { return mPackageName; } /** The calling attribution tag. */ public final @Nullable String attributionTag; public String getAttributionTag() { return mAttributionTag; } /** The calling listener id. */ public final @Nullable String listenerId; /** * The calling location permission level. This field should only be used for validating * permissions for API access. It should not be used for validating permissions for location * access - that must be done through appops. */ public final @PermissionLevel int permissionLevel; private CallerIdentity(int uid, int pid, String packageName, @Nullable String attributionTag, @Nullable String listenerId, @PermissionLevel int permissionLevel) { this.uid = uid; this.pid = pid; this.userId = UserHandle.getUserId(uid); this.packageName = Objects.requireNonNull(packageName); this.attributionTag = attributionTag; this.listenerId = listenerId; this.permissionLevel = Preconditions.checkArgumentInRange(permissionLevel, PERMISSION_NONE, PERMISSION_FINE, "permissionLevel"); public String getListenerId() { return mListenerId; } /** * Throws a security exception if the CallerIdentity does not hold a location permission. * Adds this identity to the worksource supplied, or if not worksource is supplied, creates a * new worksource representing this identity. */ public void enforceLocationPermission(@PermissionLevel int desiredPermissionLevel) { enforceLocationPermission(uid, permissionLevel, desiredPermissionLevel); public WorkSource addToWorkSource(@Nullable WorkSource workSource) { if (workSource == null) { return new WorkSource(mUid, mPackageName); } else { workSource.add(mUid, mPackageName); return workSource; } } @Override public String toString() { int length = 10 + packageName.length(); if (attributionTag != null) { length += attributionTag.length(); int length = 10 + mPackageName.length(); if (mAttributionTag != null) { length += mAttributionTag.length(); } StringBuilder builder = new StringBuilder(length); builder.append(pid).append("/").append(packageName); if (attributionTag != null) { builder.append(mPid).append("/").append(mPackageName); if (mAttributionTag != null) { builder.append("["); if (attributionTag.startsWith(packageName)) { builder.append(attributionTag.substring(packageName.length())); if (mAttributionTag.startsWith(mPackageName)) { builder.append(mAttributionTag.substring(mPackageName.length())); } else { builder.append(attributionTag); builder.append(mAttributionTag); } builder.append("]"); } Loading @@ -274,14 +192,14 @@ public final class CallerIdentity { return false; } CallerIdentity that = (CallerIdentity) o; return uid == that.uid && pid == that.pid && packageName.equals(that.packageName) && Objects.equals(attributionTag, that.attributionTag); return getUid() == that.getUid() && mPid == that.mPid && mPackageName.equals(that.mPackageName) && Objects.equals(mAttributionTag, that.mAttributionTag); } @Override public int hashCode() { return Objects.hash(uid, pid, packageName, attributionTag); return Objects.hash(mUid, mPid, mPackageName, mAttributionTag); } } services/core/java/com/android/server/location/AppOpsHelper.java +28 −24 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.location.LocationPermissions.PermissionLevel; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; Loading Loading @@ -112,13 +113,15 @@ public class AppOpsHelper { /** * Checks if the given identity may have locations delivered without noting that a location is * being delivered. This is a looser guarantee than {@link #noteLocationAccess(CallerIdentity)}, * and this function does not validate package arguments and so should not be used with * unvalidated arguments or before actually delivering locations. * being delivered. This is a looser guarantee than * {@link #noteLocationAccess(CallerIdentity, int)}, and this function does not validate package * arguments and so should not be used with unvalidated arguments or before actually delivering * locations. * * @see AppOpsManager#checkOpNoThrow(int, int, String) */ public boolean checkLocationAccess(CallerIdentity callerIdentity) { public boolean checkLocationAccess(CallerIdentity callerIdentity, @PermissionLevel int permissionLevel) { synchronized (this) { Preconditions.checkState(mAppOps != null); } Loading @@ -126,9 +129,9 @@ public class AppOpsHelper { long identity = Binder.clearCallingIdentity(); try { return mAppOps.checkOpNoThrow( CallerIdentity.asAppOp(callerIdentity.permissionLevel), callerIdentity.uid, callerIdentity.packageName) == AppOpsManager.MODE_ALLOWED; LocationPermissions.asAppOp(permissionLevel), callerIdentity.getUid(), callerIdentity.getPackageName()) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } Loading @@ -139,8 +142,9 @@ public class AppOpsHelper { * called right before a location is delivered, and if it returns false, the location should not * be delivered. */ public boolean noteLocationAccess(CallerIdentity identity) { return noteOpNoThrow(CallerIdentity.asAppOp(identity.permissionLevel), identity); public boolean noteLocationAccess(CallerIdentity identity, @PermissionLevel int permissionLevel) { return noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity); } /** Loading Loading @@ -189,10 +193,10 @@ public class AppOpsHelper { // note that this is not the no throw version of noteOp, this call may throw exceptions return mAppOps.noteOp( AppOpsManager.OP_MOCK_LOCATION, callerIdentity.uid, callerIdentity.packageName, callerIdentity.attributionTag, callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED; callerIdentity.getUid(), callerIdentity.getPackageName(), callerIdentity.getAttributionTag(), callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } Loading @@ -207,11 +211,11 @@ public class AppOpsHelper { try { return mAppOps.startOpNoThrow( appOp, callerIdentity.uid, callerIdentity.packageName, callerIdentity.getUid(), callerIdentity.getPackageName(), false, callerIdentity.attributionTag, callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED; callerIdentity.getAttributionTag(), callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } Loading @@ -226,9 +230,9 @@ public class AppOpsHelper { try { mAppOps.finishOp( appOp, callerIdentity.uid, callerIdentity.packageName, callerIdentity.attributionTag); callerIdentity.getUid(), callerIdentity.getPackageName(), callerIdentity.getAttributionTag()); } finally { Binder.restoreCallingIdentity(identity); } Loading @@ -243,10 +247,10 @@ public class AppOpsHelper { try { return mAppOps.noteOpNoThrow( appOp, callerIdentity.uid, callerIdentity.packageName, callerIdentity.attributionTag, callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED; callerIdentity.getUid(), callerIdentity.getPackageName(), callerIdentity.getAttributionTag(), callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } Loading services/core/java/com/android/server/location/LocationManagerService.java +97 −70 File changed.Preview size limit exceeded, changes collapsed. Show changes services/core/java/com/android/server/location/LocationPermissions.java 0 → 100644 +176 −0 Original line number Diff line number Diff line /* * 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.server.location; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.IntDef; import android.app.AppOpsManager; import android.content.Context; import android.os.Binder; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** Utility class for dealing with location permissions. */ public final class LocationPermissions { /** * Indicates no location permissions are present, or no location permission are required. */ public static final int PERMISSION_NONE = 0; /** * Indicates the coarse location permission is present, or either the coarse or fine permissions * are required. */ public static final int PERMISSION_COARSE = 1; /** * Indicates the fine location permission is present, or the fine location permission is * required. */ public static final int PERMISSION_FINE = 2; @IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE}) @Retention(RetentionPolicy.SOURCE) public @interface PermissionLevel {} /** * Converts the given permission level to the corresponding permission. */ public static String asPermission(@PermissionLevel int permissionLevel) { switch (permissionLevel) { case PERMISSION_COARSE: return ACCESS_COARSE_LOCATION; case PERMISSION_FINE: return ACCESS_FINE_LOCATION; default: throw new IllegalArgumentException(); } } /** * Converts the given permission level to the corresponding appop. */ public static int asAppOp(@PermissionLevel int permissionLevel) { switch (permissionLevel) { case PERMISSION_COARSE: return AppOpsManager.OP_COARSE_LOCATION; case PERMISSION_FINE: return AppOpsManager.OP_FINE_LOCATION; default: throw new IllegalArgumentException(); } } /** * Throws a security exception if the caller does not hold the required location permissions. */ public static void enforceCallingOrSelfLocationPermission(Context context, @PermissionLevel int requiredPermissionLevel) { enforceLocationPermission(Binder.getCallingUid(), getPermissionLevel(context, Binder.getCallingUid(), Binder.getCallingPid()), requiredPermissionLevel); } /** * Throws a security exception if the given uid/pid does not hold the required location * permissions. */ public static void enforceLocationPermission(Context context, int uid, int pid, @PermissionLevel int requiredPermissionLevel) { enforceLocationPermission(uid, getPermissionLevel(context, uid, pid), requiredPermissionLevel); } /** * Throws a security exception if the given permission level does not meet the required location * permission level. */ public static void enforceLocationPermission(int uid, @PermissionLevel int permissionLevel, @PermissionLevel int requiredPermissionLevel) { if (checkLocationPermission(permissionLevel, requiredPermissionLevel)) { return; } if (requiredPermissionLevel == PERMISSION_COARSE) { throw new SecurityException("uid " + uid + " does not have " + ACCESS_COARSE_LOCATION + " or " + ACCESS_FINE_LOCATION + "."); } else if (requiredPermissionLevel == PERMISSION_FINE) { throw new SecurityException("uid " + uid + " does not have " + ACCESS_FINE_LOCATION + "."); } } /** * Returns false if the caller does not hold the required location permissions. */ public static boolean checkCallingOrSelfLocationPermission(Context context, @PermissionLevel int requiredPermissionLevel) { return checkLocationPermission( getCallingOrSelfPermissionLevel(context), requiredPermissionLevel); } /** * Returns false if the given uid/pid does not hold the required location permissions. */ public static boolean checkLocationPermission(Context context, int uid, int pid, @PermissionLevel int requiredPermissionLevel) { return checkLocationPermission( getPermissionLevel(context, uid, pid), requiredPermissionLevel); } /** * Returns false if the given permission level does not meet the required location permission * level. */ public static boolean checkLocationPermission(@PermissionLevel int permissionLevel, @PermissionLevel int requiredPermissionLevel) { return permissionLevel >= requiredPermissionLevel; } /** * Returns the permission level of the caller. */ @PermissionLevel public static int getCallingOrSelfPermissionLevel(Context context) { return getPermissionLevel(context, Binder.getCallingUid(), Binder.getCallingPid()); } /** * Returns the permission level of the given uid/pid. */ @PermissionLevel public static int getPermissionLevel(Context context, int uid, int pid) { if (context.checkPermission(ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) { return PERMISSION_FINE; } if (context.checkPermission(ACCESS_COARSE_LOCATION, pid, uid) == PERMISSION_GRANTED) { return PERMISSION_COARSE; } return PERMISSION_NONE; } private LocationPermissions() {} } Loading
location/java/android/location/LocationRequest.java +22 −0 Original line number Diff line number Diff line Loading @@ -150,6 +150,8 @@ public final class LocationRequest implements Parcelable { @UnsupportedAppUsage private String mProvider; // if true, client requests coarse location, if false, client requests fine location private boolean mCoarseLocation; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private int mQuality; @UnsupportedAppUsage Loading Loading @@ -255,6 +257,7 @@ public final class LocationRequest implements Parcelable { public LocationRequest() { this( /* provider= */ LocationManager.FUSED_PROVIDER, /* coarseLocation= */ false, /* quality= */ POWER_LOW, /* interval= */ DEFAULT_INTERVAL_MS, /* fastestInterval= */ (long) (DEFAULT_INTERVAL_MS / FASTEST_INTERVAL_FACTOR), Loading @@ -273,6 +276,7 @@ public final class LocationRequest implements Parcelable { public LocationRequest(LocationRequest src) { this( src.mProvider, src.mCoarseLocation, src.mQuality, src.mInterval, src.mFastestInterval, Loading @@ -289,6 +293,7 @@ public final class LocationRequest implements Parcelable { private LocationRequest( @NonNull String provider, boolean coarseLocation, int quality, long intervalMs, long fastestIntervalMs, Loading @@ -305,6 +310,7 @@ public final class LocationRequest implements Parcelable { checkQuality(quality); mProvider = provider; mCoarseLocation = coarseLocation; mQuality = quality; mInterval = intervalMs; mFastestInterval = fastestIntervalMs; Loading @@ -320,6 +326,20 @@ public final class LocationRequest implements Parcelable { mWorkSource = workSource; } /** * @hide */ public boolean isCoarse() { return mCoarseLocation; } /** * @hide */ public void setCoarse(boolean coarse) { mCoarseLocation = coarse; } /** * Set the quality of the request. * Loading Loading @@ -700,6 +720,7 @@ public final class LocationRequest implements Parcelable { public LocationRequest createFromParcel(Parcel in) { return new LocationRequest( /* provider= */ in.readString(), /* coarseLocation= */ in.readBoolean(), /* quality= */ in.readInt(), /* interval= */ in.readLong(), /* fastestInterval= */ in.readLong(), Loading Loading @@ -728,6 +749,7 @@ public final class LocationRequest implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeString(mProvider); parcel.writeBoolean(mCoarseLocation); parcel.writeInt(mQuality); parcel.writeLong(mInterval); parcel.writeLong(mFastestInterval); Loading
location/java/android/location/util/identity/CallerIdentity.java +61 −143 Original line number Diff line number Diff line Loading @@ -16,24 +16,16 @@ package android.location.util.identity; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.IntDef; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.os.Binder; import android.os.Process; import android.os.UserHandle; import android.os.WorkSource; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; /** Loading @@ -43,60 +35,21 @@ import java.util.Objects; */ public final class CallerIdentity { public static final int PERMISSION_NONE = 0; public static final int PERMISSION_COARSE = 1; public static final int PERMISSION_FINE = 2; @IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE}) @Retention(RetentionPolicy.SOURCE) public @interface PermissionLevel {} /** * Converts the given permission level to the corresponding permission. */ public static String asPermission(@PermissionLevel int permissionLevel) { switch (permissionLevel) { case PERMISSION_COARSE: return ACCESS_COARSE_LOCATION; case PERMISSION_FINE: return ACCESS_FINE_LOCATION; default: throw new IllegalArgumentException(); } } /** * Converts the given permission level to the corresponding appop. */ public static int asAppOp(@PermissionLevel int permissionLevel) { switch (permissionLevel) { case PERMISSION_COARSE: return AppOpsManager.OP_COARSE_LOCATION; case PERMISSION_FINE: return AppOpsManager.OP_FINE_LOCATION; default: throw new IllegalArgumentException(); } } /** * Construct a CallerIdentity for test purposes. */ @VisibleForTesting public static CallerIdentity forTest(int uid, int pid, String packageName, @Nullable String attributionTag, @PermissionLevel int permissionLevel) { return new CallerIdentity(uid, pid, packageName, attributionTag, null, permissionLevel); @Nullable String attributionTag) { return new CallerIdentity(uid, pid, packageName, attributionTag, null); } /** * Creates a CallerIdentity for the current process and context. */ public static CallerIdentity fromContext(Context context) { return new CallerIdentity(Process.myUid(), Process.myPid(), context.getPackageName(), context.getAttributionTag(), null, getPermissionLevel(context, Binder.getCallingPid(), Binder.getCallingUid())); return new CallerIdentity(Process.myUid(), Process.myPid(), context.getPackageName(), context.getAttributionTag(), null); } /** Loading @@ -121,7 +74,7 @@ public final class CallerIdentity { throw new SecurityException("invalid package \"" + packageName + "\" for uid " + uid); } return fromBinderUnsafe(context, packageName, attributionTag, listenerId); return fromBinderUnsafe(packageName, attributionTag, listenerId); } /** Loading @@ -130,9 +83,9 @@ public final class CallerIdentity { * this method should only be used if the package will be validated by some other means, such as * an appops call. */ public static CallerIdentity fromBinderUnsafe(Context context, String packageName, public static CallerIdentity fromBinderUnsafe(String packageName, @Nullable String attributionTag) { return fromBinderUnsafe(context, packageName, attributionTag, null); return fromBinderUnsafe(packageName, attributionTag, null); } /** Loading @@ -141,124 +94,89 @@ public final class CallerIdentity { * calling uid - this method should only be used if the package will be validated by some other * means, such as an appops call. */ public static CallerIdentity fromBinderUnsafe(Context context, String packageName, public static CallerIdentity fromBinderUnsafe(String packageName, @Nullable String attributionTag, @Nullable String listenerId) { return new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName, attributionTag, listenerId, getPermissionLevel(context, Binder.getCallingPid(), Binder.getCallingUid())); } /** * Throws a security exception if the caller does not hold a location permission. */ public static void enforceCallingOrSelfLocationPermission(Context context, @PermissionLevel int desiredPermissionLevel) { enforceLocationPermission(Binder.getCallingUid(), getPermissionLevel(context, Binder.getCallingPid(), Binder.getCallingUid()), desiredPermissionLevel); packageName, attributionTag, listenerId); } /** * Returns false if the caller does not hold a location permission, true otherwise. */ public static boolean checkCallingOrSelfLocationPermission(Context context, @PermissionLevel int desiredPermissionLevel) { return checkLocationPermission( getPermissionLevel(context, Binder.getCallingPid(), Binder.getCallingUid()), desiredPermissionLevel); } private final int mUid; private static void enforceLocationPermission(int uid, @PermissionLevel int permissionLevel, @PermissionLevel int desiredPermissionLevel) { if (checkLocationPermission(permissionLevel, desiredPermissionLevel)) { return; } private final int mPid; if (desiredPermissionLevel == PERMISSION_COARSE) { throw new SecurityException("uid " + uid + " does not have " + ACCESS_COARSE_LOCATION + " or " + ACCESS_FINE_LOCATION + "."); } else if (desiredPermissionLevel == PERMISSION_FINE) { throw new SecurityException("uid " + uid + " does not have " + ACCESS_FINE_LOCATION + "."); } } private final String mPackageName; private static boolean checkLocationPermission(@PermissionLevel int permissionLevel, @PermissionLevel int desiredPermissionLevel) { return permissionLevel >= desiredPermissionLevel; } private final @Nullable String mAttributionTag; private static @PermissionLevel int getPermissionLevel(Context context, int pid, int uid) { if (context.checkPermission(ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) { return PERMISSION_FINE; } if (context.checkPermission(ACCESS_COARSE_LOCATION, pid, uid) == PERMISSION_GRANTED) { return PERMISSION_COARSE; } private final @Nullable String mListenerId; return PERMISSION_NONE; private CallerIdentity(int uid, int pid, String packageName, @Nullable String attributionTag, @Nullable String listenerId) { this.mUid = uid; this.mPid = pid; this.mPackageName = Objects.requireNonNull(packageName); this.mAttributionTag = attributionTag; this.mListenerId = listenerId; } /** The calling UID. */ public final int uid; public int getUid() { return mUid; } /** The calling PID. */ public final int pid; public int getPid() { return mPid; } /** The calling user. */ public final int userId; public int getUserId() { return UserHandle.getUserId(mUid); } /** The calling package name. */ public final String packageName; public String getPackageName() { return mPackageName; } /** The calling attribution tag. */ public final @Nullable String attributionTag; public String getAttributionTag() { return mAttributionTag; } /** The calling listener id. */ public final @Nullable String listenerId; /** * The calling location permission level. This field should only be used for validating * permissions for API access. It should not be used for validating permissions for location * access - that must be done through appops. */ public final @PermissionLevel int permissionLevel; private CallerIdentity(int uid, int pid, String packageName, @Nullable String attributionTag, @Nullable String listenerId, @PermissionLevel int permissionLevel) { this.uid = uid; this.pid = pid; this.userId = UserHandle.getUserId(uid); this.packageName = Objects.requireNonNull(packageName); this.attributionTag = attributionTag; this.listenerId = listenerId; this.permissionLevel = Preconditions.checkArgumentInRange(permissionLevel, PERMISSION_NONE, PERMISSION_FINE, "permissionLevel"); public String getListenerId() { return mListenerId; } /** * Throws a security exception if the CallerIdentity does not hold a location permission. * Adds this identity to the worksource supplied, or if not worksource is supplied, creates a * new worksource representing this identity. */ public void enforceLocationPermission(@PermissionLevel int desiredPermissionLevel) { enforceLocationPermission(uid, permissionLevel, desiredPermissionLevel); public WorkSource addToWorkSource(@Nullable WorkSource workSource) { if (workSource == null) { return new WorkSource(mUid, mPackageName); } else { workSource.add(mUid, mPackageName); return workSource; } } @Override public String toString() { int length = 10 + packageName.length(); if (attributionTag != null) { length += attributionTag.length(); int length = 10 + mPackageName.length(); if (mAttributionTag != null) { length += mAttributionTag.length(); } StringBuilder builder = new StringBuilder(length); builder.append(pid).append("/").append(packageName); if (attributionTag != null) { builder.append(mPid).append("/").append(mPackageName); if (mAttributionTag != null) { builder.append("["); if (attributionTag.startsWith(packageName)) { builder.append(attributionTag.substring(packageName.length())); if (mAttributionTag.startsWith(mPackageName)) { builder.append(mAttributionTag.substring(mPackageName.length())); } else { builder.append(attributionTag); builder.append(mAttributionTag); } builder.append("]"); } Loading @@ -274,14 +192,14 @@ public final class CallerIdentity { return false; } CallerIdentity that = (CallerIdentity) o; return uid == that.uid && pid == that.pid && packageName.equals(that.packageName) && Objects.equals(attributionTag, that.attributionTag); return getUid() == that.getUid() && mPid == that.mPid && mPackageName.equals(that.mPackageName) && Objects.equals(mAttributionTag, that.mAttributionTag); } @Override public int hashCode() { return Objects.hash(uid, pid, packageName, attributionTag); return Objects.hash(mUid, mPid, mPackageName, mAttributionTag); } }
services/core/java/com/android/server/location/AppOpsHelper.java +28 −24 Original line number Diff line number Diff line Loading @@ -33,6 +33,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.location.LocationPermissions.PermissionLevel; import java.util.Objects; import java.util.concurrent.CopyOnWriteArrayList; Loading Loading @@ -112,13 +113,15 @@ public class AppOpsHelper { /** * Checks if the given identity may have locations delivered without noting that a location is * being delivered. This is a looser guarantee than {@link #noteLocationAccess(CallerIdentity)}, * and this function does not validate package arguments and so should not be used with * unvalidated arguments or before actually delivering locations. * being delivered. This is a looser guarantee than * {@link #noteLocationAccess(CallerIdentity, int)}, and this function does not validate package * arguments and so should not be used with unvalidated arguments or before actually delivering * locations. * * @see AppOpsManager#checkOpNoThrow(int, int, String) */ public boolean checkLocationAccess(CallerIdentity callerIdentity) { public boolean checkLocationAccess(CallerIdentity callerIdentity, @PermissionLevel int permissionLevel) { synchronized (this) { Preconditions.checkState(mAppOps != null); } Loading @@ -126,9 +129,9 @@ public class AppOpsHelper { long identity = Binder.clearCallingIdentity(); try { return mAppOps.checkOpNoThrow( CallerIdentity.asAppOp(callerIdentity.permissionLevel), callerIdentity.uid, callerIdentity.packageName) == AppOpsManager.MODE_ALLOWED; LocationPermissions.asAppOp(permissionLevel), callerIdentity.getUid(), callerIdentity.getPackageName()) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } Loading @@ -139,8 +142,9 @@ public class AppOpsHelper { * called right before a location is delivered, and if it returns false, the location should not * be delivered. */ public boolean noteLocationAccess(CallerIdentity identity) { return noteOpNoThrow(CallerIdentity.asAppOp(identity.permissionLevel), identity); public boolean noteLocationAccess(CallerIdentity identity, @PermissionLevel int permissionLevel) { return noteOpNoThrow(LocationPermissions.asAppOp(permissionLevel), identity); } /** Loading Loading @@ -189,10 +193,10 @@ public class AppOpsHelper { // note that this is not the no throw version of noteOp, this call may throw exceptions return mAppOps.noteOp( AppOpsManager.OP_MOCK_LOCATION, callerIdentity.uid, callerIdentity.packageName, callerIdentity.attributionTag, callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED; callerIdentity.getUid(), callerIdentity.getPackageName(), callerIdentity.getAttributionTag(), callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } Loading @@ -207,11 +211,11 @@ public class AppOpsHelper { try { return mAppOps.startOpNoThrow( appOp, callerIdentity.uid, callerIdentity.packageName, callerIdentity.getUid(), callerIdentity.getPackageName(), false, callerIdentity.attributionTag, callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED; callerIdentity.getAttributionTag(), callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } Loading @@ -226,9 +230,9 @@ public class AppOpsHelper { try { mAppOps.finishOp( appOp, callerIdentity.uid, callerIdentity.packageName, callerIdentity.attributionTag); callerIdentity.getUid(), callerIdentity.getPackageName(), callerIdentity.getAttributionTag()); } finally { Binder.restoreCallingIdentity(identity); } Loading @@ -243,10 +247,10 @@ public class AppOpsHelper { try { return mAppOps.noteOpNoThrow( appOp, callerIdentity.uid, callerIdentity.packageName, callerIdentity.attributionTag, callerIdentity.listenerId) == AppOpsManager.MODE_ALLOWED; callerIdentity.getUid(), callerIdentity.getPackageName(), callerIdentity.getAttributionTag(), callerIdentity.getListenerId()) == AppOpsManager.MODE_ALLOWED; } finally { Binder.restoreCallingIdentity(identity); } Loading
services/core/java/com/android/server/location/LocationManagerService.java +97 −70 File changed.Preview size limit exceeded, changes collapsed. Show changes
services/core/java/com/android/server/location/LocationPermissions.java 0 → 100644 +176 −0 Original line number Diff line number Diff line /* * 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.server.location; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.IntDef; import android.app.AppOpsManager; import android.content.Context; import android.os.Binder; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** Utility class for dealing with location permissions. */ public final class LocationPermissions { /** * Indicates no location permissions are present, or no location permission are required. */ public static final int PERMISSION_NONE = 0; /** * Indicates the coarse location permission is present, or either the coarse or fine permissions * are required. */ public static final int PERMISSION_COARSE = 1; /** * Indicates the fine location permission is present, or the fine location permission is * required. */ public static final int PERMISSION_FINE = 2; @IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE}) @Retention(RetentionPolicy.SOURCE) public @interface PermissionLevel {} /** * Converts the given permission level to the corresponding permission. */ public static String asPermission(@PermissionLevel int permissionLevel) { switch (permissionLevel) { case PERMISSION_COARSE: return ACCESS_COARSE_LOCATION; case PERMISSION_FINE: return ACCESS_FINE_LOCATION; default: throw new IllegalArgumentException(); } } /** * Converts the given permission level to the corresponding appop. */ public static int asAppOp(@PermissionLevel int permissionLevel) { switch (permissionLevel) { case PERMISSION_COARSE: return AppOpsManager.OP_COARSE_LOCATION; case PERMISSION_FINE: return AppOpsManager.OP_FINE_LOCATION; default: throw new IllegalArgumentException(); } } /** * Throws a security exception if the caller does not hold the required location permissions. */ public static void enforceCallingOrSelfLocationPermission(Context context, @PermissionLevel int requiredPermissionLevel) { enforceLocationPermission(Binder.getCallingUid(), getPermissionLevel(context, Binder.getCallingUid(), Binder.getCallingPid()), requiredPermissionLevel); } /** * Throws a security exception if the given uid/pid does not hold the required location * permissions. */ public static void enforceLocationPermission(Context context, int uid, int pid, @PermissionLevel int requiredPermissionLevel) { enforceLocationPermission(uid, getPermissionLevel(context, uid, pid), requiredPermissionLevel); } /** * Throws a security exception if the given permission level does not meet the required location * permission level. */ public static void enforceLocationPermission(int uid, @PermissionLevel int permissionLevel, @PermissionLevel int requiredPermissionLevel) { if (checkLocationPermission(permissionLevel, requiredPermissionLevel)) { return; } if (requiredPermissionLevel == PERMISSION_COARSE) { throw new SecurityException("uid " + uid + " does not have " + ACCESS_COARSE_LOCATION + " or " + ACCESS_FINE_LOCATION + "."); } else if (requiredPermissionLevel == PERMISSION_FINE) { throw new SecurityException("uid " + uid + " does not have " + ACCESS_FINE_LOCATION + "."); } } /** * Returns false if the caller does not hold the required location permissions. */ public static boolean checkCallingOrSelfLocationPermission(Context context, @PermissionLevel int requiredPermissionLevel) { return checkLocationPermission( getCallingOrSelfPermissionLevel(context), requiredPermissionLevel); } /** * Returns false if the given uid/pid does not hold the required location permissions. */ public static boolean checkLocationPermission(Context context, int uid, int pid, @PermissionLevel int requiredPermissionLevel) { return checkLocationPermission( getPermissionLevel(context, uid, pid), requiredPermissionLevel); } /** * Returns false if the given permission level does not meet the required location permission * level. */ public static boolean checkLocationPermission(@PermissionLevel int permissionLevel, @PermissionLevel int requiredPermissionLevel) { return permissionLevel >= requiredPermissionLevel; } /** * Returns the permission level of the caller. */ @PermissionLevel public static int getCallingOrSelfPermissionLevel(Context context) { return getPermissionLevel(context, Binder.getCallingUid(), Binder.getCallingPid()); } /** * Returns the permission level of the given uid/pid. */ @PermissionLevel public static int getPermissionLevel(Context context, int uid, int pid) { if (context.checkPermission(ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) { return PERMISSION_FINE; } if (context.checkPermission(ACCESS_COARSE_LOCATION, pid, uid) == PERMISSION_GRANTED) { return PERMISSION_COARSE; } return PERMISSION_NONE; } private LocationPermissions() {} }