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

Commit 0f47284a authored by Michael Wachenschwanz's avatar Michael Wachenschwanz
Browse files

Refactor AppTimeLimitController for Session Observers

Introducing the concept of Usage Session Observers to UsageStats. A
session observer monitors usage within individual "continuous" sessions
(brief gaps of non usage may be allowed in a session and still be
considered continuous)

The new session observer in AppTimeLimitController are both similar and
different enough from the current app usage observer to warrant
refactoring TimeLimitGroup into an OOP friendly abstract base class.

Added some Observer App handling to avoid clash between registered
observers from multiple apps.

Reworded packages to observed and usage entities to accomodate future
changes, where usage may come from more than just app usage.

Reworded moveToForeground/Background to generic usage and allow multiple
usage entities to be active at the same time to accomodate future
changes, where more than just the foreground app can be considered used.

Test: atest FrameworksServicesTests:AppTimeLimitControllerTests
Bug: 111465038
Change-Id: I63aebf8b0aa5516111bd6d5e142525d0bee6ef58
parent 148eba15
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -51,4 +51,8 @@ interface IUsageStatsManager {
    void registerAppUsageObserver(int observerId, in String[] packages, long timeLimitMs,
            in PendingIntent callback, String callingPackage);
    void unregisterAppUsageObserver(int observerId, String callingPackage);
    void registerUsageSessionObserver(int sessionObserverId, in String[] observed, long timeLimitMs,
            long sessionThresholdTimeMs, in PendingIntent limitReachedCallbackIntent,
            in PendingIntent sessionEndCallbackIntent, String callingPackage);
    void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage);
}
+476 −100

File changed.

Preview size limit exceeded, changes collapsed.

+584 −265

File changed.

Preview size limit exceeded, changes collapsed.

+96 −16
Original line number Diff line number Diff line
@@ -165,7 +165,11 @@ public class UsageStatsService extends SystemService implements
        mAppStandby = new AppStandbyController(getContext(), BackgroundThread.get().getLooper());

        mAppTimeLimit = new AppTimeLimitController(
                (observerId, userId, timeLimit, timeElapsed, callbackIntent) -> {
                new AppTimeLimitController.TimeLimitCallbackListener() {
                    @Override
                    public void onLimitReached(int observerId, int userId, long timeLimit,
                            long timeElapsed, PendingIntent callbackIntent) {
                        if (callbackIntent == null) return;
                        Intent intent = new Intent();
                        intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
                        intent.putExtra(UsageStatsManager.EXTRA_TIME_LIMIT, timeLimit);
@@ -176,6 +180,22 @@ public class UsageStatsService extends SystemService implements
                            Slog.w(TAG, "Couldn't deliver callback: "
                                    + callbackIntent);
                        }
                    }

                    @Override
                    public void onSessionEnd(int observerId, int userId, long timeElapsed,
                            PendingIntent callbackIntent) {
                        if (callbackIntent == null) return;
                        Intent intent = new Intent();
                        intent.putExtra(UsageStatsManager.EXTRA_OBSERVER_ID, observerId);
                        intent.putExtra(UsageStatsManager.EXTRA_TIME_USED, timeElapsed);
                        try {
                            callbackIntent.send(getContext(), 0, intent);
                        } catch (PendingIntent.CanceledException e) {
                            Slog.w(TAG, "Couldn't deliver callback: "
                                    + callbackIntent);
                        }
                    }
                }, mHandler.getLooper());

        mAppStandby.addListener(mStandbyChangeListener);
@@ -412,12 +432,18 @@ public class UsageStatsService extends SystemService implements
            mAppStandby.reportEvent(event, elapsedRealtime, userId);
            switch (event.mEventType) {
                case Event.MOVE_TO_FOREGROUND:
                    mAppTimeLimit.moveToForeground(event.getPackageName(), event.getClassName(),
                            userId);
                    try {
                        mAppTimeLimit.noteUsageStart(event.getPackageName(), userId);
                    } catch (IllegalArgumentException iae) {
                        Slog.e(TAG, "Failed to note usage start", iae);
                    }
                    break;
                case Event.MOVE_TO_BACKGROUND:
                    mAppTimeLimit.moveToBackground(event.getPackageName(), event.getClassName(),
                            userId);
                    try {
                        mAppTimeLimit.noteUsageStop(event.getPackageName(), userId);
                    } catch (IllegalArgumentException iae) {
                        Slog.e(TAG, "Failed to note usage stop", iae);
                    }
                    break;
            }
        }
@@ -1151,16 +1177,70 @@ public class UsageStatsService extends SystemService implements
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void registerUsageSessionObserver(int sessionObserverId, String[] observed,
                long timeLimitMs, long sessionThresholdTimeMs,
                PendingIntent limitReachedCallbackIntent, PendingIntent sessionEndCallbackIntent,
                String callingPackage) {
            if (!hasObserverPermission(callingPackage)) {
                throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
            }

            if (observed == null || observed.length == 0) {
                throw new IllegalArgumentException("Must specify at least one observed entity");
            }
            if (limitReachedCallbackIntent == null) {
                throw new NullPointerException("limitReachedCallbackIntent can't be null");
            }
            final int callingUid = Binder.getCallingUid();
            final int userId = UserHandle.getUserId(callingUid);
            final long token = Binder.clearCallingIdentity();
            try {
                UsageStatsService.this.registerUsageSessionObserver(callingUid, sessionObserverId,
                        observed, timeLimitMs, sessionThresholdTimeMs, limitReachedCallbackIntent,
                        sessionEndCallbackIntent, userId);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }

        @Override
        public void unregisterUsageSessionObserver(int sessionObserverId, String callingPackage) {
            if (!hasObserverPermission(callingPackage)) {
                throw new SecurityException("Caller doesn't have OBSERVE_APP_USAGE permission");
            }

            final int callingUid = Binder.getCallingUid();
            final int userId = UserHandle.getUserId(callingUid);
            final long token = Binder.clearCallingIdentity();
            try {
                UsageStatsService.this.unregisterUsageSessionObserver(callingUid, sessionObserverId, userId);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
    }

    void registerAppUsageObserver(int callingUid, int observerId, String[] packages,
            long timeLimitMs, PendingIntent callbackIntent, int userId) {
        mAppTimeLimit.addObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
        mAppTimeLimit.addAppUsageObserver(callingUid, observerId, packages, timeLimitMs, callbackIntent,
                userId);
    }

    void unregisterAppUsageObserver(int callingUid, int observerId, int userId) {
        mAppTimeLimit.removeObserver(callingUid, observerId, userId);
        mAppTimeLimit.removeAppUsageObserver(callingUid, observerId, userId);
    }

    void registerUsageSessionObserver(int callingUid, int observerId, String[] observed,
            long timeLimitMs, long sessionThresholdTime, PendingIntent limitReachedCallbackIntent,
            PendingIntent sessionEndCallbackIntent, int userId) {
        mAppTimeLimit.addUsageSessionObserver(callingUid, observerId, observed, timeLimitMs,
                sessionThresholdTime, limitReachedCallbackIntent, sessionEndCallbackIntent, userId);
    }

    void unregisterUsageSessionObserver(int callingUid, int sessionObserverId, int userId) {
        mAppTimeLimit.removeUsageSessionObserver(callingUid, sessionObserverId, userId);
    }

    /**