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