blob: 271f2bbe3ae1f109c09b25b18b88863b846fca96 [file] [log] [blame]
/*
* Copyright (C) 2012 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 android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.os.WorkSource;
import android.util.TimeUtils;
/**
* A data object that contains quality of service parameters for requests
* to the {@link LocationManager}.
*
* <p>LocationRequest objects are used to request a quality of service
* for location updates from the Location Manager.
*
* <p>For example, if your application wants high accuracy location
* it should create a location request with {@link #setQuality} set to
* {@link #ACCURACY_FINE} or {@link #POWER_HIGH}, and it should set
* {@link #setInterval} to less than one second. This would be
* appropriate for mapping applications that are showing your location
* in real-time.
*
* <p>At the other extreme, if you want negligible power
* impact, but to still receive location updates when available, then use
* {@link #setQuality} with {@link #POWER_NONE}. With this request your
* application will not trigger (and therefore will not receive any
* power blame) any location updates, but will receive locations
* triggered by other applications. This would be appropriate for
* applications that have no firm requirement for location, but can
* take advantage when available.
*
* <p>In between these two extremes is a very common use-case, where
* applications definitely want to receive
* updates at a specified interval, and can receive them faster when
* available, but still want a low power impact. These applications
* should consider {@link #POWER_LOW} combined with a faster
* {@link #setFastestInterval} (such as 1 minute) and a slower
* {@link #setInterval} (such as 60 minutes). They will only be assigned
* power blame for the interval set by {@link #setInterval}, but can
* still receive locations triggered by other applications at a rate up
* to {@link #setFastestInterval}. This style of request is appropriate for
* many location aware applications, including background usage. Do be
* careful to also throttle {@link #setFastestInterval} if you perform
* heavy-weight work after receiving an update - such as using the network.
*
* <p>Activities should strongly consider removing all location
* request when entering the background
* (for example at {@link android.app.Activity#onPause}), or
* at least swap the request to a larger interval and lower quality.
* Future version of the location manager may automatically perform background
* throttling on behalf of applications.
*
* <p>Applications cannot specify the exact location sources that are
* used by Android's <em>Fusion Engine</em>. In fact, the system
* may have multiple location sources (providers) running and may
* fuse the results from several sources into a single Location object.
*
* <p>Location requests from applications with
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and not
* {@link android.Manifest.permission#ACCESS_FINE_LOCATION} will
* be automatically throttled to a slower interval, and the location
* object will be obfuscated to only show a coarse level of accuracy.
*
* <p>All location requests are considered hints, and you may receive
* locations that are more accurate, less accurate, and slower
* than requested.
*
* @hide
*/
@SystemApi
public final class LocationRequest implements Parcelable {
/**
* Used with {@link #setQuality} to request the most accurate locations available.
*
* <p>This may be up to 1 meter accuracy, although this is implementation dependent.
*/
public static final int ACCURACY_FINE = 100;
/**
* Used with {@link #setQuality} to request "block" level accuracy.
*
* <p>Block level accuracy is considered to be about 100 meter accuracy,
* although this is implementation dependent. Using a coarse accuracy
* such as this often consumes less power.
*/
public static final int ACCURACY_BLOCK = 102;
/**
* Used with {@link #setQuality} to request "city" level accuracy.
*
* <p>City level accuracy is considered to be about 10km accuracy,
* although this is implementation dependent. Using a coarse accuracy
* such as this often consumes less power.
*/
public static final int ACCURACY_CITY = 104;
/**
* Used with {@link #setQuality} to require no direct power impact (passive locations).
*
* <p>This location request will not trigger any active location requests,
* but will receive locations triggered by other applications. Your application
* will not receive any direct power blame for location work.
*/
public static final int POWER_NONE = 200;
/**
* Used with {@link #setQuality} to request low power impact.
*
* <p>This location request will avoid high power location work where
* possible.
*/
public static final int POWER_LOW = 201;
/**
* Used with {@link #setQuality} to allow high power consumption for location.
*
* <p>This location request will allow high power location work.
*/
public static final int POWER_HIGH = 203;
/**
* By default, mFastestInterval = FASTEST_INTERVAL_MULTIPLE * mInterval
*/
private static final double FASTEST_INTERVAL_FACTOR = 6.0; // 6x
private int mQuality = POWER_LOW;
private long mInterval = 60 * 60 * 1000; // 60 minutes
private long mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR); // 10 minutes
private boolean mExplicitFastestInterval = false;
private long mExpireAt = Long.MAX_VALUE; // no expiry
private int mNumUpdates = Integer.MAX_VALUE; // no expiry
private float mSmallestDisplacement = 0.0f; // meters
private WorkSource mWorkSource = null;
private boolean mHideFromAppOps = false; // True if this request shouldn't be counted by AppOps
private String mProvider = LocationManager.FUSED_PROVIDER; // for deprecated APIs that explicitly request a provider
/**
* Create a location request with default parameters.
*
* <p>Default parameters are for a low power, slowly updated location.
* It can then be adjusted as required by the applications before passing
* to the {@link LocationManager}
*
* @return a new location request
*/
public static LocationRequest create() {
LocationRequest request = new LocationRequest();
return request;
}
/** @hide */
@SystemApi
public static LocationRequest createFromDeprecatedProvider(String provider, long minTime,
float minDistance, boolean singleShot) {
if (minTime < 0) minTime = 0;
if (minDistance < 0) minDistance = 0;
int quality;
if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
quality = POWER_NONE;
} else if (LocationManager.GPS_PROVIDER.equals(provider)) {
quality = ACCURACY_FINE;
} else {
quality = POWER_LOW;
}
LocationRequest request = new LocationRequest()
.setProvider(provider)
.setQuality(quality)
.setInterval(minTime)
.setFastestInterval(minTime)
.setSmallestDisplacement(minDistance);
if (singleShot) request.setNumUpdates(1);
return request;
}
/** @hide */
@SystemApi
public static LocationRequest createFromDeprecatedCriteria(Criteria criteria, long minTime,
float minDistance, boolean singleShot) {
if (minTime < 0) minTime = 0;
if (minDistance < 0) minDistance = 0;
int quality;
switch (criteria.getAccuracy()) {
case Criteria.ACCURACY_COARSE:
quality = ACCURACY_BLOCK;
break;
case Criteria.ACCURACY_FINE:
quality = ACCURACY_FINE;
break;
default: {
switch (criteria.getPowerRequirement()) {
case Criteria.POWER_HIGH:
quality = POWER_HIGH;
default:
quality = POWER_LOW;
}
}
}
LocationRequest request = new LocationRequest()
.setQuality(quality)
.setInterval(minTime)
.setFastestInterval(minTime)
.setSmallestDisplacement(minDistance);
if (singleShot) request.setNumUpdates(1);
return request;
}
/** @hide */
public LocationRequest() { }
/** @hide */
public LocationRequest(LocationRequest src) {
mQuality = src.mQuality;
mInterval = src.mInterval;
mFastestInterval = src.mFastestInterval;
mExplicitFastestInterval = src.mExplicitFastestInterval;
mExpireAt = src.mExpireAt;
mNumUpdates = src.mNumUpdates;
mSmallestDisplacement = src.mSmallestDisplacement;
mProvider = src.mProvider;
mWorkSource = src.mWorkSource;
mHideFromAppOps = src.mHideFromAppOps;
}
/**
* Set the quality of the request.
*
* <p>Use with a accuracy constant such as {@link #ACCURACY_FINE}, or a power
* constant such as {@link #POWER_LOW}. You cannot request both and accuracy and
* power, only one or the other can be specified. The system will then
* maximize accuracy or minimize power as appropriate.
*
* <p>The quality of the request is a strong hint to the system for which
* location sources to use. For example, {@link #ACCURACY_FINE} is more likely
* to use GPS, and {@link #POWER_LOW} is more likely to use WIFI & Cell tower
* positioning, but it also depends on many other factors (such as which sources
* are available) and is implementation dependent.
*
* <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
* on a location request.
*
* @param quality an accuracy or power constant
* @throws InvalidArgumentException if the quality constant is not valid
* @return the same object, so that setters can be chained
*/
public LocationRequest setQuality(int quality) {
checkQuality(quality);
mQuality = quality;
return this;
}
/**
* Get the quality of the request.
*
* @return an accuracy or power constant
*/
public int getQuality() {
return mQuality;
}
/**
* Set the desired interval for active location updates, in milliseconds.
*
* <p>The location manager will actively try to obtain location updates
* for your application at this interval, so it has a
* direct influence on the amount of power used by your application.
* Choose your interval wisely.
*
* <p>This interval is inexact. You may not receive updates at all (if
* no location sources are available), or you may receive them
* slower than requested. You may also receive them faster than
* requested (if other applications are requesting location at a
* faster interval). The fastest rate that that you will receive
* updates can be controlled with {@link #setFastestInterval}.
*
* <p>Applications with only the coarse location permission may have their
* interval silently throttled.
*
* <p>An interval of 0 is allowed, but not recommended, since
* location updates may be extremely fast on future implementations.
*
* <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
* on a location request.
*
* @param millis desired interval in millisecond, inexact
* @throws InvalidArgumentException if the interval is less than zero
* @return the same object, so that setters can be chained
*/
public LocationRequest setInterval(long millis) {
checkInterval(millis);
mInterval = millis;
if (!mExplicitFastestInterval) {
mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR);
}
return this;
}
/**
* Get the desired interval of this request, in milliseconds.
*
* @return desired interval in milliseconds, inexact
*/
public long getInterval() {
return mInterval;
}
/**
* Explicitly set the fastest interval for location updates, in
* milliseconds.
*
* <p>This controls the fastest rate at which your application will
* receive location updates, which might be faster than
* {@link #setInterval} in some situations (for example, if other
* applications are triggering location updates).
*
* <p>This allows your application to passively acquire locations
* at a rate faster than it actively acquires locations, saving power.
*
* <p>Unlike {@link #setInterval}, this parameter is exact. Your
* application will never receive updates faster than this value.
*
* <p>If you don't call this method, a fastest interval
* will be selected for you. It will be a value faster than your
* active interval ({@link #setInterval}).
*
* <p>An interval of 0 is allowed, but not recommended, since
* location updates may be extremely fast on future implementations.
*
* <p>If {@link #setFastestInterval} is set slower than {@link #setInterval},
* then your effective fastest interval is {@link #setInterval}.
*
* @param millis fastest interval for updates in milliseconds, exact
* @throws InvalidArgumentException if the interval is less than zero
* @return the same object, so that setters can be chained
*/
public LocationRequest setFastestInterval(long millis) {
checkInterval(millis);
mExplicitFastestInterval = true;
mFastestInterval = millis;
return this;
}
/**
* Get the fastest interval of this request, in milliseconds.
*
* <p>The system will never provide location updates faster
* than the minimum of {@link #getFastestInterval} and
* {@link #getInterval}.
*
* @return fastest interval in milliseconds, exact
*/
public long getFastestInterval() {
return mFastestInterval;
}
/**
* Set the duration of this request, in milliseconds.
*
* <p>The duration begins immediately (and not when the request
* is passed to the location manager), so call this method again
* if the request is re-used at a later time.
*
* <p>The location manager will automatically stop updates after
* the request expires.
*
* <p>The duration includes suspend time. Values less than 0
* are allowed, but indicate that the request has already expired.
*
* @param millis duration of request in milliseconds
* @return the same object, so that setters can be chained
*/
public LocationRequest setExpireIn(long millis) {
long elapsedRealtime = SystemClock.elapsedRealtime();
// Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
if (millis > Long.MAX_VALUE - elapsedRealtime) {
mExpireAt = Long.MAX_VALUE;
} else {
mExpireAt = millis + elapsedRealtime;
}
if (mExpireAt < 0) mExpireAt = 0;
return this;
}
/**
* Set the request expiration time, in millisecond since boot.
*
* <p>This expiration time uses the same time base as {@link SystemClock#elapsedRealtime}.
*
* <p>The location manager will automatically stop updates after
* the request expires.
*
* <p>The duration includes suspend time. Values before {@link SystemClock#elapsedRealtime}
* are allowed, but indicate that the request has already expired.
*
* @param millis expiration time of request, in milliseconds since boot including suspend
* @return the same object, so that setters can be chained
*/
public LocationRequest setExpireAt(long millis) {
mExpireAt = millis;
if (mExpireAt < 0) mExpireAt = 0;
return this;
}
/**
* Get the request expiration time, in milliseconds since boot.
*
* <p>This value can be compared to {@link SystemClock#elapsedRealtime} to determine
* the time until expiration.
*
* @return expiration time of request, in milliseconds since boot including suspend
*/
public long getExpireAt() {
return mExpireAt;
}
/**
* Set the number of location updates.
*
* <p>By default locations are continuously updated until the request is explicitly
* removed, however you can optionally request a set number of updates.
* For example, if your application only needs a single fresh location,
* then call this method with a value of 1 before passing the request
* to the location manager.
*
* @param numUpdates the number of location updates requested
* @throws InvalidArgumentException if numUpdates is 0 or less
* @return the same object, so that setters can be chained
*/
public LocationRequest setNumUpdates(int numUpdates) {
if (numUpdates <= 0) throw new IllegalArgumentException("invalid numUpdates: " + numUpdates);
mNumUpdates = numUpdates;
return this;
}
/**
* Get the number of updates requested.
*
* <p>By default this is {@link Integer#MAX_VALUE}, which indicates that
* locations are updated until the request is explicitly removed.
* @return number of updates
*/
public int getNumUpdates() {
return mNumUpdates;
}
/** @hide */
public void decrementNumUpdates() {
if (mNumUpdates != Integer.MAX_VALUE) {
mNumUpdates--;
}
if (mNumUpdates < 0) {
mNumUpdates = 0;
}
}
/** @hide */
@SystemApi
public LocationRequest setProvider(String provider) {
checkProvider(provider);
mProvider = provider;
return this;
}
/** @hide */
@SystemApi
public String getProvider() {
return mProvider;
}
/** @hide */
@SystemApi
public LocationRequest setSmallestDisplacement(float meters) {
checkDisplacement(meters);
mSmallestDisplacement = meters;
return this;
}
/** @hide */
@SystemApi
public float getSmallestDisplacement() {
return mSmallestDisplacement;
}
/**
* Sets the WorkSource to use for power blaming of this location request.
*
* <p>No permissions are required to make this call, however the LocationManager
* will throw a SecurityException when requesting location updates if the caller
* doesn't have the {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission.
*
* @param workSource WorkSource defining power blame for this location request.
* @hide
*/
@SystemApi
public void setWorkSource(WorkSource workSource) {
mWorkSource = workSource;
}
/** @hide */
@SystemApi
public WorkSource getWorkSource() {
return mWorkSource;
}
/**
* Sets whether or not this location request should be hidden from AppOps.
*
* <p>Hiding a location request from AppOps will remove user visibility in the UI as to this
* request's existence. It does not affect power blaming in the Battery page.
*
* <p>No permissions are required to make this call, however the LocationManager
* will throw a SecurityException when requesting location updates if the caller
* doesn't have the {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} permission.
*
* @param hideFromAppOps If true AppOps won't keep track of this location request.
* @see android.app.AppOpsManager
* @hide
*/
@SystemApi
public void setHideFromAppOps(boolean hideFromAppOps) {
mHideFromAppOps = hideFromAppOps;
}
/** @hide */
@SystemApi
public boolean getHideFromAppOps() {
return mHideFromAppOps;
}
private static void checkInterval(long millis) {
if (millis < 0) {
throw new IllegalArgumentException("invalid interval: " + millis);
}
}
private static void checkQuality(int quality) {
switch (quality) {
case ACCURACY_FINE:
case ACCURACY_BLOCK:
case ACCURACY_CITY:
case POWER_NONE:
case POWER_LOW:
case POWER_HIGH:
break;
default:
throw new IllegalArgumentException("invalid quality: " + quality);
}
}
private static void checkDisplacement(float meters) {
if (meters < 0.0f) {
throw new IllegalArgumentException("invalid displacement: " + meters);
}
}
private static void checkProvider(String name) {
if (name == null) {
throw new IllegalArgumentException("invalid provider: " + name);
}
}
public static final Parcelable.Creator<LocationRequest> CREATOR =
new Parcelable.Creator<LocationRequest>() {
@Override
public LocationRequest createFromParcel(Parcel in) {
LocationRequest request = new LocationRequest();
request.setQuality(in.readInt());
request.setFastestInterval(in.readLong());
request.setInterval(in.readLong());
request.setExpireAt(in.readLong());
request.setNumUpdates(in.readInt());
request.setSmallestDisplacement(in.readFloat());
request.setHideFromAppOps(in.readInt() != 0);
String provider = in.readString();
if (provider != null) request.setProvider(provider);
WorkSource workSource = in.readParcelable(null);
if (workSource != null) request.setWorkSource(workSource);
return request;
}
@Override
public LocationRequest[] newArray(int size) {
return new LocationRequest[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(mQuality);
parcel.writeLong(mFastestInterval);
parcel.writeLong(mInterval);
parcel.writeLong(mExpireAt);
parcel.writeInt(mNumUpdates);
parcel.writeFloat(mSmallestDisplacement);
parcel.writeInt(mHideFromAppOps ? 1 : 0);
parcel.writeString(mProvider);
parcel.writeParcelable(mWorkSource, 0);
}
/** @hide */
public static String qualityToString(int quality) {
switch (quality) {
case ACCURACY_FINE:
return "ACCURACY_FINE";
case ACCURACY_BLOCK:
return "ACCURACY_BLOCK";
case ACCURACY_CITY:
return "ACCURACY_CITY";
case POWER_NONE:
return "POWER_NONE";
case POWER_LOW:
return "POWER_LOW";
case POWER_HIGH:
return "POWER_HIGH";
default:
return "???";
}
}
@Override
public String toString() {
StringBuilder s = new StringBuilder();
s.append("Request[").append(qualityToString(mQuality));
if (mProvider != null) s.append(' ').append(mProvider);
if (mQuality != POWER_NONE) {
s.append(" requested=");
TimeUtils.formatDuration(mInterval, s);
}
s.append(" fastest=");
TimeUtils.formatDuration(mFastestInterval, s);
if (mExpireAt != Long.MAX_VALUE) {
long expireIn = mExpireAt - SystemClock.elapsedRealtime();
s.append(" expireIn=");
TimeUtils.formatDuration(expireIn, s);
}
if (mNumUpdates != Integer.MAX_VALUE){
s.append(" num=").append(mNumUpdates);
}
s.append(']');
return s.toString();
}
}