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