• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16 
17 package com.android.server.usage;
18 
19 import static android.app.usage.UsageEvents.Event.DEVICE_SHUTDOWN;
20 import static android.app.usage.UsageEvents.Event.DEVICE_STARTUP;
21 import static android.app.usage.UsageEvents.HIDE_LOCUS_EVENTS;
22 import static android.app.usage.UsageEvents.HIDE_SHORTCUT_EVENTS;
23 import static android.app.usage.UsageEvents.OBFUSCATE_INSTANT_APPS;
24 import static android.app.usage.UsageEvents.OBFUSCATE_NOTIFICATION_EVENTS;
25 import static android.app.usage.UsageStatsManager.INTERVAL_BEST;
26 import static android.app.usage.UsageStatsManager.INTERVAL_COUNT;
27 import static android.app.usage.UsageStatsManager.INTERVAL_DAILY;
28 import static android.app.usage.UsageStatsManager.INTERVAL_MONTHLY;
29 import static android.app.usage.UsageStatsManager.INTERVAL_WEEKLY;
30 import static android.app.usage.UsageStatsManager.INTERVAL_YEARLY;
31 
32 import android.app.usage.ConfigurationStats;
33 import android.app.usage.EventList;
34 import android.app.usage.EventStats;
35 import android.app.usage.TimeSparseArray;
36 import android.app.usage.UsageEvents;
37 import android.app.usage.UsageEvents.Event;
38 import android.app.usage.UsageStats;
39 import android.app.usage.UsageStatsManager;
40 import android.content.Context;
41 import android.content.res.Configuration;
42 import android.os.SystemClock;
43 import android.os.UserHandle;
44 import android.text.format.DateUtils;
45 import android.util.ArrayMap;
46 import android.util.ArraySet;
47 import android.util.AtomicFile;
48 import android.util.Slog;
49 import android.util.SparseIntArray;
50 
51 import com.android.internal.util.ArrayUtils;
52 import com.android.internal.util.CollectionUtils;
53 import com.android.internal.util.IndentingPrintWriter;
54 import com.android.server.usage.UsageStatsDatabase.StatCombiner;
55 
56 import java.io.File;
57 import java.io.IOException;
58 import java.text.SimpleDateFormat;
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.HashMap;
62 import java.util.List;
63 
64 /**
65  * A per-user UsageStatsService. All methods are meant to be called with the main lock held
66  * in UsageStatsService.
67  */
68 class UserUsageStatsService {
69     private static final String TAG = "UsageStatsService";
70     private static final boolean DEBUG = UsageStatsService.DEBUG;
71     private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
72     private static final int sDateFormatFlags =
73             DateUtils.FORMAT_SHOW_DATE
74             | DateUtils.FORMAT_SHOW_TIME
75             | DateUtils.FORMAT_SHOW_YEAR
76             | DateUtils.FORMAT_NUMERIC_DATE;
77 
78     private final Context mContext;
79     private final UsageStatsDatabase mDatabase;
80     private final IntervalStats[] mCurrentStats;
81     private boolean mStatsChanged = false;
82     private final UnixCalendar mDailyExpiryDate;
83     private final StatsUpdatedListener mListener;
84     private final String mLogPrefix;
85     private String mLastBackgroundedPackage;
86     private final int mUserId;
87     private long mRealTimeSnapshot;
88     private long mSystemTimeSnapshot;
89 
90     private static final long[] INTERVAL_LENGTH = new long[] {
91             UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
92             UnixCalendar.MONTH_IN_MILLIS, UnixCalendar.YEAR_IN_MILLIS
93     };
94 
95     interface StatsUpdatedListener {
onStatsUpdated()96         void onStatsUpdated();
onStatsReloaded()97         void onStatsReloaded();
98         /**
99          * Callback that a system update was detected
100          * @param mUserId user that needs to be initialized
101          */
onNewUpdate(int mUserId)102         void onNewUpdate(int mUserId);
103     }
104 
UserUsageStatsService(Context context, int userId, File usageStatsDir, StatsUpdatedListener listener)105     UserUsageStatsService(Context context, int userId, File usageStatsDir,
106             StatsUpdatedListener listener) {
107         mContext = context;
108         mDailyExpiryDate = new UnixCalendar(0);
109         mDatabase = new UsageStatsDatabase(usageStatsDir);
110         mCurrentStats = new IntervalStats[INTERVAL_COUNT];
111         mListener = listener;
112         mLogPrefix = "User[" + Integer.toString(userId) + "] ";
113         mUserId = userId;
114         mRealTimeSnapshot = SystemClock.elapsedRealtime();
115         mSystemTimeSnapshot = System.currentTimeMillis();
116     }
117 
init(final long currentTimeMillis, HashMap<String, Long> installedPackages)118     void init(final long currentTimeMillis, HashMap<String, Long> installedPackages) {
119         readPackageMappingsLocked(installedPackages);
120         mDatabase.init(currentTimeMillis);
121         if (mDatabase.wasUpgradePerformed()) {
122             mDatabase.prunePackagesDataOnUpgrade(installedPackages);
123         }
124 
125         int nullCount = 0;
126         for (int i = 0; i < mCurrentStats.length; i++) {
127             mCurrentStats[i] = mDatabase.getLatestUsageStats(i);
128             if (mCurrentStats[i] == null) {
129                 // Find out how many intervals we don't have data for.
130                 // Ideally it should be all or none.
131                 nullCount++;
132             }
133         }
134 
135         if (nullCount > 0) {
136             if (nullCount != mCurrentStats.length) {
137                 // This is weird, but we shouldn't fail if something like this
138                 // happens.
139                 Slog.w(TAG, mLogPrefix + "Some stats have no latest available");
140             } else {
141                 // This must be first boot.
142             }
143 
144             // By calling loadActiveStats, we will
145             // generate new stats for each bucket.
146             loadActiveStats(currentTimeMillis);
147         } else {
148             // Set up the expiry date to be one day from the latest daily stat.
149             // This may actually be today and we will rollover on the first event
150             // that is reported.
151             updateRolloverDeadline();
152         }
153 
154         // During system reboot, add a DEVICE_SHUTDOWN event to the end of event list, the timestamp
155         // is last time UsageStatsDatabase is persisted to disk or the last event's time whichever
156         // is higher (because the file system timestamp is round down to integral seconds).
157         // Also add a DEVICE_STARTUP event with current system timestamp.
158         final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
159         if (currentDailyStats != null) {
160             final Event shutdownEvent = new Event(DEVICE_SHUTDOWN,
161                     Math.max(currentDailyStats.lastTimeSaved, currentDailyStats.endTime));
162             shutdownEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
163             currentDailyStats.addEvent(shutdownEvent);
164             final Event startupEvent = new Event(DEVICE_STARTUP, System.currentTimeMillis());
165             startupEvent.mPackage = Event.DEVICE_EVENT_PACKAGE_NAME;
166             currentDailyStats.addEvent(startupEvent);
167         }
168 
169         if (mDatabase.isNewUpdate()) {
170             notifyNewUpdate();
171         }
172     }
173 
userStopped()174     void userStopped() {
175         // Flush events to disk immediately to guarantee persistence.
176         persistActiveStats();
177     }
178 
onPackageRemoved(String packageName, long timeRemoved)179     int onPackageRemoved(String packageName, long timeRemoved) {
180         return mDatabase.onPackageRemoved(packageName, timeRemoved);
181     }
182 
readPackageMappingsLocked(HashMap<String, Long> installedPackages)183     private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) {
184         mDatabase.readMappingsLocked();
185         // Package mappings for the system user are updated after 24 hours via a job scheduled by
186         // UsageStatsIdleService to ensure restored data is not lost on first boot. Additionally,
187         // this makes user service initialization a little quicker on subsequent boots.
188         if (mUserId != UserHandle.USER_SYSTEM) {
189             updatePackageMappingsLocked(installedPackages);
190         }
191     }
192 
193     /**
194      * Compares the package mappings on disk with the ones currently installed and removes the
195      * mappings for those packages that have been uninstalled.
196      * This will only happen once per device boot, when the user is unlocked for the first time.
197      * If the user is the system user (user 0), this is delayed to ensure data for packages
198      * that were restored isn't removed before the restore is complete.
199      *
200      * @param installedPackages map of installed packages (package_name:package_install_time)
201      * @return {@code true} on a successful mappings update, {@code false} otherwise.
202      */
updatePackageMappingsLocked(HashMap<String, Long> installedPackages)203     boolean updatePackageMappingsLocked(HashMap<String, Long> installedPackages) {
204         if (ArrayUtils.isEmpty(installedPackages)) {
205             return true;
206         }
207 
208         final long timeNow = System.currentTimeMillis();
209         final ArrayList<String> removedPackages = new ArrayList<>();
210         // populate list of packages that are found in the mappings but not in the installed list
211         for (int i = mDatabase.mPackagesTokenData.packagesToTokensMap.size() - 1; i >= 0; i--) {
212             final String packageName = mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i);
213             if (!installedPackages.containsKey(packageName)) {
214                 removedPackages.add(packageName);
215             }
216         }
217         if (removedPackages.isEmpty()) {
218             return true;
219         }
220 
221         // remove packages in the mappings that are no longer installed and persist to disk
222         for (int i = removedPackages.size() - 1; i >= 0; i--) {
223             mDatabase.mPackagesTokenData.removePackage(removedPackages.get(i), timeNow);
224         }
225         try {
226             mDatabase.writeMappingsLocked();
227         } catch (Exception e) {
228             Slog.w(TAG, "Unable to write updated package mappings file on service initialization.");
229             return false;
230         }
231         return true;
232     }
233 
pruneUninstalledPackagesData()234     boolean pruneUninstalledPackagesData() {
235         return mDatabase.pruneUninstalledPackagesData();
236     }
237 
onTimeChanged(long oldTime, long newTime)238     private void onTimeChanged(long oldTime, long newTime) {
239         persistActiveStats();
240         mDatabase.onTimeChanged(newTime - oldTime);
241         loadActiveStats(newTime);
242     }
243 
244     /**
245      * This should be the only way to get the time from the system.
246      */
checkAndGetTimeLocked()247     private long checkAndGetTimeLocked() {
248         final long actualSystemTime = System.currentTimeMillis();
249         if (!UsageStatsService.ENABLE_TIME_CHANGE_CORRECTION) {
250             return actualSystemTime;
251         }
252         final long actualRealtime = SystemClock.elapsedRealtime();
253         final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
254         final long diffSystemTime = actualSystemTime - expectedSystemTime;
255         if (Math.abs(diffSystemTime) > UsageStatsService.TIME_CHANGE_THRESHOLD_MILLIS) {
256             // The time has changed.
257             Slog.i(TAG, mLogPrefix + "Time changed in by " + (diffSystemTime / 1000) + " seconds");
258             onTimeChanged(expectedSystemTime, actualSystemTime);
259             mRealTimeSnapshot = actualRealtime;
260             mSystemTimeSnapshot = actualSystemTime;
261         }
262         return actualSystemTime;
263     }
264 
265     /**
266      * Assuming the event's timestamp is measured in milliseconds since boot,
267      * convert it to a system wall time.
268      */
convertToSystemTimeLocked(Event event)269     private void convertToSystemTimeLocked(Event event) {
270         event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
271     }
272 
reportEvent(Event event)273     void reportEvent(Event event) {
274         if (DEBUG) {
275             Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
276                     + "[" + event.mTimeStamp + "]: "
277                     + eventToString(event.mEventType));
278         }
279 
280         if (event.mEventType != Event.USER_INTERACTION
281                 && event.mEventType != Event.APP_COMPONENT_USED) {
282             checkAndGetTimeLocked();
283             convertToSystemTimeLocked(event);
284         }
285 
286         if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
287             // Need to rollover
288             rolloverStats(event.mTimeStamp);
289         }
290 
291         final IntervalStats currentDailyStats = mCurrentStats[INTERVAL_DAILY];
292 
293         final Configuration newFullConfig = event.mConfiguration;
294         if (event.mEventType == Event.CONFIGURATION_CHANGE
295                 && currentDailyStats.activeConfiguration != null) {
296             // Make the event configuration a delta.
297             event.mConfiguration = Configuration.generateDelta(
298                     currentDailyStats.activeConfiguration, newFullConfig);
299         }
300 
301         if (event.mEventType != Event.SYSTEM_INTERACTION
302                 // ACTIVITY_DESTROYED is a private event. If there is preceding ACTIVITY_STOPPED
303                 // ACTIVITY_DESTROYED will be dropped. Otherwise it will be converted to
304                 // ACTIVITY_STOPPED.
305                 && event.mEventType != Event.ACTIVITY_DESTROYED
306                 // FLUSH_TO_DISK is a private event.
307                 && event.mEventType != Event.FLUSH_TO_DISK
308                 // DEVICE_SHUTDOWN is added to event list after reboot.
309                 && event.mEventType != Event.DEVICE_SHUTDOWN
310                 // We aren't interested in every instance of the APP_COMPONENT_USED event.
311                 && event.mEventType != Event.APP_COMPONENT_USED) {
312             currentDailyStats.addEvent(event);
313         }
314 
315         boolean incrementAppLaunch = false;
316         if (event.mEventType == Event.ACTIVITY_RESUMED) {
317             if (event.mPackage != null && !event.mPackage.equals(mLastBackgroundedPackage)) {
318                 incrementAppLaunch = true;
319             }
320         } else if (event.mEventType == Event.ACTIVITY_PAUSED) {
321             if (event.mPackage != null) {
322                 mLastBackgroundedPackage = event.mPackage;
323             }
324         }
325 
326         for (IntervalStats stats : mCurrentStats) {
327             switch (event.mEventType) {
328                 case Event.CONFIGURATION_CHANGE: {
329                     stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
330                 } break;
331                 case Event.CHOOSER_ACTION: {
332                     stats.updateChooserCounts(event.mPackage, event.mContentType, event.mAction);
333                     String[] annotations = event.mContentAnnotations;
334                     if (annotations != null) {
335                         for (String annotation : annotations) {
336                             stats.updateChooserCounts(event.mPackage, annotation, event.mAction);
337                         }
338                     }
339                 } break;
340                 case Event.SCREEN_INTERACTIVE: {
341                     stats.updateScreenInteractive(event.mTimeStamp);
342                 } break;
343                 case Event.SCREEN_NON_INTERACTIVE: {
344                     stats.updateScreenNonInteractive(event.mTimeStamp);
345                 } break;
346                 case Event.KEYGUARD_SHOWN: {
347                     stats.updateKeyguardShown(event.mTimeStamp);
348                 } break;
349                 case Event.KEYGUARD_HIDDEN: {
350                     stats.updateKeyguardHidden(event.mTimeStamp);
351                 } break;
352                 default: {
353                     stats.update(event.mPackage, event.getClassName(),
354                             event.mTimeStamp, event.mEventType, event.mInstanceId);
355                     if (incrementAppLaunch) {
356                         stats.incrementAppLaunchCount(event.mPackage);
357                     }
358                 } break;
359             }
360         }
361 
362         notifyStatsChanged();
363     }
364 
365     private static final StatCombiner<UsageStats> sUsageStatsCombiner =
366             new StatCombiner<UsageStats>() {
367                 @Override
368                 public void combine(IntervalStats stats, boolean mutable,
369                                     List<UsageStats> accResult) {
370                     if (!mutable) {
371                         accResult.addAll(stats.packageStats.values());
372                         return;
373                     }
374 
375                     final int statCount = stats.packageStats.size();
376                     for (int i = 0; i < statCount; i++) {
377                         accResult.add(new UsageStats(stats.packageStats.valueAt(i)));
378                     }
379                 }
380             };
381 
382     private static final StatCombiner<ConfigurationStats> sConfigStatsCombiner =
383             new StatCombiner<ConfigurationStats>() {
384                 @Override
385                 public void combine(IntervalStats stats, boolean mutable,
386                                     List<ConfigurationStats> accResult) {
387                     if (!mutable) {
388                         accResult.addAll(stats.configurations.values());
389                         return;
390                     }
391 
392                     final int configCount = stats.configurations.size();
393                     for (int i = 0; i < configCount; i++) {
394                         accResult.add(new ConfigurationStats(stats.configurations.valueAt(i)));
395                     }
396                 }
397             };
398 
399     private static final StatCombiner<EventStats> sEventStatsCombiner =
400             new StatCombiner<EventStats>() {
401                 @Override
402                 public void combine(IntervalStats stats, boolean mutable,
403                         List<EventStats> accResult) {
404                     stats.addEventStatsTo(accResult);
405                 }
406             };
407 
validRange(long currentTime, long beginTime, long endTime)408     private static boolean validRange(long currentTime, long beginTime, long endTime) {
409         return beginTime <= currentTime && beginTime < endTime;
410     }
411 
412     /**
413      * Generic query method that selects the appropriate IntervalStats for the specified time range
414      * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
415      * provided to select the stats to use from the IntervalStats object.
416      */
queryStats(int intervalType, final long beginTime, final long endTime, StatCombiner<T> combiner)417     private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
418             StatCombiner<T> combiner) {
419         if (intervalType == INTERVAL_BEST) {
420             intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
421             if (intervalType < 0) {
422                 // Nothing saved to disk yet, so every stat is just as equal (no rollover has
423                 // occurred.
424                 intervalType = INTERVAL_DAILY;
425             }
426         }
427 
428         if (intervalType < 0 || intervalType >= mCurrentStats.length) {
429             if (DEBUG) {
430                 Slog.d(TAG, mLogPrefix + "Bad intervalType used " + intervalType);
431             }
432             return null;
433         }
434 
435         final IntervalStats currentStats = mCurrentStats[intervalType];
436 
437         if (DEBUG) {
438             Slog.d(TAG, mLogPrefix + "SELECT * FROM " + intervalType + " WHERE beginTime >= "
439                     + beginTime + " AND endTime < " + endTime);
440         }
441 
442         if (beginTime >= currentStats.endTime) {
443             if (DEBUG) {
444                 Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
445                         + currentStats.endTime);
446             }
447             // Nothing newer available.
448             return null;
449         }
450 
451         // Truncate the endTime to just before the in-memory stats. Then, we'll append the
452         // in-memory stats to the results (if necessary) so as to avoid writing to disk too
453         // often.
454         final long truncatedEndTime = Math.min(currentStats.beginTime, endTime);
455 
456         // Get the stats from disk.
457         List<T> results = mDatabase.queryUsageStats(intervalType, beginTime,
458                 truncatedEndTime, combiner);
459         if (DEBUG) {
460             Slog.d(TAG, "Got " + (results != null ? results.size() : 0) + " results from disk");
461             Slog.d(TAG, "Current stats beginTime=" + currentStats.beginTime +
462                     " endTime=" + currentStats.endTime);
463         }
464 
465         // Now check if the in-memory stats match the range and add them if they do.
466         if (beginTime < currentStats.endTime && endTime > currentStats.beginTime) {
467             if (DEBUG) {
468                 Slog.d(TAG, mLogPrefix + "Returning in-memory stats");
469             }
470 
471             if (results == null) {
472                 results = new ArrayList<>();
473             }
474             mDatabase.filterStats(currentStats);
475             combiner.combine(currentStats, true, results);
476         }
477 
478         if (DEBUG) {
479             Slog.d(TAG, mLogPrefix + "Results: " + (results != null ? results.size() : 0));
480         }
481         return results;
482     }
483 
queryUsageStats(int bucketType, long beginTime, long endTime)484     List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
485         if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
486             return null;
487         }
488         return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
489     }
490 
queryConfigurationStats(int bucketType, long beginTime, long endTime)491     List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
492         if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
493             return null;
494         }
495         return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
496     }
497 
queryEventStats(int bucketType, long beginTime, long endTime)498     List<EventStats> queryEventStats(int bucketType, long beginTime, long endTime) {
499         if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
500             return null;
501         }
502         return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
503     }
504 
queryEvents(final long beginTime, final long endTime, int flags)505     UsageEvents queryEvents(final long beginTime, final long endTime, int flags) {
506         if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
507             return null;
508         }
509         final ArraySet<String> names = new ArraySet<>();
510         List<Event> results = queryStats(INTERVAL_DAILY,
511                 beginTime, endTime, new StatCombiner<Event>() {
512                     @Override
513                     public void combine(IntervalStats stats, boolean mutable,
514                             List<Event> accumulatedResult) {
515                         final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
516                         final int size = stats.events.size();
517                         for (int i = startIndex; i < size; i++) {
518                             if (stats.events.get(i).mTimeStamp >= endTime) {
519                                 return;
520                             }
521 
522                             Event event = stats.events.get(i);
523                             final int eventType = event.mEventType;
524                             if (eventType == Event.SHORTCUT_INVOCATION
525                                     && (flags & HIDE_SHORTCUT_EVENTS) == HIDE_SHORTCUT_EVENTS) {
526                                 continue;
527                             }
528                             if (eventType == Event.LOCUS_ID_SET
529                                     && (flags & HIDE_LOCUS_EVENTS) == HIDE_LOCUS_EVENTS) {
530                                 continue;
531                             }
532                             if ((eventType == Event.NOTIFICATION_SEEN
533                                     || eventType == Event.NOTIFICATION_INTERRUPTION)
534                                     && (flags & OBFUSCATE_NOTIFICATION_EVENTS)
535                                     == OBFUSCATE_NOTIFICATION_EVENTS) {
536                                 event = event.getObfuscatedNotificationEvent();
537                             }
538                             if ((flags & OBFUSCATE_INSTANT_APPS) == OBFUSCATE_INSTANT_APPS) {
539                                 event = event.getObfuscatedIfInstantApp();
540                             }
541                             if (event.mPackage != null) {
542                                 names.add(event.mPackage);
543                             }
544                             if (event.mClass != null) {
545                                 names.add(event.mClass);
546                             }
547                             if (event.mTaskRootPackage != null) {
548                                 names.add(event.mTaskRootPackage);
549                             }
550                             if (event.mTaskRootClass != null) {
551                                 names.add(event.mTaskRootClass);
552                             }
553                             accumulatedResult.add(event);
554                         }
555                     }
556                 });
557 
558         if (results == null || results.isEmpty()) {
559             return null;
560         }
561 
562         String[] table = names.toArray(new String[names.size()]);
563         Arrays.sort(table);
564         return new UsageEvents(results, table, true);
565     }
566 
queryEventsForPackage(final long beginTime, final long endTime, final String packageName, boolean includeTaskRoot)567     UsageEvents queryEventsForPackage(final long beginTime, final long endTime,
568             final String packageName, boolean includeTaskRoot) {
569         if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
570             return null;
571         }
572         final ArraySet<String> names = new ArraySet<>();
573         names.add(packageName);
574         final List<Event> results = queryStats(INTERVAL_DAILY,
575                 beginTime, endTime, (stats, mutable, accumulatedResult) -> {
576                     final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
577                     final int size = stats.events.size();
578                     for (int i = startIndex; i < size; i++) {
579                         if (stats.events.get(i).mTimeStamp >= endTime) {
580                             return;
581                         }
582 
583                         final Event event = stats.events.get(i);
584                         if (!packageName.equals(event.mPackage)) {
585                             continue;
586                         }
587                         if (event.mClass != null) {
588                             names.add(event.mClass);
589                         }
590                         if (includeTaskRoot && event.mTaskRootPackage != null) {
591                             names.add(event.mTaskRootPackage);
592                         }
593                         if (includeTaskRoot && event.mTaskRootClass != null) {
594                             names.add(event.mTaskRootClass);
595                         }
596                         accumulatedResult.add(event);
597                     }
598                 });
599 
600         if (results == null || results.isEmpty()) {
601             return null;
602         }
603 
604         final String[] table = names.toArray(new String[names.size()]);
605         Arrays.sort(table);
606         return new UsageEvents(results, table, includeTaskRoot);
607     }
608 
persistActiveStats()609     void persistActiveStats() {
610         if (mStatsChanged) {
611             Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
612             try {
613                 mDatabase.obfuscateCurrentStats(mCurrentStats);
614                 mDatabase.writeMappingsLocked();
615                 for (int i = 0; i < mCurrentStats.length; i++) {
616                     mDatabase.putUsageStats(i, mCurrentStats[i]);
617                 }
618                 mStatsChanged = false;
619             } catch (IOException e) {
620                 Slog.e(TAG, mLogPrefix + "Failed to persist active stats", e);
621             }
622         }
623     }
624 
rolloverStats(final long currentTimeMillis)625     private void rolloverStats(final long currentTimeMillis) {
626         final long startTime = SystemClock.elapsedRealtime();
627         Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
628 
629         // Finish any ongoing events with an END_OF_DAY or ROLLOVER_FOREGROUND_SERVICE event.
630         // Make a note of which components need a new CONTINUE_PREVIOUS_DAY or
631         // CONTINUING_FOREGROUND_SERVICE entry.
632         final Configuration previousConfig =
633                 mCurrentStats[INTERVAL_DAILY].activeConfiguration;
634         ArraySet<String> continuePkgs = new ArraySet<>();
635         ArrayMap<String, SparseIntArray> continueActivity =
636                 new ArrayMap<>();
637         ArrayMap<String, ArrayMap<String, Integer>> continueForegroundService =
638                 new ArrayMap<>();
639         for (IntervalStats stat : mCurrentStats) {
640             final int pkgCount = stat.packageStats.size();
641             for (int i = 0; i < pkgCount; i++) {
642                 final UsageStats pkgStats = stat.packageStats.valueAt(i);
643                 if (pkgStats.mActivities.size() > 0
644                         || !pkgStats.mForegroundServices.isEmpty()) {
645                     if (pkgStats.mActivities.size() > 0) {
646                         continueActivity.put(pkgStats.mPackageName,
647                                 pkgStats.mActivities);
648                         stat.update(pkgStats.mPackageName, null,
649                                 mDailyExpiryDate.getTimeInMillis() - 1,
650                                 Event.END_OF_DAY, 0);
651                     }
652                     if (!pkgStats.mForegroundServices.isEmpty()) {
653                         continueForegroundService.put(pkgStats.mPackageName,
654                                 pkgStats.mForegroundServices);
655                         stat.update(pkgStats.mPackageName, null,
656                                 mDailyExpiryDate.getTimeInMillis() - 1,
657                                 Event.ROLLOVER_FOREGROUND_SERVICE, 0);
658                     }
659                     continuePkgs.add(pkgStats.mPackageName);
660                     notifyStatsChanged();
661                 }
662             }
663 
664             stat.updateConfigurationStats(null,
665                     mDailyExpiryDate.getTimeInMillis() - 1);
666             stat.commitTime(mDailyExpiryDate.getTimeInMillis() - 1);
667         }
668 
669         persistActiveStats();
670         mDatabase.prune(currentTimeMillis);
671         loadActiveStats(currentTimeMillis);
672 
673         final int continueCount = continuePkgs.size();
674         for (int i = 0; i < continueCount; i++) {
675             String pkgName = continuePkgs.valueAt(i);
676             final long beginTime = mCurrentStats[INTERVAL_DAILY].beginTime;
677             for (IntervalStats stat : mCurrentStats) {
678                 if (continueActivity.containsKey(pkgName)) {
679                     final SparseIntArray eventMap =
680                             continueActivity.get(pkgName);
681                     final int size = eventMap.size();
682                     for (int j = 0; j < size; j++) {
683                         stat.update(pkgName, null, beginTime,
684                                 eventMap.valueAt(j), eventMap.keyAt(j));
685                     }
686                 }
687                 if (continueForegroundService.containsKey(pkgName)) {
688                     final ArrayMap<String, Integer> eventMap =
689                             continueForegroundService.get(pkgName);
690                     final int size = eventMap.size();
691                     for (int j = 0; j < size; j++) {
692                         stat.update(pkgName, eventMap.keyAt(j), beginTime,
693                                 eventMap.valueAt(j), 0);
694                     }
695                 }
696                 stat.updateConfigurationStats(previousConfig, beginTime);
697                 notifyStatsChanged();
698             }
699         }
700         persistActiveStats();
701 
702         final long totalTime = SystemClock.elapsedRealtime() - startTime;
703         Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
704                 + " milliseconds");
705     }
706 
notifyStatsChanged()707     private void notifyStatsChanged() {
708         if (!mStatsChanged) {
709             mStatsChanged = true;
710             mListener.onStatsUpdated();
711         }
712     }
713 
notifyNewUpdate()714     private void notifyNewUpdate() {
715         mListener.onNewUpdate(mUserId);
716     }
717 
loadActiveStats(final long currentTimeMillis)718     private void loadActiveStats(final long currentTimeMillis) {
719         for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
720             final IntervalStats stats = mDatabase.getLatestUsageStats(intervalType);
721             if (stats != null
722                     && currentTimeMillis < stats.beginTime + INTERVAL_LENGTH[intervalType]) {
723                 if (DEBUG) {
724                     Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
725                             sDateFormat.format(stats.beginTime) + "(" + stats.beginTime +
726                             ") for interval " + intervalType);
727                 }
728                 mCurrentStats[intervalType] = stats;
729             } else {
730                 // No good fit remains.
731                 if (DEBUG) {
732                     Slog.d(TAG, "Creating new stats @ " +
733                             sDateFormat.format(currentTimeMillis) + "(" +
734                             currentTimeMillis + ") for interval " + intervalType);
735                 }
736 
737                 mCurrentStats[intervalType] = new IntervalStats();
738                 mCurrentStats[intervalType].beginTime = currentTimeMillis;
739                 mCurrentStats[intervalType].endTime = currentTimeMillis + 1;
740             }
741         }
742 
743         mStatsChanged = false;
744         updateRolloverDeadline();
745 
746         // Tell the listener that the stats reloaded, which may have changed idle states.
747         mListener.onStatsReloaded();
748     }
749 
updateRolloverDeadline()750     private void updateRolloverDeadline() {
751         mDailyExpiryDate.setTimeInMillis(
752                 mCurrentStats[INTERVAL_DAILY].beginTime);
753         mDailyExpiryDate.addDays(1);
754         Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
755                 sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
756                 mDailyExpiryDate.getTimeInMillis() + ")");
757     }
758 
759     //
760     // -- DUMP related methods --
761     //
762 
checkin(final IndentingPrintWriter pw)763     void checkin(final IndentingPrintWriter pw) {
764         mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
765             @Override
766             public boolean checkin(IntervalStats stats) {
767                 printIntervalStats(pw, stats, false, false, null);
768                 return true;
769             }
770         });
771     }
772 
dump(IndentingPrintWriter pw, List<String> pkgs)773     void dump(IndentingPrintWriter pw, List<String> pkgs) {
774         dump(pw, pkgs, false);
775     }
776 
dump(IndentingPrintWriter pw, List<String> pkgs, boolean compact)777     void dump(IndentingPrintWriter pw, List<String> pkgs, boolean compact) {
778         printLast24HrEvents(pw, !compact, pkgs);
779         for (int interval = 0; interval < mCurrentStats.length; interval++) {
780             pw.print("In-memory ");
781             pw.print(intervalToString(interval));
782             pw.println(" stats");
783             printIntervalStats(pw, mCurrentStats[interval], !compact, true, pkgs);
784         }
785         if (CollectionUtils.isEmpty(pkgs)) {
786             mDatabase.dump(pw, compact);
787         }
788     }
789 
dumpDatabaseInfo(IndentingPrintWriter ipw)790     void dumpDatabaseInfo(IndentingPrintWriter ipw) {
791         mDatabase.dump(ipw, false);
792     }
793 
dumpMappings(IndentingPrintWriter ipw)794     void dumpMappings(IndentingPrintWriter ipw) {
795         mDatabase.dumpMappings(ipw);
796     }
797 
dumpFile(IndentingPrintWriter ipw, String[] args)798     void dumpFile(IndentingPrintWriter ipw, String[] args) {
799         if (args == null || args.length == 0) {
800             // dump all files for every interval for specified user
801             final int numIntervals = mDatabase.mSortedStatFiles.length;
802             for (int interval = 0; interval < numIntervals; interval++) {
803                 ipw.println("interval=" + intervalToString(interval));
804                 ipw.increaseIndent();
805                 dumpFileDetailsForInterval(ipw, interval);
806                 ipw.decreaseIndent();
807             }
808         } else {
809             final int interval;
810             try {
811                 final int intervalValue = stringToInterval(args[0]);
812                 if (intervalValue == -1) {
813                     interval = Integer.valueOf(args[0]);
814                 } else {
815                     interval = intervalValue;
816                 }
817             } catch (NumberFormatException nfe) {
818                 ipw.println("invalid interval specified.");
819                 return;
820             }
821             if (interval < 0 || interval >= mDatabase.mSortedStatFiles.length) {
822                 ipw.println("the specified interval does not exist.");
823                 return;
824             }
825             if (args.length == 1) {
826                 // dump all files in the specified interval
827                 dumpFileDetailsForInterval(ipw, interval);
828             } else {
829                 // dump details only for the specified filename
830                 final long filename;
831                 try {
832                     filename = Long.valueOf(args[1]);
833                 } catch (NumberFormatException nfe) {
834                     ipw.println("invalid filename specified.");
835                     return;
836                 }
837                 final IntervalStats stats = mDatabase.readIntervalStatsForFile(interval, filename);
838                 if (stats == null) {
839                     ipw.println("the specified filename does not exist.");
840                     return;
841                 }
842                 dumpFileDetails(ipw, stats, Long.valueOf(args[1]));
843             }
844         }
845     }
846 
dumpFileDetailsForInterval(IndentingPrintWriter ipw, int interval)847     private void dumpFileDetailsForInterval(IndentingPrintWriter ipw, int interval) {
848         final TimeSparseArray<AtomicFile> files = mDatabase.mSortedStatFiles[interval];
849         final int numFiles = files.size();
850         for (int i = 0; i < numFiles; i++) {
851             final long filename = files.keyAt(i);
852             final IntervalStats stats = mDatabase.readIntervalStatsForFile(interval, filename);
853             dumpFileDetails(ipw, stats, filename);
854             ipw.println();
855         }
856     }
857 
dumpFileDetails(IndentingPrintWriter ipw, IntervalStats stats, long filename)858     private void dumpFileDetails(IndentingPrintWriter ipw, IntervalStats stats, long filename) {
859         ipw.println("file=" + filename);
860         ipw.increaseIndent();
861         printIntervalStats(ipw, stats, false, false, null);
862         ipw.decreaseIndent();
863     }
864 
formatDateTime(long dateTime, boolean pretty)865     static String formatDateTime(long dateTime, boolean pretty) {
866         if (pretty) {
867             return "\"" + sDateFormat.format(dateTime)+ "\"";
868         }
869         return Long.toString(dateTime);
870     }
871 
formatElapsedTime(long elapsedTime, boolean pretty)872     private String formatElapsedTime(long elapsedTime, boolean pretty) {
873         if (pretty) {
874             return "\"" + DateUtils.formatElapsedTime(elapsedTime / 1000) + "\"";
875         }
876         return Long.toString(elapsedTime);
877     }
878 
879 
printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates)880     void printEvent(IndentingPrintWriter pw, Event event, boolean prettyDates) {
881         pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
882         pw.printPair("type", eventToString(event.mEventType));
883         pw.printPair("package", event.mPackage);
884         if (event.mClass != null) {
885             pw.printPair("class", event.mClass);
886         }
887         if (event.mConfiguration != null) {
888             pw.printPair("config", Configuration.resourceQualifierString(event.mConfiguration));
889         }
890         if (event.mShortcutId != null) {
891             pw.printPair("shortcutId", event.mShortcutId);
892         }
893         if (event.mEventType == Event.STANDBY_BUCKET_CHANGED) {
894             pw.printPair("standbyBucket", event.getStandbyBucket());
895             pw.printPair("reason", UsageStatsManager.reasonToString(event.getStandbyReason()));
896         } else if (event.mEventType == Event.ACTIVITY_RESUMED
897                 || event.mEventType == Event.ACTIVITY_PAUSED
898                 || event.mEventType == Event.ACTIVITY_STOPPED) {
899             pw.printPair("instanceId", event.getInstanceId());
900         }
901 
902         if (event.getTaskRootPackageName() != null) {
903             pw.printPair("taskRootPackage", event.getTaskRootPackageName());
904         }
905 
906         if (event.getTaskRootClassName() != null) {
907             pw.printPair("taskRootClass", event.getTaskRootClassName());
908         }
909 
910         if (event.mNotificationChannelId != null) {
911             pw.printPair("channelId", event.mNotificationChannelId);
912         }
913         pw.printHexPair("flags", event.mFlags);
914         pw.println();
915     }
916 
printLast24HrEvents(IndentingPrintWriter pw, boolean prettyDates, final List<String> pkgs)917     void printLast24HrEvents(IndentingPrintWriter pw, boolean prettyDates,
918             final List<String> pkgs) {
919         final long endTime = System.currentTimeMillis();
920         UnixCalendar yesterday = new UnixCalendar(endTime);
921         yesterday.addDays(-1);
922 
923         final long beginTime = yesterday.getTimeInMillis();
924 
925         List<Event> events = queryStats(INTERVAL_DAILY,
926                 beginTime, endTime, new StatCombiner<Event>() {
927                     @Override
928                     public void combine(IntervalStats stats, boolean mutable,
929                             List<Event> accumulatedResult) {
930                         final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
931                         final int size = stats.events.size();
932                         for (int i = startIndex; i < size; i++) {
933                             if (stats.events.get(i).mTimeStamp >= endTime) {
934                                 return;
935                             }
936 
937                             Event event = stats.events.get(i);
938                             if (!CollectionUtils.isEmpty(pkgs) && !pkgs.contains(event.mPackage)) {
939                                 continue;
940                             }
941                             accumulatedResult.add(event);
942                         }
943                     }
944                 });
945 
946         pw.print("Last 24 hour events (");
947         if (prettyDates) {
948             pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
949                     beginTime, endTime, sDateFormatFlags) + "\"");
950         } else {
951             pw.printPair("beginTime", beginTime);
952             pw.printPair("endTime", endTime);
953         }
954         pw.println(")");
955         if (events != null) {
956             pw.increaseIndent();
957             for (Event event : events) {
958                 printEvent(pw, event, prettyDates);
959             }
960             pw.decreaseIndent();
961         }
962     }
963 
printEventAggregation(IndentingPrintWriter pw, String label, IntervalStats.EventTracker tracker, boolean prettyDates)964     void printEventAggregation(IndentingPrintWriter pw, String label,
965             IntervalStats.EventTracker tracker, boolean prettyDates) {
966         if (tracker.count != 0 || tracker.duration != 0) {
967             pw.print(label);
968             pw.print(": ");
969             pw.print(tracker.count);
970             pw.print("x for ");
971             pw.print(formatElapsedTime(tracker.duration, prettyDates));
972             if (tracker.curStartTime != 0) {
973                 pw.print(" (now running, started at ");
974                 formatDateTime(tracker.curStartTime, prettyDates);
975                 pw.print(")");
976             }
977             pw.println();
978         }
979     }
980 
printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, boolean prettyDates, boolean skipEvents, List<String> pkgs)981     void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats,
982             boolean prettyDates, boolean skipEvents, List<String> pkgs) {
983         if (prettyDates) {
984             pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
985                     stats.beginTime, stats.endTime, sDateFormatFlags) + "\"");
986         } else {
987             pw.printPair("beginTime", stats.beginTime);
988             pw.printPair("endTime", stats.endTime);
989         }
990         pw.println();
991         pw.increaseIndent();
992         pw.println("packages");
993         pw.increaseIndent();
994         final ArrayMap<String, UsageStats> pkgStats = stats.packageStats;
995         final int pkgCount = pkgStats.size();
996         for (int i = 0; i < pkgCount; i++) {
997             final UsageStats usageStats = pkgStats.valueAt(i);
998             if (!CollectionUtils.isEmpty(pkgs) && !pkgs.contains(usageStats.mPackageName)) {
999                 continue;
1000             }
1001             pw.printPair("package", usageStats.mPackageName);
1002             pw.printPair("totalTimeUsed",
1003                     formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
1004             pw.printPair("lastTimeUsed", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
1005             pw.printPair("totalTimeVisible",
1006                     formatElapsedTime(usageStats.mTotalTimeVisible, prettyDates));
1007             pw.printPair("lastTimeVisible",
1008                     formatDateTime(usageStats.mLastTimeVisible, prettyDates));
1009             pw.printPair("lastTimeComponentUsed",
1010                     formatDateTime(usageStats.mLastTimeComponentUsed, prettyDates));
1011             pw.printPair("totalTimeFS",
1012                     formatElapsedTime(usageStats.mTotalTimeForegroundServiceUsed, prettyDates));
1013             pw.printPair("lastTimeFS",
1014                     formatDateTime(usageStats.mLastTimeForegroundServiceUsed, prettyDates));
1015             pw.printPair("appLaunchCount", usageStats.mAppLaunchCount);
1016             pw.println();
1017         }
1018         pw.decreaseIndent();
1019 
1020         pw.println();
1021         pw.println("ChooserCounts");
1022         pw.increaseIndent();
1023         for (UsageStats usageStats : pkgStats.values()) {
1024             if (!CollectionUtils.isEmpty(pkgs) && !pkgs.contains(usageStats.mPackageName)) {
1025                 continue;
1026             }
1027             pw.printPair("package", usageStats.mPackageName);
1028             if (usageStats.mChooserCounts != null) {
1029                 final int chooserCountSize = usageStats.mChooserCounts.size();
1030                 for (int i = 0; i < chooserCountSize; i++) {
1031                     final String action = usageStats.mChooserCounts.keyAt(i);
1032                     final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i);
1033                     final int annotationSize = counts.size();
1034                     for (int j = 0; j < annotationSize; j++) {
1035                         final String key = counts.keyAt(j);
1036                         final int count = counts.valueAt(j);
1037                         if (count != 0) {
1038                             pw.printPair("ChooserCounts", action + ":" + key + " is " +
1039                                     Integer.toString(count));
1040                             pw.println();
1041                         }
1042                     }
1043                 }
1044             }
1045             pw.println();
1046         }
1047         pw.decreaseIndent();
1048 
1049         if (CollectionUtils.isEmpty(pkgs)) {
1050             pw.println("configurations");
1051             pw.increaseIndent();
1052             final ArrayMap<Configuration, ConfigurationStats> configStats = stats.configurations;
1053             final int configCount = configStats.size();
1054             for (int i = 0; i < configCount; i++) {
1055                 final ConfigurationStats config = configStats.valueAt(i);
1056                 pw.printPair("config", Configuration.resourceQualifierString(
1057                         config.mConfiguration));
1058                 pw.printPair("totalTime", formatElapsedTime(config.mTotalTimeActive, prettyDates));
1059                 pw.printPair("lastTime", formatDateTime(config.mLastTimeActive, prettyDates));
1060                 pw.printPair("count", config.mActivationCount);
1061                 pw.println();
1062             }
1063             pw.decreaseIndent();
1064             pw.println("event aggregations");
1065             pw.increaseIndent();
1066             printEventAggregation(pw, "screen-interactive", stats.interactiveTracker,
1067                     prettyDates);
1068             printEventAggregation(pw, "screen-non-interactive", stats.nonInteractiveTracker,
1069                     prettyDates);
1070             printEventAggregation(pw, "keyguard-shown", stats.keyguardShownTracker,
1071                     prettyDates);
1072             printEventAggregation(pw, "keyguard-hidden", stats.keyguardHiddenTracker,
1073                     prettyDates);
1074             pw.decreaseIndent();
1075         }
1076 
1077         // The last 24 hours of events is already printed in the non checkin dump
1078         // No need to repeat here.
1079         if (!skipEvents) {
1080             pw.println("events");
1081             pw.increaseIndent();
1082             final EventList events = stats.events;
1083             final int eventCount = events != null ? events.size() : 0;
1084             for (int i = 0; i < eventCount; i++) {
1085                 final Event event = events.get(i);
1086                 if (!CollectionUtils.isEmpty(pkgs) && !pkgs.contains(event.mPackage)) {
1087                     continue;
1088                 }
1089                 printEvent(pw, event, prettyDates);
1090             }
1091             pw.decreaseIndent();
1092         }
1093         pw.decreaseIndent();
1094     }
1095 
intervalToString(int interval)1096     public static String intervalToString(int interval) {
1097         switch (interval) {
1098             case INTERVAL_DAILY:
1099                 return "daily";
1100             case INTERVAL_WEEKLY:
1101                 return "weekly";
1102             case INTERVAL_MONTHLY:
1103                 return "monthly";
1104             case INTERVAL_YEARLY:
1105                 return "yearly";
1106             default:
1107                 return "?";
1108         }
1109     }
1110 
stringToInterval(String interval)1111     private static int stringToInterval(String interval) {
1112         switch (interval.toLowerCase()) {
1113             case "daily":
1114                 return INTERVAL_DAILY;
1115             case "weekly":
1116                 return INTERVAL_WEEKLY;
1117             case "monthly":
1118                 return INTERVAL_MONTHLY;
1119             case "yearly":
1120                 return INTERVAL_YEARLY;
1121             default:
1122                 return -1;
1123         }
1124     }
1125 
eventToString(int eventType)1126     private static String eventToString(int eventType) {
1127         switch (eventType) {
1128             case Event.NONE:
1129                 return "NONE";
1130             case Event.ACTIVITY_PAUSED:
1131                 return "ACTIVITY_PAUSED";
1132             case Event.ACTIVITY_RESUMED:
1133                 return "ACTIVITY_RESUMED";
1134             case Event.FOREGROUND_SERVICE_START:
1135                 return "FOREGROUND_SERVICE_START";
1136             case Event.FOREGROUND_SERVICE_STOP:
1137                 return "FOREGROUND_SERVICE_STOP";
1138             case Event.ACTIVITY_STOPPED:
1139                 return "ACTIVITY_STOPPED";
1140             case Event.END_OF_DAY:
1141                 return "END_OF_DAY";
1142             case Event.ROLLOVER_FOREGROUND_SERVICE:
1143                 return "ROLLOVER_FOREGROUND_SERVICE";
1144             case Event.CONTINUE_PREVIOUS_DAY:
1145                 return "CONTINUE_PREVIOUS_DAY";
1146             case Event.CONTINUING_FOREGROUND_SERVICE:
1147                 return "CONTINUING_FOREGROUND_SERVICE";
1148             case Event.CONFIGURATION_CHANGE:
1149                 return "CONFIGURATION_CHANGE";
1150             case Event.SYSTEM_INTERACTION:
1151                 return "SYSTEM_INTERACTION";
1152             case Event.USER_INTERACTION:
1153                 return "USER_INTERACTION";
1154             case Event.SHORTCUT_INVOCATION:
1155                 return "SHORTCUT_INVOCATION";
1156             case Event.CHOOSER_ACTION:
1157                 return "CHOOSER_ACTION";
1158             case Event.NOTIFICATION_SEEN:
1159                 return "NOTIFICATION_SEEN";
1160             case Event.STANDBY_BUCKET_CHANGED:
1161                 return "STANDBY_BUCKET_CHANGED";
1162             case Event.NOTIFICATION_INTERRUPTION:
1163                 return "NOTIFICATION_INTERRUPTION";
1164             case Event.SLICE_PINNED:
1165                 return "SLICE_PINNED";
1166             case Event.SLICE_PINNED_PRIV:
1167                 return "SLICE_PINNED_PRIV";
1168             case Event.SCREEN_INTERACTIVE:
1169                 return "SCREEN_INTERACTIVE";
1170             case Event.SCREEN_NON_INTERACTIVE:
1171                 return "SCREEN_NON_INTERACTIVE";
1172             case Event.KEYGUARD_SHOWN:
1173                 return "KEYGUARD_SHOWN";
1174             case Event.KEYGUARD_HIDDEN:
1175                 return "KEYGUARD_HIDDEN";
1176             case Event.DEVICE_SHUTDOWN:
1177                 return "DEVICE_SHUTDOWN";
1178             case Event.DEVICE_STARTUP:
1179                 return "DEVICE_STARTUP";
1180             case Event.USER_UNLOCKED:
1181                 return "USER_UNLOCKED";
1182             case Event.USER_STOPPED:
1183                 return "USER_STOPPED";
1184             case Event.LOCUS_ID_SET:
1185                 return "LOCUS_ID_SET";
1186             case Event.APP_COMPONENT_USED:
1187                 return "APP_COMPONENT_USED";
1188             default:
1189                 return "UNKNOWN_TYPE_" + eventType;
1190         }
1191     }
1192 
getBackupPayload(String key)1193     byte[] getBackupPayload(String key){
1194         checkAndGetTimeLocked();
1195         persistActiveStats();
1196         return mDatabase.getBackupPayload(key);
1197     }
1198 
applyRestoredPayload(String key, byte[] payload)1199     void applyRestoredPayload(String key, byte[] payload){
1200         checkAndGetTimeLocked();
1201         mDatabase.applyRestoredPayload(key, payload);
1202     }
1203 }
1204