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