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