Expose App Standby APIs for apps and system

Allow apps to query their own standby bucket.
Allow privileged apps with PACKAGE_USAGE_STATS permission to
query apps.
Allow privileged apps with CHANGE_APP_IDLE_STATE to set the
standby state for apps, but not for themselves.
Removed AppStandby class and moved constants into UsageStatsManager.

Bug: 63527785
Test: cts-tradefed run cts-dev -m CtsAppUsageHostTestCases
Change-Id: I3c1c20f6ecb6d54e248233696039286b243d663c
diff --git a/api/current.txt b/api/current.txt
index 2f3fc26..c41f116 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -7209,6 +7209,7 @@
   }
 
   public final class UsageStatsManager {
+    method public int getAppStandbyBucket();
     method public boolean isAppInactive(java.lang.String);
     method public java.util.Map<java.lang.String, android.app.usage.UsageStats> queryAndAggregateUsageStats(long, long);
     method public java.util.List<android.app.usage.ConfigurationStats> queryConfigurations(int, long, long);
@@ -7219,6 +7220,10 @@
     field public static final int INTERVAL_MONTHLY = 2; // 0x2
     field public static final int INTERVAL_WEEKLY = 1; // 0x1
     field public static final int INTERVAL_YEARLY = 3; // 0x3
+    field public static final int STANDBY_BUCKET_ACTIVE = 10; // 0xa
+    field public static final int STANDBY_BUCKET_FREQUENT = 30; // 0x1e
+    field public static final int STANDBY_BUCKET_RARE = 40; // 0x28
+    field public static final int STANDBY_BUCKET_WORKING_SET = 20; // 0x14
   }
 
 }
diff --git a/api/system-current.txt b/api/system-current.txt
index 95f28e4..bc2fb99 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -608,8 +608,11 @@
   }
 
   public final class UsageStatsManager {
+    method public int getAppStandbyBucket(java.lang.String);
     method public void setAppStandbyBucket(java.lang.String, int);
     method public void whitelistAppTemporarily(java.lang.String, long, android.os.UserHandle);
+    field public static final int STANDBY_BUCKET_EXEMPTED = 5; // 0x5
+    field public static final int STANDBY_BUCKET_NEVER = 50; // 0x32
   }
 
 }
