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 *
84 * @hide
85 */
86public 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