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