summaryrefslogtreecommitdiff
path: root/location/java/android
diff options
context:
space:
mode:
author Soonil Nagarkar <sooniln@google.com> 2019-10-18 16:22:25 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2019-10-18 16:22:25 +0000
commit8eb590bdc79a89739d0cbb8545b8db7e01f76dfb (patch)
treee7f4fa6a9339a7b559dd58b57484129e8e249a3b /location/java/android
parentcd45b2ab20987665ec9324ac81a8e16a995a5da9 (diff)
parent905e7227dcd9b66441b9c3939662a8bc69094d1e (diff)
Merge "Add getCurrentLocation API to LocationManager"
Diffstat (limited to 'location/java/android')
-rw-r--r--location/java/android/location/ILocationManager.aidl8
-rw-r--r--location/java/android/location/LocationManager.java371
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;