diff --git a/core/java/android/app/usage/AppStandby.java b/core/java/android/app/usage/AppStandby.java
deleted file mode 100644
index 6f9fc2f..0000000
--- a/core/java/android/app/usage/AppStandby.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.app.usage;
-
-import android.annotation.IntDef;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Set of constants for app standby buckets and reasons. Apps will be moved into different buckets
- * that affect how frequently they can run in the background or perform other battery-consuming
- * actions. Buckets will be assigned based on how frequently or when the system thinks the user
- * is likely to use the app.
- * @hide
- */
-public class AppStandby {
-
-    /** The app was used very recently, currently in use or likely to be used very soon. */
-    public static final int STANDBY_BUCKET_ACTIVE = 0;
-
-    // Leave some gap in case we want to increase the number of buckets
-
-    /** The app was used recently and/or likely to be used in the next few hours  */
-    public static final int STANDBY_BUCKET_WORKING_SET = 3;
-
-    // Leave some gap in case we want to increase the number of buckets
-
-    /** The app was used in the last few days and/or likely to be used in the next few days */
-    public static final int STANDBY_BUCKET_FREQUENT = 6;
-
-    // Leave some gap in case we want to increase the number of buckets
-
-    /** The app has not be used for several days and/or is unlikely to be used for several days */
-    public static final int STANDBY_BUCKET_RARE = 9;
-
-    // Leave some gap in case we want to increase the number of buckets
-
-    /** The app has never been used. */
-    public static final int STANDBY_BUCKET_NEVER = 12;
-
-    /** Reason for bucketing -- default initial state */
-    public static final String REASON_DEFAULT = "default";
-
-    /** Reason for bucketing -- timeout */
-    public static final String REASON_TIMEOUT = "timeout";
-
-    /** Reason for bucketing -- usage */
-    public static final String REASON_USAGE = "usage";
-
-    /** Reason for bucketing -- forced by user / shell command */
-    public static final String REASON_FORCED = "forced";
-
-    /**
-     * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
-     * be appended.
-     */
-    public static final String REASON_PREDICTED = "predicted";
-
-    @IntDef(flag = false, value = {
-            STANDBY_BUCKET_ACTIVE,
-            STANDBY_BUCKET_WORKING_SET,
-            STANDBY_BUCKET_FREQUENT,
-            STANDBY_BUCKET_RARE,
-            STANDBY_BUCKET_NEVER,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface StandbyBuckets {}
-}
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 3a3e16e..d614b20 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -16,16 +16,18 @@
 
 package android.app.usage;
 
+import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.app.usage.AppStandby.StandbyBuckets;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.ArrayMap;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -90,6 +92,76 @@
      */
     public static final int INTERVAL_COUNT = 4;
 
+
+    /**
+     * The app is whitelisted for some reason and the bucket cannot be changed.
+     * {@hide}
+     */
+    @SystemApi
+    public static final int STANDBY_BUCKET_EXEMPTED = 5;
+
+    /**
+     * The app was used very recently, currently in use or likely to be used very soon.
+     * @see #getAppStandbyBucket()
+     */
+    public static final int STANDBY_BUCKET_ACTIVE = 10;
+
+    /**
+     * The app was used recently and/or likely to be used in the next few hours.
+     * @see #getAppStandbyBucket()
+     */
+    public static final int STANDBY_BUCKET_WORKING_SET = 20;
+
+    /**
+     * The app was used in the last few days and/or likely to be used in the next few days.
+     * @see #getAppStandbyBucket()
+     */
+    public static final int STANDBY_BUCKET_FREQUENT = 30;
+
+    /**
+     * The app has not be used for several days and/or is unlikely to be used for several days.
+     * @see #getAppStandbyBucket()
+     */
+    public static final int STANDBY_BUCKET_RARE = 40;
+
+    /**
+     * The app has never been used.
+     * {@hide}
+     */
+    @SystemApi
+    public static final int STANDBY_BUCKET_NEVER = 50;
+
+    /** {@hide} Reason for bucketing -- default initial state */
+    public static final String REASON_DEFAULT = "default";
+
+    /** {@hide} Reason for bucketing -- timeout */
+    public static final String REASON_TIMEOUT = "timeout";
+
+    /** {@hide} Reason for bucketing -- usage */
+    public static final String REASON_USAGE = "usage";
+
+    /** {@hide} Reason for bucketing -- forced by user / shell command */
+    public static final String REASON_FORCED = "forced";
+
+    /**
+     * {@hide}
+     * Reason for bucketing -- predicted. This is a prefix and the UID of the bucketeer will
+     * be appended.
+     */
+    public static final String REASON_PREDICTED = "predicted";
+
+    /** @hide */
+    @IntDef(flag = false, value = {
+            STANDBY_BUCKET_EXEMPTED,
+            STANDBY_BUCKET_ACTIVE,
+            STANDBY_BUCKET_WORKING_SET,
+            STANDBY_BUCKET_FREQUENT,
+            STANDBY_BUCKET_RARE,
+            STANDBY_BUCKET_NEVER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface StandbyBuckets {}
+
     private static final UsageEvents sEmptyResults = new UsageEvents();
 
     private final Context mContext;
@@ -237,7 +309,7 @@
     }
 
     /**
-     * @hide
+     * {@hide}
      */
     public void setAppInactive(String packageName, boolean inactive) {
         try {
@@ -248,19 +320,45 @@
     }
 
     /**
-     * @hide
+     * Returns the current standby bucket of the calling app. The system determines the standby
+     * state of the app based on app usage patterns. Standby buckets determine how much an app will
+     * be restricted from running background tasks such as jobs, alarms and certain PendingIntent
+     * callbacks.
+     * Restrictions increase progressively from {@link #STANDBY_BUCKET_ACTIVE} to
+     * {@link #STANDBY_BUCKET_RARE}, with {@link #STANDBY_BUCKET_ACTIVE} being the least
+     * restrictive. The battery level of the device might also affect the restrictions.
+     *
+     * @return the current standby bucket of the calling app.
      */
+    public @StandbyBuckets int getAppStandbyBucket() {
+        try {
+            return mService.getAppStandbyBucket(mContext.getOpPackageName(),
+                    mContext.getOpPackageName(),
+                    mContext.getUserId());
+        } catch (RemoteException e) {
+        }
+        return STANDBY_BUCKET_ACTIVE;
+    }
+
+    /**
+     * {@hide}
+     * Returns the current standby bucket of the specified app. The caller must hold the permission
+     * android.permission.PACKAGE_USAGE_STATS.
+     * @param packageName the package for which to fetch the current standby bucket.
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
     public @StandbyBuckets int getAppStandbyBucket(String packageName) {
         try {
             return mService.getAppStandbyBucket(packageName, mContext.getOpPackageName(),
                     mContext.getUserId());
         } catch (RemoteException e) {
         }
-        return AppStandby.STANDBY_BUCKET_ACTIVE;
+        return STANDBY_BUCKET_ACTIVE;
     }
 
     /**
-     * @hide
+     * {@hide}
      * Changes the app standby state to the provided bucket.
      */
     @SystemApi
diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java
index 9954484f..4b4fe72 100644
--- a/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -16,7 +16,7 @@
 
 package android.app.usage;
 
-import android.app.usage.AppStandby.StandbyBuckets;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.content.ComponentName;
 import android.content.res.Configuration;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 7eb922c..979323f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -224,6 +224,8 @@
                     return runGetInactive(pw);
                 case "set-standby-bucket":
                     return runSetStandbyBucket(pw);
+                case "get-standby-bucket":
+                    return runGetStandbyBucket(pw);
                 case "send-trim-memory":
                     return runSendTrimMemory(pw);
                 case "display":
@@ -1826,6 +1828,29 @@
         return 0;
     }
 
+    private int bucketNameToBucketValue(String name) {
+        String lower = name.toLowerCase();
+        if (lower.startsWith("ac")) {
+            return UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+        } else if (lower.startsWith("wo")) {
+            return UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+        } else if (lower.startsWith("fr")) {
+            return UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+        } else if (lower.startsWith("ra")) {
+            return UsageStatsManager.STANDBY_BUCKET_RARE;
+        } else if (lower.startsWith("ne")) {
+            return UsageStatsManager.STANDBY_BUCKET_NEVER;
+        } else {
+            try {
+                int bucket = Integer.parseInt(lower);
+                return bucket;
+            } catch (NumberFormatException nfe) {
+                getErrPrintWriter().println("Error: Unknown bucket: " + name);
+            }
+        }
+        return -1;
+    }
+
     int runSetStandbyBucket(PrintWriter pw) throws RemoteException {
         int userId = UserHandle.USER_CURRENT;
 
@@ -1840,10 +1865,33 @@
         }
         String packageName = getNextArgRequired();
         String value = getNextArgRequired();
+        int bucket = bucketNameToBucketValue(value);
+        if (bucket < 0) return -1;
 
         IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
                 Context.USAGE_STATS_SERVICE));
-        usm.setAppStandbyBucket(packageName, Integer.parseInt(value), userId);
+        usm.setAppStandbyBucket(packageName, bucketNameToBucketValue(value), userId);
+        return 0;
+    }
+
+    int runGetStandbyBucket(PrintWriter pw) throws RemoteException {
+        int userId = UserHandle.USER_CURRENT;
+
+        String opt;
+        while ((opt=getNextOption()) != null) {
+            if (opt.equals("--user")) {
+                userId = UserHandle.parseUserArg(getNextArgRequired());
+            } else {
+                getErrPrintWriter().println("Error: Unknown option: " + opt);
+                return -1;
+            }
+        }
+        String packageName = getNextArgRequired();
+
+        IUsageStatsManager usm = IUsageStatsManager.Stub.asInterface(ServiceManager.getService(
+                Context.USAGE_STATS_SERVICE));
+        int bucket = usm.getAppStandbyBucket(packageName, null, userId);
+        pw.println(bucket);
         return 0;
     }
 
