| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.android.server.usage; |
| |
| import android.app.usage.ConfigurationStats; |
| import android.app.usage.UsageEvents; |
| import android.app.usage.UsageStats; |
| import android.content.res.Configuration; |
| import android.util.ArrayMap; |
| import android.util.Slog; |
| import android.util.proto.ProtoInputStream; |
| import android.util.proto.ProtoOutputStream; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.ProtocolException; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * UsageStats reader/writer for Protocol Buffer format |
| */ |
| final class UsageStatsProto { |
| private static String TAG = "UsageStatsProto"; |
| |
| // Static-only utility class. |
| private UsageStatsProto() {} |
| |
| private static List<String> readStringPool(ProtoInputStream proto) throws IOException { |
| |
| final long token = proto.start(IntervalStatsProto.STRINGPOOL); |
| List<String> stringPool; |
| if (proto.nextField(IntervalStatsProto.StringPool.SIZE)) { |
| stringPool = new ArrayList(proto.readInt(IntervalStatsProto.StringPool.SIZE)); |
| } else { |
| stringPool = new ArrayList(); |
| } |
| while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { |
| switch (proto.getFieldNumber()) { |
| case (int) IntervalStatsProto.StringPool.STRINGS: |
| stringPool.add(proto.readString(IntervalStatsProto.StringPool.STRINGS)); |
| break; |
| } |
| } |
| proto.end(token); |
| return stringPool; |
| } |
| |
| private static void loadUsageStats(ProtoInputStream proto, long fieldId, |
| IntervalStats statsOut, List<String> stringPool) |
| throws IOException { |
| |
| final long token = proto.start(fieldId); |
| UsageStats stats; |
| if (proto.nextField(IntervalStatsProto.UsageStats.PACKAGE_INDEX)) { |
| // Fast path reading the package name index. Most cases this should work since it is |
| // written first |
| stats = statsOut.getOrCreateUsageStats( |
| stringPool.get(proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1)); |
| } else if (proto.nextField(IntervalStatsProto.UsageStats.PACKAGE)) { |
| // No package index, try package name instead |
| stats = statsOut.getOrCreateUsageStats( |
| proto.readString(IntervalStatsProto.UsageStats.PACKAGE)); |
| } else { |
| // Temporarily store collected data to a UsageStats object. This is not efficient. |
| stats = new UsageStats(); |
| } |
| |
| while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { |
| switch (proto.getFieldNumber()) { |
| case (int) IntervalStatsProto.UsageStats.PACKAGE: |
| // Fast track failed from some reason, add UsageStats object to statsOut now |
| UsageStats tempPackage = statsOut.getOrCreateUsageStats( |
| proto.readString(IntervalStatsProto.UsageStats.PACKAGE)); |
| tempPackage.mLastTimeUsed = stats.mLastTimeUsed; |
| tempPackage.mTotalTimeInForeground = stats.mTotalTimeInForeground; |
| tempPackage.mLastEvent = stats.mLastEvent; |
| tempPackage.mAppLaunchCount = stats.mAppLaunchCount; |
| stats = tempPackage; |
| break; |
| case (int) IntervalStatsProto.UsageStats.PACKAGE_INDEX: |
| // Fast track failed from some reason, add UsageStats object to statsOut now |
| UsageStats tempPackageIndex = statsOut.getOrCreateUsageStats(stringPool.get( |
| proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1)); |
| tempPackageIndex.mLastTimeUsed = stats.mLastTimeUsed; |
| tempPackageIndex.mTotalTimeInForeground = stats.mTotalTimeInForeground; |
| tempPackageIndex.mLastEvent = stats.mLastEvent; |
| tempPackageIndex.mAppLaunchCount = stats.mAppLaunchCount; |
| stats = tempPackageIndex; |
| break; |
| case (int) IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS: |
| // Time attributes stored is an offset of the beginTime. |
| stats.mLastTimeUsed = statsOut.beginTime + proto.readLong( |
| IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS); |
| break; |
| case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS: |
| stats.mTotalTimeInForeground = proto.readLong( |
| IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS); |
| break; |
| case (int) IntervalStatsProto.UsageStats.LAST_EVENT: |
| stats.mLastEvent = |
| proto.readInt(IntervalStatsProto.UsageStats.LAST_EVENT); |
| break; |
| case (int) IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT: |
| stats.mAppLaunchCount = proto.readInt( |
| IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT); |
| break; |
| case (int) IntervalStatsProto.UsageStats.CHOOSER_ACTIONS: |
| final long chooserToken = proto.start( |
| IntervalStatsProto.UsageStats.CHOOSER_ACTIONS); |
| loadChooserCounts(proto, stats); |
| proto.end(chooserToken); |
| break; |
| case (int) IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS: |
| // Time attributes stored is an offset of the beginTime. |
| stats.mLastTimeForegroundServiceUsed = statsOut.beginTime + proto.readLong( |
| IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS); |
| break; |
| case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS: |
| stats.mTotalTimeForegroundServiceUsed = proto.readLong( |
| IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS); |
| break; |
| case (int) IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS: |
| // Time attributes stored is an offset of the beginTime. |
| stats.mLastTimeVisible = statsOut.beginTime + proto.readLong( |
| IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS); |
| break; |
| case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS: |
| stats.mTotalTimeVisible = proto.readLong( |
| IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS); |
| break; |
| } |
| } |
| if (stats.mLastTimeUsed == 0) { |
| // mLastTimeUsed was not assigned, assume default value of 0 plus beginTime; |
| stats.mLastTimeUsed = statsOut.beginTime; |
| } |
| proto.end(token); |
| } |
| |
| private static void loadCountAndTime(ProtoInputStream proto, long fieldId, |
| IntervalStats.EventTracker tracker) throws IOException { |
| final long token = proto.start(fieldId); |
| while (true) { |
| switch (proto.nextField()) { |
| case (int) IntervalStatsProto.CountAndTime.COUNT: |
| tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT); |
| break; |
| case (int) IntervalStatsProto.CountAndTime.TIME_MS: |
| tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS); |
| break; |
| case ProtoInputStream.NO_MORE_FIELDS: |
| proto.end(token); |
| return; |
| } |
| } |
| } |
| |
| private static void loadChooserCounts(ProtoInputStream proto, UsageStats usageStats) |
| throws IOException { |
| if (usageStats.mChooserCounts == null) { |
| usageStats.mChooserCounts = new ArrayMap<>(); |
| } |
| String action = null; |
| ArrayMap<String, Integer> counts; |
| if (proto.nextField(IntervalStatsProto.UsageStats.ChooserAction.NAME)) { |
| // Fast path reading the action name. Most cases this should work since it is written |
| // first |
| action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME); |
| counts = usageStats.mChooserCounts.get(action); |
| if (counts == null) { |
| counts = new ArrayMap<>(); |
| usageStats.mChooserCounts.put(action, counts); |
| } |
| } else { |
| // Temporarily store collected data to an ArrayMap. This is not efficient. |
| counts = new ArrayMap<>(); |
| } |
| |
| while (true) { |
| switch (proto.nextField()) { |
| case (int) IntervalStatsProto.UsageStats.ChooserAction.NAME: |
| // Fast path failed from some reason, add the ArrayMap object to usageStats now |
| action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME); |
| usageStats.mChooserCounts.put(action, counts); |
| break; |
| case (int) IntervalStatsProto.UsageStats.ChooserAction.COUNTS: |
| final long token = proto.start( |
| IntervalStatsProto.UsageStats.ChooserAction.COUNTS); |
| loadCountsForAction(proto, counts); |
| proto.end(token); |
| case ProtoInputStream.NO_MORE_FIELDS: |
| if (action == null) { |
| // default string |
| usageStats.mChooserCounts.put("", counts); |
| } |
| return; |
| } |
| } |
| } |
| |
| private static void loadCountsForAction(ProtoInputStream proto, |
| ArrayMap<String, Integer> counts) throws IOException { |
| String category = null; |
| int count = 0; |
| while (true) { |
| switch (proto.nextField()) { |
| case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME: |
| category = proto.readString( |
| IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME); |
| break; |
| case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT: |
| count = proto.readInt( |
| IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT); |
| break; |
| case ProtoInputStream.NO_MORE_FIELDS: |
| if (category == null) { |
| counts.put("", count); |
| } else { |
| counts.put(category, count); |
| } |
| return; |
| } |
| } |
| } |
| |
| private static void loadConfigStats(ProtoInputStream proto, long fieldId, |
| IntervalStats statsOut) throws IOException { |
| final long token = proto.start(fieldId); |
| boolean configActive = false; |
| final Configuration config = new Configuration(); |
| ConfigurationStats configStats; |
| if (proto.nextField(IntervalStatsProto.Configuration.CONFIG)) { |
| // Fast path reading the configuration. Most cases this should work since it is |
| // written first |
| config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG); |
| configStats = statsOut.getOrCreateConfigurationStats(config); |
| } else { |
| // Temporarily store collected data to a ConfigurationStats object. This is not |
| // efficient. |
| configStats = new ConfigurationStats(); |
| } |
| while (true) { |
| switch (proto.nextField()) { |
| case (int) IntervalStatsProto.Configuration.CONFIG: |
| // Fast path failed from some reason, add ConfigStats object to statsOut now |
| config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG); |
| final ConfigurationStats temp = statsOut.getOrCreateConfigurationStats(config); |
| temp.mLastTimeActive = configStats.mLastTimeActive; |
| temp.mTotalTimeActive = configStats.mTotalTimeActive; |
| temp.mActivationCount = configStats.mActivationCount; |
| configStats = temp; |
| break; |
| case (int) IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS: |
| configStats.mLastTimeActive = statsOut.beginTime + proto.readLong( |
| IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS); |
| break; |
| case (int) IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS: |
| configStats.mTotalTimeActive = proto.readLong( |
| IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS); |
| break; |
| case (int) IntervalStatsProto.Configuration.COUNT: |
| configStats.mActivationCount = proto.readInt( |
| IntervalStatsProto.Configuration.COUNT); |
| break; |
| case (int) IntervalStatsProto.Configuration.ACTIVE: |
| configActive = proto.readBoolean(IntervalStatsProto.Configuration.ACTIVE); |
| break; |
| case ProtoInputStream.NO_MORE_FIELDS: |
| if (configStats.mLastTimeActive == 0) { |
| //mLastTimeActive was not assigned, assume default value of 0 plus beginTime |
| configStats.mLastTimeActive = statsOut.beginTime; |
| } |
| if (configActive) { |
| statsOut.activeConfiguration = configStats.mConfiguration; |
| } |
| proto.end(token); |
| return; |
| } |
| } |
| } |
| |
| private static void loadEvent(ProtoInputStream proto, long fieldId, IntervalStats statsOut, |
| List<String> stringPool) throws IOException { |
| final long token = proto.start(fieldId); |
| UsageEvents.Event event = statsOut.buildEvent(proto, stringPool); |
| proto.end(token); |
| if (event.mPackage == null) { |
| throw new ProtocolException("no package field present"); |
| } |
| statsOut.events.insert(event); |
| } |
| |
| private static void writeStringPool(ProtoOutputStream proto, final IntervalStats stats) |
| throws IOException { |
| final long token = proto.start(IntervalStatsProto.STRINGPOOL); |
| final int size = stats.mStringCache.size(); |
| proto.write(IntervalStatsProto.StringPool.SIZE, size); |
| for (int i = 0; i < size; i++) { |
| proto.write(IntervalStatsProto.StringPool.STRINGS, stats.mStringCache.valueAt(i)); |
| } |
| proto.end(token); |
| } |
| |
| private static void writeUsageStats(ProtoOutputStream proto, long fieldId, |
| final IntervalStats stats, final UsageStats usageStats) throws IOException { |
| final long token = proto.start(fieldId); |
| // Write the package name first, so loadUsageStats can avoid creating an extra object |
| final int packageIndex = stats.mStringCache.indexOf(usageStats.mPackageName); |
| if (packageIndex >= 0) { |
| proto.write(IntervalStatsProto.UsageStats.PACKAGE_INDEX, packageIndex + 1); |
| } else { |
| // Package not in Stringpool for some reason, write full string instead |
| Slog.w(TAG, "UsageStats package name (" + usageStats.mPackageName |
| + ") not found in IntervalStats string cache"); |
| proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName); |
| } |
| // Time attributes stored as an offset of the beginTime. |
| proto.write(IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS, |
| usageStats.mLastTimeUsed - stats.beginTime); |
| proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS, |
| usageStats.mTotalTimeInForeground); |
| proto.write(IntervalStatsProto.UsageStats.LAST_EVENT, |
| usageStats.mLastEvent); |
| // Time attributes stored as an offset of the beginTime. |
| proto.write(IntervalStatsProto.UsageStats.LAST_TIME_SERVICE_USED_MS, |
| usageStats.mLastTimeForegroundServiceUsed - stats.beginTime); |
| proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_SERVICE_USED_MS, |
| usageStats.mTotalTimeForegroundServiceUsed); |
| // Time attributes stored as an offset of the beginTime. |
| proto.write(IntervalStatsProto.UsageStats.LAST_TIME_VISIBLE_MS, |
| usageStats.mLastTimeVisible - stats.beginTime); |
| proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_VISIBLE_MS, |
| usageStats.mTotalTimeVisible); |
| proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount); |
| writeChooserCounts(proto, usageStats); |
| proto.end(token); |
| } |
| |
| private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count, |
| long time) throws IOException { |
| final long token = proto.start(fieldId); |
| proto.write(IntervalStatsProto.CountAndTime.COUNT, count); |
| proto.write(IntervalStatsProto.CountAndTime.TIME_MS, time); |
| proto.end(token); |
| } |
| |
| |
| private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats usageStats) |
| throws IOException { |
| if (usageStats == null || usageStats.mChooserCounts == null |
| || usageStats.mChooserCounts.keySet().isEmpty()) { |
| return; |
| } |
| final int chooserCountSize = usageStats.mChooserCounts.size(); |
| for (int i = 0; i < chooserCountSize; i++) { |
| final String action = usageStats.mChooserCounts.keyAt(i); |
| final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i); |
| if (action == null || counts == null || counts.isEmpty()) { |
| continue; |
| } |
| final long token = proto.start(IntervalStatsProto.UsageStats.CHOOSER_ACTIONS); |
| proto.write(IntervalStatsProto.UsageStats.ChooserAction.NAME, action); |
| writeCountsForAction(proto, counts); |
| proto.end(token); |
| } |
| } |
| |
| private static void writeCountsForAction(ProtoOutputStream proto, |
| ArrayMap<String, Integer> counts) throws IOException { |
| final int countsSize = counts.size(); |
| for (int i = 0; i < countsSize; i++) { |
| String key = counts.keyAt(i); |
| int count = counts.valueAt(i); |
| if (count > 0) { |
| final long token = proto.start(IntervalStatsProto.UsageStats.ChooserAction.COUNTS); |
| proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME, key); |
| proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT, count); |
| proto.end(token); |
| } |
| } |
| } |
| |
| private static void writeConfigStats(ProtoOutputStream proto, long fieldId, |
| final IntervalStats stats, final ConfigurationStats configStats, boolean isActive) |
| throws IOException { |
| final long token = proto.start(fieldId); |
| configStats.mConfiguration.writeToProto(proto, IntervalStatsProto.Configuration.CONFIG); |
| proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS, |
| configStats.mLastTimeActive - stats.beginTime); |
| proto.write(IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS, |
| configStats.mTotalTimeActive); |
| proto.write(IntervalStatsProto.Configuration.COUNT, configStats.mActivationCount); |
| proto.write(IntervalStatsProto.Configuration.ACTIVE, isActive); |
| proto.end(token); |
| |
| } |
| |
| private static void writeEvent(ProtoOutputStream proto, long fieldId, final IntervalStats stats, |
| final UsageEvents.Event event) throws IOException { |
| final long token = proto.start(fieldId); |
| final int packageIndex = stats.mStringCache.indexOf(event.mPackage); |
| if (packageIndex >= 0) { |
| proto.write(IntervalStatsProto.Event.PACKAGE_INDEX, packageIndex + 1); |
| } else { |
| // Package not in Stringpool for some reason, write full string instead |
| Slog.w(TAG, "Usage event package name (" + event.mPackage |
| + ") not found in IntervalStats string cache"); |
| proto.write(IntervalStatsProto.Event.PACKAGE, event.mPackage); |
| } |
| if (event.mClass != null) { |
| final int classIndex = stats.mStringCache.indexOf(event.mClass); |
| if (classIndex >= 0) { |
| proto.write(IntervalStatsProto.Event.CLASS_INDEX, classIndex + 1); |
| } else { |
| // Class not in Stringpool for some reason, write full string instead |
| Slog.w(TAG, "Usage event class name (" + event.mClass |
| + ") not found in IntervalStats string cache"); |
| proto.write(IntervalStatsProto.Event.CLASS, event.mClass); |
| } |
| } |
| proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime); |
| proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags); |
| proto.write(IntervalStatsProto.Event.TYPE, event.mEventType); |
| proto.write(IntervalStatsProto.Event.INSTANCE_ID, event.mInstanceId); |
| if (event.mTaskRootPackage != null) { |
| final int taskRootPackageIndex = stats.mStringCache.indexOf(event.mTaskRootPackage); |
| if (taskRootPackageIndex >= 0) { |
| proto.write(IntervalStatsProto.Event.TASK_ROOT_PACKAGE_INDEX, |
| taskRootPackageIndex + 1); |
| } else { |
| // Task root package not in Stringpool for some reason. |
| Slog.w(TAG, "Usage event task root package name (" + event.mTaskRootPackage |
| + ") not found in IntervalStats string cache"); |
| } |
| } |
| if (event.mTaskRootClass != null) { |
| final int taskRootClassIndex = stats.mStringCache.indexOf(event.mTaskRootClass); |
| if (taskRootClassIndex >= 0) { |
| proto.write(IntervalStatsProto.Event.TASK_ROOT_CLASS_INDEX, |
| taskRootClassIndex + 1); |
| } else { |
| // Task root class not in Stringpool for some reason. |
| Slog.w(TAG, "Usage event task root class name (" + event.mTaskRootClass |
| + ") not found in IntervalStats string cache"); |
| } |
| } |
| switch (event.mEventType) { |
| case UsageEvents.Event.CONFIGURATION_CHANGE: |
| if (event.mConfiguration != null) { |
| event.mConfiguration.writeToProto(proto, IntervalStatsProto.Event.CONFIG); |
| } |
| break; |
| case UsageEvents.Event.SHORTCUT_INVOCATION: |
| if (event.mShortcutId != null) { |
| proto.write(IntervalStatsProto.Event.SHORTCUT_ID, event.mShortcutId); |
| } |
| break; |
| case UsageEvents.Event.STANDBY_BUCKET_CHANGED: |
| if (event.mBucketAndReason != 0) { |
| proto.write(IntervalStatsProto.Event.STANDBY_BUCKET, event.mBucketAndReason); |
| } |
| break; |
| case UsageEvents.Event.NOTIFICATION_INTERRUPTION: |
| if (event.mNotificationChannelId != null) { |
| final int channelIndex = stats.mStringCache.indexOf( |
| event.mNotificationChannelId); |
| if (channelIndex >= 0) { |
| proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX, |
| channelIndex + 1); |
| } else { |
| // Channel not in Stringpool for some reason, write full string instead |
| Slog.w(TAG, "Usage event notification channel name (" |
| + event.mNotificationChannelId |
| + ") not found in IntervalStats string cache"); |
| proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL, |
| event.mNotificationChannelId); |
| } |
| } |
| break; |
| } |
| proto.end(token); |
| } |
| |
| /** |
| * Reads from the {@link ProtoInputStream}. |
| * |
| * @param proto The proto from which to read events. |
| * @param statsOut The stats object to populate with the data from the XML file. |
| */ |
| public static void read(InputStream in, IntervalStats statsOut) throws IOException { |
| final ProtoInputStream proto = new ProtoInputStream(in); |
| List<String> stringPool = null; |
| |
| statsOut.packageStats.clear(); |
| statsOut.configurations.clear(); |
| statsOut.activeConfiguration = null; |
| statsOut.events.clear(); |
| |
| while (true) { |
| switch (proto.nextField()) { |
| case (int) IntervalStatsProto.END_TIME_MS: |
| statsOut.endTime = statsOut.beginTime + proto.readLong( |
| IntervalStatsProto.END_TIME_MS); |
| break; |
| case (int) IntervalStatsProto.MAJOR_VERSION: |
| statsOut.majorVersion = proto.readInt( |
| IntervalStatsProto.MAJOR_VERSION); |
| break; |
| case (int) IntervalStatsProto.MINOR_VERSION: |
| statsOut.minorVersion = proto.readInt( |
| IntervalStatsProto.MINOR_VERSION); |
| break; |
| case (int) IntervalStatsProto.INTERACTIVE: |
| loadCountAndTime(proto, IntervalStatsProto.INTERACTIVE, |
| statsOut.interactiveTracker); |
| break; |
| case (int) IntervalStatsProto.NON_INTERACTIVE: |
| loadCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE, |
| statsOut.nonInteractiveTracker); |
| break; |
| case (int) IntervalStatsProto.KEYGUARD_SHOWN: |
| loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN, |
| statsOut.keyguardShownTracker); |
| break; |
| case (int) IntervalStatsProto.KEYGUARD_HIDDEN: |
| loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN, |
| statsOut.keyguardHiddenTracker); |
| break; |
| case (int) IntervalStatsProto.STRINGPOOL: |
| stringPool = readStringPool(proto); |
| statsOut.mStringCache.addAll(stringPool); |
| break; |
| case (int) IntervalStatsProto.PACKAGES: |
| loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool); |
| break; |
| case (int) IntervalStatsProto.CONFIGURATIONS: |
| loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut); |
| break; |
| case (int) IntervalStatsProto.EVENT_LOG: |
| loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool); |
| break; |
| case ProtoInputStream.NO_MORE_FIELDS: |
| if (statsOut.endTime == 0) { |
| // endTime not assigned, assume default value of 0 plus beginTime |
| statsOut.endTime = statsOut.beginTime; |
| } |
| statsOut.upgradeIfNeeded(); |
| return; |
| } |
| } |
| } |
| |
| /** |
| * Writes the stats object to an ProtoBuf file. |
| * |
| * @param proto The serializer to which to write the packageStats data. |
| * @param stats The stats object to write to the XML file. |
| */ |
| public static void write(OutputStream out, IntervalStats stats) throws IOException { |
| final ProtoOutputStream proto = new ProtoOutputStream(out); |
| proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime); |
| proto.write(IntervalStatsProto.MAJOR_VERSION, stats.majorVersion); |
| proto.write(IntervalStatsProto.MINOR_VERSION, stats.minorVersion); |
| // String pool should be written before the rest of the usage stats |
| writeStringPool(proto, stats); |
| |
| writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count, |
| stats.interactiveTracker.duration); |
| writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE, |
| stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration); |
| writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN, |
| stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration); |
| writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN, |
| stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration); |
| |
| final int statsCount = stats.packageStats.size(); |
| for (int i = 0; i < statsCount; i++) { |
| writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats, |
| stats.packageStats.valueAt(i)); |
| } |
| final int configCount = stats.configurations.size(); |
| for (int i = 0; i < configCount; i++) { |
| boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i)); |
| writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats, |
| stats.configurations.valueAt(i), active); |
| } |
| final int eventCount = stats.events.size(); |
| for (int i = 0; i < eventCount; i++) { |
| writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i)); |
| } |
| |
| proto.flush(); |
| } |
| |
| // TODO: move to UsageStatsProtoV2 |
| static void readPendingEvents(InputStream in, List<UsageEvents.Event> events) |
| throws IOException { |
| final ProtoInputStream proto = new ProtoInputStream(in); |
| final List<String> stringPool = new ArrayList<>(); |
| final IntervalStats tmpStatsObj = new IntervalStats(); |
| while (true) { |
| switch (proto.nextField()) { |
| case (int) IntervalStatsProto.PENDING_EVENTS: |
| loadEvent(proto, IntervalStatsProto.PENDING_EVENTS, tmpStatsObj, stringPool); |
| break; |
| case ProtoInputStream.NO_MORE_FIELDS: |
| final int eventCount = tmpStatsObj.events.size(); |
| for (int i = 0; i < eventCount; i++) { |
| events.add(tmpStatsObj.events.get(i)); |
| } |
| return; |
| } |
| } |
| } |
| |
| // TODO: move to UsageStatsProtoV2 |
| static void writePendingEvents(OutputStream out, List<UsageEvents.Event> events) |
| throws IOException { |
| final ProtoOutputStream proto = new ProtoOutputStream(out); |
| final IntervalStats tmpStatsObj = new IntervalStats(); |
| final int eventCount = events.size(); |
| for (int i = 0; i < eventCount; i++) { |
| writeEvent(proto, IntervalStatsProto.PENDING_EVENTS, tmpStatsObj, events.get(i)); |
| } |
| proto.flush(); |
| } |
| } |