1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.usage; 17 18 import android.app.usage.ConfigurationStats; 19 import android.app.usage.UsageEvents; 20 import android.app.usage.UsageEvents.Event.UserInteractionEventExtrasToken; 21 import android.app.usage.UsageStats; 22 import android.content.res.Configuration; 23 import android.os.PersistableBundle; 24 import android.util.Pair; 25 import android.util.Slog; 26 import android.util.SparseArray; 27 import android.util.SparseIntArray; 28 import android.util.proto.ProtoInputStream; 29 import android.util.proto.ProtoOutputStream; 30 31 import java.io.ByteArrayInputStream; 32 import java.io.ByteArrayOutputStream; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.io.OutputStream; 36 import java.util.ArrayList; 37 import java.util.LinkedList; 38 import java.util.Map; 39 import java.util.concurrent.TimeUnit; 40 41 /** 42 * UsageStats reader/writer V2 for Protocol Buffer format. 43 */ 44 final class UsageStatsProtoV2 { 45 private static final String TAG = "UsageStatsProtoV2"; 46 47 private static final long ONE_HOUR_MS = TimeUnit.HOURS.toMillis(1); 48 49 // Static-only utility class. UsageStatsProtoV2()50 private UsageStatsProtoV2() {} 51 parseUsageStats(ProtoInputStream proto, final long beginTime)52 private static UsageStats parseUsageStats(ProtoInputStream proto, final long beginTime) 53 throws IOException { 54 UsageStats stats = new UsageStats(); 55 // Time attributes stored is an offset of the beginTime. 56 while (true) { 57 switch (proto.nextField()) { 58 case (int) UsageStatsObfuscatedProto.PACKAGE_TOKEN: 59 stats.mPackageToken = proto.readInt( 60 UsageStatsObfuscatedProto.PACKAGE_TOKEN) - 1; 61 break; 62 case (int) UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS: 63 stats.mLastTimeUsed = beginTime + proto.readLong( 64 UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS); 65 break; 66 case (int) UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS: 67 stats.mTotalTimeInForeground = proto.readLong( 68 UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS); 69 break; 70 case (int) UsageStatsObfuscatedProto.APP_LAUNCH_COUNT: 71 stats.mAppLaunchCount = proto.readInt( 72 UsageStatsObfuscatedProto.APP_LAUNCH_COUNT); 73 break; 74 case (int) UsageStatsObfuscatedProto.CHOOSER_ACTIONS: 75 try { 76 final long token = proto.start(UsageStatsObfuscatedProto.CHOOSER_ACTIONS); 77 loadChooserCounts(proto, stats); 78 proto.end(token); 79 } catch (IOException e) { 80 Slog.e(TAG, "Unable to read chooser counts for " + stats.mPackageToken); 81 } 82 break; 83 case (int) UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS: 84 stats.mLastTimeForegroundServiceUsed = beginTime + proto.readLong( 85 UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS); 86 break; 87 case (int) UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS: 88 stats.mTotalTimeForegroundServiceUsed = proto.readLong( 89 UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS); 90 break; 91 case (int) UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS: 92 stats.mLastTimeVisible = beginTime + proto.readLong( 93 UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS); 94 break; 95 case (int) UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS: 96 stats.mTotalTimeVisible = proto.readLong( 97 UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS); 98 break; 99 case (int) UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS: 100 stats.mLastTimeComponentUsed = beginTime + proto.readLong( 101 UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS); 102 break; 103 case ProtoInputStream.NO_MORE_FIELDS: 104 return stats; 105 } 106 } 107 } 108 loadCountAndTime(ProtoInputStream proto, long fieldId, IntervalStats.EventTracker tracker)109 private static void loadCountAndTime(ProtoInputStream proto, long fieldId, 110 IntervalStats.EventTracker tracker) { 111 try { 112 final long token = proto.start(fieldId); 113 while (true) { 114 switch (proto.nextField()) { 115 case (int) IntervalStatsObfuscatedProto.CountAndTime.COUNT: 116 tracker.count = proto.readInt( 117 IntervalStatsObfuscatedProto.CountAndTime.COUNT); 118 break; 119 case (int) IntervalStatsObfuscatedProto.CountAndTime.TIME_MS: 120 tracker.duration = proto.readLong( 121 IntervalStatsObfuscatedProto.CountAndTime.TIME_MS); 122 break; 123 case ProtoInputStream.NO_MORE_FIELDS: 124 proto.end(token); 125 return; 126 } 127 } 128 } catch (IOException e) { 129 Slog.e(TAG, "Unable to read event tracker " + fieldId, e); 130 } 131 } 132 loadChooserCounts(ProtoInputStream proto, UsageStats usageStats)133 private static void loadChooserCounts(ProtoInputStream proto, UsageStats usageStats) 134 throws IOException { 135 int actionToken; 136 SparseIntArray counts; 137 if (proto.nextField(UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN)) { 138 // Fast path; this should work for most cases since the action token is written first 139 actionToken = proto.readInt(UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN) - 1; 140 counts = usageStats.mChooserCountsObfuscated.get(actionToken); 141 if (counts == null) { 142 counts = new SparseIntArray(); 143 usageStats.mChooserCountsObfuscated.put(actionToken, counts); 144 } 145 } else { 146 counts = new SparseIntArray(); 147 } 148 149 while (true) { 150 switch (proto.nextField()) { 151 case (int) UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN: 152 // Fast path failed for some reason, add the SparseIntArray object to usageStats 153 actionToken = proto.readInt( 154 UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN) - 1; 155 usageStats.mChooserCountsObfuscated.put(actionToken, counts); 156 break; 157 case (int) UsageStatsObfuscatedProto.ChooserAction.COUNTS: 158 final long token = proto.start(UsageStatsObfuscatedProto.ChooserAction.COUNTS); 159 loadCountsForAction(proto, counts); 160 proto.end(token); 161 break; 162 case ProtoInputStream.NO_MORE_FIELDS: 163 return; // if the action was never read, the loaded counts will be ignored. 164 } 165 } 166 } 167 loadCountsForAction(ProtoInputStream proto, SparseIntArray counts)168 private static void loadCountsForAction(ProtoInputStream proto, SparseIntArray counts) 169 throws IOException { 170 int categoryToken = PackagesTokenData.UNASSIGNED_TOKEN; 171 int count = 0; 172 while (true) { 173 switch (proto.nextField()) { 174 case (int) UsageStatsObfuscatedProto.ChooserAction.CategoryCount.CATEGORY_TOKEN: 175 categoryToken = proto.readInt( 176 UsageStatsObfuscatedProto.ChooserAction.CategoryCount.CATEGORY_TOKEN) 177 - 1; 178 break; 179 case (int) UsageStatsObfuscatedProto.ChooserAction.CategoryCount.COUNT: 180 count = proto.readInt( 181 UsageStatsObfuscatedProto.ChooserAction.CategoryCount.COUNT); 182 break; 183 case ProtoInputStream.NO_MORE_FIELDS: 184 if (categoryToken != PackagesTokenData.UNASSIGNED_TOKEN) { 185 counts.put(categoryToken, count); 186 } 187 return; 188 } 189 } 190 } 191 loadConfigStats(ProtoInputStream proto, IntervalStats stats)192 private static void loadConfigStats(ProtoInputStream proto, IntervalStats stats) 193 throws IOException { 194 boolean configActive = false; 195 final Configuration config = new Configuration(); 196 ConfigurationStats configStats = new ConfigurationStats(); 197 if (proto.nextField(IntervalStatsObfuscatedProto.Configuration.CONFIG)) { 198 // Fast path; this should work since the configuration is written first 199 config.readFromProto(proto, IntervalStatsObfuscatedProto.Configuration.CONFIG); 200 configStats = stats.getOrCreateConfigurationStats(config); 201 } 202 203 while (true) { 204 switch (proto.nextField()) { 205 case (int) IntervalStatsObfuscatedProto.Configuration.CONFIG: 206 // Fast path failed from some reason, add ConfigStats object to statsOut now 207 config.readFromProto(proto, IntervalStatsObfuscatedProto.Configuration.CONFIG); 208 final ConfigurationStats temp = stats.getOrCreateConfigurationStats(config); 209 temp.mLastTimeActive = configStats.mLastTimeActive; 210 temp.mTotalTimeActive = configStats.mTotalTimeActive; 211 temp.mActivationCount = configStats.mActivationCount; 212 configStats = temp; 213 break; 214 case (int) IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS: 215 configStats.mLastTimeActive = stats.beginTime + proto.readLong( 216 IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS); 217 break; 218 case (int) IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS: 219 configStats.mTotalTimeActive = proto.readLong( 220 IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS); 221 break; 222 case (int) IntervalStatsObfuscatedProto.Configuration.COUNT: 223 configStats.mActivationCount = proto.readInt( 224 IntervalStatsObfuscatedProto.Configuration.COUNT); 225 break; 226 case (int) IntervalStatsObfuscatedProto.Configuration.ACTIVE: 227 configActive = proto.readBoolean( 228 IntervalStatsObfuscatedProto.Configuration.ACTIVE); 229 break; 230 case ProtoInputStream.NO_MORE_FIELDS: 231 if (configActive) { 232 stats.activeConfiguration = configStats.mConfiguration; 233 } 234 return; 235 } 236 } 237 } 238 parseEvent(ProtoInputStream proto, long beginTime)239 private static UsageEvents.Event parseEvent(ProtoInputStream proto, long beginTime) 240 throws IOException { 241 final UsageEvents.Event event = new UsageEvents.Event(); 242 while (true) { 243 switch (proto.nextField()) { 244 case (int) EventObfuscatedProto.PACKAGE_TOKEN: 245 event.mPackageToken = proto.readInt(EventObfuscatedProto.PACKAGE_TOKEN) - 1; 246 break; 247 case (int) EventObfuscatedProto.CLASS_TOKEN: 248 event.mClassToken = proto.readInt(EventObfuscatedProto.CLASS_TOKEN) - 1; 249 break; 250 case (int) EventObfuscatedProto.TIME_MS: 251 event.mTimeStamp = beginTime + proto.readLong(EventObfuscatedProto.TIME_MS); 252 break; 253 case (int) EventObfuscatedProto.FLAGS: 254 event.mFlags = proto.readInt(EventObfuscatedProto.FLAGS); 255 break; 256 case (int) EventObfuscatedProto.TYPE: 257 event.mEventType = proto.readInt(EventObfuscatedProto.TYPE); 258 break; 259 case (int) EventObfuscatedProto.CONFIG: 260 event.mConfiguration = new Configuration(); 261 event.mConfiguration.readFromProto(proto, EventObfuscatedProto.CONFIG); 262 break; 263 case (int) EventObfuscatedProto.SHORTCUT_ID_TOKEN: 264 event.mShortcutIdToken = proto.readInt( 265 EventObfuscatedProto.SHORTCUT_ID_TOKEN) - 1; 266 break; 267 case (int) EventObfuscatedProto.STANDBY_BUCKET: 268 event.mBucketAndReason = proto.readInt(EventObfuscatedProto.STANDBY_BUCKET); 269 break; 270 case (int) EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN: 271 event.mNotificationChannelIdToken = proto.readInt( 272 EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN) - 1; 273 break; 274 case (int) EventObfuscatedProto.INSTANCE_ID: 275 event.mInstanceId = proto.readInt(EventObfuscatedProto.INSTANCE_ID); 276 break; 277 case (int) EventObfuscatedProto.TASK_ROOT_PACKAGE_TOKEN: 278 event.mTaskRootPackageToken = proto.readInt( 279 EventObfuscatedProto.TASK_ROOT_PACKAGE_TOKEN) - 1; 280 break; 281 case (int) EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN: 282 event.mTaskRootClassToken = proto.readInt( 283 EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN) - 1; 284 break; 285 case (int) EventObfuscatedProto.LOCUS_ID_TOKEN: 286 event.mLocusIdToken = proto.readInt( 287 EventObfuscatedProto.LOCUS_ID_TOKEN) - 1; 288 break; 289 case (int) EventObfuscatedProto.INTERACTION_EXTRAS: 290 try { 291 final long interactionExtrasToken = proto.start( 292 EventObfuscatedProto.INTERACTION_EXTRAS); 293 event.mUserInteractionExtrasToken = parseUserInteractionEventExtras(proto); 294 proto.end(interactionExtrasToken); 295 } catch (IOException e) { 296 Slog.e(TAG, "Unable to read some user interaction extras from proto.", e); 297 } 298 break; 299 case ProtoInputStream.NO_MORE_FIELDS: 300 return event.mPackageToken == PackagesTokenData.UNASSIGNED_TOKEN ? null : event; 301 } 302 } 303 } 304 writeOffsetTimestamp(ProtoOutputStream proto, long fieldId, long timestamp, long beginTime)305 static void writeOffsetTimestamp(ProtoOutputStream proto, long fieldId, 306 long timestamp, long beginTime) { 307 // timestamps will only be written if they're after the begin time 308 // a grace period of one hour before the begin time is allowed because of rollover logic 309 final long rolloverGracePeriod = beginTime - ONE_HOUR_MS; 310 if (timestamp > rolloverGracePeriod) { 311 // time attributes are stored as an offset of the begin time (given offset) 312 proto.write(fieldId, getOffsetTimestamp(timestamp, beginTime)); 313 } 314 } 315 getOffsetTimestamp(long timestamp, long offset)316 static long getOffsetTimestamp(long timestamp, long offset) { 317 final long offsetTimestamp = timestamp - offset; 318 // add one ms to timestamp if 0 to ensure it's written to proto (default values are ignored) 319 return offsetTimestamp == 0 ? offsetTimestamp + 1 : offsetTimestamp; 320 } 321 writeUsageStats(ProtoOutputStream proto, final long beginTime, final UsageStats stats)322 private static void writeUsageStats(ProtoOutputStream proto, final long beginTime, 323 final UsageStats stats) throws IllegalArgumentException { 324 proto.write(UsageStatsObfuscatedProto.PACKAGE_TOKEN, stats.mPackageToken + 1); 325 writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS, 326 stats.mLastTimeUsed, beginTime); 327 proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS, stats.mTotalTimeInForeground); 328 writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS, 329 stats.mLastTimeForegroundServiceUsed, beginTime); 330 proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS, 331 stats.mTotalTimeForegroundServiceUsed); 332 writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS, 333 stats.mLastTimeVisible, beginTime); 334 proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS, stats.mTotalTimeVisible); 335 writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_COMPONENT_USED_MS, 336 stats.mLastTimeComponentUsed, beginTime); 337 proto.write(UsageStatsObfuscatedProto.APP_LAUNCH_COUNT, stats.mAppLaunchCount); 338 try { 339 writeChooserCounts(proto, stats); 340 } catch (IllegalArgumentException e) { 341 Slog.e(TAG, "Unable to write chooser counts for " + stats.mPackageName, e); 342 } 343 } 344 writeCountAndTime(ProtoOutputStream proto, long fieldId, int count, long time)345 private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count, 346 long time) throws IllegalArgumentException { 347 final long token = proto.start(fieldId); 348 proto.write(IntervalStatsObfuscatedProto.CountAndTime.COUNT, count); 349 proto.write(IntervalStatsObfuscatedProto.CountAndTime.TIME_MS, time); 350 proto.end(token); 351 } 352 writeChooserCounts(ProtoOutputStream proto, final UsageStats stats)353 private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats stats) 354 throws IllegalArgumentException { 355 if (stats == null || stats.mChooserCountsObfuscated.size() == 0) { 356 return; 357 } 358 final int chooserCountSize = stats.mChooserCountsObfuscated.size(); 359 for (int i = 0; i < chooserCountSize; i++) { 360 final int action = stats.mChooserCountsObfuscated.keyAt(i); 361 final SparseIntArray counts = stats.mChooserCountsObfuscated.valueAt(i); 362 if (counts == null || counts.size() == 0) { 363 continue; 364 } 365 final long token = proto.start(UsageStatsObfuscatedProto.CHOOSER_ACTIONS); 366 proto.write(UsageStatsObfuscatedProto.ChooserAction.ACTION_TOKEN, action + 1); 367 writeCountsForAction(proto, counts); 368 proto.end(token); 369 } 370 } 371 writeCountsForAction(ProtoOutputStream proto, SparseIntArray counts)372 private static void writeCountsForAction(ProtoOutputStream proto, SparseIntArray counts) 373 throws IllegalArgumentException { 374 final int countsSize = counts.size(); 375 for (int i = 0; i < countsSize; i++) { 376 final int category = counts.keyAt(i); 377 final int count = counts.valueAt(i); 378 if (count <= 0) { 379 continue; 380 } 381 final long token = proto.start(UsageStatsObfuscatedProto.ChooserAction.COUNTS); 382 proto.write(UsageStatsObfuscatedProto.ChooserAction.CategoryCount.CATEGORY_TOKEN, 383 category + 1); 384 proto.write(UsageStatsObfuscatedProto.ChooserAction.CategoryCount.COUNT, count); 385 proto.end(token); 386 } 387 } 388 writeConfigStats(ProtoOutputStream proto, final long statsBeginTime, final ConfigurationStats configStats, boolean isActive)389 private static void writeConfigStats(ProtoOutputStream proto, final long statsBeginTime, 390 final ConfigurationStats configStats, boolean isActive) 391 throws IllegalArgumentException { 392 configStats.mConfiguration.dumpDebug(proto, 393 IntervalStatsObfuscatedProto.Configuration.CONFIG); 394 writeOffsetTimestamp(proto, IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS, 395 configStats.mLastTimeActive, statsBeginTime); 396 proto.write(IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS, 397 configStats.mTotalTimeActive); 398 proto.write(IntervalStatsObfuscatedProto.Configuration.COUNT, configStats.mActivationCount); 399 proto.write(IntervalStatsObfuscatedProto.Configuration.ACTIVE, isActive); 400 } 401 writeEvent(ProtoOutputStream proto, final long statsBeginTime, final UsageEvents.Event event)402 private static void writeEvent(ProtoOutputStream proto, final long statsBeginTime, 403 final UsageEvents.Event event) throws IOException, IllegalArgumentException { 404 proto.write(EventObfuscatedProto.PACKAGE_TOKEN, event.mPackageToken + 1); 405 if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) { 406 proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1); 407 } 408 writeOffsetTimestamp(proto, EventObfuscatedProto.TIME_MS, event.mTimeStamp, statsBeginTime); 409 proto.write(EventObfuscatedProto.FLAGS, event.mFlags); 410 proto.write(EventObfuscatedProto.TYPE, event.mEventType); 411 proto.write(EventObfuscatedProto.INSTANCE_ID, event.mInstanceId); 412 if (event.mTaskRootPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) { 413 proto.write(EventObfuscatedProto.TASK_ROOT_PACKAGE_TOKEN, 414 event.mTaskRootPackageToken + 1); 415 } 416 if (event.mTaskRootClassToken != PackagesTokenData.UNASSIGNED_TOKEN) { 417 proto.write(EventObfuscatedProto.TASK_ROOT_CLASS_TOKEN, event.mTaskRootClassToken + 1); 418 } 419 switch (event.mEventType) { 420 case UsageEvents.Event.CONFIGURATION_CHANGE: 421 if (event.mConfiguration != null) { 422 event.mConfiguration.dumpDebug(proto, EventObfuscatedProto.CONFIG); 423 } 424 break; 425 case UsageEvents.Event.STANDBY_BUCKET_CHANGED: 426 if (event.mBucketAndReason != 0) { 427 proto.write(EventObfuscatedProto.STANDBY_BUCKET, event.mBucketAndReason); 428 } 429 break; 430 case UsageEvents.Event.SHORTCUT_INVOCATION: 431 if (event.mShortcutIdToken != PackagesTokenData.UNASSIGNED_TOKEN) { 432 proto.write(EventObfuscatedProto.SHORTCUT_ID_TOKEN, event.mShortcutIdToken + 1); 433 } 434 break; 435 case UsageEvents.Event.LOCUS_ID_SET: 436 if (event.mLocusIdToken != PackagesTokenData.UNASSIGNED_TOKEN) { 437 proto.write(EventObfuscatedProto.LOCUS_ID_TOKEN, event.mLocusIdToken + 1); 438 } 439 break; 440 case UsageEvents.Event.NOTIFICATION_INTERRUPTION: 441 if (event.mNotificationChannelIdToken != PackagesTokenData.UNASSIGNED_TOKEN) { 442 proto.write(EventObfuscatedProto.NOTIFICATION_CHANNEL_ID_TOKEN, 443 event.mNotificationChannelIdToken + 1); 444 } 445 break; 446 case UsageEvents.Event.USER_INTERACTION: 447 if (event.mUserInteractionExtrasToken != null) { 448 writeUserInteractionEventExtras(proto, EventObfuscatedProto.INTERACTION_EXTRAS, 449 event.mUserInteractionExtrasToken); 450 } 451 break; 452 } 453 } 454 455 /** 456 * Populates a tokenized version of interval stats from the input stream given. 457 * 458 * @param in the input stream from which to read events. 459 * @param stats the interval stats object which will be populated. 460 */ read(InputStream in, IntervalStats stats, boolean skipEvents)461 public static void read(InputStream in, IntervalStats stats, boolean skipEvents) 462 throws IOException { 463 final ProtoInputStream proto = new ProtoInputStream(in); 464 while (true) { 465 switch (proto.nextField()) { 466 case (int) IntervalStatsObfuscatedProto.END_TIME_MS: 467 stats.endTime = stats.beginTime + proto.readLong( 468 IntervalStatsObfuscatedProto.END_TIME_MS); 469 break; 470 case (int) IntervalStatsObfuscatedProto.MAJOR_VERSION: 471 stats.majorVersion = proto.readInt(IntervalStatsObfuscatedProto.MAJOR_VERSION); 472 break; 473 case (int) IntervalStatsObfuscatedProto.MINOR_VERSION: 474 stats.minorVersion = proto.readInt(IntervalStatsObfuscatedProto.MINOR_VERSION); 475 break; 476 case (int) IntervalStatsObfuscatedProto.INTERACTIVE: 477 loadCountAndTime(proto, IntervalStatsObfuscatedProto.INTERACTIVE, 478 stats.interactiveTracker); 479 break; 480 case (int) IntervalStatsObfuscatedProto.NON_INTERACTIVE: 481 loadCountAndTime(proto, IntervalStatsObfuscatedProto.NON_INTERACTIVE, 482 stats.nonInteractiveTracker); 483 break; 484 case (int) IntervalStatsObfuscatedProto.KEYGUARD_SHOWN: 485 loadCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_SHOWN, 486 stats.keyguardShownTracker); 487 break; 488 case (int) IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN: 489 loadCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN, 490 stats.keyguardHiddenTracker); 491 break; 492 case (int) IntervalStatsObfuscatedProto.PACKAGES: 493 try { 494 final long packagesToken = proto.start( 495 IntervalStatsObfuscatedProto.PACKAGES); 496 UsageStats usageStats = parseUsageStats(proto, stats.beginTime); 497 proto.end(packagesToken); 498 if (usageStats.mPackageToken != PackagesTokenData.UNASSIGNED_TOKEN) { 499 stats.packageStatsObfuscated.put(usageStats.mPackageToken, usageStats); 500 } 501 } catch (IOException e) { 502 Slog.e(TAG, "Unable to read some usage stats from proto.", e); 503 } 504 break; 505 case (int) IntervalStatsObfuscatedProto.CONFIGURATIONS: 506 try { 507 final long configsToken = proto.start( 508 IntervalStatsObfuscatedProto.CONFIGURATIONS); 509 loadConfigStats(proto, stats); 510 proto.end(configsToken); 511 } catch (IOException e) { 512 Slog.e(TAG, "Unable to read some configuration stats from proto.", e); 513 } 514 break; 515 case (int) IntervalStatsObfuscatedProto.EVENT_LOG: 516 if (skipEvents) { 517 break; 518 } 519 try { 520 final long eventsToken = proto.start( 521 IntervalStatsObfuscatedProto.EVENT_LOG); 522 UsageEvents.Event event = parseEvent(proto, stats.beginTime); 523 proto.end(eventsToken); 524 if (event != null) { 525 stats.events.insert(event); 526 } 527 } catch (IOException e) { 528 Slog.e(TAG, "Unable to read some events from proto.", e); 529 } 530 break; 531 case ProtoInputStream.NO_MORE_FIELDS: 532 // update the begin and end time stamps for all usage stats 533 final int usageStatsSize = stats.packageStatsObfuscated.size(); 534 for (int i = 0; i < usageStatsSize; i++) { 535 final UsageStats usageStats = stats.packageStatsObfuscated.valueAt(i); 536 usageStats.mBeginTimeStamp = stats.beginTime; 537 usageStats.mEndTimeStamp = stats.endTime; 538 } 539 return; 540 } 541 } 542 } 543 544 /** 545 * Writes the tokenized interval stats object to a ProtoBuf file. 546 * 547 * @param out the output stream to which to write the interval stats data. 548 * @param stats the interval stats object to write to the proto file. 549 */ write(OutputStream out, IntervalStats stats)550 public static void write(OutputStream out, IntervalStats stats) 551 throws IOException, IllegalArgumentException { 552 final ProtoOutputStream proto = new ProtoOutputStream(out); 553 proto.write(IntervalStatsObfuscatedProto.END_TIME_MS, 554 getOffsetTimestamp(stats.endTime, stats.beginTime)); 555 proto.write(IntervalStatsObfuscatedProto.MAJOR_VERSION, stats.majorVersion); 556 proto.write(IntervalStatsObfuscatedProto.MINOR_VERSION, stats.minorVersion); 557 558 try { 559 writeCountAndTime(proto, IntervalStatsObfuscatedProto.INTERACTIVE, 560 stats.interactiveTracker.count, stats.interactiveTracker.duration); 561 writeCountAndTime(proto, IntervalStatsObfuscatedProto.NON_INTERACTIVE, 562 stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration); 563 writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_SHOWN, 564 stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration); 565 writeCountAndTime(proto, IntervalStatsObfuscatedProto.KEYGUARD_HIDDEN, 566 stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration); 567 } catch (IllegalArgumentException e) { 568 Slog.e(TAG, "Unable to write some interval stats trackers to proto.", e); 569 } 570 571 final int statsCount = stats.packageStatsObfuscated.size(); 572 for (int i = 0; i < statsCount; i++) { 573 try { 574 final long token = proto.start(IntervalStatsObfuscatedProto.PACKAGES); 575 writeUsageStats(proto, stats.beginTime, stats.packageStatsObfuscated.valueAt(i)); 576 proto.end(token); 577 } catch (IllegalArgumentException e) { 578 Slog.e(TAG, "Unable to write some usage stats to proto.", e); 579 } 580 } 581 final int configCount = stats.configurations.size(); 582 for (int i = 0; i < configCount; i++) { 583 boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i)); 584 try { 585 final long token = proto.start(IntervalStatsObfuscatedProto.CONFIGURATIONS); 586 writeConfigStats(proto, stats.beginTime, stats.configurations.valueAt(i), active); 587 proto.end(token); 588 } catch (IllegalArgumentException e) { 589 Slog.e(TAG, "Unable to write some configuration stats to proto.", e); 590 } 591 } 592 final int eventCount = stats.events.size(); 593 for (int i = 0; i < eventCount; i++) { 594 try { 595 final long token = proto.start(IntervalStatsObfuscatedProto.EVENT_LOG); 596 writeEvent(proto, stats.beginTime, stats.events.get(i)); 597 proto.end(token); 598 } catch (IllegalArgumentException e) { 599 Slog.e(TAG, "Unable to write some events to proto.", e); 600 } 601 } 602 603 proto.flush(); 604 } 605 606 /***** Read/Write obfuscated packages data logic. *****/ 607 loadPackagesMap(ProtoInputStream proto, SparseArray<ArrayList<String>> tokensToPackagesMap)608 private static void loadPackagesMap(ProtoInputStream proto, 609 SparseArray<ArrayList<String>> tokensToPackagesMap) throws IOException { 610 int key = PackagesTokenData.UNASSIGNED_TOKEN; 611 final ArrayList<String> strings = new ArrayList<>(); 612 while (true) { 613 switch (proto.nextField()) { 614 case (int) ObfuscatedPackagesProto.PackagesMap.PACKAGE_TOKEN: 615 key = proto.readInt(ObfuscatedPackagesProto.PackagesMap.PACKAGE_TOKEN) - 1; 616 break; 617 case (int) ObfuscatedPackagesProto.PackagesMap.STRINGS: 618 strings.add(proto.readString(ObfuscatedPackagesProto.PackagesMap.STRINGS)); 619 break; 620 case ProtoInputStream.NO_MORE_FIELDS: 621 if (key != PackagesTokenData.UNASSIGNED_TOKEN) { 622 tokensToPackagesMap.put(key, strings); 623 } 624 return; 625 } 626 } 627 } 628 629 /** 630 * Populates the package mappings from the input stream given. 631 * 632 * @param in the input stream from which to read the mappings. 633 * @param packagesTokenData the packages data object to which the data will be read to. 634 */ readObfuscatedData(InputStream in, PackagesTokenData packagesTokenData)635 static void readObfuscatedData(InputStream in, PackagesTokenData packagesTokenData) 636 throws IOException { 637 final ProtoInputStream proto = new ProtoInputStream(in); 638 while (true) { 639 switch (proto.nextField()) { 640 case (int) ObfuscatedPackagesProto.COUNTER: 641 packagesTokenData.counter = proto.readInt(ObfuscatedPackagesProto.COUNTER); 642 break; 643 case (int) ObfuscatedPackagesProto.PACKAGES_MAP: 644 final long token = proto.start(ObfuscatedPackagesProto.PACKAGES_MAP); 645 loadPackagesMap(proto, packagesTokenData.tokensToPackagesMap); 646 proto.end(token); 647 break; 648 case ProtoInputStream.NO_MORE_FIELDS: 649 return; 650 } 651 } 652 } 653 654 /** 655 * Writes the packages mapping data to a ProtoBuf file. 656 * 657 * @param out the output stream to which to write the mappings. 658 * @param packagesTokenData the packages data object holding the data to write. 659 */ writeObfuscatedData(OutputStream out, PackagesTokenData packagesTokenData)660 static void writeObfuscatedData(OutputStream out, PackagesTokenData packagesTokenData) 661 throws IOException, IllegalArgumentException { 662 final ProtoOutputStream proto = new ProtoOutputStream(out); 663 proto.write(ObfuscatedPackagesProto.COUNTER, packagesTokenData.counter); 664 665 final int mapSize = packagesTokenData.tokensToPackagesMap.size(); 666 for (int i = 0; i < mapSize; i++) { 667 final long token = proto.start(ObfuscatedPackagesProto.PACKAGES_MAP); 668 int packageToken = packagesTokenData.tokensToPackagesMap.keyAt(i); 669 proto.write(ObfuscatedPackagesProto.PackagesMap.PACKAGE_TOKEN, packageToken + 1); 670 671 final ArrayList<String> strings = packagesTokenData.tokensToPackagesMap.valueAt(i); 672 final int listSize = strings.size(); 673 for (int j = 0; j < listSize; j++) { 674 proto.write(ObfuscatedPackagesProto.PackagesMap.STRINGS, strings.get(j)); 675 } 676 proto.end(token); 677 } 678 679 proto.flush(); 680 } 681 682 /***** Read/Write pending events logic. *****/ 683 parsePendingEvent(ProtoInputStream proto)684 private static UsageEvents.Event parsePendingEvent(ProtoInputStream proto) throws IOException { 685 final UsageEvents.Event event = new UsageEvents.Event(); 686 while (true) { 687 switch (proto.nextField()) { 688 case (int) PendingEventProto.PACKAGE_NAME: 689 event.mPackage = proto.readString(PendingEventProto.PACKAGE_NAME); 690 break; 691 case (int) PendingEventProto.CLASS_NAME: 692 event.mClass = proto.readString(PendingEventProto.CLASS_NAME); 693 break; 694 case (int) PendingEventProto.TIME_MS: 695 event.mTimeStamp = proto.readLong(PendingEventProto.TIME_MS); 696 break; 697 case (int) PendingEventProto.FLAGS: 698 event.mFlags = proto.readInt(PendingEventProto.FLAGS); 699 break; 700 case (int) PendingEventProto.TYPE: 701 event.mEventType = proto.readInt(PendingEventProto.TYPE); 702 break; 703 case (int) PendingEventProto.CONFIG: 704 event.mConfiguration = new Configuration(); 705 event.mConfiguration.readFromProto(proto, PendingEventProto.CONFIG); 706 break; 707 case (int) PendingEventProto.SHORTCUT_ID: 708 event.mShortcutId = proto.readString(PendingEventProto.SHORTCUT_ID); 709 break; 710 case (int) PendingEventProto.STANDBY_BUCKET: 711 event.mBucketAndReason = proto.readInt(PendingEventProto.STANDBY_BUCKET); 712 break; 713 case (int) PendingEventProto.NOTIFICATION_CHANNEL_ID: 714 event.mNotificationChannelId = proto.readString( 715 PendingEventProto.NOTIFICATION_CHANNEL_ID); 716 break; 717 case (int) PendingEventProto.INSTANCE_ID: 718 event.mInstanceId = proto.readInt(PendingEventProto.INSTANCE_ID); 719 break; 720 case (int) PendingEventProto.TASK_ROOT_PACKAGE: 721 event.mTaskRootPackage = proto.readString(PendingEventProto.TASK_ROOT_PACKAGE); 722 break; 723 case (int) PendingEventProto.TASK_ROOT_CLASS: 724 event.mTaskRootClass = proto.readString(PendingEventProto.TASK_ROOT_CLASS); 725 break; 726 case (int) PendingEventProto.EXTRAS: 727 event.mExtras = parsePendingEventExtras(proto, PendingEventProto.EXTRAS); 728 break; 729 case ProtoInputStream.NO_MORE_FIELDS: 730 // Handle default values for certain events types 731 switch (event.mEventType) { 732 case UsageEvents.Event.CONFIGURATION_CHANGE: 733 if (event.mConfiguration == null) { 734 event.mConfiguration = new Configuration(); 735 } 736 break; 737 case UsageEvents.Event.SHORTCUT_INVOCATION: 738 if (event.mShortcutId == null) { 739 event.mShortcutId = ""; 740 } 741 break; 742 case UsageEvents.Event.NOTIFICATION_INTERRUPTION: 743 if (event.mNotificationChannelId == null) { 744 event.mNotificationChannelId = ""; 745 } 746 break; 747 } 748 return event.mPackage == null ? null : event; 749 } 750 } 751 } 752 753 /** 754 * Populates the list of pending events from the input stream given. 755 * 756 * @param in the input stream from which to read the pending events. 757 * @param events the list of pending events to populate. 758 */ readPendingEvents(InputStream in, LinkedList<UsageEvents.Event> events)759 static void readPendingEvents(InputStream in, LinkedList<UsageEvents.Event> events) 760 throws IOException { 761 final ProtoInputStream proto = new ProtoInputStream(in); 762 while (true) { 763 switch (proto.nextField()) { 764 case (int) IntervalStatsObfuscatedProto.PENDING_EVENTS: 765 try { 766 final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS); 767 UsageEvents.Event event = parsePendingEvent(proto); 768 proto.end(token); 769 if (event != null) { 770 events.add(event); 771 } 772 } catch (IOException e) { 773 Slog.e(TAG, "Unable to parse some pending events from proto.", e); 774 } 775 break; 776 case ProtoInputStream.NO_MORE_FIELDS: 777 return; 778 } 779 } 780 } 781 writePendingEvent(ProtoOutputStream proto, UsageEvents.Event event)782 private static void writePendingEvent(ProtoOutputStream proto, UsageEvents.Event event) 783 throws IOException, IllegalArgumentException { 784 proto.write(PendingEventProto.PACKAGE_NAME, event.mPackage); 785 if (event.mClass != null) { 786 proto.write(PendingEventProto.CLASS_NAME, event.mClass); 787 } 788 proto.write(PendingEventProto.TIME_MS, event.mTimeStamp); 789 proto.write(PendingEventProto.FLAGS, event.mFlags); 790 proto.write(PendingEventProto.TYPE, event.mEventType); 791 proto.write(PendingEventProto.INSTANCE_ID, event.mInstanceId); 792 if (event.mTaskRootPackage != null) { 793 proto.write(PendingEventProto.TASK_ROOT_PACKAGE, event.mTaskRootPackage); 794 } 795 if (event.mTaskRootClass != null) { 796 proto.write(PendingEventProto.TASK_ROOT_CLASS, event.mTaskRootClass); 797 } 798 switch (event.mEventType) { 799 case UsageEvents.Event.CONFIGURATION_CHANGE: 800 if (event.mConfiguration != null) { 801 event.mConfiguration.dumpDebug(proto, PendingEventProto.CONFIG); 802 } 803 break; 804 case UsageEvents.Event.STANDBY_BUCKET_CHANGED: 805 if (event.mBucketAndReason != 0) { 806 proto.write(PendingEventProto.STANDBY_BUCKET, event.mBucketAndReason); 807 } 808 break; 809 case UsageEvents.Event.SHORTCUT_INVOCATION: 810 if (event.mShortcutId != null) { 811 proto.write(PendingEventProto.SHORTCUT_ID, event.mShortcutId); 812 } 813 break; 814 case UsageEvents.Event.NOTIFICATION_INTERRUPTION: 815 if (event.mNotificationChannelId != null) { 816 proto.write(PendingEventProto.NOTIFICATION_CHANNEL_ID, 817 event.mNotificationChannelId); 818 } 819 break; 820 case UsageEvents.Event.USER_INTERACTION: 821 if (event.mExtras != null && event.mExtras.size() != 0) { 822 writePendingEventExtras(proto, PendingEventProto.EXTRAS, event.mExtras); 823 } 824 break; 825 } 826 } 827 828 /** 829 * Writes the pending events to a ProtoBuf file. 830 * 831 * @param out the output stream to which to write the pending events. 832 * @param events the list of pending events. 833 */ writePendingEvents(OutputStream out, LinkedList<UsageEvents.Event> events)834 static void writePendingEvents(OutputStream out, LinkedList<UsageEvents.Event> events) 835 throws IOException, IllegalArgumentException { 836 final ProtoOutputStream proto = new ProtoOutputStream(out); 837 final int eventCount = events.size(); 838 for (int i = 0; i < eventCount; i++) { 839 try { 840 final long token = proto.start(IntervalStatsObfuscatedProto.PENDING_EVENTS); 841 writePendingEvent(proto, events.get(i)); 842 proto.end(token); 843 } catch (IllegalArgumentException e) { 844 Slog.e(TAG, "Unable to write some pending events to proto.", e); 845 } 846 } 847 proto.flush(); 848 } 849 parseGlobalComponentUsage(ProtoInputStream proto)850 private static Pair<String, Long> parseGlobalComponentUsage(ProtoInputStream proto) 851 throws IOException { 852 String packageName = ""; 853 long time = 0; 854 while (true) { 855 switch (proto.nextField()) { 856 case (int) IntervalStatsObfuscatedProto.PackageUsage.PACKAGE_NAME: 857 packageName = proto.readString( 858 IntervalStatsObfuscatedProto.PackageUsage.PACKAGE_NAME); 859 break; 860 case (int) IntervalStatsObfuscatedProto.PackageUsage.TIME_MS: 861 time = proto.readLong(IntervalStatsObfuscatedProto.PackageUsage.TIME_MS); 862 break; 863 case ProtoInputStream.NO_MORE_FIELDS: 864 return new Pair<>(packageName, time); 865 } 866 } 867 } 868 869 /** 870 * Populates the map of latest package usage from the input stream given. 871 * 872 * @param in the input stream from which to read the package usage. 873 * @param lastTimeComponentUsedGlobal the map of package's global component usage to populate. 874 */ readGlobalComponentUsage(InputStream in, Map<String, Long> lastTimeComponentUsedGlobal)875 static void readGlobalComponentUsage(InputStream in, 876 Map<String, Long> lastTimeComponentUsedGlobal) throws IOException { 877 final ProtoInputStream proto = new ProtoInputStream(in); 878 while (true) { 879 switch (proto.nextField()) { 880 case (int) IntervalStatsObfuscatedProto.PACKAGE_USAGE: 881 try { 882 final long token = proto.start(IntervalStatsObfuscatedProto.PACKAGE_USAGE); 883 final Pair<String, Long> usage = parseGlobalComponentUsage(proto); 884 proto.end(token); 885 if (!usage.first.isEmpty() && usage.second > 0) { 886 lastTimeComponentUsedGlobal.put(usage.first, usage.second); 887 } 888 } catch (IOException e) { 889 Slog.e(TAG, "Unable to parse some package usage from proto.", e); 890 } 891 break; 892 case ProtoInputStream.NO_MORE_FIELDS: 893 return; 894 } 895 } 896 } 897 898 /** 899 * Writes the user-agnostic last time package usage to a ProtoBuf file. 900 * 901 * @param out the output stream to which to write the package usage 902 * @param lastTimeComponentUsedGlobal the map storing the global component usage of packages 903 */ writeGlobalComponentUsage(OutputStream out, Map<String, Long> lastTimeComponentUsedGlobal)904 static void writeGlobalComponentUsage(OutputStream out, 905 Map<String, Long> lastTimeComponentUsedGlobal) { 906 final ProtoOutputStream proto = new ProtoOutputStream(out); 907 final Map.Entry<String, Long>[] entries = 908 (Map.Entry<String, Long>[]) lastTimeComponentUsedGlobal.entrySet().toArray(); 909 final int size = entries.length; 910 for (int i = 0; i < size; ++i) { 911 if (entries[i].getValue() <= 0) continue; 912 final long token = proto.start(IntervalStatsObfuscatedProto.PACKAGE_USAGE); 913 proto.write(IntervalStatsObfuscatedProto.PackageUsage.PACKAGE_NAME, 914 entries[i].getKey()); 915 proto.write(IntervalStatsObfuscatedProto.PackageUsage.TIME_MS, entries[i].getValue()); 916 proto.end(token); 917 } 918 } 919 parseUserInteractionEventExtras( ProtoInputStream proto)920 private static UserInteractionEventExtrasToken parseUserInteractionEventExtras( 921 ProtoInputStream proto) throws IOException { 922 UserInteractionEventExtrasToken interactionExtrasToken = 923 new UserInteractionEventExtrasToken(); 924 while (true) { 925 switch (proto.nextField()) { 926 case (int) ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN: 927 interactionExtrasToken.mCategoryToken = proto.readInt( 928 ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN) - 1; 929 break; 930 case (int) ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN: 931 interactionExtrasToken.mActionToken = proto.readInt( 932 ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN) - 1; 933 break; 934 case ProtoInputStream.NO_MORE_FIELDS: 935 return interactionExtrasToken; 936 } 937 } 938 } 939 writeUserInteractionEventExtras(ProtoOutputStream proto, long fieldId, UserInteractionEventExtrasToken interactionExtras)940 static void writeUserInteractionEventExtras(ProtoOutputStream proto, long fieldId, 941 UserInteractionEventExtrasToken interactionExtras) { 942 final long token = proto.start(fieldId); 943 proto.write(ObfuscatedUserInteractionExtrasProto.CATEGORY_TOKEN, 944 interactionExtras.mCategoryToken + 1); 945 proto.write(ObfuscatedUserInteractionExtrasProto.ACTION_TOKEN, 946 interactionExtras.mActionToken + 1); 947 proto.end(token); 948 } 949 950 /** 951 * Populates the extra details for pending interaction event from the protobuf stream. 952 */ parsePendingEventExtras(ProtoInputStream proto, long fieldId)953 private static PersistableBundle parsePendingEventExtras(ProtoInputStream proto, long fieldId) 954 throws IOException { 955 return PersistableBundle.readFromStream(new ByteArrayInputStream(proto.readBytes(fieldId))); 956 } 957 958 /** 959 * Write the extra details for pending interaction event to a protobuf stream. 960 */ writePendingEventExtras(ProtoOutputStream proto, long fieldId, PersistableBundle eventExtras)961 static void writePendingEventExtras(ProtoOutputStream proto, long fieldId, 962 PersistableBundle eventExtras) throws IOException { 963 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 964 eventExtras.writeToStream(baos); 965 proto.write(fieldId, baos.toByteArray()); 966 } 967 } 968