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 android.app.AppGlobals;
20 import android.app.IActivityManager;
21 import android.app.job.JobInfo;
22 import android.app.job.JobWorkItem;
23 import android.content.ClipData;
24 import android.content.ComponentName;
25 import android.net.Uri;
26 import android.os.RemoteException;
27 import android.os.SystemClock;
28 import android.os.UserHandle;
29 import android.util.ArraySet;
30 import android.util.Slog;
31 import android.util.TimeUtils;
32 
33 import com.android.server.job.GrantedUriPermissions;
34 
35 import java.io.PrintWriter;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 
39 /**
40  * Uniquely identifies a job internally.
41  * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler.
42  * Contains current state of the requirements of the job, as well as a function to evaluate
43  * whether it's ready to run.
44  * This object is shared among the various controllers - hence why the different fields are atomic.
45  * This isn't strictly necessary because each controller is only interested in a specific field,
46  * and the receivers that are listening for global state change will all run on the main looper,
47  * but we don't enforce that so this is safer.
48  * @hide
49  */
50 public final class JobStatus {
51     static final String TAG = "JobSchedulerService";
52 
53     public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
54     public static final long NO_EARLIEST_RUNTIME = 0L;
55 
56     static final int CONSTRAINT_CHARGING = JobInfo.CONSTRAINT_FLAG_CHARGING;
57     static final int CONSTRAINT_IDLE = JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE;
58     static final int CONSTRAINT_BATTERY_NOT_LOW = JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW;
59     static final int CONSTRAINT_STORAGE_NOT_LOW = JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW;
60     static final int CONSTRAINT_TIMING_DELAY = 1<<31;
61     static final int CONSTRAINT_DEADLINE = 1<<30;
62     static final int CONSTRAINT_UNMETERED = 1<<29;
63     static final int CONSTRAINT_CONNECTIVITY = 1<<28;
64     static final int CONSTRAINT_APP_NOT_IDLE = 1<<27;
65     static final int CONSTRAINT_CONTENT_TRIGGER = 1<<26;
66     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25;
67     static final int CONSTRAINT_NOT_ROAMING = 1<<24;
68     static final int CONSTRAINT_METERED = 1<<23;
69 
70     static final int CONNECTIVITY_MASK =
71             CONSTRAINT_UNMETERED | CONSTRAINT_CONNECTIVITY |
72             CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED;
73 
74     // Soft override: ignore constraints like time that don't affect API availability
75     public static final int OVERRIDE_SOFT = 1;
76     // Full override: ignore all constraints including API-affecting like connectivity
77     public static final int OVERRIDE_FULL = 2;
78 
79     /** If not specified, trigger update delay is 10 seconds. */
80     public static final long DEFAULT_TRIGGER_UPDATE_DELAY = 10*1000;
81 
82     /** The minimum possible update delay is 1/2 second. */
83     public static final long MIN_TRIGGER_UPDATE_DELAY = 500;
84 
85     /** If not specified, trigger maxumum delay is 2 minutes. */
86     public static final long DEFAULT_TRIGGER_MAX_DELAY = 2*60*1000;
87 
88     /** The minimum possible update delay is 1 second. */
89     public static final long MIN_TRIGGER_MAX_DELAY = 1000;
90 
91     final JobInfo job;
92     /** Uid of the package requesting this job. */
93     final int callingUid;
94     final String batteryName;
95 
96     final String sourcePackageName;
97     final int sourceUserId;
98     final int sourceUid;
99     final String sourceTag;
100 
101     final String tag;
102 
103     private GrantedUriPermissions uriPerms;
104     private boolean prepared;
105 
106     static final boolean DEBUG_PREPARE = true;
107     private Throwable unpreparedPoint = null;
108 
109     /**
110      * Earliest point in the future at which this job will be eligible to run. A value of 0
111      * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
112      */
113     private final long earliestRunTimeElapsedMillis;
114     /**
115      * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
116      * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
117      */
118     private final long latestRunTimeElapsedMillis;
119 
120     /** How many times this job has failed, used to compute back-off. */
121     private final int numFailures;
122 
123     // Constraints.
124     final int requiredConstraints;
125     int satisfiedConstraints = 0;
126 
127     // Set to true if doze constraint was satisfied due to app being whitelisted.
128     public boolean dozeWhitelisted;
129 
130     /**
131      * Flag for {@link #trackingControllers}: the battery controller is currently tracking this job.
132      */
133     public static final int TRACKING_BATTERY = 1<<0;
134     /**
135      * Flag for {@link #trackingControllers}: the network connectivity controller is currently
136      * tracking this job.
137      */
138     public static final int TRACKING_CONNECTIVITY = 1<<1;
139     /**
140      * Flag for {@link #trackingControllers}: the content observer controller is currently
141      * tracking this job.
142      */
143     public static final int TRACKING_CONTENT = 1<<2;
144     /**
145      * Flag for {@link #trackingControllers}: the idle controller is currently tracking this job.
146      */
147     public static final int TRACKING_IDLE = 1<<3;
148     /**
149      * Flag for {@link #trackingControllers}: the storage controller is currently tracking this job.
150      */
151     public static final int TRACKING_STORAGE = 1<<4;
152     /**
153      * Flag for {@link #trackingControllers}: the time controller is currently tracking this job.
154      */
155     public static final int TRACKING_TIME = 1<<5;
156 
157     /**
158      * Bit mask of controllers that are currently tracking the job.
159      */
160     private int trackingControllers;
161 
162     // These are filled in by controllers when preparing for execution.
163     public ArraySet<Uri> changedUris;
164     public ArraySet<String> changedAuthorities;
165 
166     public int lastEvaluatedPriority;
167 
168     // If non-null, this is work that has been enqueued for the job.
169     public ArrayList<JobWorkItem> pendingWork;
170 
171     // If non-null, this is work that is currently being executed.
172     public ArrayList<JobWorkItem> executingWork;
173 
174     public int nextPendingWorkId = 1;
175 
176     // Used by shell commands
177     public int overrideState = 0;
178 
179     // When this job was enqueued, for ordering.  (in elapsedRealtimeMillis)
180     public long enqueueTime;
181 
182     // Metrics about queue latency.  (in uptimeMillis)
183     public long madePending;
184     public long madeActive;
185 
186     /**
187      * For use only by ContentObserverController: state it is maintaining about content URIs
188      * being observed.
189      */
190     ContentObserverController.JobInstance contentObserverJobInstance;
191 
192     /** Provide a handle to the service that this job will be run on. */
getServiceToken()193     public int getServiceToken() {
194         return callingUid;
195     }
196 
JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, String tag, int numFailures, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis)197     private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
198             int sourceUserId, String tag, int numFailures, long earliestRunTimeElapsedMillis,
199             long latestRunTimeElapsedMillis) {
200         this.job = job;
201         this.callingUid = callingUid;
202 
203         int tempSourceUid = -1;
204         if (sourceUserId != -1 && sourcePackageName != null) {
205             try {
206                 tempSourceUid = AppGlobals.getPackageManager().getPackageUid(sourcePackageName, 0,
207                         sourceUserId);
208             } catch (RemoteException ex) {
209                 // Can't happen, PackageManager runs in the same process.
210             }
211         }
212         if (tempSourceUid == -1) {
213             this.sourceUid = callingUid;
214             this.sourceUserId = UserHandle.getUserId(callingUid);
215             this.sourcePackageName = job.getService().getPackageName();
216             this.sourceTag = null;
217         } else {
218             this.sourceUid = tempSourceUid;
219             this.sourceUserId = sourceUserId;
220             this.sourcePackageName = sourcePackageName;
221             this.sourceTag = tag;
222         }
223 
224         this.batteryName = this.sourceTag != null
225                 ? this.sourceTag + ":" + job.getService().getPackageName()
226                 : job.getService().flattenToShortString();
227         this.tag = "*job*/" + this.batteryName;
228 
229         this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
230         this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
231         this.numFailures = numFailures;
232 
233         int requiredConstraints = job.getConstraintFlags();
234 
235         switch (job.getNetworkType()) {
236             case JobInfo.NETWORK_TYPE_NONE:
237                 // No constraint.
238                 break;
239             case JobInfo.NETWORK_TYPE_ANY:
240                 requiredConstraints |= CONSTRAINT_CONNECTIVITY;
241                 break;
242             case JobInfo.NETWORK_TYPE_UNMETERED:
243                 requiredConstraints |= CONSTRAINT_UNMETERED;
244                 break;
245             case JobInfo.NETWORK_TYPE_NOT_ROAMING:
246                 requiredConstraints |= CONSTRAINT_NOT_ROAMING;
247                 break;
248             case JobInfo.NETWORK_TYPE_METERED:
249                 requiredConstraints |= CONSTRAINT_METERED;
250                 break;
251             default:
252                 Slog.w(TAG, "Unrecognized networking constraint " + job.getNetworkType());
253                 break;
254         }
255 
256         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
257             requiredConstraints |= CONSTRAINT_TIMING_DELAY;
258         }
259         if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
260             requiredConstraints |= CONSTRAINT_DEADLINE;
261         }
262         if (job.getTriggerContentUris() != null) {
263             requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
264         }
265         this.requiredConstraints = requiredConstraints;
266     }
267 
268     /** Copy constructor. */
JobStatus(JobStatus jobStatus)269     public JobStatus(JobStatus jobStatus) {
270         this(jobStatus.getJob(), jobStatus.getUid(),
271                 jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
272                 jobStatus.getSourceTag(), jobStatus.getNumFailures(),
273                 jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed());
274     }
275 
276     /**
277      * Create a new JobStatus that was loaded from disk. We ignore the provided
278      * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job
279      * from the {@link com.android.server.job.JobStore} and still want to respect its
280      * wallclock runtime rather than resetting it on every boot.
281      * We consider a freshly loaded job to no longer be in back-off.
282      */
JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis)283     public JobStatus(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId,
284             String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
285         this(job, callingUid, sourcePackageName, sourceUserId, sourceTag, 0,
286                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
287     }
288 
289     /** Create a new job to be rescheduled with the provided parameters. */
JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int backoffAttempt)290     public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis,
291                       long newLatestRuntimeElapsedMillis, int backoffAttempt) {
292         this(rescheduling.job, rescheduling.getUid(),
293                 rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(),
294                 rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
295                 newLatestRuntimeElapsedMillis);
296     }
297 
298     /**
299      * Create a newly scheduled job.
300      * @param callingUid Uid of the package that scheduled this job.
301      * @param sourcePackageName Package name on whose behalf this job is scheduled. Null indicates
302      *                          the calling package is the source.
303      * @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
304      */
createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName, int sourceUserId, String tag)305     public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName,
306             int sourceUserId, String tag) {
307         final long elapsedNow = SystemClock.elapsedRealtime();
308         final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
309         if (job.isPeriodic()) {
310             latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
311             earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
312         } else {
313             earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
314                     elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
315             latestRunTimeElapsedMillis = job.hasLateConstraint() ?
316                     elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
317         }
318         return new JobStatus(job, callingUid, sourcePackageName, sourceUserId, tag, 0,
319                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
320     }
321 
enqueueWorkLocked(IActivityManager am, JobWorkItem work)322     public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) {
323         if (pendingWork == null) {
324             pendingWork = new ArrayList<>();
325         }
326         work.setWorkId(nextPendingWorkId);
327         nextPendingWorkId++;
328         if (work.getIntent() != null
329                 && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) {
330             work.setGrants(GrantedUriPermissions.createFromIntent(am, work.getIntent(), sourceUid,
331                     sourcePackageName, sourceUserId, toShortString()));
332         }
333         pendingWork.add(work);
334     }
335 
dequeueWorkLocked()336     public JobWorkItem dequeueWorkLocked() {
337         if (pendingWork != null && pendingWork.size() > 0) {
338             JobWorkItem work = pendingWork.remove(0);
339             if (work != null) {
340                 if (executingWork == null) {
341                     executingWork = new ArrayList<>();
342                 }
343                 executingWork.add(work);
344                 work.bumpDeliveryCount();
345             }
346             return work;
347         }
348         return null;
349     }
350 
hasWorkLocked()351     public boolean hasWorkLocked() {
352         return (pendingWork != null && pendingWork.size() > 0) || hasExecutingWorkLocked();
353     }
354 
hasExecutingWorkLocked()355     public boolean hasExecutingWorkLocked() {
356         return executingWork != null && executingWork.size() > 0;
357     }
358 
ungrantWorkItem(IActivityManager am, JobWorkItem work)359     private static void ungrantWorkItem(IActivityManager am, JobWorkItem work) {
360         if (work.getGrants() != null) {
361             ((GrantedUriPermissions)work.getGrants()).revoke(am);
362         }
363     }
364 
completeWorkLocked(IActivityManager am, int workId)365     public boolean completeWorkLocked(IActivityManager am, int workId) {
366         if (executingWork != null) {
367             final int N = executingWork.size();
368             for (int i = 0; i < N; i++) {
369                 JobWorkItem work = executingWork.get(i);
370                 if (work.getWorkId() == workId) {
371                     executingWork.remove(i);
372                     ungrantWorkItem(am, work);
373                     return true;
374                 }
375             }
376         }
377         return false;
378     }
379 
ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list)380     private static void ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list) {
381         if (list != null) {
382             final int N = list.size();
383             for (int i = 0; i < N; i++) {
384                 ungrantWorkItem(am, list.get(i));
385             }
386         }
387     }
388 
stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob)389     public void stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob) {
390         if (incomingJob != null) {
391             // We are replacing with a new job -- transfer the work!  We do any executing
392             // work first, since that was originally at the front of the pending work.
393             if (executingWork != null && executingWork.size() > 0) {
394                 incomingJob.pendingWork = executingWork;
395             }
396             if (incomingJob.pendingWork == null) {
397                 incomingJob.pendingWork = pendingWork;
398             } else if (pendingWork != null && pendingWork.size() > 0) {
399                 incomingJob.pendingWork.addAll(pendingWork);
400             }
401             pendingWork = null;
402             executingWork = null;
403             incomingJob.nextPendingWorkId = nextPendingWorkId;
404         } else {
405             // We are completely stopping the job...  need to clean up work.
406             ungrantWorkList(am, pendingWork);
407             pendingWork = null;
408             ungrantWorkList(am, executingWork);
409             executingWork = null;
410         }
411     }
412 
prepareLocked(IActivityManager am)413     public void prepareLocked(IActivityManager am) {
414         if (prepared) {
415             Slog.wtf(TAG, "Already prepared: " + this);
416             return;
417         }
418         prepared = true;
419         if (DEBUG_PREPARE) {
420             unpreparedPoint = null;
421         }
422         final ClipData clip = job.getClipData();
423         if (clip != null) {
424             uriPerms = GrantedUriPermissions.createFromClip(am, clip, sourceUid, sourcePackageName,
425                     sourceUserId, job.getClipGrantFlags(), toShortString());
426         }
427     }
428 
unprepareLocked(IActivityManager am)429     public void unprepareLocked(IActivityManager am) {
430         if (!prepared) {
431             Slog.wtf(TAG, "Hasn't been prepared: " + this);
432             if (DEBUG_PREPARE && unpreparedPoint != null) {
433                 Slog.e(TAG, "Was already unprepared at ", unpreparedPoint);
434             }
435             return;
436         }
437         prepared = false;
438         if (DEBUG_PREPARE) {
439             unpreparedPoint = new Throwable().fillInStackTrace();
440         }
441         if (uriPerms != null) {
442             uriPerms.revoke(am);
443             uriPerms = null;
444         }
445     }
446 
isPreparedLocked()447     public boolean isPreparedLocked() {
448         return prepared;
449     }
450 
getJob()451     public JobInfo getJob() {
452         return job;
453     }
454 
getJobId()455     public int getJobId() {
456         return job.getId();
457     }
458 
printUniqueId(PrintWriter pw)459     public void printUniqueId(PrintWriter pw) {
460         UserHandle.formatUid(pw, callingUid);
461         pw.print("/");
462         pw.print(job.getId());
463     }
464 
getNumFailures()465     public int getNumFailures() {
466         return numFailures;
467     }
468 
getServiceComponent()469     public ComponentName getServiceComponent() {
470         return job.getService();
471     }
472 
getSourcePackageName()473     public String getSourcePackageName() {
474         return sourcePackageName;
475     }
476 
getSourceUid()477     public int getSourceUid() {
478         return sourceUid;
479     }
480 
getSourceUserId()481     public int getSourceUserId() {
482         return sourceUserId;
483     }
484 
getUserId()485     public int getUserId() {
486         return UserHandle.getUserId(callingUid);
487     }
488 
getSourceTag()489     public String getSourceTag() {
490         return sourceTag;
491     }
492 
getUid()493     public int getUid() {
494         return callingUid;
495     }
496 
getBatteryName()497     public String getBatteryName() {
498         return batteryName;
499     }
500 
getTag()501     public String getTag() {
502         return tag;
503     }
504 
getPriority()505     public int getPriority() {
506         return job.getPriority();
507     }
508 
getFlags()509     public int getFlags() {
510         return job.getFlags();
511     }
512 
513     /** Does this job have any sort of networking constraint? */
hasConnectivityConstraint()514     public boolean hasConnectivityConstraint() {
515         return (requiredConstraints&CONNECTIVITY_MASK) != 0;
516     }
517 
needsAnyConnectivity()518     public boolean needsAnyConnectivity() {
519         return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
520     }
521 
needsUnmeteredConnectivity()522     public boolean needsUnmeteredConnectivity() {
523         return (requiredConstraints&CONSTRAINT_UNMETERED) != 0;
524     }
525 
needsMeteredConnectivity()526     public boolean needsMeteredConnectivity() {
527         return (requiredConstraints&CONSTRAINT_METERED) != 0;
528     }
529 
needsNonRoamingConnectivity()530     public boolean needsNonRoamingConnectivity() {
531         return (requiredConstraints&CONSTRAINT_NOT_ROAMING) != 0;
532     }
533 
hasChargingConstraint()534     public boolean hasChargingConstraint() {
535         return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
536     }
537 
hasBatteryNotLowConstraint()538     public boolean hasBatteryNotLowConstraint() {
539         return (requiredConstraints&CONSTRAINT_BATTERY_NOT_LOW) != 0;
540     }
541 
hasPowerConstraint()542     public boolean hasPowerConstraint() {
543         return (requiredConstraints&(CONSTRAINT_CHARGING|CONSTRAINT_BATTERY_NOT_LOW)) != 0;
544     }
545 
hasStorageNotLowConstraint()546     public boolean hasStorageNotLowConstraint() {
547         return (requiredConstraints&CONSTRAINT_STORAGE_NOT_LOW) != 0;
548     }
549 
hasTimingDelayConstraint()550     public boolean hasTimingDelayConstraint() {
551         return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0;
552     }
553 
hasDeadlineConstraint()554     public boolean hasDeadlineConstraint() {
555         return (requiredConstraints&CONSTRAINT_DEADLINE) != 0;
556     }
557 
hasIdleConstraint()558     public boolean hasIdleConstraint() {
559         return (requiredConstraints&CONSTRAINT_IDLE) != 0;
560     }
561 
hasContentTriggerConstraint()562     public boolean hasContentTriggerConstraint() {
563         return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
564     }
565 
getTriggerContentUpdateDelay()566     public long getTriggerContentUpdateDelay() {
567         long time = job.getTriggerContentUpdateDelay();
568         if (time < 0) {
569             return DEFAULT_TRIGGER_UPDATE_DELAY;
570         }
571         return Math.max(time, MIN_TRIGGER_UPDATE_DELAY);
572     }
573 
getTriggerContentMaxDelay()574     public long getTriggerContentMaxDelay() {
575         long time = job.getTriggerContentMaxDelay();
576         if (time < 0) {
577             return DEFAULT_TRIGGER_MAX_DELAY;
578         }
579         return Math.max(time, MIN_TRIGGER_MAX_DELAY);
580     }
581 
isPersisted()582     public boolean isPersisted() {
583         return job.isPersisted();
584     }
585 
getEarliestRunTime()586     public long getEarliestRunTime() {
587         return earliestRunTimeElapsedMillis;
588     }
589 
getLatestRunTimeElapsed()590     public long getLatestRunTimeElapsed() {
591         return latestRunTimeElapsedMillis;
592     }
593 
setChargingConstraintSatisfied(boolean state)594     boolean setChargingConstraintSatisfied(boolean state) {
595         return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
596     }
597 
setBatteryNotLowConstraintSatisfied(boolean state)598     boolean setBatteryNotLowConstraintSatisfied(boolean state) {
599         return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state);
600     }
601 
setStorageNotLowConstraintSatisfied(boolean state)602     boolean setStorageNotLowConstraintSatisfied(boolean state) {
603         return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state);
604     }
605 
setTimingDelayConstraintSatisfied(boolean state)606     boolean setTimingDelayConstraintSatisfied(boolean state) {
607         return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
608     }
609 
setDeadlineConstraintSatisfied(boolean state)610     boolean setDeadlineConstraintSatisfied(boolean state) {
611         return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
612     }
613 
setIdleConstraintSatisfied(boolean state)614     boolean setIdleConstraintSatisfied(boolean state) {
615         return setConstraintSatisfied(CONSTRAINT_IDLE, state);
616     }
617 
setConnectivityConstraintSatisfied(boolean state)618     boolean setConnectivityConstraintSatisfied(boolean state) {
619         return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
620     }
621 
setUnmeteredConstraintSatisfied(boolean state)622     boolean setUnmeteredConstraintSatisfied(boolean state) {
623         return setConstraintSatisfied(CONSTRAINT_UNMETERED, state);
624     }
625 
setMeteredConstraintSatisfied(boolean state)626     boolean setMeteredConstraintSatisfied(boolean state) {
627         return setConstraintSatisfied(CONSTRAINT_METERED, state);
628     }
629 
setNotRoamingConstraintSatisfied(boolean state)630     boolean setNotRoamingConstraintSatisfied(boolean state) {
631         return setConstraintSatisfied(CONSTRAINT_NOT_ROAMING, state);
632     }
633 
setAppNotIdleConstraintSatisfied(boolean state)634     boolean setAppNotIdleConstraintSatisfied(boolean state) {
635         return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state);
636     }
637 
setContentTriggerConstraintSatisfied(boolean state)638     boolean setContentTriggerConstraintSatisfied(boolean state) {
639         return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
640     }
641 
setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted)642     boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
643         dozeWhitelisted = whitelisted;
644         return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
645     }
646 
setConstraintSatisfied(int constraint, boolean state)647     boolean setConstraintSatisfied(int constraint, boolean state) {
648         boolean old = (satisfiedConstraints&constraint) != 0;
649         if (old == state) {
650             return false;
651         }
652         satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
653         return true;
654     }
655 
isConstraintSatisfied(int constraint)656     boolean isConstraintSatisfied(int constraint) {
657         return (satisfiedConstraints&constraint) != 0;
658     }
659 
clearTrackingController(int which)660     boolean clearTrackingController(int which) {
661         if ((trackingControllers&which) != 0) {
662             trackingControllers &= ~which;
663             return true;
664         }
665         return false;
666     }
667 
setTrackingController(int which)668     void setTrackingController(int which) {
669         trackingControllers |= which;
670     }
671 
shouldDump(int filterUid)672     public boolean shouldDump(int filterUid) {
673         return filterUid == -1 || UserHandle.getAppId(getUid()) == filterUid
674                 || UserHandle.getAppId(getSourceUid()) == filterUid;
675     }
676 
677     /**
678      * @return Whether or not this job is ready to run, based on its requirements. This is true if
679      * the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
680      * TODO: This function is called a *lot*.  We should probably just have it check an
681      * already-computed boolean, which we updated whenever we see one of the states it depends
682      * on here change.
683      */
isReady()684     public boolean isReady() {
685         // Deadline constraint trumps other constraints (except for periodic jobs where deadline
686         // is an implementation detail. A periodic job should only run if its constraints are
687         // satisfied).
688         // AppNotIdle implicit constraint must be satisfied
689         // DeviceNotDozing implicit constraint must be satisfied
690         final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint()
691                 && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0);
692         final boolean notIdle = (satisfiedConstraints & CONSTRAINT_APP_NOT_IDLE) != 0;
693         final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
694                 || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
695         return (isConstraintsSatisfied() || deadlineSatisfied) && notIdle && notDozing;
696     }
697 
698     static final int CONSTRAINTS_OF_INTEREST =
699             CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW |
700             CONSTRAINT_TIMING_DELAY |
701             CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED |
702             CONSTRAINT_NOT_ROAMING | CONSTRAINT_METERED |
703             CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
704 
705     // Soft override covers all non-"functional" constraints
706     static final int SOFT_OVERRIDE_CONSTRAINTS =
707             CONSTRAINT_CHARGING | CONSTRAINT_BATTERY_NOT_LOW | CONSTRAINT_STORAGE_NOT_LOW
708                     | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE;
709 
710     /**
711      * @return Whether the constraints set on this job are satisfied.
712      */
isConstraintsSatisfied()713     public boolean isConstraintsSatisfied() {
714         if (overrideState == OVERRIDE_FULL) {
715             // force override: the job is always runnable
716             return true;
717         }
718 
719         final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
720 
721         int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
722         if (overrideState == OVERRIDE_SOFT) {
723             // override: pretend all 'soft' requirements are satisfied
724             sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
725         }
726 
727         return (sat & req) == req;
728     }
729 
matches(int uid, int jobId)730     public boolean matches(int uid, int jobId) {
731         return this.job.getId() == jobId && this.callingUid == uid;
732     }
733 
734     @Override
toString()735     public String toString() {
736         StringBuilder sb = new StringBuilder(128);
737         sb.append("JobStatus{");
738         sb.append(Integer.toHexString(System.identityHashCode(this)));
739         sb.append(" #");
740         UserHandle.formatUid(sb, callingUid);
741         sb.append("/");
742         sb.append(job.getId());
743         sb.append(' ');
744         sb.append(batteryName);
745         sb.append(" u=");
746         sb.append(getUserId());
747         sb.append(" s=");
748         sb.append(getSourceUid());
749         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME
750                 || latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
751             long now = SystemClock.elapsedRealtime();
752             sb.append(" TIME=");
753             formatRunTime(sb, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, now);
754             sb.append(":");
755             formatRunTime(sb, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, now);
756         }
757         if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
758             sb.append(" NET=");
759             sb.append(job.getNetworkType());
760         }
761         if (job.isRequireCharging()) {
762             sb.append(" CHARGING");
763         }
764         if (job.isRequireBatteryNotLow()) {
765             sb.append(" BATNOTLOW");
766         }
767         if (job.isRequireStorageNotLow()) {
768             sb.append(" STORENOTLOW");
769         }
770         if (job.isRequireDeviceIdle()) {
771             sb.append(" IDLE");
772         }
773         if (job.isPersisted()) {
774             sb.append(" PERSISTED");
775         }
776         if ((satisfiedConstraints&CONSTRAINT_APP_NOT_IDLE) == 0) {
777             sb.append(" WAIT:APP_NOT_IDLE");
778         }
779         if ((satisfiedConstraints&CONSTRAINT_DEVICE_NOT_DOZING) == 0) {
780             sb.append(" WAIT:DEV_NOT_DOZING");
781         }
782         if (job.getTriggerContentUris() != null) {
783             sb.append(" URIS=");
784             sb.append(Arrays.toString(job.getTriggerContentUris()));
785         }
786         if (numFailures != 0) {
787             sb.append(" failures=");
788             sb.append(numFailures);
789         }
790         if (isReady()) {
791             sb.append(" READY");
792         }
793         sb.append("}");
794         return sb.toString();
795     }
796 
formatRunTime(PrintWriter pw, long runtime, long defaultValue, long now)797     private void formatRunTime(PrintWriter pw, long runtime, long  defaultValue, long now) {
798         if (runtime == defaultValue) {
799             pw.print("none");
800         } else {
801             TimeUtils.formatDuration(runtime - now, pw);
802         }
803     }
804 
formatRunTime(StringBuilder sb, long runtime, long defaultValue, long now)805     private void formatRunTime(StringBuilder sb, long runtime, long  defaultValue, long now) {
806         if (runtime == defaultValue) {
807             sb.append("none");
808         } else {
809             TimeUtils.formatDuration(runtime - now, sb);
810         }
811     }
812 
813     /**
814      * Convenience function to identify a job uniquely without pulling all the data that
815      * {@link #toString()} returns.
816      */
toShortString()817     public String toShortString() {
818         StringBuilder sb = new StringBuilder();
819         sb.append(Integer.toHexString(System.identityHashCode(this)));
820         sb.append(" #");
821         UserHandle.formatUid(sb, callingUid);
822         sb.append("/");
823         sb.append(job.getId());
824         sb.append(' ');
825         sb.append(batteryName);
826         return sb.toString();
827     }
828 
829     /**
830      * Convenience function to identify a job uniquely without pulling all the data that
831      * {@link #toString()} returns.
832      */
toShortStringExceptUniqueId()833     public String toShortStringExceptUniqueId() {
834         StringBuilder sb = new StringBuilder();
835         sb.append(Integer.toHexString(System.identityHashCode(this)));
836         sb.append(' ');
837         sb.append(batteryName);
838         return sb.toString();
839     }
840 
dumpConstraints(PrintWriter pw, int constraints)841     void dumpConstraints(PrintWriter pw, int constraints) {
842         if ((constraints&CONSTRAINT_CHARGING) != 0) {
843             pw.print(" CHARGING");
844         }
845         if ((constraints& CONSTRAINT_BATTERY_NOT_LOW) != 0) {
846             pw.print(" BATTERY_NOT_LOW");
847         }
848         if ((constraints& CONSTRAINT_STORAGE_NOT_LOW) != 0) {
849             pw.print(" STORAGE_NOT_LOW");
850         }
851         if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) {
852             pw.print(" TIMING_DELAY");
853         }
854         if ((constraints&CONSTRAINT_DEADLINE) != 0) {
855             pw.print(" DEADLINE");
856         }
857         if ((constraints&CONSTRAINT_IDLE) != 0) {
858             pw.print(" IDLE");
859         }
860         if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
861             pw.print(" CONNECTIVITY");
862         }
863         if ((constraints&CONSTRAINT_UNMETERED) != 0) {
864             pw.print(" UNMETERED");
865         }
866         if ((constraints&CONSTRAINT_NOT_ROAMING) != 0) {
867             pw.print(" NOT_ROAMING");
868         }
869         if ((constraints&CONSTRAINT_METERED) != 0) {
870             pw.print(" METERED");
871         }
872         if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) {
873             pw.print(" APP_NOT_IDLE");
874         }
875         if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) {
876             pw.print(" CONTENT_TRIGGER");
877         }
878         if ((constraints&CONSTRAINT_DEVICE_NOT_DOZING) != 0) {
879             pw.print(" DEVICE_NOT_DOZING");
880         }
881     }
882 
dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index)883     private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
884         pw.print(prefix); pw.print("  #"); pw.print(index); pw.print(": #");
885         pw.print(work.getWorkId()); pw.print(" "); pw.print(work.getDeliveryCount());
886         pw.print("x "); pw.println(work.getIntent());
887         if (work.getGrants() != null) {
888             pw.print(prefix); pw.println("  URI grants:");
889             ((GrantedUriPermissions)work.getGrants()).dump(pw, prefix + "    ");
890         }
891     }
892 
893     // Dumpsys infrastructure
dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis)894     public void dump(PrintWriter pw, String prefix, boolean full, long elapsedRealtimeMillis) {
895         pw.print(prefix); UserHandle.formatUid(pw, callingUid);
896         pw.print(" tag="); pw.println(tag);
897         pw.print(prefix);
898         pw.print("Source: uid="); UserHandle.formatUid(pw, getSourceUid());
899         pw.print(" user="); pw.print(getSourceUserId());
900         pw.print(" pkg="); pw.println(getSourcePackageName());
901         if (full) {
902             pw.print(prefix); pw.println("JobInfo:");
903             pw.print(prefix); pw.print("  Service: ");
904             pw.println(job.getService().flattenToShortString());
905             if (job.isPeriodic()) {
906                 pw.print(prefix); pw.print("  PERIODIC: interval=");
907                 TimeUtils.formatDuration(job.getIntervalMillis(), pw);
908                 pw.print(" flex="); TimeUtils.formatDuration(job.getFlexMillis(), pw);
909                 pw.println();
910             }
911             if (job.isPersisted()) {
912                 pw.print(prefix); pw.println("  PERSISTED");
913             }
914             if (job.getPriority() != 0) {
915                 pw.print(prefix); pw.print("  Priority: "); pw.println(job.getPriority());
916             }
917             if (job.getFlags() != 0) {
918                 pw.print(prefix); pw.print("  Flags: ");
919                 pw.println(Integer.toHexString(job.getFlags()));
920             }
921             pw.print(prefix); pw.print("  Requires: charging=");
922             pw.print(job.isRequireCharging()); pw.print(" batteryNotLow=");
923             pw.print(job.isRequireBatteryNotLow()); pw.print(" deviceIdle=");
924             pw.println(job.isRequireDeviceIdle());
925             if (job.getTriggerContentUris() != null) {
926                 pw.print(prefix); pw.println("  Trigger content URIs:");
927                 for (int i = 0; i < job.getTriggerContentUris().length; i++) {
928                     JobInfo.TriggerContentUri trig = job.getTriggerContentUris()[i];
929                     pw.print(prefix); pw.print("    ");
930                     pw.print(Integer.toHexString(trig.getFlags()));
931                     pw.print(' '); pw.println(trig.getUri());
932                 }
933                 if (job.getTriggerContentUpdateDelay() >= 0) {
934                     pw.print(prefix); pw.print("  Trigger update delay: ");
935                     TimeUtils.formatDuration(job.getTriggerContentUpdateDelay(), pw);
936                     pw.println();
937                 }
938                 if (job.getTriggerContentMaxDelay() >= 0) {
939                     pw.print(prefix); pw.print("  Trigger max delay: ");
940                     TimeUtils.formatDuration(job.getTriggerContentMaxDelay(), pw);
941                     pw.println();
942                 }
943             }
944             if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
945                 pw.print(prefix); pw.print("  Extras: ");
946                 pw.println(job.getExtras().toShortString());
947             }
948             if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
949                 pw.print(prefix); pw.print("  Transient extras: ");
950                 pw.println(job.getTransientExtras().toShortString());
951             }
952             if (job.getClipData() != null) {
953                 pw.print(prefix); pw.print("  Clip data: ");
954                 StringBuilder b = new StringBuilder(128);
955                 job.getClipData().toShortString(b);
956                 pw.println(b);
957             }
958             if (uriPerms != null) {
959                 pw.print(prefix); pw.println("  Granted URI permissions:");
960                 uriPerms.dump(pw, prefix + "  ");
961             }
962             if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
963                 pw.print(prefix); pw.print("  Network type: "); pw.println(job.getNetworkType());
964             }
965             if (job.getMinLatencyMillis() != 0) {
966                 pw.print(prefix); pw.print("  Minimum latency: ");
967                 TimeUtils.formatDuration(job.getMinLatencyMillis(), pw);
968                 pw.println();
969             }
970             if (job.getMaxExecutionDelayMillis() != 0) {
971                 pw.print(prefix); pw.print("  Max execution delay: ");
972                 TimeUtils.formatDuration(job.getMaxExecutionDelayMillis(), pw);
973                 pw.println();
974             }
975             pw.print(prefix); pw.print("  Backoff: policy="); pw.print(job.getBackoffPolicy());
976             pw.print(" initial="); TimeUtils.formatDuration(job.getInitialBackoffMillis(), pw);
977             pw.println();
978             if (job.hasEarlyConstraint()) {
979                 pw.print(prefix); pw.println("  Has early constraint");
980             }
981             if (job.hasLateConstraint()) {
982                 pw.print(prefix); pw.println("  Has late constraint");
983             }
984         }
985         pw.print(prefix); pw.print("Required constraints:");
986         dumpConstraints(pw, requiredConstraints);
987         pw.println();
988         if (full) {
989             pw.print(prefix); pw.print("Satisfied constraints:");
990             dumpConstraints(pw, satisfiedConstraints);
991             pw.println();
992             pw.print(prefix); pw.print("Unsatisfied constraints:");
993             dumpConstraints(pw, (requiredConstraints & ~satisfiedConstraints));
994             pw.println();
995             if (dozeWhitelisted) {
996                 pw.print(prefix); pw.println("Doze whitelisted: true");
997             }
998         }
999         if (trackingControllers != 0) {
1000             pw.print(prefix); pw.print("Tracking:");
1001             if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY");
1002             if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY");
1003             if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT");
1004             if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE");
1005             if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE");
1006             if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME");
1007             pw.println();
1008         }
1009         if (changedAuthorities != null) {
1010             pw.print(prefix); pw.println("Changed authorities:");
1011             for (int i=0; i<changedAuthorities.size(); i++) {
1012                 pw.print(prefix); pw.print("  "); pw.println(changedAuthorities.valueAt(i));
1013             }
1014             if (changedUris != null) {
1015                 pw.print(prefix); pw.println("Changed URIs:");
1016                 for (int i=0; i<changedUris.size(); i++) {
1017                     pw.print(prefix); pw.print("  "); pw.println(changedUris.valueAt(i));
1018                 }
1019             }
1020         }
1021         if (pendingWork != null && pendingWork.size() > 0) {
1022             pw.print(prefix); pw.println("Pending work:");
1023             for (int i = 0; i < pendingWork.size(); i++) {
1024                 dumpJobWorkItem(pw, prefix, pendingWork.get(i), i);
1025             }
1026         }
1027         if (executingWork != null && executingWork.size() > 0) {
1028             pw.print(prefix); pw.println("Executing work:");
1029             for (int i = 0; i < executingWork.size(); i++) {
1030                 dumpJobWorkItem(pw, prefix, executingWork.get(i), i);
1031             }
1032         }
1033         pw.print(prefix); pw.print("Enqueue time: ");
1034         TimeUtils.formatDuration(enqueueTime, elapsedRealtimeMillis, pw);
1035         pw.println();
1036         pw.print(prefix); pw.print("Run time: earliest=");
1037         formatRunTime(pw, earliestRunTimeElapsedMillis, NO_EARLIEST_RUNTIME, elapsedRealtimeMillis);
1038         pw.print(", latest=");
1039         formatRunTime(pw, latestRunTimeElapsedMillis, NO_LATEST_RUNTIME, elapsedRealtimeMillis);
1040         pw.println();
1041         if (numFailures != 0) {
1042             pw.print(prefix); pw.print("Num failures: "); pw.println(numFailures);
1043         }
1044     }
1045 }
1046