Jobs in 'active' apps are not subject to standby

Along the way we turn down the "app idle" tracking in the
jbo scheduler: it's now redundant with standby bookkeeping,
and was muddying the waters.

Bug: 74017233
Test: atest CtsJobSchedulerTestCases
Change-Id: I3220e6ef97f153b3f718e267a0dc4fbd3b926316
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 69abed3..9193129 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -614,7 +614,6 @@
         CONSTRAINT_DEADLINE = 5;
         CONSTRAINT_IDLE = 6;
         CONSTRAINT_CONNECTIVITY = 7;
-        CONSTRAINT_APP_NOT_IDLE = 8;
         CONSTRAINT_CONTENT_TRIGGER = 9;
         CONSTRAINT_DEVICE_NOT_DOZING = 10;
     }
diff --git a/services/core/java/com/android/server/AppStateTracker.java b/services/core/java/com/android/server/AppStateTracker.java
index a6b71b7..16be680 100644
--- a/services/core/java/com/android/server/AppStateTracker.java
+++ b/services/core/java/com/android/server/AppStateTracker.java
@@ -258,7 +258,7 @@
          */
         private void onRunAnyAppOpsChanged(AppStateTracker sender,
                 int uid, @NonNull String packageName) {
-            updateJobsForUidPackage(uid, packageName);
+            updateJobsForUidPackage(uid, packageName, sender.isUidActive(uid));
 
             if (!sender.areAlarmsRestricted(uid, packageName, /*allowWhileIdle=*/ false)) {
                 unblockAlarmsForUidPackage(uid, packageName);
@@ -279,9 +279,11 @@
          * This is called when the active/idle state changed for a UID.
          */
         private void onUidActiveStateChanged(AppStateTracker sender, int uid) {
-            updateJobsForUid(uid);
+            final boolean isActive = sender.isUidActive(uid);
 
-            if (sender.isUidActive(uid)) {
+            updateJobsForUid(uid, isActive);
+
+            if (isActive) {
                 unblockAlarmsForUid(uid);
             }
         }
@@ -346,14 +348,14 @@
          * Called when the job restrictions for a UID might have changed, so the job
          * scheduler should re-evaluate all restrictions for all jobs.
          */
-        public void updateJobsForUid(int uid) {
+        public void updateJobsForUid(int uid, boolean isNowActive) {
         }
 
         /**
          * Called when the job restrictions for a UID - package might have changed, so the job
          * scheduler should re-evaluate all restrictions for all jobs.
          */
-        public void updateJobsForUidPackage(int uid, String packageName) {
+        public void updateJobsForUidPackage(int uid, String packageName, boolean isNowActive) {
         }
 
         /**
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 0e7e540..790d396 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -88,7 +88,6 @@
 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
 import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
-import com.android.server.job.controllers.AppIdleController;
 import com.android.server.job.controllers.BackgroundJobsController;
 import com.android.server.job.controllers.BatteryController;
 import com.android.server.job.controllers.ConnectivityController;
@@ -1051,7 +1050,8 @@
                 final JobStatus job = jsc.getRunningJobLocked();
                 if (job != null
                         && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
-                        && !job.dozeWhitelisted) {
+                        && !job.dozeWhitelisted
+                        && !job.uidActive) {
                     // We will report active if we have a job running and it is not an exception
                     // due to being in the foreground or whitelisted.
                     active = true;
@@ -1115,7 +1115,6 @@
         mStorageController = new StorageController(this);
         mControllers.add(mStorageController);
         mControllers.add(new BackgroundJobsController(this));
-        mControllers.add(new AppIdleController(this));
         mControllers.add(new ContentObserverController(this));
         mDeviceIdleJobsController = new DeviceIdleJobsController(this);
         mControllers.add(mDeviceIdleJobsController);
@@ -1867,6 +1866,9 @@
         // scheduled are sitting there, not ready yet) and very cheap to check (just
         // a few conditions on data in JobStatus).
         if (!jobReady) {
+            if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
+                Slog.v(TAG, "    NOT READY: " + job);
+            }
             return false;
         }
 
@@ -1905,9 +1907,21 @@
         // an appropriate amount of time since the last invocation.  During device-
         // wide parole, standby bucketing is ignored.
         //
-        // But if a job has FLAG_EXEMPT_FROM_APP_STANDBY, don't check it.
-        if (!mInParole && !job.getJob().isExemptedFromAppStandby()) {
+        // Jobs in 'active' apps are not subject to standby, nor are jobs that are
+        // specifically marked as exempt.
+        if (DEBUG_STANDBY) {
+            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+                    + " parole=" + mInParole + " active=" + job.uidActive
+                    + " exempt=" + job.getJob().isExemptedFromAppStandby());
+        }
+        if (!mInParole
+                && !job.uidActive
+                && !job.getJob().isExemptedFromAppStandby()) {
             final int bucket = job.getStandbyBucket();
+            if (DEBUG_STANDBY) {
+                Slog.v(TAG, "  bucket=" + bucket + " heartbeat=" + mHeartbeat
+                        + " next=" + mNextBucketHeartbeat[bucket]);
+            }
             if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
                 // Only skip this job if the app is still waiting for the end of its nominal
                 // bucket interval.  Once it's waited that long, we let it go ahead and clear.
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
deleted file mode 100644
index ed29a4c..0000000
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ /dev/null
@@ -1,216 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.job.controllers;
-
-import android.app.usage.UsageStatsManagerInternal;
-import android.os.UserHandle;
-import android.util.Log;
-import android.util.Slog;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.LocalServices;
-import com.android.server.job.JobSchedulerService;
-import com.android.server.job.StateControllerProto;
-
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-
-/**
- * Controls when apps are considered idle and if jobs pertaining to those apps should
- * be executed. Apps that haven't been actively launched or accessed from a foreground app
- * for a certain amount of time (maybe hours or days) are considered idle. When the app comes
- * out of idle state, it will be allowed to run scheduled jobs.
- */
-public final class AppIdleController extends StateController {
-    private static final String TAG = "JobScheduler.AppIdle";
-    private static final boolean DEBUG = JobSchedulerService.DEBUG
-            || Log.isLoggable(TAG, Log.DEBUG);
-
-    private final UsageStatsManagerInternal mUsageStatsInternal;
-    private boolean mInitializedParoleOn;
-    boolean mAppIdleParoleOn;
-
-    final class GlobalUpdateFunc implements Consumer<JobStatus> {
-        boolean mChanged;
-
-        @Override
-        public void accept(JobStatus jobStatus) {
-            String packageName = jobStatus.getSourcePackageName();
-            final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
-                    jobStatus.getSourceUid(), jobStatus.getSourceUserId());
-            if (DEBUG) {
-                Slog.d(TAG, "Setting idle state of " + packageName + " to " + appIdle);
-            }
-            if (jobStatus.setAppNotIdleConstraintSatisfied(!appIdle)) {
-                mChanged = true;
-            }
-        }
-    }
-
-    final static class PackageUpdateFunc implements Consumer<JobStatus> {
-        final int mUserId;
-        final String mPackage;
-        final boolean mIdle;
-        boolean mChanged;
-
-        PackageUpdateFunc(int userId, String pkg, boolean idle) {
-            mUserId = userId;
-            mPackage = pkg;
-            mIdle = idle;
-        }
-
-        @Override
-        public void accept(JobStatus jobStatus) {
-            if (jobStatus.getSourcePackageName().equals(mPackage)
-                    && jobStatus.getSourceUserId() == mUserId) {
-                if (jobStatus.setAppNotIdleConstraintSatisfied(!mIdle)) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "App Idle state changed, setting idle state of "
-                                + mPackage + " to " + mIdle);
-                    }
-                    mChanged = true;
-                }
-            }
-        }
-    }
-
-    public AppIdleController(JobSchedulerService service) {
-        super(service);
-        mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
-        mAppIdleParoleOn = true;
-        mUsageStatsInternal.addAppIdleStateChangeListener(new AppIdleStateChangeListener());
-    }
-
-    @Override
-    public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
-        if (!mInitializedParoleOn) {
-            mInitializedParoleOn = true;
-            mAppIdleParoleOn = mUsageStatsInternal.isAppIdleParoleOn();
-        }
-        String packageName = jobStatus.getSourcePackageName();
-        final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
-                jobStatus.getSourceUid(), jobStatus.getSourceUserId());
-        if (DEBUG) {
-            Slog.d(TAG, "Start tracking, setting idle state of "
-                    + packageName + " to " + appIdle);
-        }
-        jobStatus.setAppNotIdleConstraintSatisfied(!appIdle);
-    }
-
-    @Override
-    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
-            boolean forUpdate) {
-    }
-
-    @Override
-    public void dumpControllerStateLocked(final IndentingPrintWriter pw,
-            final Predicate<JobStatus> predicate) {
-        pw.println("Parole on: " + mAppIdleParoleOn);
-        pw.println();
-
-        mService.getJobStore().forEachJob(predicate, (jobStatus) -> {
-            pw.print("#");
-            jobStatus.printUniqueId(pw);
-            pw.print(" from ");
-            UserHandle.formatUid(pw, jobStatus.getSourceUid());
-            pw.print(": ");
-            pw.print(jobStatus.getSourcePackageName());
-            if ((jobStatus.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0) {
-                pw.println(" RUNNABLE");
-            } else {
-                pw.println(" WAITING");
-            }
-        });
-    }
-
-    @Override
-    public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
-            Predicate<JobStatus> predicate) {
-        final long token = proto.start(fieldId);
-        final long mToken = proto.start(StateControllerProto.APP_IDLE);
-
-        proto.write(StateControllerProto.AppIdleController.IS_PAROLE_ON, mAppIdleParoleOn);
-
-        mService.getJobStore().forEachJob(predicate, (js) -> {
-            final long jsToken =
-                    proto.start(StateControllerProto.AppIdleController.TRACKED_JOBS);
-            js.writeToShortProto(proto, StateControllerProto.AppIdleController.TrackedJob.INFO);
-            proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_UID,
-                    js.getSourceUid());
-            proto.write(StateControllerProto.AppIdleController.TrackedJob.SOURCE_PACKAGE_NAME,
-                    js.getSourcePackageName());
-            proto.write(
-                    StateControllerProto.AppIdleController.TrackedJob.ARE_CONSTRAINTS_SATISFIED,
-                    (js.satisfiedConstraints & JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0);
-            proto.end(jsToken);
-        });
-
-        proto.end(mToken);
-        proto.end(token);
-    }
-
-    void setAppIdleParoleOn(boolean isAppIdleParoleOn) {
-        // Flag if any app's idle state has changed
-        boolean changed = false;
-        synchronized (mLock) {
-            if (mAppIdleParoleOn == isAppIdleParoleOn) {
-                return;
-            }
-            mAppIdleParoleOn = isAppIdleParoleOn;
-            GlobalUpdateFunc update = new GlobalUpdateFunc();
-            mService.getJobStore().forEachJob(update);
-            if (update.mChanged) {
-                changed = true;
-            }
-        }
-        if (changed) {
-            mStateChangedListener.onControllerStateChanged();
-        }
-    }
-
-    private final class AppIdleStateChangeListener
-            extends UsageStatsManagerInternal.AppIdleStateChangeListener {
-        @Override
-        public void onAppIdleStateChanged(String packageName, int userId, boolean idle, int bucket,
-                int reason) {
-            boolean changed = false;
-            synchronized (mLock) {
-                if (mAppIdleParoleOn) {
-                    return;
-                }
-
-                PackageUpdateFunc update = new PackageUpdateFunc(userId, packageName, idle);
-                mService.getJobStore().forEachJob(update);
-                if (update.mChanged) {
-                    changed = true;
-                }
-            }
-            if (changed) {
-                mStateChangedListener.onControllerStateChanged();
-            }
-        }
-
-        @Override
-        public void onParoleStateChanged(boolean isParoleOn) {
-            if (DEBUG) {
-                Slog.d(TAG, "Parole on: " + isParoleOn);
-            }
-            setAppIdleParoleOn(isParoleOn);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
index 36e7511..b698e5b 100644
--- a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -28,17 +28,32 @@
 import com.android.server.AppStateTracker.Listener;
 import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobStore;
 import com.android.server.job.StateControllerProto;
 import com.android.server.job.StateControllerProto.BackgroundJobsController.TrackedJob;
 
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 
+/**
+ * Tracks the following pieces of JobStatus state:
+ *
+ * - the CONSTRAINT_BACKGROUND_NOT_RESTRICTED general constraint bit, which
+ *    is used to selectively permit battery-saver exempted jobs to run; and
+ *
+ * - the uid-active boolean state expressed by the AppStateTracker.  Jobs in 'active'
+ *    uids are inherently eligible to run jobs regardless of the uid's standby bucket.
+ */
 public final class BackgroundJobsController extends StateController {
     private static final String TAG = "JobScheduler.Background";
     private static final boolean DEBUG = JobSchedulerService.DEBUG
             || Log.isLoggable(TAG, Log.DEBUG);
 
+    // Tri-state about possible "is this uid 'active'?" knowledge
+    static final int UNKNOWN = 0;
+    static final int KNOWN_ACTIVE = 1;
+    static final int KNOWN_INACTIVE = 2;
+
     private final AppStateTracker mAppStateTracker;
 
     public BackgroundJobsController(JobSchedulerService service) {
@@ -51,7 +66,7 @@
 
     @Override
     public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
-        updateSingleJobRestrictionLocked(jobStatus);
+        updateSingleJobRestrictionLocked(jobStatus, UNKNOWN);
     }
 
     @Override
@@ -137,23 +152,24 @@
     }
 
     private void updateAllJobRestrictionsLocked() {
-        updateJobRestrictionsLocked(/*filterUid=*/ -1);
+        updateJobRestrictionsLocked(/*filterUid=*/ -1, UNKNOWN);
     }
 
-    private void updateJobRestrictionsForUidLocked(int uid) {
-
-        // TODO Use forEachJobForSourceUid() once we have it.
-
-        updateJobRestrictionsLocked(/*filterUid=*/ uid);
+    private void updateJobRestrictionsForUidLocked(int uid, boolean isActive) {
+        updateJobRestrictionsLocked(uid, (isActive) ? KNOWN_ACTIVE : KNOWN_INACTIVE);
     }
 
-    private void updateJobRestrictionsLocked(int filterUid) {
-        final UpdateJobFunctor updateTrackedJobs =
-                new UpdateJobFunctor(filterUid);
+    private void updateJobRestrictionsLocked(int filterUid, int newActiveState) {
+        final UpdateJobFunctor updateTrackedJobs = new UpdateJobFunctor(newActiveState);
 
         final long start = DEBUG ? SystemClock.elapsedRealtimeNanos() : 0;
 
-        mService.getJobStore().forEachJob(updateTrackedJobs);
+        final JobStore store = mService.getJobStore();
+        if (filterUid > 0) {
+            store.forEachJobForSourceUid(filterUid, updateTrackedJobs);
+        } else {
+            store.forEachJob(updateTrackedJobs);
+        }
 
         final long time = DEBUG ? (SystemClock.elapsedRealtimeNanos() - start) : 0;
         if (DEBUG) {
@@ -170,7 +186,7 @@
         }
     }
 
-    boolean updateSingleJobRestrictionLocked(JobStatus jobStatus) {
+    boolean updateSingleJobRestrictionLocked(JobStatus jobStatus, int activeState) {
 
         final int uid = jobStatus.getSourceUid();
         final String packageName = jobStatus.getSourcePackageName();
@@ -179,28 +195,32 @@
                 (jobStatus.getInternalFlags() & JobStatus.INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION)
                         != 0);
 
-        return jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
+        final boolean isActive;
+        if (activeState == UNKNOWN) {
+            isActive = mAppStateTracker.isUidActive(uid);
+        } else {
+            isActive = (activeState == KNOWN_ACTIVE);
+        }
+        boolean didChange = jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRun);
+        didChange |= jobStatus.setUidActive(isActive);
+        return didChange;
     }
 
     private final class UpdateJobFunctor implements Consumer<JobStatus> {
-        private final int mFilterUid;
-
+        final int activeState;
         boolean mChanged = false;
         int mTotalCount = 0;
         int mCheckedCount = 0;
 
-        UpdateJobFunctor(int filterUid) {
-            mFilterUid = filterUid;
+        public UpdateJobFunctor(int newActiveState) {
+            activeState = newActiveState;
         }
 
         @Override
         public void accept(JobStatus jobStatus) {
             mTotalCount++;
-            if ((mFilterUid > 0) && (mFilterUid != jobStatus.getSourceUid())) {
-                return;
-            }
             mCheckedCount++;
-            if (updateSingleJobRestrictionLocked(jobStatus)) {
+            if (updateSingleJobRestrictionLocked(jobStatus, activeState)) {
                 mChanged = true;
             }
         }
@@ -215,16 +235,16 @@
         }
 
         @Override
-        public void updateJobsForUid(int uid) {
+        public void updateJobsForUid(int uid, boolean isActive) {
             synchronized (mLock) {
-                updateJobRestrictionsForUidLocked(uid);
+                updateJobRestrictionsForUidLocked(uid, isActive);
             }
         }
 
         @Override
-        public void updateJobsForUidPackage(int uid, String packageName) {
+        public void updateJobsForUidPackage(int uid, String packageName, boolean isActive) {
             synchronized (mLock) {
-                updateJobRestrictionsForUidLocked(uid);
+                updateJobRestrictionsForUidLocked(uid, isActive);
             }
         }
     };
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 5616197..578a32c 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -73,7 +73,6 @@
     static final int CONSTRAINT_TIMING_DELAY = 1<<31;
     static final int CONSTRAINT_DEADLINE = 1<<30;
     static final int CONSTRAINT_CONNECTIVITY = 1<<28;
-    static final int CONSTRAINT_APP_NOT_IDLE = 1<<27;
     static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25;
     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22;
@@ -153,6 +152,9 @@
     // Set to true if doze constraint was satisfied due to app being whitelisted.
     public boolean dozeWhitelisted;
 
+    // Set to true when the app is "active" per AppStateTracker
+    public boolean uidActive;
+
     /**
      * Flag for {@link #trackingControllers}: the battery controller is currently tracking this job.
      */
@@ -838,10 +840,6 @@
         return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
     }
 
-    boolean setAppNotIdleConstraintSatisfied(boolean state) {
-        return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state);
-    }
-
     boolean setContentTriggerConstraintSatisfied(boolean state) {
         return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
     }
@@ -855,6 +853,14 @@
         return setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state);
     }
 
+    boolean setUidActive(final boolean newActiveState) {
+        if (newActiveState != uidActive) {
+            uidActive = newActiveState;
+            return true;
+        }
+        return false; /* unchanged */
+    }
+
     boolean setConstraintSatisfied(int constraint, boolean state) {
         boolean old = (satisfiedConstraints&constraint) != 0;
         if (old == state) {
@@ -904,13 +910,11 @@
         // NotRestrictedInBackground implicit constraint must be satisfied
         final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint()
                 && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0);
-        final boolean notIdle = (satisfiedConstraints & CONSTRAINT_APP_NOT_IDLE) != 0;
         final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
                 || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
         final boolean notRestrictedInBg =
                 (satisfiedConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0;
-        return (isConstraintsSatisfied() || deadlineSatisfied) && notIdle && notDozing
-                && notRestrictedInBg;
+        return (isConstraintsSatisfied() || deadlineSatisfied) && notDozing && notRestrictedInBg;
     }
 
     static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
@@ -990,9 +994,6 @@
         if (job.isPersisted()) {
             sb.append(" PERSISTED");
         }
-        if ((satisfiedConstraints&CONSTRAINT_APP_NOT_IDLE) == 0) {
-            sb.append(" WAIT:APP_NOT_IDLE");
-        }
         if ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) == 0) {
             sb.append(" WAIT:DEV_NOT_DOZING");
         }
@@ -1091,9 +1092,6 @@
         if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
             pw.print(" CONNECTIVITY");
         }
