diff options
author | Soonil Nagarkar <sooniln@google.com> | 2021-02-02 14:41:00 -0800 |
---|---|---|
committer | Soonil Nagarkar <sooniln@google.com> | 2021-02-03 17:02:14 -0800 |
commit | e97179d846c3942e3455afb0b413d956093560e7 (patch) | |
tree | caa87658706be5118cd61fd0509e218ab3fc0738 | |
parent | 9ee2e0d368836a53154f45c7b6d4a34614175481 (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
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); |