Create standby bucket changed atom.

We'll use the data from the atom events to further optimize our quotas.

Bug: 135417506
Bug: 143495340
Bug: 149869487
Test: Use statsd_testdrive to test WakeupAlarmOccurred and
AppStandbyBucketChanged are both logged properly

Change-Id: Id05304175dea804d83f6056bf4da2e049496d87d
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 932c25d..46d449a 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -21,6 +21,7 @@
 import static android.app.usage.UsageStatsManager.REASON_MAIN_MASK;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
 import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
+import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
 import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
@@ -43,6 +44,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 
 import libcore.io.IoUtils;
@@ -244,9 +246,9 @@
      * @param elapsedRealtime mark as used time if non-zero
      * @param timeout set the timeout of the specified bucket, if non-zero. Can only be used
      *                with bucket values of ACTIVE and WORKING_SET.
-     * @return
+     * @return {@code appUsageHistory}
      */
-    public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
+    AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName, int userId,
             int newBucket, int usageReason, long elapsedRealtime, long timeout) {
         int bucketingReason = REASON_MAIN_USAGE | usageReason;
         final boolean isUserUsage = isUserUsage(bucketingReason);
@@ -284,11 +286,7 @@
 
         if (appUsageHistory.currentBucket > newBucket) {
             appUsageHistory.currentBucket = newBucket;
-            if (DEBUG) {
-                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
-                        .currentBucket
-                        + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
-            }
+            logAppStandbyBucketChanged(packageName, userId, newBucket, bucketingReason);
         }
         appUsageHistory.bucketingReason = bucketingReason;
 
@@ -313,7 +311,8 @@
             int usageReason, long nowElapsed, long timeout) {
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
-        return reportUsage(history, packageName, newBucket, usageReason, nowElapsed, timeout);
+        return reportUsage(history, packageName, userId, newBucket, usageReason, nowElapsed,
+                timeout);
     }
 
     private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
@@ -372,6 +371,7 @@
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory =
                 getPackageHistory(userHistory, packageName, elapsedRealtime, true);
+        final boolean changed = appUsageHistory.currentBucket != bucket;
         appUsageHistory.currentBucket = bucket;
         appUsageHistory.bucketingReason = reason;
 
@@ -385,9 +385,8 @@
             appUsageHistory.bucketActiveTimeoutTime = elapsed;
             appUsageHistory.bucketWorkingSetTimeoutTime = elapsed;
         }
-        if (DEBUG) {
-            Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
-                    + ", reason=0x0" + Integer.toHexString(appUsageHistory.bucketingReason));
+        if (changed) {
+            logAppStandbyBucketChanged(packageName, userId, bucket, reason);
         }
     }
 
@@ -485,18 +484,19 @@
 
     /* Returns the new standby bucket the app is assigned to */
     public int setIdle(String packageName, int userId, boolean idle, long elapsedRealtime) {
-        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
-        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
-                elapsedRealtime, true);
+        final int newBucket;
+        final int reason;
         if (idle) {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
-            appUsageHistory.bucketingReason = REASON_MAIN_FORCED_BY_USER;
+            newBucket = STANDBY_BUCKET_RARE;
+            reason = REASON_MAIN_FORCED_BY_USER;
         } else {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+            newBucket = STANDBY_BUCKET_ACTIVE;
             // This is to pretend that the app was just used, don't freeze the state anymore.
-            appUsageHistory.bucketingReason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
+            reason = REASON_MAIN_USAGE | REASON_SUB_USAGE_USER_INTERACTION;
         }
-        return appUsageHistory.currentBucket;
+        setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket, reason, false);
+
+        return newBucket;
     }
 
     public void clearUsage(String packageName, int userId) {
@@ -551,13 +551,27 @@
         return 0;
     }
 
+    /**
+     * Log a standby bucket change to statsd, and also logcat if debug logging is enabled.
+     */
+    private void logAppStandbyBucketChanged(String packageName, int userId, int bucket,
+            int reason) {
+        FrameworkStatsLog.write(
+                FrameworkStatsLog.APP_STANDBY_BUCKET_CHANGED,
+                packageName, userId, bucket,
+                (reason & REASON_MAIN_MASK), (reason & REASON_SUB_MASK));
+        if (DEBUG) {
+            Slog.d(TAG, "Moved " + packageName + " to bucket=" + bucket
+                    + ", reason=0x0" + Integer.toHexString(reason));
+        }
+    }
+
     @VisibleForTesting
     File getUserFile(int userId) {
         return new File(new File(new File(mStorageDir, "users"),
                 Integer.toString(userId)), APP_IDLE_FILENAME);
     }
 