@@ -2597,8 +2645,10 @@
             pw.println("      Sets the inactive state of an app.");
             pw.println("  get-inactive [--user <USER_ID>] <PACKAGE>");
             pw.println("      Returns the inactive state of an app.");
-            pw.println("  set-standby-bucket [--user <USER_ID>] <PACKAGE> <BUCKET>");
+            pw.println("  set-standby-bucket [--user <USER_ID>] <PACKAGE> active|working_set|frequent|rare");
             pw.println("      Puts an app in the standby bucket.");
+            pw.println("  get-standby-bucket [--user <USER_ID>] <PACKAGE>");
+            pw.println("      Returns the standby bucket of an app.");
             pw.println("  send-trim-memory [--user <USER_ID>] <PROCESS>");
             pw.println("          [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]");
             pw.println("      Send a memory trim event to a <PROCESS>.  May also supply a raw trim int level.");
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 4af86a0..3f014b5 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -29,7 +29,7 @@
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
 import android.app.job.JobWorkItem;
-import android.app.usage.AppStandby;
+import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.content.BroadcastReceiver;
@@ -2074,10 +2074,10 @@
 
     public static int standbyBucketToBucketIndex(int bucket) {
         // Normalize AppStandby constants to indices into our bookkeeping
-        if (bucket == AppStandby.STANDBY_BUCKET_NEVER) return 4;
-        else if (bucket >= AppStandby.STANDBY_BUCKET_RARE) return 3;
-        else if (bucket >= AppStandby.STANDBY_BUCKET_FREQUENT) return 2;
-        else if (bucket >= AppStandby.STANDBY_BUCKET_WORKING_SET) return 1;
+        if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return 4;
+        else if (bucket >= UsageStatsManager.STANDBY_BUCKET_RARE) return 3;
+        else if (bucket >= UsageStatsManager.STANDBY_BUCKET_FREQUENT) return 2;
+        else if (bucket >= UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return 1;
         else return 0;
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
index 39d256a..b62d724 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppIdleHistoryTests.java
@@ -16,14 +16,17 @@
 
 package com.android.server.usage;
 
-import android.app.usage.AppStandby;
+import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+
+import android.app.usage.UsageStatsManager;
 import android.os.FileUtils;
 import android.test.AndroidTestCase;
 
 import java.io.File;
 
-import static android.app.usage.AppStandby.*;
-
 public class AppIdleHistoryTests extends AndroidTestCase {
 
     File mStorageDir;
@@ -85,12 +88,12 @@
         AppIdleHistory aih = new AppIdleHistory(mStorageDir, 1000);
 
         aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 1000, STANDBY_BUCKET_ACTIVE,
-                AppStandby.REASON_USAGE);
+                UsageStatsManager.REASON_USAGE);
         // ACTIVE means not idle
         assertFalse(aih.isIdle(PACKAGE_1, USER_ID, 2000));
 
         aih.setAppStandbyBucket(PACKAGE_2, USER_ID, 2000, STANDBY_BUCKET_ACTIVE,
-                AppStandby.REASON_USAGE);
+                UsageStatsManager.REASON_USAGE);
         aih.setAppStandbyBucket(PACKAGE_1, USER_ID, 3000, STANDBY_BUCKET_RARE,
                 REASON_TIMEOUT);
 
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 40edfd2..87b34ab 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -16,9 +16,12 @@
 
 package com.android.server.usage;
 
