blob: 7d06682234c64d8cccbe9e1d8a4afc4a38297264 [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;
29import android.os.Process;
Matthew Williams547b8162014-10-15 10:18:11 -070030import android.util.Log;
31
Dianne Hackborn555b9c72017-04-10 15:18:05 -070032import junit.framework.Assert;
33
34import java.util.ArrayList;
Matthew Williams547b8162014-10-15 10:18:11 -070035import java.util.concurrent.CountDownLatch;
36import java.util.concurrent.TimeUnit;
37
38/**
39 * Handles callback from the framework {@link android.app.job.JobScheduler}. The behaviour of this
40 * class is configured through the static
41 * {@link TestEnvironment}.
42 */
43@TargetApi(21)
44public class MockJobService extends JobService {
45 private static final String TAG = "MockJobService";
46
47 /** Wait this long before timing out the test. */
Christopher Tate459fffd2015-01-16 15:03:46 -080048 private static final long DEFAULT_TIMEOUT_MILLIS = 30000L; // 30 seconds.
Matthew Williams547b8162014-10-15 10:18:11 -070049
Dianne Hackborn555b9c72017-04-10 15:18:05 -070050 private JobParameters mParams;
51
52 ArrayList<Intent> mReceivedWork = new ArrayList<Intent>();
53
54 @Override
55 public void onDestroy() {
56 super.onDestroy();
57 if (TestEnvironment.getTestEnvironment().getExpectedWork() != null) {
58 TestEnvironment.getTestEnvironment().notifyExecution(mParams, 0, 0, mReceivedWork,
59 null);
60 }
61 }
62
Matthew Williams547b8162014-10-15 10:18:11 -070063 @Override
64 public void onCreate() {
65 super.onCreate();
Dianne Hackborn555b9c72017-04-10 15:18:05 -070066 Log.i(TAG, "Created test service.");
Matthew Williams547b8162014-10-15 10:18:11 -070067 }
68
69 @Override
70 public boolean onStartJob(JobParameters params) {
71 Log.i(TAG, "Test job executing: " + params.getJobId());
Dianne Hackborn555b9c72017-04-10 15:18:05 -070072 mParams = params;
Matthew Williams547b8162014-10-15 10:18:11 -070073
Dianne Hackborn6a389872017-03-30 14:06:33 -070074 int permCheckRead = PackageManager.PERMISSION_DENIED;
75 int permCheckWrite = PackageManager.PERMISSION_DENIED;
76 ClipData clip = params.getClipData();
77 if (clip != null) {
78 permCheckRead = checkUriPermission(clip.getItemAt(0).getUri(), Process.myPid(),
79 Process.myUid(), Intent.FLAG_GRANT_READ_URI_PERMISSION);
80 permCheckWrite = checkUriPermission(clip.getItemAt(0).getUri(), Process.myPid(),
81 Process.myUid(), Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
82 }
83
Dianne Hackborn555b9c72017-04-10 15:18:05 -070084 TestWorkItem[] expectedWork = TestEnvironment.getTestEnvironment().getExpectedWork();
85 if (expectedWork != null) {
86 try {
87 if (TestEnvironment.getTestEnvironment().awaitDoWork()) {
88 TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
89 permCheckWrite, null, "Spent too long waiting to start executing work");
90 return false;
91 }
92 } catch (InterruptedException e) {
93 TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
94 permCheckWrite, null, "Failed waiting for work: " + e);
95 return false;
96 }
97 JobWorkItem work;
98 int index = 0;
99 while ((work = params.dequeueWork()) != null) {
100 Log.i(TAG, "Received work #" + index + ": " + work.getIntent());
101 mReceivedWork.add(work.getIntent());
102 params.completeWork(work);
103 if (index < expectedWork.length && expectedWork[index].subitems != null) {
104 final TestWorkItem[] sub = expectedWork[index].subitems;
105 final JobInfo ji = expectedWork[index].jobInfo;
106 final JobScheduler js = (JobScheduler) getSystemService(
107 Context.JOB_SCHEDULER_SERVICE);
108 for (int subi = 0; subi < sub.length; subi++) {
109 js.enqueue(ji, new JobWorkItem(sub[subi].intent));
110 }
111 }
112 }
113 Log.i(TAG, "Done with all work at #" + index);
114 // We don't notifyExecution here because we want to make sure the job properly
115 // stops itself.
116 return true;
117 } else {
118 TestEnvironment.getTestEnvironment().notifyExecution(params, permCheckRead,
119 permCheckWrite, null, null);
120 return false; // No work to do.
121 }
Matthew Williams547b8162014-10-15 10:18:11 -0700122 }
123
124 @Override
125 public boolean onStopJob(JobParameters params) {
126 return false;
127 }
128
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700129 public static final class TestWorkItem {
130 public final Intent intent;
131 public final JobInfo jobInfo;
132 public final TestWorkItem[] subitems;
133
134 public TestWorkItem(Intent _intent) {
135 intent = _intent;
136 jobInfo = null;
137 subitems = null;
138 }
139
140 public TestWorkItem(Intent _intent, JobInfo _jobInfo, TestWorkItem[] _subitems) {
141 intent = _intent;
142 jobInfo = _jobInfo;
143 subitems = _subitems;
144 }
145 }
146
Matthew Williams547b8162014-10-15 10:18:11 -0700147 /**
148 * Configures the expected behaviour for each test. This object is shared across consecutive
149 * tests, so to clear state each test is responsible for calling
150 * {@link TestEnvironment#setUp()}.
151 */
152 public static final class TestEnvironment {
153
154 private static TestEnvironment kTestEnvironment;
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700155 //public static final int INVALID_JOB_ID = -1;
Matthew Williams547b8162014-10-15 10:18:11 -0700156
157 private CountDownLatch mLatch;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700158 private CountDownLatch mDoWorkLatch;
159 private TestWorkItem[] mExpectedWork;
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700160 private JobParameters mExecutedJobParameters;
Dianne Hackborn6a389872017-03-30 14:06:33 -0700161 private int mExecutedPermCheckRead;
162 private int mExecutedPermCheckWrite;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700163 private ArrayList<Intent> mExecutedReceivedWork;
164 private String mExecutedErrorMessage;
Matthew Williams547b8162014-10-15 10:18:11 -0700165
166 public static TestEnvironment getTestEnvironment() {
167 if (kTestEnvironment == null) {
168 kTestEnvironment = new TestEnvironment();
169 }
170 return kTestEnvironment;
171 }
172
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700173 public TestWorkItem[] getExpectedWork() {
174 return mExpectedWork;
175 }
176
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700177 public JobParameters getLastJobParameters() {
178 return mExecutedJobParameters;
179 }
180
Dianne Hackborn6a389872017-03-30 14:06:33 -0700181 public int getLastPermCheckRead() {
182 return mExecutedPermCheckRead;
183 }
184
185 public int getLastPermCheckWrite() {
186 return mExecutedPermCheckWrite;
187 }
188
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700189 public ArrayList<Intent> getLastReceivedWork() {
190 return mExecutedReceivedWork;
191 }
192
193 public String getLastErrorMessage() {
194 return mExecutedErrorMessage;
195 }
196
Matthew Williams547b8162014-10-15 10:18:11 -0700197 /**
198 * Block the test thread, waiting on the JobScheduler to execute some previously scheduled
199 * job on this service.
200 */
201 public boolean awaitExecution() throws InterruptedException {
202 final boolean executed = mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700203 if (getLastErrorMessage() != null) {
204 Assert.fail(getLastErrorMessage());
205 }
Matthew Williams547b8162014-10-15 10:18:11 -0700206 return executed;
207 }
208
209 /**
210 * Block the test thread, expecting to timeout but still listening to ensure that no jobs
211 * land in the interim.
212 * @return True if the latch timed out waiting on an execution.
213 */
214 public boolean awaitTimeout() throws InterruptedException {
215 return !mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
216 }
217
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700218 public boolean awaitDoWork() throws InterruptedException {
219 return !mDoWorkLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
220 }
221
222 private void notifyExecution(JobParameters params, int permCheckRead, int permCheckWrite,
223 ArrayList<Intent> receivedWork, String errorMsg) {
Dianne Hackborn6a389872017-03-30 14:06:33 -0700224 //Log.d(TAG, "Job executed:" + params.getJobId());
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700225 mExecutedJobParameters = params;
Dianne Hackborn6a389872017-03-30 14:06:33 -0700226 mExecutedPermCheckRead = permCheckRead;
227 mExecutedPermCheckWrite = permCheckWrite;
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700228 mExecutedReceivedWork = receivedWork;
229 mExecutedErrorMessage = errorMsg;
Matthew Williams547b8162014-10-15 10:18:11 -0700230 mLatch.countDown();
231 }
232
233 public void setExpectedExecutions(int numExecutions) {
234 // For no executions expected, set count to 1 so we can still block for the timeout.
235 if (numExecutions == 0) {
236 mLatch = new CountDownLatch(1);
237 } else {
238 mLatch = new CountDownLatch(numExecutions);
239 }
240 }
241
Dianne Hackborn555b9c72017-04-10 15:18:05 -0700242 public void setExpectedWork(TestWorkItem[] work) {
243 mExpectedWork = work;
244 mDoWorkLatch = new CountDownLatch(1);
245 }
246
247 public void readyToWork() {
248 mDoWorkLatch.countDown();
249 }
250
Matthew Williams547b8162014-10-15 10:18:11 -0700251 /** Called in each testCase#setup */
252 public void setUp() {
253 mLatch = null;
Matthew Williamsbe8620e2015-05-14 17:49:50 -0700254 mExecutedJobParameters = null;
Matthew Williams547b8162014-10-15 10:18:11 -0700255 }
256
257 }
258}