1 /* 2 * Copyright (C) 2021 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 com.android.server.location.eventlog; 18 19 import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF; 20 import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY; 21 import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF; 22 import static android.os.PowerManager.LOCATION_MODE_NO_CHANGE; 23 import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; 24 import static android.util.TimeUtils.formatDuration; 25 26 import static com.android.server.location.LocationManagerService.D; 27 28 import static java.lang.Math.max; 29 import static java.lang.Math.min; 30 import static java.util.concurrent.TimeUnit.MILLISECONDS; 31 32 import android.annotation.Nullable; 33 import android.location.LocationRequest; 34 import android.location.provider.ProviderRequest; 35 import android.location.util.identity.CallerIdentity; 36 import android.os.PowerManager.LocationPowerSaveMode; 37 import android.os.SystemClock; 38 import android.util.ArrayMap; 39 import android.util.TimeUtils; 40 41 import com.android.internal.annotations.GuardedBy; 42 import com.android.internal.util.Preconditions; 43 44 import java.util.function.Consumer; 45 46 /** In memory event log for location events. */ 47 public class LocationEventLog extends LocalEventLog<Object> { 48 49 public static final LocationEventLog EVENT_LOG = new LocationEventLog(); 50 getLogSize()51 private static int getLogSize() { 52 if (D) { 53 return 600; 54 } else { 55 return 300; 56 } 57 } 58 getLocationsLogSize()59 private static int getLocationsLogSize() { 60 if (D) { 61 return 200; 62 } else { 63 return 100; 64 } 65 } 66 67 @GuardedBy("mAggregateStats") 68 private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats; 69 70 @GuardedBy("this") 71 private final LocationsEventLog mLocationsLog; 72 LocationEventLog()73 private LocationEventLog() { 74 super(getLogSize(), Object.class); 75 mAggregateStats = new ArrayMap<>(4); 76 mLocationsLog = new LocationsEventLog(getLocationsLogSize()); 77 } 78 79 /** Copies out all aggregated stats. */ copyAggregateStats()80 public ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copyAggregateStats() { 81 synchronized (mAggregateStats) { 82 ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copy = new ArrayMap<>( 83 mAggregateStats); 84 for (int i = 0; i < copy.size(); i++) { 85 copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i))); 86 } 87 return copy; 88 } 89 } 90 getAggregateStats(String provider, CallerIdentity identity)91 private AggregateStats getAggregateStats(String provider, CallerIdentity identity) { 92 synchronized (mAggregateStats) { 93 ArrayMap<CallerIdentity, AggregateStats> packageMap = mAggregateStats.get(provider); 94 if (packageMap == null) { 95 packageMap = new ArrayMap<>(2); 96 mAggregateStats.put(provider, packageMap); 97 } 98 CallerIdentity aggregate = CallerIdentity.forAggregation(identity); 99 AggregateStats stats = packageMap.get(aggregate); 100 if (stats == null) { 101 stats = new AggregateStats(); 102 packageMap.put(aggregate, stats); 103 } 104 return stats; 105 } 106 } 107 108 /** Logs a user switched event. */ logUserSwitched(int userIdFrom, int userIdTo)109 public void logUserSwitched(int userIdFrom, int userIdTo) { 110 addLog(new UserSwitchedEvent(userIdFrom, userIdTo)); 111 } 112 113 /** Logs a user visibility changed event. */ logUserVisibilityChanged(int userId, boolean visible)114 public void logUserVisibilityChanged(int userId, boolean visible) { 115 addLog(new UserVisibilityChangedEvent(userId, visible)); 116 } 117 118 /** Logs a location enabled/disabled event. */ logLocationEnabled(int userId, boolean enabled)119 public void logLocationEnabled(int userId, boolean enabled) { 120 addLog(new LocationEnabledEvent(userId, enabled)); 121 } 122 123 /** Logs a location enabled/disabled event. */ logAdasLocationEnabled(int userId, boolean enabled)124 public void logAdasLocationEnabled(int userId, boolean enabled) { 125 addLog(new LocationAdasEnabledEvent(userId, enabled)); 126 } 127 128 /** Logs a location provider enabled/disabled event. */ logProviderEnabled(String provider, int userId, boolean enabled)129 public void logProviderEnabled(String provider, int userId, boolean enabled) { 130 addLog(new ProviderEnabledEvent(provider, userId, enabled)); 131 } 132 133 /** Logs a location provider being replaced/unreplaced by a mock provider. */ logProviderMocked(String provider, boolean mocked)134 public void logProviderMocked(String provider, boolean mocked) { 135 addLog(new ProviderMockedEvent(provider, mocked)); 136 } 137 138 /** Logs a new client registration for a location provider. */ logProviderClientRegistered(String provider, CallerIdentity identity, LocationRequest request)139 public void logProviderClientRegistered(String provider, CallerIdentity identity, 140 LocationRequest request) { 141 addLog(new ProviderClientRegisterEvent(provider, true, identity, request)); 142 getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis()); 143 } 144 145 /** Logs a client unregistration for a location provider. */ logProviderClientUnregistered(String provider, CallerIdentity identity)146 public void logProviderClientUnregistered(String provider, CallerIdentity identity) { 147 addLog(new ProviderClientRegisterEvent(provider, false, identity, null)); 148 getAggregateStats(provider, identity).markRequestRemoved(); 149 } 150 151 /** Logs a client for a location provider entering the active state. */ logProviderClientActive(String provider, CallerIdentity identity)152 public void logProviderClientActive(String provider, CallerIdentity identity) { 153 getAggregateStats(provider, identity).markRequestActive(); 154 } 155 156 /** Logs a client for a location provider leaving the active state. */ logProviderClientInactive(String provider, CallerIdentity identity)157 public void logProviderClientInactive(String provider, CallerIdentity identity) { 158 getAggregateStats(provider, identity).markRequestInactive(); 159 } 160 161 /** Logs a client for a location provider entering the foreground state. */ logProviderClientForeground(String provider, CallerIdentity identity)162 public void logProviderClientForeground(String provider, CallerIdentity identity) { 163 if (D) { 164 addLog(new ProviderClientForegroundEvent(provider, true, identity)); 165 } 166 getAggregateStats(provider, identity).markRequestForeground(); 167 } 168 169 /** Logs a client for a location provider leaving the foreground state. */ logProviderClientBackground(String provider, CallerIdentity identity)170 public void logProviderClientBackground(String provider, CallerIdentity identity) { 171 if (D) { 172 addLog(new ProviderClientForegroundEvent(provider, false, identity)); 173 } 174 getAggregateStats(provider, identity).markRequestBackground(); 175 } 176 177 /** Logs a client for a location provider entering the permitted state. */ logProviderClientPermitted(String provider, CallerIdentity identity)178 public void logProviderClientPermitted(String provider, CallerIdentity identity) { 179 if (D) { 180 addLog(new ProviderClientPermittedEvent(provider, true, identity)); 181 } 182 } 183 184 /** Logs a client for a location provider leaving the permitted state. */ logProviderClientUnpermitted(String provider, CallerIdentity identity)185 public void logProviderClientUnpermitted(String provider, CallerIdentity identity) { 186 if (D) { 187 addLog(new ProviderClientPermittedEvent(provider, false, identity)); 188 } 189 } 190 191 /** Logs a change to the provider request for a location provider. */ logProviderUpdateRequest(String provider, ProviderRequest request)192 public void logProviderUpdateRequest(String provider, ProviderRequest request) { 193 addLog(new ProviderUpdateEvent(provider, request)); 194 } 195 196 /** Logs a new incoming location for a location provider. */ logProviderReceivedLocations(String provider, int numLocations)197 public void logProviderReceivedLocations(String provider, int numLocations) { 198 synchronized (this) { 199 mLocationsLog.logProviderReceivedLocations(provider, numLocations); 200 } 201 } 202 203 /** Logs a location deliver for a client of a location provider. */ logProviderDeliveredLocations(String provider, int numLocations, CallerIdentity identity)204 public void logProviderDeliveredLocations(String provider, int numLocations, 205 CallerIdentity identity) { 206 synchronized (this) { 207 mLocationsLog.logProviderDeliveredLocations(provider, numLocations, identity); 208 } 209 getAggregateStats(provider, identity).markLocationDelivered(); 210 } 211 212 /** Logs that a provider has entered or exited stationary throttling. */ logProviderStationaryThrottled(String provider, boolean throttled, ProviderRequest request)213 public void logProviderStationaryThrottled(String provider, boolean throttled, 214 ProviderRequest request) { 215 addLog(new ProviderStationaryThrottledEvent(provider, throttled, request)); 216 } 217 218 /** Logs that the location power save mode has changed. */ logLocationPowerSaveMode( @ocationPowerSaveMode int locationPowerSaveMode)219 public void logLocationPowerSaveMode( 220 @LocationPowerSaveMode int locationPowerSaveMode) { 221 addLog(new LocationPowerSaveModeEvent(locationPowerSaveMode)); 222 } 223 addLog(Object logEvent)224 private void addLog(Object logEvent) { 225 addLog(SystemClock.elapsedRealtime(), logEvent); 226 } 227 228 @Override iterate(LogConsumer<? super Object> consumer)229 public synchronized void iterate(LogConsumer<? super Object> consumer) { 230 iterate(consumer, this, mLocationsLog); 231 } 232 iterate(Consumer<String> consumer)233 public void iterate(Consumer<String> consumer) { 234 iterate(consumer, null); 235 } 236 iterate(Consumer<String> consumer, @Nullable String providerFilter)237 public void iterate(Consumer<String> consumer, @Nullable String providerFilter) { 238 long systemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime(); 239 StringBuilder builder = new StringBuilder(); 240 iterate( 241 (time, logEvent) -> { 242 boolean match = providerFilter == null || (logEvent instanceof ProviderEvent 243 && providerFilter.equals(((ProviderEvent) logEvent).mProvider)); 244 if (match) { 245 builder.setLength(0); 246 builder.append(TimeUtils.logTimeOfDay(time + systemTimeDeltaMs)); 247 builder.append(": "); 248 builder.append(logEvent); 249 consumer.accept(builder.toString()); 250 } 251 }); 252 } 253 254 private abstract static class ProviderEvent { 255 256 protected final String mProvider; 257 ProviderEvent(String provider)258 ProviderEvent(String provider) { 259 mProvider = provider; 260 } 261 } 262 263 private static final class ProviderEnabledEvent extends ProviderEvent { 264 265 private final int mUserId; 266 private final boolean mEnabled; 267 ProviderEnabledEvent(String provider, int userId, boolean enabled)268 ProviderEnabledEvent(String provider, int userId, 269 boolean enabled) { 270 super(provider); 271 mUserId = userId; 272 mEnabled = enabled; 273 } 274 275 @Override toString()276 public String toString() { 277 return mProvider + " provider [u" + mUserId + "] " + (mEnabled ? "enabled" 278 : "disabled"); 279 } 280 } 281 282 private static final class ProviderMockedEvent extends ProviderEvent { 283 284 private final boolean mMocked; 285 ProviderMockedEvent(String provider, boolean mocked)286 ProviderMockedEvent(String provider, boolean mocked) { 287 super(provider); 288 mMocked = mocked; 289 } 290 291 @Override toString()292 public String toString() { 293 if (mMocked) { 294 return mProvider + " provider added mock provider override"; 295 } else { 296 return mProvider + " provider removed mock provider override"; 297 } 298 } 299 } 300 301 private static final class ProviderClientRegisterEvent extends ProviderEvent { 302 303 private final boolean mRegistered; 304 private final CallerIdentity mIdentity; 305 @Nullable private final LocationRequest mLocationRequest; 306 ProviderClientRegisterEvent(String provider, boolean registered, CallerIdentity identity, @Nullable LocationRequest locationRequest)307 ProviderClientRegisterEvent(String provider, boolean registered, 308 CallerIdentity identity, @Nullable LocationRequest locationRequest) { 309 super(provider); 310 mRegistered = registered; 311 mIdentity = identity; 312 mLocationRequest = locationRequest; 313 } 314 315 @Override toString()316 public String toString() { 317 if (mRegistered) { 318 return mProvider + " provider +registration " + mIdentity + " -> " 319 + mLocationRequest; 320 } else { 321 return mProvider + " provider -registration " + mIdentity; 322 } 323 } 324 } 325 326 private static final class ProviderClientForegroundEvent extends ProviderEvent { 327 328 private final boolean mForeground; 329 private final CallerIdentity mIdentity; 330 ProviderClientForegroundEvent(String provider, boolean foreground, CallerIdentity identity)331 ProviderClientForegroundEvent(String provider, boolean foreground, 332 CallerIdentity identity) { 333 super(provider); 334 mForeground = foreground; 335 mIdentity = identity; 336 } 337 338 @Override toString()339 public String toString() { 340 return mProvider + " provider client " + mIdentity + " -> " 341 + (mForeground ? "foreground" : "background"); 342 } 343 } 344 345 private static final class ProviderClientPermittedEvent extends ProviderEvent { 346 347 private final boolean mPermitted; 348 private final CallerIdentity mIdentity; 349 ProviderClientPermittedEvent(String provider, boolean permitted, CallerIdentity identity)350 ProviderClientPermittedEvent(String provider, boolean permitted, CallerIdentity identity) { 351 super(provider); 352 mPermitted = permitted; 353 mIdentity = identity; 354 } 355 356 @Override toString()357 public String toString() { 358 return mProvider + " provider client " + mIdentity + " -> " 359 + (mPermitted ? "permitted" : "unpermitted"); 360 } 361 } 362 363 private static final class ProviderUpdateEvent extends ProviderEvent { 364 365 private final ProviderRequest mRequest; 366 ProviderUpdateEvent(String provider, ProviderRequest request)367 ProviderUpdateEvent(String provider, ProviderRequest request) { 368 super(provider); 369 mRequest = request; 370 } 371 372 @Override toString()373 public String toString() { 374 return mProvider + " provider request = " + mRequest; 375 } 376 } 377 378 private static final class ProviderReceiveLocationEvent extends ProviderEvent { 379 380 private final int mNumLocations; 381 ProviderReceiveLocationEvent(String provider, int numLocations)382 ProviderReceiveLocationEvent(String provider, int numLocations) { 383 super(provider); 384 mNumLocations = numLocations; 385 } 386 387 @Override toString()388 public String toString() { 389 return mProvider + " provider received location[" + mNumLocations + "]"; 390 } 391 } 392 393 private static final class ProviderDeliverLocationEvent extends ProviderEvent { 394 395 private final int mNumLocations; 396 @Nullable private final CallerIdentity mIdentity; 397 ProviderDeliverLocationEvent(String provider, int numLocations, @Nullable CallerIdentity identity)398 ProviderDeliverLocationEvent(String provider, int numLocations, 399 @Nullable CallerIdentity identity) { 400 super(provider); 401 mNumLocations = numLocations; 402 mIdentity = identity; 403 } 404 405 @Override toString()406 public String toString() { 407 return mProvider + " provider delivered location[" + mNumLocations + "] to " 408 + mIdentity; 409 } 410 } 411 412 private static final class ProviderStationaryThrottledEvent extends ProviderEvent { 413 414 private final boolean mStationaryThrottled; 415 private final ProviderRequest mRequest; 416 ProviderStationaryThrottledEvent(String provider, boolean stationaryThrottled, ProviderRequest request)417 ProviderStationaryThrottledEvent(String provider, boolean stationaryThrottled, 418 ProviderRequest request) { 419 super(provider); 420 mStationaryThrottled = stationaryThrottled; 421 mRequest = request; 422 } 423 424 @Override toString()425 public String toString() { 426 return mProvider + " provider stationary/idle " + (mStationaryThrottled ? "throttled" 427 : "unthrottled") + ", request = " + mRequest; 428 } 429 } 430 431 private static final class LocationPowerSaveModeEvent { 432 433 @LocationPowerSaveMode 434 private final int mLocationPowerSaveMode; 435 LocationPowerSaveModeEvent(@ocationPowerSaveMode int locationPowerSaveMode)436 LocationPowerSaveModeEvent(@LocationPowerSaveMode int locationPowerSaveMode) { 437 mLocationPowerSaveMode = locationPowerSaveMode; 438 } 439 440 @Override toString()441 public String toString() { 442 String mode; 443 switch (mLocationPowerSaveMode) { 444 case LOCATION_MODE_NO_CHANGE: 445 mode = "NO_CHANGE"; 446 break; 447 case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF: 448 mode = "GPS_DISABLED_WHEN_SCREEN_OFF"; 449 break; 450 case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF: 451 mode = "ALL_DISABLED_WHEN_SCREEN_OFF"; 452 break; 453 case LOCATION_MODE_FOREGROUND_ONLY: 454 mode = "FOREGROUND_ONLY"; 455 break; 456 case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF: 457 mode = "THROTTLE_REQUESTS_WHEN_SCREEN_OFF"; 458 break; 459 default: 460 mode = "UNKNOWN"; 461 break; 462 } 463 return "location power save mode changed to " + mode; 464 } 465 } 466 467 private static final class UserSwitchedEvent { 468 469 private final int mUserIdFrom; 470 private final int mUserIdTo; 471 UserSwitchedEvent(int userIdFrom, int userIdTo)472 UserSwitchedEvent(int userIdFrom, int userIdTo) { 473 mUserIdFrom = userIdFrom; 474 mUserIdTo = userIdTo; 475 } 476 477 @Override toString()478 public String toString() { 479 return "current user switched from u" + mUserIdFrom + " to u" + mUserIdTo; 480 } 481 } 482 483 private static final class UserVisibilityChangedEvent { 484 485 private final int mUserId; 486 private final boolean mVisible; 487 UserVisibilityChangedEvent(int userId, boolean visible)488 UserVisibilityChangedEvent(int userId, boolean visible) { 489 mUserId = userId; 490 mVisible = visible; 491 } 492 493 @Override toString()494 public String toString() { 495 return "[u" + mUserId + "] " + (mVisible ? "visible" : "invisible"); 496 } 497 } 498 499 private static final class LocationEnabledEvent { 500 501 private final int mUserId; 502 private final boolean mEnabled; 503 LocationEnabledEvent(int userId, boolean enabled)504 LocationEnabledEvent(int userId, boolean enabled) { 505 mUserId = userId; 506 mEnabled = enabled; 507 } 508 509 @Override toString()510 public String toString() { 511 return "location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled"); 512 } 513 } 514 515 private static final class LocationAdasEnabledEvent { 516 517 private final int mUserId; 518 private final boolean mEnabled; 519 LocationAdasEnabledEvent(int userId, boolean enabled)520 LocationAdasEnabledEvent(int userId, boolean enabled) { 521 mUserId = userId; 522 mEnabled = enabled; 523 } 524 525 @Override toString()526 public String toString() { 527 return "adas location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled"); 528 } 529 } 530 531 private static final class LocationsEventLog extends LocalEventLog<Object> { 532 LocationsEventLog(int size)533 LocationsEventLog(int size) { 534 super(size, Object.class); 535 } 536 logProviderReceivedLocations(String provider, int numLocations)537 public void logProviderReceivedLocations(String provider, int numLocations) { 538 addLog(new ProviderReceiveLocationEvent(provider, numLocations)); 539 } 540 logProviderDeliveredLocations(String provider, int numLocations, CallerIdentity identity)541 public void logProviderDeliveredLocations(String provider, int numLocations, 542 CallerIdentity identity) { 543 addLog(new ProviderDeliverLocationEvent(provider, numLocations, identity)); 544 } 545 addLog(Object logEvent)546 private void addLog(Object logEvent) { 547 this.addLog(SystemClock.elapsedRealtime(), logEvent); 548 } 549 } 550 551 /** 552 * Aggregate statistics for a single package under a single provider. 553 */ 554 public static final class AggregateStats { 555 556 @GuardedBy("this") 557 private int mAddedRequestCount; 558 @GuardedBy("this") 559 private int mActiveRequestCount; 560 @GuardedBy("this") 561 private int mForegroundRequestCount; 562 @GuardedBy("this") 563 private int mDeliveredLocationCount; 564 565 @GuardedBy("this") 566 private long mFastestIntervalMs = Long.MAX_VALUE; 567 @GuardedBy("this") 568 private long mSlowestIntervalMs = 0; 569 570 @GuardedBy("this") 571 private long mAddedTimeTotalMs; 572 @GuardedBy("this") 573 private long mAddedTimeLastUpdateRealtimeMs; 574 575 @GuardedBy("this") 576 private long mActiveTimeTotalMs; 577 @GuardedBy("this") 578 private long mActiveTimeLastUpdateRealtimeMs; 579 580 @GuardedBy("this") 581 private long mForegroundTimeTotalMs; 582 @GuardedBy("this") 583 private long mForegroundTimeLastUpdateRealtimeMs; 584 AggregateStats()585 AggregateStats() {} 586 markRequestAdded(long intervalMillis)587 synchronized void markRequestAdded(long intervalMillis) { 588 if (mAddedRequestCount++ == 0) { 589 mAddedTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime(); 590 } 591 592 mFastestIntervalMs = min(intervalMillis, mFastestIntervalMs); 593 mSlowestIntervalMs = max(intervalMillis, mSlowestIntervalMs); 594 } 595 markRequestRemoved()596 synchronized void markRequestRemoved() { 597 updateTotals(); 598 --mAddedRequestCount; 599 Preconditions.checkState(mAddedRequestCount >= 0); 600 601 mActiveRequestCount = min(mAddedRequestCount, mActiveRequestCount); 602 mForegroundRequestCount = min(mAddedRequestCount, mForegroundRequestCount); 603 } 604 markRequestActive()605 synchronized void markRequestActive() { 606 Preconditions.checkState(mAddedRequestCount > 0); 607 if (mActiveRequestCount++ == 0) { 608 mActiveTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime(); 609 } 610 } 611 markRequestInactive()612 synchronized void markRequestInactive() { 613 updateTotals(); 614 --mActiveRequestCount; 615 Preconditions.checkState(mActiveRequestCount >= 0); 616 } 617 markRequestForeground()618 synchronized void markRequestForeground() { 619 Preconditions.checkState(mAddedRequestCount > 0); 620 if (mForegroundRequestCount++ == 0) { 621 mForegroundTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime(); 622 } 623 } 624 markRequestBackground()625 synchronized void markRequestBackground() { 626 updateTotals(); 627 --mForegroundRequestCount; 628 Preconditions.checkState(mForegroundRequestCount >= 0); 629 } 630 markLocationDelivered()631 synchronized void markLocationDelivered() { 632 mDeliveredLocationCount++; 633 } 634 updateTotals()635 public synchronized void updateTotals() { 636 if (mAddedRequestCount > 0) { 637 long realtimeMs = SystemClock.elapsedRealtime(); 638 mAddedTimeTotalMs += realtimeMs - mAddedTimeLastUpdateRealtimeMs; 639 mAddedTimeLastUpdateRealtimeMs = realtimeMs; 640 } 641 if (mActiveRequestCount > 0) { 642 long realtimeMs = SystemClock.elapsedRealtime(); 643 mActiveTimeTotalMs += realtimeMs - mActiveTimeLastUpdateRealtimeMs; 644 mActiveTimeLastUpdateRealtimeMs = realtimeMs; 645 } 646 if (mForegroundRequestCount > 0) { 647 long realtimeMs = SystemClock.elapsedRealtime(); 648 mForegroundTimeTotalMs += realtimeMs - mForegroundTimeLastUpdateRealtimeMs; 649 mForegroundTimeLastUpdateRealtimeMs = realtimeMs; 650 } 651 } 652 653 @Override toString()654 public synchronized String toString() { 655 return "min/max interval = " + intervalToString(mFastestIntervalMs) + "/" 656 + intervalToString(mSlowestIntervalMs) 657 + ", total/active/foreground duration = " + formatDuration(mAddedTimeTotalMs) 658 + "/" + formatDuration(mActiveTimeTotalMs) + "/" 659 + formatDuration(mForegroundTimeTotalMs) + ", locations = " 660 + mDeliveredLocationCount; 661 } 662 intervalToString(long intervalMs)663 private static String intervalToString(long intervalMs) { 664 if (intervalMs == LocationRequest.PASSIVE_INTERVAL) { 665 return "passive"; 666 } else { 667 return MILLISECONDS.toSeconds(intervalMs) + "s"; 668 } 669 } 670 } 671 } 672