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