Provide app launch count in UsageStats

This counts the number of times the app was launched from outside
the app and ignores intra-app activity transitions.

Introduce a new permission for registering to observe app usage.

Fixes a bug where Settings couldn't force the app into another
bucket if it was recently launched.

Bug: 74335821
Fixes: 76100712
Test: Manual test using Settings
Test: UsageStatsTest to verify permission change
Change-Id: Ibd343c1cfa37089a3ac6fc30ba3194e21a9be499
diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java
index 7eef85c..2b14841 100644
--- a/core/java/android/app/usage/UsageStats.java
+++ b/core/java/android/app/usage/UsageStats.java
@@ -16,6 +16,7 @@
 
 package android.app.usage;
 
+import android.annotation.SystemApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -61,6 +62,11 @@
     /**
      * {@hide}
      */
+    public int mAppLaunchCount;
+
+    /**
+     * {@hide}
+     */
     public int mLastEvent;
 
     /**
@@ -81,6 +87,7 @@
         mLastTimeUsed = stats.mLastTimeUsed;
         mTotalTimeInForeground = stats.mTotalTimeInForeground;
         mLaunchCount = stats.mLaunchCount;
+        mAppLaunchCount = stats.mAppLaunchCount;
         mLastEvent = stats.mLastEvent;
         mChooserCounts = stats.mChooserCounts;
     }
@@ -137,6 +144,16 @@
     }
 
     /**
+     * Returns the number of times the app was launched as an activity from outside of the app.
+     * Excludes intra-app activity transitions.
+     * @hide
+     */
+    @SystemApi
+    public int getAppLaunchCount() {
+        return mAppLaunchCount;
+    }
+
+    /**
      * Add the statistics from the right {@link UsageStats} to the left. The package name for
      * both {@link UsageStats} objects must be the same.
      * @param right The {@link UsageStats} object to merge into this one.
@@ -161,6 +178,7 @@
         mEndTimeStamp = Math.max(mEndTimeStamp, right.mEndTimeStamp);
         mTotalTimeInForeground += right.mTotalTimeInForeground;
         mLaunchCount += right.mLaunchCount;
+        mAppLaunchCount += right.mAppLaunchCount;
         if (mChooserCounts == null) {
             mChooserCounts = right.mChooserCounts;
         } else if (right.mChooserCounts != null) {
@@ -196,6 +214,7 @@
         dest.writeLong(mLastTimeUsed);
         dest.writeLong(mTotalTimeInForeground);
         dest.writeInt(mLaunchCount);
+        dest.writeInt(mAppLaunchCount);
         dest.writeInt(mLastEvent);
         Bundle allCounts = new Bundle();
         if (mChooserCounts != null) {
@@ -224,6 +243,7 @@
             stats.mLastTimeUsed = in.readLong();
             stats.mTotalTimeInForeground = in.readLong();
             stats.mLaunchCount = in.readInt();
+            stats.mAppLaunchCount = in.readInt();
             stats.mLastEvent = in.readInt();
             Bundle allCounts = in.readBundle();
             if (allCounts != null) {
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 59f001c..642c5b4 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -17,6 +17,7 @@
 package android.app.usage;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -500,26 +501,28 @@
     /**
      * @hide
      * Register an app usage limit observer that receives a callback on the provided intent when
-     * the sum of usages of apps in the packages array exceeds the timeLimit specified. The
+     * the sum of usages of apps in the packages array exceeds the {@code timeLimit} specified. The
      * observer will automatically be unregistered when the time limit is reached and the intent
-     * is delivered.
+     * is delivered. Registering an {@code observerId} that was already registered will override
+     * the previous one.
      * @param observerId A unique id associated with the group of apps to be monitored. There can
      *                  be multiple groups with common packages and different time limits.
-     * @param packages The list of packages to observe for foreground activity time. Must include
-     *                 at least one package.
+     * @param packages The list of packages to observe for foreground activity time. Cannot be null
+     *                 and must include at least one package.
      * @param timeLimit The total time the set of apps can be in the foreground before the
      *                  callbackIntent is delivered. Must be greater than 0.
-     * @param timeUnit The unit for time specified in timeLimit.
+     * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
      * @param callbackIntent The PendingIntent that will be dispatched when the time limit is
      *                       exceeded by the group of apps. The delivered Intent will also contain
      *                       the extras {@link #EXTRA_OBSERVER_ID}, {@link #EXTRA_TIME_LIMIT} and
-     *                       {@link #EXTRA_TIME_USED}.
-     * @throws SecurityException if the caller doesn't have the PACKAGE_USAGE_STATS permission.
+     *                       {@link #EXTRA_TIME_USED}. Cannot be null.
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or
+     *                           is not the profile owner of this user.
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
-    public void registerAppUsageObserver(int observerId, String[] packages, long timeLimit,
-            TimeUnit timeUnit, PendingIntent callbackIntent) {
+    @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
+    public void registerAppUsageObserver(int observerId, @NonNull String[] packages, long timeLimit,
+            @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
         try {
             mService.registerAppUsageObserver(observerId, packages, timeUnit.toMillis(timeLimit),
                     callbackIntent, mContext.getOpPackageName());
@@ -529,14 +532,15 @@
 
     /**
      * @hide
-     * Unregister the app usage observer specified by the observerId. This will only apply to any
-     * observer registered by this application. Unregistering an observer that was already
+     * Unregister the app usage observer specified by the {@code observerId}. This will only apply
+     * to any observer registered by this application. Unregistering an observer that was already
      * unregistered or never registered will have no effect.
      * @param observerId The id of the observer that was previously registered.
-     * @throws SecurityException if the caller doesn't have the PACKAGE_USAGE_STATS permission.
+     * @throws SecurityException if the caller doesn't have the OBSERVE_APP_USAGE permission or is
+     *                           not the profile owner of this user.
      */
     @SystemApi
-    @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)
+    @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE)
     public void unregisterAppUsageObserver(int observerId) {
         try {
             mService.unregisterAppUsageObserver(observerId, mContext.getOpPackageName());
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9b11a33..64c50ca 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3274,6 +3274,11 @@
         android:protectionLevel="signature|privileged|development|appop" />
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
 
+    <!-- @hide @SystemApi Allows an application to observe usage time of apps. The app can register
+         for callbacks when apps reach a certain usage time limit, etc. -->
+    <permission android:name="android.permission.OBSERVE_APP_USAGE"
+        android:protectionLevel="signature|privileged" />
+
     <!-- @hide @SystemApi Allows an application to change the app idle state of an app.
          <p>Not for use by third-party applications. -->
     <permission android:name="android.permission.CHANGE_APP_IDLE_STATE"