-import static android.app.usage.AppStandby.*;
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
 import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -30,8 +33,8 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
-import android.app.usage.AppStandby;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
 import android.appwidget.AppWidgetManager;
 import android.content.Context;
 import android.content.ContextWrapper;
@@ -280,7 +283,7 @@
     public void testBuckets() throws Exception {
         AppStandbyController controller = setupController();
 
-        assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+        assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER);
 
         reportEvent(controller, USER_INTERACTION, 0);
 
@@ -312,7 +315,7 @@
         AppStandbyController controller = setupController();
         mInjector.setDisplayOn(false);
 
-        assertTimeout(controller, 0, STANDBY_BUCKET_NEVER);
+        assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER);
 
         reportEvent(controller, USER_INTERACTION, 0);
 
diff --git a/services/usage/java/com/android/server/usage/AppIdleHistory.java b/services/usage/java/com/android/server/usage/AppIdleHistory.java
index ee11241..5aef55b 100644
--- a/services/usage/java/com/android/server/usage/AppIdleHistory.java
+++ b/services/usage/java/com/android/server/usage/AppIdleHistory.java
@@ -16,10 +16,7 @@
 
 package com.android.server.usage;
 
-import static android.app.usage.AppStandby.*;
-
-import android.app.usage.AppStandby;
-import android.os.Environment;
+import android.app.usage.UsageStatsManager;
 import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
