Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b44e22c8 authored by Varun Shah's avatar Varun Shah
Browse files

Update how UsageStats timestamps are written to disk.

When writing UsageStats timestamps to disk in proto, we offset the time
by the begin time to save storage space. However, there was logic which
reset default timestamp values of 0 to begin time for those stats.

This CL standardizes how timestamps are written to disk in proto for
UsageStats and updates the parsers. Since backup and restore use
database version 4, UsageStatsProto is also updated in addition to
UsageStatsProtoV2.

Bug: 155149300
Test: atest UsageStatsDatabaseTest
Test: atest android.app.usage.cts.UsageStatsTest
Change-Id: I624e4d56c91762b56eb1c3be67cd8df033fcac95
parent 40f632b7
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -126,7 +126,7 @@ public class UsageStatsDatabaseTest {

        mIntervalStats.majorVersion = 7;
        mIntervalStats.minorVersion = 8;
        mIntervalStats.beginTime = time;
        mIntervalStats.beginTime = time - 1;
        mIntervalStats.interactiveTracker.count = 2;
        mIntervalStats.interactiveTracker.duration = 111111;
        mIntervalStats.nonInteractiveTracker.count = 3;
+0 −4
Original line number Diff line number Diff line
@@ -253,10 +253,6 @@ public class IntervalStats {
                            }
                            break;
                    }
                    if (event.mTimeStamp == 0) {
                        //mTimestamp not set, assume default value 0 plus beginTime
                        event.mTimeStamp = beginTime;
                    }
                    return event;
            }
        }
+16 −25
Original line number Diff line number Diff line
@@ -149,10 +149,6 @@ final class UsageStatsProto {
                    break;
            }
        }
        if (stats.mLastTimeUsed == 0) {
            // mLastTimeUsed was not assigned, assume default value of 0 plus beginTime;
            stats.mLastTimeUsed = statsOut.beginTime;
        }
        proto.end(token);
    }

@@ -289,10 +285,6 @@ final class UsageStatsProto {
                    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;
                    }
@@ -336,21 +328,21 @@ final class UsageStatsProto {
            // Package not in Stringpool for some reason, write full string instead
            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);
        UsageStatsProtoV2.writeOffsetTimestamp(proto,
                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);
        UsageStatsProtoV2.writeOffsetTimestamp(proto,
                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);
        UsageStatsProtoV2.writeOffsetTimestamp(proto,
                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);
@@ -411,8 +403,9 @@ final class UsageStatsProto {
            throws IllegalArgumentException {
        final long token = proto.start(fieldId);
        configStats.mConfiguration.dumpDebug(proto, IntervalStatsProto.Configuration.CONFIG);
        proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS,
                configStats.mLastTimeActive - stats.beginTime);
        UsageStatsProtoV2.writeOffsetTimestamp(proto,
                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);
@@ -439,7 +432,8 @@ final class UsageStatsProto {
                proto.write(IntervalStatsProto.Event.CLASS, event.mClass);
            }
        }
        proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime);
        UsageStatsProtoV2.writeOffsetTimestamp(proto, 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);
@@ -566,10 +560,6 @@ final class UsageStatsProto {
                    }
                    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;
            }
