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