/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.location; import com.android.internal.location.ProviderProperties; import android.Manifest; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import static android.Manifest.permission.ACCESS_COARSE_LOCATION; import static android.Manifest.permission.ACCESS_FINE_LOCATION; /** * This class provides access to the system location services. These * services allow applications to obtain periodic updates of the * device's geographical location, or to fire an application-specified * {@link Intent} when the device enters the proximity of a given * geographical location. * *
Unless noted, all Location API methods require
* the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions.
* If your application only has the coarse permission then it will not have
* access to the GPS or passive location providers. Other providers will still
* return location results, but the update rate will be throttled and the exact
* location will be obfuscated to a coarse level of accuracy.
*/
@SystemService(Context.LOCATION_SERVICE)
public class LocationManager {
private static final String TAG = "LocationManager";
private final Context mContext;
private final ILocationManager mService;
private final GnssMeasurementCallbackTransport mGnssMeasurementCallbackTransport;
private final GnssNavigationMessageCallbackTransport mGnssNavigationMessageCallbackTransport;
private final BatchedLocationCallbackTransport mBatchedLocationCallbackTransport;
private final HashMap This provider determines location based on
* availability 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.
*
* 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}.
*
* The extras Bundle for the GPS location provider can contain the
* following key/value pairs:
* 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.
*/
public static final String PASSIVE_PROVIDER = "passive";
/**
* Name of the Fused location provider.
*
* 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.
*
* @hide
*/
public static final String FUSED_PROVIDER = "fused";
/**
* Key used for the Bundle extra holding a boolean indicating whether
* a proximity alert is entering (true) or exiting (false)..
*/
public static final String KEY_PROXIMITY_ENTERING = "entering";
/**
* Key used for a Bundle extra holding an Integer status value
* when a status change is broadcast using a PendingIntent.
*/
public static final String KEY_STATUS_CHANGED = "status";
/**
* Key used for a Bundle extra holding an Boolean status value
* when a provider enabled/disabled event is broadcast using a PendingIntent.
*/
public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
/**
* Key used for a Bundle extra holding a Location value
* when a location change is broadcast using a PendingIntent.
*/
public static final String KEY_LOCATION_CHANGED = "location";
/**
* Broadcast intent action indicating that the GPS has either been
* enabled or disabled. An intent extra provides this state as a boolean,
* where {@code true} means enabled.
* @see #EXTRA_GPS_ENABLED
*
* @hide
*/
public static final String GPS_ENABLED_CHANGE_ACTION =
"android.location.GPS_ENABLED_CHANGE";
/**
* Broadcast intent action when the configured location providers
* change. For use with {@link #isProviderEnabled(String)}. If you're interacting with the
* {@link android.provider.Settings.Secure#LOCATION_MODE} API, use {@link #MODE_CHANGED_ACTION}
* instead.
*/
public static final String PROVIDERS_CHANGED_ACTION =
"android.location.PROVIDERS_CHANGED";
/**
* Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} changes.
* For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API.
* If you're interacting with {@link #isProviderEnabled(String)}, use
* {@link #PROVIDERS_CHANGED_ACTION} instead.
*
* In the future, there may be mode changes that do not result in
* {@link #PROVIDERS_CHANGED_ACTION} broadcasts.
*/
public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
/**
* Broadcast intent action indicating that the GPS has either started or
* stopped receiving GPS fixes. An intent extra provides this state as a
* boolean, where {@code true} means that the GPS is actively receiving fixes.
* @see #EXTRA_GPS_ENABLED
*
* @hide
*/
public static final String GPS_FIX_CHANGE_ACTION =
"android.location.GPS_FIX_CHANGE";
/**
* The lookup key for a boolean that indicates whether GPS is enabled or
* disabled. {@code true} means GPS is enabled. Retrieve it with
* {@link android.content.Intent#getBooleanExtra(String,boolean)}.
*
* @hide
*/
public static final String EXTRA_GPS_ENABLED = "enabled";
/**
* Broadcast intent action indicating that a high power location requests
* has either started or stopped being active. The current state of
* active location requests should be read from AppOpsManager using
* {@code OP_MONITOR_HIGH_POWER_LOCATION}.
*
* @hide
*/
public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
"android.location.HIGH_POWER_REQUEST_CHANGE";
// Map from LocationListeners to their associated ListenerTransport objects
private HashMap All providers are returned, including ones that are not permitted to
* be accessed by the calling activity or are currently disabled.
*
* @return list of Strings containing names of the provider
*/
public List Note that the requirement on monetary cost is not removed
* in this process.
*
* @param criteria the criteria that need to be matched
* @param enabledOnly if true then only a provider that is currently enabled is returned
* @return name of the provider that best matches the requirements
*/
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
checkCriteria(criteria);
try {
return mService.getBestProvider(criteria, enabledOnly);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Register for location updates using the named provider, and a
* pending intent.
*
* 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 minTime minimum time interval between location updates, in milliseconds
* @param minDistance minimum distance between location updates, in meters
* @param listener a {@link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* on this device
* @throws IllegalArgumentException if listener is null
* @throws RuntimeException if the calling thread has no Looper
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(String provider, long minTime, float minDistance,
LocationListener listener) {
checkProvider(provider);
checkListener(listener);
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, minTime, minDistance, false);
requestLocationUpdates(request, listener, null, null);
}
/**
* Register for location updates using the named provider, and a callback on
* the specified looper thread.
*
* 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 minTime minimum time interval between location updates, in milliseconds
* @param minDistance minimum distance between location updates, in meters
* @param listener a {@link LocationListener} whose
* {@link LocationListener#onLocationChanged} method will be called for
* each location update
* @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
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(String provider, long minTime, float minDistance,
LocationListener listener, Looper looper) {
checkProvider(provider);
checkListener(listener);
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, minTime, minDistance, false);
requestLocationUpdates(request, listener, looper, null);
}
/**
* Register for location updates using a Criteria, and a callback
* on the specified looper thread.
*
* See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
* for more detail on how to use this method.
*
* @param minTime minimum time interval between location updates, in milliseconds
* @param minDistance minimum distance between location updates, in meters
* @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 for
* each location update
* @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
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
LocationListener listener, Looper looper) {
checkCriteria(criteria);
checkListener(listener);
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, minTime, minDistance, false);
requestLocationUpdates(request, listener, looper, null);
}
/**
* Register for location updates using the named provider, and a
* pending intent.
*
* 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 minTime minimum time interval between location updates, in milliseconds
* @param minDistance minimum distance between location updates, in meters
* @param intent a {@link PendingIntent} to be sent for each location update
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* on this device
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(String provider, long minTime, float minDistance,
PendingIntent intent) {
checkProvider(provider);
checkPendingIntent(intent);
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, minTime, minDistance, false);
requestLocationUpdates(request, null, null, intent);
}
/**
* Register for location updates using a Criteria and pending intent.
*
* The It may take a while to receive the first location update. If
* an immediate location is required, applications may use the
* {@link #getLastKnownLocation(String)} method.
*
* Location updates are received either by {@link LocationListener}
* callbacks, or by broadcast intents to a supplied {@link PendingIntent}.
*
* If the caller supplied a pending intent, then location updates
* are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
* {@link android.location.Location} value.
*
* The location update interval can be controlled using the minTime parameter.
* The elapsed time between location updates will never be less than
* minTime, although it can be more depending on the Location Provider
* implementation and the update interval requested by other applications.
*
* Choosing a sensible value for minTime is important to conserve
* battery life. Each location update requires power from
* GPS, WIFI, Cell and other radios. Select a minTime value as high as
* possible while still providing a reasonable user experience.
* If your application is not in the foreground and showing
* location to the user then your application should avoid using an active
* provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
* but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
* or greater. If your application is in the foreground and showing
* location to the user then it is appropriate to select a faster
* update interval.
*
* The minDistance parameter can also be used to control the
* frequency of location updates. If it is greater than 0 then the
* location provider will only send your application an update when
* the location has changed by at least minDistance meters, AND
* at least minTime milliseconds have passed. However it is more
* difficult for location providers to save power using the minDistance
* parameter, so minTime should be the primary tool to conserving battery
* life.
*
* If your application wants to passively observe location
* updates triggered by other applications, but not consume
* any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
* This provider does not actively turn on or modify active location
* providers, so you do not need to be as careful about minTime and
* minDistance. However if your application performs heavy work
* on a location update (such as network activity) then you should
* select non-zero values for minTime and/or minDistance to rate-limit
* your update frequency in the case another application enables a
* location provider with extremely fast updates.
*
* In case the provider is disabled by the user, updates will stop,
* and a provider availability update will be sent.
* As soon as the provider is enabled again,
* location updates will immediately resume and a provider availability
* update sent. Providers can also send status updates, at any time,
* with extra's specific to the provider. If a callback was supplied
* then status and availability updates are via
* {@link LocationListener#onProviderDisabled},
* {@link LocationListener#onProviderEnabled} or
* {@link LocationListener#onStatusChanged}. Alternately, if a
* pending intent was supplied then status and availability updates
* are broadcast intents with extra keys of
* {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED}.
*
* If a {@link LocationListener} is used but with no Looper specified
* then the calling thread must already
* be a {@link android.os.Looper} thread such as the main thread of the
* calling Activity. If a Looper is specified with a {@link LocationListener}
* then callbacks are made on the supplied Looper thread.
*
* Prior to Jellybean, the minTime parameter was
* only a hint, and some location provider implementations ignored it.
* From Jellybean and onwards it is mandatory for Android compatible
* devices to observe both the minTime and minDistance parameters.
*
* @param minTime minimum time interval between location updates, in milliseconds
* @param minDistance minimum distance between location updates, in meters
* @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 each location update
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
PendingIntent intent) {
checkCriteria(criteria);
checkPendingIntent(intent);
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, minTime, minDistance, false);
requestLocationUpdates(request, null, null, intent);
}
/**
* Register for a single location update using the named provider and
* a callback.
*
* 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 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
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
checkProvider(provider);
checkListener(listener);
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
requestLocationUpdates(request, listener, looper, null);
}
/**
* Register for a single location update using a Criteria and
* a callback.
*
* 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
*
* @throws IllegalArgumentException if criteria is null
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
checkCriteria(criteria);
checkListener(listener);
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
requestLocationUpdates(request, listener, looper, null);
}
/**
* Register for a single location update using a named provider and pending intent.
*
* 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
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestSingleUpdate(String provider, PendingIntent intent) {
checkProvider(provider);
checkPendingIntent(intent);
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
requestLocationUpdates(request, null, null, intent);
}
/**
* Register for a single location update using a Criteria and pending intent.
*
* 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
*
* @throws IllegalArgumentException if provider is null or doesn't exist
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
checkCriteria(criteria);
checkPendingIntent(intent);
LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
criteria, 0, 0, true);
requestLocationUpdates(request, null, null, intent);
}
/**
* Register for fused location updates using a LocationRequest and callback.
*
* Upon a location update, the system delivers the new {@link Location} to the
* provided {@link LocationListener}, by calling its {@link
* LocationListener#onLocationChanged} method. The system will automatically select and enable the best providers
* to compute a location for your application. It may use only passive
* locations, or just a single location source, or it may fuse together
* multiple location sources in order to produce the best possible
* result, depending on the quality of service requested in the
* {@link LocationRequest}.
*
* LocationRequest can be null, in which case the system will choose
* default, low power parameters for location updates. You will occasionally
* receive location updates as available, without a major power impact on the
* system. If your application just needs an occasional location update
* without any strict demands, then pass a null LocationRequest.
*
* Only one LocationRequest can be registered for each unique callback
* or pending intent. So a subsequent request with the same callback or
* pending intent will over-write the previous LocationRequest.
*
* If a pending intent is supplied then location updates
* are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
* {@link android.location.Location} value. If a callback is supplied
* then location updates are made using the
* {@link LocationListener#onLocationChanged} callback, on the specified
* Looper thread. If a {@link LocationListener} is used
* but with a null Looper then the calling thread must already
* be a {@link android.os.Looper} thread (such as the main thread) and
* callbacks will occur on this thread.
*
* Provider status updates and availability updates are deprecated
* because the system is performing provider fusion on the applications
* behalf. So {@link LocationListener#onProviderDisabled},
* {@link LocationListener#onProviderEnabled}, {@link LocationListener#onStatusChanged}
* will not be called, and intents with extra keys of
* {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED} will not
* be received.
*
* To unregister for Location updates, use: {@link #removeUpdates(LocationListener)}.
*
* @param request quality of service required, null for default low power
* @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
*
* @throws IllegalArgumentException if listener is null
* @throws SecurityException if no suitable permission is present
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(LocationRequest request, LocationListener listener,
Looper looper) {
checkListener(listener);
requestLocationUpdates(request, listener, looper, null);
}
/**
* Register for fused location updates using a LocationRequest and a pending intent.
*
* Upon a location update, the system delivers the new {@link Location} with your provided
* {@link PendingIntent}, as the value for {@link LocationManager#KEY_LOCATION_CHANGED}
* in the intent's extras. To unregister for Location updates, use: {@link #removeUpdates(PendingIntent)}.
*
* See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)}
* for more detail.
*
* @param request quality of service required, null for default low power
* @param intent a {@link PendingIntent} to be sent for the location update
*
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if no suitable permission is present
*
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
checkPendingIntent(intent);
requestLocationUpdates(request, null, null, intent);
}
private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
if (listener == null) return null;
synchronized (mListeners) {
ListenerTransport transport = mListeners.get(listener);
if (transport == null) {
transport = new ListenerTransport(listener, looper);
}
mListeners.put(listener, transport);
return transport;
}
}
private void requestLocationUpdates(LocationRequest request, LocationListener listener,
Looper looper, PendingIntent intent) {
String packageName = mContext.getPackageName();
// wrap the listener class
ListenerTransport transport = wrapListener(listener, looper);
try {
mService.requestLocationUpdates(request, transport, intent, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes all location updates for the specified LocationListener.
*
* Following this call, updates will no longer
* occur for this listener.
*
* @param listener listener object that no longer needs location updates
* @throws IllegalArgumentException if listener is null
*/
public void removeUpdates(LocationListener listener) {
checkListener(listener);
String packageName = mContext.getPackageName();
ListenerTransport transport;
synchronized (mListeners) {
transport = mListeners.remove(listener);
}
if (transport == null) return;
try {
mService.removeUpdates(transport, null, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes all location updates for the specified pending intent.
*
* Following this call, updates will no longer for this pending intent.
*
* @param intent pending intent object that no longer needs location updates
* @throws IllegalArgumentException if intent is null
*/
public void removeUpdates(PendingIntent intent) {
checkPendingIntent(intent);
String packageName = mContext.getPackageName();
try {
mService.removeUpdates(null, intent, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Set a proximity alert for the location given by the position
* (latitude, longitude) and the given radius.
*
* When the device
* detects that it has entered or exited the area surrounding the
* location, the given PendingIntent will be used to create an Intent
* to be fired.
*
* The fired Intent will have a boolean extra added with key
* {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
* entering the proximity region; if false, it is exiting.
*
* Due to the approximate nature of position estimation, if the
* device passes through the given area briefly, it is possible
* that no Intent will be fired. Similarly, an Intent could be
* fired if the device passes very close to the given area but
* does not actually enter it.
*
* After the number of milliseconds given by the expiration
* parameter, the location manager will delete this proximity
* alert and no longer monitor it. A value of -1 indicates that
* there should be no expiration time.
*
* Internally, this method uses both {@link #NETWORK_PROVIDER}
* and {@link #GPS_PROVIDER}.
*
* Before API version 17, this method could be used with
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
* From API version 17 and onwards, this method requires
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
*
* @param latitude the latitude of the central point of the
* alert region
* @param longitude the longitude of the central point of the
* alert region
* @param radius the radius of the central point of the
* alert region, in meters
* @param expiration time for this proximity alert, in milliseconds,
* or -1 to indicate no expiration
* @param intent a PendingIntent that will be used to generate an Intent to
* fire when entry to or exit from the alert region is detected
*
* @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission is not present
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
PendingIntent intent) {
checkPendingIntent(intent);
if (expiration < 0) expiration = Long.MAX_VALUE;
Geofence fence = Geofence.createCircle(latitude, longitude, radius);
LocationRequest request = new LocationRequest().setExpireIn(expiration);
try {
mService.requestGeofence(request, fence, intent, mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Add a geofence with the specified LocationRequest quality of service.
*
* When the device
* detects that it has entered or exited the area surrounding the
* location, the given PendingIntent will be used to create an Intent
* to be fired.
*
* The fired Intent will have a boolean extra added with key
* {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
* entering the proximity region; if false, it is exiting.
*
* The geofence engine fuses results from all location providers to
* provide the best balance between accuracy and power. Applications
* can choose the quality of service required using the
* {@link LocationRequest} object. If it is null then a default,
* low power geo-fencing implementation is used. It is possible to cross
* a geo-fence without notification, but the system will do its best
* to detect, using {@link LocationRequest} as a hint to trade-off
* accuracy and power.
*
* The power required by the geofence engine can depend on many factors,
* such as quality and interval requested in {@link LocationRequest},
* distance to nearest geofence and current device velocity.
*
* @param request quality of service required, null for default low power
* @param fence a geographical description of the geofence area
* @param intent pending intent to receive geofence updates
*
* @throws IllegalArgumentException if fence is null
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission is not present
*
* @hide
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
checkPendingIntent(intent);
checkGeofence(fence);
try {
mService.requestGeofence(request, fence, intent, mContext.getPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes the proximity alert with the given PendingIntent.
*
* Before API version 17, this method could be used with
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
* From API version 17 and onwards, this method requires
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
*
* @param intent the PendingIntent that no longer needs to be notified of
* proximity alerts
*
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission is not present
*/
public void removeProximityAlert(PendingIntent intent) {
checkPendingIntent(intent);
String packageName = mContext.getPackageName();
try {
mService.removeGeofence(null, intent, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Remove a single geofence.
*
* This removes only the specified geofence associated with the
* specified pending intent. All other geofences remain unchanged.
*
* @param fence a geofence previously passed to {@link #addGeofence}
* @param intent a pending intent previously passed to {@link #addGeofence}
*
* @throws IllegalArgumentException if fence is null
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission is not present
*
* @hide
*/
public void removeGeofence(Geofence fence, PendingIntent intent) {
checkPendingIntent(intent);
checkGeofence(fence);
String packageName = mContext.getPackageName();
try {
mService.removeGeofence(fence, intent, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Remove all geofences registered to the specified pending intent.
*
* @param intent a pending intent previously passed to {@link #addGeofence}
*
* @throws IllegalArgumentException if intent is null
* @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
* permission is not present
*
* @hide
*/
public void removeAllGeofences(PendingIntent intent) {
checkPendingIntent(intent);
String packageName = mContext.getPackageName();
try {
mService.removeGeofence(null, intent, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns the current enabled/disabled status of the given provider.
*
* If the user has enabled this provider in the Settings menu, true
* is returned otherwise false is returned
*
* Callers should instead use
* {@link android.provider.Settings.Secure#LOCATION_MODE}
* unless they depend on provider-specific APIs such as
* {@link #requestLocationUpdates(String, long, float, LocationListener)}.
*
*
* Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
* method would throw {@link SecurityException} if the location permissions
* were not sufficient to use the specified provider.
*
* @param provider the name of the provider
* @return true if the provider exists and is enabled
*
* @throws IllegalArgumentException if provider is null
*/
public boolean isProviderEnabled(String provider) {
checkProvider(provider);
try {
return mService.isProviderEnabled(provider);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Get the last known location.
*
* This location could be very old so use
* {@link Location#getElapsedRealtimeNanos} to calculate its age. It can
* also return null if no previous location is available.
*
* Always returns immediately.
*
* @return The last known location, or null if not available
* @throws SecurityException if no suitable permission is present
*
* @hide
*/
public Location getLastLocation() {
String packageName = mContext.getPackageName();
try {
return mService.getLastLocation(null, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Returns a Location indicating the data from the last known
* location fix obtained from the given provider.
*
* This can be done
* without starting the provider. Note that this location could
* be out-of-date, for example if the device was turned off and
* moved to another location.
*
* If the provider is currently disabled, null is returned.
*
* @param provider the name of the provider
* @return the last known location for the provider, or null
*
* @throws SecurityException if no suitable permission is present
* @throws IllegalArgumentException if provider is null or doesn't exist
*/
@RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
public Location getLastKnownLocation(String provider) {
checkProvider(provider);
String packageName = mContext.getPackageName();
LocationRequest request = LocationRequest.createFromDeprecatedProvider(
provider, 0, 0, true);
try {
return mService.getLastLocation(request, packageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
// --- Mock provider support ---
// TODO: It would be fantastic to deprecate mock providers entirely, and replace
// with something closer to LocationProviderBase.java
/**
* Creates a mock location provider and adds it to the set of active providers.
*
* @param name the provider name
*
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if a provider with the given name already exists
*/
public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
ProviderProperties properties = new ProviderProperties(requiresNetwork,
requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
supportsBearing, powerRequirement, accuracy);
if (name.matches(LocationProvider.BAD_CHARS_REGEX)) {
throw new IllegalArgumentException("provider name contains illegal character: " + name);
}
try {
mService.addTestProvider(name, properties, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes the mock location provider with the given name.
*
* @param provider the provider name
*
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
*/
public void removeTestProvider(String provider) {
try {
mService.removeTestProvider(provider, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets a mock location for the given provider.
* This location will be used in place of any actual location from the provider.
* The location object must have a minimum number of fields set to be
* considered a valid LocationProvider Location, as per documentation
* on {@link Location} class.
*
* @param provider the provider name
* @param loc the mock location
*
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
* @throws IllegalArgumentException if the location is incomplete
*/
public void setTestProviderLocation(String provider, Location loc) {
if (!loc.isComplete()) {
IllegalArgumentException e = new IllegalArgumentException(
"Incomplete location object, missing timestamp or accuracy? " + loc);
if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
// just log on old platform (for backwards compatibility)
Log.w(TAG, e);
loc.makeComplete();
} else {
// really throw it!
throw e;
}
}
try {
mService.setTestProviderLocation(provider, loc, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes any mock location associated with the given provider.
*
* @param provider the provider name
*
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
*/
public void clearTestProviderLocation(String provider) {
try {
mService.clearTestProviderLocation(provider, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets a mock enabled value for the given provider. This value will be used in place
* of any actual value from the provider.
*
* @param provider the provider name
* @param enabled the mock enabled value
*
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
*/
public void setTestProviderEnabled(String provider, boolean enabled) {
try {
mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes any mock enabled value associated with the given provider.
*
* @param provider the provider name
*
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
*/
public void clearTestProviderEnabled(String provider) {
try {
mService.clearTestProviderEnabled(provider, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets mock status values for the given provider. These values will be used in place
* of any actual values from the provider.
*
* @param provider the provider name
* @param status the mock status
* @param extras a Bundle containing mock extras
* @param updateTime the mock update time
*
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
*/
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
try {
mService.setTestProviderStatus(provider, status, extras, updateTime,
mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Removes any mock status values associated with the given provider.
*
* @param provider the provider name
*
* @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
* mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
* allowed} for your app.
* @throws IllegalArgumentException if no provider with the given name exists
*/
public void clearTestProviderStatus(String provider) {
try {
mService.clearTestProviderStatus(provider, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
// --- GPS-specific support ---
// This class is used to send Gnss status events to the client's specific thread.
private class GnssStatusListenerTransport extends IGnssStatusListener.Stub {
private final GpsStatus.Listener mGpsListener;
private final GpsStatus.NmeaListener mGpsNmeaListener;
private final GnssStatus.Callback mGnssCallback;
private final OnNmeaMessageListener mGnssNmeaListener;
private class GnssHandler extends Handler {
public GnssHandler(Handler handler) {
super(handler != null ? handler.getLooper() : Looper.myLooper());
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case NMEA_RECEIVED:
synchronized (mNmeaBuffer) {
int length = mNmeaBuffer.size();
for (int i = 0; i < length; i++) {
Nmea nmea = mNmeaBuffer.get(i);
mGnssNmeaListener.onNmeaMessage(nmea.mNmea, nmea.mTimestamp);
}
mNmeaBuffer.clear();
}
break;
case GpsStatus.GPS_EVENT_STARTED:
mGnssCallback.onStarted();
break;
case GpsStatus.GPS_EVENT_STOPPED:
mGnssCallback.onStopped();
break;
case GpsStatus.GPS_EVENT_FIRST_FIX:
mGnssCallback.onFirstFix(mTimeToFirstFix);
break;
case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
mGnssCallback.onSatelliteStatusChanged(mGnssStatus);
break;
default:
break;
}
}
}
private final Handler mGnssHandler;
// This must not equal any of the GpsStatus event IDs
private static final int NMEA_RECEIVED = 1000;
private class Nmea {
long mTimestamp;
String mNmea;
Nmea(long timestamp, String nmea) {
mTimestamp = timestamp;
mNmea = nmea;
}
}
private final ArrayList
*
*/
public static final String GPS_PROVIDER = "gps";
/**
* A special location provider for receiving locations without actually initiating
* a location fix.
*
*
*
*
* requestLocationUpdates()
and
* requestSingleUpdate()
register the current activity to be
* updated periodically by the named provider, or by the provider matching
* the specified {@link Criteria}, with location and status updates.
*
*