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