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