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