-
     /**
      * Check if App Idle File exists on disk
      * @param userId
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 7a1b4f2..5992253 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -831,24 +831,24 @@
         if (eventType == UsageEvents.Event.NOTIFICATION_SEEN
                 || eventType == UsageEvents.Event.SLICE_PINNED) {
             // Mild usage elevates to WORKING_SET but doesn't change usage time.
-            mAppIdleHistory.reportUsage(appHistory, pkg,
+            mAppIdleHistory.reportUsage(appHistory, pkg, userId,
                     STANDBY_BUCKET_WORKING_SET, subReason,
                     0, elapsedRealtime + mNotificationSeenTimeoutMillis);
             nextCheckDelay = mNotificationSeenTimeoutMillis;
         } else if (eventType == UsageEvents.Event.SYSTEM_INTERACTION) {
-            mAppIdleHistory.reportUsage(appHistory, pkg,
+            mAppIdleHistory.reportUsage(appHistory, pkg, userId,
                     STANDBY_BUCKET_ACTIVE, subReason,
                     0, elapsedRealtime + mSystemInteractionTimeoutMillis);
             nextCheckDelay = mSystemInteractionTimeoutMillis;
         } else if (eventType == UsageEvents.Event.FOREGROUND_SERVICE_START) {
             // Only elevate bucket if this is the first usage of the app
             if (prevBucket != STANDBY_BUCKET_NEVER) return;
-            mAppIdleHistory.reportUsage(appHistory, pkg,
+            mAppIdleHistory.reportUsage(appHistory, pkg, userId,
                     STANDBY_BUCKET_ACTIVE, subReason,
                     0, elapsedRealtime + mInitialForegroundServiceStartTimeoutMillis);
             nextCheckDelay = mInitialForegroundServiceStartTimeoutMillis;
         } else {
-            mAppIdleHistory.reportUsage(appHistory, pkg,
+            mAppIdleHistory.reportUsage(appHistory, pkg, userId,
                     STANDBY_BUCKET_ACTIVE, subReason,
                     elapsedRealtime, elapsedRealtime + mStrongUsageTimeoutMillis);
             nextCheckDelay = mStrongUsageTimeoutMillis;
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5535b66..b86dc35 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -396,6 +396,7 @@
         ForegroundServiceAppOpSessionEnded foreground_service_app_op_session_ended =
             256  [(module) = "framework"];
         DisplayJankReported display_jank_reported = 257;
+        AppStandbyBucketChanged app_standby_bucket_changed = 258 [(module) = "framework"];
         SdkExtensionStatus sdk_extension_status = 354;
     }
 
@@ -1227,18 +1228,8 @@
     // Name of source package (for historical reasons, since BatteryStats tracked it).
     optional string package_name = 3;
 
-    // These enum values match the STANDBY_BUCKET_XXX constants defined in UsageStatsManager.java.
-    enum Bucket {
-        UNKNOWN = 0;
-        EXEMPTED = 5;
-        ACTIVE = 10;
-        WORKING_SET = 20;
-        FREQUENT = 30;
-        RARE = 40;
-        NEVER = 50;
-    }
     // The App Standby bucket of the app that scheduled the alarm at the time the alarm fired.
-    optional Bucket app_standby_bucket = 4;
+    optional AppStandbyBucketChanged.Bucket app_standby_bucket = 4;
 }
 
 /**
@@ -8724,3 +8715,45 @@
     // Total number of L5 sv status messages reports, where sv is used in fix since boot
     optional int64 l5_sv_status_reports_used_in_fix = 14;
 }
+
+/**
+ * Logs when an app is moved to a different standby bucket.
+ *
+ * Logged from:
+ *   frameworks/base/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+ */
+message AppStandbyBucketChanged {
+    optional string package_name = 1;
+
+    // Should be 0, 10, 11, 12, etc. where 0 is the owner. See UserHandle for more documentation.
+    optional int32 user_id = 2;
+
+    // These enum values match the constants defined in UsageStatsManager.java.
+    enum Bucket {
+        BUCKET_UNKNOWN = 0;
+        BUCKET_EXEMPTED = 5;
+        BUCKET_ACTIVE = 10;
+        BUCKET_WORKING_SET = 20;
+        BUCKET_FREQUENT = 30;
+        BUCKET_RARE = 40;
+        BUCKET_RESTRICTED = 45;
+        BUCKET_NEVER = 50;
+    }
+    optional Bucket bucket = 3;
+
+    enum MainReason {
+        MAIN_UNKNOWN = 0;
+        MAIN_DEFAULT = 0x0100;
+        MAIN_TIMEOUT = 0x0200;
+        MAIN_USAGE = 0x0300;
+        MAIN_FORCED_BY_USER = 0x0400;
+        MAIN_PREDICTED = 0x0500;
+        MAIN_FORCED_BY_SYSTEM = 0x0600;
+    }
+    optional MainReason main_reason = 4;
+
+    // A more detailed reason for the standby bucket change. The sub reason name is dependent on
+    // the main reason. Values are one of the REASON_SUB_XXX constants defined in
+    // UsageStatsManager.java.
+    optional int32 sub_reason = 5;
+}