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 android.text.format.DateUtils.HOUR_IN_MILLIS;
20 
21 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
22 import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX;
23 import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
24 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
25 import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
26 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
27 
28 import android.annotation.ElapsedRealtimeLong;
29 import android.annotation.NonNull;
30 import android.annotation.Nullable;
31 import android.app.AppGlobals;
32 import android.app.job.JobInfo;
33 import android.app.job.JobParameters;
34 import android.app.job.JobScheduler;
35 import android.app.job.JobWorkItem;
36 import android.app.job.UserVisibleJobSummary;
37 import android.content.ClipData;
38 import android.content.ComponentName;
39 import android.net.Network;
40 import android.net.NetworkRequest;
41 import android.net.Uri;
42 import android.os.RemoteException;
43 import android.os.UserHandle;
44 import android.provider.MediaStore;
45 import android.text.format.DateFormat;
46 import android.util.ArrayMap;
47 import android.util.ArraySet;
48 import android.util.IndentingPrintWriter;
49 import android.util.Pair;
50 import android.util.Range;
51 import android.util.Slog;
52 import android.util.TimeUtils;
53 import android.util.proto.ProtoOutputStream;
54 
55 import com.android.internal.annotations.GuardedBy;
56 import com.android.internal.annotations.VisibleForTesting;
57 import com.android.internal.util.ArrayUtils;
58 import com.android.internal.util.FrameworkStatsLog;
59 import com.android.modules.expresslog.Counter;
60 import com.android.server.LocalServices;
61 import com.android.server.job.GrantedUriPermissions;
62 import com.android.server.job.JobSchedulerInternal;
63 import com.android.server.job.JobSchedulerService;
64 import com.android.server.job.JobServerProtoEnums;
65 import com.android.server.job.JobStatusDumpProto;
66 import com.android.server.job.JobStatusShortInfoProto;
67 
68 import dalvik.annotation.optimization.NeverCompile;
69 
70 import java.io.PrintWriter;
71 import java.security.MessageDigest;
72 import java.util.ArrayList;
73 import java.util.Arrays;
74 import java.util.Collections;
75 import java.util.Objects;
76 import java.util.Random;
77 import java.util.function.Predicate;
78 
79 /**
80  * Uniquely identifies a job internally.
81  * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler.
82  * Contains current state of the requirements of the job, as well as a function to evaluate
83  * whether it's ready to run.
84  * This object is shared among the various controllers - hence why the different fields are atomic.
85  * This isn't strictly necessary because each controller is only interested in a specific field,
86  * and the receivers that are listening for global state change will all run on the main looper,
87  * but we don't enforce that so this is safer.
88  *
89  * Test: atest com.android.server.job.controllers.JobStatusTest
90  * @hide
91  */
92 public final class JobStatus {
93     private static final String TAG = "JobScheduler.JobStatus";
94     static final boolean DEBUG = JobSchedulerService.DEBUG;
95 
96     private static MessageDigest sMessageDigest;
97     /** Cache of namespace to hash to reduce how often we need to generate the namespace hash. */
98     @GuardedBy("sNamespaceHashCache")
99     private static final ArrayMap<String, String> sNamespaceHashCache = new ArrayMap<>();
100     /** Maximum size of {@link #sNamespaceHashCache}. */
101     private static final int MAX_NAMESPACE_CACHE_SIZE = 128;
102 
103     private static final int NUM_CONSTRAINT_CHANGE_HISTORY = 10;
104 
105     public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
106     public static final long NO_EARLIEST_RUNTIME = 0L;
107 
108     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
109     public static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING; // 1 < 0
110     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
111     public static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;  // 1 << 2
112     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
113     public static final int CONSTRAINT_BATTERY_NOT_LOW =
114             JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW; // 1 << 1
115     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
116     public static final int CONSTRAINT_STORAGE_NOT_LOW =
117             JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW; // 1 << 3
118     public static final int CONSTRAINT_TIMING_DELAY = 1 << 31;
119     public static final int CONSTRAINT_DEADLINE = 1 << 30;
120     public static final int CONSTRAINT_CONNECTIVITY = 1 << 28;
121     static final int CONSTRAINT_TARE_WEALTH = 1 << 27; // Implicit constraint
122     public static final int CONSTRAINT_CONTENT_TRIGGER = 1 << 26;
123     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1 << 25; // Implicit constraint
124     static final int CONSTRAINT_WITHIN_QUOTA = 1 << 24;      // Implicit constraint
125     static final int CONSTRAINT_PREFETCH = 1 << 23;
126     static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
127     static final int CONSTRAINT_FLEXIBLE = 1 << 21;
128 
129     private static final int IMPLICIT_CONSTRAINTS = 0
130             | CONSTRAINT_BACKGROUND_NOT_RESTRICTED
131             | CONSTRAINT_DEVICE_NOT_DOZING
132             | CONSTRAINT_TARE_WEALTH
133             | CONSTRAINT_WITHIN_QUOTA;
134 
135     // The following set of dynamic constraints are for specific use cases (as explained in their
136     // relative naming and comments). Right now, they apply different constraints, which is fine,
137     // but if in the future, we have overlapping dynamic constraint sets, removing one constraint
138     // set may accidentally remove a constraint applied by another dynamic set.
139     // TODO: properly handle overlapping dynamic constraint sets
140 
141     /**
142      * The additional set of dynamic constraints that must be met if the job's effective bucket is
143      * {@link JobSchedulerService#RESTRICTED_INDEX}. Connectivity can be ignored if the job doesn't
144      * need network.
145      */
146     private static final int DYNAMIC_RESTRICTED_CONSTRAINTS =
147             CONSTRAINT_BATTERY_NOT_LOW
148                     | CONSTRAINT_CHARGING
149                     | CONSTRAINT_CONNECTIVITY
150                     | CONSTRAINT_IDLE;
151 
152     /**
153      * Keeps track of how many flexible constraints must be satisfied for the job to execute.
154      */
155     private final int mNumRequiredFlexibleConstraints;
156 
157     /**
158      * Number of required flexible constraints that have been dropped.
159      */
160     private int mNumDroppedFlexibleConstraints;
161 
162     /** If the job is going to be passed an unmetered network. */
163     private boolean mHasAccessToUnmetered;
164 
165     /** If the effective bucket has been downgraded once due to being buggy. */
166     private boolean mIsDowngradedDueToBuggyApp;
167 
168     /**
169      * The additional set of dynamic constraints that must be met if this is an expedited job that
170      * had a long enough run while the device was Dozing or in battery saver.
171      */
172     private static final int DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS =
173             CONSTRAINT_DEVICE_NOT_DOZING | CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
174 
175     /**
176      * Standard media URIs that contain the media files that might be important to the user.
177      * @see #mHasMediaBackupExemption
178      */
179     private static final Uri[] MEDIA_URIS_FOR_STANDBY_EXEMPTION = {
180             MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
181             MediaStore.Video.Media.EXTERNAL_CONTENT_URI,
182     };
183 
184     /**
185      * The constraints that we want to log to statsd.
186      *
187      * Constraints that can be inferred from other atoms have been excluded to avoid logging too
188      * much information and to reduce redundancy:
189      *
190      * * CONSTRAINT_CHARGING can be inferred with PluggedStateChanged (Atom #32)
191      * * CONSTRAINT_BATTERY_NOT_LOW can be inferred with BatteryLevelChanged (Atom #30)
192      * * CONSTRAINT_CONNECTIVITY can be partially inferred with ConnectivityStateChanged
193      * (Atom #98) and BatterySaverModeStateChanged (Atom #20).
194      * * CONSTRAINT_DEVICE_NOT_DOZING can be mostly inferred with DeviceIdleModeStateChanged
195      * (Atom #21)
196      * * CONSTRAINT_BACKGROUND_NOT_RESTRICTED can be inferred with BatterySaverModeStateChanged
197      * (Atom #20)
198      * * CONSTRAINT_STORAGE_NOT_LOW can be inferred with LowStorageStateChanged (Atom #130)
199      */
200     private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
201             | CONSTRAINT_DEADLINE
202             | CONSTRAINT_PREFETCH
203             | CONSTRAINT_TARE_WEALTH
204             | CONSTRAINT_TIMING_DELAY
205             | CONSTRAINT_WITHIN_QUOTA;
206 
207     // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot
208     private static final boolean STATS_LOG_ENABLED = false;
209 
210     // No override.
211     public static final int OVERRIDE_NONE = 0;
212     // Override to improve sorting order. Does not affect constraint evaluation.
213     public static final int OVERRIDE_SORTING = 1;
214     // Soft override: ignore constraints like time that don't affect API availability
215     public static final int OVERRIDE_SOFT = 2;
216     // Full override: ignore all constraints including API-affecting like connectivity
217     public static final int OVERRIDE_FULL = 3;
218 
219     /** If not specified, trigger update delay is 10 seconds. */
220     public static final long DEFAULT_TRIGGER_UPDATE_DELAY = 10*1000;
221 
222     /** The minimum possible update delay is 1/2 second. */
223     public static final long MIN_TRIGGER_UPDATE_DELAY = 500;
224 
225     /** If not specified, trigger maximum delay is 2 minutes. */
226     public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000;
227 
228     /** The minimum possible update delay is 1 second. */
229     public static final long MIN_TRIGGER_MAX_DELAY = 1000;
230 
231     final JobInfo job;
232     /**
233      * Uid of the package requesting this job.  This can differ from the "source"
234      * uid when the job was scheduled on the app's behalf, such as with the jobs
235      * that underly Sync Manager operation.
236      */
237     final int callingUid;
238     final String batteryName;
239 
240     /**
241      * Identity of the app in which the job is hosted.
242      */
243     final String sourcePackageName;
244     final int sourceUserId;
245     final int sourceUid;
246     final String sourceTag;
247     @Nullable
248     private final String mNamespace;
249     @Nullable
250     private final String mNamespaceHash;
251     /** An ID that can be used to uniquely identify the job when logging statsd metrics. */
252     private final long mLoggingJobId;
253 
254     final String tag;
255 
256     private GrantedUriPermissions uriPerms;
257     private boolean prepared;
258 
259     static final boolean DEBUG_PREPARE = true;
260     private Throwable unpreparedPoint = null;
261 
262     /**
263      * Earliest point in the future at which this job will be eligible to run. A value of 0
264      * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
265      */
266     private final long earliestRunTimeElapsedMillis;
267     /**
268      * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
269      * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
270      */
271     private final long latestRunTimeElapsedMillis;
272 
273     /**
274      * Valid only for periodic jobs. The original latest point in the future at which this
275      * job was expected to run.
276      */
277     private long mOriginalLatestRunTimeElapsedMillis;
278 
279     /**
280      * How many times this job has failed to complete on its own
281      * (via {@link android.app.job.JobService#jobFinished(JobParameters, boolean)} or because of
282      * a timeout).
283      * This count doesn't include most times JobScheduler decided to stop the job
284      * (via {@link android.app.job.JobService#onStopJob(JobParameters)}.
285      */
286     private final int numFailures;
287 
288     /**
289      * The number of times JobScheduler has forced this job to stop due to reasons mostly outside
290      * of the app's control.
291      */
292     private final int mNumSystemStops;
293 
294     /**
295      * Which app standby bucket this job's app is in.  Updated when the app is moved to a
296      * different bucket.
297      */
298     private int standbyBucket;
299 
300     /**
301      * Whether we've logged an error due to standby bucket mismatch with active uid state.
302      */
303     private boolean mLoggedBucketMismatch;
304 
305     /**
306      * Debugging: timestamp if we ever defer this job based on standby bucketing, this
307      * is when we did so.
308      */
309     private long whenStandbyDeferred;
310 
311     /** The first time this job was force batched. */
312     private long mFirstForceBatchedTimeElapsed;
313 
314     // Constraints.
315     final int requiredConstraints;
316     private final int mPreferredConstraints;
317     private final int mRequiredConstraintsOfInterest;
318     int satisfiedConstraints = 0;
319     private int mSatisfiedConstraintsOfInterest = 0;
320     /**
321      * Set of constraints that must be satisfied for the job if/because it's in the RESTRICTED
322      * bucket.
323      */
324     private int mDynamicConstraints = 0;
325 
326     /**
327      * Indicates whether the job is responsible for backing up media, so we can be lenient in
328      * applying standby throttling.
329      *
330      * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or
331      * network changes, in which case this exemption does not make sense.
332      */
333     private boolean mHasMediaBackupExemption;
334     private final boolean mHasExemptedMediaUrisOnly;
335 
336     // Set to true if doze constraint was satisfied due to app being whitelisted.
337     boolean appHasDozeExemption;
338 
339     // Set to true when the app is "active" per AppStateTracker
340     public boolean uidActive;
341 
342     /**
343      * Flag for {@link #trackingControllers}: the battery controller is currently tracking this job.
344      */
345     public static final int TRACKING_BATTERY = 1<<0;
346     /**
347      * Flag for {@link #trackingControllers}: the network connectivity controller is currently
348      * tracking this job.
349      */
350     public static final int TRACKING_CONNECTIVITY = 1<<1;
351     /**
352      * Flag for {@link #trackingControllers}: the content observer controller is currently
353      * tracking this job.
354      */
355     public static final int TRACKING_CONTENT = 1<<2;
356     /**
357      * Flag for {@link #trackingControllers}: the idle controller is currently tracking this job.
358      */
359     public static final int TRACKING_IDLE = 1<<3;
360     /**
361      * Flag for {@link #trackingControllers}: the storage controller is currently tracking this job.
362      */
363     public static final int TRACKING_STORAGE = 1<<4;
364     /**
365      * Flag for {@link #trackingControllers}: the time controller is currently tracking this job.
366      */
367     public static final int TRACKING_TIME = 1<<5;
368     /**
369      * Flag for {@link #trackingControllers}: the quota controller is currently tracking this job.
370      */
371     public static final int TRACKING_QUOTA = 1 << 6;
372 
373     /**
374      * Flag for {@link #trackingControllers}: the flexibility controller is currently tracking this
375      * job.
376      */
377     public static final int TRACKING_FLEXIBILITY = 1 << 7;
378 
379     /**
380      * Bit mask of controllers that are currently tracking the job.
381      */
382     private int trackingControllers;
383 
384     /**
385      * Flag for {@link #mInternalFlags}: this job was scheduled when the app that owns the job
386      * service (not necessarily the caller) was in the foreground and the job has no time
387      * constraints, which makes it exempted from the battery saver job restriction.
388      *
389      * @hide
390      */
391     public static final int INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION = 1 << 0;
392     /**
393      * Flag for {@link #mInternalFlags}: this job was stopped by the user for some reason
394      * and is thus considered demoted from whatever privileged state it had in the past.
395      */
396     public static final int INTERNAL_FLAG_DEMOTED_BY_USER = 1 << 1;
397     /**
398      * Flag for {@link #mInternalFlags}: this job is demoted by the system
399      * from running as a user-initiated job.
400      */
401     public static final int INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ = 1 << 2;
402 
403     /** Minimum difference between start and end time to have flexible constraint */
404     @VisibleForTesting
405     static final long MIN_WINDOW_FOR_FLEXIBILITY_MS = HOUR_IN_MILLIS;
406     /**
407      * Versatile, persistable flags for a job that's updated within the system server,
408      * as opposed to {@link JobInfo#flags} that's set by callers.
409      */
410     private int mInternalFlags;
411 
412     /**
413      * The cumulative amount of time this job has run for, including previous executions.
414      * This is reset for periodic jobs upon a successful job execution.
415      */
416     private long mCumulativeExecutionTimeMs;
417 
418     // These are filled in by controllers when preparing for execution.
419     public ArraySet<Uri> changedUris;
420     public ArraySet<String> changedAuthorities;
421     public Network network;
422     public String serviceProcessName;
423 
424     /** The evaluated bias of the job when it started running. */
425     public int lastEvaluatedBias;
426 
427     /**
428      * Whether or not this particular JobStatus instance was treated as an EJ when it started
429      * running. This isn't copied over when a job is rescheduled.
430      */
431     public boolean startedAsExpeditedJob = false;
432     /**
433      * Whether or not this particular JobStatus instance was treated as a user-initiated job
434      * when it started running. This isn't copied over when a job is rescheduled.
435      */
436     public boolean startedAsUserInitiatedJob = false;
437     /**
438      * Whether this particular JobStatus instance started with the foreground flag
439      * (or more accurately, did <b>not</b> have the
440      * {@link android.content.Context#BIND_NOT_FOREGROUND} flag
441      * included in its binding flags when started).
442      */
443     public boolean startedWithForegroundFlag = false;
444 
445     public boolean startedWithImmediacyPrivilege = false;
446 
447     // If non-null, this is work that has been enqueued for the job.
448     public ArrayList<JobWorkItem> pendingWork;
449 
450     // If non-null, this is work that is currently being executed.
451     public ArrayList<JobWorkItem> executingWork;
452 
453     public int nextPendingWorkId = 1;
454 
455     // Used by shell commands
456     public int overrideState = JobStatus.OVERRIDE_NONE;
457 
458     // When this job was enqueued, for ordering.  (in elapsedRealtimeMillis)
459     @ElapsedRealtimeLong
460     public long enqueueTime;
461 
462     // Metrics about queue latency.  (in uptimeMillis)
463     public long madePending;
464     public long madeActive;
465 
466     /**
467      * Last time a job finished successfully for a periodic job, in the currentTimeMillis time,
468      * for dumpsys.
469      */
470     private long mLastSuccessfulRunTime;
471 
472     /**
473      * Last time a job finished unsuccessfully, in the currentTimeMillis time, for dumpsys.
474      */
475     private long mLastFailedRunTime;
476 
477     /** Whether or not the app is background restricted by the user (FAS). */
478     private boolean mIsUserBgRestricted;
479 
480     /**
481      * Transient: when a job is inflated from disk before we have a reliable RTC clock time,
482      * we retain the canonical (delay, deadline) scheduling tuple read out of the persistent
483      * store in UTC so that we can fix up the job's scheduling criteria once we get a good
484      * wall-clock time.  If we have to persist the job again before the clock has been updated,
485      * we record these times again rather than calculating based on the earliest/latest elapsed
486      * time base figures.
487      *
488      * 'first' is the earliest/delay time, and 'second' is the latest/deadline time.
489      */
490     private Pair<Long, Long> mPersistedUtcTimes;
491 
492     private int mConstraintChangeHistoryIndex = 0;
493     private final long[] mConstraintUpdatedTimesElapsed = new long[NUM_CONSTRAINT_CHANGE_HISTORY];
494     private final int[] mConstraintStatusHistory = new int[NUM_CONSTRAINT_CHANGE_HISTORY];
495 
496     /**
497      * For use only by ContentObserverController: state it is maintaining about content URIs
498      * being observed.
499      */
500     ContentObserverController.JobInstance contentObserverJobInstance;
501 
502     private long mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
503     private long mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
504     private long mMinimumNetworkChunkBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
505 
506     /**
507      * Whether or not this job is approved to be treated as expedited per quota policy.
508      */
509     private boolean mExpeditedQuotaApproved;
510     /**
511      * Whether or not this job is approved to be treated as expedited per TARE policy.
512      */
513     private boolean mExpeditedTareApproved;
514 
515     /**
516      * Summary describing this job. Lazily created in {@link #getUserVisibleJobSummary()}
517      * since not every job will need it.
518      */
519     private UserVisibleJobSummary mUserVisibleJobSummary;
520 
521     /////// Booleans that track if a job is ready to run. They should be updated whenever dependent
522     /////// states change.
523 
524     /**
525      * The deadline for the job has passed. This is only good for non-periodic jobs. A periodic job
526      * should only run if its constraints are satisfied.
527      * Computed as: NOT periodic AND has deadline constraint AND deadline constraint satisfied.
528      */
529     private boolean mReadyDeadlineSatisfied;
530 
531     /**
532      * The device isn't Dozing or this job is exempt from Dozing (eg. it will be in the foreground
533      * or will run as an expedited job). This implicit constraint must be satisfied.
534      */
535     private boolean mReadyNotDozing;
536 
537     /**
538      * The job is not restricted from running in the background (due to Battery Saver). This
539      * implicit constraint must be satisfied.
540      */
541     private boolean mReadyNotRestrictedInBg;
542 
543     /** The job is within its quota based on its standby bucket. */
544     private boolean mReadyWithinQuota;
545 
546     /** The job has enough credits to run based on TARE. */
547     private boolean mReadyTareWealth;
548 
549     /** The job's dynamic requirements have been satisfied. */
550     private boolean mReadyDynamicSatisfied;
551 
552     /**
553      * The job prefers an unmetered network if it has the connectivity constraint but is
554      * okay with any meteredness.
555      */
556     private final boolean mPreferUnmetered;
557 
558     /** The reason a job most recently went from ready to not ready. */
559     private int mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
560 
561     /**
562      * Core constructor for JobStatus instances.  All other ctors funnel down to this one.
563      *
564      * @param job The actual requested parameters for the job
565      * @param callingUid Identity of the app that is scheduling the job.  This may not be the
566      *     app in which the job is implemented; such as with sync jobs.
567      * @param sourcePackageName The package name of the app in which the job will run.
568      * @param sourceUserId The user in which the job will run
569      * @param standbyBucket The standby bucket that the source package is currently assigned to,
570      *     cached here for speed of handling during runnability evaluations (and updated when bucket
571      *     assignments are changed)
572      * @param namespace The custom namespace the app put this job in.
573      * @param tag A string associated with the job for debugging/logging purposes.
574      * @param numFailures Count of how many times this job has requested a reschedule because
575      *     its work was not yet finished.
576      * @param numSystemStops Count of how many times JobScheduler has forced this job to stop due to
577      *     factors mostly out of the app's control.
578      * @param earliestRunTimeElapsedMillis Milestone: earliest point in time at which the job
579      *     is to be considered runnable
580      * @param latestRunTimeElapsedMillis Milestone: point in time at which the job will be
581      *     considered overdue
582      * @param lastSuccessfulRunTime When did we last run this job to completion?
583      * @param lastFailedRunTime When did we last run this job only to have it stop incomplete?
584      * @param internalFlags Non-API property flags about this job
585      */
JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, int standbyBucket, @Nullable String namespace, String tag, int numFailures, int numSystemStops, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, int internalFlags, int dynamicConstraints)586     private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
587             int sourceUserId, int standbyBucket, @Nullable String namespace, String tag,
588             int numFailures, int numSystemStops,
589             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
590             long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs,
591             int internalFlags,
592             int dynamicConstraints) {
593         this.job = job;
594         this.callingUid = callingUid;
595         this.standbyBucket = standbyBucket;
596         mNamespace = namespace;
597         mNamespaceHash = generateNamespaceHash(namespace);
598         mLoggingJobId = generateLoggingId(namespace, job.getId());
599 
600         int tempSourceUid = -1;
601         if (sourceUserId != -1 && sourcePackageName != null) {
602             try {
603                 tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
604                         sourceUserId);
605             } catch (RemoteException ex) {
606                 // Can't happen, PackageManager runs in the same process.
607             }
608         }
609         if (tempSourceUid == -1) {
610             this.sourceUid = callingUid;
611             this.sourceUserId = UserHandle.getUserId(callingUid);
612             this.sourcePackageName = job.getService().getPackageName();
613             this.sourceTag = null;
614         } else {
615             this.sourceUid = tempSourceUid;
616             this.sourceUserId = sourceUserId;
617             this.sourcePackageName = sourcePackageName;
618             this.sourceTag = tag;
619         }
620 
621         final String bnNamespace = namespace == null ? "" :  "@" + namespace + "@";
622         this.batteryName = this.sourceTag != null
623                 ? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName()
624                 : bnNamespace + job.getService().flattenToShortString();
625         this.tag = "*job*/" + this.batteryName + "#" + job.getId();
626 
627         this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
628         this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
629         this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
630         this.numFailures = numFailures;
631         mNumSystemStops = numSystemStops;
632 
633         int requiredConstraints = job.getConstraintFlags();
634         if (job.getRequiredNetwork() != null) {
635             requiredConstraints |= CONSTRAINT_CONNECTIVITY;
636         }
637         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
638             requiredConstraints |= CONSTRAINT_TIMING_DELAY;
639         }
640         if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
641             requiredConstraints |= CONSTRAINT_DEADLINE;
642         }
643         if (job.isPrefetch()) {
644             requiredConstraints |= CONSTRAINT_PREFETCH;
645         }
646         boolean exemptedMediaUrisOnly = false;
647         if (job.getTriggerContentUris() != null) {
648             requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
649             exemptedMediaUrisOnly = true;
650             for (JobInfo.TriggerContentUri uri : job.getTriggerContentUris()) {
651                 if (!ArrayUtils.contains(MEDIA_URIS_FOR_STANDBY_EXEMPTION, uri.getUri())) {
652                     exemptedMediaUrisOnly = false;
653                     break;
654                 }
655             }
656         }
657         mHasExemptedMediaUrisOnly = exemptedMediaUrisOnly;
658 
659         mPreferredConstraints = job.getPreferredConstraintFlags();
660 
661         // Exposing a preferredNetworkRequest API requires that we make sure that the preferred
662         // NetworkRequest is a subset of the required NetworkRequest. We currently don't have the
663         // code to ensure that, so disable this part for now.
664         // TODO(236261941): look into enabling flexible network constraint requests
665         mPreferUnmetered = false;
666                 // && job.getRequiredNetwork() != null
667                 // && !job.getRequiredNetwork().hasCapability(NET_CAPABILITY_NOT_METERED);
668 
669         final boolean satisfiesMinWindowException =
670                 (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis)
671                 >= MIN_WINDOW_FOR_FLEXIBILITY_MS;
672 
673         // The first time a job is rescheduled it will not be subject to flexible constraints.
674         // Otherwise, every consecutive reschedule increases a jobs' flexibility deadline.
675         if (mPreferredConstraints != 0 && !isRequestedExpeditedJob() && !job.isUserInitiated()
676                 && satisfiesMinWindowException
677                 && (numFailures + numSystemStops) != 1) {
678             mNumRequiredFlexibleConstraints = Integer.bitCount(mPreferredConstraints);
679             requiredConstraints |= CONSTRAINT_FLEXIBLE;
680         } else {
681             mNumRequiredFlexibleConstraints = 0;
682         }
683 
684         this.requiredConstraints = requiredConstraints;
685         mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
686         addDynamicConstraints(dynamicConstraints);
687         mReadyNotDozing = canRunInDoze();
688         if (standbyBucket == RESTRICTED_INDEX) {
689             addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
690         } else {
691             mReadyDynamicSatisfied = false;
692         }
693 
694         mCumulativeExecutionTimeMs = cumulativeExecutionTimeMs;
695 
696         mLastSuccessfulRunTime = lastSuccessfulRunTime;
697         mLastFailedRunTime = lastFailedRunTime;
698 
699         mInternalFlags = internalFlags;
700 
701         updateNetworkBytesLocked();
702 
703         if (job.getRequiredNetwork() != null) {
704             // Later, when we check if a given network satisfies the required
705             // network, we need to know the UID that is requesting it, so push
706             // our source UID into place.
707             final JobInfo.Builder builder = new JobInfo.Builder(job);
708             final NetworkRequest.Builder requestBuilder =
709                     new NetworkRequest.Builder(job.getRequiredNetwork());
710             requestBuilder.setUids(
711                     Collections.singleton(new Range<Integer>(this.sourceUid, this.sourceUid)));
712             builder.setRequiredNetwork(requestBuilder.build());
713             // Don't perform validation checks at this point since we've already passed the
714             // initial validation check.
715             job = builder.build(false, false);
716         }
717 
718         updateMediaBackupExemptionStatus();
719     }
720 
721     /** Copy constructor: used specifically when cloning JobStatus objects for persistence,
722      *   so we preserve RTC window bounds if the source object has them. */
JobStatus(JobStatus jobStatus)723     public JobStatus(JobStatus jobStatus) {
724         this(jobStatus.getJob(), jobStatus.getUid(),
725                 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
726                 jobStatus.getStandbyBucket(), jobStatus.getNamespace(),
727                 jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(),
728                 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
729                 jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
730                 jobStatus.getCumulativeExecutionTimeMs(),
731                 jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints);
732         mPersistedUtcTimes = jobStatus.mPersistedUtcTimes;
733         if (jobStatus.mPersistedUtcTimes != null) {
734             if (DEBUG) {
735                 Slog.i(TAG, "Cloning job with persisted run times", new RuntimeException("here"));
736             }
737         }
738         if (jobStatus.executingWork != null && jobStatus.executingWork.size() > 0) {
739             executingWork = new ArrayList<>(jobStatus.executingWork);
740         }
741         if (jobStatus.pendingWork != null && jobStatus.pendingWork.size() > 0) {
742             pendingWork = new ArrayList<>(jobStatus.pendingWork);
743         }
744     }
745 
746     /**
747      * Create a new JobStatus that was loaded from disk. We ignore the provided
748      * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job
749      * from the {@link com.android.server.job.JobStore} and still want to respect its
750      * wallclock runtime rather than resetting it on every boot.
751      * We consider a freshly loaded job to no longer be in back-off, and the associated
752      * standby bucket is whatever the OS thinks it should be at this moment.
753      */
JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId, int standbyBucket, @Nullable String namespace, String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, Pair<Long, Long> persistedExecutionTimesUTC, int innerFlags, int dynamicConstraints)754     public JobStatus(JobInfo job, int callingUid, String sourcePkgName, int sourceUserId,
755             int standbyBucket, @Nullable String namespace, String sourceTag,
756             long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
757             long lastSuccessfulRunTime, long lastFailedRunTime,
758             long cumulativeExecutionTimeMs,
759             Pair<Long, Long> persistedExecutionTimesUTC,
760             int innerFlags, int dynamicConstraints) {
761         this(job, callingUid, sourcePkgName, sourceUserId,
762                 standbyBucket, namespace,
763                 sourceTag, /* numFailures */ 0, /* numSystemStops */ 0,
764                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
765                 lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs,
766                 innerFlags, dynamicConstraints);
767 
768         // Only during initial inflation do we record the UTC-timebase execution bounds
769         // read from the persistent store.  If we ever have to recreate the JobStatus on
770         // the fly, it means we're rescheduling the job; and this means that the calculated
771         // elapsed timebase bounds intrinsically become correct.
772         this.mPersistedUtcTimes = persistedExecutionTimesUTC;
773         if (persistedExecutionTimesUTC != null) {
774             if (DEBUG) {
775                 Slog.i(TAG, "+ restored job with RTC times because of bad boot clock");
776             }
777         }
778     }
779 
780     /** Create a new job to be rescheduled with the provided parameters. */
JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs)781     public JobStatus(JobStatus rescheduling,
782             long newEarliestRuntimeElapsedMillis,
783             long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops,
784             long lastSuccessfulRunTime, long lastFailedRunTime,
785             long cumulativeExecutionTimeMs) {
786         this(rescheduling.job, rescheduling.getUid(),
787                 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
788                 rescheduling.getStandbyBucket(), rescheduling.getNamespace(),
789                 rescheduling.getSourceTag(), numFailures, numSystemStops,
790                 newEarliestRuntimeElapsedMillis,
791                 newLatestRuntimeElapsedMillis,
792                 lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs,
793                 rescheduling.getInternalFlags(),
794                 rescheduling.mDynamicConstraints);
795     }
796 
797     /**
798      * Create a newly scheduled job.
799      * @param callingUid Uid of the package that scheduled this job.
800      * @param sourcePkg Package name of the app that will actually run the job.  Null indicates
801      *     that the calling package is the source.
802      * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
803      *     caller.
804      */
createFromJobInfo(JobInfo job, int callingUid, String sourcePkg, int sourceUserId, @Nullable String namespace, String tag)805     public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePkg,
806             int sourceUserId, @Nullable String namespace, String tag) {
807         final long elapsedNow = sElapsedRealtimeClock.millis();
808         final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
809         if (job.isPeriodic()) {
810             // Make sure period is in the interval [min_possible_period, max_possible_period].
811             final long period = Math.max(JobInfo.getMinPeriodMillis(),
812                     Math.min(JobSchedulerService.MAX_ALLOWED_PERIOD_MS, job.getIntervalMillis()));
813             latestRunTimeElapsedMillis = elapsedNow + period;
814             earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis
815                     // Make sure flex is in the interval [min_possible_flex, period].
816                     - Math.max(JobInfo.getMinFlexMillis(), Math.min(period, job.getFlexMillis()));
817         } else {
818             earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
819                     elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
820             latestRunTimeElapsedMillis = job.hasLateConstraint() ?
821                     elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
822         }
823         String jobPackage = (sourcePkg != null) ? sourcePkg : job.getService().getPackageName();
824 
825         int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
826                 sourceUserId, elapsedNow);
827         return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
828                 standbyBucket, namespace, tag, /* numFailures */ 0, /* numSystemStops */ 0,
829                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
830                 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
831                 /* cumulativeExecutionTime */ 0,
832                 /*innerFlags=*/ 0, /* dynamicConstraints */ 0);
833     }
834 
generateLoggingId(@ullable String namespace, int jobId)835     private long generateLoggingId(@Nullable String namespace, int jobId) {
836         if (namespace == null) {
837             return jobId;
838         }
839         return ((long) namespace.hashCode()) << 31 | jobId;
840     }
841 
842     @Nullable
generateNamespaceHash(@ullable String namespace)843     private static String generateNamespaceHash(@Nullable String namespace) {
844         if (namespace == null) {
845             return null;
846         }
847         if (namespace.trim().isEmpty()) {
848             // Input is composed of all spaces (or nothing at all).
849             return namespace;
850         }
851         synchronized (sNamespaceHashCache) {
852             final int idx = sNamespaceHashCache.indexOfKey(namespace);
853             if (idx >= 0) {
854                 return sNamespaceHashCache.valueAt(idx);
855             }
856         }
857         String hash = null;
858         try {
859             // .hashCode() can result in conflicts that would make distinguishing between
860             // namespaces hard and reduce the accuracy of certain metrics. Use SHA-256
861             // to generate the hash since the probability of collision is extremely low.
862             if (sMessageDigest == null) {
863                 sMessageDigest = MessageDigest.getInstance("SHA-256");
864             }
865             final byte[] digest = sMessageDigest.digest(namespace.getBytes());
866             // Convert to hexadecimal representation
867             StringBuilder hexBuilder = new StringBuilder(digest.length);
868             for (byte byteChar : digest) {
869                 hexBuilder.append(String.format("%02X", byteChar));
870             }
871             hash = hexBuilder.toString();
872         } catch (Exception e) {
873             Slog.wtf(TAG, "Couldn't hash input", e);
874         }
875         if (hash == null) {
876             // If we get to this point, something went wrong with the MessageDigest above.
877             // Don't return the raw input value (which would defeat the purpose of hashing).
878             return "failed_namespace_hash";
879         }
880         hash = hash.intern();
881         synchronized (sNamespaceHashCache) {
882             if (sNamespaceHashCache.size() >= MAX_NAMESPACE_CACHE_SIZE) {
883                 // Drop a random mapping instead of dropping at a predefined index to avoid
884                 // potentially always dropping the same mapping.
885                 sNamespaceHashCache.removeAt((new Random()).nextInt(MAX_NAMESPACE_CACHE_SIZE));
886             }
887             sNamespaceHashCache.put(namespace, hash);
888         }
889         return hash;
890     }
891 
enqueueWorkLocked(JobWorkItem work)892     public void enqueueWorkLocked(JobWorkItem work) {
893         if (pendingWork == null) {
894             pendingWork = new ArrayList<>();
895         }
896         work.setWorkId(nextPendingWorkId);
897         nextPendingWorkId++;
898         if (work.getIntent() != null
899                 && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) {
900             work.setGrants(GrantedUriPermissions.createFromIntent(work.getIntent(), sourceUid,
901                     sourcePackageName, sourceUserId, toShortString()));
902         }
903         pendingWork.add(work);
904         updateNetworkBytesLocked();
905     }
906 
dequeueWorkLocked()907     public JobWorkItem dequeueWorkLocked() {
908         if (pendingWork != null && pendingWork.size() > 0) {
909             JobWorkItem work = pendingWork.remove(0);
910             if (work != null) {
911                 if (executingWork == null) {
912                     executingWork = new ArrayList<>();
913                 }
914                 executingWork.add(work);
915                 work.bumpDeliveryCount();
916             }
917             return work;
918         }
919         return null;
920     }
921 
922     /** Returns the number of {@link JobWorkItem JobWorkItems} attached to this job. */
getWorkCount()923     public int getWorkCount() {
924         final int pendingCount = pendingWork == null ? 0 : pendingWork.size();
925         final int executingCount = executingWork == null ? 0 : executingWork.size();
926         return pendingCount + executingCount;
927     }
928 
hasWorkLocked()929     public boolean hasWorkLocked() {
930         return (pendingWork != null && pendingWork.size() > 0) || hasExecutingWorkLocked();
931     }
932 
hasExecutingWorkLocked()933     public boolean hasExecutingWorkLocked() {
934         return executingWork != null && executingWork.size() > 0;
935     }
936 
ungrantWorkItem(JobWorkItem work)937     private static void ungrantWorkItem(JobWorkItem work) {
938         if (work.getGrants() != null) {
939             ((GrantedUriPermissions)work.getGrants()).revoke();
940         }
941     }
942 
943     /**
944      * Returns {@code true} if the JobWorkItem queue was updated,
945      * and {@code false} if nothing changed.
946      */
completeWorkLocked(int workId)947     public boolean completeWorkLocked(int workId) {
948         if (executingWork != null) {
949             final int N = executingWork.size();
950             for (int i = 0; i < N; i++) {
951                 JobWorkItem work = executingWork.get(i);
952                 if (work.getWorkId() == workId) {
953                     executingWork.remove(i);
954                     ungrantWorkItem(work);
955                     updateNetworkBytesLocked();
956                     return true;
957                 }
958             }
959         }
960         return false;
961     }
962 
ungrantWorkList(ArrayList<JobWorkItem> list)963     private static void ungrantWorkList(ArrayList<JobWorkItem> list) {
964         if (list != null) {
965             final int N = list.size();
966             for (int i = 0; i < N; i++) {
967                 ungrantWorkItem(list.get(i));
968             }
969         }
970     }
971 
stopTrackingJobLocked(JobStatus incomingJob)972     public void stopTrackingJobLocked(JobStatus incomingJob) {
973         if (incomingJob != null) {
974             // We are replacing with a new job -- transfer the work!  We do any executing
975             // work first, since that was originally at the front of the pending work.
976             if (executingWork != null && executingWork.size() > 0) {
977                 incomingJob.pendingWork = executingWork;
978             }
979             if (incomingJob.pendingWork == null) {
980                 incomingJob.pendingWork = pendingWork;
981             } else if (pendingWork != null && pendingWork.size() > 0) {
982                 incomingJob.pendingWork.addAll(pendingWork);
983             }
984             pendingWork = null;
985             executingWork = null;
986             incomingJob.nextPendingWorkId = nextPendingWorkId;
987             incomingJob.updateNetworkBytesLocked();
988         } else {
989             // We are completely stopping the job...  need to clean up work.
990             ungrantWorkList(pendingWork);
991             pendingWork = null;
992             ungrantWorkList(executingWork);
993             executingWork = null;
994         }
995         updateNetworkBytesLocked();
996     }
997 
prepareLocked()998     public void prepareLocked() {
999         if (prepared) {
1000             Slog.wtf(TAG, "Already prepared: " + this);
1001             return;
1002         }
1003         prepared = true;
1004         if (DEBUG_PREPARE) {
1005             unpreparedPoint = null;
1006         }
1007         final ClipData clip = job.getClipData();
1008         if (clip != null) {
1009             uriPerms = GrantedUriPermissions.createFromClip(clip, sourceUid, sourcePackageName,
1010                     sourceUserId, job.getClipGrantFlags(), toShortString());
1011         }
1012     }
1013 
unprepareLocked()1014     public void unprepareLocked() {
1015         if (!prepared) {
1016             Slog.wtf(TAG, "Hasn't been prepared: " + this);
1017             if (DEBUG_PREPARE && unpreparedPoint != null) {
1018                 Slog.e(TAG, "Was already unprepared at ", unpreparedPoint);
1019             }
1020             return;
1021         }
1022         prepared = false;
1023         if (DEBUG_PREPARE) {
1024             unpreparedPoint = new Throwable().fillInStackTrace();
1025         }
1026         if (uriPerms != null) {
1027             uriPerms.revoke();
1028             uriPerms = null;
1029         }
1030     }
1031 
isPreparedLocked()1032     public boolean isPreparedLocked() {
1033         return prepared;
1034     }
1035 
getJob()1036     public JobInfo getJob() {
1037         return job;
1038     }
1039 
getJobId()1040     public int getJobId() {
1041         return job.getId();
1042     }
1043 
1044     /** Returns an ID that can be used to uniquely identify the job when logging statsd metrics. */
getLoggingJobId()1045     public long getLoggingJobId() {
1046         return mLoggingJobId;
1047     }
1048 
printUniqueId(PrintWriter pw)1049     public void printUniqueId(PrintWriter pw) {
1050         if (mNamespace != null) {
1051             pw.print(mNamespace);
1052             pw.print(":");
1053         } else {
1054             pw.print("#");
1055         }
1056         UserHandle.formatUid(pw, callingUid);
1057         pw.print("/");
1058         pw.print(job.getId());
1059     }
1060 
1061     /**
1062      * Returns the number of times the job stopped previously for reasons that appeared to be within
1063      * the app's control.
1064      */
getNumFailures()1065     public int getNumFailures() {
1066         return numFailures;
1067     }
1068 
1069     /**
1070      * Returns the number of times the system stopped a previous execution of this job for reasons
1071      * that were likely outside the app's control.
1072      */
getNumSystemStops()1073     public int getNumSystemStops() {
1074         return mNumSystemStops;
1075     }
1076 
1077     /** Returns the total number of times we've attempted to run this job in the past. */
getNumPreviousAttempts()1078     public int getNumPreviousAttempts() {
1079         return numFailures + mNumSystemStops;
1080     }
1081 
getServiceComponent()1082     public ComponentName getServiceComponent() {
1083         return job.getService();
1084     }
1085 
getSourcePackageName()1086     public String getSourcePackageName() {
1087         return sourcePackageName;
1088     }
1089 
getSourceUid()1090     public int getSourceUid() {
1091         return sourceUid;
1092     }
1093 
getSourceUserId()1094     public int getSourceUserId() {
1095         return sourceUserId;
1096     }
1097 
getUserId()1098     public int getUserId() {
1099         return UserHandle.getUserId(callingUid);
1100     }
1101 
shouldBlameSourceForTimeout()1102     private boolean shouldBlameSourceForTimeout() {
1103         // If the system scheduled the job on behalf of an app, assume the app is the one
1104         // doing the work and blame the app directly. This is the case with things like
1105         // syncs via SyncManager.
1106         // If the system didn't schedule the job on behalf of an app, then
1107         // blame the app doing the actual work. Proxied jobs are a little tricky.
1108         // Proxied jobs scheduled by built-in system apps like DownloadManager may be fine
1109         // and we could consider exempting those jobs. For example, in DownloadManager's
1110         // case, all it does is download files and the code is vetted. A timeout likely
1111         // means it's downloading a large file, which isn't an error. For now, DownloadManager
1112         // is an exempted app, so this shouldn't be an issue.
1113         // However, proxied jobs coming from other system apps (such as those that can
1114         // be updated separately from an OTA) may not be fine and we would want to apply
1115         // this policy to those jobs/apps.
1116         // TODO(284512488): consider exempting DownloadManager or other system apps
1117         return UserHandle.isCore(callingUid);
1118     }
1119 
1120     /**
1121      * Returns the package name that should most likely be blamed for the job timing out.
1122      */
getTimeoutBlamePackageName()1123     public String getTimeoutBlamePackageName() {
1124         if (shouldBlameSourceForTimeout()) {
1125             return sourcePackageName;
1126         }
1127         return getServiceComponent().getPackageName();
1128     }
1129 
1130     /**
1131      * Returns the UID that should most likely be blamed for the job timing out.
1132      */
getTimeoutBlameUid()1133     public int getTimeoutBlameUid() {
1134         if (shouldBlameSourceForTimeout()) {
1135             return sourceUid;
1136         }
1137         return callingUid;
1138     }
1139 
1140     /**
1141      * Returns the userId that should most likely be blamed for the job timing out.
1142      */
getTimeoutBlameUserId()1143     public int getTimeoutBlameUserId() {
1144         if (shouldBlameSourceForTimeout()) {
1145             return sourceUserId;
1146         }
1147         return UserHandle.getUserId(callingUid);
1148     }
1149 
1150     /**
1151      * Returns an appropriate standby bucket for the job, taking into account any standby
1152      * exemptions.
1153      */
getEffectiveStandbyBucket()1154     public int getEffectiveStandbyBucket() {
1155         final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
1156         final boolean isBuggy = jsi.isAppConsideredBuggy(
1157                 getUserId(), getServiceComponent().getPackageName(),
1158                 getTimeoutBlameUserId(), getTimeoutBlamePackageName());
1159 
1160         final int actualBucket = getStandbyBucket();
1161         if (actualBucket == EXEMPTED_INDEX) {
1162             // EXEMPTED apps always have their jobs exempted, even if they're buggy, because the
1163             // user has explicitly told the system to avoid restricting the app for power reasons.
1164             if (isBuggy) {
1165                 final String pkg;
1166                 if (getServiceComponent().getPackageName().equals(sourcePackageName)) {
1167                     pkg = sourcePackageName;
1168                 } else {
1169                     pkg = getServiceComponent().getPackageName() + "/" + sourcePackageName;
1170                 }
1171                 Slog.w(TAG, "Exempted app " + pkg + " considered buggy");
1172             }
1173             return actualBucket;
1174         }
1175         if (uidActive || getJob().isExemptedFromAppStandby()) {
1176             // Treat these cases as if they're in the ACTIVE bucket so that they get throttled
1177             // like other ACTIVE apps.
1178             return ACTIVE_INDEX;
1179         }
1180 
1181         final int bucketWithMediaExemption;
1182         if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX
1183                 && mHasMediaBackupExemption) {
1184             // Treat it as if it's at most WORKING_INDEX (lower index grants higher quota) since
1185             // media backup jobs are important to the user, and the source package may not have
1186             // been used directly in a while.
1187             bucketWithMediaExemption = Math.min(WORKING_INDEX, actualBucket);
1188         } else {
1189             bucketWithMediaExemption = actualBucket;
1190         }
1191 
1192         // If the app is considered buggy, but hasn't yet been put in the RESTRICTED bucket
1193         // (potentially because it's used frequently by the user), limit its effective bucket
1194         // so that it doesn't get to run as much as a normal ACTIVE app.
1195         if (isBuggy && bucketWithMediaExemption < WORKING_INDEX) {
1196             if (!mIsDowngradedDueToBuggyApp) {
1197                 // Safety check to avoid logging multiple times for the same job.
1198                 Counter.logIncrementWithUid(
1199                         "job_scheduler.value_job_quota_reduced_due_to_buggy_uid",
1200                         getTimeoutBlameUid());
1201                 mIsDowngradedDueToBuggyApp = true;
1202             }
1203             return WORKING_INDEX;
1204         }
1205         return bucketWithMediaExemption;
1206     }
1207 
1208     /** Returns the real standby bucket of the job. */
getStandbyBucket()1209     public int getStandbyBucket() {
1210         return standbyBucket;
1211     }
1212 
setStandbyBucket(int newBucket)1213     public void setStandbyBucket(int newBucket) {
1214         if (newBucket == RESTRICTED_INDEX) {
1215             // Adding to the bucket.
1216             addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
1217         } else if (standbyBucket == RESTRICTED_INDEX) {
1218             // Removing from the RESTRICTED bucket.
1219             removeDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
1220         }
1221 
1222         standbyBucket = newBucket;
1223         mLoggedBucketMismatch = false;
1224     }
1225 
1226     /**
1227      * Log a bucket mismatch if this is the first time for this job.
1228      */
maybeLogBucketMismatch()1229     public void maybeLogBucketMismatch() {
1230         if (!mLoggedBucketMismatch) {
1231             Slog.wtf(TAG,
1232                     "App " + getSourcePackageName() + " became active but still in NEVER bucket");
1233             mLoggedBucketMismatch = true;
1234         }
1235     }
1236 
1237     // Called only by the standby monitoring code
getWhenStandbyDeferred()1238     public long getWhenStandbyDeferred() {
1239         return whenStandbyDeferred;
1240     }
1241 
1242     // Called only by the standby monitoring code
setWhenStandbyDeferred(long now)1243     public void setWhenStandbyDeferred(long now) {
1244         whenStandbyDeferred = now;
1245     }
1246 
1247     /**
1248      * Returns the first time this job was force batched, in the elapsed realtime timebase. Will be
1249      * 0 if this job was never force batched.
1250      */
getFirstForceBatchedTimeElapsed()1251     public long getFirstForceBatchedTimeElapsed() {
1252         return mFirstForceBatchedTimeElapsed;
1253     }
1254 
setFirstForceBatchedTimeElapsed(long now)1255     public void setFirstForceBatchedTimeElapsed(long now) {
1256         mFirstForceBatchedTimeElapsed = now;
1257     }
1258 
1259     /**
1260      * Re-evaluates the media backup exemption status.
1261      *
1262      * @return true if the exemption status changed
1263      */
updateMediaBackupExemptionStatus()1264     public boolean updateMediaBackupExemptionStatus() {
1265         final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
1266         boolean hasMediaExemption = mHasExemptedMediaUrisOnly
1267                 && !job.hasLateConstraint()
1268                 && job.getRequiredNetwork() != null
1269                 && getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT
1270                 && sourcePackageName.equals(jsi.getCloudMediaProviderPackage(sourceUserId));
1271         if (mHasMediaBackupExemption == hasMediaExemption) {
1272             return false;
1273         }
1274         mHasMediaBackupExemption = hasMediaExemption;
1275         return true;
1276     }
1277 
1278     @Nullable
getNamespace()1279     public String getNamespace() {
1280         return mNamespace;
1281     }
1282 
1283     @Nullable
getNamespaceHash()1284     public String getNamespaceHash() {
1285         return mNamespaceHash;
1286     }
1287 
getSourceTag()1288     public String getSourceTag() {
1289         return sourceTag;
1290     }
1291 
getUid()1292     public int getUid() {
1293         return callingUid;
1294     }
1295 
getBatteryName()1296     public String getBatteryName() {
1297         return batteryName;
1298     }
1299 
getTag()1300     public String getTag() {
1301         return tag;
1302     }
1303 
getBias()1304     public int getBias() {
1305         return job.getBias();
1306     }
1307 
1308     /**
1309      * Returns the priority of the job, which may be adjusted due to various factors.
1310      * @see JobInfo.Builder#setPriority(int)
1311      */
1312     @JobInfo.Priority
getEffectivePriority()1313     public int getEffectivePriority() {
1314         final boolean isDemoted =
1315                 (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) != 0
1316                         || (job.isUserInitiated()
1317                         && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) != 0);
1318         final int maxPriority;
1319         if (isDemoted) {
1320             // If the job was demoted for some reason, limit its priority to HIGH.
1321             maxPriority = JobInfo.PRIORITY_HIGH;
1322         } else {
1323             maxPriority = JobInfo.PRIORITY_MAX;
1324         }
1325         final int rawPriority = Math.min(maxPriority, job.getPriority());
1326         if (numFailures < 2) {
1327             return rawPriority;
1328         }
1329         if (shouldTreatAsUserInitiatedJob()) {
1330             // Don't drop priority of UI jobs.
1331             return rawPriority;
1332         }
1333         // Slowly decay priority of jobs to prevent starvation of other jobs.
1334         if (isRequestedExpeditedJob()) {
1335             // EJs can't fall below HIGH priority.
1336             return JobInfo.PRIORITY_HIGH;
1337         }
1338         // Set a maximum priority based on the number of failures.
1339         final int dropPower = numFailures / 2;
1340         switch (dropPower) {
1341             case 1: return Math.min(JobInfo.PRIORITY_DEFAULT, rawPriority);
1342             case 2: return Math.min(JobInfo.PRIORITY_LOW, rawPriority);
1343             default: return JobInfo.PRIORITY_MIN;
1344         }
1345     }
1346 
getFlags()1347     public int getFlags() {
1348         return job.getFlags();
1349     }
1350 
getInternalFlags()1351     public int getInternalFlags() {
1352         return mInternalFlags;
1353     }
1354 
addInternalFlags(int flags)1355     public void addInternalFlags(int flags) {
1356         mInternalFlags |= flags;
1357     }
1358 
removeInternalFlags(int flags)1359     public void removeInternalFlags(int flags) {
1360         mInternalFlags = mInternalFlags & ~flags;
1361     }
1362 
getPreferredConstraintFlags()1363     int getPreferredConstraintFlags() {
1364         return mPreferredConstraints;
1365     }
1366 
getSatisfiedConstraintFlags()1367     public int getSatisfiedConstraintFlags() {
1368         return satisfiedConstraints;
1369     }
1370 
maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker)1371     public void maybeAddForegroundExemption(Predicate<Integer> uidForegroundChecker) {
1372         // Jobs with time constraints shouldn't be exempted.
1373         if (job.hasEarlyConstraint() || job.hasLateConstraint()) {
1374             return;
1375         }
1376         // Already exempted, skip the foreground check.
1377         if ((mInternalFlags & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
1378             return;
1379         }
1380         if (uidForegroundChecker.test(getSourceUid())) {
1381             addInternalFlags(INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION);
1382         }
1383     }
1384 
updateNetworkBytesLocked()1385     private void updateNetworkBytesLocked() {
1386         mTotalNetworkDownloadBytes = job.getEstimatedNetworkDownloadBytes();
1387         if (mTotalNetworkDownloadBytes < 0) {
1388             // Legacy apps may have provided invalid negative values. Ignore invalid values.
1389             mTotalNetworkDownloadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
1390         }
1391         mTotalNetworkUploadBytes = job.getEstimatedNetworkUploadBytes();
1392         if (mTotalNetworkUploadBytes < 0) {
1393             // Legacy apps may have provided invalid negative values. Ignore invalid values.
1394             mTotalNetworkUploadBytes = JobInfo.NETWORK_BYTES_UNKNOWN;
1395         }
1396         // Minimum network chunk bytes has had data validation since its introduction, so no
1397         // need to do validation again.
1398         mMinimumNetworkChunkBytes = job.getMinimumNetworkChunkBytes();
1399 
1400         if (pendingWork != null) {
1401             for (int i = 0; i < pendingWork.size(); i++) {
1402                 long downloadBytes = pendingWork.get(i).getEstimatedNetworkDownloadBytes();
1403                 if (downloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN && downloadBytes > 0) {
1404                     // If any component of the job has unknown usage, we won't have a
1405                     // complete picture of what data will be used. However, we use what we are given
1406                     // to get us as close to the complete picture as possible.
1407                     if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1408                         mTotalNetworkDownloadBytes += downloadBytes;
1409                     } else {
1410                         mTotalNetworkDownloadBytes = downloadBytes;
1411                     }
1412                 }
1413                 long uploadBytes = pendingWork.get(i).getEstimatedNetworkUploadBytes();
1414                 if (uploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN && uploadBytes > 0) {
1415                     // If any component of the job has unknown usage, we won't have a
1416                     // complete picture of what data will be used. However, we use what we are given
1417                     // to get us as close to the complete picture as possible.
1418                     if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1419                         mTotalNetworkUploadBytes += uploadBytes;
1420                     } else {
1421                         mTotalNetworkUploadBytes = uploadBytes;
1422                     }
1423                 }
1424                 final long chunkBytes = pendingWork.get(i).getMinimumNetworkChunkBytes();
1425                 if (mMinimumNetworkChunkBytes == JobInfo.NETWORK_BYTES_UNKNOWN) {
1426                     mMinimumNetworkChunkBytes = chunkBytes;
1427                 } else if (chunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
1428                     mMinimumNetworkChunkBytes = Math.min(mMinimumNetworkChunkBytes, chunkBytes);
1429                 }
1430             }
1431         }
1432     }
1433 
getEstimatedNetworkDownloadBytes()1434     public long getEstimatedNetworkDownloadBytes() {
1435         return mTotalNetworkDownloadBytes;
1436     }
1437 
getEstimatedNetworkUploadBytes()1438     public long getEstimatedNetworkUploadBytes() {
1439         return mTotalNetworkUploadBytes;
1440     }
1441 
getMinimumNetworkChunkBytes()1442     public long getMinimumNetworkChunkBytes() {
1443         return mMinimumNetworkChunkBytes;
1444     }
1445 
1446     /** Does this job have any sort of networking constraint? */
hasConnectivityConstraint()1447     public boolean hasConnectivityConstraint() {
1448         // No need to check mDynamicConstraints since connectivity will only be in that list if
1449         // it's already in the requiredConstraints list.
1450         return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
1451     }
1452 
hasChargingConstraint()1453     public boolean hasChargingConstraint() {
1454         return hasConstraint(CONSTRAINT_CHARGING);
1455     }
1456 
hasBatteryNotLowConstraint()1457     public boolean hasBatteryNotLowConstraint() {
1458         return hasConstraint(CONSTRAINT_BATTERY_NOT_LOW);
1459     }
1460 
1461     /** Returns true if the job requires charging OR battery not low. */
hasPowerConstraint()1462     boolean hasPowerConstraint() {
1463         return hasConstraint(CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW);
1464     }
1465 
hasStorageNotLowConstraint()1466     public boolean hasStorageNotLowConstraint() {
1467         return hasConstraint(CONSTRAINT_STORAGE_NOT_LOW);
1468     }
1469 
hasTimingDelayConstraint()1470     public boolean hasTimingDelayConstraint() {
1471         return hasConstraint(CONSTRAINT_TIMING_DELAY);
1472     }
1473 
hasDeadlineConstraint()1474     public boolean hasDeadlineConstraint() {
1475         return hasConstraint(CONSTRAINT_DEADLINE);
1476     }
1477 
hasIdleConstraint()1478     public boolean hasIdleConstraint() {
1479         return hasConstraint(CONSTRAINT_IDLE);
1480     }
1481 
hasContentTriggerConstraint()1482     public boolean hasContentTriggerConstraint() {
1483         // No need to check mDynamicConstraints since content trigger will only be in that list if
1484         // it's already in the requiredConstraints list.
1485         return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
1486     }
1487 
1488     /** Returns true if the job has flexible job constraints enabled */
hasFlexibilityConstraint()1489     public boolean hasFlexibilityConstraint() {
1490         return (requiredConstraints & CONSTRAINT_FLEXIBLE) != 0;
1491     }
1492 
1493     /** Returns the number of flexible job constraints required to be satisfied to execute */
getNumRequiredFlexibleConstraints()1494     public int getNumRequiredFlexibleConstraints() {
1495         return mNumRequiredFlexibleConstraints - mNumDroppedFlexibleConstraints;
1496     }
1497 
1498     /**
1499      * Returns the number of required flexible job constraints that have been dropped with time.
1500      * The lower this number is the easier it is for the flexibility constraint to be satisfied.
1501      */
getNumDroppedFlexibleConstraints()1502     public int getNumDroppedFlexibleConstraints() {
1503         return mNumDroppedFlexibleConstraints;
1504     }
1505 
1506     /**
1507      * Checks both {@link #requiredConstraints} and {@link #mDynamicConstraints} to see if this job
1508      * requires the specified constraint.
1509      */
hasConstraint(int constraint)1510     private boolean hasConstraint(int constraint) {
1511         return (requiredConstraints & constraint) != 0 || (mDynamicConstraints & constraint) != 0;
1512     }
1513 
getTriggerContentUpdateDelay()1514     public long getTriggerContentUpdateDelay() {
1515         long time = job.getTriggerContentUpdateDelay();
1516         if (time < 0) {
1517             return DEFAULT_TRIGGER_UPDATE_DELAY;
1518         }
1519         return Math.max(time, MIN_TRIGGER_UPDATE_DELAY);
1520     }
1521 
getTriggerContentMaxDelay()1522     public long getTriggerContentMaxDelay() {
1523         long time = job.getTriggerContentMaxDelay();
1524         if (time < 0) {
1525             return DEFAULT_TRIGGER_MAX_DELAY;
1526         }
1527         return Math.max(time, MIN_TRIGGER_MAX_DELAY);
1528     }
1529 
isPersisted()1530     public boolean isPersisted() {
1531         return job.isPersisted();
1532     }
1533 
getCumulativeExecutionTimeMs()1534     public long getCumulativeExecutionTimeMs() {
1535         return mCumulativeExecutionTimeMs;
1536     }
1537 
incrementCumulativeExecutionTime(long incrementMs)1538     public void incrementCumulativeExecutionTime(long incrementMs) {
1539         mCumulativeExecutionTimeMs += incrementMs;
1540     }
1541 
getEarliestRunTime()1542     public long getEarliestRunTime() {
1543         return earliestRunTimeElapsedMillis;
1544     }
1545 
getLatestRunTimeElapsed()1546     public long getLatestRunTimeElapsed() {
1547         return latestRunTimeElapsedMillis;
1548     }
1549 
getOriginalLatestRunTimeElapsed()1550     public long getOriginalLatestRunTimeElapsed() {
1551         return mOriginalLatestRunTimeElapsedMillis;
1552     }
1553 
setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed)1554     public void setOriginalLatestRunTimeElapsed(long latestRunTimeElapsed) {
1555         mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsed;
1556     }
1557 
1558     /** Sets the jobs access to an unmetered network. */
setHasAccessToUnmetered(boolean access)1559     void setHasAccessToUnmetered(boolean access) {
1560         mHasAccessToUnmetered = access;
1561     }
1562 
getHasAccessToUnmetered()1563     boolean getHasAccessToUnmetered() {
1564         return mHasAccessToUnmetered;
1565     }
1566 
getPreferUnmetered()1567     boolean getPreferUnmetered() {
1568         return mPreferUnmetered;
1569     }
1570 
1571     @JobParameters.StopReason
getStopReason()1572     public int getStopReason() {
1573         return mReasonReadyToUnready;
1574     }
1575 
1576     /**
1577      * Return the fractional position of "now" within the "run time" window of
1578      * this job.
1579      * <p>
1580      * For example, if the earliest run time was 10 minutes ago, and the latest
1581      * run time is 30 minutes from now, this would return 0.25.
1582      * <p>
1583      * If the job has no window defined, returns 1. When only an earliest or
1584      * latest time is defined, it's treated as an infinitely small window at
1585      * that time.
1586      */
getFractionRunTime()1587     public float getFractionRunTime() {
1588         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
1589         if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME
1590                 && latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) {
1591             return 1;
1592         } else if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) {
1593             return now >= latestRunTimeElapsedMillis ? 1 : 0;
1594         } else if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) {
1595             return now >= earliestRunTimeElapsedMillis ? 1 : 0;
1596         } else {
1597             if (now <= earliestRunTimeElapsedMillis) {
1598                 return 0;
1599             } else if (now >= latestRunTimeElapsedMillis) {
1600                 return 1;
1601             } else {
1602                 return (float) (now - earliestRunTimeElapsedMillis)
1603                         / (float) (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis);
1604             }
1605         }
1606     }
1607 
getPersistedUtcTimes()1608     public Pair<Long, Long> getPersistedUtcTimes() {
1609         return mPersistedUtcTimes;
1610     }
1611 
clearPersistedUtcTimes()1612     public void clearPersistedUtcTimes() {
1613         mPersistedUtcTimes = null;
1614     }
1615 
1616     /** @return true if the app has requested that this run as an expedited job. */
isRequestedExpeditedJob()1617     public boolean isRequestedExpeditedJob() {
1618         return (getFlags() & JobInfo.FLAG_EXPEDITED) != 0;
1619     }
1620 
1621     /**
1622      * @return true if all expedited job requirements are satisfied and therefore this should be
1623      * treated as an expedited job.
1624      */
shouldTreatAsExpeditedJob()1625     public boolean shouldTreatAsExpeditedJob() {
1626         return mExpeditedQuotaApproved && mExpeditedTareApproved && isRequestedExpeditedJob();
1627     }
1628 
1629     /**
1630      * @return true if the job was scheduled as a user-initiated job and it hasn't been downgraded
1631      * for any reason.
1632      */
shouldTreatAsUserInitiatedJob()1633     public boolean shouldTreatAsUserInitiatedJob() {
1634         // isUserBgRestricted is intentionally excluded from this method. It should be fine to
1635         // treat the job as a UI job while the app is TOP, but just not in the background.
1636         // Instead of adding a proc state check here, the parts of JS that can make the distinction
1637         // and care about the distinction can do the check.
1638         return getJob().isUserInitiated()
1639                 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0
1640                 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0;
1641     }
1642 
1643     /**
1644      * Return a summary that uniquely identifies the underlying job.
1645      */
1646     @NonNull
getUserVisibleJobSummary()1647     public UserVisibleJobSummary getUserVisibleJobSummary() {
1648         if (mUserVisibleJobSummary == null) {
1649             mUserVisibleJobSummary = new UserVisibleJobSummary(
1650                     callingUid, getServiceComponent().getPackageName(),
1651                     getSourceUserId(), getSourcePackageName(),
1652                     getNamespace(), getJobId());
1653         }
1654         return mUserVisibleJobSummary;
1655     }
1656 
1657     /**
1658      * @return true if this is a job whose execution should be made visible to the user.
1659      */
isUserVisibleJob()1660     public boolean isUserVisibleJob() {
1661         return shouldTreatAsUserInitiatedJob() || startedAsUserInitiatedJob;
1662     }
1663 
1664     /**
1665      * @return true if the job is exempted from Doze restrictions and therefore allowed to run
1666      * in Doze.
1667      */
canRunInDoze()1668     public boolean canRunInDoze() {
1669         return appHasDozeExemption
1670                 || (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
1671                 || shouldTreatAsUserInitiatedJob()
1672                 // EJs can't run in Doze if we explicitly require that the device is not Dozing.
1673                 || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
1674                         && (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
1675     }
1676 
canRunInBatterySaver()1677     boolean canRunInBatterySaver() {
1678         return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
1679                 || shouldTreatAsUserInitiatedJob()
1680                 // EJs can't run in Battery Saver if we explicitly require that Battery Saver is off
1681                 || ((shouldTreatAsExpeditedJob() || startedAsExpeditedJob)
1682                         && (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
1683     }
1684 
1685     /** Returns whether or not the app is background restricted by the user (FAS). */
isUserBgRestricted()1686     public boolean isUserBgRestricted() {
1687         return mIsUserBgRestricted;
1688     }
1689 
1690     /** @return true if the constraint was changed, false otherwise. */
setChargingConstraintSatisfied(final long nowElapsed, boolean state)1691     boolean setChargingConstraintSatisfied(final long nowElapsed, boolean state) {
1692         return setConstraintSatisfied(CONSTRAINT_CHARGING, nowElapsed, state);
1693     }
1694 
1695     /** @return true if the constraint was changed, false otherwise. */
setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state)1696     boolean setBatteryNotLowConstraintSatisfied(final long nowElapsed, boolean state) {
1697         return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, nowElapsed, state);
1698     }
1699 
1700     /** @return true if the constraint was changed, false otherwise. */
setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state)1701     boolean setStorageNotLowConstraintSatisfied(final long nowElapsed, boolean state) {
1702         return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, nowElapsed, state);
1703     }
1704 
1705     /** @return true if the constraint was changed, false otherwise. */
setPrefetchConstraintSatisfied(final long nowElapsed, boolean state)1706     boolean setPrefetchConstraintSatisfied(final long nowElapsed, boolean state) {
1707         return setConstraintSatisfied(CONSTRAINT_PREFETCH, nowElapsed, state);
1708     }
1709 
1710     /** @return true if the constraint was changed, false otherwise. */
setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state)1711     boolean setTimingDelayConstraintSatisfied(final long nowElapsed, boolean state) {
1712         return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, nowElapsed, state);
1713     }
1714 
1715     /** @return true if the constraint was changed, false otherwise. */
setDeadlineConstraintSatisfied(final long nowElapsed, boolean state)1716     boolean setDeadlineConstraintSatisfied(final long nowElapsed, boolean state) {
1717         if (setConstraintSatisfied(CONSTRAINT_DEADLINE, nowElapsed, state)) {
1718             // The constraint was changed. Update the ready flag.
1719             mReadyDeadlineSatisfied = !job.isPeriodic() && hasDeadlineConstraint() && state;
1720             return true;
1721         }
1722         return false;
1723     }
1724 
1725     /** @return true if the constraint was changed, false otherwise. */
setIdleConstraintSatisfied(final long nowElapsed, boolean state)1726     boolean setIdleConstraintSatisfied(final long nowElapsed, boolean state) {
1727         return setConstraintSatisfied(CONSTRAINT_IDLE, nowElapsed, state);
1728     }
1729 
1730     /** @return true if the constraint was changed, false otherwise. */
setConnectivityConstraintSatisfied(final long nowElapsed, boolean state)1731     boolean setConnectivityConstraintSatisfied(final long nowElapsed, boolean state) {
1732         return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, nowElapsed, state);
1733     }
1734 
1735     /** @return true if the constraint was changed, false otherwise. */
setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state)1736     boolean setContentTriggerConstraintSatisfied(final long nowElapsed, boolean state) {
1737         return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, nowElapsed, state);
1738     }
1739 
1740     /** @return true if the constraint was changed, false otherwise. */
setDeviceNotDozingConstraintSatisfied(final long nowElapsed, boolean state, boolean whitelisted)1741     boolean setDeviceNotDozingConstraintSatisfied(final long nowElapsed,
1742             boolean state, boolean whitelisted) {
1743         appHasDozeExemption = whitelisted;
1744         if (setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, nowElapsed, state)) {
1745             // The constraint was changed. Update the ready flag.
1746             mReadyNotDozing = state || canRunInDoze();
1747             return true;
1748         }
1749         return false;
1750     }
1751 
1752     /** @return true if the constraint was changed, false otherwise. */
setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state, boolean isUserBgRestricted)1753     boolean setBackgroundNotRestrictedConstraintSatisfied(final long nowElapsed, boolean state,
1754             boolean isUserBgRestricted) {
1755         mIsUserBgRestricted = isUserBgRestricted;
1756         if (setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, nowElapsed, state)) {
1757             // The constraint was changed. Update the ready flag.
1758             mReadyNotRestrictedInBg = state;
1759             return true;
1760         }
1761         return false;
1762     }
1763 
1764     /** @return true if the constraint was changed, false otherwise. */
setQuotaConstraintSatisfied(final long nowElapsed, boolean state)1765     boolean setQuotaConstraintSatisfied(final long nowElapsed, boolean state) {
1766         if (setConstraintSatisfied(CONSTRAINT_WITHIN_QUOTA, nowElapsed, state)) {
1767             // The constraint was changed. Update the ready flag.
1768             mReadyWithinQuota = state;
1769             return true;
1770         }
1771         return false;
1772     }
1773 
1774     /** @return true if the constraint was changed, false otherwise. */
setTareWealthConstraintSatisfied(final long nowElapsed, boolean state)1775     boolean setTareWealthConstraintSatisfied(final long nowElapsed, boolean state) {
1776         if (setConstraintSatisfied(CONSTRAINT_TARE_WEALTH, nowElapsed, state)) {
1777             // The constraint was changed. Update the ready flag.
1778             mReadyTareWealth = state;
1779             return true;
1780         }
1781         return false;
1782     }
1783 
1784     /** @return true if the constraint was changed, false otherwise. */
setFlexibilityConstraintSatisfied(final long nowElapsed, boolean state)1785     boolean setFlexibilityConstraintSatisfied(final long nowElapsed, boolean state) {
1786         return setConstraintSatisfied(CONSTRAINT_FLEXIBLE, nowElapsed, state);
1787     }
1788 
1789     /**
1790      * Sets whether or not this job is approved to be treated as an expedited job based on quota
1791      * policy.
1792      *
1793      * @return true if the approval bit was changed, false otherwise.
1794      */
setExpeditedJobQuotaApproved(final long nowElapsed, boolean state)1795     boolean setExpeditedJobQuotaApproved(final long nowElapsed, boolean state) {
1796         if (mExpeditedQuotaApproved == state) {
1797             return false;
1798         }
1799         final boolean wasReady = !state && isReady();
1800         mExpeditedQuotaApproved = state;
1801         updateExpeditedDependencies();
1802         final boolean isReady = isReady();
1803         if (wasReady && !isReady) {
1804             mReasonReadyToUnready = JobParameters.STOP_REASON_QUOTA;
1805         } else if (!wasReady && isReady) {
1806             mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
1807         }
1808         return true;
1809     }
1810 
1811     /**
1812      * Sets whether or not this job is approved to be treated as an expedited job based on TARE
1813      * policy.
1814      *
1815      * @return true if the approval bit was changed, false otherwise.
1816      */
setExpeditedJobTareApproved(final long nowElapsed, boolean state)1817     boolean setExpeditedJobTareApproved(final long nowElapsed, boolean state) {
1818         if (mExpeditedTareApproved == state) {
1819             return false;
1820         }
1821         final boolean wasReady = !state && isReady();
1822         mExpeditedTareApproved = state;
1823         updateExpeditedDependencies();
1824         final boolean isReady = isReady();
1825         if (wasReady && !isReady) {
1826             mReasonReadyToUnready = JobParameters.STOP_REASON_QUOTA;
1827         } else if (!wasReady && isReady) {
1828             mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
1829         }
1830         return true;
1831     }
1832 
updateExpeditedDependencies()1833     private void updateExpeditedDependencies() {
1834         // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag.
1835         // Making it also track requested-expedited jobs would add unnecessary hops since the
1836         // controller would then defer to canRunInDoze. Avoid the hops and just update
1837         // mReadyNotDozing directly.
1838         mReadyNotDozing = isConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING) || canRunInDoze();
1839     }
1840 
1841     /** @return true if the state was changed, false otherwise. */
setUidActive(final boolean newActiveState)1842     boolean setUidActive(final boolean newActiveState) {
1843         if (newActiveState != uidActive) {
1844             uidActive = newActiveState;
1845             return true;
1846         }
1847         return false; /* unchanged */
1848     }
1849 
1850     /** @return true if the constraint was changed, false otherwise. */
setConstraintSatisfied(int constraint, final long nowElapsed, boolean state)1851     boolean setConstraintSatisfied(int constraint, final long nowElapsed, boolean state) {
1852         boolean old = (satisfiedConstraints&constraint) != 0;
1853         if (old == state) {
1854             return false;
1855         }
1856         if (DEBUG) {
1857             Slog.v(TAG,
1858                     "Constraint " + constraint + " is " + (!state ? "NOT " : "") + "satisfied for "
1859                             + toShortString());
1860         }
1861         final boolean wasReady = !state && isReady();
1862         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
1863         mSatisfiedConstraintsOfInterest = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
1864         mReadyDynamicSatisfied = mDynamicConstraints != 0
1865                 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
1866         if (STATS_LOG_ENABLED && (STATSD_CONSTRAINTS_TO_LOG & constraint) != 0) {
1867             FrameworkStatsLog.write_non_chained(
1868                     FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED,
1869                     sourceUid, null, getBatteryName(), getProtoConstraint(constraint),
1870                     state ? FrameworkStatsLog.SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED
1871                             : FrameworkStatsLog
1872                                     .SCHEDULED_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
1873         }
1874 
1875         mConstraintUpdatedTimesElapsed[mConstraintChangeHistoryIndex] = nowElapsed;
1876         mConstraintStatusHistory[mConstraintChangeHistoryIndex] = satisfiedConstraints;
1877         mConstraintChangeHistoryIndex =
1878                 (mConstraintChangeHistoryIndex + 1) % NUM_CONSTRAINT_CHANGE_HISTORY;
1879 
1880         // Can't use isReady() directly since "cache booleans" haven't updated yet.
1881         final boolean isReady = readinessStatusWithConstraint(constraint, state);
1882         if (wasReady && !isReady) {
1883             mReasonReadyToUnready = constraintToStopReason(constraint);
1884         } else if (!wasReady && isReady) {
1885             mReasonReadyToUnready = JobParameters.STOP_REASON_UNDEFINED;
1886         }
1887 
1888         return true;
1889     }
1890 
1891     @JobParameters.StopReason
constraintToStopReason(int constraint)1892     private int constraintToStopReason(int constraint) {
1893         switch (constraint) {
1894             case CONSTRAINT_BATTERY_NOT_LOW:
1895                 if ((requiredConstraints & constraint) != 0) {
1896                     // The developer requested this constraint, so it makes sense to return the
1897                     // explicit constraint reason.
1898                     return JobParameters.STOP_REASON_CONSTRAINT_BATTERY_NOT_LOW;
1899                 }
1900                 // Hard-coding right now since the current dynamic constraint sets don't overlap
1901                 // TODO: return based on active dynamic constraint sets when they start overlapping
1902                 return JobParameters.STOP_REASON_APP_STANDBY;
1903             case CONSTRAINT_CHARGING:
1904                 if ((requiredConstraints & constraint) != 0) {
1905                     // The developer requested this constraint, so it makes sense to return the
1906                     // explicit constraint reason.
1907                     return JobParameters.STOP_REASON_CONSTRAINT_CHARGING;
1908                 }
1909                 // Hard-coding right now since the current dynamic constraint sets don't overlap
1910                 // TODO: return based on active dynamic constraint sets when they start overlapping
1911                 return JobParameters.STOP_REASON_APP_STANDBY;
1912             case CONSTRAINT_CONNECTIVITY:
1913                 return JobParameters.STOP_REASON_CONSTRAINT_CONNECTIVITY;
1914             case CONSTRAINT_IDLE:
1915                 if ((requiredConstraints & constraint) != 0) {
1916                     // The developer requested this constraint, so it makes sense to return the
1917                     // explicit constraint reason.
1918                     return JobParameters.STOP_REASON_CONSTRAINT_DEVICE_IDLE;
1919                 }
1920                 // Hard-coding right now since the current dynamic constraint sets don't overlap
1921                 // TODO: return based on active dynamic constraint sets when they start overlapping
1922                 return JobParameters.STOP_REASON_APP_STANDBY;
1923             case CONSTRAINT_STORAGE_NOT_LOW:
1924                 return JobParameters.STOP_REASON_CONSTRAINT_STORAGE_NOT_LOW;
1925 
1926             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
1927                 // The BACKGROUND_NOT_RESTRICTED constraint could be dissatisfied either because
1928                 // the app is background restricted, or because we're restricting background work
1929                 // in battery saver. Assume that background restriction is the reason apps that
1930                 // are background restricted have their jobs stopped, and battery saver otherwise.
1931                 // This has the benefit of being consistent for background restricted apps
1932                 // (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of
1933                 // battery saver state.
1934                 if (mIsUserBgRestricted) {
1935                     return JobParameters.STOP_REASON_BACKGROUND_RESTRICTION;
1936                 }
1937                 return JobParameters.STOP_REASON_DEVICE_STATE;
1938             case CONSTRAINT_DEVICE_NOT_DOZING:
1939                 return JobParameters.STOP_REASON_DEVICE_STATE;
1940 
1941             case CONSTRAINT_PREFETCH:
1942                 return JobParameters.STOP_REASON_ESTIMATED_APP_LAUNCH_TIME_CHANGED;
1943 
1944             case CONSTRAINT_TARE_WEALTH:
1945             case CONSTRAINT_WITHIN_QUOTA:
1946                 return JobParameters.STOP_REASON_QUOTA;
1947 
1948             // These should never be stop reasons since they can never go from true to false.
1949             case CONSTRAINT_CONTENT_TRIGGER:
1950             case CONSTRAINT_DEADLINE:
1951             case CONSTRAINT_TIMING_DELAY:
1952             default:
1953                 Slog.wtf(TAG, "Unsupported constraint (" + constraint + ") --stop reason mapping");
1954                 return JobParameters.STOP_REASON_UNDEFINED;
1955         }
1956     }
1957 
1958     /**
1959      * If {@link #isReady()} returns false, this will return a single reason why the job isn't
1960      * ready. If {@link #isReady()} returns true, this will return
1961      * {@link JobScheduler#PENDING_JOB_REASON_UNDEFINED}.
1962      */
1963     @JobScheduler.PendingJobReason
getPendingJobReason()1964     public int getPendingJobReason() {
1965         final int unsatisfiedConstraints = ~satisfiedConstraints
1966                 & (requiredConstraints | mDynamicConstraints | IMPLICIT_CONSTRAINTS);
1967         if ((CONSTRAINT_BACKGROUND_NOT_RESTRICTED & unsatisfiedConstraints) != 0) {
1968             // The BACKGROUND_NOT_RESTRICTED constraint could be unsatisfied either because
1969             // the app is background restricted, or because we're restricting background work
1970             // in battery saver. Assume that background restriction is the reason apps that
1971             // jobs are not ready, and battery saver otherwise.
1972             // This has the benefit of being consistent for background restricted apps
1973             // (they'll always get BACKGROUND_RESTRICTION) as the reason, regardless of
1974             // battery saver state.
1975             if (mIsUserBgRestricted) {
1976                 return JobScheduler.PENDING_JOB_REASON_BACKGROUND_RESTRICTION;
1977             }
1978             return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
1979         }
1980         if ((CONSTRAINT_BATTERY_NOT_LOW & unsatisfiedConstraints) != 0) {
1981             if ((CONSTRAINT_BATTERY_NOT_LOW & requiredConstraints) != 0) {
1982                 // The developer requested this constraint, so it makes sense to return the
1983                 // explicit constraint reason.
1984                 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_BATTERY_NOT_LOW;
1985             }
1986             // Hard-coding right now since the current dynamic constraint sets don't overlap
1987             // TODO: return based on active dynamic constraint sets when they start overlapping
1988             return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
1989         }
1990         if ((CONSTRAINT_CHARGING & unsatisfiedConstraints) != 0) {
1991             if ((CONSTRAINT_CHARGING & requiredConstraints) != 0) {
1992                 // The developer requested this constraint, so it makes sense to return the
1993                 // explicit constraint reason.
1994                 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CHARGING;
1995             }
1996             // Hard-coding right now since the current dynamic constraint sets don't overlap
1997             // TODO: return based on active dynamic constraint sets when they start overlapping
1998             return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
1999         }
2000         if ((CONSTRAINT_CONNECTIVITY & unsatisfiedConstraints) != 0) {
2001             return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONNECTIVITY;
2002         }
2003         if ((CONSTRAINT_CONTENT_TRIGGER & unsatisfiedConstraints) != 0) {
2004             return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_CONTENT_TRIGGER;
2005         }
2006         if ((CONSTRAINT_DEVICE_NOT_DOZING & unsatisfiedConstraints) != 0) {
2007             return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
2008         }
2009         if ((CONSTRAINT_FLEXIBLE & unsatisfiedConstraints) != 0) {
2010             return JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION;
2011         }
2012         if ((CONSTRAINT_IDLE & unsatisfiedConstraints) != 0) {
2013             if ((CONSTRAINT_IDLE & requiredConstraints) != 0) {
2014                 // The developer requested this constraint, so it makes sense to return the
2015                 // explicit constraint reason.
2016                 return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_DEVICE_IDLE;
2017             }
2018             // Hard-coding right now since the current dynamic constraint sets don't overlap
2019             // TODO: return based on active dynamic constraint sets when they start overlapping
2020             return JobScheduler.PENDING_JOB_REASON_APP_STANDBY;
2021         }
2022         if ((CONSTRAINT_PREFETCH & unsatisfiedConstraints) != 0) {
2023             return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_PREFETCH;
2024         }
2025         if ((CONSTRAINT_STORAGE_NOT_LOW & unsatisfiedConstraints) != 0) {
2026             return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_STORAGE_NOT_LOW;
2027         }
2028         if ((CONSTRAINT_TARE_WEALTH & unsatisfiedConstraints) != 0) {
2029             return JobScheduler.PENDING_JOB_REASON_QUOTA;
2030         }
2031         if ((CONSTRAINT_TIMING_DELAY & unsatisfiedConstraints) != 0) {
2032             return JobScheduler.PENDING_JOB_REASON_CONSTRAINT_MINIMUM_LATENCY;
2033         }
2034         if ((CONSTRAINT_WITHIN_QUOTA & unsatisfiedConstraints) != 0) {
2035             return JobScheduler.PENDING_JOB_REASON_QUOTA;
2036         }
2037 
2038         if (getEffectiveStandbyBucket() == NEVER_INDEX) {
2039             Slog.wtf(TAG, "App in NEVER bucket querying pending job reason");
2040             // The user hasn't officially launched this app.
2041             return JobScheduler.PENDING_JOB_REASON_USER;
2042         }
2043         if (serviceProcessName != null) {
2044             return JobScheduler.PENDING_JOB_REASON_APP;
2045         }
2046 
2047         if (!isReady()) {
2048             Slog.wtf(TAG, "Unknown reason job isn't ready");
2049         }
2050         return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
2051     }
2052 
2053     /** @return whether or not the @param constraint is satisfied */
isConstraintSatisfied(int constraint)2054     public boolean isConstraintSatisfied(int constraint) {
2055         return (satisfiedConstraints&constraint) != 0;
2056     }
2057 
isExpeditedQuotaApproved()2058     boolean isExpeditedQuotaApproved() {
2059         return mExpeditedQuotaApproved;
2060     }
2061 
clearTrackingController(int which)2062     boolean clearTrackingController(int which) {
2063         if ((trackingControllers&which) != 0) {
2064             trackingControllers &= ~which;
2065             return true;
2066         }
2067         return false;
2068     }
2069 
setTrackingController(int which)2070     void setTrackingController(int which) {
2071         trackingControllers |= which;
2072     }
2073 
2074     /** Adjusts the number of required flexible constraints by the given number */
adjustNumRequiredFlexibleConstraints(int adjustment)2075     public void adjustNumRequiredFlexibleConstraints(int adjustment) {
2076         mNumDroppedFlexibleConstraints = Math.max(0, Math.min(mNumRequiredFlexibleConstraints,
2077                 mNumDroppedFlexibleConstraints - adjustment));
2078     }
2079 
2080     /**
2081      * Add additional constraints to prevent this job from running when doze or battery saver are
2082      * active.
2083      */
disallowRunInBatterySaverAndDoze()2084     public void disallowRunInBatterySaverAndDoze() {
2085         addDynamicConstraints(DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS);
2086     }
2087 
2088     /**
2089      * Indicates that this job cannot run without the specified constraints. This is evaluated
2090      * separately from the job's explicitly requested constraints and MUST be satisfied before
2091      * the job can run if the app doesn't have quota.
2092      */
2093     @VisibleForTesting
addDynamicConstraints(int constraints)2094     public void addDynamicConstraints(int constraints) {
2095         if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
2096             // Quota should never be used as a dynamic constraint.
2097             Slog.wtf(TAG, "Tried to set quota as a dynamic constraint");
2098             constraints &= ~CONSTRAINT_WITHIN_QUOTA;
2099         }
2100         if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) {
2101             // Quota should never be used as a dynamic constraint.
2102             Slog.wtf(TAG, "Tried to set TARE as a dynamic constraint");
2103             constraints &= ~CONSTRAINT_TARE_WEALTH;
2104         }
2105 
2106         // Connectivity and content trigger are special since they're only valid to add if the
2107         // job has requested network or specific content URIs. Adding these constraints to jobs
2108         // that don't need them doesn't make sense.
2109         if (!hasConnectivityConstraint()) {
2110             constraints &= ~CONSTRAINT_CONNECTIVITY;
2111         }
2112         if (!hasContentTriggerConstraint()) {
2113             constraints &= ~CONSTRAINT_CONTENT_TRIGGER;
2114         }
2115 
2116         mDynamicConstraints |= constraints;
2117         mReadyDynamicSatisfied = mDynamicConstraints != 0
2118                 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
2119     }
2120 
2121     /**
2122      * Removes dynamic constraints from a job, meaning that the requirements are not required for
2123      * the job to run (if the job itself hasn't requested the constraint. This is separate from
2124      * the job's explicitly requested constraints and does not remove those requested constraints.
2125      *
2126      */
removeDynamicConstraints(int constraints)2127     private void removeDynamicConstraints(int constraints) {
2128         mDynamicConstraints &= ~constraints;
2129         mReadyDynamicSatisfied = mDynamicConstraints != 0
2130                 && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
2131     }
2132 
getLastSuccessfulRunTime()2133     public long getLastSuccessfulRunTime() {
2134         return mLastSuccessfulRunTime;
2135     }
2136 
getLastFailedRunTime()2137     public long getLastFailedRunTime() {
2138         return mLastFailedRunTime;
2139     }
2140 
2141     /**
2142      * @return Whether or not this job is ready to run, based on its requirements.
2143      */
isReady()2144     public boolean isReady() {
2145         return isReady(mSatisfiedConstraintsOfInterest);
2146     }
2147 
2148     /**
2149      * @return Whether or not this job would be ready to run if it had the specified constraint
2150      * granted, based on its requirements.
2151      */
wouldBeReadyWithConstraint(int constraint)2152     boolean wouldBeReadyWithConstraint(int constraint) {
2153         return readinessStatusWithConstraint(constraint, true);
2154     }
2155 
2156     @VisibleForTesting
readinessStatusWithConstraint(int constraint, boolean value)2157     boolean readinessStatusWithConstraint(int constraint, boolean value) {
2158         boolean oldValue = false;
2159         int satisfied = mSatisfiedConstraintsOfInterest;
2160         switch (constraint) {
2161             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
2162                 oldValue = mReadyNotRestrictedInBg;
2163                 mReadyNotRestrictedInBg = value;
2164                 break;
2165             case CONSTRAINT_DEADLINE:
2166                 oldValue = mReadyDeadlineSatisfied;
2167                 mReadyDeadlineSatisfied = value;
2168                 break;
2169             case CONSTRAINT_DEVICE_NOT_DOZING:
2170                 oldValue = mReadyNotDozing;
2171                 mReadyNotDozing = value;
2172                 break;
2173             case CONSTRAINT_TARE_WEALTH:
2174                 oldValue = mReadyTareWealth;
2175                 mReadyTareWealth = value;
2176                 break;
2177             case CONSTRAINT_WITHIN_QUOTA:
2178                 oldValue = mReadyWithinQuota;
2179                 mReadyWithinQuota = value;
2180                 break;
2181             default:
2182                 if (value) {
2183                     satisfied |= constraint;
2184                 } else {
2185                     satisfied &= ~constraint;
2186                 }
2187                 mReadyDynamicSatisfied = mDynamicConstraints != 0
2188                         && mDynamicConstraints == (satisfied & mDynamicConstraints);
2189 
2190                 break;
2191         }
2192 
2193         // The flexibility constraint relies on other constraints to be satisfied.
2194         // This function lacks the information to determine if flexibility will be satisfied.
2195         // But for the purposes of this function it is still useful to know the jobs' readiness
2196         // not including the flexibility constraint. If flexibility is the constraint in question
2197         // we can proceed as normal.
2198         if (constraint != CONSTRAINT_FLEXIBLE) {
2199             satisfied |= CONSTRAINT_FLEXIBLE;
2200         }
2201 
2202         boolean toReturn = isReady(satisfied);
2203 
2204         switch (constraint) {
2205             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
2206                 mReadyNotRestrictedInBg = oldValue;
2207                 break;
2208             case CONSTRAINT_DEADLINE:
2209                 mReadyDeadlineSatisfied = oldValue;
2210                 break;
2211             case CONSTRAINT_DEVICE_NOT_DOZING:
2212                 mReadyNotDozing = oldValue;
2213                 break;
2214             case CONSTRAINT_TARE_WEALTH:
2215                 mReadyTareWealth = oldValue;
2216                 break;
2217             case CONSTRAINT_WITHIN_QUOTA:
2218                 mReadyWithinQuota = oldValue;
2219                 break;
2220             default:
2221                 mReadyDynamicSatisfied = mDynamicConstraints != 0
2222                         && mDynamicConstraints == (satisfiedConstraints & mDynamicConstraints);
2223                 break;
2224         }
2225         return toReturn;
2226     }
2227 
isReady(int satisfiedConstraints)2228     private boolean isReady(int satisfiedConstraints) {
2229         // Quota and dynamic constraints trump all other constraints.
2230         // NEVER jobs are not supposed to run at all. Since we're using quota to allow parole
2231         // sessions (exempt from dynamic restrictions), we need the additional check to ensure
2232         // that NEVER jobs don't run.
2233         // TODO: cleanup quota and standby bucket management so we don't need the additional checks
2234         if (((!mReadyWithinQuota || !mReadyTareWealth)
2235                 && !mReadyDynamicSatisfied && !shouldTreatAsExpeditedJob())
2236                 || getEffectiveStandbyBucket() == NEVER_INDEX) {
2237             return false;
2238         }
2239         // Deadline constraint trumps other constraints besides quota and dynamic (except for
2240         // periodic jobs where deadline is an implementation detail. A periodic job should only
2241         // run if its constraints are satisfied).
2242         // DeviceNotDozing implicit constraint must be satisfied
2243         // NotRestrictedInBackground implicit constraint must be satisfied
2244         return mReadyNotDozing && mReadyNotRestrictedInBg && (serviceProcessName != null)
2245                 && (mReadyDeadlineSatisfied || isConstraintsSatisfied(satisfiedConstraints));
2246     }
2247 
2248     /** All constraints besides implicit and deadline. */
2249     static final int CONSTRAINTS_OF_INTEREST = CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW
2250             | CONSTRAINT_STORAGE_NOT_LOW | CONSTRAINT_TIMING_DELAY | CONSTRAINT_CONNECTIVITY
2251             | CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER | CONSTRAINT_PREFETCH
2252             | CONSTRAINT_FLEXIBLE;
2253 
2254     // Soft override covers all non-"functional" constraints
2255     static final int SOFT_OVERRIDE_CONSTRAINTS =
2256             CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW
2257                     | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE | CONSTRAINT_PREFETCH
2258                     | CONSTRAINT_FLEXIBLE;
2259 
2260     /** Returns true whenever all dynamically set constraints are satisfied. */
areDynamicConstraintsSatisfied()2261     public boolean areDynamicConstraintsSatisfied() {
2262         return mReadyDynamicSatisfied;
2263     }
2264 
2265     /**
2266      * @return Whether the constraints set on this job are satisfied.
2267      */
isConstraintsSatisfied()2268     public boolean isConstraintsSatisfied() {
2269         return isConstraintsSatisfied(mSatisfiedConstraintsOfInterest);
2270     }
2271 
isConstraintsSatisfied(int satisfiedConstraints)2272     private boolean isConstraintsSatisfied(int satisfiedConstraints) {
2273         if (overrideState == OVERRIDE_FULL) {
2274             // force override: the job is always runnable
2275             return true;
2276         }
2277 
2278         int sat = satisfiedConstraints;
2279         if (overrideState == OVERRIDE_SOFT) {
2280             // override: pretend all 'soft' requirements are satisfied
2281             sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
2282         }
2283 
2284         return (sat & mRequiredConstraintsOfInterest) == mRequiredConstraintsOfInterest;
2285     }
2286 
2287     /**
2288      * Returns true if the given parameters match this job's unique identifier.
2289      */
matches(int uid, @Nullable String namespace, int jobId)2290     public boolean matches(int uid, @Nullable String namespace, int jobId) {
2291         return this.job.getId() == jobId && this.callingUid == uid
2292                 && Objects.equals(mNamespace, namespace);
2293     }
2294 
2295     @Override
toString()2296     public String toString() {
2297         StringBuilder sb = new StringBuilder(128);
2298         sb.append("JobStatus{");
2299         sb.append(Integer.toHexString(System.identityHashCode(this)));
2300         if (mNamespace != null) {
2301             sb.append(" ");
2302             sb.append(mNamespace);
2303             sb.append(":");
2304         } else {
2305             sb.append(" #");
2306         }
2307         UserHandle.formatUid(sb, callingUid);
2308         sb.append("/");
2309         sb.append(job.getId());
2310         sb.append(' ');
2311         sb.append(batteryName);
2312         sb.append(" u=");
2313         sb.append(getUserId());
2314         sb.append(" s=");
2315         sb.append(getSourceUid());
2316         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME
2317                 || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
2318             long now = sElapsedRealtimeClock.millis();
2319             sb.append(" TIME=");
2320             formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now);
2321             sb.append(":");
2322             formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now);
2323         }
2324         if (job.getRequiredNetwork() != null) {
2325             sb.append(" NET");
2326         }
2327         if (job.isRequireCharging()) {
2328             sb.append(" CHARGING");
2329         }
2330         if (job.isRequireBatteryNotLow()) {
2331             sb.append(" BATNOTLOW");
2332         }
2333         if (job.isRequireStorageNotLow()) {
2334             sb.append(" STORENOTLOW");
2335         }
2336         if (job.isRequireDeviceIdle()) {
2337             sb.append(" IDLE");
2338         }
2339         if (job.isPeriodic()) {
2340             sb.append(" PERIODIC");
2341         }
2342         if (job.isPersisted()) {
2343             sb.append(" PERSISTED");
2344         }
2345         if ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) == 0) {
2346             sb.append(" WAIT:DEV_NOT_DOZING");
2347         }
2348         if (job.getTriggerContentUris() != null) {
2349             sb.append(" URIS=");
2350             sb.append(Arrays.toString(job.getTriggerContentUris()));
2351         }
2352         if (numFailures != 0) {
2353             sb.append(" failures=");
2354             sb.append(numFailures);
2355         }
2356         if (mNumSystemStops != 0) {
2357             sb.append(" system stops=");
2358             sb.append(mNumSystemStops);
2359         }
2360         if (isReady()) {
2361             sb.append(" READY");
2362         } else {
2363             sb.append(" satisfied:0x").append(Integer.toHexString(satisfiedConstraints));
2364             final int requiredConstraints = mRequiredConstraintsOfInterest | IMPLICIT_CONSTRAINTS;
2365             sb.append(" unsatisfied:0x").append(Integer.toHexString(
2366                     (satisfiedConstraints & requiredConstraints) ^ requiredConstraints));
2367         }
2368         sb.append("}");
2369         return sb.toString();
2370     }
2371 
formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now)2372     private void formatRunTime(PrintWriter pw, long runtime, long  defaultValue, long now) {
2373         if (runtime == defaultValue) {
2374             pw.print("none");
2375         } else {
2376             TimeUtils.formatDuration(runtime - now, pw);
2377         }
2378     }
2379 
formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now)2380     private void formatRunTime(StringBuilder sb, long runtime, long  defaultValue, long now) {
2381         if (runtime == defaultValue) {
2382             sb.append("none");
2383         } else {
2384             TimeUtils.formatDuration(runtime - now, sb);
2385         }
2386     }
2387 
2388     /**
2389      * Convenience function to identify a job uniquely without pulling all the data that
2390      * {@link #toString()} returns.
2391      */
toShortString()2392     public String toShortString() {
2393         StringBuilder sb = new StringBuilder();
2394         sb.append(Integer.toHexString(System.identityHashCode(this)));
2395         if (mNamespace != null) {
2396             sb.append(" {").append(mNamespace).append("}");
2397         }
2398         sb.append(" #");
2399         UserHandle.formatUid(sb, callingUid);
2400         sb.append("/");
2401         sb.append(job.getId());
2402         sb.append(' ');
2403         sb.append(batteryName);
2404         return sb.toString();
2405     }
2406 
2407     /**
2408      * Convenience function to identify a job uniquely without pulling all the data that
2409      * {@link #toString()} returns.
2410      */
toShortStringExceptUniqueId()2411     public String toShortStringExceptUniqueId() {
2412         StringBuilder sb = new StringBuilder();
2413         sb.append(Integer.toHexString(System.identityHashCode(this)));
2414         sb.append(' ');
2415         sb.append(batteryName);
2416         return sb.toString();
2417     }
2418 
2419     /**
2420      * Convenience function to dump data that identifies a job uniquely to proto. This is intended
2421      * to mimic {@link #toShortString}.
2422      */
writeToShortProto(ProtoOutputStream proto, long fieldId)2423     public void writeToShortProto(ProtoOutputStream proto, long fieldId) {
2424         final long token = proto.start(fieldId);
2425 
2426         proto.write(JobStatusShortInfoProto.CALLING_UID, callingUid);
2427         proto.write(JobStatusShortInfoProto.JOB_ID, job.getId());
2428         proto.write(JobStatusShortInfoProto.BATTERY_NAME, batteryName);
2429 
2430         proto.end(token);
2431     }
2432 
dumpConstraints(PrintWriter pw, int constraints)2433     static void dumpConstraints(PrintWriter pw, int constraints) {
2434         if ((constraints & CONSTRAINT_CHARGING) != 0) {
2435             pw.print(" CHARGING");
2436         }
2437         if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
2438             pw.print(" BATTERY_NOT_LOW");
2439         }
2440         if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
2441             pw.print(" STORAGE_NOT_LOW");
2442         }
2443         if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
2444             pw.print(" TIMING_DELAY");
2445         }
2446         if ((constraints & CONSTRAINT_DEADLINE) != 0) {
2447             pw.print(" DEADLINE");
2448         }
2449         if ((constraints & CONSTRAINT_IDLE) != 0) {
2450             pw.print(" IDLE");
2451         }
2452         if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
2453             pw.print(" CONNECTIVITY");
2454         }
2455         if ((constraints & CONSTRAINT_FLEXIBLE) != 0) {
2456             pw.print(" FLEXIBILITY");
2457         }
2458         if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
2459             pw.print(" CONTENT_TRIGGER");
2460         }
2461         if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
2462             pw.print(" DEVICE_NOT_DOZING");
2463         }
2464         if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
2465             pw.print(" BACKGROUND_NOT_RESTRICTED");
2466         }
2467         if ((constraints & CONSTRAINT_PREFETCH) != 0) {
2468             pw.print(" PREFETCH");
2469         }
2470         if ((constraints & CONSTRAINT_TARE_WEALTH) != 0) {
2471             pw.print(" TARE_WEALTH");
2472         }
2473         if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
2474             pw.print(" WITHIN_QUOTA");
2475         }
2476         if (constraints != 0) {
2477             pw.print(" [0x");
2478             pw.print(Integer.toHexString(constraints));
2479             pw.print("]");
2480         }
2481     }
2482 
2483     /** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */
getProtoConstraint(int constraint)2484     static int getProtoConstraint(int constraint) {
2485         switch (constraint) {
2486             case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
2487                 return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
2488             case CONSTRAINT_BATTERY_NOT_LOW:
2489                 return JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW;
2490             case CONSTRAINT_CHARGING:
2491                 return JobServerProtoEnums.CONSTRAINT_CHARGING;
2492             case CONSTRAINT_CONNECTIVITY:
2493                 return JobServerProtoEnums.CONSTRAINT_CONNECTIVITY;
2494             case CONSTRAINT_CONTENT_TRIGGER:
2495                 return JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER;
2496             case CONSTRAINT_DEADLINE:
2497                 return JobServerProtoEnums.CONSTRAINT_DEADLINE;
2498             case CONSTRAINT_DEVICE_NOT_DOZING:
2499                 return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING;
2500             case CONSTRAINT_FLEXIBLE:
2501                 return JobServerProtoEnums.CONSTRAINT_FLEXIBILITY;
2502             case CONSTRAINT_IDLE:
2503                 return JobServerProtoEnums.CONSTRAINT_IDLE;
2504             case CONSTRAINT_PREFETCH:
2505                 return JobServerProtoEnums.CONSTRAINT_PREFETCH;
2506             case CONSTRAINT_STORAGE_NOT_LOW:
2507                 return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW;
2508             case CONSTRAINT_TARE_WEALTH:
2509                 return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH;
2510             case CONSTRAINT_TIMING_DELAY:
2511                 return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY;
2512             case CONSTRAINT_WITHIN_QUOTA:
2513                 return JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA;
2514             default:
2515                 return JobServerProtoEnums.CONSTRAINT_UNKNOWN;
2516         }
2517     }
2518 
2519     /** Writes constraints to the given repeating proto field. */
dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints)2520     void dumpConstraints(ProtoOutputStream proto, long fieldId, int constraints) {
2521         if ((constraints & CONSTRAINT_CHARGING) != 0) {
2522             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CHARGING);
2523         }
2524         if ((constraints & CONSTRAINT_BATTERY_NOT_LOW) != 0) {
2525             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BATTERY_NOT_LOW);
2526         }
2527         if ((constraints & CONSTRAINT_STORAGE_NOT_LOW) != 0) {
2528             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW);
2529         }
2530         if ((constraints & CONSTRAINT_TIMING_DELAY) != 0) {
2531             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_TIMING_DELAY);
2532         }
2533         if ((constraints & CONSTRAINT_DEADLINE) != 0) {
2534             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEADLINE);
2535         }
2536         if ((constraints & CONSTRAINT_IDLE) != 0) {
2537             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_IDLE);
2538         }
2539         if ((constraints & CONSTRAINT_CONNECTIVITY) != 0) {
2540             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONNECTIVITY);
2541         }
2542         if ((constraints & CONSTRAINT_CONTENT_TRIGGER) != 0) {
2543             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_CONTENT_TRIGGER);
2544         }
2545         if ((constraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
2546             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING);
2547         }
2548         if ((constraints & CONSTRAINT_WITHIN_QUOTA) != 0) {
2549             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_WITHIN_QUOTA);
2550         }
2551         if ((constraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
2552             proto.write(fieldId, JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED);
2553         }
2554     }
2555 
dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index)2556     private void dumpJobWorkItem(IndentingPrintWriter pw, JobWorkItem work, int index) {
2557         pw.increaseIndent();
2558         pw.print("#"); pw.print(index); pw.print(": #");
2559         pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount());
2560         pw.print("x "); pw.println(work.getIntent());
2561         if (work.getGrants() != null) {
2562             pw.println("URI grants:");
2563             pw.increaseIndent();
2564             ((GrantedUriPermissions) work.getGrants()).dump(pw);
2565             pw.decreaseIndent();
2566         }
2567         pw.decreaseIndent();
2568     }
2569 
dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work)2570     private void dumpJobWorkItem(ProtoOutputStream proto, long fieldId, JobWorkItem work) {
2571         final long token = proto.start(fieldId);
2572 
2573         proto.write(JobStatusDumpProto.JobWorkItem.WORK_ID, work.getWorkId());
2574         proto.write(JobStatusDumpProto.JobWorkItem.DELIVERY_COUNT, work.getDeliveryCount());
2575         if (work.getIntent() != null) {
2576             work.getIntent().dumpDebug(proto, JobStatusDumpProto.JobWorkItem.INTENT);
2577         }
2578         Object grants = work.getGrants();
2579         if (grants != null) {
2580             ((GrantedUriPermissions) grants).dump(proto, JobStatusDumpProto.JobWorkItem.URI_GRANTS);
2581         }
2582 
2583         proto.end(token);
2584     }
2585 
2586     /**
2587      * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
2588      */
getBucketName()2589     String getBucketName() {
2590         return bucketName(standbyBucket);
2591     }
2592 
2593     /**
2594      * Returns a bucket name based on the normalized bucket indices, not the AppStandby constants.
2595      */
bucketName(int standbyBucket)2596     static String bucketName(int standbyBucket) {
2597         switch (standbyBucket) {
2598             case 0: return "ACTIVE";
2599             case 1: return "WORKING_SET";
2600             case 2: return "FREQUENT";
2601             case 3: return "RARE";
2602             case 4: return "NEVER";
2603             case 5: return "RESTRICTED";
2604             case 6: return "EXEMPTED";
2605             default:
2606                 return "Unknown: " + standbyBucket;
2607         }
2608     }
2609 
2610     // Dumpsys infrastructure
2611     @NeverCompile // Avoid size overhead of debugging code.
dump(IndentingPrintWriter pw, boolean full, long nowElapsed)2612     public void dump(IndentingPrintWriter pw,  boolean full, long nowElapsed) {
2613         UserHandle.formatUid(pw, callingUid);
2614         pw.print(" tag="); pw.println(tag);
2615 
2616         pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
2617         pw.print(" user="); pw.print(getSourceUserId());
2618         pw.print(" pkg="); pw.println(getSourcePackageName());
2619         if (full) {
2620             pw.println("JobInfo:");
2621             pw.increaseIndent();
2622 
2623             pw.print("Service: ");
2624             pw.println(job.getService().flattenToShortString());
2625             if (job.isPeriodic()) {
2626                 pw.print("PERIODIC: interval=");
2627                 TimeUtils.formatDuration(job.getIntervalMillis(), pw);
2628                 pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw);
2629                 pw.println();
2630             }
2631             if (job.isPersisted()) {
2632                 pw.println("PERSISTED");
2633             }
2634             if (job.getBias() != 0) {
2635                 pw.print("Bias: ");
2636                 pw.println(JobInfo.getBiasString(job.getBias()));
2637             }
2638             pw.print("Priority: ");
2639             pw.print(JobInfo.getPriorityString(job.getPriority()));
2640             final int effectivePriority = getEffectivePriority();
2641             if (effectivePriority != job.getPriority()) {
2642                 pw.print(" effective=");
2643                 pw.print(JobInfo.getPriorityString(effectivePriority));
2644             }
2645             pw.println();
2646             if (job.getFlags() != 0) {
2647                 pw.print("Flags: ");
2648                 pw.println(Integer.toHexString(job.getFlags()));
2649             }
2650             if (getInternalFlags() != 0) {
2651                 pw.print("Internal flags: ");
2652                 pw.print(Integer.toHexString(getInternalFlags()));
2653 
2654                 if ((getInternalFlags()&INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0) {
2655                     pw.print(" HAS_FOREGROUND_EXEMPTION");
2656                 }
2657                 pw.println();
2658             }
2659             pw.print("Requires: charging=");
2660             pw.print(job.isRequireCharging()); pw.print(" batteryNotLow=");
2661             pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle=");
2662             pw.println(job.isRequireDeviceIdle());
2663             if (job.getTriggerContentUris() != null) {
2664                 pw.println("Trigger content URIs:");
2665                 pw.increaseIndent();
2666                 for (int i = 0; i < job.getTriggerContentUris().length; i++) {
2667                     JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
2668                     pw.print(Integer.toHexString(trig.getFlags()));
2669                     pw.print(' '); pw.println(trig.getUri());
2670                 }
2671                 pw.decreaseIndent();
2672                 if (job.getTriggerContentUpdateDelay() >= 0) {
2673                     pw.print("Trigger update delay: ");
2674                     TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw);
2675                     pw.println();
2676                 }
2677                 if (job.getTriggerContentMaxDelay() >= 0) {
2678                     pw.print("Trigger max delay: ");
2679                     TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw);
2680                     pw.println();
2681                 }
2682                 pw.print("Has media backup exemption", mHasMediaBackupExemption).println();
2683             }
2684             if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
2685                 pw.print("Extras: ");
2686                 pw.println(job.getExtras().toShortString());
2687             }
2688             if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
2689                 pw.print("Transient extras: ");
2690                 pw.println(job.getTransientExtras().toShortString());
2691             }
2692             if (job.getClipData() != null) {
2693                 pw.print("Clip data: ");
2694                 StringBuilder b = new StringBuilder(128);
2695                 b.append(job.getClipData());
2696                 pw.println(b);
2697             }
2698             if (uriPerms != null) {
2699                 pw.println("Granted URI permissions:");
2700                 uriPerms.dump(pw);
2701             }
2702             if (job.getRequiredNetwork() != null) {
2703                 pw.print("Network type: ");
2704                 pw.println(job.getRequiredNetwork());
2705             }
2706             if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2707                 pw.print("Network download bytes: ");
2708                 pw.println(mTotalNetworkDownloadBytes);
2709             }
2710             if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2711                 pw.print("Network upload bytes: ");
2712                 pw.println(mTotalNetworkUploadBytes);
2713             }
2714             if (mMinimumNetworkChunkBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2715                 pw.print("Minimum network chunk bytes: ");
2716                 pw.println(mMinimumNetworkChunkBytes);
2717             }
2718             if (job.getMinLatencyMillis() != 0) {
2719                 pw.print("Minimum latency: ");
2720                 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
2721                 pw.println();
2722             }
2723             if (job.getMaxExecutionDelayMillis() != 0) {
2724                 pw.print("Max execution delay: ");
2725                 TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
2726                 pw.println();
2727             }
2728             pw.print("Backoff: policy="); pw.print(job.getBackoffPolicy());
2729             pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
2730             pw.println();
2731             if (job.hasEarlyConstraint()) {
2732                 pw.println("Has early constraint");
2733             }
2734             if (job.hasLateConstraint()) {
2735                 pw.println("Has late constraint");
2736             }
2737 
2738             pw.decreaseIndent();
2739         }
2740 
2741         pw.print("Required constraints:");
2742         dumpConstraints(pw, requiredConstraints);
2743         pw.println();
2744         pw.print("Preferred constraints:");
2745         dumpConstraints(pw, mPreferredConstraints);
2746         pw.println();
2747         pw.print("Dynamic constraints:");
2748         dumpConstraints(pw, mDynamicConstraints);
2749         pw.println();
2750         if (full) {
2751             pw.print("Satisfied constraints:");
2752             dumpConstraints(pw, satisfiedConstraints);
2753             pw.println();
2754             pw.print("Unsatisfied constraints:");
2755             dumpConstraints(pw,
2756                     ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA | CONSTRAINT_TARE_WEALTH)
2757                             & ~satisfiedConstraints));
2758             pw.println();
2759             if (hasFlexibilityConstraint()) {
2760                 pw.print("Num Required Flexible constraints: ");
2761                 pw.print(getNumRequiredFlexibleConstraints());
2762                 pw.println();
2763                 pw.print("Num Dropped Flexible constraints: ");
2764                 pw.print(getNumDroppedFlexibleConstraints());
2765                 pw.println();
2766             }
2767 
2768             pw.println("Constraint history:");
2769             pw.increaseIndent();
2770             for (int h = 0; h < NUM_CONSTRAINT_CHANGE_HISTORY; ++h) {
2771                 final int idx = (h + mConstraintChangeHistoryIndex) % NUM_CONSTRAINT_CHANGE_HISTORY;
2772                 if (mConstraintUpdatedTimesElapsed[idx] == 0) {
2773                     continue;
2774                 }
2775                 TimeUtils.formatDuration(mConstraintUpdatedTimesElapsed[idx], nowElapsed, pw);
2776                 // dumpConstraints prepends with a space, so no need to add a space after the =
2777                 pw.print(" =");
2778                 dumpConstraints(pw, mConstraintStatusHistory[idx]);
2779                 pw.println();
2780             }
2781             pw.decreaseIndent();
2782 
2783             if (appHasDozeExemption) {
2784                 pw.println("Doze whitelisted: true");
2785             }
2786             if (uidActive) {
2787                 pw.println("Uid: active");
2788             }
2789             if (job.isExemptedFromAppStandby()) {
2790                 pw.println("Is exempted from app standby");
2791             }
2792         }
2793         if (trackingControllers != 0) {
2794             pw.print("Tracking:");
2795             if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY");
2796             if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY");
2797             if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT");
2798             if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE");
2799             if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE");
2800             if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME");
2801             if ((trackingControllers & TRACKING_QUOTA) != 0) pw.print(" QUOTA");
2802             pw.println();
2803         }
2804 
2805         pw.println("Implicit constraints:");
2806         pw.increaseIndent();
2807         pw.print("readyNotDozing: ");
2808         pw.println(mReadyNotDozing);
2809         pw.print("readyNotRestrictedInBg: ");
2810         pw.println(mReadyNotRestrictedInBg);
2811         if (!job.isPeriodic() && hasDeadlineConstraint()) {
2812             pw.print("readyDeadlineSatisfied: ");
2813             pw.println(mReadyDeadlineSatisfied);
2814         }
2815         if (mDynamicConstraints != 0) {
2816             pw.print("readyDynamicSatisfied: ");
2817             pw.println(mReadyDynamicSatisfied);
2818         }
2819         pw.print("readyComponentEnabled: ");
2820         pw.println(serviceProcessName != null);
2821         if ((getFlags() & JobInfo.FLAG_EXPEDITED) != 0) {
2822             pw.print("expeditedQuotaApproved: ");
2823             pw.print(mExpeditedQuotaApproved);
2824             pw.print(" expeditedTareApproved: ");
2825             pw.print(mExpeditedTareApproved);
2826             pw.print(" (started as EJ: ");
2827             pw.print(startedAsExpeditedJob);
2828             pw.println(")");
2829         }
2830         if ((getFlags() & JobInfo.FLAG_USER_INITIATED) != 0) {
2831             pw.print("userInitiatedApproved: ");
2832             pw.print(shouldTreatAsUserInitiatedJob());
2833             pw.print(" (started as UIJ: ");
2834             pw.print(startedAsUserInitiatedJob);
2835             pw.println(")");
2836         }
2837         pw.decreaseIndent();
2838 
2839         pw.print("Started with foreground flag: ");
2840         pw.println(startedWithForegroundFlag);
2841         if (mIsUserBgRestricted) {
2842             pw.println("User BG restricted");
2843         }
2844 
2845         if (changedAuthorities != null) {
2846             pw.println("Changed authorities:");
2847             pw.increaseIndent();
2848             for (int i=0; i<changedAuthorities.size(); i++) {
2849                 pw.println(changedAuthorities.valueAt(i));
2850             }
2851             pw.decreaseIndent();
2852         }
2853         if (changedUris != null) {
2854             pw.println("Changed URIs:");
2855             pw.increaseIndent();
2856             for (int i = 0; i < changedUris.size(); i++) {
2857                 pw.println(changedUris.valueAt(i));
2858             }
2859             pw.decreaseIndent();
2860         }
2861         if (network != null) {
2862             pw.print("Network: "); pw.println(network);
2863         }
2864         if (pendingWork != null && pendingWork.size() > 0) {
2865             pw.println("Pending work:");
2866             for (int i = 0; i < pendingWork.size(); i++) {
2867                 dumpJobWorkItem(pw, pendingWork.get(i), i);
2868             }
2869         }
2870         if (executingWork != null && executingWork.size() > 0) {
2871             pw.println("Executing work:");
2872             for (int i = 0; i < executingWork.size(); i++) {
2873                 dumpJobWorkItem(pw, executingWork.get(i), i);
2874             }
2875         }
2876         pw.print("Standby bucket: ");
2877         pw.println(getBucketName());
2878         pw.increaseIndent();
2879         if (whenStandbyDeferred != 0) {
2880             pw.print("Deferred since: ");
2881             TimeUtils.formatDuration(whenStandbyDeferred, nowElapsed, pw);
2882             pw.println();
2883         }
2884         if (mFirstForceBatchedTimeElapsed != 0) {
2885             pw.print("Time since first force batch attempt: ");
2886             TimeUtils.formatDuration(mFirstForceBatchedTimeElapsed, nowElapsed, pw);
2887             pw.println();
2888         }
2889         pw.decreaseIndent();
2890 
2891         pw.print("Enqueue time: ");
2892         TimeUtils.formatDuration(enqueueTime, nowElapsed, pw);
2893         pw.println();
2894         pw.print("Run time: earliest=");
2895         formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, nowElapsed);
2896         pw.print(", latest=");
2897         formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed);
2898         pw.print(", original latest=");
2899         formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed);
2900         pw.println();
2901         if (mCumulativeExecutionTimeMs != 0) {
2902             pw.print("Cumulative execution time=");
2903             TimeUtils.formatDuration(mCumulativeExecutionTimeMs, pw);
2904             pw.println();
2905         }
2906         if (numFailures != 0) {
2907             pw.print("Num failures: "); pw.println(numFailures);
2908         }
2909         if (mNumSystemStops != 0) {
2910             pw.print("Num system stops: "); pw.println(mNumSystemStops);
2911         }
2912         if (mLastSuccessfulRunTime != 0) {
2913             pw.print("Last successful run: ");
2914             pw.println(formatTime(mLastSuccessfulRunTime));
2915         }
2916         if (mLastFailedRunTime != 0) {
2917             pw.print("Last failed run: ");
2918             pw.println(formatTime(mLastFailedRunTime));
2919         }
2920     }
2921 
formatTime(long time)2922     private static CharSequence formatTime(long time) {
2923         return DateFormat.format("yyyy-MM-dd HH:mm:ss", time);
2924     }
2925 
dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis)2926     public void dump(ProtoOutputStream proto, long fieldId, boolean full, long elapsedRealtimeMillis) {
2927         final long token = proto.start(fieldId);
2928 
2929         proto.write(JobStatusDumpProto.CALLING_UID, callingUid);
2930         proto.write(JobStatusDumpProto.TAG, tag);
2931         proto.write(JobStatusDumpProto.SOURCE_UID, getSourceUid());
2932         proto.write(JobStatusDumpProto.SOURCE_USER_ID, getSourceUserId());
2933         proto.write(JobStatusDumpProto.SOURCE_PACKAGE_NAME, getSourcePackageName());
2934 
2935         if (full) {
2936             final long jiToken = proto.start(JobStatusDumpProto.JOB_INFO);
2937 
2938             job.getService().dumpDebug(proto, JobStatusDumpProto.JobInfo.SERVICE);
2939 
2940             proto.write(JobStatusDumpProto.JobInfo.IS_PERIODIC, job.isPeriodic());
2941             proto.write(JobStatusDumpProto.JobInfo.PERIOD_INTERVAL_MS, job.getIntervalMillis());
2942             proto.write(JobStatusDumpProto.JobInfo.PERIOD_FLEX_MS, job.getFlexMillis());
2943 
2944             proto.write(JobStatusDumpProto.JobInfo.IS_PERSISTED, job.isPersisted());
2945             proto.write(JobStatusDumpProto.JobInfo.PRIORITY, job.getBias());
2946             proto.write(JobStatusDumpProto.JobInfo.FLAGS, job.getFlags());
2947             proto.write(JobStatusDumpProto.INTERNAL_FLAGS, getInternalFlags());
2948             // Foreground exemption can be determined from internal flags value.
2949 
2950             proto.write(JobStatusDumpProto.JobInfo.REQUIRES_CHARGING, job.isRequireCharging());
2951             proto.write(JobStatusDumpProto.JobInfo.REQUIRES_BATTERY_NOT_LOW, job.isRequireBatteryNotLow());
2952             proto.write(JobStatusDumpProto.JobInfo.REQUIRES_DEVICE_IDLE, job.isRequireDeviceIdle());
2953 
2954             if (job.getTriggerContentUris() != null) {
2955                 for (int i = 0; i < job.getTriggerContentUris().length; i++) {
2956                     final long tcuToken = proto.start(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_URIS);
2957                     JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
2958 
2959                     proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.FLAGS, trig.getFlags());
2960                     Uri u = trig.getUri();
2961                     if (u != null) {
2962                         proto.write(JobStatusDumpProto.JobInfo.TriggerContentUri.URI, u.toString());
2963                     }
2964 
2965                     proto.end(tcuToken);
2966                 }
2967                 if (job.getTriggerContentUpdateDelay() >= 0) {
2968                     proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_UPDATE_DELAY_MS,
2969                             job.getTriggerContentUpdateDelay());
2970                 }
2971                 if (job.getTriggerContentMaxDelay() >= 0) {
2972                     proto.write(JobStatusDumpProto.JobInfo.TRIGGER_CONTENT_MAX_DELAY_MS,
2973                             job.getTriggerContentMaxDelay());
2974                 }
2975             }
2976             if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
2977                 job.getExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.EXTRAS);
2978             }
2979             if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
2980                 job.getTransientExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS);
2981             }
2982             if (job.getClipData() != null) {
2983                 job.getClipData().dumpDebug(proto, JobStatusDumpProto.JobInfo.CLIP_DATA);
2984             }
2985             if (uriPerms != null) {
2986                 uriPerms.dump(proto, JobStatusDumpProto.JobInfo.GRANTED_URI_PERMISSIONS);
2987             }
2988             if (mTotalNetworkDownloadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2989                 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_DOWNLOAD_BYTES,
2990                         mTotalNetworkDownloadBytes);
2991             }
2992             if (mTotalNetworkUploadBytes != JobInfo.NETWORK_BYTES_UNKNOWN) {
2993                 proto.write(JobStatusDumpProto.JobInfo.TOTAL_NETWORK_UPLOAD_BYTES,
2994                         mTotalNetworkUploadBytes);
2995             }
2996             proto.write(JobStatusDumpProto.JobInfo.MIN_LATENCY_MS, job.getMinLatencyMillis());
2997             proto.write(JobStatusDumpProto.JobInfo.MAX_EXECUTION_DELAY_MS, job.getMaxExecutionDelayMillis());
2998 
2999             final long bpToken = proto.start(JobStatusDumpProto.JobInfo.BACKOFF_POLICY);
3000             proto.write(JobStatusDumpProto.JobInfo.Backoff.POLICY, job.getBackoffPolicy());
3001             proto.write(JobStatusDumpProto.JobInfo.Backoff.INITIAL_BACKOFF_MS,
3002                     job.getInitialBackoffMillis());
3003             proto.end(bpToken);
3004 
3005             proto.write(JobStatusDumpProto.JobInfo.HAS_EARLY_CONSTRAINT, job.hasEarlyConstraint());
3006             proto.write(JobStatusDumpProto.JobInfo.HAS_LATE_CONSTRAINT, job.hasLateConstraint());
3007 
3008             proto.end(jiToken);
3009         }
3010 
3011         dumpConstraints(proto, JobStatusDumpProto.REQUIRED_CONSTRAINTS, requiredConstraints);
3012         dumpConstraints(proto, JobStatusDumpProto.DYNAMIC_CONSTRAINTS, mDynamicConstraints);
3013         if (full) {
3014             dumpConstraints(proto, JobStatusDumpProto.SATISFIED_CONSTRAINTS, satisfiedConstraints);
3015             dumpConstraints(proto, JobStatusDumpProto.UNSATISFIED_CONSTRAINTS,
3016                     ((requiredConstraints | CONSTRAINT_WITHIN_QUOTA) & ~satisfiedConstraints));
3017             proto.write(JobStatusDumpProto.IS_DOZE_WHITELISTED, appHasDozeExemption);
3018             proto.write(JobStatusDumpProto.IS_UID_ACTIVE, uidActive);
3019             proto.write(JobStatusDumpProto.IS_EXEMPTED_FROM_APP_STANDBY,
3020                     job.isExemptedFromAppStandby());
3021         }
3022 
3023         // Tracking controllers
3024         if ((trackingControllers&TRACKING_BATTERY) != 0) {
3025             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
3026                     JobStatusDumpProto.TRACKING_BATTERY);
3027         }
3028         if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) {
3029             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
3030                     JobStatusDumpProto.TRACKING_CONNECTIVITY);
3031         }
3032         if ((trackingControllers&TRACKING_CONTENT) != 0) {
3033             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
3034                     JobStatusDumpProto.TRACKING_CONTENT);
3035         }
3036         if ((trackingControllers&TRACKING_IDLE) != 0) {
3037             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
3038                     JobStatusDumpProto.TRACKING_IDLE);
3039         }
3040         if ((trackingControllers&TRACKING_STORAGE) != 0) {
3041             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
3042                     JobStatusDumpProto.TRACKING_STORAGE);
3043         }
3044         if ((trackingControllers&TRACKING_TIME) != 0) {
3045             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
3046                     JobStatusDumpProto.TRACKING_TIME);
3047         }
3048         if ((trackingControllers & TRACKING_QUOTA) != 0) {
3049             proto.write(JobStatusDumpProto.TRACKING_CONTROLLERS,
3050                     JobStatusDumpProto.TRACKING_QUOTA);
3051         }
3052 
3053         // Implicit constraints
3054         final long icToken = proto.start(JobStatusDumpProto.IMPLICIT_CONSTRAINTS);
3055         proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_DOZING, mReadyNotDozing);
3056         proto.write(JobStatusDumpProto.ImplicitConstraints.IS_NOT_RESTRICTED_IN_BG,
3057                 mReadyNotRestrictedInBg);
3058         // mReadyDeadlineSatisfied isn't an implicit constraint...and can be determined from other
3059         // field values.
3060         proto.write(JobStatusDumpProto.ImplicitConstraints.IS_DYNAMIC_SATISFIED,
3061                 mReadyDynamicSatisfied);
3062         proto.end(icToken);
3063 
3064         if (changedAuthorities != null) {
3065             for (int k = 0; k < changedAuthorities.size(); k++) {
3066                 proto.write(JobStatusDumpProto.CHANGED_AUTHORITIES, changedAuthorities.valueAt(k));
3067             }
3068         }
3069         if (changedUris != null) {
3070             for (int i = 0; i < changedUris.size(); i++) {
3071                 Uri u = changedUris.valueAt(i);
3072                 proto.write(JobStatusDumpProto.CHANGED_URIS, u.toString());
3073             }
3074         }
3075 
3076         if (pendingWork != null) {
3077             for (int i = 0; i < pendingWork.size(); i++) {
3078                 dumpJobWorkItem(proto, JobStatusDumpProto.PENDING_WORK, pendingWork.get(i));
3079             }
3080         }
3081         if (executingWork != null) {
3082             for (int i = 0; i < executingWork.size(); i++) {
3083                 dumpJobWorkItem(proto, JobStatusDumpProto.EXECUTING_WORK, executingWork.get(i));
3084             }
3085         }
3086 
3087         proto.write(JobStatusDumpProto.STANDBY_BUCKET, standbyBucket);
3088         proto.write(JobStatusDumpProto.ENQUEUE_DURATION_MS, elapsedRealtimeMillis - enqueueTime);
3089         proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_DEFERRAL_MS,
3090                 whenStandbyDeferred == 0 ? 0 : elapsedRealtimeMillis - whenStandbyDeferred);
3091         proto.write(JobStatusDumpProto.TIME_SINCE_FIRST_FORCE_BATCH_ATTEMPT_MS,
3092                 mFirstForceBatchedTimeElapsed == 0
3093                         ? 0 : elapsedRealtimeMillis - mFirstForceBatchedTimeElapsed);
3094         if (earliestRunTimeElapsedMillis == NO_EARLIEST_RUNTIME) {
3095             proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS, 0);
3096         } else {
3097             proto.write(JobStatusDumpProto.TIME_UNTIL_EARLIEST_RUNTIME_MS,
3098                     earliestRunTimeElapsedMillis - elapsedRealtimeMillis);
3099         }
3100         if (latestRunTimeElapsedMillis == NO_LATEST_RUNTIME) {
3101             proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS, 0);
3102         } else {
3103             proto.write(JobStatusDumpProto.TIME_UNTIL_LATEST_RUNTIME_MS,
3104                     latestRunTimeElapsedMillis - elapsedRealtimeMillis);
3105         }
3106         proto.write(JobStatusDumpProto.ORIGINAL_LATEST_RUNTIME_ELAPSED,
3107                 mOriginalLatestRunTimeElapsedMillis);
3108 
3109         proto.write(JobStatusDumpProto.NUM_FAILURES, numFailures + mNumSystemStops);
3110         proto.write(JobStatusDumpProto.LAST_SUCCESSFUL_RUN_TIME, mLastSuccessfulRunTime);
3111         proto.write(JobStatusDumpProto.LAST_FAILED_RUN_TIME, mLastFailedRunTime);
3112 
3113         proto.end(token);
3114     }
3115 }
3116