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