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