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