From b44e22c8d9098f35a1e1dcff055549eb669dffb1 Mon Sep 17 00:00:00 2001 From: Varun Shah Date: Fri, 1 May 2020 00:28:59 -0700 Subject: [PATCH] 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 --- .../server/usage/UsageStatsDatabaseTest.java | 2 +- .../android/server/usage/IntervalStats.java | 4 -- .../android/server/usage/UsageStatsProto.java | 41 ++++++------- .../server/usage/UsageStatsProtoV2.java | 57 ++++++++++--------- 4 files changed, 48 insertions(+), 56 deletions(-) diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java index f1c39067994c1..60390dc3995e7 100644 --- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java +++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java @@ -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; diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 8fadf5eb93339..5ee3b4859d548 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -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; } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java index 932784d334b8e..463fc378c27d7 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsProto.java +++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java @@ -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 diff --git a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java index e4aa9fe0dc1ea..e6d28417d3ca6 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java +++ b/services/usage/java/com/android/server/usage/UsageStatsProtoV2.java @@ -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);