diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index 9aa0c870e5126..46bd22148fb2e 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -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. * diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 6e597b2c1d631..ff004094ec59d 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -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. * - *
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. + *
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. * *
See {@link LocationRequest} documentation for an explanation of various request parameters * and how they can affect the received locations. * - *
If your application wants to passively observe location updates from any provider, then + *
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 { * *
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. * - *
When location callbacks are invoked, the system will hold a wakelock on your + *
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. * + *
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. + * *
To unregister for location updates, use {@link #removeUpdates(LocationListener)}.
*
* @param provider a provider listed by {@link #getAllProviders()}
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 0521b10a2530a..c57794f0f04a0 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -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.
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index e05bd3c22ae86..d3aa977f85b1a 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -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;
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 72734c47873eb..0329c3c78e20c 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -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;
diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java
index 138301ae934d5..cd8bf4a0154d3 100644
--- a/services/core/java/com/android/server/location/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/LocationProviderManager.java
@@ -16,13 +16,14 @@
package com.android.server.location;
-import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
-import static android.app.AlarmManager.WINDOW_EXACT;
+import static android.app.compat.CompatChanges.isChangeEnabled;
+import static android.location.LocationManager.DELIVER_HISTORICAL_LOCATIONS;
import static android.location.LocationManager.FUSED_PROVIDER;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.KEY_LOCATION_CHANGED;
import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
import static android.location.LocationManager.PASSIVE_PROVIDER;
+import static android.location.LocationRequest.PASSIVE_INTERVAL;
import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE;
import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF;
import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY;
@@ -36,11 +37,11 @@ import static com.android.server.location.LocationPermissions.PERMISSION_COARSE;
import static com.android.server.location.LocationPermissions.PERMISSION_FINE;
import static com.android.server.location.LocationPermissions.PERMISSION_NONE;
+import static java.lang.Math.max;
import static java.lang.Math.min;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
import android.annotation.Nullable;
-import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -88,6 +89,7 @@ import com.android.server.PendingIntentUtils;
import com.android.server.location.LocationPermissions.PermissionLevel;
import com.android.server.location.listeners.ListenerMultiplexer;
import com.android.server.location.listeners.RemoteListenerRegistration;
+import com.android.server.location.util.AlarmHelper;
import com.android.server.location.util.AppForegroundHelper;
import com.android.server.location.util.AppForegroundHelper.AppForegroundListener;
import com.android.server.location.util.AppOpsHelper;
@@ -118,12 +120,12 @@ class LocationProviderManager extends
LocationProviderManager.Registration, ProviderRequest> implements
AbstractLocationProvider.Listener {
- // fastest interval at which clients may receive coarse locations
- public static final long FASTEST_COARSE_INTERVAL_MS = 10 * 60 * 1000;
-
private static final String WAKELOCK_TAG = "*location*";
private static final long WAKELOCK_TIMEOUT_MS = 30 * 1000;
+ // fastest interval at which clients may receive coarse locations
+ private static final long MIN_COARSE_INTERVAL_MS = 10 * 60 * 1000;
+
// max interval to be considered "high power" request
private static final long MAX_HIGH_POWER_INTERVAL_MS = 5 * 60 * 1000;
@@ -133,8 +135,15 @@ class LocationProviderManager extends
// max timeout allowed for getting the current location
private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
- // max jitter allowed for fastest interval evaluation
- private static final int MAX_FASTEST_INTERVAL_JITTER_MS = 100;
+ // max jitter allowed for min update interval as a percentage of the interval
+ private static final float FASTEST_INTERVAL_JITTER_PERCENTAGE = .10f;
+
+ // max absolute jitter allowed for min update interval evaluation
+ private static final int MAX_FASTEST_INTERVAL_JITTER_MS = 5 * 1000;
+
+ // minimum amount of request delay in order to respect the delay, below this value the request
+ // will just be scheduled immediately
+ private static final long MIN_REQUEST_DELAY_MS = 30 * 1000;
protected interface LocationTransport {
@@ -221,6 +230,7 @@ class LocationProviderManager extends
/**
* Must be implemented to return the location this operation intends to deliver.
*/
+ @Nullable
Location getLocation();
}
@@ -312,7 +322,7 @@ class LocationProviderManager extends
mLocationAttributionHelper.reportLocationStart(getIdentity(), getName(), getKey());
}
onHighPowerUsageChanged();
- return null;
+ return onProviderListenerActive();
}
@Override
@@ -325,6 +335,22 @@ class LocationProviderManager extends
if (!getRequest().isHiddenFromAppOps()) {
mLocationAttributionHelper.reportLocationStop(getIdentity(), getName(), getKey());
}
+ return onProviderListenerInactive();
+ }
+
+ /**
+ * Subclasses may override this instead of {@link #onActive()}.
+ */
+ @GuardedBy("mLock")
+ protected LocationListenerOperation onProviderListenerActive() {
+ return null;
+ }
+
+ /**
+ * Subclasses may override this instead of {@link #onInactive()} ()}.
+ */
+ @GuardedBy("mLock")
+ protected LocationListenerOperation onProviderListenerInactive() {
return null;
}
@@ -333,6 +359,14 @@ class LocationProviderManager extends
return mProviderLocationRequest;
}
+ @GuardedBy("mLock")
+ final void initializeLastLocation(@Nullable Location location) {
+ if (mLastLocation == null) {
+ mLastLocation = location;
+ }
+ }
+
+ @GuardedBy("mLock")
public final Location getLastDeliveredLocation() {
return mLastLocation;
}
@@ -465,9 +499,27 @@ class LocationProviderManager extends
}
private LocationRequest calculateProviderLocationRequest() {
- LocationRequest.Builder builder = new LocationRequest.Builder(super.getRequest());
+ LocationRequest baseRequest = super.getRequest();
+ LocationRequest.Builder builder = new LocationRequest.Builder(baseRequest);
- if (super.getRequest().isLocationSettingsIgnored()) {
+ if (mPermissionLevel < PERMISSION_FINE) {
+ switch (baseRequest.getQuality()) {
+ case LocationRequest.ACCURACY_FINE:
+ builder.setQuality(LocationRequest.ACCURACY_BLOCK);
+ break;
+ case LocationRequest.POWER_HIGH:
+ builder.setQuality(LocationRequest.POWER_LOW);
+ break;
+ }
+ if (baseRequest.getIntervalMillis() < MIN_COARSE_INTERVAL_MS) {
+ builder.setIntervalMillis(MIN_COARSE_INTERVAL_MS);
+ }
+ if (baseRequest.getMinUpdateIntervalMillis() < MIN_COARSE_INTERVAL_MS) {
+ builder.clearMinUpdateIntervalMillis();
+ }
+ }
+
+ if (baseRequest.isLocationSettingsIgnored()) {
// if we are not currently allowed use location settings ignored, disable it
if (!mSettingsHelper.getIgnoreSettingsPackageWhitelist().contains(
getIdentity().getPackageName()) && !mLocationManagerInternal.isProvider(
@@ -476,10 +528,10 @@ class LocationProviderManager extends
}
}
- if (!super.getRequest().isLocationSettingsIgnored() && !isThrottlingExempt()) {
+ if (!baseRequest.isLocationSettingsIgnored() && !isThrottlingExempt()) {
// throttle in the background
if (!mForeground) {
- builder.setIntervalMillis(Math.max(super.getRequest().getIntervalMillis(),
+ builder.setIntervalMillis(max(baseRequest.getIntervalMillis(),
mSettingsHelper.getBackgroundThrottleIntervalMs()));
}
}
@@ -534,7 +586,7 @@ class LocationProviderManager extends
}
protected abstract class LocationRegistration extends Registration implements
- AlarmManager.OnAlarmListener, ProviderEnabledListener {
+ OnAlarmListener, ProviderEnabledListener {
private final PowerManager.WakeLock mWakeLock;
@@ -561,17 +613,15 @@ class LocationProviderManager extends
@GuardedBy("mLock")
@Override
protected final void onProviderListenerRegister() {
- mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs(
- SystemClock.elapsedRealtime());
+ long registerTimeMs = SystemClock.elapsedRealtime();
+ mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs(registerTimeMs);
// add alarm for expiration
- if (mExpirationRealtimeMs < SystemClock.elapsedRealtime()) {
- remove();
+ if (mExpirationRealtimeMs <= registerTimeMs) {
+ onAlarm();
} else if (mExpirationRealtimeMs < Long.MAX_VALUE) {
- AlarmManager alarmManager = Objects.requireNonNull(
- mContext.getSystemService(AlarmManager.class));
- alarmManager.set(ELAPSED_REALTIME_WAKEUP, mExpirationRealtimeMs, WINDOW_EXACT,
- 0, this, FgThread.getHandler(), getWorkSource());
+ mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this,
+ getWorkSource());
}
// start listening for provider enabled/disabled events
@@ -594,9 +644,7 @@ class LocationProviderManager extends
// remove alarm for expiration
if (mExpirationRealtimeMs < Long.MAX_VALUE) {
- AlarmManager alarmManager = Objects.requireNonNull(
- mContext.getSystemService(AlarmManager.class));
- alarmManager.cancel(this);
+ mAlarmHelper.cancel(this);
}
onLocationListenerUnregister();
@@ -614,6 +662,39 @@ class LocationProviderManager extends
@GuardedBy("mLock")
protected void onLocationListenerUnregister() {}
+ @GuardedBy("mLock")
+ @Override
+ protected final LocationListenerOperation onProviderListenerActive() {
+ // a new registration may not get a location immediately, the provider request may be
+ // delayed. therefore we deliver a historical location if available. since delivering an
+ // older location could be considered a breaking change for some applications, we only
+ // do so for apps targeting S+.
+ if (isChangeEnabled(DELIVER_HISTORICAL_LOCATIONS, getIdentity().getUid())) {
+ long maxLocationAgeMs = getRequest().getIntervalMillis();
+ Location lastDeliveredLocation = getLastDeliveredLocation();
+ if (lastDeliveredLocation != null) {
+ // ensure that location is fresher than the last delivered location
+ maxLocationAgeMs = min(maxLocationAgeMs,
+ lastDeliveredLocation.getElapsedRealtimeAgeMillis() - 1);
+ }
+
+ // requests are never delayed less than MIN_REQUEST_DELAY_MS, so it only makes sense
+ // to deliver historical locations to clients with a last location older than that
+ if (maxLocationAgeMs > MIN_REQUEST_DELAY_MS) {
+ Location lastLocation = getLastLocationUnsafe(
+ getIdentity().getUserId(),
+ PERMISSION_FINE, // acceptLocationChange() handles coarsening this
+ getRequest().isLocationSettingsIgnored(),
+ maxLocationAgeMs);
+ if (lastLocation != null) {
+ return acceptLocationChange(lastLocation);
+ }
+ }
+ }
+
+ return null;
+ }
+
@Override
public void onAlarm() {
if (D) {
@@ -624,6 +705,8 @@ class LocationProviderManager extends
synchronized (mLock) {
remove();
+ // no need to remove alarm after it's fired
+ mExpirationRealtimeMs = Long.MAX_VALUE;
}
}
@@ -658,11 +741,11 @@ class LocationProviderManager extends
Location lastDeliveredLocation = getLastDeliveredLocation();
if (lastDeliveredLocation != null) {
// check fastest interval
- long deltaMs = NANOSECONDS.toMillis(
- location.getElapsedRealtimeNanos()
- - lastDeliveredLocation.getElapsedRealtimeNanos());
- if (deltaMs < getRequest().getMinUpdateIntervalMillis()
- - MAX_FASTEST_INTERVAL_JITTER_MS) {
+ long deltaMs = location.getElapsedRealtimeMillis()
+ - lastDeliveredLocation.getElapsedRealtimeMillis();
+ long maxJitterMs = min((long) (FASTEST_INTERVAL_JITTER_PERCENTAGE
+ * getRequest().getIntervalMillis()), MAX_FASTEST_INTERVAL_JITTER_MS);
+ if (deltaMs < getRequest().getMinUpdateIntervalMillis() - maxJitterMs) {
return null;
}
@@ -871,7 +954,7 @@ class LocationProviderManager extends
}
protected final class GetCurrentLocationListenerRegistration extends Registration implements
- IBinder.DeathRecipient, ProviderEnabledListener, AlarmManager.OnAlarmListener {
+ IBinder.DeathRecipient, ProviderEnabledListener, OnAlarmListener {
private volatile LocationTransport mTransport;
@@ -902,15 +985,15 @@ class LocationProviderManager extends
remove();
}
- mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs(
- SystemClock.elapsedRealtime());
+ long registerTimeMs = SystemClock.elapsedRealtime();
+ mExpirationRealtimeMs = getRequest().getExpirationRealtimeMs(registerTimeMs);
// add alarm for expiration
- if (mExpirationRealtimeMs < Long.MAX_VALUE) {
- AlarmManager alarmManager = Objects.requireNonNull(
- mContext.getSystemService(AlarmManager.class));
- alarmManager.set(ELAPSED_REALTIME_WAKEUP, mExpirationRealtimeMs, WINDOW_EXACT,
- 0, this, FgThread.getHandler(), getWorkSource());
+ if (mExpirationRealtimeMs <= registerTimeMs) {
+ onAlarm();
+ } else if (mExpirationRealtimeMs < Long.MAX_VALUE) {
+ mAlarmHelper.setDelayedAlarm(mExpirationRealtimeMs - registerTimeMs, this,
+ getWorkSource());
}
// if this request is ignoring location settings, then we don't want to immediately fail
@@ -935,9 +1018,7 @@ class LocationProviderManager extends
// remove alarm for expiration
if (mExpirationRealtimeMs < Long.MAX_VALUE) {
- AlarmManager alarmManager = Objects.requireNonNull(
- mContext.getSystemService(AlarmManager.class));
- alarmManager.cancel(this);
+ mAlarmHelper.cancel(this);
}
((IBinder) getKey()).unlinkToDeath(this, 0);
@@ -953,6 +1034,8 @@ class LocationProviderManager extends
synchronized (mLock) {
deliverLocation(null);
+ // no need to remove alarm after it's fired
+ mExpirationRealtimeMs = Long.MAX_VALUE;
}
}
@@ -964,6 +1047,12 @@ class LocationProviderManager extends
Preconditions.checkState(Thread.holdsLock(mLock));
}
+ // check expiration time - alarm is not guaranteed to go off at the right time,
+ // especially for short intervals
+ if (SystemClock.elapsedRealtime() >= mExpirationRealtimeMs) {
+ fineLocation = null;
+ }
+
// lastly - note app ops
Location location;
if (fineLocation == null) {
@@ -1077,6 +1166,7 @@ class LocationProviderManager extends
protected final LocationManagerInternal mLocationManagerInternal;
protected final SettingsHelper mSettingsHelper;
protected final UserInfoHelper mUserInfoHelper;
+ protected final AlarmHelper mAlarmHelper;
protected final AppOpsHelper mAppOpsHelper;
protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final AppForegroundHelper mAppForegroundHelper;
@@ -1120,6 +1210,9 @@ class LocationProviderManager extends
// acquiring mLock makes operations on mProvider atomic, but is otherwise unnecessary
protected final MockableLocationProvider mProvider;
+ @GuardedBy("mLock")
+ @Nullable private OnAlarmListener mDelayedRegister;
+
LocationProviderManager(Context context, Injector injector, String name,
@Nullable PassiveLocationProviderManager passiveManager) {
mContext = context;
@@ -1135,6 +1228,7 @@ class LocationProviderManager extends
LocalServices.getService(LocationManagerInternal.class));
mSettingsHelper = injector.getSettingsHelper();
mUserInfoHelper = injector.getUserInfoHelper();
+ mAlarmHelper = injector.getAlarmHelper();
mAppOpsHelper = injector.getAppOpsHelper();
mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mAppForegroundHelper = injector.getAppForegroundHelper();
@@ -1344,7 +1438,7 @@ class LocationProviderManager extends
}
Location location = getLastLocationUnsafe(identity.getUserId(), permissionLevel,
- ignoreLocationSettings);
+ ignoreLocationSettings, Long.MAX_VALUE);
// we don't note op here because we don't know what the client intends to do with the
// location, the client is responsible for noting if necessary
@@ -1364,13 +1458,14 @@ class LocationProviderManager extends
*/
@Nullable
public Location getLastLocationUnsafe(int userId, @PermissionLevel int permissionLevel,
- boolean ignoreLocationSettings) {
+ boolean ignoreLocationSettings, long maximumAgeMs) {
if (userId == UserHandle.USER_ALL) {
+ // find the most recent location across all users
Location lastLocation = null;
final int[] runningUserIds = mUserInfoHelper.getRunningUserIds();
for (int i = 0; i < runningUserIds.length; i++) {
Location next = getLastLocationUnsafe(runningUserIds[i], permissionLevel,
- ignoreLocationSettings);
+ ignoreLocationSettings, maximumAgeMs);
if (lastLocation == null || (next != null && next.getElapsedRealtimeNanos()
> lastLocation.getElapsedRealtimeNanos())) {
lastLocation = next;
@@ -1381,18 +1476,30 @@ class LocationProviderManager extends
Preconditions.checkArgument(userId >= 0);
+ Location location;
synchronized (mLock) {
LastLocation lastLocation = mLastLocations.get(userId);
if (lastLocation == null) {
- return null;
+ location = null;
+ } else {
+ location = lastLocation.get(permissionLevel, ignoreLocationSettings);
}
- return lastLocation.get(permissionLevel, ignoreLocationSettings);
}
+
+ if (location == null) {
+ return null;
+ }
+
+ if (location.getElapsedRealtimeAgeMillis() > maximumAgeMs) {
+ return null;
+ }
+
+ return location;
}
public void injectLastLocation(Location location, int userId) {
synchronized (mLock) {
- if (getLastLocationUnsafe(userId, PERMISSION_FINE, false) == null) {
+ if (getLastLocationUnsafe(userId, PERMISSION_FINE, false, Long.MAX_VALUE) == null) {
setLastLocation(location, userId);
}
}
@@ -1455,22 +1562,14 @@ class LocationProviderManager extends
return null;
}
- Location lastLocation = getLastLocationUnsafe(callerIdentity.getUserId(),
- permissionLevel, request.isLocationSettingsIgnored());
+ Location lastLocation = getLastLocationUnsafe(
+ callerIdentity.getUserId(),
+ permissionLevel,
+ request.isLocationSettingsIgnored(),
+ MAX_CURRENT_LOCATION_AGE_MS);
if (lastLocation != null) {
- long locationAgeMs = NANOSECONDS.toMillis(
- SystemClock.elapsedRealtimeNanos()
- - lastLocation.getElapsedRealtimeNanos());
- if (locationAgeMs < MAX_CURRENT_LOCATION_AGE_MS) {
- registration.deliverLocation(lastLocation);
- return null;
- }
-
- if (!mAppForegroundHelper.isAppForeground(Binder.getCallingUid())
- && locationAgeMs < mSettingsHelper.getBackgroundThrottleIntervalMs()) {
- registration.deliverLocation(null);
- return null;
- }
+ registration.deliverLocation(lastLocation);
+ return null;
}
// if last location isn't good enough then we add a location request
@@ -1627,6 +1726,16 @@ class LocationProviderManager extends
registration.isForeground());
}
+ @GuardedBy("mLock")
+ @Override
+ protected void onRegistrationReplaced(Object key, Registration oldRegistration,
+ Registration newRegistration) {
+ // by saving the last delivered location state we are able to potentially delay the
+ // resulting provider request longer and save additional power
+ newRegistration.initializeLastLocation(oldRegistration.getLastDeliveredLocation());
+ super.onRegistrationReplaced(key, oldRegistration, newRegistration);
+ }
+
@GuardedBy("mLock")
@Override
protected void onRegistrationRemoved(Object key, Registration registration) {
@@ -1652,21 +1761,61 @@ class LocationProviderManager extends
@GuardedBy("mLock")
@Override
- protected boolean registerWithService(ProviderRequest mergedRequest,
+ protected boolean registerWithService(ProviderRequest request,
Collection