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

Commit 2546cef5 authored by Varun Shah's avatar Varun Shah
Browse files

Added APIs for App Usage Limits.

Added a new AppUsageLimit group observer which follows the same pattern as
other UsageGroups. This specific observer allows the launcher to query
for the AppUsageLimit, available via the new LauncherApps API below. The
observer can be registered and unregistered via the respective new APIs in
UsageStats.

LauncherApps has a new API which allows it to get the AppUsageLimit for
a specified package and user, initally set via the API in UsageStats.
This new API allows the launcher to query specifics about the limit such
as how much usage time the limit has, and how much total usage time is
remaining.

Bug: 117409586
Test: atest FrameworksServicesTests:AppTimeLimitControllerTests
Test: atest android.app.usage.cts.UsageStatsTest#testObserveUsagePermissionForRegisterObserver
Test: atest android.app.usage.cts.UsageStatsTest#testObserveUsagePermissionForUnregisterObserver
Test: manual (mmma frameworks/base/tests/UsageStatsTest/)
Change-Id: Ifaffab629409e9191e40404a949c8df70bd3f7cb
parent 203445c8
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -11210,6 +11210,7 @@ package android.content.pm {
  public class LauncherApps {
    method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(String, android.os.UserHandle);
    method @Nullable public android.content.pm.LauncherApps.AppUsageLimit getAppUsageLimit(String, android.os.UserHandle);
    method public android.content.pm.ApplicationInfo getApplicationInfo(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
    method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
    method public java.util.List<android.os.UserHandle> getProfiles();
@@ -11237,6 +11238,14 @@ package android.content.pm {
    field public static final String EXTRA_PIN_ITEM_REQUEST = "android.content.pm.extra.PIN_ITEM_REQUEST";
  }
  public static final class LauncherApps.AppUsageLimit implements android.os.Parcelable {
    method public int describeContents();
    method public long getTotalUsageLimit();
    method public long getUsageRemaining();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.content.pm.LauncherApps.AppUsageLimit> CREATOR;
  }
  public abstract static class LauncherApps.Callback {
    ctor public LauncherApps.Callback();
    method public abstract void onPackageAdded(String, android.os.UserHandle);
+2 −0
Original line number Diff line number Diff line
@@ -1112,6 +1112,7 @@ package android.app.usage {
    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getAppStandbyBucket(String);
    method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public java.util.Map<java.lang.String,java.lang.Integer> getAppStandbyBuckets();
    method public int getUsageSource();
    method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void registerAppUsageLimitObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
    method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerAppUsageObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent);
    method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void registerUsageSessionObserver(int, @NonNull String[], long, @NonNull java.util.concurrent.TimeUnit, long, @NonNull java.util.concurrent.TimeUnit, @NonNull android.app.PendingIntent, @Nullable android.app.PendingIntent);
    method public void reportUsageStart(@NonNull android.app.Activity, @NonNull String);
@@ -1119,6 +1120,7 @@ package android.app.usage {
    method public void reportUsageStop(@NonNull android.app.Activity, @NonNull String);
    method @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE) public void setAppStandbyBucket(String, int);
    method @RequiresPermission(android.Manifest.permission.CHANGE_APP_IDLE_STATE) public void setAppStandbyBuckets(java.util.Map<java.lang.String,java.lang.Integer>);
    method @RequiresPermission(allOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.OBSERVE_APP_USAGE}) public void unregisterAppUsageLimitObserver(int);
    method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void unregisterAppUsageObserver(int);
    method @RequiresPermission(android.Manifest.permission.OBSERVE_APP_USAGE) public void unregisterUsageSessionObserver(int);
    method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(String, long, android.os.UserHandle);
+3 −0
Original line number Diff line number Diff line
@@ -55,6 +55,9 @@ interface IUsageStatsManager {
            long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
            in PendingIntent sessionEndCallbackIntent, String callingPackage);
    void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
    void registerAppUsageLimitObserver(int observerId, in String[] packages, long timeLimitMs,
            in PendingIntent callback, String callingPackage);
    void unregisterAppUsageLimitObserver(int observerId, String callingPackage);
    void reportUsageStart(in IBinder activity, String token, String callingPackage);
    void reportPastUsageStart(in IBinder activity, String token, long timeAgoMs,
            String callingPackage);
+76 −6
Original line number Diff line number Diff line
@@ -619,7 +619,7 @@ public final class UsageStatsManager {
     * @param timeLimit The total time the set of apps can be in the foreground before the
     *                  callbackIntent is delivered. Must be at least one minute.
     * @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
     * @param callbackIntent The PendingIntent that will be dispatched when the usage 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}. Cannot be null.
@@ -682,14 +682,14 @@ public final class UsageStatsManager {
     * @param sessionThresholdTimeUnit The unit for time specified in {@code sessionThreshold}.
     *                                 Cannot be null.
     * @param limitReachedCallbackIntent The {@link 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
     *                                   usage 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}. Cannot be null.
     * @param sessionEndCallbackIntent The {@link PendingIntent}  that will be dispatched when the
     *                                 session has ended after the time limit has been exceeded. The
     *                                 session is considered at its end after the {@code observed}
     *                                 usage has stopped and an additional {@code
     *                                 session has ended after the usage limit has been exceeded.
     *                                 The session is considered at its end after the {@code
     *                                 observed} usage has stopped and an additional {@code
     *                                 sessionThresholdTime} has passed. The delivered Intent will
     *                                 also contain the extras {@link #EXTRA_OBSERVER_ID} and {@link
     *                                 #EXTRA_TIME_USED}. Can be null.
@@ -735,6 +735,74 @@ public final class UsageStatsManager {
        }
    }

    /**
     * Register a usage limit observer that receives a callback on the provided intent when the
     * sum of usages of apps and tokens in the provided {@code observedEntities} array exceeds the
     * {@code timeLimit} specified. The structure of a token is a {@link String} with the reporting
     * package's name and a token that the calling app will use, separated by the forward slash
     * character. Example: com.reporting.package/5OM3*0P4QU3-7OK3N
     * <p>
     * Registering an {@code observerId} that was already registered will override the previous one.
     * No more than 1000 unique {@code observerId} may be registered by a single uid
     * at any one time.
     * A limit may be unregistered via {@link #unregisterAppUsageLimitObserver}
     * <p>
     * This method is similar to {@link #registerAppUsageObserver}, but the usage limit set here
     * will be visible to the launcher so that it can report the limit to the user and how much
     * of it is remaining.
     * @see android.content.pm.LauncherApps#getAppUsageLimit
     *
     * @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 observedEntities The list of packages and token to observe for usage time. Cannot be
     *                         null and must include at least one package or token.
     * @param timeLimit The total time the set of apps can be in the foreground before the
     *                  callbackIntent is delivered. Must be at least one minute.
     * @param timeUnit The unit for time specified in {@code timeLimit}. Cannot be null.
     * @param callbackIntent The PendingIntent that will be dispatched when the  usage 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}. Cannot be null.
     * @throws SecurityException if the caller doesn't have both SUSPEND_APPS and OBSERVE_APP_USAGE
     *                           permissions.
     * @hide
     */
    @SystemApi
    @RequiresPermission(allOf = {
            android.Manifest.permission.SUSPEND_APPS,
            android.Manifest.permission.OBSERVE_APP_USAGE})
    public void registerAppUsageLimitObserver(int observerId, @NonNull String[] observedEntities,
            long timeLimit, @NonNull TimeUnit timeUnit, @NonNull PendingIntent callbackIntent) {
        try {
            mService.registerAppUsageLimitObserver(observerId, observedEntities,
                    timeUnit.toMillis(timeLimit), callbackIntent, mContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Unregister the app usage limit 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 both SUSPEND_APPS and OBSERVE_APP_USAGE
     *                           permissions.
     * @hide
     */
    @SystemApi
    @RequiresPermission(allOf = {
            android.Manifest.permission.SUSPEND_APPS,
            android.Manifest.permission.OBSERVE_APP_USAGE})
    public void unregisterAppUsageLimitObserver(int observerId) {
        try {
            mService.unregisterAppUsageLimitObserver(observerId, mContext.getOpPackageName());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
     * Report usage associated with a particular {@code token} has started. Tokens are app defined
     * strings used to represent usage of in-app features. Apps with the {@link
@@ -743,6 +811,7 @@ public final class UsageStatsManager {
     * and usage will be considered stopped if the activity stops or crashes.
     * @see #registerAppUsageObserver
     * @see #registerUsageSessionObserver
     * @see #registerAppUsageLimitObserver
     *
     * @param activity The activity {@code token} is associated with.
     * @param token The token to report usage against.
@@ -766,6 +835,7 @@ public final class UsageStatsManager {
     * {@code activity} and usage will be considered stopped if the activity stops or crashes.
     * @see #registerAppUsageObserver
     * @see #registerUsageSessionObserver
     * @see #registerAppUsageLimitObserver
     *
     * @param activity The activity {@code token} is associated with.
     * @param token The token to report usage against.
+37 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.UserIdInt;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.content.ComponentName;
import android.content.res.Configuration;
import android.os.UserHandle;

import java.util.List;
import java.util.Set;
@@ -270,4 +271,40 @@ public abstract class UsageStatsManagerInternal {
     * @param userId which user the app is associated with
     */
    public abstract void reportExemptedSyncStart(String packageName, @UserIdInt int userId);

    /**
     * Returns an object describing the app usage limit for the given package which was set via
     * {@link UsageStatsManager#registerAppUsageLimitObserver}.
     * If there are multiple limits that apply to the package, the one with the smallest
     * time remaining will be returned.
     *
     * @param packageName name of the package whose app usage limit will be returned
     * @param user the user associated with the limit
     * @return an {@link AppUsageLimitData} object describing the app time limit containing
     * the given package, with the smallest time remaining.
     */
    public abstract AppUsageLimitData getAppUsageLimit(String packageName, UserHandle user);

    /** A class which is used to share the usage limit data for an app or a group of apps. */
    public static class AppUsageLimitData {
        private final boolean mGroupLimit;
        private final long mTotalUsageLimit;
        private final long mUsageRemaining;

        public AppUsageLimitData(boolean groupLimit, long totalUsageLimit, long usageRemaining) {
            this.mGroupLimit = groupLimit;
            this.mTotalUsageLimit = totalUsageLimit;
            this.mUsageRemaining = usageRemaining;
        }

        public boolean isGroupLimit() {
            return mGroupLimit;
        }
        public long getTotalUsageLimit() {
            return mTotalUsageLimit;
        }
        public long getUsageRemaining() {
            return mUsageRemaining;
        }
    }
}
Loading