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

Commit d117b293 authored by Christopher Tate's avatar Christopher Tate
Browse files

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

Bug: 70297451
Test: manual
Change-Id: I095d86f17a589d8b6531fb71018cfd4276989ba4
parent 182b3f99
Loading
Loading
Loading
Loading
+21 −0
Original line number Original line 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.
         * allowed to do work even if they're idle or in a low bucket.
         */
         */
        public abstract void onParoleStateChanged(boolean isParoleOn);
        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 */
    /**  Backup/Restore API */
@@ -206,4 +214,17 @@ public abstract class UsageStatsManagerInternal {
     * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}.
     * indicated here before by a call to {@link #setLastJobRunTime(String, int, long)}.
     */
     */
    public abstract long getTimeSinceLastJobRun(String packageName, @UserIdInt int userId);
    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 Original line Diff line number Diff line
@@ -55,6 +55,14 @@ public interface JobSchedulerInternal {
    void removeBackingUpUid(int uid);
    void removeBackingUpUid(int uid);
    void clearAllBackingUpUids();
    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();
    JobStorePersistStats getPersistStats();


    /**
    /**
+44 −0
Original line number Original line Diff line number Diff line
@@ -1029,6 +1029,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.
     * Initializes the system service.
     * <p>
     * <p>
@@ -2074,6 +2079,11 @@ public final class JobSchedulerService extends com.android.server.SystemService
            }
            }
        }
        }


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

        @Override
        @Override
        public JobStorePersistStats getPersistStats() {
        public JobStorePersistStats getPersistStats() {
            synchronized (mLock) {
            synchronized (mLock) {
@@ -2132,6 +2142,40 @@ public final class JobSchedulerService extends com.android.server.SystemService
            }
            }
            mInParole = isParoleOn;
            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) {
    public static int standbyBucketToBucketIndex(int bucket) {
+7 −0
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@ import android.app.job.IJobService;
import android.app.job.JobInfo;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobParameters;
import android.app.job.JobWorkItem;
import android.app.job.JobWorkItem;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
@@ -46,6 +47,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.app.IBatteryStats;
import com.android.server.EventLogTags;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
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
            // 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.
            // it was inflated from disk with not-yet-coherent delay/deadline bounds.
            job.clearPersistedUtcTimes();
            job.clearPersistedUtcTimes();
+29 −12
Original line number Original line Diff line number Diff line
@@ -36,6 +36,8 @@ import android.util.Xml;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.job.JobSchedulerInternal;


import libcore.io.IoUtils;
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
     * 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
     * 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.
     * elevated bucket for a while until some important task gets to run.
     * @param packageName
     * @param appUsageHistory the usage record for the app being updated
     * @param userId
     * @param packageName name of the app being updated, for logging purposes
     * @param bucket the bucket to set the app to
     * @param newBucket the bucket to set the app to
     * @param elapsedRealtime mark as used time if non-zero
     * @param elapsedRealtime mark as used time if non-zero
     * @param timeout set the timeout of the specified bucket, if non-zero
     * @param timeout set the timeout of the specified bucket, if non-zero
     * @return
     * @return
     */
     */
    public int reportUsage(String packageName, int userId, int bucket, long elapsedRealtime,
    public AppUsageHistory reportUsage(AppUsageHistory appUsageHistory, String packageName,
            long timeout) {
            int newBucket, long elapsedRealtime, long timeout) {
        ArrayMap<String, AppUsageHistory> userHistory = getUserHistory(userId);
        AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
                elapsedRealtime, true);

        if (elapsedRealtime != 0) {
        if (elapsedRealtime != 0) {
            appUsageHistory.lastUsedElapsedTime = mElapsedDuration
            appUsageHistory.lastUsedElapsedTime = mElapsedDuration
                    + (elapsedRealtime - mElapsedSnapshot);
                    + (elapsedRealtime - mElapsedSnapshot);
            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
            appUsageHistory.lastUsedScreenTime = getScreenOnTime(elapsedRealtime);
        }
        }


        if (appUsageHistory.currentBucket > bucket) {
        if (appUsageHistory.currentBucket > newBucket) {
            appUsageHistory.currentBucket = bucket;
            appUsageHistory.currentBucket = newBucket;
            if (DEBUG) {
            if (DEBUG) {
                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
                Slog.d(TAG, "Moved " + packageName + " to bucket=" + appUsageHistory
                        .currentBucket
                        .currentBucket
@@ -235,7 +233,26 @@ public class AppIdleHistory {
        }
        }
        appUsageHistory.bucketingReason = REASON_USAGE;
        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) {
    private ArrayMap<String, AppUsageHistory> getUserHistory(int userId) {
Loading