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.GnssMeasurementRequest;
34 import android.location.LocationRequest;
35 import android.location.provider.ProviderRequest;
36 import android.location.util.identity.CallerIdentity;
37 import android.os.PowerManager.LocationPowerSaveMode;
38 import android.os.SystemClock;
39 import android.util.ArrayMap;
40 import android.util.TimeUtils;
41 
42 import com.android.internal.annotations.GuardedBy;
43 import com.android.internal.util.Preconditions;
44 
45 import java.util.function.Consumer;
46 
47 /** In memory event log for location events. */
48 public class LocationEventLog extends LocalEventLog<Object> {
49 
50     public static final LocationEventLog EVENT_LOG = new LocationEventLog();
51 
getLogSize()52     private static int getLogSize() {
53         if (D) {
54             return 600;
55         } else {
56             return 300;
57         }
58     }
59 
getLocationsLogSize()60     private static int getLocationsLogSize() {
61         if (D) {
62             return 400;
63         } else {
64             return 200;
65         }
66     }
67 
68     @GuardedBy("mAggregateStats")
69     private final ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> mAggregateStats;
70 
71     @GuardedBy("mGnssMeasAggregateStats")
72     private final ArrayMap<CallerIdentity, GnssMeasurementAggregateStats> mGnssMeasAggregateStats;
73 
74     @GuardedBy("this")
75     private final LocationsEventLog mLocationsLog;
76 
LocationEventLog()77     private LocationEventLog() {
78         super(getLogSize(), Object.class);
79         mAggregateStats = new ArrayMap<>(4);
80         mGnssMeasAggregateStats = new ArrayMap<>();
81         mLocationsLog = new LocationsEventLog(getLocationsLogSize());
82     }
83 
84     /** Copies out all aggregated stats. */
copyAggregateStats()85     public ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copyAggregateStats() {
86         synchronized (mAggregateStats) {
87             ArrayMap<String, ArrayMap<CallerIdentity, AggregateStats>> copy = new ArrayMap<>(
88                     mAggregateStats);
89             for (int i = 0; i < copy.size(); i++) {
90                 copy.setValueAt(i, new ArrayMap<>(copy.valueAt(i)));
91             }
92             return copy;
93         }
94     }
95 
getAggregateStats(String provider, CallerIdentity identity)96     private AggregateStats getAggregateStats(String provider, CallerIdentity identity) {
97         synchronized (mAggregateStats) {
98             ArrayMap<CallerIdentity, AggregateStats> packageMap = mAggregateStats.get(provider);
99             if (packageMap == null) {
100                 packageMap = new ArrayMap<>(2);
101                 mAggregateStats.put(provider, packageMap);
102             }
103             CallerIdentity aggregate = CallerIdentity.forAggregation(identity);
104             AggregateStats stats = packageMap.get(aggregate);
105             if (stats == null) {
106                 stats = new AggregateStats();
107                 packageMap.put(aggregate, stats);
108             }
109             return stats;
110         }
111     }
112 
113     /** Copies out gnss measurement aggregated stats. */
114     public ArrayMap<CallerIdentity, GnssMeasurementAggregateStats>
copyGnssMeasurementAggregateStats()115             copyGnssMeasurementAggregateStats() {
116         synchronized (mGnssMeasAggregateStats) {
117             ArrayMap<CallerIdentity, GnssMeasurementAggregateStats> copy = new ArrayMap<>(
118                     mGnssMeasAggregateStats);
119             return copy;
120         }
121     }
122 
getGnssMeasurementAggregateStats( CallerIdentity identity)123     private GnssMeasurementAggregateStats getGnssMeasurementAggregateStats(
124             CallerIdentity identity) {
125         synchronized (mGnssMeasAggregateStats) {
126             CallerIdentity aggregate = CallerIdentity.forAggregation(identity);
127             GnssMeasurementAggregateStats stats = mGnssMeasAggregateStats.get(aggregate);
128             if (stats == null) {
129                 stats = new GnssMeasurementAggregateStats();
130                 mGnssMeasAggregateStats.put(aggregate, stats);
131             }
132             return stats;
133         }
134     }
135 
136     /** Logs a user switched event. */
logUserSwitched(int userIdFrom, int userIdTo)137     public void logUserSwitched(int userIdFrom, int userIdTo) {
138         addLog(new UserSwitchedEvent(userIdFrom, userIdTo));
139     }
140 
141     /** Logs a user visibility changed event. */
logUserVisibilityChanged(int userId, boolean visible)142     public void logUserVisibilityChanged(int userId, boolean visible) {
143         addLog(new UserVisibilityChangedEvent(userId, visible));
144     }
145 
146     /** Logs a location enabled/disabled event. */
logLocationEnabled(int userId, boolean enabled)147     public void logLocationEnabled(int userId, boolean enabled) {
148         addLog(new LocationEnabledEvent(userId, enabled));
149     }
150 
151     /** Logs a location enabled/disabled event. */
logAdasLocationEnabled(int userId, boolean enabled)152     public void logAdasLocationEnabled(int userId, boolean enabled) {
153         addLog(new LocationAdasEnabledEvent(userId, enabled));
154     }
155 
156     /** Logs a location provider enabled/disabled event. */
logProviderEnabled(String provider, int userId, boolean enabled)157     public void logProviderEnabled(String provider, int userId, boolean enabled) {
158         addLog(new ProviderEnabledEvent(provider, userId, enabled));
159     }
160 
161     /** Logs a location provider being replaced/unreplaced by a mock provider. */
logProviderMocked(String provider, boolean mocked)162     public void logProviderMocked(String provider, boolean mocked) {
163         addLog(new ProviderMockedEvent(provider, mocked));
164     }
165 
166     /** Logs a new client registration for a location provider. */
logProviderClientRegistered(String provider, CallerIdentity identity, LocationRequest request)167     public void logProviderClientRegistered(String provider, CallerIdentity identity,
168             LocationRequest request) {
169         addLog(new ProviderClientRegisterEvent(provider, true, identity, request));
170         getAggregateStats(provider, identity).markRequestAdded(request.getIntervalMillis());
171     }
172 
173     /** Logs a client unregistration for a location provider. */
logProviderClientUnregistered(String provider, CallerIdentity identity)174     public void logProviderClientUnregistered(String provider, CallerIdentity identity) {
175         addLog(new ProviderClientRegisterEvent(provider, false, identity, null));
176         getAggregateStats(provider, identity).markRequestRemoved();
177     }
178 
179     /** Logs a client for a location provider entering the active state. */
logProviderClientActive(String provider, CallerIdentity identity)180     public void logProviderClientActive(String provider, CallerIdentity identity) {
181         getAggregateStats(provider, identity).markRequestActive();
182     }
183 
184     /** Logs a client for a location provider leaving the active state. */
logProviderClientInactive(String provider, CallerIdentity identity)185     public void logProviderClientInactive(String provider, CallerIdentity identity) {
186         getAggregateStats(provider, identity).markRequestInactive();
187     }
188 
189     /** Logs a client for a location provider entering the foreground state. */
logProviderClientForeground(String provider, CallerIdentity identity)190     public void logProviderClientForeground(String provider, CallerIdentity identity) {
191         if (D) {
192             addLog(new ProviderClientForegroundEvent(provider, true, identity));
193         }
194         getAggregateStats(provider, identity).markRequestForeground();
195     }
196 
197     /** Logs a client for a location provider leaving the foreground state. */
logProviderClientBackground(String provider, CallerIdentity identity)198     public void logProviderClientBackground(String provider, CallerIdentity identity) {
199         if (D) {
200             addLog(new ProviderClientForegroundEvent(provider, false, identity));
201         }
202         getAggregateStats(provider, identity).markRequestBackground();
203     }
204 
205     /** Logs a client for a location provider entering the permitted state. */
logProviderClientPermitted(String provider, CallerIdentity identity)206     public void logProviderClientPermitted(String provider, CallerIdentity identity) {
207         if (D) {
208             addLog(new ProviderClientPermittedEvent(provider, true, identity));
209         }
210     }
211 
212     /** Logs a client for a location provider leaving the permitted state. */
logProviderClientUnpermitted(String provider, CallerIdentity identity)213     public void logProviderClientUnpermitted(String provider, CallerIdentity identity) {
214         if (D) {
215             addLog(new ProviderClientPermittedEvent(provider, false, identity));
216         }
217     }
218 
219     /** Logs a change to the provider request for a location provider. */
logProviderUpdateRequest(String provider, ProviderRequest request)220     public void logProviderUpdateRequest(String provider, ProviderRequest request) {
221         addLog(new ProviderUpdateEvent(provider, request));
222     }
223 
224     /** Logs a new incoming location for a location provider. */
logProviderReceivedLocations(String provider, int numLocations)225     public void logProviderReceivedLocations(String provider, int numLocations) {
226         synchronized (this) {
227             mLocationsLog.logProviderReceivedLocations(provider, numLocations);
228         }
229     }
230 
231     /** Logs a location deliver for a client of a location provider. */
logProviderDeliveredLocations(String provider, int numLocations, CallerIdentity identity)232     public void logProviderDeliveredLocations(String provider, int numLocations,
233             CallerIdentity identity) {
234         synchronized (this) {
235             mLocationsLog.logProviderDeliveredLocations(provider, numLocations, identity);
236         }
237         getAggregateStats(provider, identity).markLocationDelivered();
238     }
239 
240     /** Logs that a provider has entered or exited stationary throttling. */
logProviderStationaryThrottled(String provider, boolean throttled, ProviderRequest request)241     public void logProviderStationaryThrottled(String provider, boolean throttled,
242             ProviderRequest request) {
243         addLog(new ProviderStationaryThrottledEvent(provider, throttled, request));
244     }
245 
246     /** Logs that the location power save mode has changed. */
logLocationPowerSaveMode( @ocationPowerSaveMode int locationPowerSaveMode)247     public void logLocationPowerSaveMode(
248             @LocationPowerSaveMode int locationPowerSaveMode) {
249         addLog(new LocationPowerSaveModeEvent(locationPowerSaveMode));
250     }
251 
252     /** Logs a new client registration for a GNSS Measurement. */
logGnssMeasurementClientRegistered(CallerIdentity identity, GnssMeasurementRequest request)253     public void logGnssMeasurementClientRegistered(CallerIdentity identity,
254             GnssMeasurementRequest request) {
255         addLog(new GnssMeasurementClientRegisterEvent(true, identity, request));
256         getGnssMeasurementAggregateStats(identity).markRequestAdded(request.getIntervalMillis(),
257                 request.isFullTracking());
258     }
259 
260     /** Logs a new client unregistration for a GNSS Measurement. */
logGnssMeasurementClientUnregistered(CallerIdentity identity)261     public void logGnssMeasurementClientUnregistered(CallerIdentity identity) {
262         addLog(new GnssMeasurementClientRegisterEvent(false, identity, null));
263         getGnssMeasurementAggregateStats(identity).markRequestRemoved();
264     }
265 
266     /** Logs a GNSS measurement event deliver for a client. */
logGnssMeasurementsDelivered(int numGnssMeasurements, CallerIdentity identity)267     public void logGnssMeasurementsDelivered(int numGnssMeasurements,
268             CallerIdentity identity) {
269         synchronized (this) {
270             mLocationsLog.logDeliveredGnssMeasurements(numGnssMeasurements, identity);
271         }
272         getGnssMeasurementAggregateStats(identity).markGnssMeasurementDelivered();
273     }
274 
addLog(Object logEvent)275     private void addLog(Object logEvent) {
276         addLog(SystemClock.elapsedRealtime(), logEvent);
277     }
278 
279     @Override
iterate(LogConsumer<? super Object> consumer)280     public synchronized void iterate(LogConsumer<? super Object> consumer) {
281         iterate(consumer, this, mLocationsLog);
282     }
283 
iterate(Consumer<String> consumer)284     public void iterate(Consumer<String> consumer) {
285         iterate(consumer, null);
286     }
287 
iterate(Consumer<String> consumer, @Nullable String providerFilter)288     public void iterate(Consumer<String> consumer, @Nullable String providerFilter) {
289         long systemTimeDeltaMs = System.currentTimeMillis() - SystemClock.elapsedRealtime();
290         StringBuilder builder = new StringBuilder();
291         iterate(
292                 (time, logEvent) -> {
293                     boolean match = providerFilter == null || (logEvent instanceof ProviderEvent
294                             && providerFilter.equals(((ProviderEvent) logEvent).mProvider));
295                     if (match) {
296                         builder.setLength(0);
297                         builder.append(TimeUtils.logTimeOfDay(time + systemTimeDeltaMs));
298                         builder.append(": ");
299                         builder.append(logEvent);
300                         consumer.accept(builder.toString());
301                     }
302                 });
303     }
304 
305     private abstract static class ProviderEvent {
306 
307         protected final String mProvider;
308 
ProviderEvent(String provider)309         ProviderEvent(String provider) {
310             mProvider = provider;
311         }
312     }
313 
314     private static final class ProviderEnabledEvent extends ProviderEvent {
315 
316         private final int mUserId;
317         private final boolean mEnabled;
318 
ProviderEnabledEvent(String provider, int userId, boolean enabled)319         ProviderEnabledEvent(String provider, int userId,
320                 boolean enabled) {
321             super(provider);
322             mUserId = userId;
323             mEnabled = enabled;
324         }
325 
326         @Override
toString()327         public String toString() {
328             return mProvider + " provider [u" + mUserId + "] " + (mEnabled ? "enabled"
329                     : "disabled");
330         }
331     }
332 
333     private static final class ProviderMockedEvent extends ProviderEvent {
334 
335         private final boolean mMocked;
336 
ProviderMockedEvent(String provider, boolean mocked)337         ProviderMockedEvent(String provider, boolean mocked) {
338             super(provider);
339             mMocked = mocked;
340         }
341 
342         @Override
toString()343         public String toString() {
344             if (mMocked) {
345                 return mProvider + " provider added mock provider override";
346             } else {
347                 return mProvider + " provider removed mock provider override";
348             }
349         }
350     }
351 
352     private static final class ProviderClientRegisterEvent extends ProviderEvent {
353 
354         private final boolean mRegistered;
355         private final CallerIdentity mIdentity;
356         @Nullable private final LocationRequest mLocationRequest;
357 
ProviderClientRegisterEvent(String provider, boolean registered, CallerIdentity identity, @Nullable LocationRequest locationRequest)358         ProviderClientRegisterEvent(String provider, boolean registered,
359                 CallerIdentity identity, @Nullable LocationRequest locationRequest) {
360             super(provider);
361             mRegistered = registered;
362             mIdentity = identity;
363             mLocationRequest = locationRequest;
364         }
365 
366         @Override
toString()367         public String toString() {
368             if (mRegistered) {
369                 return mProvider + " provider +registration " + mIdentity + " -> "
370                         + mLocationRequest;
371             } else {
372                 return mProvider + " provider -registration " + mIdentity;
373             }
374         }
375     }
376 
377     private static final class ProviderClientForegroundEvent extends ProviderEvent {
378 
379         private final boolean mForeground;
380         private final CallerIdentity mIdentity;
381 
ProviderClientForegroundEvent(String provider, boolean foreground, CallerIdentity identity)382         ProviderClientForegroundEvent(String provider, boolean foreground,
383                 CallerIdentity identity) {
384             super(provider);
385             mForeground = foreground;
386             mIdentity = identity;
387         }
388 
389         @Override
toString()390         public String toString() {
391             return mProvider + " provider client " + mIdentity + " -> "
392                     + (mForeground ? "foreground" : "background");
393         }
394     }
395 
396     private static final class ProviderClientPermittedEvent extends ProviderEvent {
397 
398         private final boolean mPermitted;
399         private final CallerIdentity mIdentity;
400 
ProviderClientPermittedEvent(String provider, boolean permitted, CallerIdentity identity)401         ProviderClientPermittedEvent(String provider, boolean permitted, CallerIdentity identity) {
402             super(provider);
403             mPermitted = permitted;
404             mIdentity = identity;
405         }
406 
407         @Override
toString()408         public String toString() {
409             return mProvider + " provider client " + mIdentity + " -> "
410                     + (mPermitted ? "permitted" : "unpermitted");
411         }
412     }
413 
414     private static final class ProviderUpdateEvent extends ProviderEvent {
415 
416         private final ProviderRequest mRequest;
417 
ProviderUpdateEvent(String provider, ProviderRequest request)418         ProviderUpdateEvent(String provider, ProviderRequest request) {
419             super(provider);
420             mRequest = request;
421         }
422 
423         @Override
toString()424         public String toString() {
425             return mProvider + " provider request = " + mRequest;
426         }
427     }
428 
429     private static final class ProviderReceiveLocationEvent extends ProviderEvent {
430 
431         private final int mNumLocations;
432 
ProviderReceiveLocationEvent(String provider, int numLocations)433         ProviderReceiveLocationEvent(String provider, int numLocations) {
434             super(provider);
435             mNumLocations = numLocations;
436         }
437 
438         @Override
toString()439         public String toString() {
440             return mProvider + " provider received location[" + mNumLocations + "]";
441         }
442     }
443 
444     private static final class ProviderDeliverLocationEvent extends ProviderEvent {
445 
446         private final int mNumLocations;
447         @Nullable private final CallerIdentity mIdentity;
448 
ProviderDeliverLocationEvent(String provider, int numLocations, @Nullable CallerIdentity identity)449         ProviderDeliverLocationEvent(String provider, int numLocations,
450                 @Nullable CallerIdentity identity) {
451             super(provider);
452             mNumLocations = numLocations;
453             mIdentity = identity;
454         }
455 
456         @Override
toString()457         public String toString() {
458             return mProvider + " provider delivered location[" + mNumLocations + "] to "
459                     + mIdentity;
460         }
461     }
462 
463     private static final class ProviderStationaryThrottledEvent extends ProviderEvent {
464 
465         private final boolean mStationaryThrottled;
466         private final ProviderRequest mRequest;
467 
ProviderStationaryThrottledEvent(String provider, boolean stationaryThrottled, ProviderRequest request)468         ProviderStationaryThrottledEvent(String provider, boolean stationaryThrottled,
469                 ProviderRequest request) {
470             super(provider);
471             mStationaryThrottled = stationaryThrottled;
472             mRequest = request;
473         }
474 
475         @Override
toString()476         public String toString() {
477             return mProvider + " provider stationary/idle " + (mStationaryThrottled ? "throttled"
478                     : "unthrottled") + ", request = " + mRequest;
479         }
480     }
481 
482     private static final class LocationPowerSaveModeEvent {
483 
484         @LocationPowerSaveMode
485         private final int mLocationPowerSaveMode;
486 
LocationPowerSaveModeEvent(@ocationPowerSaveMode int locationPowerSaveMode)487         LocationPowerSaveModeEvent(@LocationPowerSaveMode int locationPowerSaveMode) {
488             mLocationPowerSaveMode = locationPowerSaveMode;
489         }
490 
491         @Override
toString()492         public String toString() {
493             String mode;
494             switch (mLocationPowerSaveMode) {
495                 case LOCATION_MODE_NO_CHANGE:
496                     mode = "NO_CHANGE";
497                     break;
498                 case LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF:
499                     mode = "GPS_DISABLED_WHEN_SCREEN_OFF";
500                     break;
501                 case LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF:
502                     mode = "ALL_DISABLED_WHEN_SCREEN_OFF";
503                     break;
504                 case LOCATION_MODE_FOREGROUND_ONLY:
505                     mode = "FOREGROUND_ONLY";
506                     break;
507                 case LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF:
508                     mode = "THROTTLE_REQUESTS_WHEN_SCREEN_OFF";
509                     break;
510                 default:
511                     mode = "UNKNOWN";
512                     break;
513             }
514             return "location power save mode changed to " + mode;
515         }
516     }
517 
518     private static final class UserSwitchedEvent {
519 
520         private final int mUserIdFrom;
521         private final int mUserIdTo;
522 
UserSwitchedEvent(int userIdFrom, int userIdTo)523         UserSwitchedEvent(int userIdFrom, int userIdTo) {
524             mUserIdFrom = userIdFrom;
525             mUserIdTo = userIdTo;
526         }
527 
528         @Override
toString()529         public String toString() {
530             return "current user switched from u" + mUserIdFrom + " to u" + mUserIdTo;
531         }
532     }
533 
534     private static final class UserVisibilityChangedEvent {
535 
536         private final int mUserId;
537         private final boolean mVisible;
538 
UserVisibilityChangedEvent(int userId, boolean visible)539         UserVisibilityChangedEvent(int userId, boolean visible) {
540             mUserId = userId;
541             mVisible = visible;
542         }
543 
544         @Override
toString()545         public String toString() {
546             return "[u" + mUserId + "] " + (mVisible ? "visible" : "invisible");
547         }
548     }
549 
550     private static final class LocationEnabledEvent {
551 
552         private final int mUserId;
553         private final boolean mEnabled;
554 
LocationEnabledEvent(int userId, boolean enabled)555         LocationEnabledEvent(int userId, boolean enabled) {
556             mUserId = userId;
557             mEnabled = enabled;
558         }
559 
560         @Override
toString()561         public String toString() {
562             return "location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
563         }
564     }
565 
566     private static final class LocationAdasEnabledEvent {
567 
568         private final int mUserId;
569         private final boolean mEnabled;
570 
LocationAdasEnabledEvent(int userId, boolean enabled)571         LocationAdasEnabledEvent(int userId, boolean enabled) {
572             mUserId = userId;
573             mEnabled = enabled;
574         }
575 
576         @Override
toString()577         public String toString() {
578             return "adas location [u" + mUserId + "] " + (mEnabled ? "enabled" : "disabled");
579         }
580     }
581 
582     private static final class GnssMeasurementClientRegisterEvent{
583 
584         private final boolean mRegistered;
585         private final CallerIdentity mIdentity;
586         @Nullable
587         private final GnssMeasurementRequest mGnssMeasurementRequest;
588 
GnssMeasurementClientRegisterEvent(boolean registered, CallerIdentity identity, @Nullable GnssMeasurementRequest measurementRequest)589         GnssMeasurementClientRegisterEvent(boolean registered,
590                 CallerIdentity identity, @Nullable GnssMeasurementRequest measurementRequest) {
591             mRegistered = registered;
592             mIdentity = identity;
593             mGnssMeasurementRequest = measurementRequest;
594         }
595 
596         @Override
toString()597         public String toString() {
598             if (mRegistered) {
599                 return "gnss measurements +registration " + mIdentity + " -> "
600                         + mGnssMeasurementRequest;
601             } else {
602                 return "gnss measurements -registration " + mIdentity;
603             }
604         }
605     }
606 
607     private static final class GnssMeasurementDeliverEvent {
608 
609         private final int mNumGnssMeasurements;
610         @Nullable
611         private final CallerIdentity mIdentity;
612 
GnssMeasurementDeliverEvent(int numGnssMeasurements, @Nullable CallerIdentity identity)613         GnssMeasurementDeliverEvent(int numGnssMeasurements,
614                 @Nullable CallerIdentity identity) {
615             mNumGnssMeasurements = numGnssMeasurements;
616             mIdentity = identity;
617         }
618 
619         @Override
toString()620         public String toString() {
621             return "gnss measurements delivered GnssMeasurements[" + mNumGnssMeasurements + "]"
622                     + " to " + mIdentity;
623         }
624     }
625 
626     private static final class LocationsEventLog extends LocalEventLog<Object> {
627 
LocationsEventLog(int size)628         LocationsEventLog(int size) {
629             super(size, Object.class);
630         }
631 
logProviderReceivedLocations(String provider, int numLocations)632         public void logProviderReceivedLocations(String provider, int numLocations) {
633             addLog(new ProviderReceiveLocationEvent(provider, numLocations));
634         }
635 
logDeliveredGnssMeasurements(int numGnssMeasurements, CallerIdentity identity)636         public void logDeliveredGnssMeasurements(int numGnssMeasurements,
637                 CallerIdentity identity) {
638             addLog(new GnssMeasurementDeliverEvent(numGnssMeasurements, identity));
639         }
640 
logProviderDeliveredLocations(String provider, int numLocations, CallerIdentity identity)641         public void logProviderDeliveredLocations(String provider, int numLocations,
642                 CallerIdentity identity) {
643             addLog(new ProviderDeliverLocationEvent(provider, numLocations, identity));
644         }
645 
addLog(Object logEvent)646         private void addLog(Object logEvent) {
647             this.addLog(SystemClock.elapsedRealtime(), logEvent);
648         }
649     }
650 
651     /**
652      * Aggregate statistics for a single package under a single provider.
653      */
654     public static final class AggregateStats {
655 
656         @GuardedBy("this")
657         private int mAddedRequestCount;
658         @GuardedBy("this")
659         private int mActiveRequestCount;
660         @GuardedBy("this")
661         private int mForegroundRequestCount;
662         @GuardedBy("this")
663         private int mDeliveredLocationCount;
664 
665         @GuardedBy("this")
666         private long mFastestIntervalMs = Long.MAX_VALUE;
667         @GuardedBy("this")
668         private long mSlowestIntervalMs = 0;
669 
670         @GuardedBy("this")
671         private long mAddedTimeTotalMs;
672         @GuardedBy("this")
673         private long mAddedTimeLastUpdateRealtimeMs;
674 
675         @GuardedBy("this")
676         private long mActiveTimeTotalMs;
677         @GuardedBy("this")
678         private long mActiveTimeLastUpdateRealtimeMs;
679 
680         @GuardedBy("this")
681         private long mForegroundTimeTotalMs;
682         @GuardedBy("this")
683         private long mForegroundTimeLastUpdateRealtimeMs;
684 
AggregateStats()685         AggregateStats() {}
686 
markRequestAdded(long intervalMillis)687         synchronized void markRequestAdded(long intervalMillis) {
688             if (mAddedRequestCount++ == 0) {
689                 mAddedTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
690             }
691 
692             mFastestIntervalMs = min(intervalMillis, mFastestIntervalMs);
693             mSlowestIntervalMs = max(intervalMillis, mSlowestIntervalMs);
694         }
695 
markRequestRemoved()696         synchronized void markRequestRemoved() {
697             updateTotals();
698             --mAddedRequestCount;
699             Preconditions.checkState(mAddedRequestCount >= 0);
700 
701             mActiveRequestCount = min(mAddedRequestCount, mActiveRequestCount);
702             mForegroundRequestCount = min(mAddedRequestCount, mForegroundRequestCount);
703         }
704 
markRequestActive()705         synchronized void markRequestActive() {
706             Preconditions.checkState(mAddedRequestCount > 0);
707             if (mActiveRequestCount++ == 0) {
708                 mActiveTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
709             }
710         }
711 
markRequestInactive()712         synchronized void markRequestInactive() {
713             updateTotals();
714             --mActiveRequestCount;
715             Preconditions.checkState(mActiveRequestCount >= 0);
716         }
717 
markRequestForeground()718         synchronized void markRequestForeground() {
719             Preconditions.checkState(mAddedRequestCount > 0);
720             if (mForegroundRequestCount++ == 0) {
721                 mForegroundTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
722             }
723         }
724 
markRequestBackground()725         synchronized void markRequestBackground() {
726             updateTotals();
727             --mForegroundRequestCount;
728             Preconditions.checkState(mForegroundRequestCount >= 0);
729         }
730 
markLocationDelivered()731         synchronized void markLocationDelivered() {
732             mDeliveredLocationCount++;
733         }
734 
updateTotals()735         public synchronized void updateTotals() {
736             if (mAddedRequestCount > 0) {
737                 long realtimeMs = SystemClock.elapsedRealtime();
738                 mAddedTimeTotalMs += realtimeMs - mAddedTimeLastUpdateRealtimeMs;
739                 mAddedTimeLastUpdateRealtimeMs = realtimeMs;
740             }
741             if (mActiveRequestCount > 0) {
742                 long realtimeMs = SystemClock.elapsedRealtime();
743                 mActiveTimeTotalMs += realtimeMs - mActiveTimeLastUpdateRealtimeMs;
744                 mActiveTimeLastUpdateRealtimeMs = realtimeMs;
745             }
746             if (mForegroundRequestCount > 0) {
747                 long realtimeMs = SystemClock.elapsedRealtime();
748                 mForegroundTimeTotalMs += realtimeMs - mForegroundTimeLastUpdateRealtimeMs;
749                 mForegroundTimeLastUpdateRealtimeMs = realtimeMs;
750             }
751         }
752 
753         @Override
toString()754         public synchronized String toString() {
755             return "min/max interval = " + intervalToString(mFastestIntervalMs) + "/"
756                     + intervalToString(mSlowestIntervalMs)
757                     + ", total/active/foreground duration = " + formatDuration(mAddedTimeTotalMs)
758                     + "/" + formatDuration(mActiveTimeTotalMs) + "/"
759                     + formatDuration(mForegroundTimeTotalMs) + ", locations = "
760                     + mDeliveredLocationCount;
761         }
762 
intervalToString(long intervalMs)763         private static String intervalToString(long intervalMs) {
764             if (intervalMs == LocationRequest.PASSIVE_INTERVAL) {
765                 return "passive";
766             } else {
767                 return MILLISECONDS.toSeconds(intervalMs) + "s";
768             }
769         }
770     }
771 
772     /**
773      * Aggregate statistics for GNSS measurements.
774      */
775     public static final class GnssMeasurementAggregateStats {
776         @GuardedBy("this")
777         private int mAddedRequestCount;
778         @GuardedBy("this")
779         private int mReceivedMeasurementEventCount;
780         @GuardedBy("this")
781         private long mAddedTimeTotalMs;
782         @GuardedBy("this")
783         private long mAddedTimeLastUpdateRealtimeMs;
784         @GuardedBy("this")
785         private long mFastestIntervalMs = Long.MAX_VALUE;
786         @GuardedBy("this")
787         private long mSlowestIntervalMs = 0;
788         @GuardedBy("this")
789         private boolean mHasFullTracking;
790         @GuardedBy("this")
791         private boolean mHasDutyCycling;
792 
GnssMeasurementAggregateStats()793         GnssMeasurementAggregateStats() {
794         }
795 
markRequestAdded(long intervalMillis, boolean fullTracking)796         synchronized void markRequestAdded(long intervalMillis, boolean fullTracking) {
797             if (mAddedRequestCount++ == 0) {
798                 mAddedTimeLastUpdateRealtimeMs = SystemClock.elapsedRealtime();
799             }
800             if (fullTracking) {
801                 mHasFullTracking = true;
802             } else {
803                 mHasDutyCycling = true;
804             }
805             mFastestIntervalMs = min(intervalMillis, mFastestIntervalMs);
806             mSlowestIntervalMs = max(intervalMillis, mSlowestIntervalMs);
807         }
808 
markRequestRemoved()809         synchronized void markRequestRemoved() {
810             updateTotals();
811             --mAddedRequestCount;
812             Preconditions.checkState(mAddedRequestCount >= 0);
813         }
814 
markGnssMeasurementDelivered()815         synchronized void markGnssMeasurementDelivered() {
816             mReceivedMeasurementEventCount++;
817         }
818 
updateTotals()819         public synchronized void updateTotals() {
820             if (mAddedRequestCount > 0) {
821                 long realtimeMs = SystemClock.elapsedRealtime();
822                 mAddedTimeTotalMs += realtimeMs - mAddedTimeLastUpdateRealtimeMs;
823                 mAddedTimeLastUpdateRealtimeMs = realtimeMs;
824             }
825         }
826 
827         @Override
toString()828         public synchronized String toString() {
829             return "min/max interval = "
830                     + intervalToString(mFastestIntervalMs) + "/"
831                     + intervalToString(mSlowestIntervalMs)
832                     + ", total duration = " + formatDuration(mAddedTimeTotalMs)
833                     + ", tracking mode = " + trackingModeToString() + ", GNSS measurement events = "
834                     + mReceivedMeasurementEventCount;
835         }
836 
intervalToString(long intervalMs)837         private static String intervalToString(long intervalMs) {
838             if (intervalMs == GnssMeasurementRequest.PASSIVE_INTERVAL) {
839                 return "passive";
840             } else {
841                 return MILLISECONDS.toSeconds(intervalMs) + "s";
842             }
843         }
844 
845         @GuardedBy("this")
trackingModeToString()846         private String trackingModeToString() {
847             if (mHasFullTracking && mHasDutyCycling) {
848                 return "mixed tracking mode";
849             } else if (mHasFullTracking) {
850                 return "always full-tracking";
851             } else {
852                 return "always duty-cycling";
853             }
854         }
855     }
856 }
857