@@ -99,7 +96,8 @@
         final byte[] recent = new byte[HISTORY_SIZE];
         long lastUsedElapsedTime;
         long lastUsedScreenTime;
-        @StandbyBuckets int currentBucket;
+        @UsageStatsManager.StandbyBuckets
+        int currentBucket;
         String bucketingReason;
         int lastInformedBucket;
     }
@@ -190,14 +188,14 @@
                 + (elapsedRealtime - mElapsedSnapshot);
         appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
         appUsageHistory.recent[HISTORY_SIZE - 1] = FLAG_LAST_STATE | FLAG_PARTIAL_ACTIVE;
-        if (appUsageHistory.currentBucket > STANDBY_BUCKET_ACTIVE) {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+        if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
             if (DEBUG) {
                 Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
                         + ", reason=" + appUsageHistory.bucketingReason);
             }
         }
-        appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+        appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
 
         return appUsageHistory.currentBucket;
     }
@@ -206,15 +204,15 @@
         ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
         AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                 elapsedRealtime, true);
-        if (appUsageHistory.currentBucket > STANDBY_BUCKET_WORKING_SET) {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_WORKING_SET;
+        if (appUsageHistory.currentBucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
             if (DEBUG) {
                 Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory.currentBucket
                         + ", reason=" + appUsageHistory.bucketingReason);
             }
         }
         // TODO: Should this be a different reason for partial usage?
-        appUsageHistory.bucketingReason = AppStandby.REASON_USAGE;
+        appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
 
         return appUsageHistory.currentBucket;
     }
@@ -271,8 +269,9 @@
             appUsageHistory = new AppUsageHistory();
             appUsageHistory.lastUsedElapsedTime = getElapsedTime(elapsedRealtime);
             appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
-            appUsageHistory.currentBucket = AppStandby.STANDBY_BUCKET_NEVER;
-            appUsageHistory.bucketingReason = REASON_DEFAULT;
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_NEVER;
+            appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
+            appUsageHistory.lastInformedBucket = -1;
             userHistory.put(packageName, appUsageHistory);
         }
         return appUsageHistory;
@@ -289,7 +288,7 @@
         if (appUsageHistory == null) {
             return false; // Default to not idle
         } else {
-            return appUsageHistory.currentBucket >= AppStandby.STANDBY_BUCKET_RARE;
+            return appUsageHistory.currentBucket >= UsageStatsManager.STANDBY_BUCKET_RARE;
             // Whether or not it's passed will now be externally calculated and the
             // bucket will be pushed to the history using setAppStandbyBucket()
             //return hasPassedThresholds(appUsageHistory, elapsedRealtime);
@@ -333,12 +332,12 @@
         AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                 elapsedRealtime, true);
         if (idle) {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_RARE;
-            appUsageHistory.bucketingReason = REASON_FORCED;
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_RARE;
+            appUsageHistory.bucketingReason = UsageStatsManager.REASON_FORCED;
         } else {
-            appUsageHistory.currentBucket = STANDBY_BUCKET_ACTIVE;
+            appUsageHistory.currentBucket = UsageStatsManager.STANDBY_BUCKET_ACTIVE;
             // This is to pretend that the app was just used, don't freeze the state anymore.
-            appUsageHistory.bucketingReason = REASON_USAGE;
+            appUsageHistory.bucketingReason = UsageStatsManager.REASON_USAGE;
         }
         return appUsageHistory.currentBucket;
     }
