Allow system jobs to be exempted from app-standby

This flag will allow the sync manager to exempt certain kind of sync requests.

- This will only exempt jobs from app-standby.

- EBS still won't exempt them. Battery saver cuts background network anyway, so
background syncs will still not work, and it didn't used to work pre-P either.

- Manual force-app standby still doesn't allow them to run.

Bug: 72443754
Test: Build and boot. The behavior shouldn't change since none uses the flag yet.
Test: atest CtsJobSchedulerTestCases
Change-Id: I806b97bb4b7da773479e878e6eccb792b03eadc1
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 2066f2a..c087bff 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1809,7 +1809,9 @@
         // If the app is in a non-active standby bucket, make sure we've waited
         // an appropriate amount of time since the last invocation.  During device-
         // wide parole, standby bucketing is ignored.
-        if (!mInParole) {
+        //
+        // But if a job has FLAG_EXEMPT_FROM_APP_STANDBY, don't check it.
+        if (!mInParole && !job.getJob().isExemptedFromAppStandby()) {
             final int bucket = job.getStandbyBucket();
             if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
                 // Only skip this job if it's still waiting for the end of its (initial) nominal
@@ -2295,6 +2297,22 @@
             return canPersist;
         }
 
+        private void validateJobFlags(JobInfo job, int callingUid) {
+            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
+                getContext().enforceCallingOrSelfPermission(
+                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
+            }
+            if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
+                if (callingUid != Process.SYSTEM_UID) {
+                    throw new SecurityException("Job has invalid flags");
+                }
+                if (job.hasLateConstraint() || job.hasEarlyConstraint()) {
+                    Slog.wtf(TAG, "Jobs with time-constraints mustn't have"
+                            +" FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
+                }
+            }
+        }
+
         // IJobScheduler implementation
         @Override
         public int schedule(JobInfo job) throws RemoteException {
@@ -2313,10 +2331,7 @@
                 }
             }
 
-            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
-                getContext().enforceCallingOrSelfPermission(
-                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
-            }
+            validateJobFlags(job, uid);
 
             long ident = Binder.clearCallingIdentity();
             try {
@@ -2344,10 +2359,7 @@
                 throw new NullPointerException("work is null");
             }
 
-            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
-                getContext().enforceCallingOrSelfPermission(
-                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
-            }
+            validateJobFlags(job, uid);
 
             long ident = Binder.clearCallingIdentity();
             try {
@@ -2378,10 +2390,7 @@
                         + " not permitted to schedule jobs for other apps");
             }
 
-            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
-                getContext().enforceCallingOrSelfPermission(
-                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
-            }
+            validateJobFlags(job, callerUid);
 
             long ident = Binder.clearCallingIdentity();
             try {