Home | History | Annotate | Download | only in location
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.location;
     18 
     19 import android.os.Parcel;
     20 import android.os.Parcelable;
     21 import android.os.SystemClock;
     22 import android.util.TimeUtils;
     23 
     24 
     25 /**
     26  * A data object that contains quality of service parameters for requests
     27  * to the {@link LocationManager}.
     28  *
     29  * <p>LocationRequest objects are used to request a quality of service
     30  * for location updates from the Location Manager.
     31  *
     32  * <p>For example, if your application wants high accuracy location
     33  * it should create a location request with {@link #setQuality} set to
     34  * {@link #ACCURACY_FINE} or {@link #POWER_HIGH}, and it should set
     35  * {@link #setInterval} to less than one second. This would be
     36  * appropriate for mapping applications that are showing your location
     37  * in real-time.
     38  *
     39  * <p>At the other extreme, if you want negligible power
     40  * impact, but to still receive location updates when available, then use
     41  * {@link #setQuality} with {@link #POWER_NONE}. With this request your
     42  * application will not trigger (and therefore will not receive any
     43  * power blame) any location updates, but will receive locations
     44  * triggered by other applications. This would be appropriate for
     45  * applications that have no firm requirement for location, but can
     46  * take advantage when available.
     47  *
     48  * <p>In between these two extremes is a very common use-case, where
     49  * applications definitely want to receive
     50  * updates at a specified interval, and can receive them faster when
     51  * available, but still want a low power impact. These applications
     52  * should consider {@link #POWER_LOW} combined with a faster
     53  * {@link #setFastestInterval} (such as 1 minute) and a slower
     54  * {@link #setInterval} (such as 60 minutes). They will only be assigned
     55  * power blame for the interval set by {@link #setInterval}, but can
     56  * still receive locations triggered by other applications at a rate up
     57  * to {@link #setFastestInterval}. This style of request is appropriate for
     58  * many location aware applications, including background usage. Do be
     59  * careful to also throttle {@link #setFastestInterval} if you perform
     60  * heavy-weight work after receiving an update - such as using the network.
     61  *
     62  * <p>Activities should strongly consider removing all location
     63  * request when entering the background
     64  * (for example at {@link android.app.Activity#onPause}), or
     65  * at least swap the request to a larger interval and lower quality.
     66  * Future version of the location manager may automatically perform background
     67  * throttling on behalf of applications.
     68  *
     69  * <p>Applications cannot specify the exact location sources that are
     70  * used by Android's <em>Fusion Engine</em>. In fact, the system
     71  * may have multiple location sources (providers) running and may
     72  * fuse the results from several sources into a single Location object.
     73  *
     74  * <p>Location requests from applications with
     75  * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and not
     76  * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} will
     77  * be automatically throttled to a slower interval, and the location
     78  * object will be obfuscated to only show a coarse level of accuracy.
     79  *
     80  * <p>All location requests are considered hints, and you may receive
     81  * locations that are more accurate, less accurate, and slower
     82  * than requested.
     83  *
     84  * @hide
     85  */
     86 public final class LocationRequest implements Parcelable {
     87     /**
     88      * Used with {@link #setQuality} to request the most accurate locations available.
     89      *
     90      * <p>This may be up to 1 meter accuracy, although this is implementation dependent.
     91      */
     92     public static final int ACCURACY_FINE = 100;
     93 
     94     /**
     95      * Used with {@link #setQuality} to request "block" level accuracy.
     96      *
     97      * <p>Block level accuracy is considered to be about 100 meter accuracy,
     98      * although this is implementation dependent. Using a coarse accuracy
     99      * such as this often consumes less power.
    100      */
    101     public static final int ACCURACY_BLOCK = 102;
    102 
    103     /**
    104      * Used with {@link #setQuality} to request "city" level accuracy.
    105      *
    106      * <p>City level accuracy is considered to be about 10km accuracy,
    107      * although this is implementation dependent. Using a coarse accuracy
    108      * such as this often consumes less power.
    109      */
    110     public static final int ACCURACY_CITY = 104;
    111 
    112     /**
    113      * Used with {@link #setQuality} to require no direct power impact (passive locations).
    114      *
    115      * <p>This location request will not trigger any active location requests,
    116      * but will receive locations triggered by other applications. Your application
    117      * will not receive any direct power blame for location work.
    118      */
    119     public static final int POWER_NONE = 200;
    120 
    121     /**
    122      * Used with {@link #setQuality} to request low power impact.
    123      *
    124      * <p>This location request will avoid high power location work where
    125      * possible.
    126      */
    127     public static final int POWER_LOW = 201;
    128 
    129     /**
    130      * Used with {@link #setQuality} to allow high power consumption for location.
    131      *
    132      * <p>This location request will allow high power location work.
    133      */
    134     public static final int POWER_HIGH = 203;
    135 
    136     /**
    137      * By default, mFastestInterval = FASTEST_INTERVAL_MULTIPLE * mInterval
    138      */
    139     private static final double FASTEST_INTERVAL_FACTOR = 6.0;  // 6x
    140 
    141     private int mQuality = POWER_LOW;
    142     private long mInterval = 60 * 60 * 1000;   // 60 minutes
    143     private long mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR);  // 10 minutes
    144     private boolean mExplicitFastestInterval = false;
    145     private long mExpireAt = Long.MAX_VALUE;  // no expiry
    146     private int mNumUpdates = Integer.MAX_VALUE;  // no expiry
    147     private float mSmallestDisplacement = 0.0f;    // meters
    148 
    149     private String mProvider = LocationManager.FUSED_PROVIDER;  // for deprecated APIs that explicitly request a provider
    150 
    151     /**
    152      * Create a location request with default parameters.
    153      *
    154      * <p>Default parameters are for a low power, slowly updated location.
    155      * It can then be adjusted as required by the applications before passing
    156      * to the {@link LocationManager}
    157      *
    158      * @return a new location request
    159      */
    160     public static LocationRequest create() {
    161         LocationRequest request = new LocationRequest();
    162         return request;
    163     }
    164 
    165     /** @hide */
    166     public static LocationRequest createFromDeprecatedProvider(String provider, long minTime,
    167             float minDistance, boolean singleShot) {
    168         if (minTime < 0) minTime = 0;
    169         if (minDistance < 0) minDistance = 0;
    170 
    171         int quality;
    172         if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
    173             quality = POWER_NONE;
    174         } else if (LocationManager.GPS_PROVIDER.equals(provider)) {
    175             quality = ACCURACY_FINE;
    176         } else {
    177             quality = POWER_LOW;
    178         }
    179 
    180         LocationRequest request = new LocationRequest()
    181             .setProvider(provider)
    182             .setQuality(quality)
    183             .setInterval(minTime)
    184             .setFastestInterval(minTime)
    185             .setSmallestDisplacement(minDistance);
    186         if (singleShot) request.setNumUpdates(1);
    187         return request;
    188     }
    189 
    190     /** @hide */
    191     public static LocationRequest createFromDeprecatedCriteria(Criteria criteria, long minTime,
    192             float minDistance, boolean singleShot) {
    193         if (minTime < 0) minTime = 0;
    194         if (minDistance < 0) minDistance = 0;
    195 
    196         int quality;
    197         switch (criteria.getAccuracy()) {
    198             case Criteria.ACCURACY_COARSE:
    199                 quality = ACCURACY_BLOCK;
    200                 break;
    201             case Criteria.ACCURACY_FINE:
    202                 quality = ACCURACY_FINE;
    203                 break;
    204             default: {
    205                 switch (criteria.getPowerRequirement()) {
    206                     case Criteria.POWER_HIGH:
    207                         quality = POWER_HIGH;
    208                     default:
    209                         quality = POWER_LOW;
    210                 }
    211             }
    212         }
    213 
    214         LocationRequest request = new LocationRequest()
    215             .setQuality(quality)
    216             .setInterval(minTime)
    217             .setFastestInterval(minTime)
    218             .setSmallestDisplacement(minDistance);
    219         if (singleShot) request.setNumUpdates(1);
    220         return request;
    221     }
    222 
    223     /** @hide */
    224     public LocationRequest() { }
    225 
    226     /** @hide */
    227     public LocationRequest(LocationRequest src) {
    228         mQuality = src.mQuality;
    229         mInterval = src.mInterval;
    230         mFastestInterval = src.mFastestInterval;
    231         mExplicitFastestInterval = src.mExplicitFastestInterval;
    232         mExpireAt = src.mExpireAt;
    233         mNumUpdates = src.mNumUpdates;
    234         mSmallestDisplacement = src.mSmallestDisplacement;
    235         mProvider = src.mProvider;
    236     }
    237 
    238     /**
    239      * Set the quality of the request.
    240      *
    241      * <p>Use with a accuracy constant such as {@link #ACCURACY_FINE}, or a power
    242      * constant such as {@link #POWER_LOW}. You cannot request both and accuracy and
    243      * power, only one or the other can be specified. The system will then
    244      * maximize accuracy or minimize power as appropriate.
    245      *
    246      * <p>The quality of the request is a strong hint to the system for which
    247      * location sources to use. For example, {@link #ACCURACY_FINE} is more likely
    248      * to use GPS, and {@link #POWER_LOW} is more likely to use WIFI & Cell tower
    249      * positioning, but it also depends on many other factors (such as which sources
    250      * are available) and is implementation dependent.
    251      *
    252      * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
    253      * on a location request.
    254      *
    255      * @param quality an accuracy or power constant
    256      * @throws InvalidArgumentException if the quality constant is not valid
    257      * @return the same object, so that setters can be chained
    258      */
    259     public LocationRequest setQuality(int quality) {
    260         checkQuality(quality);
    261         mQuality = quality;
    262         return this;
    263     }
    264 
    265     /**
    266      * Get the quality of the request.
    267      *
    268      * @return an accuracy or power constant
    269      */
    270     public int getQuality() {
    271         return mQuality;
    272     }
    273 
    274     /**
    275      * Set the desired interval for active location updates, in milliseconds.
    276      *
    277      * <p>The location manager will actively try to obtain location updates
    278      * for your application at this interval, so it has a
    279      * direct influence on the amount of power used by your application.
    280      * Choose your interval wisely.
    281      *
    282      * <p>This interval is inexact. You may not receive updates at all (if
    283      * no location sources are available), or you may receive them
    284      * slower than requested. You may also receive them faster than
    285      * requested (if other applications are requesting location at a
    286      * faster interval). The fastest rate that that you will receive
    287      * updates can be controlled with {@link #setFastestInterval}.
    288      *
    289      * <p>Applications with only the coarse location permission may have their
    290      * interval silently throttled.
    291      *
    292      * <p>An interval of 0 is allowed, but not recommended, since
    293      * location updates may be extremely fast on future implementations.
    294      *
    295      * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
    296      * on a location request.
    297      *
    298      * @param millis desired interval in millisecond, inexact
    299      * @throws InvalidArgumentException if the interval is less than zero
    300      * @return the same object, so that setters can be chained
    301      */
    302     public LocationRequest setInterval(long millis) {
    303         checkInterval(millis);
    304         mInterval = millis;
    305         if (!mExplicitFastestInterval) {
    306             mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR);
    307         }
    308         return this;
    309     }
    310 
    311     /**
    312      * Get the desired interval of this request, in milliseconds.
    313      *
    314      * @return desired interval in milliseconds, inexact
    315      */
    316     public long getInterval() {
    317         return mInterval;
    318     }
    319 
    320     /**
    321      * Explicitly set the fastest interval for location updates, in
    322      * milliseconds.
    323      *
    324      * <p>This controls the fastest rate at which your application will
    325      * receive location updates, which might be faster than
    326      * {@link #setInterval} in some situations (for example, if other
    327      * applications are triggering location updates).
    328      *
    329      * <p>This allows your application to passively acquire locations
    330      * at a rate faster than it actively acquires locations, saving power.
    331      *
    332      * <p>Unlike {@link #setInterval}, this parameter is exact. Your
    333      * application will never receive updates faster than this value.
    334      *
    335      * <p>If you don't call this method, a fastest interval
    336      * will be selected for you. It will be a value faster than your
    337      * active interval ({@link #setInterval}).
    338      *
    339      * <p>An interval of 0 is allowed, but not recommended, since
    340      * location updates may be extremely fast on future implementations.
    341      *
    342      * <p>If {@link #setFastestInterval} is set slower than {@link #setInterval},
    343      * then your effective fastest interval is {@link #setInterval}.
    344      *
    345      * @param millis fastest interval for updates in milliseconds, exact
    346      * @throws InvalidArgumentException if the interval is less than zero
    347      * @return the same object, so that setters can be chained
    348      */
    349     public LocationRequest setFastestInterval(long millis) {
    350         checkInterval(millis);
    351         mExplicitFastestInterval = true;
    352         mFastestInterval = millis;
    353         return this;
    354     }
    355 
    356     /**
    357      * Get the fastest interval of this request, in milliseconds.
    358      *
    359      * <p>The system will never provide location updates faster
    360      * than the minimum of {@link #getFastestInterval} and
    361      * {@link #getInterval}.
    362      *
    363      * @return fastest interval in milliseconds, exact
    364      */
    365     public long getFastestInterval() {
    366         return mFastestInterval;
    367     }
    368 
    369     /**
    370      * Set the duration of this request, in milliseconds.
    371      *
    372      * <p>The duration begins immediately (and not when the request
    373      * is passed to the location manager), so call this method again
    374      * if the request is re-used at a later time.
    375      *
    376      * <p>The location manager will automatically stop updates after
    377      * the request expires.
    378      *
    379      * <p>The duration includes suspend time. Values less than 0
    380      * are allowed, but indicate that the request has already expired.
    381      *
    382      * @param millis duration of request in milliseconds
    383      * @return the same object, so that setters can be chained
    384      */
    385     public LocationRequest setExpireIn(long millis) {
    386         long elapsedRealtime = SystemClock.elapsedRealtime();
    387 
    388         // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
    389         if (millis > Long.MAX_VALUE - elapsedRealtime) {
    390           mExpireAt = Long.MAX_VALUE;
    391         } else {
    392           mExpireAt = millis + elapsedRealtime;
    393         }
    394 
    395         if (mExpireAt < 0) mExpireAt = 0;
    396         return this;
    397     }
    398 
    399     /**
    400      * Set the request expiration time, in millisecond since boot.
    401      *
    402      * <p>This expiration time uses the same time base as {@link SystemClock#elapsedRealtime}.
    403      *
    404      * <p>The location manager will automatically stop updates after
    405      * the request expires.
    406      *
    407      * <p>The duration includes suspend time. Values before {@link SystemClock#elapsedRealtime}
    408      * are allowed,  but indicate that the request has already expired.
    409      *
    410      * @param millis expiration time of request, in milliseconds since boot including suspend
    411      * @return the same object, so that setters can be chained
    412      */
    413     public LocationRequest setExpireAt(long millis) {
    414         mExpireAt = millis;
    415         if (mExpireAt < 0) mExpireAt = 0;
    416         return this;
    417     }
    418 
    419     /**
    420      * Get the request expiration time, in milliseconds since boot.
    421      *
    422      * <p>This value can be compared to {@link SystemClock#elapsedRealtime} to determine
    423      * the time until expiration.
    424      *
    425      * @return expiration time of request, in milliseconds since boot including suspend
    426      */
    427     public long getExpireAt() {
    428         return mExpireAt;
    429     }
    430 
    431     /**
    432      * Set the number of location updates.
    433      *
    434      * <p>By default locations are continuously updated until the request is explicitly
    435      * removed, however you can optionally request a set number of updates.
    436      * For example, if your application only needs a single fresh location,
    437      * then call this method with a value of 1 before passing the request
    438      * to the location manager.
    439      *
    440      * @param numUpdates the number of location updates requested
    441      * @throws InvalidArgumentException if numUpdates is 0 or less
    442      * @return the same object, so that setters can be chained
    443      */
    444     public LocationRequest setNumUpdates(int numUpdates) {
    445         if (numUpdates <= 0) throw new IllegalArgumentException("invalid numUpdates: " + numUpdates);
    446         mNumUpdates = numUpdates;
    447         return this;
    448     }
    449 
    450     /**
    451      * Get the number of updates requested.
    452      *
    453      * <p>By default this is {@link Integer#MAX_VALUE}, which indicates that
    454      * locations are updated until the request is explicitly removed.
    455      * @return number of updates
    456      */
    457     public int getNumUpdates() {
    458         return mNumUpdates;
    459     }
    460 
    461     /** @hide */
    462     public void decrementNumUpdates() {
    463         if (mNumUpdates != Integer.MAX_VALUE) {
    464             mNumUpdates--;
    465         }
    466         if (mNumUpdates < 0) {
    467             mNumUpdates = 0;
    468         }
    469     }
    470 
    471 
    472     /** @hide */
    473     public LocationRequest setProvider(String provider) {
    474         checkProvider(provider);
    475         mProvider = provider;
    476         return this;
    477     }
    478 
    479     /** @hide */
    480     public String getProvider() {
    481         return mProvider;
    482     }
    483 
    484     /** @hide */
    485     public LocationRequest setSmallestDisplacement(float meters) {
    486         checkDisplacement(meters);
    487         mSmallestDisplacement = meters;
    488         return this;
    489     }
    490 
    491     /** @hide */
    492     public float getSmallestDisplacement() {
    493         return mSmallestDisplacement;
    494     }
    495 
    496     private static void checkInterval(long millis) {
    497         if (millis < 0) {
    498             throw new IllegalArgumentException("invalid interval: " + millis);
    499         }
    500     }
    501 
    502     private static void checkQuality(int quality) {
    503         switch (quality) {
    504             case ACCURACY_FINE:
    505             case ACCURACY_BLOCK:
    506             case ACCURACY_CITY:
    507             case POWER_NONE:
    508             case POWER_LOW:
    509             case POWER_HIGH:
    510                 break;
    511             default:
    512                 throw new IllegalArgumentException("invalid quality: " + quality);
    513         }
    514     }
    515 
    516     private static void checkDisplacement(float meters) {
    517         if (meters < 0.0f) {
    518             throw new IllegalArgumentException("invalid displacement: " + meters);
    519         }
    520     }
    521 
    522     private static void checkProvider(String name) {
    523         if (name == null) {
    524             throw new IllegalArgumentException("invalid provider: " + name);
    525         }
    526     }
    527 
    528     public static final Parcelable.Creator<LocationRequest> CREATOR =
    529             new Parcelable.Creator<LocationRequest>() {
    530         @Override
    531         public LocationRequest createFromParcel(Parcel in) {
    532             LocationRequest request = new LocationRequest();
    533             request.setQuality(in.readInt());
    534             request.setFastestInterval(in.readLong());
    535             request.setInterval(in.readLong());
    536             request.setExpireAt(in.readLong());
    537             request.setNumUpdates(in.readInt());
    538             request.setSmallestDisplacement(in.readFloat());
    539             String provider = in.readString();
    540             if (provider != null) request.setProvider(provider);
    541             return request;
    542         }
    543         @Override
    544         public LocationRequest[] newArray(int size) {
    545             return new LocationRequest[size];
    546         }
    547     };
    548 
    549     @Override
    550     public int describeContents() {
    551         return 0;
    552     }
    553 
    554     @Override
    555     public void writeToParcel(Parcel parcel, int flags) {
    556         parcel.writeInt(mQuality);
    557         parcel.writeLong(mFastestInterval);
    558         parcel.writeLong(mInterval);
    559         parcel.writeLong(mExpireAt);
    560         parcel.writeInt(mNumUpdates);
    561         parcel.writeFloat(mSmallestDisplacement);
    562         parcel.writeString(mProvider);
    563     }
    564 
    565     /** @hide */
    566     public static String qualityToString(int quality) {
    567         switch (quality) {
    568             case ACCURACY_FINE:
    569                 return "ACCURACY_FINE";
    570             case ACCURACY_BLOCK:
    571                 return "ACCURACY_BLOCK";
    572             case ACCURACY_CITY:
    573                 return "ACCURACY_CITY";
    574             case POWER_NONE:
    575                 return "POWER_NONE";
    576             case POWER_LOW:
    577                 return "POWER_LOW";
    578             case POWER_HIGH:
    579                 return "POWER_HIGH";
    580             default:
    581                 return "???";
    582         }
    583     }
    584 
    585     @Override
    586     public String toString() {
    587         StringBuilder s = new StringBuilder();
    588         s.append("Request[").append(qualityToString(mQuality));
    589         if (mProvider != null) s.append(' ').append(mProvider);
    590         if (mQuality != POWER_NONE) {
    591             s.append(" requested=");
    592             TimeUtils.formatDuration(mInterval, s);
    593         }
    594         s.append(" fastest=");
    595         TimeUtils.formatDuration(mFastestInterval, s);
    596         if (mExpireAt != Long.MAX_VALUE) {
    597             long expireIn = mExpireAt - SystemClock.elapsedRealtime();
    598             s.append(" expireIn=");
    599             TimeUtils.formatDuration(expireIn, s);
    600         }
    601         if (mNumUpdates != Integer.MAX_VALUE){
    602             s.append(" num=").append(mNumUpdates);
    603         }
    604         s.append(']');
    605         return s.toString();
    606     }
    607 }
    608