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