1 /* 2 * Copyright (C) 2017 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 android.jobscheduler.cts; 18 19 import android.annotation.TargetApi; 20 import android.app.job.JobInfo; 21 import android.app.job.JobWorkItem; 22 import android.content.ContentProviderClient; 23 import android.content.Intent; 24 import android.jobscheduler.MockJobService.TestWorkItem; 25 import android.net.Uri; 26 27 import com.android.compatibility.common.util.SystemUtil; 28 29 import java.util.ArrayList; 30 31 /** 32 * Schedules jobs with the {@link android.app.job.JobScheduler} by enqueue work in to 33 * them and processing it. 34 */ 35 @TargetApi(26) 36 public class EnqueueJobWorkTest extends ConstraintTest { 37 private static final String TAG = "ClipDataJobTest"; 38 39 /** Unique identifier for the job scheduled by this suite of tests. */ 40 public static final int ENQUEUE_WORK_JOB_ID = EnqueueJobWorkTest.class.hashCode(); 41 42 private JobInfo.Builder mBuilder; 43 private ContentProviderClient mProvider; 44 45 @Override 46 public void setUp() throws Exception { 47 super.setUp(); 48 49 mBuilder = new JobInfo.Builder(ENQUEUE_WORK_JOB_ID, kJobServiceComponent); 50 mProvider = getContext().getContentResolver().acquireContentProviderClient(mFirstUri); 51 } 52 53 @Override 54 public void tearDown() throws Exception { 55 super.tearDown(); 56 mProvider.close(); 57 mJobScheduler.cancel(ENQUEUE_WORK_JOB_ID); 58 } 59 60 private boolean intentEquals(Intent i1, Intent i2) { 61 if (i1 == i2) { 62 return true; 63 } 64 if (i1 == null || i2 == null) { 65 return false; 66 } 67 return i1.filterEquals(i2); 68 } 69 70 private void compareWork(TestWorkItem[] expected, ArrayList<JobWorkItem> received) { 71 if (received == null) { 72 fail("Didn't receive any expected work."); 73 } 74 ArrayList<TestWorkItem> expectedArray = new ArrayList<>(); 75 for (int i = 0; i < expected.length; i++) { 76 expectedArray.add(expected[i]); 77 } 78 for (int i = 0; i < received.size(); i++) { 79 JobWorkItem work = received.get(i); 80 if (i < expected.length && expected[i].subitems != null) { 81 TestWorkItem[] sub = expected[i].subitems; 82 for (int j = 0; j < sub.length; j++) { 83 expectedArray.add(sub[j]); 84 } 85 } 86 if (i >= expectedArray.size()) { 87 fail("Received more than " + expected.length + " work items, first extra is " 88 + work); 89 } 90 if (!intentEquals(work.getIntent(), expectedArray.get(i).intent)) { 91 fail("Received work #" + i + " " + work.getIntent() + " but expected " + expected[i]); 92 } 93 if (work.getDeliveryCount() != expectedArray.get(i).deliveryCount) { 94 fail("Received work #" + i + " " + work.getIntent() + " delivery count is " 95 + work.getDeliveryCount() + " but expected " 96 + expectedArray.get(i).deliveryCount); 97 } 98 } 99 if (received.size() < expected.length) { 100 fail("Received only " + received.size() + " work items, but expected " 101 + expected.length); 102 } 103 } 104 105 /** 106 * Test basic enqueueing of work. 107 */ 108 public void testEnqueueOneWork() throws Exception { 109 Intent work1 = new Intent("work1"); 110 TestWorkItem[] work = new TestWorkItem[] { new TestWorkItem(work1) }; 111 kTestEnvironment.setExpectedExecutions(1); 112 kTestEnvironment.setExpectedWork(work); 113 mJobScheduler.enqueue(mBuilder.setOverrideDeadline(0).build(), new JobWorkItem(work1)); 114 kTestEnvironment.readyToWork(); 115 assertTrue("Job with work enqueued did not fire.", 116 kTestEnvironment.awaitExecution()); 117 compareWork(work, kTestEnvironment.getLastReceivedWork()); 118 if (kTestEnvironment.getLastErrorMessage() != null) { 119 fail(kTestEnvironment.getLastErrorMessage()); 120 } 121 } 122 123 /** 124 * Test basic enqueueing batches of work. 125 */ 126 public void testEnqueueMultipleWork() throws Exception { 127 Intent work1 = new Intent("work1"); 128 Intent work2 = new Intent("work2"); 129 Intent work3 = new Intent("work3"); 130 Intent work4 = new Intent("work4"); 131 Intent work5 = new Intent("work5"); 132 Intent work6 = new Intent("work6"); 133 Intent work7 = new Intent("work7"); 134 Intent work8 = new Intent("work8"); 135 TestWorkItem[] work = new TestWorkItem[] { 136 new TestWorkItem(work1), new TestWorkItem(work2), new TestWorkItem(work3), 137 new TestWorkItem(work4), new TestWorkItem(work5), new TestWorkItem(work6), 138 new TestWorkItem(work7), new TestWorkItem(work8) }; 139 kTestEnvironment.setExpectedExecutions(1); 140 kTestEnvironment.setExpectedWork(work); 141 JobInfo ji = mBuilder.setOverrideDeadline(0).build(); 142 mJobScheduler.enqueue(ji, new JobWorkItem(work1)); 143 mJobScheduler.enqueue(ji, new JobWorkItem(work2)); 144 mJobScheduler.enqueue(ji, new JobWorkItem(work3)); 145 mJobScheduler.enqueue(ji, new JobWorkItem(work4)); 146 mJobScheduler.enqueue(ji, new JobWorkItem(work5)); 147 mJobScheduler.enqueue(ji, new JobWorkItem(work6)); 148 mJobScheduler.enqueue(ji, new JobWorkItem(work7)); 149 mJobScheduler.enqueue(ji, new JobWorkItem(work8)); 150 kTestEnvironment.readyToWork(); 151 assertTrue("Job with work enqueued did not fire.", 152 kTestEnvironment.awaitExecution()); 153 compareWork(work, kTestEnvironment.getLastReceivedWork()); 154 } 155 156 /** 157 * Test basic enqueueing batches of work, with new work coming in while processing existing 158 * work. 159 */ 160 public void testEnqueueMultipleSubWork() throws Exception { 161 Intent work1 = new Intent("work1"); 162 Intent work2 = new Intent("work2"); 163 Intent work3 = new Intent("work3"); 164 Intent work4 = new Intent("work4"); 165 Intent work5 = new Intent("work5"); 166 Intent work6 = new Intent("work6"); 167 Intent work7 = new Intent("work7"); 168 Intent work8 = new Intent("work8"); 169 JobInfo ji = mBuilder.setOverrideDeadline(0).build(); 170 TestWorkItem[] work = new TestWorkItem[]{ 171 new TestWorkItem(work1), new TestWorkItem(work2), new TestWorkItem(work3), 172 new TestWorkItem(work4, ji, new TestWorkItem[] { 173 new TestWorkItem(work5), new TestWorkItem(work6), 174 new TestWorkItem(work7), new TestWorkItem(work8)}) 175 }; 176 kTestEnvironment.setExpectedExecutions(1); 177 kTestEnvironment.setExpectedWork(work); 178 mJobScheduler.enqueue(ji, new JobWorkItem(work1)); 179 mJobScheduler.enqueue(ji, new JobWorkItem(work2)); 180 mJobScheduler.enqueue(ji, new JobWorkItem(work3)); 181 mJobScheduler.enqueue(ji, new JobWorkItem(work4)); 182 kTestEnvironment.readyToWork(); 183 assertTrue("Job with work enqueued did not fire.", 184 kTestEnvironment.awaitExecution()); 185 compareWork(work, kTestEnvironment.getLastReceivedWork()); 186 } 187 188 /** 189 * Test job getting stopped while processing work and that work being redelivered. 190 */ 191 public void testEnqueueMultipleRedeliver() throws Exception { 192 Intent work1 = new Intent("work1"); 193 Intent work2 = new Intent("work2"); 194 Intent work3 = new Intent("work3"); 195 Intent work4 = new Intent("work4"); 196 Intent work5 = new Intent("work5"); 197 Intent work6 = new Intent("work6"); 198 TestWorkItem[] initialWork = new TestWorkItem[] { 199 new TestWorkItem(work1), new TestWorkItem(work2), new TestWorkItem(work3), 200 new TestWorkItem(work4, TestWorkItem.FLAG_WAIT_FOR_STOP, 1) }; 201 kTestEnvironment.setExpectedExecutions(1); 202 kTestEnvironment.setExpectedWaitForStop(); 203 kTestEnvironment.setExpectedWork(initialWork); 204 JobInfo ji = mBuilder.setOverrideDeadline(0).build(); 205 mJobScheduler.enqueue(ji, new JobWorkItem(work1)); 206 mJobScheduler.enqueue(ji, new JobWorkItem(work2)); 207 mJobScheduler.enqueue(ji, new JobWorkItem(work3)); 208 mJobScheduler.enqueue(ji, new JobWorkItem(work4)); 209 kTestEnvironment.readyToWork(); 210 211 // Now wait for the job to get to the point where it is processing the last 212 // work and waiting for it to be stopped. 213 assertTrue("Job with work enqueued did not wait to stop.", 214 kTestEnvironment.awaitWaitingForStop()); 215 216 // Cause the job to timeout (stop) immediately, and wait for its execution to finish. 217 SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler timeout " 218 + kJobServiceComponent.getPackageName() + " " + ENQUEUE_WORK_JOB_ID); 219 assertTrue("Job with work enqueued did not finish.", 220 kTestEnvironment.awaitExecution()); 221 compareWork(initialWork, kTestEnvironment.getLastReceivedWork()); 222 223 // Now we are going to add some more work, restart the job, and see if it correctly 224 // redelivers the last work and delivers the new work. 225 TestWorkItem[] finalWork = new TestWorkItem[] { 226 new TestWorkItem(work4, 0, 2), new TestWorkItem(work5), new TestWorkItem(work6) }; 227 kTestEnvironment.setExpectedExecutions(1); 228 kTestEnvironment.setExpectedWork(finalWork); 229 mJobScheduler.enqueue(ji, new JobWorkItem(work5)); 230 mJobScheduler.enqueue(ji, new JobWorkItem(work6)); 231 kTestEnvironment.readyToWork(); 232 SystemUtil.runShellCommand(getInstrumentation(), "cmd jobscheduler run " 233 + kJobServiceComponent.getPackageName() + " " + ENQUEUE_WORK_JOB_ID); 234 235 assertTrue("Restarted with work enqueued did not execute.", 236 kTestEnvironment.awaitExecution()); 237 compareWork(finalWork, kTestEnvironment.getLastReceivedWork()); 238 } 239 240 /** 241 * Test basic enqueueing batches of work. 242 */ 243 public void testEnqueueMultipleUriGrantWork() throws Exception { 244 // Start out with storage low, so job is enqueued but not executed yet. 245 setStorageState(true); 246 247 // We need to get a permission grant so that we can grant it to ourself. 248 mProvider.call("grant", MY_PACKAGE, mFirstUriBundle); 249 mProvider.call("grant", MY_PACKAGE, mSecondUriBundle); 250 assertHasUriPermission(mFirstUri, Intent.FLAG_GRANT_READ_URI_PERMISSION 251 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 252 assertHasUriPermission(mSecondUri, Intent.FLAG_GRANT_READ_URI_PERMISSION 253 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 254 255 Intent work1 = new Intent("work1"); 256 work1.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 257 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 258 work1.setData(mFirstUri); 259 work1.setClipData(mSecondClipData); 260 261 Intent work2 = new Intent("work2"); 262 work2.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION 263 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 264 work2.setData(mFirstUri); 265 266 TestWorkItem[] work = new TestWorkItem[] { 267 new TestWorkItem(work1, new Uri[] { mFirstUri, mSecondUri}, new Uri[0]), 268 new TestWorkItem(work2, new Uri[] { mFirstUri }, new Uri[] { mSecondUri}) }; 269 kTestEnvironment.setExpectedExecutions(1); 270 kTestEnvironment.setExpectedWork(work); 271 JobInfo ji = mBuilder.setOverrideDeadline(0).setRequiresStorageNotLow(true).build(); 272 mJobScheduler.enqueue(ji, new JobWorkItem(work1)); 273 mJobScheduler.enqueue(ji, new JobWorkItem(work2)); 274 275 // Remove the explicit grant, we should still have a grant due to the job. 276 mProvider.call("revoke", MY_PACKAGE, mFirstUriBundle); 277 mProvider.call("revoke", MY_PACKAGE, mSecondUriBundle); 278 assertHasUriPermission(mFirstUri, Intent.FLAG_GRANT_READ_URI_PERMISSION 279 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 280 assertHasUriPermission(mSecondUri, Intent.FLAG_GRANT_READ_URI_PERMISSION 281 | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); 282 283 kTestEnvironment.readyToWork(); 284 285 // Now allow the job to run. 286 setStorageState(false); 287 288 assertTrue("Job with work enqueued did not fire.", 289 kTestEnvironment.awaitExecution()); 290 compareWork(work, kTestEnvironment.getLastReceivedWork()); 291 292 // And wait for everything to be cleaned up. 293 waitPermissionRevoke(mFirstUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 5000); 294 waitPermissionRevoke(mSecondUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 5000); 295 } 296 } 297