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                         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 */
LocationRequest()232     public LocationRequest() { }
233 
234     /** @hide */
LocationRequest(LocationRequest src)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      */
setQuality(int quality)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      */
getQuality()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      */
setInterval(long millis)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      */
getInterval()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      */
setFastestInterval(long millis)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      */
getFastestInterval()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      */
setExpireIn(long millis)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      */
setExpireAt(long millis)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      */
getExpireAt()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      */
setNumUpdates(int numUpdates)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      */
getNumUpdates()467     public int getNumUpdates() {
468         return mNumUpdates;
469     }
470 
471     /** @hide */
decrementNumUpdates()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
setProvider(String provider)484     public LocationRequest setProvider(String provider) {
485         checkProvider(provider);
486         mProvider = provider;
487         return this;
488     }
489 
490     /** @hide */
491     @SystemApi
getProvider()492     public String getProvider() {
493         return mProvider;
494     }
495 
496     /** @hide */
497     @SystemApi
setSmallestDisplacement(float meters)498     public LocationRequest setSmallestDisplacement(float meters) {
499         checkDisplacement(meters);
500         mSmallestDisplacement = meters;
501         return this;
502     }
503 
504     /** @hide */
505     @SystemApi
getSmallestDisplacement()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
setWorkSource(WorkSource workSource)521     public void setWorkSource(WorkSource workSource) {
522         mWorkSource = workSource;
523     }
524 
525     /** @hide */
526     @SystemApi
getWorkSource()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
setHideFromAppOps(boolean hideFromAppOps)546     public void setHideFromAppOps(boolean hideFromAppOps) {
547         mHideFromAppOps = hideFromAppOps;
548     }
549 
550     /** @hide */
551     @SystemApi
getHideFromAppOps()552     public boolean getHideFromAppOps() {
553         return mHideFromAppOps;
554     }
555 
checkInterval(long millis)556     private static void checkInterval(long millis) {
557         if (millis < 0) {
558             throw new IllegalArgumentException("invalid interval: " + millis);
559         }
560     }
561 
checkQuality(int quality)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 
checkDisplacement(float meters)576     private static void checkDisplacement(float meters) {
577         if (meters < 0.0f) {
578             throw new IllegalArgumentException("invalid displacement: " + meters);
579         }
580     }
581 
checkProvider(String name)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
describeContents()613     public int describeContents() {
614         return 0;
615     }
616 
617     @Override
writeToParcel(Parcel parcel, int flags)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 */
qualityToString(int quality)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
toString()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