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

Commit 6e410506 authored by Soonil Nagarkar's avatar Soonil Nagarkar
Browse files

Fix background request throttling + jitter calculation

Prevent applications from bypassing background location throttling by
adding/removing requests. This allows provider managers to delay
requests in the event the interval has decreased so that power can be
saved. Clients that target S and above may now receive historical
locations in exchange, so that they are not losing locations overall.
Locationp providers can optionally implement delays themselves in the
event of an interval increase.

Also updates jitter calculation to be a capped percentage of the
request interval, rather than a flat cap.

Bug: 73144566
Test: manual + presubmits
Change-Id: I243e813f0c0c504850e2a3e777787f49fc6f7a57
parent 3709d3ef
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.location;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -585,6 +587,11 @@ public class Location implements Parcelable {
        return mElapsedRealtimeNanos;
    }

    /** @hide */
    public long getElapsedRealtimeMillis() {
        return NANOSECONDS.toMillis(getElapsedRealtimeNanos());
    }

    /** @hide */
    public long getElapsedRealtimeAgeNanos(long referenceRealtimeNs) {
        return referenceRealtimeNs - mElapsedRealtimeNanos;
@@ -595,6 +602,11 @@ public class Location implements Parcelable {
        return getElapsedRealtimeAgeNanos(SystemClock.elapsedRealtimeNanos());
    }

    /** @hide */
    public long getElapsedRealtimeAgeMillis() {
        return NANOSECONDS.toMillis(getElapsedRealtimeAgeNanos());
    }

    /**
     * Set the time of this fix, in elapsed real-time since system boot.
     *
+24 −5
Original line number Diff line number Diff line
@@ -88,6 +88,16 @@ import java.util.function.Consumer;
@RequiresFeature(PackageManager.FEATURE_LOCATION)
public class LocationManager {

    /**
     * For apps targeting Android S and above, location clients may receive historical locations
     * (from before the present time) under some circumstances.
     *
     * @hide
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    public static final long DELIVER_HISTORICAL_LOCATIONS = 73144566L;

    /**
     * For apps targeting Android R and above, {@link #getProvider(String)} will no longer throw any
     * security exceptions.
@@ -1256,13 +1266,15 @@ public class LocationManager {
     * arguments. The same listener may be used across multiple providers with different requests
     * for each provider.
     *
     * <p>It may take a while to receive the first location update. If an immediate location is
     * required, applications may use the {@link #getLastKnownLocation(String)} method.
     * <p>It may take some time to receive the first location update depending on the conditions the
     * device finds itself in. In order to take advantage of cached locations, application may
     * consider using {@link #getLastKnownLocation(String)} or {@link #getCurrentLocation(String,
     * LocationRequest, CancellationSignal, Executor, Consumer)} instead.
     *
     * <p>See {@link LocationRequest} documentation for an explanation of various request parameters
     * and how they can affect the received locations.
     *
     * <p> If your application wants to passively observe location updates from any provider, then
     * <p>If your application wants to passively observe location updates from all providers, then
     * use the {@link #PASSIVE_PROVIDER}. This provider does not turn on or modify active location
     * providers, so you do not need to be as careful about minimum time and minimum distance
     * parameters. However, if your application performs heavy work on a location update (such as
@@ -1271,13 +1283,20 @@ public class LocationManager {
     *
     * <p>In case the provider you have selected is disabled, location updates will cease, and a
     * provider availability update will be sent. As soon as the provider is enabled again, another
     * provider availability update will be sent and location updates will immediately resume.
     * provider availability update will be sent and location updates will resume.
     *
     * <p>When location callbacks are invoked, the system will hold a wakelock on your
     * application's behalf for some period of time, but not indefinitely. If your application
     * requires a long running wakelock within the location callback, you should acquire it
     * yourself.
     *
     * <p>Spamming location requests is a drain on system resources, and the system has preventative
     * measures in place to ensure that this behavior will never result in more locations than could
     * be achieved with a single location request with an equivalent interval that is left in place
     * the whole time. As part of this amelioration, applications that target Android S and above
     * may receive cached or historical locations through their listener. These locations will never
     * be older than the interval of the location request.
     *
     * <p>To unregister for location updates, use {@link #removeUpdates(LocationListener)}.
     *
     * @param provider a provider listed by {@link #getAllProviders()}
+13 −0
Original line number Diff line number Diff line
@@ -27,6 +27,8 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
@@ -44,6 +46,17 @@ import java.util.Objects;
 */
public final class LocationRequest implements Parcelable {

    /**
     * For apps targeting Android S and above, all LocationRequest objects marked as low power will
     * throw exceptions if the caller does not have the LOCATION_HARDWARE permission, instead of
     * silently dropping the low power part of the request.
     *
     * @hide
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
    public static final long LOW_POWER_EXCEPTIONS = 168936375L;

    /**
     * Represents a passive only request. Such a request will not trigger any active locations or
     * power usage itself, but may receive locations generated in response to other requests.
+0 −2
Original line number Diff line number Diff line
@@ -21,8 +21,6 @@ import static android.location.LocationManager.NETWORK_PROVIDER;

import static androidx.test.ext.truth.location.LocationSubject.assertThat;

import static com.google.common.truth.Truth.assertThat;

import android.content.Context;
import android.location.Criteria;
import android.location.Location;
+26 −24
Original line number Diff line number Diff line
@@ -23,10 +23,10 @@ import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.NETWORK_PROVIDER;
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_FINE;
import static com.android.server.location.LocationProviderManager.FASTEST_COARSE_INTERVAL_MS;

import static java.util.concurrent.TimeUnit.NANOSECONDS;

@@ -36,6 +36,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.PendingIntent;
import android.app.compat.CompatChanges;
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
@@ -82,12 +83,12 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.location.LocationPermissions.PermissionLevel;
import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
import com.android.server.location.LocationRequestStatistics.PackageStatistics;
import com.android.server.location.geofence.GeofenceManager;
import com.android.server.location.geofence.GeofenceProxy;
import com.android.server.location.gnss.GnssManagerService;
import com.android.server.location.util.AlarmHelper;
import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppOpsHelper;
import com.android.server.location.util.Injector;
@@ -97,6 +98,7 @@ import com.android.server.location.util.LocationPowerSaveModeHelper;
import com.android.server.location.util.LocationUsageLogger;
import com.android.server.location.util.ScreenInteractiveHelper;
import com.android.server.location.util.SettingsHelper;
import com.android.server.location.util.SystemAlarmHelper;
import com.android.server.location.util.SystemAppForegroundHelper;
import com.android.server.location.util.SystemAppOpsHelper;
import com.android.server.location.util.SystemLocationPermissionsHelper;
@@ -569,7 +571,7 @@ public class LocationManagerService extends ILocationManager.Stub {
                    new IllegalArgumentException());
        }

        request = validateAndSanitizeLocationRequest(request, permissionLevel);
        request = validateLocationRequest(request);

        LocationProviderManager manager = getLocationProviderManager(provider);
        Preconditions.checkArgument(manager != null,
@@ -591,7 +593,7 @@ public class LocationManagerService extends ILocationManager.Stub {
        // clients in the system process must have an attribution tag set
        Preconditions.checkArgument(identity.getPid() != Process.myPid() || attributionTag != null);

        request = validateAndSanitizeLocationRequest(request, permissionLevel);
        request = validateLocationRequest(request);

        LocationProviderManager manager = getLocationProviderManager(provider);
        Preconditions.checkArgument(manager != null,
@@ -600,8 +602,7 @@ public class LocationManagerService extends ILocationManager.Stub {
        manager.registerLocationRequest(request, identity, permissionLevel, pendingIntent);
    }

    private LocationRequest validateAndSanitizeLocationRequest(LocationRequest request,
            @PermissionLevel int permissionLevel) {
    private LocationRequest validateLocationRequest(LocationRequest request) {
        WorkSource workSource = request.getWorkSource();
        if (workSource != null && !workSource.isEmpty()) {
            mContext.enforceCallingOrSelfPermission(
@@ -620,26 +621,20 @@ public class LocationManagerService extends ILocationManager.Stub {
        }

        LocationRequest.Builder sanitized = new LocationRequest.Builder(request);
        if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE) != PERMISSION_GRANTED) {
            sanitized.setLowPower(false);
        }
        if (permissionLevel < PERMISSION_FINE) {
            switch (request.getQuality()) {
                case LocationRequest.ACCURACY_FINE:
                    sanitized.setQuality(LocationRequest.ACCURACY_BLOCK);
                    break;
                case LocationRequest.POWER_HIGH:
                    sanitized.setQuality(LocationRequest.POWER_LOW);
                    break;
            }

            if (request.getIntervalMillis() < FASTEST_COARSE_INTERVAL_MS) {
                sanitized.setIntervalMillis(FASTEST_COARSE_INTERVAL_MS);
        if (CompatChanges.isChangeEnabled(LOW_POWER_EXCEPTIONS, Binder.getCallingUid())) {
            if (request.isLowPower()) {
                mContext.enforceCallingOrSelfPermission(
                        permission.LOCATION_HARDWARE,
                        "low power request requires " + permission.LOCATION_HARDWARE);
            }
            if (request.getMinUpdateIntervalMillis() < FASTEST_COARSE_INTERVAL_MS) {
                sanitized.clearMinUpdateIntervalMillis();
        } else {
            if (mContext.checkCallingPermission(permission.LOCATION_HARDWARE)
                    != PERMISSION_GRANTED) {
                sanitized.setLowPower(false);
            }
        }

        if (request.getWorkSource() != null) {
            if (request.getWorkSource().isEmpty()) {
                sanitized.setWorkSource(null);
@@ -716,7 +711,7 @@ public class LocationManagerService extends ILocationManager.Stub {
        // clients in the system process must have an attribution tag set
        Preconditions.checkState(identity.getPid() != Process.myPid() || attributionTag != null);

        request = validateAndSanitizeLocationRequest(request, permissionLevel);
        request = validateLocationRequest(request);

        LocationProviderManager manager = getLocationProviderManager(provider);
        Preconditions.checkArgument(manager != null,
@@ -735,7 +730,7 @@ public class LocationManagerService extends ILocationManager.Stub {

            // use fine permission level to avoid creating unnecessary coarse locations
            Location location = gpsManager.getLastLocationUnsafe(UserHandle.USER_ALL,
                    PERMISSION_FINE, false);
                    PERMISSION_FINE, false, Long.MAX_VALUE);
            if (location == null) {
                return null;
            }
@@ -1237,6 +1232,7 @@ public class LocationManagerService extends ILocationManager.Stub {
    private static class SystemInjector implements Injector {

        private final UserInfoHelper mUserInfoHelper;
        private final AlarmHelper mAlarmHelper;
        private final SystemAppOpsHelper mAppOpsHelper;
        private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
        private final SystemSettingsHelper mSettingsHelper;
@@ -1249,6 +1245,7 @@ public class LocationManagerService extends ILocationManager.Stub {

        SystemInjector(Context context, UserInfoHelper userInfoHelper) {
            mUserInfoHelper = userInfoHelper;
            mAlarmHelper = new SystemAlarmHelper(context);
            mAppOpsHelper = new SystemAppOpsHelper(context);
            mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
                    mAppOpsHelper);
@@ -1275,6 +1272,11 @@ public class LocationManagerService extends ILocationManager.Stub {
            return mUserInfoHelper;
        }

        @Override
        public AlarmHelper getAlarmHelper() {
            return mAlarmHelper;
        }

        @Override
        public AppOpsHelper getAppOpsHelper() {
            return mAppOpsHelper;
Loading