blob: 1955483ae13250f7cfa3d5daa1f8fcd6cb57f8f8 [file] [log] [blame]
Matthew Williams547b8162014-10-15 10:18:11 -07001/*
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 android.jobscheduler;
18
19import android.annotation.TargetApi;
Dianne Hackborn555b9c72017-04-10 15:18:05 -070020import android.app.job.JobInfo;
Matthew Williams547b8162014-10-15 10:18:11 -070021import android.app.job.JobParameters;
Dianne Hackborn555b9c72017-04-10 15:18:05 -070022import android.app.job.JobScheduler;
Matthew Williams547b8162014-10-15 10:18:11 -070023import android.app.job.JobService;
Dianne Hackborn555b9c72017-04-10 15:18:05 -070024import android.app.job.JobWorkItem;
Dianne Hackborn6a389872017-03-30 14:06:33 -070025import android.content.ClipData;
Dianne Hackborn555b9c72017-04-10 15:18:05 -070026import android.content.Context;
Dianne Hackborn6a389872017-03-30 14:06:33 -070027import android.content.Intent;
28import android.content.pm.PackageManager;
Dianne Hackborn9bfd0bf2017-04-13 18:07:53 -070029import android.net.Uri;
Dianne Hackborn6a389872017-03-30 14:06:33 -070030import android.os.Process;
Matthew Williams547b8162014-10-15 10:18:11 -070031import android.util.Log;
32
Dianne Hackborn555b9c72017-04-10 15:18:05 -070033import junit.framework.Assert;
34
35import java.util.ArrayList;
Matthew Williams547b8162014-10-15 10:18:11 -070036import java.util.concurrent.CountDownLatch;
37import java.util.concurrent.TimeUnit;
38
39/**
40 * Handles callback from the framework {@link android.app.job.JobScheduler}. The behaviour of this
41 * class is configured through the static
42 * {@link TestEnvironment}.
43 */
44@TargetApi(21)
45public class MockJobService extends JobService {
46 private static final String TAG = "MockJobService";
47
48 /** Wait this long before timing out the test. */
Christopher Tate459fffd2015-01-16 15:03:46 -080049 private static final long DEFAULT_TIMEOUT_MILLIS = 30000L; // 30 seconds.
Matthew Williams547b8162014-10-15 10:18:11 -070050
Dianne Hackborn555b9c72017-04-10 15:18:05 -070051 private JobParameters mParams;
52
53 ArrayList<Intent> mReceivedWork = new ArrayList<Intent>();
54
55 @Override
56 public void onDestroy() {
57 super.onDestroy();
58 if (TestEnvironment.getTestEnvironment().getExpectedWork() != null) {
59 TestEnvironment.getTestEnvironment().notifyExecution(mParams, 0, 0, mReceivedWork,
60 null);
61 }
62 }
63
Matthew Williams547b8162014-10-15 10:18:11 -070064 @Override
65 public void onCreate() {
66 super.onCreate();
Dianne Hackborn555b9c72017-04-10 15:18:05 -070067 Log.i(TAG, "Created test service.");
Matthew Williams547b8162014-10-15 10:18:11 -070068 }
69
70 @Override
71 public boolean onStartJob(JobParameters params) {
72 Log.i(TAG, "Test job executing: " + params.getJobId());
Dianne Hackborn555b9c72017-04-10 15:18:05 -070073 mParams = params;
Matthew Williams547b8162014-10-15 10:18:11 -070074
Dianne Hackborn6a389872017-03-30 14:06:33 -070075 int permCheckRead = PackageManager.PERMISSION_DENIED;
76 int permCheckWrite = PackageManager.PERMISSION_DENIED;
77 ClipData clip = params.getClipData();
78 if (clip != null) {
79 permCheckRead = checkUriPermission(clip.getItemAt(0).getUri(), Process.myPid(),
80 Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION);
81 permCheckWrite = checkUriPermission(clip.getItemAt(0).getUri(), Process.myPid(),
82 Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
83 }
84
Dianne Hackborn555b9c72017-04-10 15:18:05 -070085 TestWorkItem[] expectedWork = TestEnvironment.getTestEnvironment().getExpectedWork();
86 if (expectedWork != null) {
87 try {
88 if (TestEnvironment.getTestEnvironment().awaitDoWork()) {
89 TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
90 permCheckWrite, null, "Spent too long waiting to start executing work");
91 return false;
92 }
93 } catch (InterruptedException e) {
94 TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
95 permCheckWrite, null, "Failed waiting for work: " + e);
96 return false;
97 }
98 JobWorkItem work;
99 int index = 0;
100 while ((work = params.dequeueWork()) != null) {
101 Log.i(TAG, "Received work #" + index + ": " + work.getIntent());
102 mReceivedWork.add(work.getIntent());
Dianne Hackborn9bfd0bf2017-04-13 18:07:53 -0700103
104 if (index < expectedWork.length) {
105 TestWorkItem expected = expectedWork[index];
106 int grantFlags = work.getIntent().getFlags();
107 if (expected.requireUrisGranted != null) {
108 for (int ui = 0; ui < expected.requireUrisGranted.length; ui++) {
109 if ((grantFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
110 if (checkUriPermission(expected.requireUrisGranted[ui],
111 Process.myPid(), Process.myUid(),
112 Intent.FLAG_GRANT_READ_URI_PERMISSION)
113 != PackageManager.PERMISSION_GRANTED) {
114 TestEnvironment.getTestEnvironment().notifyExecution(params,
115 permCheckRead, permCheckWrite, null,
116 "Expected read permission but not granted: "
117 + expected.requireUrisGranted[ui]
118 + " @ #" + index);
119 return false;
120 }
121 }
122 if ((grantFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
123 if (checkUriPermission(expected.requireUrisGranted[ui],
124 Process.myPid(), Process.myUid(),
125 Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
126 != PackageManager.PERMISSION_GRANTED) {
127 TestEnvironment.getTestEnvironment().notifyExecution(params,
128 permCheckRead, permCheckWrite, null,
129 "Expected write permission but not granted: "
130 + expected.requireUrisGranted[ui]
131 + " @ #" + index);
132 return false;
133 }
134 }
135 }
136 }
137 if (expected.requireUrisNotGranted != null) {
138 // XXX note no delay here, current impl will have fully revoked the
139 // permission by the time we return from completing the last work.
140 for (int ui = 0; ui < expected.requireUrisNotGranted.length; ui++) {
141 if ((grantFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0) {
142 if (checkUriPermission(expected.requireUrisNotGranted[ui],
143 Process.myPid(), Process.myUid(),
144 Intent.FLAG_GRANT_READ_URI_PERMISSION)
145 != PackageManager.PERMISSION_DENIED) {
146 TestEnvironment.getTestEnvironment().notifyExecution(params,
147 permCheckRead, permCheckWrite, null,
148 "Not expected read permission but granted: "
149 + expected.requireUrisNotGranted[ui]
150 + " @ #" + index);
151 return false;
152 }
153 }
154 if ((grantFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0) {
155 if (checkUriPermission(expected.requireUrisNotGranted[ui],
156 Process.myPid(), Process.myUid(),
157 Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
158 != PackageManager.PERMISSION_DENIED) {
159 TestEnvironment.getTestEnvironment().notifyExecution(params,
160 permCheckRead, permCheckWrite, null,
161 "Not expected write permission but granted: "
162 + expected.requireUrisNotGranted[ui]
163 + " @ #" + index);
164 return false;
165 }
166 }
167 }
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700168 }
169 }
Dianne Hackborn9bfd0bf2017-04-13 18:07:53 -0700170
171 params.completeWork(work);
172
173 if (index < expectedWork.length) {
174 TestWorkItem expected = expectedWork[index];
175 if (expected.subitems != null) {
176 final TestWorkItem[] sub = expected.subitems;
177 final JobInfo ji = expected.jobInfo;
178 final JobScheduler js = (JobScheduler) getSystemService(
179 Context.JOB_SCHEDULER_SERVICE);
180 for (int subi = 0; subi < sub.length; subi++) {
181 js.enqueue(ji, new JobWorkItem(sub[subi].intent));
182 }
183 }
184 }
185
186 index++;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700187 }
188 Log.i(TAG, "Done with all work at #" + index);
189 // We don't notifyExecution here because we want to make sure the job properly
190 // stops itself.
191 return true;
192 } else {
193 TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
194 permCheckWrite, null, null);
195 return false; // No work to do.
196 }
Matthew Williams547b8162014-10-15 10:18:11 -0700197 }
198
199 @Override
200 public boolean onStopJob(JobParameters params) {
201 return false;
202 }
203
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700204 public static final class TestWorkItem {
205 public final Intent intent;
206 public final JobInfo jobInfo;
207 public final TestWorkItem[] subitems;
Dianne Hackborn9bfd0bf2017-04-13 18:07:53 -0700208 public final Uri[] requireUrisGranted;
209 public final Uri[] requireUrisNotGranted;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700210
211 public TestWorkItem(Intent _intent) {
212 intent = _intent;
213 jobInfo = null;
214 subitems = null;
Dianne Hackborn9bfd0bf2017-04-13 18:07:53 -0700215 requireUrisGranted = null;
216 requireUrisNotGranted = null;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700217 }
218
219 public TestWorkItem(Intent _intent, JobInfo _jobInfo, TestWorkItem[] _subitems) {
220 intent = _intent;
221 jobInfo = _jobInfo;
222 subitems = _subitems;
Dianne Hackborn9bfd0bf2017-04-13 18:07:53 -0700223 requireUrisGranted = null;
224 requireUrisNotGranted = null;
225 }
226
227 public TestWorkItem(Intent _intent, Uri[] _requireUrisGranted,
228 Uri[] _requireUrisNotGranted) {
229 intent = _intent;
230 jobInfo = null;
231 subitems = null;
232 requireUrisGranted = _requireUrisGranted;
233 requireUrisNotGranted = _requireUrisNotGranted;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700234 }
235 }
236
Matthew Williams547b8162014-10-15 10:18:11 -0700237 /**
238 * Configures the expected behaviour for each test. This object is shared across consecutive
239 * tests, so to clear state each test is responsible for calling
240 * {@link TestEnvironment#setUp()}.
241 */
242 public static final class TestEnvironment {
243
244 private static TestEnvironment kTestEnvironment;
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700245 //public static final int INVALID_JOB_ID = -1;
Matthew Williams547b8162014-10-15 10:18:11 -0700246
247 private CountDownLatch mLatch;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700248 private CountDownLatch mDoWorkLatch;
249 private TestWorkItem[] mExpectedWork;
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700250 private JobParameters mExecutedJobParameters;
Dianne Hackborn6a389872017-03-30 14:06:33 -0700251 private int mExecutedPermCheckRead;
252 private int mExecutedPermCheckWrite;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700253 private ArrayList<Intent> mExecutedReceivedWork;
254 private String mExecutedErrorMessage;
Matthew Williams547b8162014-10-15 10:18:11 -0700255
256 public static TestEnvironment getTestEnvironment() {
257 if (kTestEnvironment == null) {
258 kTestEnvironment = new TestEnvironment();
259 }
260 return kTestEnvironment;
261 }
262
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700263 public TestWorkItem[] getExpectedWork() {
264 return mExpectedWork;
265 }
266
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700267 public JobParameters getLastJobParameters() {
268 return mExecutedJobParameters;
269 }
270
Dianne Hackborn6a389872017-03-30 14:06:33 -0700271 public int getLastPermCheckRead() {
272 return mExecutedPermCheckRead;
273 }
274
275 public int getLastPermCheckWrite() {
276 return mExecutedPermCheckWrite;
277 }
278
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700279 public ArrayList<Intent> getLastReceivedWork() {
280 return mExecutedReceivedWork;
281 }
282
283 public String getLastErrorMessage() {
284 return mExecutedErrorMessage;
285 }
286
Matthew Williams547b8162014-10-15 10:18:11 -0700287 /**
288 * Block the test thread, waiting on the JobScheduler to execute some previously scheduled
289 * job on this service.
290 */
291 public boolean awaitExecution() throws InterruptedException {
292 final boolean executed = mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700293 if (getLastErrorMessage() != null) {
294 Assert.fail(getLastErrorMessage());
295 }
Matthew Williams547b8162014-10-15 10:18:11 -0700296 return executed;
297 }
298
299 /**
300 * Block the test thread, expecting to timeout but still listening to ensure that no jobs
301 * land in the interim.
302 * @return True if the latch timed out waiting on an execution.
303 */
304 public boolean awaitTimeout() throws InterruptedException {
305 return !mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
306 }
307
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700308 public boolean awaitDoWork() throws InterruptedException {
309 return !mDoWorkLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
310 }
311
312 private void notifyExecution(JobParameters params, int permCheckRead, int permCheckWrite,
313 ArrayList<Intent> receivedWork, String errorMsg) {
Dianne Hackborn6a389872017-03-30 14:06:33 -0700314 //Log.d(TAG, "Job executed:" + params.getJobId());
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700315 mExecutedJobParameters = params;
Dianne Hackborn6a389872017-03-30 14:06:33 -0700316 mExecutedPermCheckRead = permCheckRead;
317 mExecutedPermCheckWrite = permCheckWrite;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700318 mExecutedReceivedWork = receivedWork;
319 mExecutedErrorMessage = errorMsg;
Matthew Williams547b8162014-10-15 10:18:11 -0700320 mLatch.countDown();
321 }
322
323 public void setExpectedExecutions(int numExecutions) {
324 // For no executions expected, set count to 1 so we can still block for the timeout.
325 if (numExecutions == 0) {
326 mLatch = new CountDownLatch(1);
327 } else {
328 mLatch = new CountDownLatch(numExecutions);
329 }
330 }
331
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700332 public void setExpectedWork(TestWorkItem[] work) {
333 mExpectedWork = work;
334 mDoWorkLatch = new CountDownLatch(1);
335 }
336
337 public void readyToWork() {
338 mDoWorkLatch.countDown();
339 }
340
Matthew Williams547b8162014-10-15 10:18:11 -0700341 /** Called in each testCase#setup */
342 public void setUp() {
343 mLatch = null;
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700344 mExecutedJobParameters = null;
Matthew Williams547b8162014-10-15 10:18:11 -0700345 }
346
347 }
348}