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