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