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
17package com.android.server.job.controllers;
18
19import android.app.AppGlobals;
20import android.app.IActivityManager;
21import android.app.job.JobInfo;
22import android.app.job.JobWorkItem;
23import android.content.ClipData;
24import android.content.ComponentName;
25import android.net.Uri;
26import android.os.RemoteException;
27import android.os.SystemClock;
28import android.os.UserHandle;
29import android.util.ArraySet;
30import android.util.Slog;
31import android.util.TimeUtils;
32
33import com.android.server.job.GrantedUriPermissions;
34
35import java.io.PrintWriter;
36import java.util.ArrayList;
37import 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 */
50public 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. */
193    public int getServiceToken() {
194        return callingUid;
195    }
196
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. */
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     */
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. */
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     */
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
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
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
351    public boolean hasWorkLocked() {
352        return (pendingWork != null && pendingWork.size() > 0) || hasExecutingWorkLocked();
353    }
354
355    public boolean hasExecutingWorkLocked() {
356        return executingWork != null && executingWork.size() > 0;
357    }
358
359    private static void ungrantWorkItem(IActivityManager am, JobWorkItem work) {
360        if (work.getGrants() != null) {
361            ((GrantedUriPermissions)work.getGrants()).revoke(am);
362        }
363    }
364
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
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
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
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
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
447    public boolean isPreparedLocked() {
448        return prepared;
449    }
450
451    public JobInfo getJob() {
452        return job;
453    }
454
455    public int getJobId() {
456        return job.getId();
457    }
458
459    public void printUniqueId(PrintWriter pw) {
460        UserHandle.formatUid(pw, callingUid);
461        pw.print("/");
462        pw.print(job.getId());
463    }
464
465    public int getNumFailures() {
466        return numFailures;
467    }
468
469    public ComponentName getServiceComponent() {
470        return job.getService();
471    }
472
473    public String getSourcePackageName() {
474        return sourcePackageName;
475    }
476
477    public int getSourceUid() {
478        return sourceUid;
479    }
480
481    public int getSourceUserId() {
482        return sourceUserId;
483    }
484
485    public int getUserId() {
486        return UserHandle.getUserId(callingUid);
487    }
488
489    public String getSourceTag() {
490        return sourceTag;
491    }
492
493    public int getUid() {
494        return callingUid;
495    }
496
497    public String getBatteryName() {
498        return batteryName;
499    }
500
501    public String getTag() {
502        return tag;
503    }
504
505    public int getPriority() {
506        return job.getPriority();
507    }
508
509    public int getFlags() {
510        return job.getFlags();
511    }
512
513    /** Does this job have any sort of networking constraint? */
514    public boolean hasConnectivityConstraint() {
515        return (requiredConstraints&CONNECTIVITY_MASK) != 0;
516    }
517
518    public boolean needsAnyConnectivity() {
519        return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
520    }
521
522    public boolean needsUnmeteredConnectivity() {
523        return (requiredConstraints&CONSTRAINT_UNMETERED) != 0;
524    }
525
526    public boolean needsMeteredConnectivity() {
527        return (requiredConstraints&CONSTRAINT_METERED) != 0;
528    }
529
530    public boolean needsNonRoamingConnectivity() {
531        return (requiredConstraints&CONSTRAINT_NOT_ROAMING) != 0;
532    }
533
534    public boolean hasChargingConstraint() {
535        return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
536    }
537
538    public boolean hasBatteryNotLowConstraint() {
539        return (requiredConstraints&CONSTRAINT_BATTERY_NOT_LOW) != 0;
540    }
541
542    public boolean hasPowerConstraint() {
543        return (requiredConstraints&(CONSTRAINT_CHARGING|CONSTRAINT_BATTERY_NOT_LOW)) != 0;
544    }
545
546    public boolean hasStorageNotLowConstraint() {
547        return (requiredConstraints&CONSTRAINT_STORAGE_NOT_LOW) != 0;
548    }
549
550    public boolean hasTimingDelayConstraint() {
551        return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0;
552    }
553
554    public boolean hasDeadlineConstraint() {
555        return (requiredConstraints&CONSTRAINT_DEADLINE) != 0;
556    }
557
558    public boolean hasIdleConstraint() {
559        return (requiredConstraints&CONSTRAINT_IDLE) != 0;
560    }
561
562    public boolean hasContentTriggerConstraint() {
563        return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
564    }
565
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
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
582    public boolean isPersisted() {
583        return job.isPersisted();
584    }
585
586    public long getEarliestRunTime() {
587        return earliestRunTimeElapsedMillis;
588    }
589
590    public long getLatestRunTimeElapsed() {
591        return latestRunTimeElapsedMillis;
592    }
593
594    boolean setChargingConstraintSatisfied(boolean state) {
595        return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
596    }
597
598    boolean setBatteryNotLowConstraintSatisfied(boolean state) {
599        return setConstraintSatisfied(CONSTRAINT_BATTERY_NOT_LOW, state);
600    }
601
602    boolean setStorageNotLowConstraintSatisfied(boolean state) {
603        return setConstraintSatisfied(CONSTRAINT_STORAGE_NOT_LOW, state);
604    }
605
606    boolean setTimingDelayConstraintSatisfied(boolean state) {
607        return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
608    }
609
610    boolean setDeadlineConstraintSatisfied(boolean state) {
611        return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
612    }
613
614    boolean setIdleConstraintSatisfied(boolean state) {
615        return setConstraintSatisfied(CONSTRAINT_IDLE, state);
616    }
617
618    boolean setConnectivityConstraintSatisfied(boolean state) {
619        return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
620    }
621
622    boolean setUnmeteredConstraintSatisfied(boolean state) {
623        return setConstraintSatisfied(CONSTRAINT_UNMETERED, state);
624    }
625
626    boolean setMeteredConstraintSatisfied(boolean state) {
627        return setConstraintSatisfied(CONSTRAINT_METERED, state);
628    }
629
630    boolean setNotRoamingConstraintSatisfied(boolean state) {
631        return setConstraintSatisfied(CONSTRAINT_NOT_ROAMING, state);
632    }
633
634    boolean setAppNotIdleConstraintSatisfied(boolean state) {
635        return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state);
636    }
637
638    boolean setContentTriggerConstraintSatisfied(boolean state) {
639        return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
640    }
641
642    boolean setDeviceNotDozingConstraintSatisfied(boolean state, boolean whitelisted) {
643        dozeWhitelisted = whitelisted;
644        return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
645    }
646
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
656    boolean isConstraintSatisfied(int constraint) {
657        return (satisfiedConstraints&constraint) != 0;
658    }
659
660    boolean clearTrackingController(int which) {
661        if ((trackingControllers&which) != 0) {
662            trackingControllers &= ~which;
663            return true;
664        }
665        return false;
666    }
667
668    void setTrackingController(int which) {
669        trackingControllers |= which;
670    }
671
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     */
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     */
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
730    public boolean matches(int uid, int jobId) {
731        return this.job.getId() == jobId && this.callingUid == uid;
732    }
733
734    @Override
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
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
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     */
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     */
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
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
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
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