• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.job.controllers;
18 
19 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
20 import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX;
21 import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
22 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
23 import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
24 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
25 
26 import android.annotation.ElapsedRealtimeLong;
27 import android.app.AppGlobals;
28 import android.app.job.JobInfo;
29 import android.app.job.JobParameters;
30 import android.app.job.JobWorkItem;
31 import android.content.ClipData;
32 import android.content.ComponentName;
33 import android.content.pm.ServiceInfo;
34 import android.net.Network;
35 import android.net.NetworkRequest;
36 import android.net.Uri;
37 import android.os.RemoteException;
38 import android.os.UserHandle;
39 import android.provider.MediaStore;
40 import android.text.format.DateFormat;
41 import android.util.ArraySet;
42 import android.util.IndentingPrintWriter;
43 import android.util.Pair;
44 import android.util.Range;
45 import android.util.Slog;
46 import android.util.TimeUtils;
47 import android.util.proto.ProtoOutputStream;
48 
49 import com.android.internal.util.ArrayUtils;
50 import com.android.internal.util.FrameworkStatsLog;
51 import com.android.server.LocalServices;
52 import com.android.server.job.GrantedUriPermissions;
53 import com.android.server.job.JobSchedulerInternal;
54 import com.android.server.job.JobSchedulerService;
55 import com.android.server.job.JobServerProtoEnums;
56 import com.android.server.job.JobStatusDumpProto;
57 import com.android.server.job.JobStatusShortInfoProto;
58 
59 import dalvik.annotation.optimization.NeverCompile;
60 
61 import java.io.PrintWriter;
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Collections;
65 import java.util.function.Predicate;
66 
67 /**
68  * Uniquely identifies a job internally.
69  * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler.
70  * Contains current state of the requirements of the job, as well as a function to evaluate
71  * whether it's ready to run.
72  * This object is shared among the various controllers - hence why the different fields are atomic.
73  * This isn't strictly necessary because each controller is only interested in a specific field,
74  * and the receivers that are listening for global state change will all run on the main looper,
75  * but we don't enforce that so this is safer.
76  *
77  * Test: atest com.android.server.job.controllers.JobStatusTest
78  * @hide
79  */
80 public final class JobStatus {
81     private static final String TAG = "JobScheduler.JobStatus";
82     static final boolean DEBUG = JobSchedulerService.DEBUG;
83 
84     private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10;
85 
86     public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
87     public static final long NO_EARLIEST_RUNTIME = 0L;
88 
89     static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
90     static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;  // 1 << 2
91     static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
92     static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
93     static final int CONSTRAINT_TIMING_DELAY = 1<<31;
94     static final int CONSTRAINT_DEADLINE = 1<<30;
95     static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
96     static final int CONSTRAINT_TARE_WEALTH = 1 << 27; // Implicit constraint
97     static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
98     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
99     static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;      // Implicit constraint
100     static final int CONSTRAINT_PREFETCH = 1 << 23;
101     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
102 
103     // The following set of dynamic constraints are for specific use cases (as explained in their
104     // relative naming and comments). Right now, they apply different constraints, which is fine,
105     // but if in the future, we have overlapping dynamic constraint sets, removing one constraint
106     // set may accidentally remove a constraint applied by another dynamic set.
107     // TODO: properly handle overlapping dynamic constraint sets
108 
109     /**
110      * The additional set of dynamic constraints that must be met if the job's effective bucket is
111      * {@link JobSchedulerService#RESTRICTED_INDEX}. Connectivity can be ignored if the job doesn't
112      * need network.
113      */
114     private static final int DYNAMIC_RESTRICTED_CONSTRAINTS =
115             CONSTRAINT_BATTERY_NOT_LOW
116                     | CONSTRAINT_CHARGING
117                     | CONSTRAINT_CONNECTIVITY
118                     | CONSTRAINT_IDLE;
119 
120     /**
121      * The additional set of dynamic constraints that must be met if this is an expedited job that
122      * had a long enough run while the device was Dozing or in battery saver.
123      */
124     private static final int DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS =
125             CONSTRAINT_DEVICE_NOT_DOZING | CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
126 
127     /**
128      * Standard media URIs that contain the media files that might be important to the user.
129      * @see #mHasMediaBackupExemption
130      */
131     private static final Uri[] MEDIA_URIS_FOR_STANDBY_EXEMPTION = {
132             MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
133             MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
134     };
135 
136     /**
137      * The constraints that we want to log to statsd.
138      *
139      * Constraints that can be inferred from other atoms have been excluded to avoid logging too
140      * much information and to reduce redundancy:
141      *
142      * * CONSTRAINT_CHARGING can be inferred with PluggedStateChanged (Atom #32)
143      * * CONSTRAINT_BATTERY_NOT_LOW can be inferred with BatteryLevelChanged (Atom #30)
144      * * CONSTRAINT_CONNECTIVITY can be partially inferred with ConnectivityStateChanged
145      * (Atom #98) and BatterySaverModeStateChanged (Atom #20).
146      * * CONSTRAINT_DEVICE_NOT_DOZING can be mostly inferred with DeviceIdleModeStateChanged
147      * (Atom #21)
148      * * CONSTRAINT_BACKGROUND_NOT_RESTRICTED can be inferred with BatterySaverModeStateChanged
149      * (Atom #20)
150      * * CONSTRAINT_STORAGE_NOT_LOW can be inferred with LowStorageStateChanged (Atom #130)
151      */
152     private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
153             | CONSTRAINT_DEADLINE
154             | CONSTRAINT_PREFETCH
155             | CONSTRAINT_TARE_WEALTH
156             | CONSTRAINT_TIMING_DELAY
157             | CONSTRAINT_WITHIN_QUOTA;
158 
159     // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot
160     private static final boolean STATS_LOG_ENABLED = false;
161 
162     // No override.
163     public static final int OVERRIDE_NONE = 0;
164     // Override to improve sorting order. Does not affect constraint evaluation.
165     public static final int OVERRIDE_SORTING = 1;
166     // Soft override: ignore constraints like time that don't affect API availability
167     public static final int OVERRIDE_SOFT = 2;
168     // Full override: ignore all constraints including API-affecting like connectivity
169     public static final int OVERRIDE_FULL = 3;
170 
171     /** If not specified, trigger update delay is 10 seconds. */
172     public static final long DEFAULT_TRIGGER_UPDATE_DELAY = 10*1000;
173 
174     /** The minimum possible update delay is 1/2 second. */
175     public static final long MIN_TRIGGER_UPDATE_DELAY = 500;
176 
177     /** If not specified, trigger maximum delay is 2 minutes. */
178     public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000;
179 
180     /** The minimum possible update delay is 1 second. */
181     public static final long MIN_TRIGGER_MAX_DELAY = 1000;
182 
183     final JobInfo job;
184     /**
185      * Uid of the package requesting this job.  This can differ from the "source"
186      * uid when the job was scheduled on the app's behalf, such as with the jobs
187      * that underly Sync Manager operation.
188      */
189     final int callingUid;
190     final String batteryName;
191 
192     /**
193      * Identity of the app in which the job is hosted.
194      */
195     final String sourcePackageName;
196     final int sourceUserId;
197     final int sourceUid;
198     final String sourceTag;
199 
200     final String tag;
201 
202     private GrantedUriPermissions uriPerms;
203     private boolean prepared;
204 
205     static final boolean DEBUG_PREPARE = true;
206     private Throwable unpreparedPoint = null;
207 
208     /**
209      * Earliest point in the future at which this job will be eligible to run. A value of 0
210      * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
211      */
212     private final long earliestRunTimeElapsedMillis;
213     /**
214      * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
215      * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
216      */
217     private final long latestRunTimeElapsedMillis;
218 
219     /**
220      * Valid only for periodic jobs. The original latest point in the future at which this
221      * job was expected to run.
222      */
223     private long mOriginalLatestRunTimeElapsedMillis;
224 
225     /** How many times this job has failed, used to compute back-off. */
226     private final int numFailures;
227 
228     /**
229      * Which app standby bucket this job's app is in.  Updated when the app is moved to a
230      * different bucket.
231      */
232     private int standbyBucket;
233 
234     /**
235      * Whether we've logged an error due to standby bucket mismatch with active uid state.
236      */
237     private boolean mLoggedBucketMismatch;
238 
239     /**
240      * Debugging: timestamp if we ever defer this job based on standby bucketing, this
241      * is when we did so.
242      */
243     private long whenStandbyDeferred;
244 
245     /** The first time this job was force batched. */
246     private long mFirstForceBatchedTimeElapsed;
247 
248     // Constraints.
249     final int requiredConstraints;
250     private final int mRequiredConstraintsOfInterest;
251     int satisfiedConstraints = 0;
252     private int mSatisfiedConstraintsOfInterest = 0;
253     /**
254      * Set of constraints that must be satisfied for the job if/because it's in the RESTRICTED
255      * bucket.
256      */
257     private int mDynamicConstraints = 0;
258 
259     /**
260      * Indicates whether the job is responsible for backing up media, so we can be lenient in
261      * applying standby throttling.
262      *
263      * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or
264      * network changes, in which case this exemption does not make sense.
265      */
266     private boolean mHasMediaBackupExemption;
267     private final boolean mHasExemptedMediaUrisOnly;
268 
269     // Set to true if doze constraint was satisfied due to app being whitelisted.
270     boolean appHasDozeExemption;
271 
272     // Set to true when the app is "active" per AppStateTracker
273     public boolean uidActive;
274 
275     /**
276      * Flag for {@link #trackingControllers}: the battery controller is currently tracking this job.
277      */
278     public static final int TRACKING_BATTERY = 1<<0;
279     /**
280      * Flag for {@link #trackingControllers}: the network connectivity controller is currently
281      * tracking this job.
282      */
283     public static final int TRACKING_CONNECTIVITY = 1<<1;
284     /**
285      * Flag for {@link #trackingControllers}: the content observer controller is currently
286      * tracking this job.
287      */
288     public static final int TRACKING_CONTENT = 1<<2;
289     /**
290      * Flag for {@link #trackingControllers}: the idle controller is currently tracking this job.
291      */
292     public static final int TRACKING_IDLE = 1<<3;
293     /**
294      * Flag for {@link #trackingControllers}: the storage controller is currently tracking this job.
295      */
296     public static final int TRACKING_STORAGE = 1<<4;
297     /**
298      * Flag for {@link #trackingControllers}: the time controller is currently tracking this job.
299      */
300     public static final int TRACKING_TIME = 1<<5;
301     /**
302      * Flag for {@link #trackingControllers}: the quota controller is currently tracking this job.
303      */
304     public static final int TRACKING_QUOTA = 1 << 6;
305 
306     /**
307      * Bit mask of controllers that are currently tracking the job.
308      */
309     private int trackingControllers;
310 
311     /**
312      * Flag for {@link #mInternalFlags}: this job was scheduled when the app that owns the job
313      * service (not necessarily the caller) was in the foreground and the job has no time
314      * constraints, which makes it exempted from the battery saver job restriction.
315      *
316      * @hide
317      */
318     public static final int INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION = 1 << 0;
319 
320     /**
321      * Versatile, persistable flags for a job that's updated within the system server,
322      * as opposed to {@link JobInfo#flags} that's set by callers.
323      */
324     private int mInternalFlags;
325 
326     // These are filled in by controllers when preparing for execution.
327     public ArraySet<Uri> changedUris;
328     public ArraySet<String> changedAuthorities;
329     public Network network;
330     public ServiceInfo serviceInfo;
331 
332     /** The evaluated bias of the job when it started running. */
333     public int lastEvaluatedBias;
334 
335     /**
336      * Whether or not this particular JobStatus instance was treated as an EJ when it started
337      * running. This isn't copied over when a job is rescheduled.
338      */
339     public boolean startedAsExpeditedJob = false;
340 
341     // If non-null, this is work that has been enqueued for the job.
342     public ArrayList<JobWorkItem> pendingWork;
343 
344     // If non-null, this is work that is currently being executed.
345     public ArrayList<JobWorkItem> executingWork;
346 
347     public int nextPendingWorkId = 1;
348 
349     // Used by shell commands
350     public int overrideState = JobStatus.OVERRIDE_NONE;
351 
352     // When this job was enqueued, for ordering.  (in elapsedRealtimeMillis)
353     @ElapsedRealtimeLong
354     public long enqueueTime;
355 
356     // Metrics about queue latency.  (in uptimeMillis)
357     public long madePending;
358     public long madeActive;
359 
360     /**
361      * Last time a job finished successfully for a periodic job, in the currentTimeMillis time,
362      * for dumpsys.
363      */
364     private long mLastSuccessfulRunTime;
365 
366     /**
367      * Last time a job finished unsuccessfully, in the currentTimeMillis time, for dumpsys.
368      */
369     private long mLastFailedRunTime;
370 
371     /** Whether or not the app is background restricted by the user (FAS). */
372     private boolean mIsUserBgRestricted;
373 
374     /**
375      * Transient: when a job is inflated from disk before we have a reliable RTC clock time,
376      * we retain the canonical (delay, deadline) scheduling tuple read out of the persistent
377      * store in UTC so that we can fix up the job's scheduling criteria once we get a good
378      * wall-clock time.  If we have to persist the job again before the clock has been updated,
379      * we record these times again rather than calculating based on the earliest/latest elapsed
380      * time base figures.
381      *
382      * 'first' is the earliest/delay time, and 'second' is the latest/deadline time.
383      */
384     private Pair<Long, Long> mPersistedUtcTimes;
385 
386     private int mConstraintChangeHistoryIndex = 0;
387     private final long[] mConstraintUpdatedTimesElapsed = new long[NUM_CONSTRAINT_CHANGE_HISTORY];
388     private final int[] mConstraintStatusHistory = new int[NUM_CONSTRAINT_CHANGE_HISTORY];
389 
390     /**
391      * For use only by ContentObserverController: state it is maintaining about content URIs
392      * being observed.
393      */
394     ContentObserverController.JobInstance contentObserverJobInstance;
395 
396     private long mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
397     private long mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
398     private long mMinimumNetworkChunkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
399 
400     /**
401      * Whether or not this job is approved to be treated as expedited per quota policy.
402      */
403     private boolean mExpeditedQuotaApproved;
404     /**
405      * Whether or not this job is approved to be treated as expedited per TARE policy.
406      */
407     private boolean mExpeditedTareApproved;
408 
409     /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
410     /////// states change.
411 
412     /**
413      * The deadline for the job has passed. This is only good for non-periodic jobs. A periodic job
414      * should only run if its constraints are satisfied.
415      * Computed as: NOT periodic AND has deadline constraint AND deadline constraint satisfied.
416      */
417     private boolean mReadyDeadlineSatisfied;
418 
419     /**
420      * The device isn't Dozing or this job is exempt from Dozing (eg. it will be in the foreground
421      * or will run as an expedited job). This implicit constraint must be satisfied.
422      */
423     private boolean mReadyNotDozing;
424 
425     /**
426      * The job is not restricted from running in the background (due to Battery Saver). This
427      * implicit constraint must be satisfied.
428      */
429     private boolean mReadyNotRestrictedInBg;
430 
431     /** The job is within its quota based on its standby bucket. */
432     private boolean mReadyWithinQuota;
433 
434     /** The job has enough credits to run based on TARE. */
435     private boolean mReadyTareWealth;
436 
437     /** The job's dynamic requirements have been satisfied. */
438     private boolean mReadyDynamicSatisfied;
439 
440     /** The reason a job most recently went from ready to not ready. */
441     private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
442 
443     /**
444      * Core constructor for JobStatus instances.  All other ctors funnel down to this one.
445      *
446      * @param job The actual requested parameters for the job
447      * @param callingUid Identity of the app that is scheduling the job.  This may not be the
448      *     app in which the job is implemented; such as with sync jobs.
449      * @param sourcePackageName The package name of the app in which the job will run.
450      * @param sourceUserId The user in which the job will run
451      * @param standbyBucket The standby bucket that the source package is currently assigned to,
452      *     cached here for speed of handling during runnability evaluations (and updated when bucket
453      *     assignments are changed)
454      * @param tag A string associated with the job for debugging/logging purposes.
455      * @param numFailures Count of how many times this job has requested a reschedule because
456      *     its work was not yet finished.
457      * @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job
458      *     is to be considered runnable
459      * @param latestRunTimeElapsedMillis Milestone: point in time at which the job will be
460      *     considered overdue
461      * @param lastSuccessfulRunTime When did we last run this job to completion?
462      * @param lastFailedRunTime When did we last run this job only to have it stop incomplete?
463      * @param internalFlags Non-API property flags about this job
464      */
JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, int standbyBucket, String tag, int numFailures, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags, int dynamicConstraints)465     private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
466             int sourceUserId, int standbyBucket, String tag, int numFailures,
467             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
468             long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags,
469             int dynamicConstraints) {
470         this.job = job;
471         this.callingUid = callingUid;
472         this.standbyBucket = standbyBucket;
473 
474         int tempSourceUid = -1;
475         if (sourceUserId != -1 && sourcePackageName != null) {
476             try {
477                 tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
478                         sourceUserId);
479             } catch (RemoteException ex) {
480                 // Can't happen, PackageManager runs in the same process.
481             }
482         }
483         if (tempSourceUid == -1) {
484             this.sourceUid = callingUid;
485             this.sourceUserId = UserHandle.getUserId(callingUid);
486             this.sourcePackageName = job.getService().getPackageName();
487             this.sourceTag = null;
488         } else {
489             this.sourceUid = tempSourceUid;
490             this.sourceUserId = sourceUserId;
491             this.sourcePackageName = sourcePackageName;
492             this.sourceTag = tag;
493         }
494 
495         this.batteryName = this.sourceTag != null
496                 ? this.sourceTag + ":" + job.getService().getPackageName()
497                 : job.getService().flattenToShortString();
498         this.tag = "*job*/" + this.batteryName + "#" + job.getId();
499 
500         this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
501         this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
502         this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
503         this.numFailures = numFailures;
504 
505         int requiredConstraints = job.getConstraintFlags();
506         if (job.getRequiredNetwork() != null) {
507             requiredConstraints |= CONSTRAINT_CONNECTIVITY;
508         }
509         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
510             requiredConstraints |= CONSTRAINT_TIMING_DELAY;
511         }
512         if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
513             requiredConstraints |= CONSTRAINT_DEADLINE;
514         }
515         if (job.isPrefetch()) {
516             requiredConstraints |= CONSTRAINT_PREFETCH;
517         }
518         boolean exemptedMediaUrisOnly = false;
519         if (job.getTriggerContentUris() != null) {
520             requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
521             exemptedMediaUrisOnly = true;
522             for (JobInfo.TriggerContentUri uri : job.getTriggerContentUris()) {
523                 if (!ArrayUtils.contains(MEDIA_URIS_FOR_STANDBY_EXEMPTION, uri.getUri())) {
524                     exemptedMediaUrisOnly = false;
525                     break;
526                 }
527             }
528         }
529         mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly;
530         this.requiredConstraints = requiredConstraints;
531         mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
532         addDynamicConstraints(dynamicConstraints);
533         mReadyNotDozing = canRunInDoze();
534         if (standbyBucket == RESTRICTED_INDEX) {
535             addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
536         } else {
537             mReadyDynamicSatisfied = false;
538         }
539 
540         mLastSuccessfulRunTime = lastSuccessfulRunTime;
541         mLastFailedRunTime = lastFailedRunTime;
542 
543         mInternalFlags = internalFlags;
544 
545         updateNetworkBytesLocked();
546 
547         if (job.getRequiredNetwork() != null) {
548             // Later, when we check if a given network satisfies the required
549             // network, we need to know the UID that is requesting it, so push
550             // our source UID into place.
551             final JobInfo.Builder builder = new JobInfo.Builder(job);
552             final NetworkRequest.Builder requestBuilder =
553                     new NetworkRequest.Builder(job.getRequiredNetwork());
554             requestBuilder.setUids(
555                     Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid)));
556             builder.setRequiredNetwork(requestBuilder.build());
557             // Don't perform prefetch-deadline check at this point. We've already passed the
558             // initial validation check.
559             job = builder.build(false);
560         }
561 
562         updateMediaBackupExemptionStatus();
563     }
564 
565     /** Copy constructor: used specifically when cloning JobStatus objects for persistence,
566      *   so we preserve RTC window bounds if the source object has them. */
JobStatus(JobStatus jobStatus)567     public JobStatus(JobStatus jobStatus) {
568         this(jobStatus.getJob(), jobStatus.getUid(),
569                 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
570                 jobStatus.getStandbyBucket(),
571                 jobStatus.getSourceTag(), jobStatus.getNumFailures(),
572                 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
573                 jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
574                 jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints);
575         mPersistedUtcTimes = jobStatus.mPersistedUtcTimes;
576         if (jobStatus.mPersistedUtcTimes != null) {
577             if (DEBUG) {
578                 Slog.i(TAG, "Cloning job with persisted run times", new RuntimeException("here"));
579             }
580         }
581     }
582 
583     /**
584      * Create a new JobStatus that was loaded from disk. We ignore the provided
585      * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job
586      * from the {@link com.android.server.job.JobStore} and still want to respect its
587      * wallclock runtime rather than resetting it on every boot.
588      * We consider a freshly loaded job to no longer be in back-off, and the associated
589      * standby bucket is whatever the OS thinks it should be at this moment.
590      */
JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId, int standbyBucket, String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, Pair<Long, Long> persistedExecutionTimesUTC, int innerFlags, int dynamicConstraints)591     public JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId,
592             int standbyBucket, String sourceTag,
593             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
594             long lastSuccessfulRunTime, long lastFailedRunTime,
595             Pair<Long, Long> persistedExecutionTimesUTC,
596             int innerFlags, int dynamicConstraints) {
597         this(job, callingUid, sourcePkgName, sourceUserId,
598                 standbyBucket,
599                 sourceTag, 0,
600                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
601                 lastSuccessfulRunTime, lastFailedRunTime, innerFlags, dynamicConstraints);
602 
603         // Only during initial inflation do we record the UTC-timebase execution bounds
604         // read from the persistent store.  If we ever have to recreate the JobStatus on
605         // the fly, it means we're rescheduling the job; and this means that the calculated
606         // elapsed timebase bounds intrinsically become correct.
607         this.mPersistedUtcTimes = persistedExecutionTimesUTC;
608         if (persistedExecutionTimesUTC != null) {
609             if (DEBUG) {
610                 Slog.i(TAG, "+ restored job with RTC times because of bad boot clock");
611             }
612         }
613     }
614 
615     /** Create a new job to be rescheduled with the provided parameters. */
JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int backoffAttempt, long lastSuccessfulRunTime, long lastFailedRunTime)616     public JobStatus(JobStatus rescheduling,
617             long newEarliestRuntimeElapsedMillis,
618             long newLatestRuntimeElapsedMillis, int backoffAttempt,
619             long lastSuccessfulRunTime, long lastFailedRunTime) {
620         this(rescheduling.job, rescheduling.getUid(),
621                 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
622                 rescheduling.getStandbyBucket(),
623                 rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
624                 newLatestRuntimeElapsedMillis,
625                 lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags(),
626                 rescheduling.mDynamicConstraints);
627     }
628 
629     /**
630      * Create a newly scheduled job.
631      * @param callingUid Uid of the package that scheduled this job.
632      * @param sourcePkg Package name of the app that will actually run the job.  Null indicates
633      *     that the calling package is the source.
634      * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
635      *     caller.
636      */
createFromJobInfo(JobInfo job, int callingUid, String sourcePkg, int sourceUserId, String tag)637     public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePkg,
638             int sourceUserId, String tag) {
639         final long elapsedNow = sElapsedRealtimeClock.millis();
640         final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
641         if (job.isPeriodic()) {
642             // Make sure period is in the interval [min_possible_period, max_possible_period].
643             final long period = Math.max(JobInfo.getMinPeriodMillis(),
644                     Math.min(JobSchedulerService.MAX_ALLOWED_PERIOD_MS, job.getIntervalMillis()));
645             latestRunTimeElapsedMillis = elapsedNow + period;
646             earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis
647                     // Make sure flex is in the interval [min_possible_flex, period].
648                     - Math.max(JobInfo.getMinFlexMillis(), Math.min(period, job.getFlexMillis()));
649         } else {
650             earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
651                     elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
652             latestRunTimeElapsedMillis = job.hasLateConstraint() ?
653                     elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
654         }
655         String jobPackage = (sourcePkg != null) ? sourcePkg : job.getService().getPackageName();
656 
657         int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
658                 sourceUserId, elapsedNow);
659         return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
660                 standbyBucket, tag, 0,
661                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
662                 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
663                 /*innerFlags=*/ 0, /* dynamicConstraints */ 0);
664     }
665 
enqueueWorkLocked(JobWorkItem work)666     public void enqueueWorkLocked(JobWorkItem work) {
667         if (pendingWork == null) {
668             pendingWork = new ArrayList<>();
669         }
670         work.setWorkId(nextPendingWorkId);
671         nextPendingWorkId++;
672         if (work.getIntent() != null
673                 && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) {
674             work.setGrants(GrantedUriPermissions.createFromIntent(work.getIntent(), sourceUid,
675                     sourcePackageName, sourceUserId, toShortString()));
676         }
677         pendingWork.add(work);
678         updateNetworkBytesLocked();
679     }
680 
dequeueWorkLocked()681     public JobWorkItem dequeueWorkLocked() {
682         if (pendingWork != null && pendingWork.size() > 0) {
683             JobWorkItem work = pendingWork.remove(0);
684             if (work != null) {
685                 if (executingWork == null) {
686                     executingWork = new ArrayList<>();
687                 }
688                 executingWork.add(work);
689                 work.bumpDeliveryCount();
690             }
691             updateNetworkBytesLocked();
692             return work;
693         }
694         return null;
695     }
696 
hasWorkLocked()697     public boolean hasWorkLocked() {
698         return (pendingWork != null && pendingWork.size() > 0) || hasExecutingWorkLocked();
699     }
700 
hasExecutingWorkLocked()701     public boolean hasExecutingWorkLocked() {
702         return executingWork != null && executingWork.size() > 0;
703     }
704 
ungrantWorkItem(JobWorkItem work)705     private static void ungrantWorkItem(JobWorkItem work) {
706         if (work.getGrants() != null) {
707             ((GrantedUriPermissions)work.getGrants()).revoke();
708         }
709     }
710 
completeWorkLocked(int workId)711     public boolean completeWorkLocked(int workId) {
712         if (executingWork != null) {
713             final int N = executingWork.size();
714             for (int i = 0; i < N; i++) {
715                 JobWorkItem work = executingWork.get(i);
716                 if (work.getWorkId() == workId) {
717                     executingWork.remove(i);
718                     ungrantWorkItem(work);
719                     return true;
720                 }
721             }
722         }
723         return false;
724     }
725 
ungrantWorkList(ArrayList<JobWorkItem> list)726     private static void ungrantWorkList(ArrayList<JobWorkItem> list) {
727         if (list != null) {
728             final int N = list.size();
729             for (int i = 0; i < N; i++) {
730                 ungrantWorkItem(list.get(i));
731             }
732         }
733     }
734 
stopTrackingJobLocked(JobStatus incomingJob)735     public void stopTrackingJobLocked(JobStatus incomingJob) {
736         if (incomingJob != null) {
737             // We are replacing with a new job -- transfer the work!  We do any executing
738             // work first, since that was originally at the front of the pending work.
739             if (executingWork != null && executingWork.size() > 0) {
740                 incomingJob.pendingWork = executingWork;
741             }
742             if (incomingJob.pendingWork == null) {
743                 incomingJob.pendingWork = pendingWork;
744             } else if (pendingWork != null && pendingWork.size() > 0) {
745                 incomingJob.pendingWork.addAll(pendingWork);
746             }
747             pendingWork = null;
748             executingWork = null;
749             incomingJob.nextPendingWorkId = nextPendingWorkId;
750             incomingJob.updateNetworkBytesLocked();
751         } else {
752             // We are completely stopping the job...  need to clean up work.
753             ungrantWorkList(pendingWork);
754             pendingWork = null;
755             ungrantWorkList(executingWork);
756             executingWork = null;
757         }
758         updateNetworkBytesLocked();
759     }
760 
prepareLocked()761     public void prepareLocked() {
762         if (prepared) {
763             Slog.wtf(TAG, "Already prepared: " + this);
764             return;
765         }
766         prepared = true;
767         if (DEBUG_PREPARE) {
768             unpreparedPoint = null;
769         }
770         final ClipData clip = job.getClipData();
771         if (clip != null) {
772             uriPerms = GrantedUriPermissions.createFromClip(clip, sourceUid, sourcePackageName,
773                     sourceUserId, job.getClipGrantFlags(), toShortString());
774         }
775     }
776 
unprepareLocked()777     public void unprepareLocked() {
778         if (!prepared) {
779             Slog.wtf(TAG, "Hasn't been prepared: " + this);
780             if (DEBUG_PREPARE && unpreparedPoint != null) {
781                 Slog.e(TAG, "Was already unprepared at ", unpreparedPoint);
782             }
783             return;
784         }
785         prepared = false;
786         if (DEBUG_PREPARE) {
787             unpreparedPoint = new Throwable().fillInStackTrace();
788         }
789         if (uriPerms != null) {
790             uriPerms.revoke();
791             uriPerms = null;
792         }
793     }
794 
isPreparedLocked()795     public boolean isPreparedLocked() {
796         return prepared;
797     }
798 
getJob()799     public JobInfo getJob() {
800         return job;
801     }
802 
getJobId()803     public int getJobId() {
804         return job.getId();
805     }
806 
printUniqueId(PrintWriter pw)807     public void printUniqueId(PrintWriter pw) {
808         UserHandle.formatUid(pw, callingUid);
809         pw.print("/");
810         pw.print(job.getId());
811     }
812 
getNumFailures()813     public int getNumFailures() {
814         return numFailures;
815     }
816 
getServiceComponent()817     public ComponentName getServiceComponent() {
818         return job.getService();
819     }
820 
getSourcePackageName()821     public String getSourcePackageName() {
822         return sourcePackageName;
823     }
824 
getSourceUid()825     public int getSourceUid() {
826         return sourceUid;
827     }
828 
getSourceUserId()829     public int getSourceUserId() {
830         return sourceUserId;
831     }
832 
getUserId()833     public int getUserId() {
834         return UserHandle.getUserId(callingUid);
835     }
836 
837     /**
838      * Returns an appropriate standby bucket for the job, taking into account any standby
839      * exemptions.
840      */
getEffectiveStandbyBucket()841     public int getEffectiveStandbyBucket() {
842         final int actualBucket = getStandbyBucket();
843         if (actualBucket == EXEMPTED_INDEX) {
844             return actualBucket;
845         }
846         if (uidActive || getJob().isExemptedFromAppStandby()) {
847             // Treat these cases as if they're in the ACTIVE bucket so that they get throttled
848             // like other ACTIVE apps.
849             return ACTIVE_INDEX;
850         }
851         if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX
852                 && mHasMediaBackupExemption) {
853             // Cap it at WORKING_INDEX as media back up jobs are important to the user, and the
854             // source package may not have been used directly in a while.
855             return Math.min(WORKING_INDEX, actualBucket);
856         }
857         return actualBucket;
858     }
859 
860     /** Returns the real standby bucket of the job. */
getStandbyBucket()861     public int getStandbyBucket() {
862         return standbyBucket;
863     }
864 
setStandbyBucket(int newBucket)865     public void setStandbyBucket(int newBucket) {
866         if (newBucket == RESTRICTED_INDEX) {
867             // Adding to the bucket.
868             addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
869         } else if (standbyBucket == RESTRICTED_INDEX) {
870             // Removing from the RESTRICTED bucket.
871             removeDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
872         }
873 
874         standbyBucket = newBucket;
875         mLoggedBucketMismatch = false;
876     }
877 
878     /**
879      * Log a bucket mismatch if this is the first time for this job.
880      */
maybeLogBucketMismatch()881     public void maybeLogBucketMismatch() {
882         if (!mLoggedBucketMismatch) {
883             Slog.wtf(TAG,
884                     "App " + getSourcePackageName() + " became active but still in NEVER bucket");
885             mLoggedBucketMismatch = true;
886         }
887     }
888 
889     // Called only by the standby monitoring code
getWhenStandbyDeferred()890     public long getWhenStandbyDeferred() {
891         return whenStandbyDeferred;
892     }
893 
894     // Called only by the standby monitoring code
setWhenStandbyDeferred(long now)895     public void setWhenStandbyDeferred(long now) {
896         whenStandbyDeferred = now;
897     }
898 
899     /**
900      * Returns the first time this job was force batched, in the elapsed realtime timebase. Will be
901      * 0 if this job was never force batched.
902      */
getFirstForceBatchedTimeElapsed()903     public long getFirstForceBatchedTimeElapsed() {
904         return mFirstForceBatchedTimeElapsed;
905     }
906 
setFirstForceBatchedTimeElapsed(long now)907     public void setFirstForceBatchedTimeElapsed(long now) {
908         mFirstForceBatchedTimeElapsed = now;
909     }
910 
911     /**
912      * Re-evaluates the media backup exemption status.
913      *
914      * @return true if the exemption status changed
915      */
updateMediaBackupExemptionStatus()916     public boolean updateMediaBackupExemptionStatus() {
917         final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
918         boolean hasMediaExemption = mHasExemptedMediaUrisOnly
919                 && !job.hasLateConstraint()
920                 && job.getRequiredNetwork() != null
921                 && getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT
922                 && sourcePackageName.equals(jsi.getCloudMediaProviderPackage(sourceUserId));
923         if (mHasMediaBackupExemption == hasMediaExemption) {
924             return false;
925         }
926         mHasMediaBackupExemption = hasMediaExemption;
927         return true;
928     }
929 
getSourceTag()930     public String getSourceTag() {
931         return sourceTag;
932     }
933 
getUid()934     public int getUid() {
935         return callingUid;
936     }
937 
getBatteryName()938     public String getBatteryName() {
939         return batteryName;
940     }
941 
getTag()942     public String getTag() {
943         return tag;
944     }
945 
getBias()946     public int getBias() {
947         return job.getBias();
948     }
949 
950     /**
951      * Returns the priority of the job, which may be adjusted due to various factors.
952      * @see JobInfo.Builder#setPriority(int)
953      */
954     @JobInfo.Priority
getEffectivePriority()955     public int getEffectivePriority() {
956         final int rawPriority = job.getPriority();
957         if (numFailures < 2) {
958             return rawPriority;
959         }
960         // Slowly decay priority of jobs to prevent starvation of other jobs.
961         if (isRequestedExpeditedJob()) {
962             // EJs can't fall below HIGH priority.
963             return JobInfo.PRIORITY_HIGH;
964         }
965         // Set a maximum priority based on the number of failures.
966         final int dropPower = numFailures / 2;
967         switch (dropPower) {
968             case 1: return Math.min(JobInfo.PRIORITY_DEFAULT, rawPriority);
969             case 2: return Math.min(JobInfo.PRIORITY_LOW, rawPriority);
970             default: return JobInfo.PRIORITY_MIN;
971         }
972     }
973 
getFlags()974     public int getFlags() {
975         return job.getFlags();
976     }
977 
getInternalFlags()978     public int getInternalFlags() {
979         return mInternalFlags;
980     }
981 
addInternalFlags(int flags)982     public void addInternalFlags(int flags) {
983         mInternalFlags |= flags;
984     }
985 
getSatisfiedConstraintFlags()986     public int getSatisfiedConstraintFlags() {
987         return satisfiedConstraints;
988     }
989 
maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker)990     public void maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker) {
991         // Jobs with time constraints shouldn't be exempted.
992         if (job.hasEarlyConstraint() || job.hasLateConstraint()) {
993             return;
994         }
995         // Already exempted, skip the foreground check.
996         if ((mInternalFlags & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
997             return;
998         }
999         if (uidForegroundChecker.test(getSourceUid())) {
1000             addInternalFlags(INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION);
1001         }
1002     }
1003 
updateNetworkBytesLocked()1004     private void updateNetworkBytesLocked() {
1005         mTotalNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
1006         mTotalNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
1007         mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
1008 
1009         if (pendingWork != null) {
1010             for (int i = 0; i < pendingWork.size(); i++) {
1011                 if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1012                     // If any component of the job has unknown usage, we don't have a
1013                     // complete picture of what data will be used, and we have to treat the
1014                     // entire up/download as unknown.
1015                     long downloadBytes = pendingWork.get(i).getEstimatedNetworkDownloadBytes();
1016                     if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1017                         mTotalNetworkDownloadBytes += downloadBytes;
1018                     }
1019                 }
1020                 if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1021                     // If any component of the job has unknown usage, we don't have a
1022                     // complete picture of what data will be used, and we have to treat the
1023                     // entire up/download as unknown.
1024                     long uploadBytes = pendingWork.get(i).getEstimatedNetworkUploadBytes();
1025                     if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1026                         mTotalNetworkUploadBytes += uploadBytes;
1027                     }
1028                 }
1029                 final long chunkBytes = pendingWork.get(i).getMinimumNetworkChunkBytes();
1030                 if (mMinimumNetworkChunkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
1031                     mMinimumNetworkChunkBytes = chunkBytes;
1032                 } else if (chunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1033                     mMinimumNetworkChunkBytes = Math.min(mMinimumNetworkChunkBytes, chunkBytes);
1034                 }
1035             }
1036         }
1037     }
1038 
getEstimatedNetworkDownloadBytes()1039     public long getEstimatedNetworkDownloadBytes() {
1040         return mTotalNetworkDownloadBytes;
1041     }
1042 
getEstimatedNetworkUploadBytes()1043     public long getEstimatedNetworkUploadBytes() {
1044         return mTotalNetworkUploadBytes;
1045     }
1046 
getMinimumNetworkChunkBytes()1047     public long getMinimumNetworkChunkBytes() {
1048         return mMinimumNetworkChunkBytes;
1049     }
1050 
1051     /** Does this job have any sort of networking constraint? */
hasConnectivityConstraint()1052     public boolean hasConnectivityConstraint() {
1053         // No need to check mDynamicConstraints since connectivity will only be in that list if
1054         // it's already in the requiredConstraints list.
1055         return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
1056     }
1057 
hasChargingConstraint()1058     public boolean hasChargingConstraint() {
1059         return hasConstraint(CONSTRAINT_CHARGING);
1060     }
1061 
hasBatteryNotLowConstraint()1062     public boolean hasBatteryNotLowConstraint() {
1063         return hasConstraint(CONSTRAINT_BATTERY_NOT_LOW);
1064     }
1065 
1066     /** Returns true if the job requires charging OR battery not low. */
hasPowerConstraint()1067     boolean hasPowerConstraint() {
1068         return hasConstraint(CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW);
1069     }
1070 
hasStorageNotLowConstraint()1071     public boolean hasStorageNotLowConstraint() {
1072         return hasConstraint(CONSTRAINT_STORAGE_NOT_LOW);
1073     }
1074 
hasTimingDelayConstraint()1075     public boolean hasTimingDelayConstraint() {
1076         return hasConstraint(CONSTRAINT_TIMING_DELAY);
1077     }
1078 
hasDeadlineConstraint()1079     public boolean hasDeadlineConstraint() {
1080         return hasConstraint(CONSTRAINT_DEADLINE);
1081     }
1082 
hasIdleConstraint()1083     public boolean hasIdleConstraint() {
1084         return hasConstraint(CONSTRAINT_IDLE);
1085     }
1086 
hasContentTriggerConstraint()1087     public boolean hasContentTriggerConstraint() {
1088         // No need to check mDynamicConstraints since content trigger will only be in that list if
1089         // it's already in the requiredConstraints list.
1090         return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
1091     }
1092 
1093     /**
1094      * Checks both {@link #requiredConstraints} and {@link #mDynamicConstraints} to see if this job
1095      * requires the specified constraint.
1096      */
hasConstraint(int constraint)1097     private boolean hasConstraint(int constraint) {
1098         return (requiredConstraints & constraint) != 0 || (mDynamicConstraints & constraint) != 0;
1099     }
1100 
getTriggerContentUpdateDelay()1101     public long getTriggerContentUpdateDelay() {
1102         long time = job.getTriggerContentUpdateDelay();
1103         if (time < 0) {
1104             return DEFAULT_TRIGGER_UPDATE_DELAY;
1105         }
1106         return Math.max(time, MIN_TRIGGER_UPDATE_DELAY);
1107     }
1108 
getTriggerContentMaxDelay()1109     public long getTriggerContentMaxDelay() {
1110         long time = job.getTriggerContentMaxDelay();
1111         if (time < 0) {
1112             return DEFAULT_TRIGGER_MAX_DELAY;
1113         }
1114         return Math.max(time, MIN_TRIGGER_MAX_DELAY);
1115     }
1116 
isPersisted()1117     public boolean isPersisted() {
1118         return job.isPersisted();
1119     }
1120 
getEarliestRunTime()1121     public long getEarliestRunTime() {
1122         return earliestRunTimeElapsedMillis;
1123     }
1124 
getLatestRunTimeElapsed()1125     public long getLatestRunTimeElapsed() {
1126         return latestRunTimeElapsedMillis;
1127     }
1128 
getOriginalLatestRunTimeElapsed()1129     public long getOriginalLatestRunTimeElapsed() {
1130         return mOriginalLatestRunTimeElapsedMillis;
1131     }
1132 
setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed)1133     public void setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed) {
1134         mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed;
1135     }
1136 
1137     @JobParameters.StopReason
getStopReason()1138     public int getStopReason() {
1139         return mReasonReadyToUnready;
1140     }
1141 
1142     /**
1143      * Return the fractional position of "now" within the "run time" window of
1144      * this job.
1145      * <p>
1146      * For example, if the earliest run time was 10 minutes ago, and the latest
1147      * run time is 30 minutes from now, this would return 0.25.
1148      * <p>
1149      * If the job has no window defined, returns 1. When only an earliest or
1150      * latest time is defined, it's treated as an infinitely small window at
1151      * that time.
1152      */
getFractionRunTime()1153     public float getFractionRunTime() {
1154         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
1155         if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME
1156                 && latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) {
1157             return 1;
1158         } else if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) {
1159             return now >= latestRunTimeElapsedMillis ? 1 : 0;
1160         } else if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) {
1161             return now >= earliestRunTimeElapsedMillis ? 1 : 0;
1162         } else {
1163             if (now <= earliestRunTimeElapsedMillis) {
1164                 return 0;
1165             } else if (now >= latestRunTimeElapsedMillis) {
1166                 return 1;
1167             } else {
1168                 return (float) (now - earliestRunTimeElapsedMillis)
1169                         / (float) (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis);
1170             }
1171         }
1172     }
1173 
getPersistedUtcTimes()1174     public Pair<Long, Long> getPersistedUtcTimes() {
1175         return mPersistedUtcTimes;
1176     }
1177 
clearPersistedUtcTimes()1178     public void clearPersistedUtcTimes() {
1179         mPersistedUtcTimes = null;
1180     }
1181 
1182     /** @return true if the app has requested that this run as an expedited job. */
isRequestedExpeditedJob()1183     public boolean isRequestedExpeditedJob() {
1184         return (getFlags() & JobInfo.FLAG_EXPEDITED) != 0;
1185     }
1186 
1187     /**
1188      * @return true if all expedited job requirements are satisfied and therefore this should be
1189      * treated as an expedited job.
1190      */
shouldTreatAsExpeditedJob()1191     public boolean shouldTreatAsExpeditedJob() {
1192         return mExpeditedQuotaApproved && mExpeditedTareApproved && isRequestedExpeditedJob();
1193     }
1194 
1195     /**
1196      * @return true if the job is exempted from Doze restrictions and therefore allowed to run
1197      * in Doze.
1198      */
canRunInDoze()1199     public boolean canRunInDoze() {
1200         return appHasDozeExemption
1201                 || (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
1202                 || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
1203                 && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
1204     }
1205 
canRunInBatterySaver()1206     boolean canRunInBatterySaver() {
1207         return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
1208                 || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
1209                 && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
1210     }
1211 
1212     /** @return true if the constraint was changed, false otherwise. */
setChargingConstraintSatisfied(final long nowElapsed, boolean state)1213     boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) {
1214         return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state);
1215     }
1216 
1217     /** @return true if the constraint was changed, false otherwise. */
setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state)1218     boolean setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state) {
1219         return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, nowElapsed, state);
1220     }
1221 
1222     /** @return true if the constraint was changed, false otherwise. */
setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state)1223     boolean setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state) {
1224         return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, nowElapsed, state);
1225     }
1226 
1227     /** @return true if the constraint was changed, false otherwise. */
setPrefetchConstraintSatisfied(final long nowElapsed, boolean state)1228     boolean setPrefetchConstraintSatisfied(final long nowElapsed, boolean state) {
1229         return setConstraintSatisfied(CONSTRAINT_PREFETCH, nowElapsed, state);
1230     }
1231 
1232     /** @return true if the constraint was changed, false otherwise. */
setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state)1233     boolean setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state) {
1234         return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, nowElapsed, state);
1235     }
1236 
1237     /** @return true if the constraint was changed, false otherwise. */
setDeadlineConstraintSatisfied(final long nowElapsed, boolean state)1238     boolean setDeadlineConstraintSatisfied(final long nowElapsed, boolean state) {
1239         if (setConstraintSatisfied(CONSTRAINT_DEADLINE, nowElapsed, state)) {
1240             // The constraint was changed. Update the ready flag.
1241             mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state;
1242             return true;
1243         }
1244         return false;
1245     }
1246 
1247     /** @return true if the constraint was changed, false otherwise. */
setIdleConstraintSatisfied(final long nowElapsed, boolean state)1248     boolean setIdleConstraintSatisfied(final long nowElapsed, boolean state) {
1249         return setConstraintSatisfied(CONSTRAINT_IDLE, nowElapsed, state);
1250     }
1251 
1252     /** @return true if the constraint was changed, false otherwise. */
setConnectivityConstraintSatisfied(final long nowElapsed, boolean state)1253     boolean setConnectivityConstraintSatisfied(final long nowElapsed, boolean state) {
1254         return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, nowElapsed, state);
1255     }
1256 
1257     /** @return true if the constraint was changed, false otherwise. */
setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state)1258     boolean setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state) {
1259         return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, nowElapsed, state);
1260     }
1261 
1262     /** @return true if the constraint was changed, false otherwise. */
setDeviceNotDozingConstraintSatisfied(final long nowElapsed, boolean state, boolean whitelisted)1263     boolean setDeviceNotDozingConstraintSatisfied(final long nowElapsed,
1264             boolean state, boolean whitelisted) {
1265         appHasDozeExemption = whitelisted;
1266         if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, nowElapsed, state)) {
1267             // The constraint was changed. Update the ready flag.
1268             mReadyNotDozing = state || canRunInDoze();
1269             return true;
1270         }
1271         return false;
1272     }
1273 
1274     /** @return true if the constraint was changed, false otherwise. */
setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state, boolean isUserBgRestricted)1275     boolean setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state,
1276             boolean isUserBgRestricted) {
1277         mIsUserBgRestricted = isUserBgRestricted;
1278         if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, nowElapsed, state)) {
1279             // The constraint was changed. Update the ready flag.
1280             mReadyNotRestrictedInBg = state;
1281             return true;
1282         }
1283         return false;
1284     }
1285 
1286     /** @return true if the constraint was changed, false otherwise. */
setQuotaConstraintSatisfied(final long nowElapsed, boolean state)1287     boolean setQuotaConstraintSatisfied(final long nowElapsed, boolean state) {
1288         if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, nowElapsed, state)) {
1289             // The constraint was changed. Update the ready flag.
1290             mReadyWithinQuota = state;
1291             return true;
1292         }
1293         return false;
1294     }
1295 
1296     /** @return true if the constraint was changed, false otherwise. */
setTareWealthConstraintSatisfied(final long nowElapsed, boolean state)1297     boolean setTareWealthConstraintSatisfied(final long nowElapsed, boolean state) {
1298         if (setConstraintSatisfied(CONSTRAINT_TARE_WEALTH, nowElapsed, state)) {
1299             // The constraint was changed. Update the ready flag.
1300             mReadyTareWealth = state;
1301             return true;
1302         }
1303         return false;
1304     }
1305 
1306     /**
1307      * Sets whether or not this job is approved to be treated as an expedited job based on quota
1308      * policy.
1309      *
1310      * @return true if the approval bit was changed, false otherwise.
1311      */
setExpeditedJobQuotaApproved(final long nowElapsed, boolean state)1312     boolean setExpeditedJobQuotaApproved(final long nowElapsed, boolean state) {
1313         if (mExpeditedQuotaApproved == state) {
1314             return false;
1315         }
1316         final boolean wasReady = !state && isReady();
1317         mExpeditedQuotaApproved = state;
1318         updateExpeditedDependencies();
1319         final boolean isReady = isReady();
1320         if (wasReady && !isReady) {
1321             mReasonReadyToUnready = JobParameters.STOP_REASON_QUOTA;
1322         } else if (!wasReady && isReady) {
1323             mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
1324         }
1325         return true;
1326     }
1327 
1328     /**
1329      * Sets whether or not this job is approved to be treated as an expedited job based on TARE
1330      * policy.
1331      *
1332      * @return true if the approval bit was changed, false otherwise.
1333      */
setExpeditedJobTareApproved(final long nowElapsed, boolean state)1334     boolean setExpeditedJobTareApproved(final long nowElapsed, boolean state) {
1335         if (mExpeditedTareApproved == state) {
1336             return false;
1337         }
1338         final boolean wasReady = !state && isReady();
1339         mExpeditedTareApproved = state;
1340         updateExpeditedDependencies();
1341         final boolean isReady = isReady();
1342         if (wasReady && !isReady) {
1343             mReasonReadyToUnready = JobParameters.STOP_REASON_QUOTA;
1344         } else if (!wasReady && isReady) {
1345             mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
1346         }
1347         return true;
1348     }
1349 
updateExpeditedDependencies()1350     private void updateExpeditedDependencies() {
1351         // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag.
1352         // Making it also track requested-expedited jobs would add unnecessary hops since the
1353         // controller would then defer to canRunInDoze. Avoid the hops and just update
1354         // mReadyNotDozing directly.
1355         mReadyNotDozing = isConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING) || canRunInDoze();
1356     }
1357 
1358     /** @return true if the state was changed, false otherwise. */
setUidActive(final boolean newActiveState)1359     boolean setUidActive(final boolean newActiveState) {
1360         if (newActiveState != uidActive) {
1361             uidActive = newActiveState;
1362             return true;
1363         }
1364         return false; /* unchanged */
1365     }
1366 
1367     /** @return true if the constraint was changed, false otherwise. */
setConstraintSatisfied(int constraint, final long nowElapsed, boolean state)1368     boolean setConstraintSatisfied(int constraint, final long nowElapsed, boolean state) {
1369         boolean old = (satisfiedConstraints&constraint) != 0;
1370         if (old == state) {
1371             return false;
1372         }
1373         if (DEBUG) {
1374             Slog.v(TAG,
1375                     "Constraint " + constraint + " is " + (!state ? "NOT " : "") + "satisfied for "
1376                             + toShortString());
1377         }
1378         final boolean wasReady = !state && isReady();
1379         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
1380         mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
1381         mReadyDynamicSatisfied = mDynamicConstraints != 0
1382                 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
1383         if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) {
1384             FrameworkStatsLog.write_non_chained(
1385                     FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED,
1386                     sourceUid, null, getBatteryName(), getProtoConstraint(constraint),
1387                     state ? FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED
1388                             : FrameworkStatsLog
1389                                     .SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
1390         }
1391 
1392         mConstraintUpdatedTimesElapsed[mConstraintChangeHistoryIndex] = nowElapsed;
1393         mConstraintStatusHistory[mConstraintChangeHistoryIndex] = satisfiedConstraints;
1394         mConstraintChangeHistoryIndex =
1395                 (mConstraintChangeHistoryIndex + 1) % NUM_CONSTRAINT_CHANGE_HISTORY;
1396 
1397         // Can't use isReady() directly since "cache booleans" haven't updated yet.
1398         final boolean isReady = readinessStatusWithConstraint(constraint, state);
1399         if (wasReady && !isReady) {
1400             mReasonReadyToUnready = constraintToStopReason(constraint);
1401         } else if (!wasReady && isReady) {
1402             mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
1403         }
1404 
1405         return true;
1406     }
1407 
1408     @JobParameters.StopReason
constraintToStopReason(int constraint)1409     private int constraintToStopReason(int constraint) {
1410         switch (constraint) {
1411             case CONSTRAINT_BATTERY_NOT_LOW:
1412                 if ((requiredConstraints & constraint) != 0) {
1413                     // The developer requested this constraint, so it makes sense to return the
1414                     // explicit constraint reason.
1415                     return JobParameters.STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW;
1416                 }
1417                 // Hard-coding right now since the current dynamic constraint sets don't overlap
1418                 // TODO: return based on active dynamic constraint sets when they start overlapping
1419                 return JobParameters.STOP_REASON_APP_STANDBY;
1420             case CONSTRAINT_CHARGING:
1421                 if ((requiredConstraints & constraint) != 0) {
1422                     // The developer requested this constraint, so it makes sense to return the
1423                     // explicit constraint reason.
1424                     return JobParameters.STOP_REASON_CONSTRAINT_CHARGING;
1425                 }
1426                 // Hard-coding right now since the current dynamic constraint sets don't overlap
1427                 // TODO: return based on active dynamic constraint sets when they start overlapping
1428                 return JobParameters.STOP_REASON_APP_STANDBY;
1429             case CONSTRAINT_CONNECTIVITY:
1430                 return JobParameters.STOP_REASON_CONSTRAINT_CONNECTIVITY;
1431             case CONSTRAINT_IDLE:
1432                 if ((requiredConstraints & constraint) != 0) {
1433                     // The developer requested this constraint, so it makes sense to return the
1434                     // explicit constraint reason.
1435                     return JobParameters.STOP_REASON_CONSTRAINT_DEVICE_IDLE;
1436                 }
1437                 // Hard-coding right now since the current dynamic constraint sets don't overlap
1438                 // TODO: return based on active dynamic constraint sets when they start overlapping
1439                 return JobParameters.STOP_REASON_APP_STANDBY;
1440             case CONSTRAINT_STORAGE_NOT_LOW:
1441                 return JobParameters.STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW;
1442 
1443             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
1444                 // The BACKGROUND_NOT_RESTRICTED constraint could be dissatisfied either because
1445                 // the app is background restricted, or because we're restricting background work
1446                 // in battery saver. Assume that background restriction is the reason apps that
1447                 // are background restricted have their jobs stopped, and battery saver otherwise.
1448                 // This has the benefit of being consistent for background restricted apps
1449                 // (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of
1450                 // battery saver state.
1451                 if (mIsUserBgRestricted) {
1452                     return JobParameters.STOP_REASON_BACKGROUND_RESTRICTION;
1453                 }
1454                 return JobParameters.STOP_REASON_DEVICE_STATE;
1455             case CONSTRAINT_DEVICE_NOT_DOZING:
1456                 return JobParameters.STOP_REASON_DEVICE_STATE;
1457 
1458             case CONSTRAINT_PREFETCH:
1459                 return JobParameters.STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED;
1460 
1461             case CONSTRAINT_TARE_WEALTH:
1462             case CONSTRAINT_WITHIN_QUOTA:
1463                 return JobParameters.STOP_REASON_QUOTA;
1464 
1465             // These should never be stop reasons since they can never go from true to false.
1466             case CONSTRAINT_CONTENT_TRIGGER:
1467             case CONSTRAINT_DEADLINE:
1468             case CONSTRAINT_TIMING_DELAY:
1469             default:
1470                 Slog.wtf(TAG, "Unsupported constraint (" + constraint + ") --stop reason mapping");
1471                 return JobParameters.STOP_REASON_UNDEFINED;
1472         }
1473     }
1474 
isConstraintSatisfied(int constraint)1475     boolean isConstraintSatisfied(int constraint) {
1476         return (satisfiedConstraints&constraint) != 0;
1477     }
1478 
isExpeditedQuotaApproved()1479     boolean isExpeditedQuotaApproved() {
1480         return mExpeditedQuotaApproved;
1481     }
1482 
clearTrackingController(int which)1483     boolean clearTrackingController(int which) {
1484         if ((trackingControllers&which) != 0) {
1485             trackingControllers &= ~which;
1486             return true;
1487         }
1488         return false;
1489     }
1490 
setTrackingController(int which)1491     void setTrackingController(int which) {
1492         trackingControllers |= which;
1493     }
1494 
1495     /**
1496      * Add additional constraints to prevent this job from running when doze or battery saver are
1497      * active.
1498      */
disallowRunInBatterySaverAndDoze()1499     public void disallowRunInBatterySaverAndDoze() {
1500         addDynamicConstraints(DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS);
1501     }
1502 
1503     /**
1504      * Indicates that this job cannot run without the specified constraints. This is evaluated
1505      * separately from the job's explicitly requested constraints and MUST be satisfied before
1506      * the job can run if the app doesn't have quota.
1507      */
addDynamicConstraints(int constraints)1508     private void addDynamicConstraints(int constraints) {
1509         if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
1510             // Quota should never be used as a dynamic constraint.
1511             Slog.wtf(TAG, "Tried to set quota as a dynamic constraint");
1512             constraints &= ~CONSTRAINT_WITHIN_QUOTA;
1513         }
1514         if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) {
1515             // Quota should never be used as a dynamic constraint.
1516             Slog.wtf(TAG, "Tried to set TARE as a dynamic constraint");
1517             constraints &= ~CONSTRAINT_TARE_WEALTH;
1518         }
1519 
1520         // Connectivity and content trigger are special since they're only valid to add if the
1521         // job has requested network or specific content URIs. Adding these constraints to jobs
1522         // that don't need them doesn't make sense.
1523         if (!hasConnectivityConstraint()) {
1524             constraints &= ~CONSTRAINT_CONNECTIVITY;
1525         }
1526         if (!hasContentTriggerConstraint()) {
1527             constraints &= ~CONSTRAINT_CONTENT_TRIGGER;
1528         }
1529 
1530         mDynamicConstraints |= constraints;
1531         mReadyDynamicSatisfied = mDynamicConstraints != 0
1532                 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
1533     }
1534 
1535     /**
1536      * Removes dynamic constraints from a job, meaning that the requirements are not required for
1537      * the job to run (if the job itself hasn't requested the constraint. This is separate from
1538      * the job's explicitly requested constraints and does not remove those requested constraints.
1539      *
1540      */
removeDynamicConstraints(int constraints)1541     private void removeDynamicConstraints(int constraints) {
1542         mDynamicConstraints &= ~constraints;
1543         mReadyDynamicSatisfied = mDynamicConstraints != 0
1544                 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
1545     }
1546 
getLastSuccessfulRunTime()1547     public long getLastSuccessfulRunTime() {
1548         return mLastSuccessfulRunTime;
1549     }
1550 
getLastFailedRunTime()1551     public long getLastFailedRunTime() {
1552         return mLastFailedRunTime;
1553     }
1554 
1555     /**
1556      * @return Whether or not this job is ready to run, based on its requirements.
1557      */
isReady()1558     public boolean isReady() {
1559         return isReady(mSatisfiedConstraintsOfInterest);
1560     }
1561 
1562     /**
1563      * @return Whether or not this job would be ready to run if it had the specified constraint
1564      * granted, based on its requirements.
1565      */
wouldBeReadyWithConstraint(int constraint)1566     boolean wouldBeReadyWithConstraint(int constraint) {
1567         return readinessStatusWithConstraint(constraint, true);
1568     }
1569 
readinessStatusWithConstraint(int constraint, boolean value)1570     private boolean readinessStatusWithConstraint(int constraint, boolean value) {
1571         boolean oldValue = false;
1572         int satisfied = mSatisfiedConstraintsOfInterest;
1573         switch (constraint) {
1574             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
1575                 oldValue = mReadyNotRestrictedInBg;
1576                 mReadyNotRestrictedInBg = value;
1577                 break;
1578             case CONSTRAINT_DEADLINE:
1579                 oldValue = mReadyDeadlineSatisfied;
1580                 mReadyDeadlineSatisfied = value;
1581                 break;
1582             case CONSTRAINT_DEVICE_NOT_DOZING:
1583                 oldValue = mReadyNotDozing;
1584                 mReadyNotDozing = value;
1585                 break;
1586             case CONSTRAINT_TARE_WEALTH:
1587                 oldValue = mReadyTareWealth;
1588                 mReadyTareWealth = value;
1589                 break;
1590             case CONSTRAINT_WITHIN_QUOTA:
1591                 oldValue = mReadyWithinQuota;
1592                 mReadyWithinQuota = value;
1593                 break;
1594             default:
1595                 if (value) {
1596                     satisfied |= constraint;
1597                 } else {
1598                     satisfied &= ~constraint;
1599                 }
1600                 mReadyDynamicSatisfied = mDynamicConstraints != 0
1601                         && mDynamicConstraints == (satisfied & mDynamicConstraints);
1602 
1603                 break;
1604         }
1605 
1606         boolean toReturn = isReady(satisfied);
1607 
1608         switch (constraint) {
1609             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
1610                 mReadyNotRestrictedInBg = oldValue;
1611                 break;
1612             case CONSTRAINT_DEADLINE:
1613                 mReadyDeadlineSatisfied = oldValue;
1614                 break;
1615             case CONSTRAINT_DEVICE_NOT_DOZING:
1616                 mReadyNotDozing = oldValue;
1617                 break;
1618             case CONSTRAINT_TARE_WEALTH:
1619                 mReadyTareWealth = oldValue;
1620                 break;
1621             case CONSTRAINT_WITHIN_QUOTA:
1622                 mReadyWithinQuota = oldValue;
1623                 break;
1624             default:
1625                 mReadyDynamicSatisfied = mDynamicConstraints != 0
1626                         && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
1627                 break;
1628         }
1629         return toReturn;
1630     }
1631 
isReady(int satisfiedConstraints)1632     private boolean isReady(int satisfiedConstraints) {
1633         // Quota and dynamic constraints trump all other constraints.
1634         // NEVER jobs are not supposed to run at all. Since we're using quota to allow parole
1635         // sessions (exempt from dynamic restrictions), we need the additional check to ensure
1636         // that NEVER jobs don't run.
1637         // TODO: cleanup quota and standby bucket management so we don't need the additional checks
1638         if (((!mReadyWithinQuota || !mReadyTareWealth)
1639                 && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob())
1640                 || getEffectiveStandbyBucket() == NEVER_INDEX) {
1641             return false;
1642         }
1643         // Deadline constraint trumps other constraints besides quota and dynamic (except for
1644         // periodic jobs where deadline is an implementation detail. A periodic job should only
1645         // run if its constraints are satisfied).
1646         // DeviceNotDozing implicit constraint must be satisfied
1647         // NotRestrictedInBackground implicit constraint must be satisfied
1648         return mReadyNotDozing && mReadyNotRestrictedInBg && (serviceInfo != null)
1649                 && (mReadyDeadlineSatisfied || isConstraintsSatisfied(satisfiedConstraints));
1650     }
1651 
1652     /** All constraints besides implicit and deadline. */
1653     static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
1654             | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY
1655             | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER | CONSTRAINT_PREFETCH;
1656 
1657     // Soft override covers all non-"functional" constraints
1658     static final int SOFT_OVERRIDE_CONSTRAINTS =
1659             CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW
1660                     | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE | CONSTRAINT_PREFETCH;
1661 
1662     /** Returns true whenever all dynamically set constraints are satisfied. */
areDynamicConstraintsSatisfied()1663     public boolean areDynamicConstraintsSatisfied() {
1664         return mReadyDynamicSatisfied;
1665     }
1666 
1667     /**
1668      * @return Whether the constraints set on this job are satisfied.
1669      */
isConstraintsSatisfied()1670     public boolean isConstraintsSatisfied() {
1671         return isConstraintsSatisfied(mSatisfiedConstraintsOfInterest);
1672     }
1673 
isConstraintsSatisfied(int satisfiedConstraints)1674     private boolean isConstraintsSatisfied(int satisfiedConstraints) {
1675         if (overrideState == OVERRIDE_FULL) {
1676             // force override: the job is always runnable
1677             return true;
1678         }
1679 
1680         int sat = satisfiedConstraints;
1681         if (overrideState == OVERRIDE_SOFT) {
1682             // override: pretend all 'soft' requirements are satisfied
1683             sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
1684         }
1685 
1686         return (sat & mRequiredConstraintsOfInterest) == mRequiredConstraintsOfInterest;
1687     }
1688 
matches(int uid, int jobId)1689     public boolean matches(int uid, int jobId) {
1690         return this.job.getId() == jobId && this.callingUid == uid;
1691     }
1692 
1693     @Override
toString()1694     public String toString() {
1695         StringBuilder sb = new StringBuilder(128);
1696         sb.append("JobStatus{");
1697         sb.append(Integer.toHexString(System.identityHashCode(this)));
1698         sb.append(" #");
1699         UserHandle.formatUid(sb, callingUid);
1700         sb.append("/");
1701         sb.append(job.getId());
1702         sb.append(' ');
1703         sb.append(batteryName);
1704         sb.append(" u=");
1705         sb.append(getUserId());
1706         sb.append(" s=");
1707         sb.append(getSourceUid());
1708         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME
1709                 || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
1710             long now = sElapsedRealtimeClock.millis();
1711             sb.append(" TIME=");
1712             formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now);
1713             sb.append(":");
1714             formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now);
1715         }
1716         if (job.getRequiredNetwork() != null) {
1717             sb.append(" NET");
1718         }
1719         if (job.isRequireCharging()) {
1720             sb.append(" CHARGING");
1721         }
1722         if (job.isRequireBatteryNotLow()) {
1723             sb.append(" BATNOTLOW");
1724         }
1725         if (job.isRequireStorageNotLow()) {
1726             sb.append(" STORENOTLOW");
1727         }
1728         if (job.isRequireDeviceIdle()) {
1729             sb.append(" IDLE");
1730         }
1731         if (job.isPeriodic()) {
1732             sb.append(" PERIODIC");
1733         }
1734         if (job.isPersisted()) {
1735             sb.append(" PERSISTED");
1736         }
1737         if ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) == 0) {
1738             sb.append(" WAIT:DEV_NOT_DOZING");
1739         }
1740         if (job.getTriggerContentUris() != null) {
1741             sb.append(" URIS=");
1742             sb.append(Arrays.toString(job.getTriggerContentUris()));
1743         }
1744         if (numFailures != 0) {
1745             sb.append(" failures=");
1746             sb.append(numFailures);
1747         }
1748         if (isReady()) {
1749             sb.append(" READY");
1750         } else {
1751             sb.append(" satisfied:0x").append(Integer.toHexString(satisfiedConstraints));
1752             sb.append(" unsatisfied:0x").append(Integer.toHexString(
1753                     (satisfiedConstraints & mRequiredConstraintsOfInterest)
1754                             ^ mRequiredConstraintsOfInterest));
1755         }
1756         sb.append("}");
1757         return sb.toString();
1758     }
1759 
formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now)1760     private void formatRunTime(PrintWriter pw, long runtime, long  defaultValue, long now) {
1761         if (runtime == defaultValue) {
1762             pw.print("none");
1763         } else {
1764             TimeUtils.formatDuration(runtime - now, pw);
1765         }
1766     }
1767 
formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now)1768     private void formatRunTime(StringBuilder sb, long runtime, long  defaultValue, long now) {
1769         if (runtime == defaultValue) {
1770             sb.append("none");
1771         } else {
1772             TimeUtils.formatDuration(runtime - now, sb);
1773         }
1774     }
1775 
1776     /**
1777      * Convenience function to identify a job uniquely without pulling all the data that
1778      * {@link #toString()} returns.
1779      */
toShortString()1780     public String toShortString() {
1781         StringBuilder sb = new StringBuilder();
1782         sb.append(Integer.toHexString(System.identityHashCode(this)));
1783         sb.append(" #");
1784         UserHandle.formatUid(sb, callingUid);
1785         sb.append("/");
1786         sb.append(job.getId());
1787         sb.append(' ');
1788         sb.append(batteryName);
1789         return sb.toString();
1790     }
1791 
1792     /**
1793      * Convenience function to identify a job uniquely without pulling all the data that
1794      * {@link #toString()} returns.
1795      */
toShortStringExceptUniqueId()1796     public String toShortStringExceptUniqueId() {
1797         StringBuilder sb = new StringBuilder();
1798         sb.append(Integer.toHexString(System.identityHashCode(this)));
1799         sb.append(' ');
1800         sb.append(batteryName);
1801         return sb.toString();
1802     }
1803 
1804     /**
1805      * Convenience function to dump data that identifies a job uniquely to proto. This is intended
1806      * to mimic {@link #toShortString}.
1807      */
writeToShortProto(ProtoOutputStream proto, long fieldId)1808     public void writeToShortProto(ProtoOutputStream proto, long fieldId) {
1809         final long token = proto.start(fieldId);
1810 
1811         proto.write(JobStatusShortInfoProto.CALLING_UID, callingUid);
1812         proto.write(JobStatusShortInfoProto.JOB_ID, job.getId());
1813         proto.write(JobStatusShortInfoProto.BATTERY_NAME, batteryName);
1814 
1815         proto.end(token);
1816     }
1817 
dumpConstraints(PrintWriter pw, int constraints)1818     void dumpConstraints(PrintWriter pw, int constraints) {
1819         if ((constraints&CONSTRAINT_CHARGING) != 0) {
1820             pw.print(" CHARGING");
1821         }
1822         if ((constraints& CONSTRAINT_BATTERY_NOT_LOW) != 0) {
1823             pw.print(" BATTERY_NOT_LOW");
1824         }
1825         if ((constraints& CONSTRAINT_STORAGE_NOT_LOW) != 0) {
1826             pw.print(" STORAGE_NOT_LOW");
1827         }
1828         if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) {
1829             pw.print(" TIMING_DELAY");
1830         }
1831         if ((constraints&CONSTRAINT_DEADLINE) != 0) {
1832             pw.print(" DEADLINE");
1833         }
1834         if ((constraints&CONSTRAINT_IDLE) != 0) {
1835             pw.print(" IDLE");
1836         }
1837         if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
1838             pw.print(" CONNECTIVITY");
1839         }
1840         if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) {
1841             pw.print(" CONTENT_TRIGGER");
1842         }
1843         if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
1844             pw.print(" DEVICE_NOT_DOZING");
1845         }
1846         if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
1847             pw.print(" BACKGROUND_NOT_RESTRICTED");
1848         }
1849         if ((constraints & CONSTRAINT_PREFETCH) != 0) {
1850             pw.print(" PREFETCH");
1851         }
1852         if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) {
1853             pw.print(" TARE_WEALTH");
1854         }
1855         if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
1856             pw.print(" WITHIN_QUOTA");
1857         }
1858         if (constraints != 0) {
1859             pw.print(" [0x");
1860             pw.print(Integer.toHexString(constraints));
1861             pw.print("]");
1862         }
1863     }
1864 
1865     /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */
getProtoConstraint(int constraint)1866     static int getProtoConstraint(int constraint) {
1867         switch (constraint) {
1868             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
1869                 return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
1870             case CONSTRAINT_BATTERY_NOT_LOW:
1871                 return JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW;
1872             case CONSTRAINT_CHARGING:
1873                 return JobServerProtoEnums.CONSTRAINT_CHARGING;
1874             case CONSTRAINT_CONNECTIVITY:
1875                 return JobServerProtoEnums.CONSTRAINT_CONNECTIVITY;
1876             case CONSTRAINT_CONTENT_TRIGGER:
1877                 return JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER;
1878             case CONSTRAINT_DEADLINE:
1879                 return JobServerProtoEnums.CONSTRAINT_DEADLINE;
1880             case CONSTRAINT_DEVICE_NOT_DOZING:
1881                 return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING;
1882             case CONSTRAINT_IDLE:
1883                 return JobServerProtoEnums.CONSTRAINT_IDLE;
1884             case CONSTRAINT_PREFETCH:
1885                 return JobServerProtoEnums.CONSTRAINT_PREFETCH;
1886             case CONSTRAINT_STORAGE_NOT_LOW:
1887                 return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW;
1888             case CONSTRAINT_TARE_WEALTH:
1889                 return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH;
1890             case CONSTRAINT_TIMING_DELAY:
1891                 return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY;
1892             case CONSTRAINT_WITHIN_QUOTA:
1893                 return JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA;
1894             default:
1895                 return JobServerProtoEnums.CONSTRAINT_UNKNOWN;
1896         }
1897     }
1898 
1899     /** Writes constraints to the given repeating proto field. */
dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints)1900     void dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints) {
1901         if ((constraints & CONSTRAINT_CHARGING) != 0) {
1902             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CHARGING);
1903         }
1904         if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
1905             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW);
1906         }
1907         if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
1908             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW);
1909         }
1910         if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
1911             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_TIMING_DELAY);
1912         }
1913         if ((constraints & CONSTRAINT_DEADLINE) != 0) {
1914             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEADLINE);
1915         }
1916         if ((constraints & CONSTRAINT_IDLE) != 0) {
1917             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_IDLE);
1918         }
1919         if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
1920             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONNECTIVITY);
1921         }
1922         if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
1923             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER);
1924         }
1925         if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
1926             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING);
1927         }
1928         if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
1929             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA);
1930         }
1931         if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
1932             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
1933         }
1934     }
1935 
dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index)1936     private void dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index) {
1937         pw.increaseIndent();
1938         pw.print("#"); pw.print(index); pw.print(": #");
1939         pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount());
1940         pw.print("x "); pw.println(work.getIntent());
1941         if (work.getGrants() != null) {
1942             pw.println("URI grants:");
1943             pw.increaseIndent();
1944             ((GrantedUriPermissions) work.getGrants()).dump(pw);
1945             pw.decreaseIndent();
1946         }
1947         pw.decreaseIndent();
1948     }
1949 
dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work)1950     private void dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work) {
1951         final long token = proto.start(fieldId);
1952 
1953         proto.write(JobStatusDumpProto.JobWorkItem.WORK_ID, work.getWorkId());
1954         proto.write(JobStatusDumpProto.JobWorkItem.DELIVERY_COUNT, work.getDeliveryCount());
1955         if (work.getIntent() != null) {
1956             work.getIntent().dumpDebug(proto, JobStatusDumpProto.JobWorkItem.INTENT);
1957         }
1958         Object grants = work.getGrants();
1959         if (grants != null) {
1960             ((GrantedUriPermissions) grants).dump(proto, JobStatusDumpProto.JobWorkItem.URI_GRANTS);
1961         }
1962 
1963         proto.end(token);
1964     }
1965 
1966     /**
1967      * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
1968      */
getBucketName()1969     String getBucketName() {
1970         return bucketName(standbyBucket);
1971     }
1972 
1973     /**
1974      * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
1975      */
bucketName(int standbyBucket)1976     static String bucketName(int standbyBucket) {
1977         switch (standbyBucket) {
1978             case 0: return "ACTIVE";
1979             case 1: return "WORKING_SET";
1980             case 2: return "FREQUENT";
1981             case 3: return "RARE";
1982             case 4: return "NEVER";
1983             case 5: return "RESTRICTED";
1984             case 6: return "EXEMPTED";
1985             default:
1986                 return "Unknown: " + standbyBucket;
1987         }
1988     }
1989 
1990     // Dumpsys infrastructure
1991     @NeverCompile // Avoid size overhead of debugging code.
dump(IndentingPrintWriter pw, boolean full, long nowElapsed)1992     public void dump(IndentingPrintWriter pw,  boolean full, long nowElapsed) {
1993         UserHandle.formatUid(pw, callingUid);
1994         pw.print(" tag="); pw.println(tag);
1995 
1996         pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
1997         pw.print(" user="); pw.print(getSourceUserId());
1998         pw.print(" pkg="); pw.println(getSourcePackageName());
1999         if (full) {
2000             pw.println("JobInfo:");
2001             pw.increaseIndent();
2002 
2003             pw.print("Service: ");
2004             pw.println(job.getService().flattenToShortString());
2005             if (job.isPeriodic()) {
2006                 pw.print("PERIODIC: interval=");
2007                 TimeUtils.formatDuration(job.getIntervalMillis(), pw);
2008                 pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw);
2009                 pw.println();
2010             }
2011             if (job.isPersisted()) {
2012                 pw.println("PERSISTED");
2013             }
2014             if (job.getBias() != 0) {
2015                 pw.print("Bias: ");
2016                 pw.println(JobInfo.getBiasString(job.getBias()));
2017             }
2018             pw.print("Priority: ");
2019             pw.print(JobInfo.getPriorityString(job.getPriority()));
2020             final int effectivePriority = getEffectivePriority();
2021             if (effectivePriority != job.getPriority()) {
2022                 pw.print(" effective=");
2023                 pw.print(JobInfo.getPriorityString(effectivePriority));
2024             }
2025             pw.println();
2026             if (job.getFlags() != 0) {
2027                 pw.print("Flags: ");
2028                 pw.println(Integer.toHexString(job.getFlags()));
2029             }
2030             if (getInternalFlags() != 0) {
2031                 pw.print("Internal flags: ");
2032                 pw.print(Integer.toHexString(getInternalFlags()));
2033 
2034                 if ((getInternalFlags()&INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
2035                     pw.print(" HAS_FOREGROUND_EXEMPTION");
2036                 }
2037                 pw.println();
2038             }
2039             pw.print("Requires: charging=");
2040             pw.print(job.isRequireCharging()); pw.print(" batteryNotLow=");
2041             pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle=");
2042             pw.println(job.isRequireDeviceIdle());
2043             if (job.getTriggerContentUris() != null) {
2044                 pw.println("Trigger content URIs:");
2045                 pw.increaseIndent();
2046                 for (int i = 0; i < job.getTriggerContentUris().length; i++) {
2047                     JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
2048                     pw.print(Integer.toHexString(trig.getFlags()));
2049                     pw.print(' '); pw.println(trig.getUri());
2050                 }
2051                 pw.decreaseIndent();
2052                 if (job.getTriggerContentUpdateDelay() >= 0) {
2053                     pw.print("Trigger update delay: ");
2054                     TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw);
2055                     pw.println();
2056                 }
2057                 if (job.getTriggerContentMaxDelay() >= 0) {
2058                     pw.print("Trigger max delay: ");
2059                     TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw);
2060                     pw.println();
2061                 }
2062                 pw.print("Has media backup exemption", mHasMediaBackupExemption).println();
2063             }
2064             if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
2065                 pw.print("Extras: ");
2066                 pw.println(job.getExtras().toShortString());
2067             }
2068             if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
2069                 pw.print("Transient extras: ");
2070                 pw.println(job.getTransientExtras().toShortString());
2071             }
2072             if (job.getClipData() != null) {
2073                 pw.print("Clip data: ");
2074                 StringBuilder b = new StringBuilder(128);
2075                 b.append(job.getClipData());
2076                 pw.println(b);
2077             }
2078             if (uriPerms != null) {
2079                 pw.println("Granted URI permissions:");
2080                 uriPerms.dump(pw);
2081             }
2082             if (job.getRequiredNetwork() != null) {
2083                 pw.print("Network type: ");
2084                 pw.println(job.getRequiredNetwork());
2085             }
2086             if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2087                 pw.print("Network download bytes: ");
2088                 pw.println(mTotalNetworkDownloadBytes);
2089             }
2090             if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2091                 pw.print("Network upload bytes: ");
2092                 pw.println(mTotalNetworkUploadBytes);
2093             }
2094             if (mMinimumNetworkChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2095                 pw.print("Minimum network chunk bytes: ");
2096                 pw.println(mMinimumNetworkChunkBytes);
2097             }
2098             if (job.getMinLatencyMillis() != 0) {
2099                 pw.print("Minimum latency: ");
2100                 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
2101                 pw.println();
2102             }
2103             if (job.getMaxExecutionDelayMillis() != 0) {
2104                 pw.print("Max execution delay: ");
2105                 TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
2106                 pw.println();
2107             }
2108             pw.print("Backoff: policy="); pw.print(job.getBackoffPolicy());
2109             pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
2110             pw.println();
2111             if (job.hasEarlyConstraint()) {
2112                 pw.println("Has early constraint");
2113             }
2114             if (job.hasLateConstraint()) {
2115                 pw.println("Has late constraint");
2116             }
2117 
2118             pw.decreaseIndent();
2119         }
2120 
2121         pw.print("Required constraints:");
2122         dumpConstraints(pw, requiredConstraints);
2123         pw.println();
2124         pw.print("Dynamic constraints:");
2125         dumpConstraints(pw, mDynamicConstraints);
2126         pw.println();
2127         if (full) {
2128             pw.print("Satisfied constraints:");
2129             dumpConstraints(pw, satisfiedConstraints);
2130             pw.println();
2131             pw.print("Unsatisfied constraints:");
2132             dumpConstraints(pw,
2133                     ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA | CONSTRAINT_TARE_WEALTH)
2134                             & ~satisfiedConstraints));
2135             pw.println();
2136 
2137             pw.println("Constraint history:");
2138             pw.increaseIndent();
2139             for (int h = 0; h < NUM_CONSTRAINT_CHANGE_HISTORY; ++h) {
2140                 final int idx = (h + mConstraintChangeHistoryIndex) % NUM_CONSTRAINT_CHANGE_HISTORY;
2141                 if (mConstraintUpdatedTimesElapsed[idx] == 0) {
2142                     continue;
2143                 }
2144                 TimeUtils.formatDuration(mConstraintUpdatedTimesElapsed[idx], nowElapsed, pw);
2145                 // dumpConstraints prepends with a space, so no need to add a space after the =
2146                 pw.print(" =");
2147                 dumpConstraints(pw, mConstraintStatusHistory[idx]);
2148                 pw.println();
2149             }
2150             pw.decreaseIndent();
2151 
2152             if (appHasDozeExemption) {
2153                 pw.println("Doze whitelisted: true");
2154             }
2155             if (uidActive) {
2156                 pw.println("Uid: active");
2157             }
2158             if (job.isExemptedFromAppStandby()) {
2159                 pw.println("Is exempted from app standby");
2160             }
2161         }
2162         if (trackingControllers != 0) {
2163             pw.print("Tracking:");
2164             if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY");
2165             if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY");
2166             if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT");
2167             if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE");
2168             if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE");
2169             if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME");
2170             if ((trackingControllers & TRACKING_QUOTA) != 0) pw.print(" QUOTA");
2171             pw.println();
2172         }
2173 
2174         pw.println("Implicit constraints:");
2175         pw.increaseIndent();
2176         pw.print("readyNotDozing: ");
2177         pw.println(mReadyNotDozing);
2178         pw.print("readyNotRestrictedInBg: ");
2179         pw.println(mReadyNotRestrictedInBg);
2180         if (!job.isPeriodic() && hasDeadlineConstraint()) {
2181             pw.print("readyDeadlineSatisfied: ");
2182             pw.println(mReadyDeadlineSatisfied);
2183         }
2184         if (mDynamicConstraints != 0) {
2185             pw.print("readyDynamicSatisfied: ");
2186             pw.println(mReadyDynamicSatisfied);
2187         }
2188         pw.print("readyComponentEnabled: ");
2189         pw.println(serviceInfo != null);
2190         if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) {
2191             pw.print("expeditedQuotaApproved: ");
2192             pw.print(mExpeditedQuotaApproved);
2193             pw.print(" expeditedTareApproved: ");
2194             pw.print(mExpeditedTareApproved);
2195             pw.print(" (started as EJ: ");
2196             pw.print(startedAsExpeditedJob);
2197             pw.println(")");
2198         }
2199         pw.decreaseIndent();
2200 
2201         if (changedAuthorities != null) {
2202             pw.println("Changed authorities:");
2203             pw.increaseIndent();
2204             for (int i=0; i<changedAuthorities.size(); i++) {
2205                 pw.println(changedAuthorities.valueAt(i));
2206             }
2207             pw.decreaseIndent();
2208         }
2209         if (changedUris != null) {
2210             pw.println("Changed URIs:");
2211             pw.increaseIndent();
2212             for (int i = 0; i < changedUris.size(); i++) {
2213                 pw.println(changedUris.valueAt(i));
2214             }
2215             pw.decreaseIndent();
2216         }
2217         if (network != null) {
2218             pw.print("Network: "); pw.println(network);
2219         }
2220         if (pendingWork != null && pendingWork.size() > 0) {
2221             pw.println("Pending work:");
2222             for (int i = 0; i < pendingWork.size(); i++) {
2223                 dumpJobWorkItem(pw, pendingWork.get(i), i);
2224             }
2225         }
2226         if (executingWork != null && executingWork.size() > 0) {
2227             pw.println("Executing work:");
2228             for (int i = 0; i < executingWork.size(); i++) {
2229                 dumpJobWorkItem(pw, executingWork.get(i), i);
2230             }
2231         }
2232         pw.print("Standby bucket: ");
2233         pw.println(getBucketName());
2234         pw.increaseIndent();
2235         if (whenStandbyDeferred != 0) {
2236             pw.print("Deferred since: ");
2237             TimeUtils.formatDuration(whenStandbyDeferred, nowElapsed, pw);
2238             pw.println();
2239         }
2240         if (mFirstForceBatchedTimeElapsed != 0) {
2241             pw.print("Time since first force batch attempt: ");
2242             TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, nowElapsed, pw);
2243             pw.println();
2244         }
2245         pw.decreaseIndent();
2246 
2247         pw.print("Enqueue time: ");
2248         TimeUtils.formatDuration(enqueueTime, nowElapsed, pw);
2249         pw.println();
2250         pw.print("Run time: earliest=");
2251         formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, nowElapsed);
2252         pw.print(", latest=");
2253         formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed);
2254         pw.print(", original latest=");
2255         formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed);
2256         pw.println();
2257         if (numFailures != 0) {
2258             pw.print("Num failures: "); pw.println(numFailures);
2259         }
2260         if (mLastSuccessfulRunTime != 0) {
2261             pw.print("Last successful run: ");
2262             pw.println(formatTime(mLastSuccessfulRunTime));
2263         }
2264         if (mLastFailedRunTime != 0) {
2265             pw.print("Last failed run: ");
2266             pw.println(formatTime(mLastFailedRunTime));
2267         }
2268     }
2269 
formatTime(long time)2270     private static CharSequence formatTime(long time) {
2271         return DateFormat.format("yyyy-MM-dd HH:mm:ss", time);
2272     }
2273 
dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis)2274     public void dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis) {
2275         final long token = proto.start(fieldId);
2276 
2277         proto.write(JobStatusDumpProto.CALLING_UID, callingUid);
2278         proto.write(JobStatusDumpProto.TAG, tag);
2279         proto.write(JobStatusDumpProto.SOURCE_UID, getSourceUid());
2280         proto.write(JobStatusDumpProto.SOURCE_USER_ID, getSourceUserId());
2281         proto.write(JobStatusDumpProto.SOURCE_PACKAGE_NAME, getSourcePackageName());
2282 
2283         if (full) {
2284             final long jiToken = proto.start(JobStatusDumpProto.JOB_INFO);
2285 
2286             job.getService().dumpDebug(proto, JobStatusDumpProto.JobInfo.SERVICE);
2287 
2288             proto.write(JobStatusDumpProto.JobInfo.IS_PERIODIC, job.isPeriodic());
2289             proto.write(JobStatusDumpProto.JobInfo.PERIOD_INTERVAL_MS, job.getIntervalMillis());
2290             proto.write(JobStatusDumpProto.JobInfo.PERIOD_FLEX_MS, job.getFlexMillis());
2291 
2292             proto.write(JobStatusDumpProto.JobInfo.IS_PERSISTED, job.isPersisted());
2293             proto.write(JobStatusDumpProto.JobInfo.PRIORITY, job.getBias());
2294             proto.write(JobStatusDumpProto.JobInfo.FLAGS, job.getFlags());
2295             proto.write(JobStatusDumpProto.INTERNAL_FLAGS, getInternalFlags());
2296             // Foreground exemption can be determined from internal flags value.
2297 
2298             proto.write(JobStatusDumpProto.JobInfo.REQUIRES_CHARGING, job.isRequireCharging());
2299             proto.write(JobStatusDumpProto.JobInfo.REQUIRES_BATTERY_NOT_LOW, job.isRequireBatteryNotLow());
2300             proto.write(JobStatusDumpProto.JobInfo.REQUIRES_DEVICE_IDLE, job.isRequireDeviceIdle());
2301 
2302             if (job.getTriggerContentUris() != null) {
2303                 for (int i = 0; i < job.getTriggerContentUris().length; i++) {
2304                     final long tcuToken = proto.start(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_URIS);
2305                     JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
2306 
2307                     proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.FLAGS, trig.getFlags());
2308                     Uri u = trig.getUri();
2309                     if (u != null) {
2310                         proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.URI, u.toString());
2311                     }
2312 
2313                     proto.end(tcuToken);
2314                 }
2315                 if (job.getTriggerContentUpdateDelay() >= 0) {
2316                     proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_UPDATE_DELAY_MS,
2317                             job.getTriggerContentUpdateDelay());
2318                 }
2319                 if (job.getTriggerContentMaxDelay() >= 0) {
2320                     proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_MAX_DELAY_MS,
2321                             job.getTriggerContentMaxDelay());
2322                 }
2323             }
2324             if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
2325                 job.getExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.EXTRAS);
2326             }
2327             if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
2328                 job.getTransientExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS);
2329             }
2330             if (job.getClipData() != null) {
2331                 job.getClipData().dumpDebug(proto, JobStatusDumpProto.JobInfo.CLIP_DATA);
2332             }
2333             if (uriPerms != null) {
2334                 uriPerms.dump(proto, JobStatusDumpProto.JobInfo.GRANTED_URI_PERMISSIONS);
2335             }
2336             if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2337                 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_DOWNLOAD_BYTES,
2338                         mTotalNetworkDownloadBytes);
2339             }
2340             if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2341                 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_UPLOAD_BYTES,
2342                         mTotalNetworkUploadBytes);
2343             }
2344             proto.write(JobStatusDumpProto.JobInfo.MIN_LATENCY_MS, job.getMinLatencyMillis());
2345             proto.write(JobStatusDumpProto.JobInfo.MAX_EXECUTION_DELAY_MS, job.getMaxExecutionDelayMillis());
2346 
2347             final long bpToken = proto.start(JobStatusDumpProto.JobInfo.BACKOFF_POLICY);
2348             proto.write(JobStatusDumpProto.JobInfo.Backoff.POLICY, job.getBackoffPolicy());
2349             proto.write(JobStatusDumpProto.JobInfo.Backoff.INITIAL_BACKOFF_MS,
2350                     job.getInitialBackoffMillis());
2351             proto.end(bpToken);
2352 
2353             proto.write(JobStatusDumpProto.JobInfo.HAS_EARLY_CONSTRAINT, job.hasEarlyConstraint());
2354             proto.write(JobStatusDumpProto.JobInfo.HAS_LATE_CONSTRAINT, job.hasLateConstraint());
2355 
2356             proto.end(jiToken);
2357         }
2358 
2359         dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints);
2360         dumpConstraints(proto, JobStatusDumpProto.DYNAMIC_CONSTRAINTS, mDynamicConstraints);
2361         if (full) {
2362             dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
2363             dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
2364                     ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
2365             proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, appHasDozeExemption);
2366             proto.write(JobStatusDumpProto.IS_UID_ACTIVE, uidActive);
2367             proto.write(JobStatusDumpProto.IS_EXEMPTED_FROM_APP_STANDBY,
2368                     job.isExemptedFromAppStandby());
2369         }
2370 
2371         // Tracking controllers
2372         if ((trackingControllers&TRACKING_BATTERY) != 0) {
2373             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
2374                     JobStatusDumpProto.TRACKING_BATTERY);
2375         }
2376         if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) {
2377             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
2378                     JobStatusDumpProto.TRACKING_CONNECTIVITY);
2379         }
2380         if ((trackingControllers&TRACKING_CONTENT) != 0) {
2381             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
2382                     JobStatusDumpProto.TRACKING_CONTENT);
2383         }
2384         if ((trackingControllers&TRACKING_IDLE) != 0) {
2385             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
2386                     JobStatusDumpProto.TRACKING_IDLE);
2387         }
2388         if ((trackingControllers&TRACKING_STORAGE) != 0) {
2389             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
2390                     JobStatusDumpProto.TRACKING_STORAGE);
2391         }
2392         if ((trackingControllers&TRACKING_TIME) != 0) {
2393             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
2394                     JobStatusDumpProto.TRACKING_TIME);
2395         }
2396         if ((trackingControllers & TRACKING_QUOTA) != 0) {
2397             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
2398                     JobStatusDumpProto.TRACKING_QUOTA);
2399         }
2400 
2401         // Implicit constraints
2402         final long icToken = proto.start(JobStatusDumpProto.IMPLICIT_CONSTRAINTS);
2403         proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_DOZING, mReadyNotDozing);
2404         proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_RESTRICTED_IN_BG,
2405                 mReadyNotRestrictedInBg);
2406         // mReadyDeadlineSatisfied isn't an implicit constraint...and can be determined from other
2407         // field values.
2408         proto.write(JobStatusDumpProto.ImplicitConstraints.IS_DYNAMIC_SATISFIED,
2409                 mReadyDynamicSatisfied);
2410         proto.end(icToken);
2411 
2412         if (changedAuthorities != null) {
2413             for (int k = 0; k < changedAuthorities.size(); k++) {
2414                 proto.write(JobStatusDumpProto.CHANGED_AUTHORITIES, changedAuthorities.valueAt(k));
2415             }
2416         }
2417         if (changedUris != null) {
2418             for (int i = 0; i < changedUris.size(); i++) {
2419                 Uri u = changedUris.valueAt(i);
2420                 proto.write(JobStatusDumpProto.CHANGED_URIS, u.toString());
2421             }
2422         }
2423 
2424         if (pendingWork != null) {
2425             for (int i = 0; i < pendingWork.size(); i++) {
2426                 dumpJobWorkItem(proto, JobStatusDumpProto.PENDING_WORK, pendingWork.get(i));
2427             }
2428         }
2429         if (executingWork != null) {
2430             for (int i = 0; i < executingWork.size(); i++) {
2431                 dumpJobWorkItem(proto, JobStatusDumpProto.EXECUTING_WORK, executingWork.get(i));
2432             }
2433         }
2434 
2435         proto.write(JobStatusDumpProto.STANDBY_BUCKET, standbyBucket);
2436         proto.write(JobStatusDumpProto.ENQUEUE_DURATION_MS, elapsedRealtimeMillis - enqueueTime);
2437         proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_DEFERRAL_MS,
2438                 whenStandbyDeferred == 0 ? 0 : elapsedRealtimeMillis - whenStandbyDeferred);
2439         proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_FORCE_BATCH_ATTEMPT_MS,
2440                 mFirstForceBatchedTimeElapsed == 0
2441                         ? 0 : elapsedRealtimeMillis - mFirstForceBatchedTimeElapsed);
2442         if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) {
2443             proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 0);
2444         } else {
2445             proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS,
2446                     earliestRunTimeElapsedMillis - elapsedRealtimeMillis);
2447         }
2448         if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) {
2449             proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS, 0);
2450         } else {
2451             proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS,
2452                     latestRunTimeElapsedMillis - elapsedRealtimeMillis);
2453         }
2454         proto.write(JobStatusDumpProto.ORIGINAL_LATEST_RUNTIME_ELAPSED,
2455                 mOriginalLatestRunTimeElapsedMillis);
2456 
2457         proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures);
2458         proto.write(JobStatusDumpProto.LAST_SUCCESSFUL_RUN_TIME, mLastSuccessfulRunTime);
2459         proto.write(JobStatusDumpProto.LAST_FAILED_RUN_TIME, mLastFailedRunTime);
2460 
2461         proto.end(token);
2462     }
2463 }
2464