JobStatus.java revision 900c67fc51fc2672458dd1c9641250f2ecc01a31
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.job.JobInfo;
20import android.content.ComponentName;
21import android.os.PersistableBundle;
22import android.os.SystemClock;
23import android.os.UserHandle;
24
25import java.io.PrintWriter;
26import java.util.concurrent.atomic.AtomicBoolean;
27
28/**
29 * Uniquely identifies a job internally.
30 * Created from the public {@link android.app.job.JobInfo} object when it lands on the scheduler.
31 * Contains current state of the requirements of the job, as well as a function to evaluate
32 * whether it's ready to run.
33 * This object is shared among the various controllers - hence why the different fields are atomic.
34 * This isn't strictly necessary because each controller is only interested in a specific field,
35 * and the receivers that are listening for global state change will all run on the main looper,
36 * but we don't enforce that so this is safer.
37 * @hide
38 */
39public class JobStatus {
40    public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
41    public static final long NO_EARLIEST_RUNTIME = 0L;
42
43    final JobInfo job;
44    final int uId;
45
46    // Constraints.
47    final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
48    final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
49    final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean();
50    final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
51    final AtomicBoolean unmeteredConstraintSatisfied = new AtomicBoolean();
52    final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
53
54    /**
55     * Earliest point in the future at which this job will be eligible to run. A value of 0
56     * indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
57     */
58    private long earliestRunTimeElapsedMillis;
59    /**
60     * Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
61     * indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
62     */
63    private long latestRunTimeElapsedMillis;
64    /** How many times this job has failed, used to compute back-off. */
65    private final int numFailures;
66
67    /** Provide a handle to the service that this job will be run on. */
68    public int getServiceToken() {
69        return uId;
70    }
71
72    private JobStatus(JobInfo job, int uId, int numFailures) {
73        this.job = job;
74        this.uId = uId;
75        this.numFailures = numFailures;
76    }
77
78    /** Create a newly scheduled job. */
79    public JobStatus(JobInfo job, int uId) {
80        this(job, uId, 0);
81
82        final long elapsedNow = SystemClock.elapsedRealtime();
83
84        if (job.isPeriodic()) {
85            earliestRunTimeElapsedMillis = elapsedNow;
86            latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
87        } else {
88            earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
89                    elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
90            latestRunTimeElapsedMillis = job.hasLateConstraint() ?
91                    elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
92        }
93    }
94
95    /**
96     * Create a new JobStatus that was loaded from disk. We ignore the provided
97     * {@link android.app.job.JobInfo} time criteria because we can load a persisted periodic job
98     * from the {@link com.android.server.job.JobStore} and still want to respect its
99     * wallclock runtime rather than resetting it on every boot.
100     * We consider a freshly loaded job to no longer be in back-off.
101     */
102    public JobStatus(JobInfo job, int uId, long earliestRunTimeElapsedMillis,
103                      long latestRunTimeElapsedMillis) {
104        this(job, uId, 0);
105
106        this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
107        this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
108    }
109
110    /** Create a new job to be rescheduled with the provided parameters. */
111    public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis,
112                      long newLatestRuntimeElapsedMillis, int backoffAttempt) {
113        this(rescheduling.job, rescheduling.getUid(), backoffAttempt);
114
115        earliestRunTimeElapsedMillis = newEarliestRuntimeElapsedMillis;
116        latestRunTimeElapsedMillis = newLatestRuntimeElapsedMillis;
117    }
118
119    public JobInfo getJob() {
120        return job;
121    }
122
123    public int getJobId() {
124        return job.getId();
125    }
126
127    public int getNumFailures() {
128        return numFailures;
129    }
130
131    public ComponentName getServiceComponent() {
132        return job.getService();
133    }
134
135    public int getUserId() {
136        return UserHandle.getUserId(uId);
137    }
138
139    public int getUid() {
140        return uId;
141    }
142
143    public PersistableBundle getExtras() {
144        return job.getExtras();
145    }
146
147    public boolean hasConnectivityConstraint() {
148        return job.getNetworkCapabilities() == JobInfo.NetworkType.ANY;
149    }
150
151    public boolean hasUnmeteredConstraint() {
152        return job.getNetworkCapabilities() == JobInfo.NetworkType.UNMETERED;
153    }
154
155    public boolean hasChargingConstraint() {
156        return job.isRequireCharging();
157    }
158
159    public boolean hasTimingDelayConstraint() {
160        return earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME;
161    }
162
163    public boolean hasDeadlineConstraint() {
164        return latestRunTimeElapsedMillis != NO_LATEST_RUNTIME;
165    }
166
167    public boolean hasIdleConstraint() {
168        return job.isRequireDeviceIdle();
169    }
170
171    public boolean isPersisted() {
172        return job.isPersisted();
173    }
174
175    public long getEarliestRunTime() {
176        return earliestRunTimeElapsedMillis;
177    }
178
179    public long getLatestRunTimeElapsed() {
180        return latestRunTimeElapsedMillis;
181    }
182
183    /**
184     * @return Whether or not this job is ready to run, based on its requirements.
185     */
186    public synchronized boolean isReady() {
187        return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
188                && (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
189                && (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
190                && (!hasUnmeteredConstraint() || unmeteredConstraintSatisfied.get())
191                && (!hasIdleConstraint() || idleConstraintSatisfied.get())
192                // Also ready if the deadline has expired - special case.
193                || (hasDeadlineConstraint() && deadlineConstraintSatisfied.get());
194    }
195
196    public boolean matches(int uid, int jobId) {
197        return this.job.getId() == jobId && this.uId == uid;
198    }
199
200    @Override
201    public String toString() {
202        return String.valueOf(hashCode()).substring(0, 3) + ".."
203                + ":[" + job.getService()
204                + ",jId=" + job.getId()
205                + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")"
206                + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging()
207                + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
208                + (isReady() ? "(READY)" : "")
209                + "]";
210    }
211    // Dumpsys infrastructure
212    public void dump(PrintWriter pw, String prefix) {
213        pw.println(this.toString());
214    }
215}
216