-        if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) {
-            pw.print(" APP_NOT_IDLE");
-        }
         if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) {
             pw.print(" CONTENT_TRIGGER");
         }
@@ -1133,9 +1131,6 @@
         if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
             proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONNECTIVITY);
         }
-        if ((constraints & CONSTRAINT_APP_NOT_IDLE) != 0) {
-            proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_APP_NOT_IDLE);
-        }
         if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
             proto.write(fieldId, JobStatusDumpProto.CONSTRAINT_CONTENT_TRIGGER);
         }
@@ -1307,6 +1302,9 @@
             if (dozeWhitelisted) {
                 pw.print(prefix); pw.println("Doze whitelisted: true");
             }
+            if (uidActive) {
+                pw.print(prefix); pw.println("Uid: active");
+            }
         }
         if (trackingControllers != 0) {
             pw.print(prefix); pw.print("Tracking:");
diff --git a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
index b62f1ce..5daacd7 100644
--- a/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/AppStateTrackerTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
@@ -729,8 +730,8 @@
     private void assertNoCallbacks(Listener l) throws Exception {
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -752,8 +753,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -766,8 +767,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(1)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -796,8 +797,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -808,8 +809,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -819,8 +820,8 @@
         setAppOps(UID_10_2, PACKAGE_2, false);
 
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -835,8 +836,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2));
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -849,8 +850,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(1)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -864,8 +865,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -876,8 +877,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(1)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -889,8 +890,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -901,8 +902,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -915,8 +916,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -928,8 +929,8 @@
         waitUntilMainHandlerDrain();
         // Called once for updating all whitelist and once for updating temp whitelist
         verify(l, times(2)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -940,8 +941,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(1)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -953,8 +954,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -965,8 +966,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(anyInt());
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -985,8 +986,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
@@ -997,8 +998,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -1009,8 +1010,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
@@ -1021,8 +1022,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -1035,8 +1036,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(1)).updateAllJobs();
-        verify(l, times(0)).updateJobsForUid(eq(UID_10_1));
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(1)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -1047,8 +1048,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
@@ -1059,8 +1060,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());
@@ -1071,8 +1072,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1));
@@ -1083,8 +1084,8 @@
 
         waitUntilMainHandlerDrain();
         verify(l, times(0)).updateAllJobs();
-        verify(l, times(1)).updateJobsForUid(eq(UID_10_1));
-        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString());
+        verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean());
+        verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean());
 
         verify(l, times(0)).unblockAllUnrestrictedAlarms();
         verify(l, times(0)).unblockAlarmsForUid(anyInt());