Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0875bf8c authored by Soonil Nagarkar's avatar Soonil Nagarkar
Browse files

Block immutable PIs from location APIs

Bug: 171317480
Test: atest CtsLocationFineTestCases
Change-Id: Ifba03b853f0681a5949d89c4a8e7c2de9a5c6018
parent c335e905
Loading
Loading
Loading
Loading
+52 −29
Original line number Original line Diff line number Diff line
@@ -76,18 +76,27 @@ import java.util.function.Consumer;
 * obtain periodic updates of the device's geographical location, or to be notified when the device
 * obtain periodic updates of the device's geographical location, or to be notified when the device
 * enters the proximity of a given geographical location.
 * enters the proximity of a given geographical location.
 *
 *
 * <p class="note">Unless noted, all Location API methods require the {@link
 * <p class="note">Unless otherwise noted, all Location API methods require the
 * android.Manifest.permission#ACCESS_COARSE_LOCATION} or {@link
 * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
 * android.Manifest.permission#ACCESS_FINE_LOCATION} permissions. If your application only has the
 * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions. If your application only
 * coarse permission then it will not have access to fine location providers. Other providers will
 * has the coarse permission then providers will still return location results, but the exact
 * still return location results, but the exact location will be obfuscated to a coarse level of
 * location will be obfuscated to a coarse level of accuracy.
 * accuracy.
 */
 */
@SuppressWarnings({"deprecation"})
@SuppressWarnings({"deprecation"})
@SystemService(Context.LOCATION_SERVICE)
@SystemService(Context.LOCATION_SERVICE)
@RequiresFeature(PackageManager.FEATURE_LOCATION)
@RequiresFeature(PackageManager.FEATURE_LOCATION)
public class LocationManager {
public class LocationManager {


    /**
     * For apps targeting Android S and above, immutable PendingIntents passed into location APIs
     * will generate an IllegalArgumentException.
     *
     * @hide
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    public static final long BLOCK_IMMUTABLE_PENDING_INTENTS = 171317480L;

    /**
    /**
     * For apps targeting Android S and above, LocationRequest system APIs may not be used with
     * For apps targeting Android S and above, LocationRequest system APIs may not be used with
     * PendingIntent location requests.
     * PendingIntent location requests.
@@ -96,7 +105,7 @@ public class LocationManager {
     */
     */
    @ChangeId
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    public static final long PREVENT_PENDING_INTENT_SYSTEM_API_USAGE = 169887240L;
    public static final long BLOCK_PENDING_INTENT_SYSTEM_API_USAGE = 169887240L;


    /**
    /**
     * For apps targeting Android S and above, location clients may receive historical locations
     * For apps targeting Android S and above, location clients may receive historical locations
@@ -116,7 +125,7 @@ public class LocationManager {
     */
     */
    @ChangeId
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
    private static final long GET_PROVIDER_SECURITY_EXCEPTIONS = 150935354L;
    public static final long GET_PROVIDER_SECURITY_EXCEPTIONS = 150935354L;


    /**
    /**
     * For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a
     * For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a
@@ -126,7 +135,7 @@ public class LocationManager {
     */
     */
    @ChangeId
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
    private static final long TARGETED_PENDING_INTENT = 148963590L;
    public static final long BLOCK_UNTARGETED_PENDING_INTENTS = 148963590L;


    /**
    /**
     * For apps targeting Android K and above, incomplete locations may not be passed to
     * For apps targeting Android K and above, incomplete locations may not be passed to
@@ -136,7 +145,7 @@ public class LocationManager {
     */
     */
    @ChangeId
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
    private static final long INCOMPLETE_LOCATION = 148964793L;
    public static final long BLOCK_INCOMPLETE_LOCATIONS = 148964793L;


    /**
    /**
     * For apps targeting Android S and above, all {@link GpsStatus} API usage must be replaced with
     * For apps targeting Android S and above, all {@link GpsStatus} API usage must be replaced with
@@ -146,7 +155,7 @@ public class LocationManager {
     */
     */
    @ChangeId
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    private static final long GPS_STATUS_USAGE = 144027538L;
    public static final long BLOCK_GPS_STATUS_USAGE = 144027538L;


    /**
    /**
     * Name of the network location provider.
     * Name of the network location provider.
@@ -1372,11 +1381,16 @@ public class LocationManager {
        Preconditions.checkArgument(locationRequest != null, "invalid null location request");
        Preconditions.checkArgument(locationRequest != null, "invalid null location request");
        Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
        Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");


        if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
        if (Compatibility.isChangeEnabled(BLOCK_UNTARGETED_PENDING_INTENTS)) {
            Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
            Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
                    "pending intent must be targeted to a package");
                    "pending intent must be targeted to a package");
        }
        }


        if (Compatibility.isChangeEnabled(BLOCK_IMMUTABLE_PENDING_INTENTS)) {
            Preconditions.checkArgument(!pendingIntent.isImmutable(),
                    "pending intent must be mutable");
        }

        try {
        try {
            mService.registerLocationPendingIntent(provider, locationRequest, pendingIntent,
            mService.registerLocationPendingIntent(provider, locationRequest, pendingIntent,
                    mContext.getPackageName(), mContext.getAttributionTag());
                    mContext.getPackageName(), mContext.getAttributionTag());
@@ -1729,7 +1743,7 @@ public class LocationManager {
        Preconditions.checkArgument(provider != null, "invalid null provider");
        Preconditions.checkArgument(provider != null, "invalid null provider");
        Preconditions.checkArgument(location != null, "invalid null location");
        Preconditions.checkArgument(location != null, "invalid null location");


        if (Compatibility.isChangeEnabled(INCOMPLETE_LOCATION)) {
        if (Compatibility.isChangeEnabled(BLOCK_INCOMPLETE_LOCATIONS)) {
            Preconditions.checkArgument(location.isComplete(),
            Preconditions.checkArgument(location.isComplete(),
                    "incomplete location object, missing timestamp or accuracy?");
                    "incomplete location object, missing timestamp or accuracy?");
        } else {
        } else {
@@ -1826,24 +1840,33 @@ public class LocationManager {
     * @param radius        the radius of the central point of the alert region in meters
     * @param radius        the radius of the central point of the alert region in meters
     * @param expiration    expiration realtime for this proximity alert in milliseconds, or -1 to
     * @param expiration    expiration realtime for this proximity alert in milliseconds, or -1 to
     *                      indicate no expiration
     *                      indicate no expiration
     * @param intent     a {@link PendingIntent} that will sent when entry to or exit from the alert
     * @param pendingIntent a {@link PendingIntent} that will sent when entry to or exit from the
     *                   region is detected
     *                      alert region is detected
     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
     * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
     *                           permission is not present
     *                           permission is not present
     */
     */
    @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
    public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
            @NonNull PendingIntent intent) {
            @NonNull PendingIntent pendingIntent) {
        Preconditions.checkArgument(intent != null, "invalid null pending intent");
        Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
        if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {

            Preconditions.checkArgument(intent.isTargetedToPackage(),
        if (Compatibility.isChangeEnabled(BLOCK_UNTARGETED_PENDING_INTENTS)) {
            Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
                    "pending intent must be targeted to a package");
                    "pending intent must be targeted to a package");
        }
        }
        if (expiration < 0) expiration = Long.MAX_VALUE;

        if (Compatibility.isChangeEnabled(BLOCK_IMMUTABLE_PENDING_INTENTS)) {
            Preconditions.checkArgument(!pendingIntent.isImmutable(),
                    "pending intent must be mutable");
        }

        if (expiration < 0) {
            expiration = Long.MAX_VALUE;
        }


        try {
        try {
            Geofence fence = Geofence.createCircle(latitude, longitude, radius, expiration);
            Geofence fence = Geofence.createCircle(latitude, longitude, radius, expiration);
            mService.requestGeofence(fence, intent, mContext.getPackageName(),
            mService.requestGeofence(fence, pendingIntent, mContext.getPackageName(),
                    mContext.getAttributionTag());
                    mContext.getAttributionTag());
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            throw e.rethrowFromSystemServer();
@@ -1941,7 +1964,7 @@ public class LocationManager {
    @Deprecated
    @Deprecated
    @RequiresPermission(ACCESS_FINE_LOCATION)
    @RequiresPermission(ACCESS_FINE_LOCATION)
    public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) {
    public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) {
        if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
        if (Compatibility.isChangeEnabled(BLOCK_GPS_STATUS_USAGE)) {
            throw new UnsupportedOperationException(
            throw new UnsupportedOperationException(
                    "GpsStatus APIs not supported, please use GnssStatus APIs instead");
                    "GpsStatus APIs not supported, please use GnssStatus APIs instead");
        }
        }
@@ -1976,7 +1999,7 @@ public class LocationManager {
    @Deprecated
    @Deprecated
    @RequiresPermission(ACCESS_FINE_LOCATION)
    @RequiresPermission(ACCESS_FINE_LOCATION)
    public boolean addGpsStatusListener(GpsStatus.Listener listener) {
    public boolean addGpsStatusListener(GpsStatus.Listener listener) {
        if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
        if (Compatibility.isChangeEnabled(BLOCK_GPS_STATUS_USAGE)) {
            throw new UnsupportedOperationException(
            throw new UnsupportedOperationException(
                    "GpsStatus APIs not supported, please use GnssStatus APIs instead");
                    "GpsStatus APIs not supported, please use GnssStatus APIs instead");
        }
        }
@@ -1996,7 +2019,7 @@ public class LocationManager {
     */
     */
    @Deprecated
    @Deprecated
    public void removeGpsStatusListener(GpsStatus.Listener listener) {
    public void removeGpsStatusListener(GpsStatus.Listener listener) {
        if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
        if (Compatibility.isChangeEnabled(BLOCK_GPS_STATUS_USAGE)) {
            throw new UnsupportedOperationException(
            throw new UnsupportedOperationException(
                    "GpsStatus APIs not supported, please use GnssStatus APIs instead");
                    "GpsStatus APIs not supported, please use GnssStatus APIs instead");
        }
        }
+10 −9
Original line number Original line Diff line number Diff line
@@ -21,10 +21,10 @@ import static android.app.compat.CompatChanges.isChangeEnabled;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.location.LocationManager.BLOCK_PENDING_INTENT_SYSTEM_API_USAGE;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
import static android.location.LocationManager.PREVENT_PENDING_INTENT_SYSTEM_API_USAGE;
import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS;
import static android.location.LocationRequest.LOW_POWER_EXCEPTIONS;


import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
@@ -597,15 +597,16 @@ public class LocationManagerService extends ILocationManager.Stub {
        // simplest to ensure these apis are simply never set for pending intent requests. the same
        // simplest to ensure these apis are simply never set for pending intent requests. the same
        // does not apply for listener requests since those will have the process (including the
        // does not apply for listener requests since those will have the process (including the
        // listener) killed on permission removal
        // listener) killed on permission removal
        if (isChangeEnabled(BLOCK_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) {
            boolean usesSystemApi = request.isLowPower()
            boolean usesSystemApi = request.isLowPower()
                    || request.isHiddenFromAppOps()
                    || request.isHiddenFromAppOps()
                    || request.isLocationSettingsIgnored()
                    || request.isLocationSettingsIgnored()
                    || !request.getWorkSource().isEmpty();
                    || !request.getWorkSource().isEmpty();
        if (usesSystemApi
            if (usesSystemApi) {
                && isChangeEnabled(PREVENT_PENDING_INTENT_SYSTEM_API_USAGE, identity.getUid())) {
                throw new SecurityException(
                throw new SecurityException(
                        "PendingIntent location requests may not use system APIs: " + request);
                        "PendingIntent location requests may not use system APIs: " + request);
            }
            }
        }


        request = validateLocationRequest(request, identity);
        request = validateLocationRequest(request, identity);


+4 −4
Original line number Original line Diff line number Diff line
@@ -296,15 +296,15 @@ public class GeofenceManager extends
            @Nullable String attributionTag) {
            @Nullable String attributionTag) {
        LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE);
        LocationPermissions.enforceCallingOrSelfLocationPermission(mContext, PERMISSION_FINE);


        CallerIdentity callerIdentity = CallerIdentity.fromBinder(mContext, packageName,
        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName,
                attributionTag, AppOpsManager.toReceiverId(pendingIntent));
                attributionTag, AppOpsManager.toReceiverId(pendingIntent));


        final long identity = Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();
        try {
        try {
            putRegistration(new GeofenceKey(pendingIntent, geofence),
            putRegistration(new GeofenceKey(pendingIntent, geofence),
                    new GeofenceRegistration(geofence, callerIdentity, pendingIntent));
                    new GeofenceRegistration(geofence, identity, pendingIntent));
        } finally {
        } finally {
            Binder.restoreCallingIdentity(identity);
            Binder.restoreCallingIdentity(ident);
        }
        }
    }
    }