@@ -435,12 +434,12 @@
                         String currentBucketString = parser.getAttributeValue(null,
                                 ATTR_CURRENT_BUCKET);
                         appUsageHistory.currentBucket = currentBucketString == null
-                                ? AppStandby.STANDBY_BUCKET_ACTIVE
+                                ? UsageStatsManager.STANDBY_BUCKET_ACTIVE
                                 : Integer.parseInt(currentBucketString);
                         appUsageHistory.bucketingReason =
                                 parser.getAttributeValue(null, ATTR_BUCKETING_REASON);
                         if (appUsageHistory.bucketingReason == null) {
-                            appUsageHistory.bucketingReason = REASON_DEFAULT;
+                            appUsageHistory.bucketingReason = UsageStatsManager.REASON_DEFAULT;
                         }
                         userHistory.put(packageName, appUsageHistory);
                     }
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 3c099c2..4527879 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -16,15 +16,23 @@
 
 package com.android.server.usage;
 
+import static android.app.usage.UsageStatsManager.REASON_FORCED;
+import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_USAGE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+
 import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
 import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
 
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.admin.DevicePolicyManager;
-import android.app.usage.AppStandby;
-import android.app.usage.AppStandby.StandbyBuckets;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManager;
 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
 import android.appwidget.AppWidgetManager;
 import android.content.BroadcastReceiver;
@@ -102,10 +110,10 @@
     };
 
     static final int[] THRESHOLD_BUCKETS = {
-            AppStandby.STANDBY_BUCKET_ACTIVE,
-            AppStandby.STANDBY_BUCKET_WORKING_SET,
-            AppStandby.STANDBY_BUCKET_FREQUENT,
-            AppStandby.STANDBY_BUCKET_RARE
+            STANDBY_BUCKET_ACTIVE,
+            STANDBY_BUCKET_WORKING_SET,
+            STANDBY_BUCKET_FREQUENT,
+            STANDBY_BUCKET_RARE
     };
 
     // To name the lock for stack traces