@@ -585,7 +575,8 @@ final class UsageStatsProto {
    public static void write(OutputStream out, IntervalStats stats)
            throws IOException, IllegalArgumentException {
        final ProtoOutputStream proto = new ProtoOutputStream(out);
        proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime);
        proto.write(IntervalStatsProto.END_TIME_MS,
                UsageStatsProtoV2.getOffsetTimestamp(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
+31 −26
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;

/**
 * UsageStats reader/writer V2 for Protocol Buffer format.
@@ -37,6 +38,8 @@ import java.util.LinkedList;
final class UsageStatsProtoV2 {
    private static final String TAG = "UsageStatsProtoV2";

    private static final long ONE_HOUR_MS = TimeUnit.HOURS.toMillis(1);

    // Static-only utility class.
    private UsageStatsProtoV2() {}

@@ -88,10 +91,6 @@ final class UsageStatsProtoV2 {
                            UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS);
                    break;
                case ProtoInputStream.NO_MORE_FIELDS:
                    // mLastTimeUsed was not read, assume default value of 0 plus beginTime
                    if (stats.mLastTimeUsed == 0) {
                        stats.mLastTimeUsed = beginTime;
                    }
                    return stats;
            }
        }
@@ -219,10 +218,6 @@ final class UsageStatsProtoV2 {
                            IntervalStatsObfuscatedProto.Configuration.ACTIVE);
                    break;
                case ProtoInputStream.NO_MORE_FIELDS:
                    // mLastTimeActive was not assigned, assume default value of 0 plus beginTime
                    if (configStats.mLastTimeActive == 0) {
                        configStats.mLastTimeActive = stats.beginTime;
                    }
                    if (configActive) {
                        stats.activeConfiguration = configStats.mConfiguration;
                    }
@@ -282,27 +277,40 @@ final class UsageStatsProtoV2 {
                            EventObfuscatedProto.LOCUS_ID_TOKEN) - 1;
                    break;
                case ProtoInputStream.NO_MORE_FIELDS:
                    // timeStamp was not read, assume default value 0 plus beginTime
                    if (event.mTimeStamp == 0) {
                        event.mTimeStamp = beginTime;
                    }
                    return event.mPackageToken == PackagesTokenData.UNASSIGNED_TOKEN ? null : event;
            }
        }
    }

    static void writeOffsetTimestamp(ProtoOutputStream proto, long fieldId,
            long timestamp, long beginTime) {
        // timestamps will only be written if they're after the begin time
        // a grace period of one hour before the begin time is allowed because of rollover logic
        final long rolloverGracePeriod = beginTime - ONE_HOUR_MS;
        if (timestamp > rolloverGracePeriod) {
            // time attributes are stored as an offset of the begin time (given offset)
            proto.write(fieldId, getOffsetTimestamp(timestamp, beginTime));
        }
    }

    static long getOffsetTimestamp(long timestamp, long offset) {
        final long offsetTimestamp = timestamp - offset;
        // add one ms to timestamp if 0 to ensure it's written to proto (default values are ignored)
        return offsetTimestamp == 0 ? offsetTimestamp + 1 : offsetTimestamp;
    }

    private static void writeUsageStats(ProtoOutputStream proto, final long beginTime,
            final UsageStats stats) throws IllegalArgumentException {
        // Time attributes stored as an offset of the beginTime.
        proto.write(UsageStatsObfuscatedProto.PACKAGE_TOKEN, stats.mPackageToken + 1);
        proto.write(UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS, stats.mLastTimeUsed - beginTime);
        writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_ACTIVE_MS,
                stats.mLastTimeUsed, beginTime);
        proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_ACTIVE_MS, stats.mTotalTimeInForeground);
        proto.write(UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS,
                stats.mLastTimeForegroundServiceUsed - beginTime);
        writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_SERVICE_USED_MS,
                stats.mLastTimeForegroundServiceUsed, beginTime);
        proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_SERVICE_USED_MS,
                stats.mTotalTimeForegroundServiceUsed);
        proto.write(UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS,
                stats.mLastTimeVisible - beginTime);
        writeOffsetTimestamp(proto, UsageStatsObfuscatedProto.LAST_TIME_VISIBLE_MS,
                stats.mLastTimeVisible, beginTime);
        proto.write(UsageStatsObfuscatedProto.TOTAL_TIME_VISIBLE_MS, stats.mTotalTimeVisible);
        proto.write(UsageStatsObfuscatedProto.APP_LAUNCH_COUNT, stats.mAppLaunchCount);
        try {
@@ -361,8 +369,8 @@ final class UsageStatsProtoV2 {
            throws IllegalArgumentException {
        configStats.mConfiguration.dumpDebug(proto,
                IntervalStatsObfuscatedProto.Configuration.CONFIG);
        proto.write(IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS,
                configStats.mLastTimeActive - statsBeginTime);
        writeOffsetTimestamp(proto, IntervalStatsObfuscatedProto.Configuration.LAST_TIME_ACTIVE_MS,
                configStats.mLastTimeActive, statsBeginTime);
        proto.write(IntervalStatsObfuscatedProto.Configuration.TOTAL_TIME_ACTIVE_MS,
                configStats.mTotalTimeActive);
        proto.write(IntervalStatsObfuscatedProto.Configuration.COUNT, configStats.mActivationCount);
@@ -375,7 +383,7 @@ final class UsageStatsProtoV2 {
        if (event.mClassToken != PackagesTokenData.UNASSIGNED_TOKEN) {
            proto.write(EventObfuscatedProto.CLASS_TOKEN, event.mClassToken + 1);
        }
        proto.write(EventObfuscatedProto.TIME_MS, event.mTimeStamp - statsBeginTime);
        writeOffsetTimestamp(proto, EventObfuscatedProto.TIME_MS, event.mTimeStamp, statsBeginTime);
        proto.write(EventObfuscatedProto.FLAGS, event.mFlags);
        proto.write(EventObfuscatedProto.TYPE, event.mEventType);
        proto.write(EventObfuscatedProto.INSTANCE_ID, event.mInstanceId);
@@ -489,10 +497,6 @@ final class UsageStatsProtoV2 {
                    }
                    break;
                case ProtoInputStream.NO_MORE_FIELDS:
                    // endTime not assigned, assume default value of 0 plus beginTime
                    if (stats.endTime == 0) {
                        stats.endTime = stats.beginTime;
                    }
                    // update the begin and end time stamps for all usage stats
                    final int usageStatsSize = stats.packageStatsObfuscated.size();
                    for (int i = 0; i < usageStatsSize; i++) {
@@ -514,7 +518,8 @@ final class UsageStatsProtoV2 {
    public static void write(OutputStream out, IntervalStats stats)
            throws IOException, IllegalArgumentException {
        final ProtoOutputStream proto = new ProtoOutputStream(out);
        proto.write(IntervalStatsObfuscatedProto.END_TIME_MS, stats.endTime - stats.beginTime);
        proto.write(IntervalStatsObfuscatedProto.END_TIME_MS,
                getOffsetTimestamp(stats.endTime, stats.beginTime));
        proto.write(IntervalStatsObfuscatedProto.MAJOR_VERSION, stats.majorVersion);
        proto.write(IntervalStatsObfuscatedProto.MINOR_VERSION, stats.minorVersion);