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.sElapsedRealtimeClock;
20 
21 import android.app.AppGlobals;
22 import android.app.IActivityManager;
23 import android.app.job.JobInfo;
24 import android.app.job.JobWorkItem;
25 import android.content.ClipData;
26 import android.content.ComponentName;
27 import android.content.pm.PackageManagerInternal;
28 import android.net.Network;
29 import android.net.Uri;
30 import android.os.RemoteException;
31 import android.os.UserHandle;
32 import android.text.format.TimeMigrationUtils;
33 import android.util.ArraySet;
34 import android.util.Pair;
35 import android.util.Slog;
36 import android.util.StatsLog;
37 import android.util.TimeUtils;
38 import android.util.proto.ProtoOutputStream;
39 
40 import com.android.server.LocalServices;
41 import com.android.server.job.GrantedUriPermissions;
42 import com.android.server.job.JobSchedulerInternal;
43 import com.android.server.job.JobSchedulerService;
44 import com.android.server.job.JobServerProtoEnums;
45 import com.android.server.job.JobStatusDumpProto;
46 import com.android.server.job.JobStatusShortInfoProto;
47 
48 import java.io.PrintWriter;
49 import java.util.ArrayList;
50 import java.util.Arrays;
51 import java.util.function.Predicate;
52 
53 /**
54  * Uniquely identifies a job internally.
55  * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler.
56  * Contains current state of the requirements of the job, as well as a function to evaluate
57  * whether it's ready to run.
58  * This object is shared among the various controllers - hence why the different fields are atomic.
59  * This isn't strictly necessary because each controller is only interested in a specific field,
60  * and the receivers that are listening for global state change will all run on the main looper,
61  * but we don't enforce that so this is safer.
62  *
63  * Test: atest com.android.server.job.controllers.JobStatusTest
64  * @hide
65  */
66 public final class JobStatus {
67     static final String TAG = "JobSchedulerService";
68     static final boolean DEBUG = JobSchedulerService.DEBUG;
69 
70     public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
71     public static final long NO_EARLIEST_RUNTIME = 0L;
72 
73     static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
74     static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;  // 1 << 2
75     static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
76     static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
77     static final int CONSTRAINT_TIMING_DELAY = 1<<31;
78     static final int CONSTRAINT_DEADLINE = 1<<30;
79     static final int CONSTRAINT_CONNECTIVITY = 1<<28;
80     static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
81     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
82     static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;      // Implicit constraint
83     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
84 
85     /**
86      * The constraints that we want to log to statsd.
87      *
88      * Constraints that can be inferred from other atoms have been excluded to avoid logging too
89      * much information and to reduce redundancy:
90      *
91      * * CONSTRAINT_CHARGING can be inferred with PluggedStateChanged (Atom #32)
92      * * CONSTRAINT_BATTERY_NOT_LOW can be inferred with BatteryLevelChanged (Atom #30)
93      * * CONSTRAINT_CONNECTIVITY can be partially inferred with ConnectivityStateChanged
94      * (Atom #98) and BatterySaverModeStateChanged (Atom #20).
95      * * CONSTRAINT_DEVICE_NOT_DOZING can be mostly inferred with DeviceIdleModeStateChanged
96      * (Atom #21)
97      * * CONSTRAINT_BACKGROUND_NOT_RESTRICTED can be inferred with BatterySaverModeStateChanged
98      * (Atom #20)
99      */
100     private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
101             | CONSTRAINT_DEADLINE
102             | CONSTRAINT_IDLE
103             | CONSTRAINT_STORAGE_NOT_LOW
104             | CONSTRAINT_TIMING_DELAY
105             | CONSTRAINT_WITHIN_QUOTA;
106 
107     // TODO(b/129954980)
108     private static final boolean STATS_LOG_ENABLED = false;
109 
110     // Soft override: ignore constraints like time that don't affect API availability
111     public static final int OVERRIDE_SOFT = 1;
112     // Full override: ignore all constraints including API-affecting like connectivity
113     public static final int OVERRIDE_FULL = 2;
114 
115     /** If not specified, trigger update delay is 10 seconds. */
116     public static final long DEFAULT_TRIGGER_UPDATE_DELAY = 10*1000;
117 
118     /** The minimum possible update delay is 1/2 second. */
119     public static final long MIN_TRIGGER_UPDATE_DELAY = 500;
120 
121     /** If not specified, trigger maxumum delay is 2 minutes. */
122     public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000;
123 
124     /** The minimum possible update delay is 1 second. */
125     public static final long MIN_TRIGGER_MAX_DELAY = 1000;
126 
127     final JobInfo job;
128     /**
129      * Uid of the package requesting this job.  This can differ from the "source"
130      * uid when the job was scheduled on the app's behalf, such as with the jobs
131      * that underly Sync Manager operation.
132      */
133     final int callingUid;
134     final int targetSdkVersion;
135     final String batteryName;
136 
137     /**
138      * Identity of the app in which the job is hosted.
139      */
140     final String sourcePackageName;
141     final int sourceUserId;
142     final int sourceUid;
143     final String sourceTag;
144 
145     final String tag;
146 
147     private GrantedUriPermissions uriPerms;
148     private boolean prepared;
149 
150     static final boolean DEBUG_PREPARE = true;
151     private Throwable unpreparedPoint = null;
152 
153     /**
154      * Earliest point in the future at which this job will be eligible to run. A value of 0
155      * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
156      */
157     private final long earliestRunTimeElapsedMillis;
158     /**
159      * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
160      * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
161      */
162     private final long latestRunTimeElapsedMillis;
163 
164     /**
165      * Valid only for periodic jobs. The original latest point in the future at which this
166      * job was expected to run.
167      */
168     private long mOriginalLatestRunTimeElapsedMillis;
169 
170     /** How many times this job has failed, used to compute back-off. */
171     private final int numFailures;
172 
173     /**
174      * Current standby heartbeat when this job was scheduled or last ran.  Used to
175      * pin the runnability check regardless of the job's app moving between buckets.
176      */
177     private final long baseHeartbeat;
178 
179     /**
180      * Which app standby bucket this job's app is in.  Updated when the app is moved to a
181      * different bucket.
182      */
183     private int standbyBucket;
184 
185     /**
186      * Debugging: timestamp if we ever defer this job based on standby bucketing, this
187      * is when we did so.
188      */
189     private long whenStandbyDeferred;
190 
191     // Constraints.
192     final int requiredConstraints;
193     private final int mRequiredConstraintsOfInterest;
194     int satisfiedConstraints = 0;
195     private int mSatisfiedConstraintsOfInterest = 0;
196 
197     // Set to true if doze constraint was satisfied due to app being whitelisted.
198     public boolean dozeWhitelisted;
199 
200     // Set to true when the app is "active" per AppStateTracker
201     public boolean uidActive;
202 
203     /**
204      * Flag for {@link #trackingControllers}: the battery controller is currently tracking this job.
205      */
206     public static final int TRACKING_BATTERY = 1<<0;
207     /**
208      * Flag for {@link #trackingControllers}: the network connectivity controller is currently
209      * tracking this job.
210      */
211     public static final int TRACKING_CONNECTIVITY = 1<<1;
212     /**
213      * Flag for {@link #trackingControllers}: the content observer controller is currently
214      * tracking this job.
215      */
216     public static final int TRACKING_CONTENT = 1<<2;
217     /**
218      * Flag for {@link #trackingControllers}: the idle controller is currently tracking this job.
219      */
220     public static final int TRACKING_IDLE = 1<<3;
221     /**
222      * Flag for {@link #trackingControllers}: the storage controller is currently tracking this job.
223      */
224     public static final int TRACKING_STORAGE = 1<<4;
225     /**
226      * Flag for {@link #trackingControllers}: the time controller is currently tracking this job.
227      */
228     public static final int TRACKING_TIME = 1<<5;
229     /**
230      * Flag for {@link #trackingControllers}: the quota controller is currently tracking this job.
231      */
232     public static final int TRACKING_QUOTA = 1 << 6;
233 
234     /**
235      * Bit mask of controllers that are currently tracking the job.
236      */
237     private int trackingControllers;
238 
239     /**
240      * Flag for {@link #mInternalFlags}: this job was scheduled when the app that owns the job
241      * service (not necessarily the caller) was in the foreground and the job has no time
242      * constraints, which makes it exempted from the battery saver job restriction.
243      *
244      * @hide
245      */
246     public static final int INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION = 1 << 0;
247 
248     /**
249      * Versatile, persistable flags for a job that's updated within the system server,
250      * as opposed to {@link JobInfo#flags} that's set by callers.
251      */
252     private int mInternalFlags;
253 
254     // These are filled in by controllers when preparing for execution.
255     public ArraySet<Uri> changedUris;
256     public ArraySet<String> changedAuthorities;
257     public Network network;
258 
259     public int lastEvaluatedPriority;
260 
261     // If non-null, this is work that has been enqueued for the job.
262     public ArrayList<JobWorkItem> pendingWork;
263 
264     // If non-null, this is work that is currently being executed.
265     public ArrayList<JobWorkItem> executingWork;
266 
267     public int nextPendingWorkId = 1;
268 
269     // Used by shell commands
270     public int overrideState = 0;
271 
272     // When this job was enqueued, for ordering.  (in elapsedRealtimeMillis)
273     public long enqueueTime;
274 
275     // Metrics about queue latency.  (in uptimeMillis)
276     public long madePending;
277     public long madeActive;
278 
279     /**
280      * Last time a job finished successfully for a periodic job, in the currentTimeMillis time,
281      * for dumpsys.
282      */
283     private long mLastSuccessfulRunTime;
284 
285     /**
286      * Last time a job finished unsuccessfully, in the currentTimeMillis time, for dumpsys.
287      */
288     private long mLastFailedRunTime;
289 
290     /**
291      * Transient: when a job is inflated from disk before we have a reliable RTC clock time,
292      * we retain the canonical (delay, deadline) scheduling tuple read out of the persistent
293      * store in UTC so that we can fix up the job's scheduling criteria once we get a good
294      * wall-clock time.  If we have to persist the job again before the clock has been updated,
295      * we record these times again rather than calculating based on the earliest/latest elapsed
296      * time base figures.
297      *
298      * 'first' is the earliest/delay time, and 'second' is the latest/deadline time.
299      */
300     private Pair<Long, Long> mPersistedUtcTimes;
301 
302     /**
303      * For use only by ContentObserverController: state it is maintaining about content URIs
304      * being observed.
305      */
306     ContentObserverController.JobInstance contentObserverJobInstance;
307 
308     private long totalNetworkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
309 
310     /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
311     /////// states change.
312 
313     /**
314      * The deadline for the job has passed. This is only good for non-periodic jobs. A periodic job
315      * should only run if its constraints are satisfied.
316      * Computed as: NOT periodic AND has deadline constraint AND deadline constraint satisfied.
317      */
318     private boolean mReadyDeadlineSatisfied;
319 
320     /**
321      * The device isn't Dozing or this job will be in the foreground. This implicit constraint must
322      * be satisfied.
323      */
324     private boolean mReadyNotDozing;
325 
326     /**
327      * The job is not restricted from running in the background (due to Battery Saver). This
328      * implicit constraint must be satisfied.
329      */
330     private boolean mReadyNotRestrictedInBg;
331 
332     /** The job is within its quota based on its standby bucket. */
333     private boolean mReadyWithinQuota;
334 
335     /** Provide a handle to the service that this job will be run on. */
getServiceToken()336     public int getServiceToken() {
337         return callingUid;
338     }
339 
340     /**
341      * Core constructor for JobStatus instances.  All other ctors funnel down to this one.
342      *
343      * @param job The actual requested parameters for the job
344      * @param callingUid Identity of the app that is scheduling the job.  This may not be the
345      *     app in which the job is implemented; such as with sync jobs.
346      * @param targetSdkVersion The targetSdkVersion of the app in which the job will run.
347      * @param sourcePackageName The package name of the app in which the job will run.
348      * @param sourceUserId The user in which the job will run
349      * @param standbyBucket The standby bucket that the source package is currently assigned to,
350      *     cached here for speed of handling during runnability evaluations (and updated when bucket
351      *     assignments are changed)
352      * @param heartbeat Timestamp of when the job was created, in the standby-related
353      *     timebase.
354      * @param tag A string associated with the job for debugging/logging purposes.
355      * @param numFailures Count of how many times this job has requested a reschedule because
356      *     its work was not yet finished.
357      * @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job
358      *     is to be considered runnable
359      * @param latestRunTimeElapsedMillis Milestone: point in time at which the job will be
360      *     considered overdue
361      * @param lastSuccessfulRunTime When did we last run this job to completion?
362      * @param lastFailedRunTime When did we last run this job only to have it stop incomplete?
363      * @param internalFlags Non-API property flags about this job
364      */
JobStatus(JobInfo job, int callingUid, int targetSdkVersion, String sourcePackageName, int sourceUserId, int standbyBucket, long heartbeat, String tag, int numFailures, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags)365     private JobStatus(JobInfo job, int callingUid, int targetSdkVersion, String sourcePackageName,
366             int sourceUserId, int standbyBucket, long heartbeat, String tag, int numFailures,
367             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
368             long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags) {
369         this.job = job;
370         this.callingUid = callingUid;
371         this.targetSdkVersion = targetSdkVersion;
372         this.standbyBucket = standbyBucket;
373         this.baseHeartbeat = heartbeat;
374 
375         int tempSourceUid = -1;
376         if (sourceUserId != -1 && sourcePackageName != null) {
377             try {
378                 tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
379                         sourceUserId);
380             } catch (RemoteException ex) {
381                 // Can't happen, PackageManager runs in the same process.
382             }
383         }
384         if (tempSourceUid == -1) {
385             this.sourceUid = callingUid;
386             this.sourceUserId = UserHandle.getUserId(callingUid);
387             this.sourcePackageName = job.getService().getPackageName();
388             this.sourceTag = null;
389         } else {
390             this.sourceUid = tempSourceUid;
391             this.sourceUserId = sourceUserId;
392             this.sourcePackageName = sourcePackageName;
393             this.sourceTag = tag;
394         }
395 
396         this.batteryName = this.sourceTag != null
397                 ? this.sourceTag + ":" + job.getService().getPackageName()
398                 : job.getService().flattenToShortString();
399         this.tag = "*job*/" + this.batteryName;
400 
401         this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
402         this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
403         this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
404         this.numFailures = numFailures;
405 
406         int requiredConstraints = job.getConstraintFlags();
407         if (job.getRequiredNetwork() != null) {
408             requiredConstraints |= CONSTRAINT_CONNECTIVITY;
409         }
410         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
411             requiredConstraints |= CONSTRAINT_TIMING_DELAY;
412         }
413         if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
414             requiredConstraints |= CONSTRAINT_DEADLINE;
415         }
416         if (job.getTriggerContentUris() != null) {
417             requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
418         }
419         this.requiredConstraints = requiredConstraints;
420         mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
421         mReadyNotDozing = (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
422 
423         mLastSuccessfulRunTime = lastSuccessfulRunTime;
424         mLastFailedRunTime = lastFailedRunTime;
425 
426         mInternalFlags = internalFlags;
427 
428         updateEstimatedNetworkBytesLocked();
429 
430         if (job.getRequiredNetwork() != null) {
431             // Later, when we check if a given network satisfies the required
432             // network, we need to know the UID that is requesting it, so push
433             // our source UID into place.
434             job.getRequiredNetwork().networkCapabilities.setSingleUid(this.sourceUid);
435         }
436     }
437 
438     /** Copy constructor: used specifically when cloning JobStatus objects for persistence,
439      *   so we preserve RTC window bounds if the source object has them. */
JobStatus(JobStatus jobStatus)440     public JobStatus(JobStatus jobStatus) {
441         this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.targetSdkVersion,
442                 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
443                 jobStatus.getStandbyBucket(), jobStatus.getBaseHeartbeat(),
444                 jobStatus.getSourceTag(), jobStatus.getNumFailures(),
445                 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
446                 jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
447                 jobStatus.getInternalFlags());
448         mPersistedUtcTimes = jobStatus.mPersistedUtcTimes;
449         if (jobStatus.mPersistedUtcTimes != null) {
450             if (DEBUG) {
451                 Slog.i(TAG, "Cloning job with persisted run times", new RuntimeException("here"));
452             }
453         }
454     }
455 
456     /**
457      * Create a new JobStatus that was loaded from disk. We ignore the provided
458      * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job
459      * from the {@link com.android.server.job.JobStore} and still want to respect its
460      * wallclock runtime rather than resetting it on every boot.
461      * We consider a freshly loaded job to no longer be in back-off, and the associated
462      * standby bucket is whatever the OS thinks it should be at this moment.
463      */
JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId, int standbyBucket, long baseHeartbeat, String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, Pair<Long, Long> persistedExecutionTimesUTC, int innerFlags)464     public JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId,
465             int standbyBucket, long baseHeartbeat, String sourceTag,
466             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
467             long lastSuccessfulRunTime, long lastFailedRunTime,
468             Pair<Long, Long> persistedExecutionTimesUTC,
469             int innerFlags) {
470         this(job, callingUid, resolveTargetSdkVersion(job), sourcePkgName, sourceUserId,
471                 standbyBucket, baseHeartbeat,
472                 sourceTag, 0,
473                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
474                 lastSuccessfulRunTime, lastFailedRunTime, innerFlags);
475 
476         // Only during initial inflation do we record the UTC-timebase execution bounds
477         // read from the persistent store.  If we ever have to recreate the JobStatus on
478         // the fly, it means we're rescheduling the job; and this means that the calculated
479         // elapsed timebase bounds intrinsically become correct.
480         this.mPersistedUtcTimes = persistedExecutionTimesUTC;
481         if (persistedExecutionTimesUTC != null) {
482             if (DEBUG) {
483                 Slog.i(TAG, "+ restored job with RTC times because of bad boot clock");
484             }
485         }
486     }
487 
488     /** Create a new job to be rescheduled with the provided parameters. */
JobStatus(JobStatus rescheduling, long newBaseHeartbeat, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int backoffAttempt, long lastSuccessfulRunTime, long lastFailedRunTime)489     public JobStatus(JobStatus rescheduling, long newBaseHeartbeat,
490             long newEarliestRuntimeElapsedMillis,
491             long newLatestRuntimeElapsedMillis, int backoffAttempt,
492             long lastSuccessfulRunTime, long lastFailedRunTime) {
493         this(rescheduling.job, rescheduling.getUid(), resolveTargetSdkVersion(rescheduling.job),
494                 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
495                 rescheduling.getStandbyBucket(), newBaseHeartbeat,
496                 rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
497                 newLatestRuntimeElapsedMillis,
498                 lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags());
499     }
500 
501     /**
502      * Create a newly scheduled job.
503      * @param callingUid Uid of the package that scheduled this job.
504      * @param sourcePkg Package name of the app that will actually run the job.  Null indicates
505      *     that the calling package is the source.
506      * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
507      *     caller.
508      */
createFromJobInfo(JobInfo job, int callingUid, String sourcePkg, int sourceUserId, String tag)509     public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePkg,
510             int sourceUserId, String tag) {
511         final long elapsedNow = sElapsedRealtimeClock.millis();
512         final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
513         if (job.isPeriodic()) {
514             // Make sure period is in the interval [min_possible_period, max_possible_period].
515             final long period = Math.max(JobInfo.getMinPeriodMillis(),
516                     Math.min(JobSchedulerService.MAX_ALLOWED_PERIOD_MS, job.getIntervalMillis()));
517             latestRunTimeElapsedMillis = elapsedNow + period;
518             earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis
519                     // Make sure flex is in the interval [min_possible_flex, period].
520                     - Math.max(JobInfo.getMinFlexMillis(), Math.min(period, job.getFlexMillis()));
521         } else {
522             earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
523                     elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
524             latestRunTimeElapsedMillis = job.hasLateConstraint() ?
525                     elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
526         }
527         String jobPackage = (sourcePkg != null) ? sourcePkg : job.getService().getPackageName();
528 
529         int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
530                 sourceUserId, elapsedNow);
531         JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
532         long currentHeartbeat = js != null
533                 ? js.baseHeartbeatForApp(jobPackage, sourceUserId, standbyBucket)
534                 : 0;
535         return new JobStatus(job, callingUid, resolveTargetSdkVersion(job), sourcePkg, sourceUserId,
536                 standbyBucket, currentHeartbeat, tag, 0,
537                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
538                 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
539                 /*innerFlags=*/ 0);
540     }
541 
enqueueWorkLocked(IActivityManager am, JobWorkItem work)542     public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) {
543         if (pendingWork == null) {
544             pendingWork = new ArrayList<>();
545         }
546         work.setWorkId(nextPendingWorkId);
547         nextPendingWorkId++;
548         if (work.getIntent() != null
549                 && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) {
550             work.setGrants(GrantedUriPermissions.createFromIntent(am, work.getIntent(), sourceUid,
551                     sourcePackageName, sourceUserId, toShortString()));
552         }
553         pendingWork.add(work);
554         updateEstimatedNetworkBytesLocked();
555     }
556 
dequeueWorkLocked()557     public JobWorkItem dequeueWorkLocked() {
558         if (pendingWork != null && pendingWork.size() > 0) {
559             JobWorkItem work = pendingWork.remove(0);
560             if (work != null) {
561                 if (executingWork == null) {
562                     executingWork = new ArrayList<>();
563                 }
564                 executingWork.add(work);
565                 work.bumpDeliveryCount();
566             }
567             updateEstimatedNetworkBytesLocked();
568             return work;
569         }
570         return null;
571     }
572 
hasWorkLocked()573     public boolean hasWorkLocked() {
574         return (pendingWork != null && pendingWork.size() > 0) || hasExecutingWorkLocked();
575     }
576 
hasExecutingWorkLocked()577     public boolean hasExecutingWorkLocked() {
578         return executingWork != null && executingWork.size() > 0;
579     }
580 
ungrantWorkItem(IActivityManager am, JobWorkItem work)581     private static void ungrantWorkItem(IActivityManager am, JobWorkItem work) {
582         if (work.getGrants() != null) {
583             ((GrantedUriPermissions)work.getGrants()).revoke(am);
584         }
585     }
586 
completeWorkLocked(IActivityManager am, int workId)587     public boolean completeWorkLocked(IActivityManager am, int workId) {
588         if (executingWork != null) {
589             final int N = executingWork.size();
590             for (int i = 0; i < N; i++) {
591                 JobWorkItem work = executingWork.get(i);
592                 if (work.getWorkId() == workId) {
593                     executingWork.remove(i);
594                     ungrantWorkItem(am, work);
595                     return true;
596                 }
597             }
598         }
599         return false;
600     }
601 
ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list)602     private static void ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list) {
603         if (list != null) {
604             final int N = list.size();
605             for (int i = 0; i < N; i++) {
606                 ungrantWorkItem(am, list.get(i));
607             }
608         }
609     }
610 
stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob)611     public void stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob) {
612         if (incomingJob != null) {
613             // We are replacing with a new job -- transfer the work!  We do any executing
614             // work first, since that was originally at the front of the pending work.
615             if (executingWork != null && executingWork.size() > 0) {
616                 incomingJob.pendingWork = executingWork;
617             }
618             if (incomingJob.pendingWork == null) {
619                 incomingJob.pendingWork = pendingWork;
620             } else if (pendingWork != null && pendingWork.size() > 0) {
621                 incomingJob.pendingWork.addAll(pendingWork);
622             }
623             pendingWork = null;
624             executingWork = null;
625             incomingJob.nextPendingWorkId = nextPendingWorkId;
626             incomingJob.updateEstimatedNetworkBytesLocked();
627         } else {
628             // We are completely stopping the job...  need to clean up work.
629             ungrantWorkList(am, pendingWork);
630             pendingWork = null;
631             ungrantWorkList(am, executingWork);
632             executingWork = null;
633         }
634         updateEstimatedNetworkBytesLocked();
635     }
636 
prepareLocked(IActivityManager am)637     public void prepareLocked(IActivityManager am) {
638         if (prepared) {
639             Slog.wtf(TAG, "Already prepared: " + this);
640             return;
641         }
642         prepared = true;
643         if (DEBUG_PREPARE) {
644             unpreparedPoint = null;
645         }
646         final ClipData clip = job.getClipData();
647         if (clip != null) {
648             uriPerms = GrantedUriPermissions.createFromClip(am, clip, sourceUid, sourcePackageName,
649                     sourceUserId, job.getClipGrantFlags(), toShortString());
650         }
651     }
652 
unprepareLocked(IActivityManager am)653     public void unprepareLocked(IActivityManager am) {
654         if (!prepared) {
655             Slog.wtf(TAG, "Hasn't been prepared: " + this);
656             if (DEBUG_PREPARE && unpreparedPoint != null) {
657                 Slog.e(TAG, "Was already unprepared at ", unpreparedPoint);
658             }
659             return;
660         }
661         prepared = false;
662         if (DEBUG_PREPARE) {
663             unpreparedPoint = new Throwable().fillInStackTrace();
664         }
665         if (uriPerms != null) {
666             uriPerms.revoke(am);
667             uriPerms = null;
668         }
669     }
670 
isPreparedLocked()671     public boolean isPreparedLocked() {
672         return prepared;
673     }
674 
getJob()675     public JobInfo getJob() {
676         return job;
677     }
678 
getJobId()679     public int getJobId() {
680         return job.getId();
681     }
682 
getTargetSdkVersion()683     public int getTargetSdkVersion() {
684         return targetSdkVersion;
685     }
686 
printUniqueId(PrintWriter pw)687     public void printUniqueId(PrintWriter pw) {
688         UserHandle.formatUid(pw, callingUid);
689         pw.print("/");
690         pw.print(job.getId());
691     }
692 
getNumFailures()693     public int getNumFailures() {
694         return numFailures;
695     }
696 
getServiceComponent()697     public ComponentName getServiceComponent() {
698         return job.getService();
699     }
700 
getSourcePackageName()701     public String getSourcePackageName() {
702         return sourcePackageName;
703     }
704 
getSourceUid()705     public int getSourceUid() {
706         return sourceUid;
707     }
708 
getSourceUserId()709     public int getSourceUserId() {
710         return sourceUserId;
711     }
712 
getUserId()713     public int getUserId() {
714         return UserHandle.getUserId(callingUid);
715     }
716 
getStandbyBucket()717     public int getStandbyBucket() {
718         return standbyBucket;
719     }
720 
getBaseHeartbeat()721     public long getBaseHeartbeat() {
722         return baseHeartbeat;
723     }
724 
setStandbyBucket(int newBucket)725     public void setStandbyBucket(int newBucket) {
726         standbyBucket = newBucket;
727     }
728 
729     // Called only by the standby monitoring code
getWhenStandbyDeferred()730     public long getWhenStandbyDeferred() {
731         return whenStandbyDeferred;
732     }
733 
734     // Called only by the standby monitoring code
setWhenStandbyDeferred(long now)735     public void setWhenStandbyDeferred(long now) {
736         whenStandbyDeferred = now;
737     }
738 
getSourceTag()739     public String getSourceTag() {
740         return sourceTag;
741     }
742 
getUid()743     public int getUid() {
744         return callingUid;
745     }
746 
getBatteryName()747     public String getBatteryName() {
748         return batteryName;
749     }
750 
getTag()751     public String getTag() {
752         return tag;
753     }
754 
getPriority()755     public int getPriority() {
756         return job.getPriority();
757     }
758 
getFlags()759     public int getFlags() {
760         return job.getFlags();
761     }
762 
getInternalFlags()763     public int getInternalFlags() {
764         return mInternalFlags;
765     }
766 
addInternalFlags(int flags)767     public void addInternalFlags(int flags) {
768         mInternalFlags |= flags;
769     }
770 
getSatisfiedConstraintFlags()771     public int getSatisfiedConstraintFlags() {
772         return satisfiedConstraints;
773     }
774 
maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker)775     public void maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker) {
776         // Jobs with time constraints shouldn't be exempted.
777         if (job.hasEarlyConstraint() || job.hasLateConstraint()) {
778             return;
779         }
780         // Already exempted, skip the foreground check.
781         if ((mInternalFlags & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
782             return;
783         }
784         if (uidForegroundChecker.test(getSourceUid())) {
785             addInternalFlags(INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION);
786         }
787     }
788 
updateEstimatedNetworkBytesLocked()789     private void updateEstimatedNetworkBytesLocked() {
790         totalNetworkBytes = computeEstimatedNetworkBytesLocked();
791     }
792 
computeEstimatedNetworkBytesLocked()793     private long computeEstimatedNetworkBytesLocked() {
794         // If any component of the job has unknown usage, we don't have a
795         // complete picture of what data will be used, and we have to treat the
796         // entire job as unknown.
797         long totalNetworkBytes = 0;
798         long networkBytes = job.getEstimatedNetworkBytes();
799         if (networkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
800             return JobInfo.NETWORK_BYTES_UNKNOWN;
801         } else {
802             totalNetworkBytes += networkBytes;
803         }
804         if (pendingWork != null) {
805             for (int i = 0; i < pendingWork.size(); i++) {
806                 networkBytes = pendingWork.get(i).getEstimatedNetworkBytes();
807                 if (networkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
808                     return JobInfo.NETWORK_BYTES_UNKNOWN;
809                 } else {
810                     totalNetworkBytes += networkBytes;
811                 }
812             }
813         }
814         return totalNetworkBytes;
815     }
816 
getEstimatedNetworkBytes()817     public long getEstimatedNetworkBytes() {
818         return totalNetworkBytes;
819     }
820 
821     /** Does this job have any sort of networking constraint? */
hasConnectivityConstraint()822     public boolean hasConnectivityConstraint() {
823         return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
824     }
825 
hasChargingConstraint()826     public boolean hasChargingConstraint() {
827         return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
828     }
829 
hasBatteryNotLowConstraint()830     public boolean hasBatteryNotLowConstraint() {
831         return (requiredConstraints&CONSTRAINT_BATTERY_NOT_LOW) != 0;
832     }
833 
hasPowerConstraint()834     public boolean hasPowerConstraint() {
835         return (requiredConstraints&(CONSTRAINT_CHARGING|CONSTRAINT_BATTERY_NOT_LOW)) != 0;
836     }
837 
hasStorageNotLowConstraint()838     public boolean hasStorageNotLowConstraint() {
839         return (requiredConstraints&CONSTRAINT_STORAGE_NOT_LOW) != 0;
840     }
841 
hasTimingDelayConstraint()842     public boolean hasTimingDelayConstraint() {
843         return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0;
844     }
845 
hasDeadlineConstraint()846     public boolean hasDeadlineConstraint() {
847         return (requiredConstraints&CONSTRAINT_DEADLINE) != 0;
848     }
849 
hasIdleConstraint()850     public boolean hasIdleConstraint() {
851         return (requiredConstraints&CONSTRAINT_IDLE) != 0;
852     }
853 
hasContentTriggerConstraint()854     public boolean hasContentTriggerConstraint() {
855         return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
856     }
857 
getTriggerContentUpdateDelay()858     public long getTriggerContentUpdateDelay() {
859         long time = job.getTriggerContentUpdateDelay();
860         if (time < 0) {
861             return DEFAULT_TRIGGER_UPDATE_DELAY;
862         }
863         return Math.max(time, MIN_TRIGGER_UPDATE_DELAY);
864     }
865 
getTriggerContentMaxDelay()866     public long getTriggerContentMaxDelay() {
867         long time = job.getTriggerContentMaxDelay();
868         if (time < 0) {
869             return DEFAULT_TRIGGER_MAX_DELAY;
870         }
871         return Math.max(time, MIN_TRIGGER_MAX_DELAY);
872     }
873 
isPersisted()874     public boolean isPersisted() {
875         return job.isPersisted();
876     }
877 
getEarliestRunTime()878     public long getEarliestRunTime() {
879         return earliestRunTimeElapsedMillis;
880     }
881 
getLatestRunTimeElapsed()882     public long getLatestRunTimeElapsed() {
883         return latestRunTimeElapsedMillis;
884     }
885 
getOriginalLatestRunTimeElapsed()886     public long getOriginalLatestRunTimeElapsed() {
887         return mOriginalLatestRunTimeElapsedMillis;
888     }
889 
setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed)890     public void setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed) {
891         mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed;
892     }
893 
894     /**
895      * Return the fractional position of "now" within the "run time" window of
896      * this job.
897      * <p>
898      * For example, if the earliest run time was 10 minutes ago, and the latest
899      * run time is 30 minutes from now, this would return 0.25.
900      * <p>
901      * If the job has no window defined, returns 1. When only an earliest or
902      * latest time is defined, it's treated as an infinitely small window at
903      * that time.
904      */
getFractionRunTime()905     public float getFractionRunTime() {
906         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
907         if (earliestRunTimeElapsedMillis == 0 && latestRunTimeElapsedMillis == Long.MAX_VALUE) {
908             return 1;
909         } else if (earliestRunTimeElapsedMillis == 0) {
910             return now >= latestRunTimeElapsedMillis ? 1 : 0;
911         } else if (latestRunTimeElapsedMillis == Long.MAX_VALUE) {
912             return now >= earliestRunTimeElapsedMillis ? 1 : 0;
913         } else {
914             if (now <= earliestRunTimeElapsedMillis) {
915                 return 0;
916             } else if (now >= latestRunTimeElapsedMillis) {
917                 return 1;
918             } else {
919                 return (float) (now - earliestRunTimeElapsedMillis)
920                         / (float) (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis);
921             }
922         }
923     }
924 
getPersistedUtcTimes()925     public Pair<Long, Long> getPersistedUtcTimes() {
926         return mPersistedUtcTimes;
927     }
928 
clearPersistedUtcTimes()929     public void clearPersistedUtcTimes() {
930         mPersistedUtcTimes = null;
931     }
932 
933     /** @return true if the constraint was changed, false otherwise. */
setChargingConstraintSatisfied(boolean state)934     boolean setChargingConstraintSatisfied(boolean state) {
935         return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
936     }
937 
938     /** @return true if the constraint was changed, false otherwise. */
setBatteryNotLowConstraintSatisfied(boolean state)939     boolean setBatteryNotLowConstraintSatisfied(boolean state) {
940         return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state);
941     }
942 
943     /** @return true if the constraint was changed, false otherwise. */
setStorageNotLowConstraintSatisfied(boolean state)944     boolean setStorageNotLowConstraintSatisfied(boolean state) {
945         return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state);
946     }
947 
948     /** @return true if the constraint was changed, false otherwise. */
setTimingDelayConstraintSatisfied(boolean state)949     boolean setTimingDelayConstraintSatisfied(boolean state) {
950         return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
951     }
952 
953     /** @return true if the constraint was changed, false otherwise. */
setDeadlineConstraintSatisfied(boolean state)954     boolean setDeadlineConstraintSatisfied(boolean state) {
955         if (setConstraintSatisfied(CONSTRAINT_DEADLINE, state)) {
956             // The constraint was changed. Update the ready flag.
957             mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state;
958             return true;
959         }
960         return false;
961     }
962 
963     /** @return true if the constraint was changed, false otherwise. */
setIdleConstraintSatisfied(boolean state)964     boolean setIdleConstraintSatisfied(boolean state) {
965         return setConstraintSatisfied(CONSTRAINT_IDLE, state);
966     }
967 
968     /** @return true if the constraint was changed, false otherwise. */
setConnectivityConstraintSatisfied(boolean state)969     boolean setConnectivityConstraintSatisfied(boolean state) {
970         return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
971     }
972 
973     /** @return true if the constraint was changed, false otherwise. */
setContentTriggerConstraintSatisfied(boolean state)974     boolean setContentTriggerConstraintSatisfied(boolean state) {
975         return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
976     }
977 
978     /** @return true if the constraint was changed, false otherwise. */
setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted)979     boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
980         dozeWhitelisted = whitelisted;
981         if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state)) {
982             // The constraint was changed. Update the ready flag.
983             mReadyNotDozing = state || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
984             return true;
985         }
986         return false;
987     }
988 
989     /** @return true if the constraint was changed, false otherwise. */
setBackgroundNotRestrictedConstraintSatisfied(boolean state)990     boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
991         if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state)) {
992             // The constraint was changed. Update the ready flag.
993             mReadyNotRestrictedInBg = state;
994             return true;
995         }
996         return false;
997     }
998 
999     /** @return true if the constraint was changed, false otherwise. */
setQuotaConstraintSatisfied(boolean state)1000     boolean setQuotaConstraintSatisfied(boolean state) {
1001         if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, state)) {
1002             // The constraint was changed. Update the ready flag.
1003             mReadyWithinQuota = state;
1004             return true;
1005         }
1006         return false;
1007     }
1008 
1009     /** @return true if the state was changed, false otherwise. */
setUidActive(final boolean newActiveState)1010     boolean setUidActive(final boolean newActiveState) {
1011         if (newActiveState != uidActive) {
1012             uidActive = newActiveState;
1013             return true;
1014         }
1015         return false; /* unchanged */
1016     }
1017 
1018     /** @return true if the constraint was changed, false otherwise. */
setConstraintSatisfied(int constraint, boolean state)1019     boolean setConstraintSatisfied(int constraint, boolean state) {
1020         boolean old = (satisfiedConstraints&constraint) != 0;
1021         if (old == state) {
1022             return false;
1023         }
1024         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
1025         mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
1026         if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) {
1027             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED,
1028                     sourceUid, null, getBatteryName(), getProtoConstraint(constraint),
1029                     state ? StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED
1030                             : StatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
1031         }
1032         return true;
1033     }
1034 
isConstraintSatisfied(int constraint)1035     boolean isConstraintSatisfied(int constraint) {
1036         return (satisfiedConstraints&constraint) != 0;
1037     }
1038 
clearTrackingController(int which)1039     boolean clearTrackingController(int which) {
1040         if ((trackingControllers&which) != 0) {
1041             trackingControllers &= ~which;
1042             return true;
1043         }
1044         return false;
1045     }
1046 
setTrackingController(int which)1047     void setTrackingController(int which) {
1048         trackingControllers |= which;
1049     }
1050 
getLastSuccessfulRunTime()1051     public long getLastSuccessfulRunTime() {
1052         return mLastSuccessfulRunTime;
1053     }
1054 
getLastFailedRunTime()1055     public long getLastFailedRunTime() {
1056         return mLastFailedRunTime;
1057     }
1058 
1059     /**
1060      * @return Whether or not this job is ready to run, based on its requirements.
1061      */
isReady()1062     public boolean isReady() {
1063         return isReady(mSatisfiedConstraintsOfInterest);
1064     }
1065 
1066     /**
1067      * @return Whether or not this job would be ready to run if it had the specified constraint
1068      * granted, based on its requirements.
1069      */
wouldBeReadyWithConstraint(int constraint)1070     boolean wouldBeReadyWithConstraint(int constraint) {
1071         boolean oldValue = false;
1072         int satisfied = mSatisfiedConstraintsOfInterest;
1073         switch (constraint) {
1074             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
1075                 oldValue = mReadyNotRestrictedInBg;
1076                 mReadyNotRestrictedInBg = true;
1077                 break;
1078             case CONSTRAINT_DEADLINE:
1079                 oldValue = mReadyDeadlineSatisfied;
1080                 mReadyDeadlineSatisfied = true;
1081                 break;
1082             case CONSTRAINT_DEVICE_NOT_DOZING:
1083                 oldValue = mReadyNotDozing;
1084                 mReadyNotDozing = true;
1085                 break;
1086             case CONSTRAINT_WITHIN_QUOTA:
1087                 oldValue = mReadyWithinQuota;
1088                 mReadyWithinQuota = true;
1089                 break;
1090             default:
1091                 satisfied |= constraint;
1092                 break;
1093         }
1094 
1095         boolean toReturn = isReady(satisfied);
1096 
1097         switch (constraint) {
1098             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
1099                 mReadyNotRestrictedInBg = oldValue;
1100                 break;
1101             case CONSTRAINT_DEADLINE:
1102                 mReadyDeadlineSatisfied = oldValue;
1103                 break;
1104             case CONSTRAINT_DEVICE_NOT_DOZING:
1105                 mReadyNotDozing = oldValue;
1106                 break;
1107             case CONSTRAINT_WITHIN_QUOTA:
1108                 mReadyWithinQuota = oldValue;
1109                 break;
1110         }
1111         return toReturn;
1112     }
1113 
isReady(int satisfiedConstraints)1114     private boolean isReady(int satisfiedConstraints) {
1115         // Quota constraints trumps all other constraints.
1116         if (!mReadyWithinQuota) {
1117             return false;
1118         }
1119         // Deadline constraint trumps other constraints besides quota (except for periodic jobs
1120         // where deadline is an implementation detail. A periodic job should only run if its
1121         // constraints are satisfied).
1122         // DeviceNotDozing implicit constraint must be satisfied
1123         // NotRestrictedInBackground implicit constraint must be satisfied
1124         return mReadyNotDozing && mReadyNotRestrictedInBg && (mReadyDeadlineSatisfied
1125                 || isConstraintsSatisfied(satisfiedConstraints));
1126     }
1127 
1128     static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
1129             | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY
1130             | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
1131 
1132     // Soft override covers all non-"functional" constraints
1133     static final int SOFT_OVERRIDE_CONSTRAINTS =
1134             CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW
1135                     | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE;
1136 
1137     /**
1138      * @return Whether the constraints set on this job are satisfied.
1139      */
isConstraintsSatisfied()1140     public boolean isConstraintsSatisfied() {
1141         return isConstraintsSatisfied(mSatisfiedConstraintsOfInterest);
1142     }
1143 
isConstraintsSatisfied(int satisfiedConstraints)1144     private boolean isConstraintsSatisfied(int satisfiedConstraints) {
1145         if (overrideState == OVERRIDE_FULL) {
1146             // force override: the job is always runnable
1147             return true;
1148         }
1149 
1150         int sat = satisfiedConstraints;
1151         if (overrideState == OVERRIDE_SOFT) {
1152             // override: pretend all 'soft' requirements are satisfied
1153             sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
1154         }
1155 
1156         return (sat & mRequiredConstraintsOfInterest) == mRequiredConstraintsOfInterest;
1157     }
1158 
matches(int uid, int jobId)1159     public boolean matches(int uid, int jobId) {
1160         return this.job.getId() == jobId && this.callingUid == uid;
1161     }
1162 
1163     @Override
toString()1164     public String toString() {
1165         StringBuilder sb = new StringBuilder(128);
1166         sb.append("JobStatus{");
1167         sb.append(Integer.toHexString(System.identityHashCode(this)));
1168         sb.append(" #");
1169         UserHandle.formatUid(sb, callingUid);
1170         sb.append("/");
1171         sb.append(job.getId());
1172         sb.append(' ');
1173         sb.append(batteryName);
1174         sb.append(" u=");
1175         sb.append(getUserId());
1176         sb.append(" s=");
1177         sb.append(getSourceUid());
1178         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME
1179                 || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
1180             long now = sElapsedRealtimeClock.millis();
1181             sb.append(" TIME=");
1182             formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now);
1183             sb.append(":");
1184             formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now);
1185         }
1186         if (job.getRequiredNetwork() != null) {
1187             sb.append(" NET");
1188         }
1189         if (job.isRequireCharging()) {
1190             sb.append(" CHARGING");
1191         }
1192         if (job.isRequireBatteryNotLow()) {
1193             sb.append(" BATNOTLOW");
1194         }
1195         if (job.isRequireStorageNotLow()) {
1196             sb.append(" STORENOTLOW");
1197         }
1198         if (job.isRequireDeviceIdle()) {
1199             sb.append(" IDLE");
1200         }
1201         if (job.isPeriodic()) {
1202             sb.append(" PERIODIC");
1203         }
1204         if (job.isPersisted()) {
1205             sb.append(" PERSISTED");
1206         }
1207         if ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) == 0) {
1208             sb.append(" WAIT:DEV_NOT_DOZING");
1209         }
1210         if (job.getTriggerContentUris() != null) {
1211             sb.append(" URIS=");
1212             sb.append(Arrays.toString(job.getTriggerContentUris()));
1213         }
1214         if (numFailures != 0) {
1215             sb.append(" failures=");
1216             sb.append(numFailures);
1217         }
1218         if (isReady()) {
1219             sb.append(" READY");
1220         }
1221         sb.append("}");
1222         return sb.toString();
1223     }
1224 
formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now)1225     private void formatRunTime(PrintWriter pw, long runtime, long  defaultValue, long now) {
1226         if (runtime == defaultValue) {
1227             pw.print("none");
1228         } else {
1229             TimeUtils.formatDuration(runtime - now, pw);
1230         }
1231     }
1232 
formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now)1233     private void formatRunTime(StringBuilder sb, long runtime, long  defaultValue, long now) {
1234         if (runtime == defaultValue) {
1235             sb.append("none");
1236         } else {
1237             TimeUtils.formatDuration(runtime - now, sb);
1238         }
1239     }
1240 
1241     /**
1242      * Convenience function to identify a job uniquely without pulling all the data that
1243      * {@link #toString()} returns.
1244      */
toShortString()1245     public String toShortString() {
1246         StringBuilder sb = new StringBuilder();
1247         sb.append(Integer.toHexString(System.identityHashCode(this)));
1248         sb.append(" #");
1249         UserHandle.formatUid(sb, callingUid);
1250         sb.append("/");
1251         sb.append(job.getId());
1252         sb.append(' ');
1253         sb.append(batteryName);
1254         return sb.toString();
1255     }
1256 
1257     /**
1258      * Convenience function to identify a job uniquely without pulling all the data that
1259      * {@link #toString()} returns.
1260      */
toShortStringExceptUniqueId()1261     public String toShortStringExceptUniqueId() {
1262         StringBuilder sb = new StringBuilder();
1263         sb.append(Integer.toHexString(System.identityHashCode(this)));
1264         sb.append(' ');
1265         sb.append(batteryName);
1266         return sb.toString();
1267     }
1268 
1269     /**
1270      * Convenience function to dump data that identifies a job uniquely to proto. This is intended
1271      * to mimic {@link #toShortString}.
1272      */
writeToShortProto(ProtoOutputStream proto, long fieldId)1273     public void writeToShortProto(ProtoOutputStream proto, long fieldId) {
1274         final long token = proto.start(fieldId);
1275 
1276         proto.write(JobStatusShortInfoProto.CALLING_UID, callingUid);
1277         proto.write(JobStatusShortInfoProto.JOB_ID, job.getId());
1278         proto.write(JobStatusShortInfoProto.BATTERY_NAME, batteryName);
1279 
1280         proto.end(token);
1281     }
1282 
dumpConstraints(PrintWriter pw, int constraints)1283     void dumpConstraints(PrintWriter pw, int constraints) {
1284         if ((constraints&CONSTRAINT_CHARGING) != 0) {
1285             pw.print(" CHARGING");
1286         }
1287         if ((constraints& CONSTRAINT_BATTERY_NOT_LOW) != 0) {
1288             pw.print(" BATTERY_NOT_LOW");
1289         }
1290         if ((constraints& CONSTRAINT_STORAGE_NOT_LOW) != 0) {
1291             pw.print(" STORAGE_NOT_LOW");
1292         }
1293         if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) {
1294             pw.print(" TIMING_DELAY");
1295         }
1296         if ((constraints&CONSTRAINT_DEADLINE) != 0) {
1297             pw.print(" DEADLINE");
1298         }
1299         if ((constraints&CONSTRAINT_IDLE) != 0) {
1300             pw.print(" IDLE");
1301         }
1302         if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
1303             pw.print(" CONNECTIVITY");
1304         }
1305         if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) {
1306             pw.print(" CONTENT_TRIGGER");
1307         }
1308         if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
1309             pw.print(" DEVICE_NOT_DOZING");
1310         }
1311         if ((constraints&CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
1312             pw.print(" BACKGROUND_NOT_RESTRICTED");
1313         }
1314         if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
1315             pw.print(" WITHIN_QUOTA");
1316         }
1317         if (constraints != 0) {
1318             pw.print(" [0x");
1319             pw.print(Integer.toHexString(constraints));
1320             pw.print("]");
1321         }
1322     }
1323 
1324     /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */
getProtoConstraint(int constraint)1325     private int getProtoConstraint(int constraint) {
1326         switch (constraint) {
1327             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
1328                 return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
1329             case CONSTRAINT_BATTERY_NOT_LOW:
1330                 return JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW;
1331             case CONSTRAINT_CHARGING:
1332                 return JobServerProtoEnums.CONSTRAINT_CHARGING;
1333             case CONSTRAINT_CONNECTIVITY:
1334                 return JobServerProtoEnums.CONSTRAINT_CONNECTIVITY;
1335             case CONSTRAINT_CONTENT_TRIGGER:
1336                 return JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER;
1337             case CONSTRAINT_DEADLINE:
1338                 return JobServerProtoEnums.CONSTRAINT_DEADLINE;
1339             case CONSTRAINT_DEVICE_NOT_DOZING:
1340                 return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING;
1341             case CONSTRAINT_IDLE:
1342                 return JobServerProtoEnums.CONSTRAINT_IDLE;
1343             case CONSTRAINT_STORAGE_NOT_LOW:
1344                 return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW;
1345             case CONSTRAINT_TIMING_DELAY:
1346                 return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY;
1347             case CONSTRAINT_WITHIN_QUOTA:
1348                 return JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA;
1349             default:
1350                 return JobServerProtoEnums.CONSTRAINT_UNKNOWN;
1351         }
1352     }
1353 
1354     /** Writes constraints to the given repeating proto field. */
dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints)1355     void dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints) {
1356         if ((constraints & CONSTRAINT_CHARGING) != 0) {
1357             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CHARGING);
1358         }
1359         if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
1360             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW);
1361         }
1362         if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
1363             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW);
1364         }
1365         if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
1366             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_TIMING_DELAY);
1367         }
1368         if ((constraints & CONSTRAINT_DEADLINE) != 0) {
1369             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEADLINE);
1370         }
1371         if ((constraints & CONSTRAINT_IDLE) != 0) {
1372             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_IDLE);
1373         }
1374         if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
1375             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONNECTIVITY);
1376         }
1377         if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
1378             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER);
1379         }
1380         if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
1381             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING);
1382         }
1383         if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
1384             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA);
1385         }
1386         if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
1387             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
1388         }
1389     }
1390 
dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index)1391     private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
1392         pw.print(prefix); pw.print("  #"); pw.print(index); pw.print(": #");
1393         pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount());
1394         pw.print("x "); pw.println(work.getIntent());
1395         if (work.getGrants() != null) {
1396             pw.print(prefix); pw.println("  URI grants:");
1397             ((GrantedUriPermissions)work.getGrants()).dump(pw, prefix + "    ");
1398         }
1399     }
1400 
dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work)1401     private void dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work) {
1402         final long token = proto.start(fieldId);
1403 
1404         proto.write(JobStatusDumpProto.JobWorkItem.WORK_ID, work.getWorkId());
1405         proto.write(JobStatusDumpProto.JobWorkItem.DELIVERY_COUNT, work.getDeliveryCount());
1406         if (work.getIntent() != null) {
1407             work.getIntent().writeToProto(proto, JobStatusDumpProto.JobWorkItem.INTENT);
1408         }
1409         Object grants = work.getGrants();
1410         if (grants != null) {
1411             ((GrantedUriPermissions) grants).dump(proto, JobStatusDumpProto.JobWorkItem.URI_GRANTS);
1412         }
1413 
1414         proto.end(token);
1415     }
1416 
1417     /**
1418      * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
1419      */
getBucketName()1420     String getBucketName() {
1421         return bucketName(standbyBucket);
1422     }
1423 
1424     /**
1425      * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
1426      */
bucketName(int standbyBucket)1427     static String bucketName(int standbyBucket) {
1428         switch (standbyBucket) {
1429             case 0: return "ACTIVE";
1430             case 1: return "WORKING_SET";
1431             case 2: return "FREQUENT";
1432             case 3: return "RARE";
1433             case 4: return "NEVER";
1434             default:
1435                 return "Unknown: " + standbyBucket;
1436         }
1437     }
1438 
resolveTargetSdkVersion(JobInfo job)1439     private static int resolveTargetSdkVersion(JobInfo job) {
1440         return LocalServices.getService(PackageManagerInternal.class)
1441                 .getPackageTargetSdkVersion(job.getService().getPackageName());
1442     }
1443 
1444     // Dumpsys infrastructure
dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis)1445     public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) {
1446         pw.print(prefix); UserHandle.formatUid(pw, callingUid);
1447         pw.print(" tag="); pw.println(tag);
1448         pw.print(prefix);
1449         pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
1450         pw.print(" user="); pw.print(getSourceUserId());
1451         pw.print(" pkg="); pw.println(getSourcePackageName());
1452         if (full) {
1453             pw.print(prefix); pw.println("JobInfo:");
1454             pw.print(prefix); pw.print("  Service: ");
1455             pw.println(job.getService().flattenToShortString());
1456             if (job.isPeriodic()) {
1457                 pw.print(prefix); pw.print("  PERIODIC: interval=");
1458                 TimeUtils.formatDuration(job.getIntervalMillis(), pw);
1459                 pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw);
1460                 pw.println();
1461             }
1462             if (job.isPersisted()) {
1463                 pw.print(prefix); pw.println("  PERSISTED");
1464             }
1465             if (job.getPriority() != 0) {
1466                 pw.print(prefix); pw.print("  Priority: ");
1467                 pw.println(JobInfo.getPriorityString(job.getPriority()));
1468             }
1469             if (job.getFlags() != 0) {
1470                 pw.print(prefix); pw.print("  Flags: ");
1471                 pw.println(Integer.toHexString(job.getFlags()));
1472             }
1473             if (getInternalFlags() != 0) {
1474                 pw.print(prefix); pw.print("  Internal flags: ");
1475                 pw.print(Integer.toHexString(getInternalFlags()));
1476 
1477                 if ((getInternalFlags()&INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
1478                     pw.print(" HAS_FOREGROUND_EXEMPTION");
1479                 }
1480                 pw.println();
1481             }
1482             pw.print(prefix); pw.print("  Requires: charging=");
1483             pw.print(job.isRequireCharging()); pw.print(" batteryNotLow=");
1484             pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle=");
1485             pw.println(job.isRequireDeviceIdle());
1486             if (job.getTriggerContentUris() != null) {
1487                 pw.print(prefix); pw.println("  Trigger content URIs:");
1488                 for (int i = 0; i < job.getTriggerContentUris().length; i++) {
1489                     JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
1490                     pw.print(prefix); pw.print("    ");
1491                     pw.print(Integer.toHexString(trig.getFlags()));
1492                     pw.print(' '); pw.println(trig.getUri());
1493                 }
1494                 if (job.getTriggerContentUpdateDelay() >= 0) {
1495                     pw.print(prefix); pw.print("  Trigger update delay: ");
1496                     TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw);
1497                     pw.println();
1498                 }
1499                 if (job.getTriggerContentMaxDelay() >= 0) {
1500                     pw.print(prefix); pw.print("  Trigger max delay: ");
1501                     TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw);
1502                     pw.println();
1503                 }
1504             }
1505             if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
1506                 pw.print(prefix); pw.print("  Extras: ");
1507                 pw.println(job.getExtras().toShortString());
1508             }
1509             if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
1510                 pw.print(prefix); pw.print("  Transient extras: ");
1511                 pw.println(job.getTransientExtras().toShortString());
1512             }
1513             if (job.getClipData() != null) {
1514                 pw.print(prefix); pw.print("  Clip data: ");
1515                 StringBuilder b = new StringBuilder(128);
1516                 job.getClipData().toShortString(b);
1517                 pw.println(b);
1518             }
1519             if (uriPerms != null) {
1520                 pw.print(prefix); pw.println("  Granted URI permissions:");
1521                 uriPerms.dump(pw, prefix + "  ");
1522             }
1523             if (job.getRequiredNetwork() != null) {
1524                 pw.print(prefix); pw.print("  Network type: ");
1525                 pw.println(job.getRequiredNetwork());
1526             }
1527             if (totalNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1528                 pw.print(prefix); pw.print("  Network bytes: ");
1529                 pw.println(totalNetworkBytes);
1530             }
1531             if (job.getMinLatencyMillis() != 0) {
1532                 pw.print(prefix); pw.print("  Minimum latency: ");
1533                 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
1534                 pw.println();
1535             }
1536             if (job.getMaxExecutionDelayMillis() != 0) {
1537                 pw.print(prefix); pw.print("  Max execution delay: ");
1538                 TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
1539                 pw.println();
1540             }
1541             pw.print(prefix); pw.print("  Backoff: policy="); pw.print(job.getBackoffPolicy());
1542             pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
1543             pw.println();
1544             if (job.hasEarlyConstraint()) {
1545                 pw.print(prefix); pw.println("  Has early constraint");
1546             }
1547             if (job.hasLateConstraint()) {
1548                 pw.print(prefix); pw.println("  Has late constraint");
1549             }
1550         }
1551         pw.print(prefix); pw.print("Required constraints:");
1552         dumpConstraints(pw, requiredConstraints);
1553         pw.println();
1554         if (full) {
1555             pw.print(prefix); pw.print("Satisfied constraints:");
1556             dumpConstraints(pw, satisfiedConstraints);
1557             pw.println();
1558             pw.print(prefix); pw.print("Unsatisfied constraints:");
1559             dumpConstraints(pw,
1560                     ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
1561             pw.println();
1562             if (dozeWhitelisted) {
1563                 pw.print(prefix); pw.println("Doze whitelisted: true");
1564             }
1565             if (uidActive) {
1566                 pw.print(prefix); pw.println("Uid: active");
1567             }
1568             if (job.isExemptedFromAppStandby()) {
1569                 pw.print(prefix); pw.println("Is exempted from app standby");
1570             }
1571         }
1572         if (trackingControllers != 0) {
1573             pw.print(prefix); pw.print("Tracking:");
1574             if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY");
1575             if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY");
1576             if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT");
1577             if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE");
1578             if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE");
1579             if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME");
1580             if ((trackingControllers & TRACKING_QUOTA) != 0) pw.print(" QUOTA");
1581             pw.println();
1582         }
1583 
1584         pw.print(prefix); pw.println("Implicit constraints:");
1585         pw.print(prefix); pw.print("  readyNotDozing: ");
1586         pw.println(mReadyNotDozing);
1587         pw.print(prefix); pw.print("  readyNotRestrictedInBg: ");
1588         pw.println(mReadyNotRestrictedInBg);
1589         if (!job.isPeriodic() && hasDeadlineConstraint()) {
1590             pw.print(prefix); pw.print("  readyDeadlineSatisfied: ");
1591             pw.println(mReadyDeadlineSatisfied);
1592         }
1593 
1594         if (changedAuthorities != null) {
1595             pw.print(prefix); pw.println("Changed authorities:");
1596             for (int i=0; i<changedAuthorities.size(); i++) {
1597                 pw.print(prefix); pw.print("  "); pw.println(changedAuthorities.valueAt(i));
1598             }
1599             if (changedUris != null) {
1600                 pw.print(prefix); pw.println("Changed URIs:");
1601                 for (int i=0; i<changedUris.size(); i++) {
1602                     pw.print(prefix); pw.print("  "); pw.println(changedUris.valueAt(i));
1603                 }
1604             }
1605         }
1606         if (network != null) {
1607             pw.print(prefix); pw.print("Network: "); pw.println(network);
1608         }
1609         if (pendingWork != null && pendingWork.size() > 0) {
1610             pw.print(prefix); pw.println("Pending work:");
1611             for (int i = 0; i < pendingWork.size(); i++) {
1612                 dumpJobWorkItem(pw, prefix, pendingWork.get(i), i);
1613             }
1614         }
1615         if (executingWork != null && executingWork.size() > 0) {
1616             pw.print(prefix); pw.println("Executing work:");
1617             for (int i = 0; i < executingWork.size(); i++) {
1618                 dumpJobWorkItem(pw, prefix, executingWork.get(i), i);
1619             }
1620         }
1621         pw.print(prefix); pw.print("Standby bucket: ");
1622         pw.println(getBucketName());
1623         if (standbyBucket > 0) {
1624             pw.print(prefix); pw.print("Base heartbeat: ");
1625             pw.println(baseHeartbeat);
1626         }
1627         if (whenStandbyDeferred != 0) {
1628             pw.print(prefix); pw.print("  Deferred since: ");
1629             TimeUtils.formatDuration(whenStandbyDeferred, elapsedRealtimeMillis, pw);
1630             pw.println();
1631         }
1632         pw.print(prefix); pw.print("Enqueue time: ");
1633         TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw);
1634         pw.println();
1635         pw.print(prefix); pw.print("Run time: earliest=");
1636         formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis);
1637         pw.print(", latest=");
1638         formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis);
1639         pw.print(", original latest=");
1640         formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis,
1641                 NO_LATEST_RUNTIME, elapsedRealtimeMillis);
1642         pw.println();
1643         if (numFailures != 0) {
1644             pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures);
1645         }
1646         if (mLastSuccessfulRunTime != 0) {
1647             pw.print(prefix); pw.print("Last successful run: ");
1648             pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastSuccessfulRunTime));
1649         }
1650         if (mLastFailedRunTime != 0) {
1651             pw.print(prefix); pw.print("Last failed run: ");
1652             pw.println(TimeMigrationUtils.formatMillisWithFixedFormat(mLastFailedRunTime));
1653         }
1654     }
1655 
dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis)1656     public void dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis) {
1657         final long token = proto.start(fieldId);
1658 
1659         proto.write(JobStatusDumpProto.CALLING_UID, callingUid);
1660         proto.write(JobStatusDumpProto.TAG, tag);
1661         proto.write(JobStatusDumpProto.SOURCE_UID, getSourceUid());
1662         proto.write(JobStatusDumpProto.SOURCE_USER_ID, getSourceUserId());
1663         proto.write(JobStatusDumpProto.SOURCE_PACKAGE_NAME, getSourcePackageName());
1664         proto.write(JobStatusDumpProto.INTERNAL_FLAGS, getInternalFlags());
1665 
1666         if (full) {
1667             final long jiToken = proto.start(JobStatusDumpProto.JOB_INFO);
1668 
1669             job.getService().writeToProto(proto, JobStatusDumpProto.JobInfo.SERVICE);
1670 
1671             proto.write(JobStatusDumpProto.JobInfo.IS_PERIODIC, job.isPeriodic());
1672             proto.write(JobStatusDumpProto.JobInfo.PERIOD_INTERVAL_MS, job.getIntervalMillis());
1673             proto.write(JobStatusDumpProto.JobInfo.PERIOD_FLEX_MS, job.getFlexMillis());
1674 
1675             proto.write(JobStatusDumpProto.JobInfo.IS_PERSISTED, job.isPersisted());
1676             proto.write(JobStatusDumpProto.JobInfo.PRIORITY, job.getPriority());
1677             proto.write(JobStatusDumpProto.JobInfo.FLAGS, job.getFlags());
1678 
1679             proto.write(JobStatusDumpProto.JobInfo.REQUIRES_CHARGING, job.isRequireCharging());
1680             proto.write(JobStatusDumpProto.JobInfo.REQUIRES_BATTERY_NOT_LOW, job.isRequireBatteryNotLow());
1681             proto.write(JobStatusDumpProto.JobInfo.REQUIRES_DEVICE_IDLE, job.isRequireDeviceIdle());
1682 
1683             if (job.getTriggerContentUris() != null) {
1684                 for (int i = 0; i < job.getTriggerContentUris().length; i++) {
1685                     final long tcuToken = proto.start(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_URIS);
1686                     JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
1687 
1688                     proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.FLAGS, trig.getFlags());
1689                     Uri u = trig.getUri();
1690                     if (u != null) {
1691                         proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.URI, u.toString());
1692                     }
1693 
1694                     proto.end(tcuToken);
1695                 }
1696                 if (job.getTriggerContentUpdateDelay() >= 0) {
1697                     proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_UPDATE_DELAY_MS,
1698                             job.getTriggerContentUpdateDelay());
1699                 }
1700                 if (job.getTriggerContentMaxDelay() >= 0) {
1701                     proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_MAX_DELAY_MS,
1702                             job.getTriggerContentMaxDelay());
1703                 }
1704             }
1705             if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
1706                 job.getExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.EXTRAS);
1707             }
1708             if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
1709                 job.getTransientExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS);
1710             }
1711             if (job.getClipData() != null) {
1712                 job.getClipData().writeToProto(proto, JobStatusDumpProto.JobInfo.CLIP_DATA);
1713             }
1714             if (uriPerms != null) {
1715                 uriPerms.dump(proto, JobStatusDumpProto.JobInfo.GRANTED_URI_PERMISSIONS);
1716             }
1717             if (job.getRequiredNetwork() != null) {
1718                 job.getRequiredNetwork().writeToProto(proto, JobStatusDumpProto.JobInfo.REQUIRED_NETWORK);
1719             }
1720             if (totalNetworkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1721                 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_BYTES, totalNetworkBytes);
1722             }
1723             proto.write(JobStatusDumpProto.JobInfo.MIN_LATENCY_MS, job.getMinLatencyMillis());
1724             proto.write(JobStatusDumpProto.JobInfo.MAX_EXECUTION_DELAY_MS, job.getMaxExecutionDelayMillis());
1725 
1726             final long bpToken = proto.start(JobStatusDumpProto.JobInfo.BACKOFF_POLICY);
1727             proto.write(JobStatusDumpProto.JobInfo.Backoff.POLICY, job.getBackoffPolicy());
1728             proto.write(JobStatusDumpProto.JobInfo.Backoff.INITIAL_BACKOFF_MS,
1729                     job.getInitialBackoffMillis());
1730             proto.end(bpToken);
1731 
1732             proto.write(JobStatusDumpProto.JobInfo.HAS_EARLY_CONSTRAINT, job.hasEarlyConstraint());
1733             proto.write(JobStatusDumpProto.JobInfo.HAS_LATE_CONSTRAINT, job.hasLateConstraint());
1734 
1735             proto.end(jiToken);
1736         }
1737 
1738         dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints);
1739         if (full) {
1740             dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
1741             dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
1742                     ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
1743             proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, dozeWhitelisted);
1744             proto.write(JobStatusDumpProto.IS_UID_ACTIVE, uidActive);
1745             proto.write(JobStatusDumpProto.IS_EXEMPTED_FROM_APP_STANDBY,
1746                     job.isExemptedFromAppStandby());
1747         }
1748 
1749         // Tracking controllers
1750         if ((trackingControllers&TRACKING_BATTERY) != 0) {
1751             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
1752                     JobStatusDumpProto.TRACKING_BATTERY);
1753         }
1754         if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) {
1755             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
1756                     JobStatusDumpProto.TRACKING_CONNECTIVITY);
1757         }
1758         if ((trackingControllers&TRACKING_CONTENT) != 0) {
1759             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
1760                     JobStatusDumpProto.TRACKING_CONTENT);
1761         }
1762         if ((trackingControllers&TRACKING_IDLE) != 0) {
1763             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
1764                     JobStatusDumpProto.TRACKING_IDLE);
1765         }
1766         if ((trackingControllers&TRACKING_STORAGE) != 0) {
1767             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
1768                     JobStatusDumpProto.TRACKING_STORAGE);
1769         }
1770         if ((trackingControllers&TRACKING_TIME) != 0) {
1771             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
1772                     JobStatusDumpProto.TRACKING_TIME);
1773         }
1774         if ((trackingControllers & TRACKING_QUOTA) != 0) {
1775             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
1776                     JobStatusDumpProto.TRACKING_QUOTA);
1777         }
1778 
1779         // Implicit constraints
1780         final long icToken = proto.start(JobStatusDumpProto.IMPLICIT_CONSTRAINTS);
1781         proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_DOZING, mReadyNotDozing);
1782         proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_RESTRICTED_IN_BG,
1783                 mReadyNotRestrictedInBg);
1784         proto.end(icToken);
1785 
1786         if (changedAuthorities != null) {
1787             for (int k = 0; k < changedAuthorities.size(); k++) {
1788                 proto.write(JobStatusDumpProto.CHANGED_AUTHORITIES, changedAuthorities.valueAt(k));
1789             }
1790         }
1791         if (changedUris != null) {
1792             for (int i = 0; i < changedUris.size(); i++) {
1793                 Uri u = changedUris.valueAt(i);
1794                 proto.write(JobStatusDumpProto.CHANGED_URIS, u.toString());
1795             }
1796         }
1797 
1798         if (network != null) {
1799             network.writeToProto(proto, JobStatusDumpProto.NETWORK);
1800         }
1801 
1802         if (pendingWork != null && pendingWork.size() > 0) {
1803             for (int i = 0; i < pendingWork.size(); i++) {
1804                 dumpJobWorkItem(proto, JobStatusDumpProto.PENDING_WORK, pendingWork.get(i));
1805             }
1806         }
1807         if (executingWork != null && executingWork.size() > 0) {
1808             for (int i = 0; i < executingWork.size(); i++) {
1809                 dumpJobWorkItem(proto, JobStatusDumpProto.EXECUTING_WORK, executingWork.get(i));
1810             }
1811         }
1812 
1813         proto.write(JobStatusDumpProto.STANDBY_BUCKET, standbyBucket);
1814         proto.write(JobStatusDumpProto.ENQUEUE_DURATION_MS, elapsedRealtimeMillis - enqueueTime);
1815         if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) {
1816             proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 0);
1817         } else {
1818             proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS,
1819                     earliestRunTimeElapsedMillis - elapsedRealtimeMillis);
1820         }
1821         if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) {
1822             proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS, 0);
1823         } else {
1824             proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS,
1825                     latestRunTimeElapsedMillis - elapsedRealtimeMillis);
1826         }
1827 
1828         proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures);
1829         proto.write(JobStatusDumpProto.LAST_SUCCESSFUL_RUN_TIME, mLastSuccessfulRunTime);
1830         proto.write(JobStatusDumpProto.LAST_FAILED_RUN_TIME, mLastFailedRunTime);
1831 
1832         proto.end(token);
1833     }
1834 }
1835