summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSoonil Nagarkar <sooniln@google.com>2021-02-02 14:41:00 -0800
committerSoonil Nagarkar <sooniln@google.com>2021-02-03 17:02:14 -0800
commite97179d846c3942e3455afb0b413d956093560e7 (patch)
treecaa87658706be5118cd61fd0509e218ab3fc0738
parent9ee2e0d368836a53154f45c7b6d4a34614175481 (diff)
Update batching APIs
After API council feedback, use List<Location> in public APIs rather than LocationResult. Bug: 173712888 Test: atest CtsLocationFineTestCases Change-Id: I02caa1f9c7164b52ef55c71ca14fb2bd7e311a85
-rw-r--r--core/api/current.txt16
-rw-r--r--core/api/system-current.txt6
-rw-r--r--location/java/android/location/ILocationListener.aidl4
-rw-r--r--location/java/android/location/LocationListener.java16
-rw-r--r--location/java/android/location/LocationManager.java23
-rw-r--r--location/java/android/location/LocationRequest.java2
-rw-r--r--location/java/android/location/LocationResult.java64
-rw-r--r--location/java/android/location/provider/ILocationProviderManager.aidl5
-rw-r--r--location/java/android/location/provider/LocationProviderBase.java88
-rw-r--r--location/lib/api/current.txt4
-rw-r--r--location/lib/java/com/android/location/provider/LocationProviderBase.java101
-rw-r--r--packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java13
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java3
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java15
-rw-r--r--services/core/java/com/android/server/location/provider/MockLocationProvider.java2
-rw-r--r--services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java18
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java41
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java4
18 files changed, 259 insertions, 166 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 3efb78943bd7..68508263a8a1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -19451,7 +19451,7 @@ package android.location {
public interface LocationListener {
method public default void onFlushComplete(int);
method public void onLocationChanged(@NonNull android.location.Location);
- method public default void onLocationChanged(@NonNull android.location.LocationResult);
+ method public default void onLocationChanged(@NonNull java.util.List<android.location.Location>);
method public default void onProviderDisabled(@NonNull String);
method public default void onProviderEnabled(@NonNull String);
method @Deprecated public default void onStatusChanged(String, int, android.os.Bundle);
@@ -19537,8 +19537,8 @@ package android.location {
field public static final String FUSED_PROVIDER = "fused";
field public static final String GPS_PROVIDER = "gps";
field public static final String KEY_FLUSH_COMPLETE = "flushComplete";
+ field public static final String KEY_LOCATIONS = "locations";
field public static final String KEY_LOCATION_CHANGED = "location";
- field public static final String KEY_LOCATION_RESULT = "locationResult";
field public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
field public static final String KEY_PROXIMITY_ENTERING = "entering";
field @Deprecated public static final String KEY_STATUS_CHANGED = "status";
@@ -19596,18 +19596,6 @@ package android.location {
method @NonNull public android.location.LocationRequest.Builder setQuality(int);
}
- public final class LocationResult implements android.os.Parcelable {
- method @NonNull public java.util.List<android.location.Location> asList();
- method @NonNull public static android.location.LocationResult create(@NonNull android.location.Location);
- method @NonNull public static android.location.LocationResult create(@NonNull java.util.List<android.location.Location>);
- method public int describeContents();
- method @NonNull public android.location.Location get(@IntRange(from=0) int);
- method @NonNull public android.location.Location getLastLocation();
- method @IntRange(from=1) public int size();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationResult> CREATOR;
- }
-
public interface OnNmeaMessageListener {
method public void onNmeaMessage(String, long);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b748b918d1c6..45dace49b261 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4680,10 +4680,6 @@ package android.location {
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource);
}
- public final class LocationResult implements android.os.Parcelable {
- method @NonNull public static android.location.LocationResult wrap(@NonNull android.location.Location);
- }
-
public final class SatellitePvt implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.location.SatellitePvt.ClockInfo getClockInfo();
@@ -4750,7 +4746,7 @@ package android.location.provider {
method public abstract void onSendExtraCommand(@NonNull String, @Nullable android.os.Bundle);
method public abstract void onSetRequest(@NonNull android.location.provider.ProviderRequest);
method public void reportLocation(@NonNull android.location.Location);
- method public void reportLocation(@NonNull android.location.LocationResult);
+ method public void reportLocations(@NonNull java.util.List<android.location.Location>);
method public void setAllowed(boolean);
method public void setProperties(@NonNull android.location.provider.ProviderProperties);
field public static final String ACTION_FUSED_PROVIDER = "com.android.location.service.FusedLocationProvider";
diff --git a/location/java/android/location/ILocationListener.aidl b/location/java/android/location/ILocationListener.aidl
index ce92661cb87c..40d8615b95f7 100644
--- a/location/java/android/location/ILocationListener.aidl
+++ b/location/java/android/location/ILocationListener.aidl
@@ -16,7 +16,7 @@
package android.location;
-import android.location.LocationResult;
+import android.location.Location;
import android.os.IRemoteCallback;
/**
@@ -24,7 +24,7 @@ import android.os.IRemoteCallback;
*/
oneway interface ILocationListener
{
- void onLocationChanged(in LocationResult locationResult, in @nullable IRemoteCallback onCompleteCallback);
+ void onLocationChanged(in List<Location> locations, in @nullable IRemoteCallback onCompleteCallback);
void onProviderEnabledChanged(String provider, boolean enabled);
void onFlushComplete(int requestCode);
}
diff --git a/location/java/android/location/LocationListener.java b/location/java/android/location/LocationListener.java
index 523117b39762..35a40910e373 100644
--- a/location/java/android/location/LocationListener.java
+++ b/location/java/android/location/LocationListener.java
@@ -19,6 +19,7 @@ package android.location;
import android.annotation.NonNull;
import android.os.Bundle;
+import java.util.List;
import java.util.concurrent.Executor;
/**
@@ -48,15 +49,18 @@ public interface LocationListener {
/**
* Called when the location has changed and locations are being delivered in batches. The
* default implementation calls through to ({@link #onLocationChanged(Location)} with all
- * locations in the batch, from earliest to latest.
+ * locations in the batch. The list of locations is always guaranteed to be non-empty, and is
+ * always guaranteed to be ordered from earliest location to latest location (so that the
+ * earliest location in the batch is at index 0 in the list, and the latest location in the
+ * batch is at index size-1 in the list).
*
* @see LocationRequest#getMaxUpdateDelayMillis()
- * @param locationResult the location result list
+ * @param locations the location list
*/
- default void onLocationChanged(@NonNull LocationResult locationResult) {
- final int size = locationResult.size();
- for (int i = 0; i < size; ++i) {
- onLocationChanged(locationResult.get(i));
+ default void onLocationChanged(@NonNull List<Location> locations) {
+ final int size = locations.size();
+ for (int i = 0; i < size; i++) {
+ onLocationChanged(locations.get(i));
}
}
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index fdb044d0dcf6..add3d31e9435 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -231,19 +231,26 @@ public class LocationManager {
/**
* Key used for an extra holding a {@link Location} value when a location change is sent using
- * a PendingIntent.
+ * a PendingIntent. If the location change includes a list of batched locations via
+ * {@link #KEY_LOCATIONS} then this key will still be present, and will hold the last location
+ * in the batch. Use {@link Intent#getParcelableExtra(String)} to retrieve the location.
*
* @see #requestLocationUpdates(String, LocationRequest, PendingIntent)
*/
public static final String KEY_LOCATION_CHANGED = "location";
/**
- * Key used for an extra holding a {@link LocationResult} value when a location change is sent
- * using a PendingIntent.
+ * Key used for an extra holding a array of {@link Location}s when a location change is sent
+ * using a PendingIntent. This key will only be present if the location change includes
+ * multiple (ie, batched) locations, otherwise only {@link #KEY_LOCATION_CHANGED} will be
+ * present. Use {@link Intent#getParcelableArrayExtra(String)} to retrieve the locations.
+ *
+ * <p>The array of locations will never be empty, and will ordered from earliest location to
+ * latest location, the same as with {@link LocationListener#onLocationChanged(List)}.
*
* @see #requestLocationUpdates(String, LocationRequest, PendingIntent)
*/
- public static final String KEY_LOCATION_RESULT = "locationResult";
+ public static final String KEY_LOCATIONS = "locations";
/**
* Key used for an extra holding an integer request code when location flush completion is sent
@@ -2969,12 +2976,12 @@ public class LocationManager {
}
@Override
- public void onLocationChanged(LocationResult locationResult,
+ public void onLocationChanged(List<Location> locations,
@Nullable IRemoteCallback onCompleteCallback) {
executeSafely(mExecutor, () -> mListener, new ListenerOperation<LocationListener>() {
@Override
public void operate(LocationListener listener) {
- listener.onLocationChanged(locationResult);
+ listener.onLocationChanged(locations);
}
@Override
@@ -3321,8 +3328,8 @@ public class LocationManager {
}
@Override
- public void onLocationChanged(@NonNull LocationResult locationResult) {
- mCallback.onLocationBatch(locationResult.asList());
+ public void onLocationChanged(@NonNull List<Location> locations) {
+ mCallback.onLocationBatch(locations);
}
}
diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java
index 323e740ee8e3..cb56ee5318ee 100644
--- a/location/java/android/location/LocationRequest.java
+++ b/location/java/android/location/LocationRequest.java
@@ -605,7 +605,7 @@ public final class LocationRequest implements Parcelable {
* When available, batching can provide substantial power savings to the device, and clients are
* encouraged to take advantage where appropriate for the use case.
*
- * @see LocationListener#onLocationChanged(LocationResult)
+ * @see LocationListener#onLocationChanged(java.util.List)
* @return the maximum time by which a location update may be delayed
*/
public @IntRange(from = 0) long getMaxUpdateDelayMillis() {
diff --git a/location/java/android/location/LocationResult.java b/location/java/android/location/LocationResult.java
index 79a000c23484..8423000b6276 100644
--- a/location/java/android/location/LocationResult.java
+++ b/location/java/android/location/LocationResult.java
@@ -19,7 +19,6 @@ package android.location;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,25 +33,31 @@ import java.util.function.Predicate;
/**
* A location result representing a list of locations, ordered from earliest to latest.
+ *
+ * @hide
*/
public final class LocationResult implements Parcelable {
/**
- * Creates a new LocationResult from the given location.
+ * Creates a new LocationResult from the given locations, making a copy of each location.
+ * Locations must be ordered in the same order they were derived (earliest to latest).
*/
- public static @NonNull LocationResult create(@NonNull Location location) {
- ArrayList<Location> locations = new ArrayList<>(1);
- locations.add(new Location(Objects.requireNonNull(location)));
- return new LocationResult(locations);
+ public static @NonNull LocationResult create(@NonNull List<Location> locations) {
+ Preconditions.checkArgument(!locations.isEmpty());
+ ArrayList<Location> locationsCopy = new ArrayList<>(locations.size());
+ for (Location location : locations) {
+ locationsCopy.add(new Location(Objects.requireNonNull(location)));
+ }
+ return new LocationResult(locationsCopy);
}
/**
- * Creates a new LocationResult from the given locations. Locations must be ordered in the same
- * order they were derived (earliest to latest).
+ * Creates a new LocationResult from the given locations, making a copy of each location.
+ * Locations must be ordered in the same order they were derived (earliest to latest).
*/
- public static @NonNull LocationResult create(@NonNull List<Location> locations) {
- Preconditions.checkArgument(!locations.isEmpty());
- ArrayList<Location> locationsCopy = new ArrayList<>(locations.size());
+ public static @NonNull LocationResult create(@NonNull Location... locations) {
+ Preconditions.checkArgument(locations.length > 0);
+ ArrayList<Location> locationsCopy = new ArrayList<>(locations.length);
for (Location location : locations) {
locationsCopy.add(new Location(Objects.requireNonNull(location)));
}
@@ -60,16 +65,27 @@ public final class LocationResult implements Parcelable {
}
/**
- * Creates a new LocationResult that takes ownership of the given location without copying it.
- * Callers must ensure the given location is never mutated after this method is called.
- *
- * @hide
+ * Creates a new LocationResult that takes ownership of the given locations without copying
+ * them. Callers must ensure the given locations are never mutated after this method is called.
+ * Locations must be ordered in the same order they were derived (earliest to latest).
+ */
+ public static @NonNull LocationResult wrap(@NonNull List<Location> locations) {
+ Preconditions.checkArgument(!locations.isEmpty());
+ return new LocationResult(new ArrayList<>(locations));
+ }
+
+ /**
+ * Creates a new LocationResult that takes ownership of the given locations without copying
+ * them. Callers must ensure the given locations are never mutated after this method is called.
+ * Locations must be ordered in the same order they were derived (earliest to latest).
*/
- @SystemApi
- public static @NonNull LocationResult wrap(@NonNull Location location) {
- ArrayList<Location> locations = new ArrayList<>(1);
- locations.add(Objects.requireNonNull(location));
- return new LocationResult(locations);
+ public static @NonNull LocationResult wrap(@NonNull Location... locations) {
+ Preconditions.checkArgument(locations.length > 0);
+ ArrayList<Location> newLocations = new ArrayList<>(locations.length);
+ for (Location location : locations) {
+ newLocations.add(Objects.requireNonNull(location));
+ }
+ return new LocationResult(newLocations);
}
private final ArrayList<Location> mLocations;
@@ -112,7 +128,7 @@ public final class LocationResult implements Parcelable {
}
/**
- * Returns the numer of locations in this location result.
+ * Returns the number of locations in this location result.
*/
public @IntRange(from = 1) int size() {
return mLocations.size();
@@ -139,9 +155,9 @@ public final class LocationResult implements Parcelable {
* @hide
*/
public @NonNull LocationResult deepCopy() {
- ArrayList<Location> copy = new ArrayList<>(mLocations.size());
final int size = mLocations.size();
- for (int i = 0; i < size; ++i) {
+ ArrayList<Location> copy = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
copy.add(new Location(mLocations.get(i)));
}
return new LocationResult(copy);
@@ -164,7 +180,7 @@ public final class LocationResult implements Parcelable {
* Returns a LocationResult with only locations that pass the given predicate. This
* implementation will avoid allocations when no locations are filtered out. The predicate is
* guaranteed to be invoked once per location, in order from earliest to latest. If all
- * locations are filtered out a null value is returned instead of an empty LocationResult.
+ * locations are filtered out a null value is returned.
*
* @hide
*/
diff --git a/location/java/android/location/provider/ILocationProviderManager.aidl b/location/java/android/location/provider/ILocationProviderManager.aidl
index e3f51d9a23e1..50ed046f09cd 100644
--- a/location/java/android/location/provider/ILocationProviderManager.aidl
+++ b/location/java/android/location/provider/ILocationProviderManager.aidl
@@ -16,7 +16,7 @@
package android.location.provider;
-import android.location.LocationResult;
+import android.location.Location;
import android.location.provider.ProviderProperties;
/**
@@ -28,6 +28,7 @@ interface ILocationProviderManager {
void onSetAllowed(boolean allowed);
void onSetProperties(in ProviderProperties properties);
- void onReportLocation(in LocationResult locationResult);
+ void onReportLocation(in Location location);
+ void onReportLocations(in List<Location> locations);
void onFlushComplete();
}
diff --git a/location/java/android/location/provider/LocationProviderBase.java b/location/java/android/location/provider/LocationProviderBase.java
index 8f455cd7df07..ae6395d5d12d 100644
--- a/location/java/android/location/provider/LocationProviderBase.java
+++ b/location/java/android/location/provider/LocationProviderBase.java
@@ -25,12 +25,13 @@ import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
-import android.location.LocationResult;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -200,35 +201,29 @@ public abstract class LocationProviderBase {
* Reports a new location from this provider.
*/
public void reportLocation(@NonNull Location location) {
- reportLocation(LocationResult.create(location));
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onReportLocation(stripExtras(location));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
+ Log.w(mTag, e);
+ }
+ }
}
/**
- * Reports a new location result from this provider.
- *
- * <p>May only be used from Android S onwards.
+ * Reports a new batch of locations from this provider. Locations must be ordered in the list
+ * from earliest first to latest last.
*/
- public void reportLocation(@NonNull LocationResult locationResult) {
+ public void reportLocations(@NonNull List<Location> locations) {
ILocationProviderManager manager = mManager;
if (manager != null) {
- locationResult = locationResult.map(location -> {
- // remove deprecated extras to save on serialization costs
- Bundle extras = location.getExtras();
- if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
- || extras.containsKey("coarseLocation"))) {
- location = new Location(location);
- extras = location.getExtras();
- extras.remove(EXTRA_NO_GPS_LOCATION);
- extras.remove("coarseLocation");
- if (extras.isEmpty()) {
- location.setExtras(null);
- }
- }
- return location;
- });
+
try {
- manager.onReportLocation(locationResult);
+ manager.onReportLocations(stripExtras(locations));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (RuntimeException e) {
@@ -246,9 +241,9 @@ public abstract class LocationProviderBase {
/**
* Requests a flush of any pending batched locations. The callback must always be invoked once
- * per invocation, and should be invoked after {@link #reportLocation(LocationResult)} has been
- * invoked with any flushed locations. The callback may be invoked immediately if no locations
- * are flushed.
+ * per invocation, and should be invoked after {@link #reportLocation(Location)} or
+ * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may
+ * be invoked immediately if no locations are flushed.
*/
public abstract void onFlush(@NonNull OnFlushCompleteCallback callback);
@@ -259,6 +254,49 @@ public abstract class LocationProviderBase {
@SuppressLint("NullableCollection")
@Nullable Bundle extras);
+ private static Location stripExtras(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
+ || extras.containsKey("indoorProbability")
+ || extras.containsKey("coarseLocation"))) {
+ location = new Location(location);
+ extras = location.getExtras();
+ extras.remove(EXTRA_NO_GPS_LOCATION);
+ extras.remove("indoorProbability");
+ extras.remove("coarseLocation");
+ if (extras.isEmpty()) {
+ location.setExtras(null);
+ }
+ }
+ return location;
+ }
+
+ private static List<Location> stripExtras(List<Location> locations) {
+ List<Location> mapped = locations;
+ final int size = locations.size();
+ int i = 0;
+ for (Location location : locations) {
+ Location newLocation = stripExtras(location);
+ if (mapped != locations) {
+ mapped.add(newLocation);
+ } else if (newLocation != location) {
+ mapped = new ArrayList<>(size);
+ int j = 0;
+ for (Location copiedLocation : locations) {
+ if (j >= i) {
+ break;
+ }
+ mapped.add(copiedLocation);
+ j++;
+ }
+ mapped.add(newLocation);
+ }
+ i++;
+ }
+
+ return mapped;
+ }
+
private final class Service extends ILocationProvider.Stub {
Service() {}
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index 338d7ccd6c91..0d81f36f540b 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -21,8 +21,8 @@ package com.android.location.provider {
method @Deprecated protected void onInit();
method @Deprecated protected boolean onSendExtraCommand(@Nullable String, @Nullable android.os.Bundle);
method @Deprecated protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
- method @Deprecated public void reportLocation(android.location.Location);
- method @Deprecated public void reportLocation(android.location.LocationResult);
+ method @Deprecated public void reportLocation(@NonNull android.location.Location);
+ method @Deprecated public void reportLocations(@NonNull java.util.List<android.location.Location>);
method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setAdditionalProviderPackages(java.util.List<java.lang.String>);
method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.R) public void setAllowed(boolean);
method @Deprecated @RequiresApi(android.os.Build.VERSION_CODES.Q) public void setEnabled(boolean);
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index aea93ce80e9b..7f1cf6dc3459 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,13 +16,13 @@
package com.android.location.provider;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.location.LocationResult;
import android.location.provider.ILocationProvider;
import android.location.provider.ILocationProviderManager;
import android.location.provider.ProviderProperties;
@@ -39,6 +39,7 @@ import androidx.annotation.RequiresApi;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -94,10 +95,6 @@ public abstract class LocationProviderBase {
*/
public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
- private static final String EXTRA_KEY_COARSE_LOCATION = "coarseLocation";
- private static final String EXTRA_KEY_NO_GPS_LOCATION = "noGPSLocation";
- private static final String EXTRA_KEY_INDOOR_PROB = "indoorProbability";
-
final String mTag;
@Nullable final String mPackageName;
@Nullable final String mAttributionTag;
@@ -254,20 +251,11 @@ public abstract class LocationProviderBase {
/**
* Reports a new location from this provider.
*/
- public void reportLocation(Location location) {
- reportLocation(LocationResult.create(location));
- }
-
- /**
- * Reports a new location from this provider.
- */
- public void reportLocation(LocationResult locationResult) {
+ public void reportLocation(@NonNull Location location) {
ILocationProviderManager manager = mManager;
if (manager != null) {
- locationResult = locationResult.map(this::cleanUpExtras);
-
try {
- manager.onReportLocation(locationResult);
+ manager.onReportLocation(stripExtras(location));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (RuntimeException e) {
@@ -277,30 +265,20 @@ public abstract class LocationProviderBase {
}
/**
- * Remove deprecated/unnecessary extras to save on serialization costs.
- *
- * {@link #EXTRA_KEY_NO_GPS_LOCATION} and {@link #EXTRA_KEY_COARSE_LOCATION} are deprecated.
- *
- * {@link #EXTRA_KEY_INDOOR_PROB} should only be used in the framework.
+ * Reports a new batch of locations from this provider. Locations must be ordered in the list
+ * from earliest first to latest last.
*/
- private Location cleanUpExtras(Location location) {
- Bundle extras = location.getExtras();
- if (extras == null) {
- return location;
- }
- if (extras.containsKey(EXTRA_KEY_NO_GPS_LOCATION)
- || extras.containsKey(EXTRA_KEY_COARSE_LOCATION)
- || extras.containsKey(EXTRA_KEY_INDOOR_PROB)) {
- location = new Location(location);
- extras = location.getExtras();
- extras.remove(EXTRA_KEY_NO_GPS_LOCATION);
- extras.remove(EXTRA_KEY_COARSE_LOCATION);
- extras.remove(EXTRA_KEY_INDOOR_PROB);
- if (extras.isEmpty()) {
- location.setExtras(null);
+ public void reportLocations(@NonNull List<Location> locations) {
+ ILocationProviderManager manager = mManager;
+ if (manager != null) {
+ try {
+ manager.onReportLocations(stripExtras(locations));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (RuntimeException e) {
+ Log.w(mTag, e);
}
}
- return location;
}
protected void onInit() {
@@ -336,9 +314,9 @@ public abstract class LocationProviderBase {
/**
* Requests a flush of any pending batched locations. The callback must always be invoked once
- * per invocation, and should be invoked after {@link #reportLocation(LocationResult)} has been
- * invoked with any flushed locations. The callback may be invoked immediately if no locations
- * are flushed.
+ * per invocation, and should be invoked after {@link #reportLocation(Location)} or
+ * {@link #reportLocations(List)} has been invoked with any flushed locations. The callback may
+ * be invoked immediately if no locations are flushed.
*/
protected void onFlush(OnFlushCompleteCallback callback) {
callback.onFlushComplete();
@@ -433,4 +411,47 @@ public abstract class LocationProviderBase {
onSendExtraCommand(command, extras);
}
}
+
+ private static Location stripExtras(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras != null && (extras.containsKey(EXTRA_NO_GPS_LOCATION)
+ || extras.containsKey("indoorProbability")
+ || extras.containsKey("coarseLocation"))) {
+ location = new Location(location);
+ extras = location.getExtras();
+ extras.remove(EXTRA_NO_GPS_LOCATION);
+ extras.remove("indoorProbability");
+ extras.remove("coarseLocation");
+ if (extras.isEmpty()) {
+ location.setExtras(null);
+ }
+ }
+ return location;
+ }
+
+ private static List<Location> stripExtras(List<Location> locations) {
+ List<Location> mapped = locations;
+ final int size = locations.size();
+ int i = 0;
+ for (Location location : locations) {
+ Location newLocation = stripExtras(location);
+ if (mapped != locations) {
+ mapped.add(newLocation);
+ } else if (newLocation != location) {
+ mapped = new ArrayList<>(size);
+ int j = 0;
+ for (Location copiedLocation : locations) {
+ if (j >= i) {
+ break;
+ }
+ mapped.add(copiedLocation);
+ j++;
+ }
+ mapped.add(newLocation);
+ }
+ i++;
+ }
+
+ return mapped;
+ }
}
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 d47231147a38..7cc599499ca1 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
@@ -26,7 +26,6 @@ import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationRequest;
-import android.location.LocationResult;
import android.location.provider.ILocationProvider;
import android.location.provider.ILocationProviderManager;
import android.location.provider.ProviderProperties;
@@ -49,6 +48,7 @@ import org.junit.runner.RunWith;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
+import java.util.List;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -167,10 +167,13 @@ public class FusedLocationServiceTest {
}
@Override
- public void onReportLocation(LocationResult locationResult) {
- for (int i = 0; i < locationResult.size(); i++) {
- mLocations.add(locationResult.get(i));
- }
+ public void onReportLocation(Location location) {
+ mLocations.add(location);
+ }
+
+ @Override
+ public void onReportLocations(List<Location> locations) {
+ mLocations.addAll(locations);
}
@Override
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index d16267f0582e..9216a6b245a6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -103,7 +103,6 @@ import com.android.server.location.provider.AbstractLocationProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -1489,7 +1488,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
}
if (locations.length > 0) {
- reportLocation(LocationResult.create(Arrays.asList(locations)).validate());
+ reportLocation(LocationResult.wrap(locations).validate());
}
for (Runnable listener : listeners) {
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 06ca9ec387a9..221d4fb40ef9 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -20,8 +20,8 @@ import static android.app.compat.CompatChanges.isChangeEnabled;
import static android.location.LocationManager.DELIVER_HISTORICAL_LOCATIONS;
import static android.location.LocationManager.GPS_PROVIDER;
import static android.location.LocationManager.KEY_FLUSH_COMPLETE;
+import static android.location.LocationManager.KEY_LOCATIONS;
import static android.location.LocationManager.KEY_LOCATION_CHANGED;
-import static android.location.LocationManager.KEY_LOCATION_RESULT;
import static android.location.LocationManager.KEY_PROVIDER_ENABLED;
import static android.location.LocationManager.PASSIVE_PROVIDER;
import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE;
@@ -187,7 +187,8 @@ public class LocationProviderManager extends
@Override
public void deliverOnLocationChanged(LocationResult locationResult,
@Nullable Runnable onCompleteCallback) throws RemoteException {
- mListener.onLocationChanged(locationResult, SingleUseCallback.wrap(onCompleteCallback));
+ mListener.onLocationChanged(locationResult.asList(),
+ SingleUseCallback.wrap(onCompleteCallback));
}
@Override
@@ -222,12 +223,16 @@ public class LocationProviderManager extends
// allows apps to start a fg service in response to a location PI
options.setTemporaryAppWhitelistDuration(TEMPORARY_APP_ALLOWLIST_DURATION_MS);
+ Intent intent = new Intent().putExtra(KEY_LOCATION_CHANGED,
+ locationResult.getLastLocation());
+ if (locationResult.size() > 1) {
+ intent.putExtra(KEY_LOCATIONS, locationResult.asList().toArray(new Location[0]));
+ }
+
mPendingIntent.send(
mContext,
0,
- new Intent()
- .putExtra(KEY_LOCATION_CHANGED, locationResult.getLastLocation())
- .putExtra(KEY_LOCATION_RESULT, locationResult),
+ intent,
onCompleteCallback != null ? (pI, i, rC, rD, rE) -> onCompleteCallback.run()
: null,
null,
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index dce7b081de6e..0d8f64377db0 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -53,7 +53,7 @@ public class MockLocationProvider extends AbstractLocationProvider {
Location location = new Location(l);
location.setIsFromMockProvider(true);
mLocation = location;
- reportLocation(LocationResult.wrap(location));
+ reportLocation(LocationResult.wrap(location).validate());
}
@Override
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index c274c2848ab6..32d637ffa730 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -21,6 +21,7 @@ import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
+import android.location.Location;
import android.location.LocationResult;
import android.location.provider.ILocationProvider;
import android.location.provider.ILocationProviderManager;
@@ -40,6 +41,7 @@ import com.android.server.location.provider.AbstractLocationProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
/**
@@ -260,13 +262,25 @@ public class ProxyLocationProvider extends AbstractLocationProvider {
// executed on binder thread
@Override
- public void onReportLocation(LocationResult locationResult) {
+ public void onReportLocation(Location location) {
synchronized (mLock) {
if (mProxy != this) {
return;
}
- reportLocation(locationResult.validate());
+ reportLocation(LocationResult.wrap(location).validate());
+ }
+ }
+
+ // executed on binder thread
+ @Override
+ public void onReportLocations(List<Location> locations) {
+ synchronized (mLock) {
+ if (mProxy != this) {
+ return;
+ }
+
+ reportLocation(LocationResult.wrap(locations).validate());
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 84bfc9be3d41..3b5cc887c798 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -96,6 +96,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -383,7 +384,7 @@ public class LocationProviderManagerTest {
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener).onLocationChanged(eq(loc), nullable(IRemoteCallback.class));
+ verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
}
@Test
@@ -406,13 +407,13 @@ public class LocationProviderManagerTest {
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener).onLocationChanged(eq(loc), nullable(IRemoteCallback.class));
+ verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
verify(listener, timeout(TIMEOUT_MS).times(1)).onProviderEnabledChanged(NAME, false);
loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener, times(1)).onLocationChanged(any(LocationResult.class),
+ verify(listener, times(1)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
mInjector.getSettingsHelper().setLocationEnabled(true, CURRENT_USER);
@@ -422,7 +423,7 @@ public class LocationProviderManagerTest {
verify(listener, timeout(TIMEOUT_MS).times(2)).onProviderEnabledChanged(NAME, false);
loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener, times(1)).onLocationChanged(any(LocationResult.class),
+ verify(listener, times(1)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
mProvider.setAllowed(true);
@@ -430,7 +431,7 @@ public class LocationProviderManagerTest {
loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener).onLocationChanged(eq(loc), nullable(IRemoteCallback.class));
+ verify(listener).onLocationChanged(eq(loc.asList()), nullable(IRemoteCallback.class));
}
@Test
@@ -447,7 +448,7 @@ public class LocationProviderManagerTest {
LocationResult loc = createLocationResult(NAME, mRandom);
mProvider.setProviderLocation(loc);
- verify(listener, timeout(TIMEOUT_MS).times(1)).onLocationChanged(eq(loc),
+ verify(listener, timeout(TIMEOUT_MS).times(1)).onLocationChanged(eq(loc.asList()),
nullable(IRemoteCallback.class));
}
@@ -462,7 +463,7 @@ public class LocationProviderManagerTest {
mManager.unregisterLocationRequest(listener);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
mInjector.getSettingsHelper().setLocationEnabled(false, CURRENT_USER);
@@ -493,7 +494,7 @@ public class LocationProviderManagerTest {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mManager.unregisterLocationRequest(listener);
blocker.countDown();
- verify(listener, after(TIMEOUT_MS).never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, after(TIMEOUT_MS).never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -513,7 +514,7 @@ public class LocationProviderManagerTest {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, times(5)).onLocationChanged(any(LocationResult.class),
+ verify(listener, times(5)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -528,7 +529,7 @@ public class LocationProviderManagerTest {
mInjector.getAlarmHelper().incrementAlarmTime(5000);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -544,7 +545,7 @@ public class LocationProviderManagerTest {
Thread.sleep(25);
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -561,7 +562,7 @@ public class LocationProviderManagerTest {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1)).onLocationChanged(
- any(LocationResult.class), nullable(IRemoteCallback.class));
+ any(List.class), nullable(IRemoteCallback.class));
}
@Test
@@ -578,7 +579,7 @@ public class LocationProviderManagerTest {
mProvider.setProviderLocation(loc);
verify(listener, times(1)).onLocationChanged(
- any(LocationResult.class), nullable(IRemoteCallback.class));
+ any(List.class), nullable(IRemoteCallback.class));
}
@Test
@@ -592,7 +593,7 @@ public class LocationProviderManagerTest {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
- verify(listener, never()).onLocationChanged(any(LocationResult.class),
+ verify(listener, never()).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
}
@@ -622,7 +623,7 @@ public class LocationProviderManagerTest {
verify(mWakeLock, never()).release();
blocker.countDown();
- verify(listener, timeout(TIMEOUT_MS)).onLocationChanged(any(LocationResult.class),
+ verify(listener, timeout(TIMEOUT_MS)).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
verify(mWakeLock).acquire(anyLong());
verify(mWakeLock, timeout(TIMEOUT_MS)).release();
@@ -640,7 +641,7 @@ public class LocationProviderManagerTest {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1))
- .onLocationChanged(any(LocationResult.class), nullable(IRemoteCallback.class));
+ .onLocationChanged(any(List.class), nullable(IRemoteCallback.class));
}
@Test
@@ -657,7 +658,7 @@ public class LocationProviderManagerTest {
mProvider.setProviderLocation(createLocation(NAME, mRandom));
mProvider.setProviderLocation(createLocation(NAME, mRandom));
verify(listener, times(1))
- .onLocationChanged(any(LocationResult.class), nullable(IRemoteCallback.class));
+ .onLocationChanged(any(List.class), nullable(IRemoteCallback.class));
}
@Test
@@ -746,7 +747,7 @@ public class LocationProviderManagerTest {
mProvider.completeFlushes();
InOrder inOrder = inOrder(listener);
- inOrder.verify(listener).onLocationChanged(eq(loc), any(IRemoteCallback.class));
+ inOrder.verify(listener).onLocationChanged(eq(loc.asList()), any(IRemoteCallback.class));
inOrder.verify(listener).onFlushComplete(99);
}
@@ -838,7 +839,7 @@ public class LocationProviderManagerTest {
.build();
mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1);
- verify(listener1).onLocationChanged(any(LocationResult.class),
+ verify(listener1).onLocationChanged(any(List.class),
nullable(IRemoteCallback.class));
assertThat(mProvider.getRequest().isActive()).isFalse();
@@ -989,7 +990,7 @@ public class LocationProviderManagerTest {
private ILocationListener createMockLocationListener() {
return spy(new ILocationListener.Stub() {
@Override
- public void onLocationChanged(LocationResult location,
+ public void onLocationChanged(List<Location> locations,
IRemoteCallback onCompleteCallback) {
if (onCompleteCallback != null) {
try {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index 99846c517b7a..07170dacb4da 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -185,8 +185,8 @@ public class MockableLocationProviderTest {
@Test
public void testReportLocation() {
- LocationResult realLocation = LocationResult.create(new Location("real"));
- LocationResult mockLocation = LocationResult.create(new Location("mock"));
+ LocationResult realLocation = LocationResult.wrap(new Location("real"));
+ LocationResult mockLocation = LocationResult.wrap(new Location("mock"));
mRealProvider.reportLocation(realLocation);
assertThat(mListener.getNextLocationResult()).isEqualTo(realLocation);