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