@@ -371,18 +379,18 @@
                 }
                 if (isSpecial) {
                     maybeInformListeners(packageName, userId, elapsedRealtime,
-                            AppStandby.STANDBY_BUCKET_ACTIVE);
+                            STANDBY_BUCKET_ACTIVE);
                 } else {
                     synchronized (mAppIdleLock) {
                         String bucketingReason = mAppIdleHistory.getAppStandbyReason(packageName,
                                 userId, elapsedRealtime);
                         // If the bucket was forced by the developer, leave it alone
-                        if (AppStandby.REASON_FORCED.equals(bucketingReason)) {
+                        if (REASON_FORCED.equals(bucketingReason)) {
                             continue;
                         }
                         // If the bucket was moved up due to usage, let the timeouts apply.
-                        if (AppStandby.REASON_USAGE.equals(bucketingReason)
-                                || AppStandby.REASON_TIMEOUT.equals(bucketingReason)) {
+                        if (REASON_USAGE.equals(bucketingReason)
+                                || REASON_TIMEOUT.equals(bucketingReason)) {
                             int oldBucket = mAppIdleHistory.getAppStandbyBucket(packageName, userId,
                                     elapsedRealtime);
                             int newBucket = getBucketForLocked(packageName, userId,
@@ -393,7 +401,7 @@
                             }
                             if (oldBucket < newBucket) {
                                 mAppIdleHistory.setAppStandbyBucket(packageName, userId,
-                                        elapsedRealtime, newBucket, AppStandby.REASON_TIMEOUT);
+                                        elapsedRealtime, newBucket, REASON_TIMEOUT);
                                 maybeInformListeners(packageName, userId, elapsedRealtime,
                                         newBucket);
                             }
@@ -742,7 +750,7 @@
             long elapsedRealtime, boolean shouldObfuscateInstantApps) {
         if (shouldObfuscateInstantApps &&
                 mInjector.isPackageEphemeral(userId, packageName)) {
-            return AppStandby.STANDBY_BUCKET_ACTIVE;
+            return STANDBY_BUCKET_ACTIVE;
         }
 
         return mAppIdleHistory.getAppStandbyBucket(packageName, userId, elapsedRealtime);
@@ -811,7 +819,7 @@
     }
 
     void informListeners(String packageName, int userId, int bucket) {
-        final boolean idle = bucket >= AppStandby.STANDBY_BUCKET_RARE;
+        final boolean idle = bucket >= STANDBY_BUCKET_RARE;
         for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
             listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
         }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 65c1cef..878fbed 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -20,11 +20,11 @@
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.IUidObserver;
-import android.app.usage.AppStandby;
 import android.app.usage.ConfigurationStats;
 import android.app.usage.IUsageStatsManager;
 import android.app.usage.UsageEvents;
-import android.app.usage.AppStandby.StandbyBuckets;
+import android.app.usage.UsageStatsManager;
+import android.app.usage.UsageStatsManager.StandbyBuckets;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManagerInternal;
@@ -149,6 +149,8 @@
 
         publishLocalService(UsageStatsManagerInternal.class, new LocalService());
         publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
+        // Make sure we initialize the data, in case job scheduler needs it early.
+        getUserDataAndInitializeIfNeededLocked(UserHandle.USER_SYSTEM, mSystemTimeSnapshot);
     }
 
     @Override
@@ -678,10 +680,6 @@
 
         @Override
         public int getAppStandbyBucket(String packageName, String callingPackage, int userId) {
-            if (!hasPermission(callingPackage)) {
-                throw new SecurityException("Don't have permission to query app standby bucket");
-            }
-
             final int callingUid = Binder.getCallingUid();
             try {
                 userId = ActivityManager.getService().handleIncomingUser(
@@ -690,6 +688,14 @@
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
+            // If the calling app is asking about itself, continue, else check for permission.
+            if (mPackageManagerInternal.getPackageUid(packageName, PackageManager.MATCH_ANY_USER,
+                    userId) != callingUid) {
+                if (!hasPermission(callingPackage)) {
+                    throw new SecurityException(
+                            "Don't have permission to query app standby bucket");
+                }
+            }
             final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller(callingUid,
                     userId);
             final long token = Binder.clearCallingIdentity();
@@ -707,6 +713,10 @@
             getContext().enforceCallingPermission(Manifest.permission.CHANGE_APP_IDLE_STATE,
                     "No permission to change app standby state");
 
+            if (bucket < UsageStatsManager.STANDBY_BUCKET_ACTIVE
+                    || bucket > UsageStatsManager.STANDBY_BUCKET_NEVER) {
+                throw new IllegalArgumentException("Cannot set the standby bucket to " + bucket);
+            }
             final int callingUid = Binder.getCallingUid();
             try {
                 userId = ActivityManager.getService().handleIncomingUser(
@@ -717,8 +727,13 @@
             }
             final long token = Binder.clearCallingIdentity();
             try {
+                // Caller cannot set their own standby state
+                if (mPackageManagerInternal.getPackageUid(packageName,
+                        PackageManager.MATCH_ANY_USER, userId) == callingUid) {
+                    throw new IllegalArgumentException("Cannot set your own standby bucket");
+                }
                 mAppStandby.setAppStandbyBucket(packageName, userId, bucket,
-                        AppStandby.REASON_PREDICTED + ":" + callingUid,
+                        UsageStatsManager.REASON_PREDICTED + ":" + callingUid,
                         SystemClock.elapsedRealtime());
             } finally {
                 Binder.restoreCallingIdentity(token);