diff options
| author | 2019-10-18 16:22:25 +0000 | |
|---|---|---|
| committer | 2019-10-18 16:22:25 +0000 | |
| commit | 8eb590bdc79a89739d0cbb8545b8db7e01f76dfb (patch) | |
| tree | e7f4fa6a9339a7b559dd58b57484129e8e249a3b /location/java/android | |
| parent | cd45b2ab20987665ec9324ac81a8e16a995a5da9 (diff) | |
| parent | 905e7227dcd9b66441b9c3939662a8bc69094d1e (diff) | |
Merge "Add getCurrentLocation API to LocationManager"
Diffstat (limited to 'location/java/android')
| -rw-r--r-- | location/java/android/location/ILocationManager.aidl | 8 | ||||
| -rw-r--r-- | location/java/android/location/LocationManager.java | 371 |
2 files changed, 295 insertions, 84 deletions
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index daa2e08a6780..91f3a20fc11d 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -31,6 +31,7 @@ import android.location.Location; import android.location.LocationRequest; import android.location.LocationTime; import android.os.Bundle; +import android.os.ICancellationSignal; import com.android.internal.location.ProviderProperties; @@ -41,6 +42,11 @@ import com.android.internal.location.ProviderProperties; */ interface ILocationManager { + Location getLastLocation(in LocationRequest request, String packageName); + boolean getCurrentLocation(in LocationRequest request, + in ICancellationSignal cancellationSignal, in ILocationListener listener, + String packageName, String listenerIdentifier); + void requestLocationUpdates(in LocationRequest request, in ILocationListener listener, in PendingIntent intent, String packageName, String listenerIdentifier); void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName); @@ -49,8 +55,6 @@ interface ILocationManager in PendingIntent intent, String packageName, String listenerIdentifier); void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName); - Location getLastLocation(in LocationRequest request, String packageName); - boolean registerGnssStatusCallback(IGnssStatusListener callback, String packageName); void unregisterGnssStatusCallback(IGnssStatusListener callback); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 987947b93060..9e17e9541e5c 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission.LOCATION_HARDWARE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; +import static android.app.AlarmManager.ELAPSED_REALTIME; import android.Manifest; import android.annotation.CallbackExecutor; @@ -31,17 +32,21 @@ import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.app.AlarmManager; import android.app.PendingIntent; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; import android.os.Build; import android.os.Bundle; +import android.os.CancellationSignal; import android.os.Handler; import android.os.HandlerExecutor; +import android.os.ICancellationSignal; import android.os.Looper; import android.os.Process; import android.os.RemoteException; +import android.os.SystemClock; import android.os.UserHandle; import android.provider.Settings; import android.util.ArrayMap; @@ -55,6 +60,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; +import java.util.function.Consumer; /** * This class provides access to the system location services. These services allow applications to @@ -77,22 +83,20 @@ public class LocationManager { /** * Name of the network location provider. - * <p>This provider determines location based on - * availability of cell tower and WiFi access points. Results are retrieved - * by means of a network lookup. + * + * <p>This provider determines location based on nearby of cell tower and WiFi access points. + * Results are retrieved by means of a network lookup. */ public static final String NETWORK_PROVIDER = "network"; /** - * Name of the GPS location provider. + * Name of the GNSS location provider. * - * <p>This provider determines location using - * satellites. Depending on conditions, this provider may take a while to return - * a location fix. Requires the permission - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}. + * <p>This provider determines location using GNSS satellites. Depending on conditions, this + * provider may take a while to return a location fix. Requires the + * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission. * - * <p> The extras Bundle for the GPS location provider can contain the - * following key/value pairs: + * <p>The extras Bundle for the GPS location provider can contain the following key/value pairs: * <ul> * <li> satellites - the number of satellites used to derive the fix * </ul> @@ -100,26 +104,24 @@ public class LocationManager { public static final String GPS_PROVIDER = "gps"; /** - * A special location provider for receiving locations without actually initiating - * a location fix. + * A special location provider for receiving locations without actually initiating a location + * fix. * - * <p>This provider can be used to passively receive location updates - * when other applications or services request them without actually requesting - * the locations yourself. This provider will return locations generated by other - * providers. You can query the {@link Location#getProvider()} method to determine - * the origin of the location update. Requires the permission - * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, although if the GPS is - * not enabled this provider might only return coarse fixes. + * <p>This provider can be used to passively receive location updates when other applications or + * services request them without actually requesting the locations yourself. This provider will + * only return locations generated by other providers. You can query the + * {@link Location#getProvider()} method to determine the actual provider that supplied the + * location update. Requires the {@link android.Manifest.permission#ACCESS_FINE_LOCATION} + * permission, although there is no guarantee of fine locations. */ public static final String PASSIVE_PROVIDER = "passive"; /** - * Name of the Fused location provider. + * The fused location provider. * - * <p>This provider combines inputs for all possible location sources - * to provide the best possible Location fix. It is implicitly - * used for all API's that involve the {@link LocationRequest} - * object. + * <p>This provider combines may combine inputs from several location sources to provide the + * best possible location fix. It is implicitly used for all API's that involve the + * {@link LocationRequest} object. * * @hide */ @@ -228,6 +230,8 @@ public class LocationManager { public static final String METADATA_SETTINGS_FOOTER_STRING = "com.android.settings.location.FOOTER_STRING"; + private static final long GET_CURRENT_LOCATION_TIMEOUT_MS = 30 * 1000; + private final Context mContext; @UnsupportedAppUsage @@ -522,6 +526,13 @@ public class LocationManager { * location. The returned location may be quite old in some circumstances, so the age of the * location should always be checked. * + * <p>This will never activate sensors to compute a new location, and will only ever return a + * cached location. + * + * <p>See also {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} which + * will always attempt to return a current location, but will potentially use additional power + * in the course of the attempt as compared to this method. + * * @param provider the name of the provider * @return the last known location for the given provider, or null if not available * @throws SecurityException if no suitable permission is present @@ -550,36 +561,117 @@ public class LocationManager { * @return A identifying string */ private static String getListenerIdentifier(@NonNull Object listener) { - StringBuilder sb = new StringBuilder(); + return listener.getClass().getName() + + '@' + + Integer.toHexString(System.identityHashCode(listener)); + } + + /** + * Asynchronously returns a single current location fix. This may activate sensors in order to + * compute a new location, unlike {@link #getLastKnownLocation(String)}, which will only return + * a cached fix if available. The given callback will be invoked once and only once, either with + * a valid location fix or with a null location fix if the provider was unable to generate a + * valid location. + * + * <p>A client may supply an optional {@link CancellationSignal}. If this is used to cancel the + * operation, no callback should be expected after the cancellation. + * + * <p>This method may return locations from the very recent past (on the order of several + * seconds), but will never return older locations (for example, several minutes old or older). + * Clients may rely upon the guarantee that if this method returns a location, it will represent + * the best estimation of the location of the device in the present moment. + * + * <p>Clients calling this method from the background may notice that the method fails to + * determine a valid location fix more often than while in the foreground. Background + * applications may be throttled in their location accesses to some degree. + * + * @param provider the name of the provider with which to register + * @param cancellationSignal an optional signal that allows for cancelling this call + * @param executor the callback will take place on this {@link Executor} + * @param consumer the callback invoked with either a {@link Location} or null + * + * @throws IllegalArgumentException if provider is null or doesn't exist + * @throws IllegalArgumentException if executor is null + * @throws IllegalArgumentException if consumer is null + * @throws SecurityException if no suitable permission is present + */ + @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) + public void getCurrentLocation(@NonNull String provider, + @Nullable CancellationSignal cancellationSignal, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) { + getCurrentLocation(LocationRequest.createFromDeprecatedProvider(provider, 0, 0, true), + cancellationSignal, executor, consumer); + } + + /** + * Asynchronously returns a single current location fix based on the given + * {@link LocationRequest}. + * + * <p>See {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} for more + * information. + * + * @param locationRequest the location request containing location parameters + * @param cancellationSignal an optional signal that allows for cancelling this call + * @param executor the callback will take place on this {@link Executor} + * @param consumer the callback invoked with either a {@link Location} or null + * + * @throws IllegalArgumentException if provider is null or doesn't exist + * @throws IllegalArgumentException if executor is null + * @throws IllegalArgumentException if consumer is null + * @throws SecurityException if no suitable permission is present + * @hide + */ + @SystemApi + @TestApi + @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) + public void getCurrentLocation(@NonNull LocationRequest locationRequest, + @Nullable CancellationSignal cancellationSignal, + @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) { + LocationRequest currentLocationRequest = new LocationRequest(locationRequest) + .setNumUpdates(1).setExpireIn(GET_CURRENT_LOCATION_TIMEOUT_MS); + + GetCurrentLocationTransport listenerTransport = new GetCurrentLocationTransport(executor, + consumer); + + if (cancellationSignal != null) { + cancellationSignal.throwIfCanceled(); + } - sb.append(listener.getClass().getName()); - sb.append('@'); - sb.append(Integer.toHexString(System.identityHashCode(listener))); + ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport(); - return sb.toString(); + try { + if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal, + listenerTransport, mContext.getPackageName(), + getListenerIdentifier(consumer))) { + listenerTransport.register(mContext.getSystemService(AlarmManager.class), + remoteCancellationSignal); + if (cancellationSignal != null) { + cancellationSignal.setOnCancelListener(listenerTransport::cancel); + } + } else { + listenerTransport.fail(); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** - * Register for a single location update using the named provider and - * a callback. + * Register for a single location update using the named provider and a callback. * - * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener, Looper)} - * for more detail on how to use this method. + * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener, Looper)} for + * more detail on how to use this method. * * @param provider the name of the provider with which to register - * @param listener a {@link LocationListener} whose - * {@link LocationListener#onLocationChanged} method will be called when - * the location update is available - * @param looper a Looper object whose message queue will be used to - * implement the callback mechanism, or null to make callbacks on the calling - * thread + * @param listener the listener to receive location updates + * @param looper the looper handling listener callbacks, or null to use the looper of the + * calling thread * * @throws IllegalArgumentException if provider is null or doesn't exist * @throws IllegalArgumentException if listener is null - * @throws SecurityException if no suitable permission is present - * @deprecated This method can drain much more battery than expected if it is not possible to - * calculate location. Prefer any of the requestLocationUpdates() methods which require explicit - * removal. + * @throws SecurityException if no suitable permission is present + * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} + * instead as it does not carry a risk of extreme battery drain. */ @Deprecated @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) @@ -594,27 +686,21 @@ public class LocationManager { } /** - * Register for a single location update using a Criteria and - * a callback. + * Register for a single location update using a Criteria and a callback. * - * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} - * for more detail on how to use this method. + * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} for more detail + * on how to use this method. * - * @param criteria contains parameters for the location manager to choose the - * appropriate provider and parameters to compute the location - * @param listener a {@link LocationListener} whose - * {@link LocationListener#onLocationChanged} method will be called when - * the location update is available - * @param looper a Looper object whose message queue will be used to - * implement the callback mechanism, or null to make callbacks on the calling - * thread + * @param criteria contains parameters to choose the appropriate provider for location updates + * @param listener the listener to receive location updates + * @param looper the looper handling listener callbacks, or null to use the looper of the + * calling thread * * @throws IllegalArgumentException if criteria is null * @throws IllegalArgumentException if listener is null - * @throws SecurityException if no suitable permission is present - * @deprecated This method can drain much more battery than expected if it is not possible to - * calculate location. Prefer any of the requestLocationUpdates() methods which require explicit - * removal. + * @throws SecurityException if no suitable permission is present + * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} + * instead as it does not carry a risk of extreme battery drain. */ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void requestSingleUpdate( @@ -632,54 +718,54 @@ public class LocationManager { /** * Register for a single location update using a named provider and pending intent. * - * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} - * for more detail on how to use this method. + * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} for more detail + * on how to use this method. * - * @param provider the name of the provider with which to register - * @param intent a {@link PendingIntent} to be sent for the location update + * @param provider the name of the provider with which to register + * @param pendingIntent the pending intent to send location updates * * @throws IllegalArgumentException if provider is null or doesn't exist * @throws IllegalArgumentException if intent is null - * @throws SecurityException if no suitable permission is present - * @deprecated This method can drain much more battery than expected if it is not possible to - * calculate location. Prefer any of the requestLocationUpdates() methods which require explicit - * removal. + * @throws SecurityException if no suitable permission is present + * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} + * instead as it does not carry a risk of extreme battery drain. */ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) - public void requestSingleUpdate(@NonNull String provider, @NonNull PendingIntent intent) { + public void requestSingleUpdate(@NonNull String provider, + @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(provider != null, "invalid null provider"); - checkPendingIntent(intent); + checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedProvider( provider, 0, 0, true); - requestLocationUpdates(request, intent); + requestLocationUpdates(request, pendingIntent); } /** * Register for a single location update using a Criteria and pending intent. * - * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} - * for more detail on how to use this method. + * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)} for more detail + * on how to use this method. * - * @param criteria contains parameters for the location manager to choose the - * appropriate provider and parameters to compute the location - * @param intent a {@link PendingIntent} to be sent for the location update + * @param criteria contains parameters to choose the appropriate provider for location + * updates + * @param pendingIntent the pending intent to send location updates * * @throws IllegalArgumentException if provider is null or doesn't exist * @throws IllegalArgumentException if intent is null - * @throws SecurityException if no suitable permission is present - * @deprecated This method can drain much more battery than expected if it is not possible to - * calculate location. Prefer any of the requestLocationUpdates() methods which require explicit - * removal. + * @throws SecurityException if no suitable permission is present + * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} + * instead as it does not carry a risk of extreme battery drain. */ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) - public void requestSingleUpdate(@NonNull Criteria criteria, @NonNull PendingIntent intent) { + public void requestSingleUpdate(@NonNull Criteria criteria, + @NonNull PendingIntent pendingIntent) { Preconditions.checkArgument(criteria != null, "invalid null criteria"); - checkPendingIntent(intent); + checkPendingIntent(pendingIntent); LocationRequest request = LocationRequest.createFromDeprecatedCriteria( criteria, 0, 0, true); - requestLocationUpdates(request, intent); + requestLocationUpdates(request, pendingIntent); } /** @@ -2295,6 +2381,127 @@ public class LocationManager { } } + private static class GetCurrentLocationTransport extends ILocationListener.Stub implements + AlarmManager.OnAlarmListener { + + @GuardedBy("this") + @Nullable + private Executor mExecutor; + + @GuardedBy("this") + @Nullable + private Consumer<Location> mConsumer; + + @GuardedBy("this") + @Nullable + private AlarmManager mAlarmManager; + + @GuardedBy("this") + @Nullable + private ICancellationSignal mRemoteCancellationSignal; + + private GetCurrentLocationTransport(Executor executor, Consumer<Location> consumer) { + Preconditions.checkArgument(executor != null, "illegal null executor"); + Preconditions.checkArgument(consumer != null, "illegal null consumer"); + mExecutor = executor; + mConsumer = consumer; + mAlarmManager = null; + mRemoteCancellationSignal = null; + } + + public synchronized void register(AlarmManager alarmManager, + ICancellationSignal remoteCancellationSignal) { + if (mConsumer == null) { + return; + } + + mAlarmManager = alarmManager; + mAlarmManager.set( + ELAPSED_REALTIME, + SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_TIMEOUT_MS, + "GetCurrentLocation", + this, + null); + + mRemoteCancellationSignal = remoteCancellationSignal; + } + + public synchronized void cancel() { + mExecutor = null; + mConsumer = null; + + if (mAlarmManager != null) { + mAlarmManager.cancel(this); + mAlarmManager = null; + } + + if (mRemoteCancellationSignal != null) { + try { + mRemoteCancellationSignal.cancel(); + } catch (RemoteException e) { + // ignore + } + mRemoteCancellationSignal = null; + } + } + + public void fail() { + deliverResult(null); + } + + @Override + public void onAlarm() { + synchronized (this) { + // save ourselves a pointless x-process call to cancel the alarm + mAlarmManager = null; + } + + deliverResult(null); + } + + @Override + public void onLocationChanged(Location location) { + synchronized (this) { + // save ourselves a pointless x-process call to cancel the location request + mRemoteCancellationSignal = null; + } + + deliverResult(location); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) {} + + @Override + public void onProviderEnabled(String provider) {} + + @Override + public void onProviderDisabled(String provider) { + // in the event of the provider being disabled it is unlikely that we will get further + // locations, so fail early so the client isn't left waiting hopelessly + deliverResult(null); + } + + private synchronized void deliverResult(@Nullable Location location) { + if (mExecutor == null) { + return; + } + + mExecutor.execute(() -> { + Consumer<Location> consumer; + synchronized (GetCurrentLocationTransport.this) { + if (mConsumer == null) { + return; + } + consumer = mConsumer; + cancel(); + } + + consumer.accept(location); + }); + } + } + private class LocationListenerTransport extends ILocationListener.Stub { private final LocationListener mListener; |