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