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

Commit 55c84552 authored by Chris Tate's avatar Chris Tate Committed by Android (Google) Code Review
Browse files

Merge "Let the job scheduler know when the user interacts with an app"

parents 52f8309a d117b293
Loading
Loading
Loading
Loading
+21 −0
Original line number Diff line number Diff line
@@ -146,6 +146,14 @@ public abstract class UsageStatsManagerInternal {
         * allowed to do work even if they're idle or in a low bucket.
         */
        public abstract void onParoleStateChanged(boolean isParoleOn);

        /**
         * Optional callback to inform the listener that the app has transitioned into
         * an active state due to user interaction.
         */
        public void onUserInteractionStarted(String packageName, @UserIdInt int userId) {
            // No-op by default
        }
    }

    /**  Backup/Restore API */
@@ -212,4 +220,17 @@ public abstract class UsageStatsManagerInternal {
     * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}.
     */
    public abstract long getTimeSinceLastJobRun(String packageName, @UserIdInt int userId);

    /**
     * Report a few data points about an app's job state at the current time.
     *
     * @param packageName the app whose job state is being described
     * @param userId which user the app is associated with
     * @param numDeferredJobs the number of pending jobs that were deferred
     *   due to bucketing policy
     * @param timeSinceLastJobRun number of milliseconds since the last time one of
     *   this app's jobs was executed
     */
    public abstract void reportAppJobState(String packageName, @UserIdInt int userId,
            int numDeferredJobs, long timeSinceLastJobRun);
}
+8 −0
Original line number Diff line number Diff line
@@ -64,6 +64,14 @@ public interface JobSchedulerInternal {
    void removeBackingUpUid(int uid);
    void clearAllBackingUpUids();

    /**
     * The user has started interacting with the app.  Take any appropriate action.
     */
    void reportAppUsage(String packageName, int userId);

    /**
     * Report a snapshot of sync-related jobs back to the sync manager
     */
    JobStorePersistStats getPersistStats();

    /**
+44 −0
Original line number Diff line number Diff line
@@ -1071,6 +1071,11 @@ public final class JobSchedulerService extends com.android.server.SystemService
        }
    }

    void reportAppUsage(String packageName, int userId) {
        // This app just transitioned into interactive use or near equivalent, so we should
        // take a look at its job state for feedback purposes.
    }

    /**
     * Initializes the system service.
     * <p>
@@ -2150,6 +2155,11 @@ public final class JobSchedulerService extends com.android.server.SystemService
            }
        }

        @Override
        public void reportAppUsage(String packageName, int userId) {
            JobSchedulerService.this.reportAppUsage(packageName, userId);
        }

        @Override
        public JobStorePersistStats getPersistStats() {
            synchronized (mLock) {
@@ -2212,6 +2222,40 @@ public final class JobSchedulerService extends com.android.server.SystemService
            }
            mInParole = isParoleOn;
        }

        @Override
        public void onUserInteractionStarted(String packageName, int userId) {
            final int uid = mLocalPM.getPackageUid(packageName,
                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
            if (uid < 0) {
                // Quietly ignore; the case is already logged elsewhere
                return;
            }

            final long sinceLast = sElapsedRealtimeClock.millis() -
                    mUsageStats.getTimeSinceLastJobRun(packageName, userId);
            final DeferredJobCounter counter = new DeferredJobCounter();
            synchronized (mLock) {
                mJobs.forEachJobForSourceUid(uid, counter);
            }

            mUsageStats.reportAppJobState(packageName, userId, counter.numDeferred(), sinceLast);
        }
    }

    static class DeferredJobCounter implements JobStatusFunctor {
        private int mDeferred = 0;

        public int numDeferred() {
            return mDeferred;
        }

        @Override
        public void process(JobStatus job) {
            if (job.getWhenStandbyDeferred() > 0) {
                mDeferred++;
            }
        }
    }

    public static int standbyBucketToBucketIndex(int bucket) {
+7 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.app.job.IJobService;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -46,6 +47,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;

/**
@@ -238,6 +240,11 @@ public final class JobServiceContext implements ServiceConnection {
                }
            }

            UsageStatsManagerInternal usageStats =
                    LocalServices.getService(UsageStatsManagerInternal.class);
            usageStats.setLastJobRunTime(job.getSourcePackageName(), job.getSourceUserId(),
                    mExecutionStartTimeElapsed);

            // Once we'e begun executing a job, we by definition no longer care whether
            // it was inflated from disk with not-yet-coherent delay/deadline bounds.
            job.clearPersistedUtcTimes();
+29 −12
Original line number Diff line number Diff line
@@ -36,6 +36,8 @@ import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerInternal;

import libcore.io.IoUtils;

@@ -202,27 +204,23 @@ public class AppIdleHistory {
     * that's in the future, then the usage event is temporary and keeps the app in the specified
     * bucket at least until the timeout is reached. This can be used to keep the app in an
     * elevated bucket for a while until some important task gets to run.
     * @param packageName
     * @param userId
     * @param bucket the bucket to set the app to
     * @param appUsageHistory the usage record for the app being updated
     * @param packageName name of the app being updated, for logging purposes
     * @param newBucket the bucket to set the app to
     * @param elapsedRealtime mark as used time if non-zero
     * @param timeout set the timeout of the specified bucket, if non-zero
     * @return
     */
    public int reportUsage(String packageName, int userId, int bucket, long elapsedRealtime,
            long timeout) {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                elapsedRealtime, true);

    public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
            int newBucket, long elapsedRealtime, long timeout) {
        if (elapsedRealtime != 0) {
            appUsageHistory.lastUsedElapsedTime = mElapsedDuration
                    + (elapsedRealtime - mElapsedSnapshot);
            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
        }

        if (appUsageHistory.currentBucket > bucket) {
            appUsageHistory.currentBucket = bucket;
        if (appUsageHistory.currentBucket > newBucket) {
            appUsageHistory.currentBucket = newBucket;
            if (DEBUG) {
                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
                        .currentBucket
@@ -235,7 +233,26 @@ public class AppIdleHistory {
        }
        appUsageHistory.bucketingReason = REASON_USAGE;

        return appUsageHistory.currentBucket;
        return appUsageHistory;
    }

    /**
     * Mark the app as used and update the bucket if necessary. If there is a timeout specified
     * that's in the future, then the usage event is temporary and keeps the app in the specified
     * bucket at least until the timeout is reached. This can be used to keep the app in an
     * elevated bucket for a while until some important task gets to run.
     * @param packageName
     * @param userId
     * @param newBucket the bucket to set the app to
     * @param elapsedRealtime mark as used time if non-zero
     * @param timeout set the timeout of the specified bucket, if non-zero
     * @return
     */
    public AppUsageHistory reportUsage(String packageName, int userId, int newBucket,
            long nowElapsed, long timeout) {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
        AppUsageHistory history = getPackageHistory(userHistory, packageName, nowElapsed, true);
        return reportUsage(history, packageName, newBucket, nowElapsed, timeout);
    }

    private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
Loading