All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.usage.UsageStatsProto Maven / Gradle / Ivy

/*
 * 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 readStringPool(ProtoInputStream proto) throws IOException {

        final long token = proto.start(IntervalStatsProto.STRINGPOOL);
        List stringPool;
        if (proto.isNextField(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 stringPool)
            throws IOException {

        final long token = proto.start(fieldId);
        UsageStats stats;
        if (proto.isNextField(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.isNextField(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 counts;
        if (proto.isNextField(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 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.isNextField(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 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 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 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 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();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy