Loading core/proto/android/server/jobscheduler.proto +2 −0 Original line number Diff line number Diff line Loading @@ -558,4 +558,6 @@ message JobStatusDumpProto { optional int64 last_successful_run_time = 22; optional int64 last_failed_run_time = 23; optional int64 internal_flags = 24; } services/core/java/com/android/server/ForceAppStandbyTracker.java +7 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.AppOpsManager.PackageOps; import android.app.IActivityManager; Loading Loading @@ -825,9 +826,10 @@ public class ForceAppStandbyTracker { /** * @return whether jobs should be restricted for a UID package-name. */ public boolean areJobsRestricted(int uid, @NonNull String packageName) { public boolean areJobsRestricted(int uid, @NonNull String packageName, boolean hasForegroundExemption) { return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true, /* exemptOnBatterySaver =*/ false); hasForegroundExemption); } /** Loading Loading @@ -861,7 +863,9 @@ public class ForceAppStandbyTracker { /** * @return whether a UID is in the foreground or not. * * Note clients normally shouldn't need to access it. It's only for dumpsys. * Note this information is based on the UID proc state callback, meaning it's updated * asynchronously and may subtly be stale. If the fresh data is needed, use * {@link ActivityManagerInternal#getUidProcessState} instead. */ public boolean isInForeground(int uid) { if (UserHandle.isCore(uid)) { Loading services/core/java/com/android/server/job/JobSchedulerService.java +42 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.app.IUidObserver; import android.app.job.IJobScheduler; Loading Loading @@ -73,8 +74,10 @@ import com.android.internal.app.procstats.ProcessStats; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.DeviceIdleController; import com.android.server.FgThread; import com.android.server.ForceAppStandbyTracker; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob; import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob; Loading Loading @@ -102,6 +105,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.function.Predicate; /** * Responsible for taking jobs representing work to be performed by a client app, and determining Loading Loading @@ -175,8 +179,10 @@ public final class JobSchedulerService extends com.android.server.SystemService final JobSchedulerStub mJobSchedulerStub; PackageManagerInternal mLocalPM; ActivityManagerInternal mActivityManagerInternal; IBatteryStats mBatteryStats; DeviceIdleController.LocalService mLocalDeviceIdleController; final ForceAppStandbyTracker mForceAppStandbyTracker; /** * Set to true once we are allowed to run third party apps. Loading Loading @@ -778,6 +784,22 @@ public final class JobSchedulerService extends com.android.server.SystemService mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle); } /** * Return whether an UID is in the foreground or not. */ private boolean isUidInForeground(int uid) { synchronized (mLock) { if (mUidPriorityOverride.get(uid, 0) > 0) { return true; } } // Note UID observer may not be called in time, so we always check with the AM. return mActivityManagerInternal.getUidProcessState(uid) <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; } private final Predicate<Integer> mIsUidInForegroundPredicate = this::isUidInForeground; public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName, int userId, String tag) { try { Loading @@ -797,12 +819,25 @@ public final class JobSchedulerService extends com.android.server.SystemService // Fast path: we are adding work to an existing job, and the JobInfo is not // changing. We can just directly enqueue this work in to the job. if (toCancel.getJob().equals(job)) { toCancel.enqueueWorkLocked(ActivityManager.getService(), work); // If any of work item is enqueued when the source is in the foreground, // exempt the entire job. toCancel.maybeAddForegroundExemption(mIsUidInForegroundPredicate); return JobScheduler.RESULT_SUCCESS; } } JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); // Give exemption if the source is in the foreground just now. // Note if it's a sync job, this method is called on the handler so it's not exactly // the state when requestSync() was called, but that should be fine because of the // 1 minute foreground grace period. jobStatus.maybeAddForegroundExemption(mIsUidInForegroundPredicate); if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString()); // Jobs on behalf of others don't apply to the per-app job cap if (ENFORCE_MAX_JOBS && packageName == null) { Loading Loading @@ -1047,6 +1082,8 @@ public final class JobSchedulerService extends com.android.server.SystemService super(context); mLocalPM = LocalServices.getService(PackageManagerInternal.class); mActivityManagerInternal = Preconditions.checkNotNull( LocalServices.getService(ActivityManagerInternal.class)); mHandler = new JobHandler(context.getMainLooper()); mConstants = new Constants(mHandler); Loading Loading @@ -1078,6 +1115,8 @@ public final class JobSchedulerService extends com.android.server.SystemService mDeviceIdleJobsController = DeviceIdleJobsController.get(this); mControllers.add(mDeviceIdleJobsController); mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context); // If the job store determined that it can't yet reschedule persisted jobs, // we need to start watching the clock. if (!mJobs.jobTimesInflatedValid()) { Loading Loading @@ -1137,6 +1176,9 @@ public final class JobSchedulerService extends com.android.server.SystemService public void onBootPhase(int phase) { if (PHASE_SYSTEM_SERVICES_READY == phase) { mConstants.start(getContext().getContentResolver()); mForceAppStandbyTracker.start(); // Register br for package removals and user removals. final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); Loading services/core/java/com/android/server/job/JobStore.java +12 −2 Original line number Diff line number Diff line Loading @@ -72,6 +72,9 @@ import java.util.Set; * This is important b/c {@link com.android.server.job.JobStore.WriteJobsMapToDiskRunnable} * and {@link com.android.server.job.JobStore.ReadJobMapFromDiskRunnable} lock on that * object. * * Test: * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java */ public final class JobStore { private static final String TAG = "JobStore"; Loading Loading @@ -427,6 +430,9 @@ public final class JobStore { out.attribute(null, "uid", Integer.toString(jobStatus.getUid())); out.attribute(null, "priority", String.valueOf(jobStatus.getPriority())); out.attribute(null, "flags", String.valueOf(jobStatus.getFlags())); if (jobStatus.getInternalFlags() != 0) { out.attribute(null, "internalFlags", String.valueOf(jobStatus.getInternalFlags())); } out.attribute(null, "lastSuccessfulRunTime", String.valueOf(jobStatus.getLastSuccessfulRunTime())); Loading Loading @@ -689,6 +695,7 @@ public final class JobStore { int uid, sourceUserId; long lastSuccessfulRunTime; long lastFailedRunTime; int internalFlags = 0; // Read out job identifier attributes and priority. try { Loading @@ -704,6 +711,10 @@ public final class JobStore { if (val != null) { jobBuilder.setFlags(Integer.parseInt(val)); } val = parser.getAttributeValue(null, "internalFlags"); if (val != null) { internalFlags = Integer.parseInt(val); } val = parser.getAttributeValue(null, "sourceUserId"); sourceUserId = val == null ? -1 : Integer.parseInt(val); Loading @@ -718,7 +729,6 @@ public final class JobStore { } String sourcePackageName = parser.getAttributeValue(null, "sourcePackageName"); final String sourceTag = parser.getAttributeValue(null, "sourceTag"); int eventType; Loading Loading @@ -857,7 +867,7 @@ public final class JobStore { appBucket, currentHeartbeat, sourceTag, elapsedRuntimes.first, elapsedRuntimes.second, lastSuccessfulRunTime, lastFailedRunTime, (rtcIsGood) ? null : rtcRuntimes); (rtcIsGood) ? null : rtcRuntimes, internalFlags); return js; } Loading services/core/java/com/android/server/job/controllers/BackgroundJobsController.java +3 −1 Original line number Diff line number Diff line Loading @@ -197,7 +197,9 @@ public final class BackgroundJobsController extends StateController { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName); final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName, (jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0); return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun); } Loading Loading
core/proto/android/server/jobscheduler.proto +2 −0 Original line number Diff line number Diff line Loading @@ -558,4 +558,6 @@ message JobStatusDumpProto { optional int64 last_successful_run_time = 22; optional int64 last_failed_run_time = 23; optional int64 internal_flags = 24; }
services/core/java/com/android/server/ForceAppStandbyTracker.java +7 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.server; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.AppOpsManager.PackageOps; import android.app.IActivityManager; Loading Loading @@ -825,9 +826,10 @@ public class ForceAppStandbyTracker { /** * @return whether jobs should be restricted for a UID package-name. */ public boolean areJobsRestricted(int uid, @NonNull String packageName) { public boolean areJobsRestricted(int uid, @NonNull String packageName, boolean hasForegroundExemption) { return isRestricted(uid, packageName, /*useTempWhitelistToo=*/ true, /* exemptOnBatterySaver =*/ false); hasForegroundExemption); } /** Loading Loading @@ -861,7 +863,9 @@ public class ForceAppStandbyTracker { /** * @return whether a UID is in the foreground or not. * * Note clients normally shouldn't need to access it. It's only for dumpsys. * Note this information is based on the UID proc state callback, meaning it's updated * asynchronously and may subtly be stale. If the fresh data is needed, use * {@link ActivityManagerInternal#getUidProcessState} instead. */ public boolean isInForeground(int uid) { if (UserHandle.isCore(uid)) { Loading
services/core/java/com/android/server/job/JobSchedulerService.java +42 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppGlobals; import android.app.IUidObserver; import android.app.job.IJobScheduler; Loading Loading @@ -73,8 +74,10 @@ import com.android.internal.app.procstats.ProcessStats; import com.android.internal.os.BackgroundThread; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.Preconditions; import com.android.server.DeviceIdleController; import com.android.server.FgThread; import com.android.server.ForceAppStandbyTracker; import com.android.server.LocalServices; import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob; import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob; Loading Loading @@ -102,6 +105,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; import java.util.function.Predicate; /** * Responsible for taking jobs representing work to be performed by a client app, and determining Loading Loading @@ -175,8 +179,10 @@ public final class JobSchedulerService extends com.android.server.SystemService final JobSchedulerStub mJobSchedulerStub; PackageManagerInternal mLocalPM; ActivityManagerInternal mActivityManagerInternal; IBatteryStats mBatteryStats; DeviceIdleController.LocalService mLocalDeviceIdleController; final ForceAppStandbyTracker mForceAppStandbyTracker; /** * Set to true once we are allowed to run third party apps. Loading Loading @@ -778,6 +784,22 @@ public final class JobSchedulerService extends com.android.server.SystemService mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle); } /** * Return whether an UID is in the foreground or not. */ private boolean isUidInForeground(int uid) { synchronized (mLock) { if (mUidPriorityOverride.get(uid, 0) > 0) { return true; } } // Note UID observer may not be called in time, so we always check with the AM. return mActivityManagerInternal.getUidProcessState(uid) <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; } private final Predicate<Integer> mIsUidInForegroundPredicate = this::isUidInForeground; public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName, int userId, String tag) { try { Loading @@ -797,12 +819,25 @@ public final class JobSchedulerService extends com.android.server.SystemService // Fast path: we are adding work to an existing job, and the JobInfo is not // changing. We can just directly enqueue this work in to the job. if (toCancel.getJob().equals(job)) { toCancel.enqueueWorkLocked(ActivityManager.getService(), work); // If any of work item is enqueued when the source is in the foreground, // exempt the entire job. toCancel.maybeAddForegroundExemption(mIsUidInForegroundPredicate); return JobScheduler.RESULT_SUCCESS; } } JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag); // Give exemption if the source is in the foreground just now. // Note if it's a sync job, this method is called on the handler so it's not exactly // the state when requestSync() was called, but that should be fine because of the // 1 minute foreground grace period. jobStatus.maybeAddForegroundExemption(mIsUidInForegroundPredicate); if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString()); // Jobs on behalf of others don't apply to the per-app job cap if (ENFORCE_MAX_JOBS && packageName == null) { Loading Loading @@ -1047,6 +1082,8 @@ public final class JobSchedulerService extends com.android.server.SystemService super(context); mLocalPM = LocalServices.getService(PackageManagerInternal.class); mActivityManagerInternal = Preconditions.checkNotNull( LocalServices.getService(ActivityManagerInternal.class)); mHandler = new JobHandler(context.getMainLooper()); mConstants = new Constants(mHandler); Loading Loading @@ -1078,6 +1115,8 @@ public final class JobSchedulerService extends com.android.server.SystemService mDeviceIdleJobsController = DeviceIdleJobsController.get(this); mControllers.add(mDeviceIdleJobsController); mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context); // If the job store determined that it can't yet reschedule persisted jobs, // we need to start watching the clock. if (!mJobs.jobTimesInflatedValid()) { Loading Loading @@ -1137,6 +1176,9 @@ public final class JobSchedulerService extends com.android.server.SystemService public void onBootPhase(int phase) { if (PHASE_SYSTEM_SERVICES_READY == phase) { mConstants.start(getContext().getContentResolver()); mForceAppStandbyTracker.start(); // Register br for package removals and user removals. final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); Loading
services/core/java/com/android/server/job/JobStore.java +12 −2 Original line number Diff line number Diff line Loading @@ -72,6 +72,9 @@ import java.util.Set; * This is important b/c {@link com.android.server.job.JobStore.WriteJobsMapToDiskRunnable} * and {@link com.android.server.job.JobStore.ReadJobMapFromDiskRunnable} lock on that * object. * * Test: * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java */ public final class JobStore { private static final String TAG = "JobStore"; Loading Loading @@ -427,6 +430,9 @@ public final class JobStore { out.attribute(null, "uid", Integer.toString(jobStatus.getUid())); out.attribute(null, "priority", String.valueOf(jobStatus.getPriority())); out.attribute(null, "flags", String.valueOf(jobStatus.getFlags())); if (jobStatus.getInternalFlags() != 0) { out.attribute(null, "internalFlags", String.valueOf(jobStatus.getInternalFlags())); } out.attribute(null, "lastSuccessfulRunTime", String.valueOf(jobStatus.getLastSuccessfulRunTime())); Loading Loading @@ -689,6 +695,7 @@ public final class JobStore { int uid, sourceUserId; long lastSuccessfulRunTime; long lastFailedRunTime; int internalFlags = 0; // Read out job identifier attributes and priority. try { Loading @@ -704,6 +711,10 @@ public final class JobStore { if (val != null) { jobBuilder.setFlags(Integer.parseInt(val)); } val = parser.getAttributeValue(null, "internalFlags"); if (val != null) { internalFlags = Integer.parseInt(val); } val = parser.getAttributeValue(null, "sourceUserId"); sourceUserId = val == null ? -1 : Integer.parseInt(val); Loading @@ -718,7 +729,6 @@ public final class JobStore { } String sourcePackageName = parser.getAttributeValue(null, "sourcePackageName"); final String sourceTag = parser.getAttributeValue(null, "sourceTag"); int eventType; Loading Loading @@ -857,7 +867,7 @@ public final class JobStore { appBucket, currentHeartbeat, sourceTag, elapsedRuntimes.first, elapsedRuntimes.second, lastSuccessfulRunTime, lastFailedRunTime, (rtcIsGood) ? null : rtcRuntimes); (rtcIsGood) ? null : rtcRuntimes, internalFlags); return js; } Loading
services/core/java/com/android/server/job/controllers/BackgroundJobsController.java +3 −1 Original line number Diff line number Diff line Loading @@ -197,7 +197,9 @@ public final class BackgroundJobsController extends StateController { final int uid = jobStatus.getSourceUid(); final String packageName = jobStatus.getSourcePackageName(); final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName); final boolean canRun = !mForceAppStandbyTracker.areJobsRestricted(uid, packageName, (jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0); return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun); } Loading