1 /* 2 * Copyright (C) 2020 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 static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; 20 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; 21 22 import android.app.job.JobInfo; 23 import android.content.ClipData; 24 import android.content.Intent; 25 import android.net.NetworkRequest; 26 import android.net.Uri; 27 import android.os.Bundle; 28 import android.os.PersistableBundle; 29 import android.provider.ContactsContract; 30 import android.provider.MediaStore; 31 32 /** 33 * Tests related to created and reading JobInfo objects. 34 */ 35 public class JobInfoTest extends BaseJobSchedulerTest { 36 private static final int JOB_ID = JobInfoTest.class.hashCode(); 37 38 @Override tearDown()39 public void tearDown() throws Exception { 40 mJobScheduler.cancel(JOB_ID); 41 42 // The super method should be called at the end. 43 super.tearDown(); 44 } 45 testBackoffCriteria()46 public void testBackoffCriteria() { 47 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 48 .setBackoffCriteria(12345, JobInfo.BACKOFF_POLICY_LINEAR) 49 .build(); 50 assertEquals(12345, ji.getInitialBackoffMillis()); 51 assertEquals(JobInfo.BACKOFF_POLICY_LINEAR, ji.getBackoffPolicy()); 52 // Confirm JobScheduler accepts the JobInfo object. 53 mJobScheduler.schedule(ji); 54 55 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 56 .setBackoffCriteria(54321, JobInfo.BACKOFF_POLICY_EXPONENTIAL) 57 .build(); 58 assertEquals(54321, ji.getInitialBackoffMillis()); 59 assertEquals(JobInfo.BACKOFF_POLICY_EXPONENTIAL, ji.getBackoffPolicy()); 60 // Confirm JobScheduler accepts the JobInfo object. 61 mJobScheduler.schedule(ji); 62 } 63 testBatteryNotLow()64 public void testBatteryNotLow() { 65 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 66 .setRequiresBatteryNotLow(true) 67 .build(); 68 assertTrue(ji.isRequireBatteryNotLow()); 69 // Confirm JobScheduler accepts the JobInfo object. 70 mJobScheduler.schedule(ji); 71 72 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 73 .setRequiresBatteryNotLow(false) 74 .build(); 75 assertFalse(ji.isRequireBatteryNotLow()); 76 // Confirm JobScheduler accepts the JobInfo object. 77 mJobScheduler.schedule(ji); 78 } 79 testCharging()80 public void testCharging() { 81 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 82 .setRequiresCharging(true) 83 .build(); 84 assertTrue(ji.isRequireCharging()); 85 // Confirm JobScheduler accepts the JobInfo object. 86 mJobScheduler.schedule(ji); 87 88 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 89 .setRequiresCharging(false) 90 .build(); 91 assertFalse(ji.isRequireCharging()); 92 // Confirm JobScheduler accepts the JobInfo object. 93 mJobScheduler.schedule(ji); 94 } 95 testClipData()96 public void testClipData() { 97 final ClipData clipData = ClipData.newPlainText("test", "testText"); 98 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 99 .setClipData(clipData, Intent.FLAG_GRANT_READ_URI_PERMISSION) 100 .build(); 101 assertEquals(clipData, ji.getClipData()); 102 assertEquals(Intent.FLAG_GRANT_READ_URI_PERMISSION, ji.getClipGrantFlags()); 103 // Confirm JobScheduler accepts the JobInfo object. 104 mJobScheduler.schedule(ji); 105 106 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 107 .setClipData(null, 0) 108 .build(); 109 assertNull(ji.getClipData()); 110 assertEquals(0, ji.getClipGrantFlags()); 111 // Confirm JobScheduler accepts the JobInfo object. 112 mJobScheduler.schedule(ji); 113 } 114 testDeviceIdle()115 public void testDeviceIdle() { 116 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 117 .setRequiresDeviceIdle(true) 118 .build(); 119 assertTrue(ji.isRequireDeviceIdle()); 120 // Confirm JobScheduler accepts the JobInfo object. 121 mJobScheduler.schedule(ji); 122 123 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 124 .setRequiresDeviceIdle(false) 125 .build(); 126 assertFalse(ji.isRequireDeviceIdle()); 127 // Confirm JobScheduler accepts the JobInfo object. 128 mJobScheduler.schedule(ji); 129 } 130 testEstimatedNetworkBytes()131 public void testEstimatedNetworkBytes() { 132 assertBuildFails( 133 "Successfully built a JobInfo specifying estimated network bytes without" 134 + " requesting network", 135 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 136 .setEstimatedNetworkBytes(500, 1000)); 137 138 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 139 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 140 .setEstimatedNetworkBytes(500, 1000) 141 .build(); 142 assertEquals(500, ji.getEstimatedNetworkDownloadBytes()); 143 assertEquals(1000, ji.getEstimatedNetworkUploadBytes()); 144 // Confirm JobScheduler accepts the JobInfo object. 145 mJobScheduler.schedule(ji); 146 } 147 testExtras()148 public void testExtras() { 149 final PersistableBundle pb = new PersistableBundle(); 150 pb.putInt("random_key", 42); 151 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 152 .setPersisted(true) 153 .setExtras(pb) 154 .build(); 155 final PersistableBundle extras = ji.getExtras(); 156 assertNotNull(extras); 157 assertEquals(1, extras.keySet().size()); 158 assertEquals(42, extras.getInt("random_key")); 159 // Confirm JobScheduler accepts the JobInfo object. 160 mJobScheduler.schedule(ji); 161 } 162 testExpeditedJob()163 public void testExpeditedJob() { 164 // Test all allowed constraints. 165 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 166 .setExpedited(true) 167 .setPersisted(true) 168 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 169 .setRequiresStorageNotLow(true) 170 .build(); 171 assertTrue(ji.isExpedited()); 172 // Confirm JobScheduler accepts the JobInfo object. 173 mJobScheduler.schedule(ji); 174 175 // Test disallowed constraints. 176 final String failureMessage = 177 "Successfully built an expedited JobInfo object with disallowed constraints"; 178 assertBuildFails(failureMessage, 179 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 180 .setExpedited(true) 181 .setMinimumLatency(100)); 182 assertBuildFails(failureMessage, 183 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 184 .setExpedited(true) 185 .setOverrideDeadline(200)); 186 assertBuildFails(failureMessage, 187 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 188 .setExpedited(true) 189 .setPeriodic(15 * 60_000)); 190 assertBuildFails(failureMessage, 191 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 192 .setExpedited(true) 193 .setImportantWhileForeground(true)); 194 assertBuildFails(failureMessage, 195 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 196 .setExpedited(true) 197 .setPrefetch(true)); 198 assertBuildFails(failureMessage, 199 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 200 .setExpedited(true) 201 .setRequiresDeviceIdle(true)); 202 assertBuildFails(failureMessage, 203 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 204 .setExpedited(true) 205 .setRequiresBatteryNotLow(true)); 206 assertBuildFails(failureMessage, 207 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 208 .setExpedited(true) 209 .setRequiresCharging(true)); 210 final JobInfo.TriggerContentUri tcu = new JobInfo.TriggerContentUri( 211 Uri.parse("content://" + MediaStore.AUTHORITY + "/"), 212 JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS); 213 assertBuildFails(failureMessage, 214 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 215 .setExpedited(true) 216 .addTriggerContentUri(tcu)); 217 } 218 testImportantWhileForeground()219 public void testImportantWhileForeground() { 220 // Assert the default value is false 221 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 222 .build(); 223 assertFalse(ji.isImportantWhileForeground()); 224 // Confirm JobScheduler accepts the JobInfo object. 225 mJobScheduler.schedule(ji); 226 227 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 228 .setImportantWhileForeground(true) 229 .build(); 230 assertTrue(ji.isImportantWhileForeground()); 231 // Confirm JobScheduler accepts the JobInfo object. 232 mJobScheduler.schedule(ji); 233 234 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 235 .setImportantWhileForeground(false) 236 .build(); 237 assertFalse(ji.isImportantWhileForeground()); 238 // Confirm JobScheduler accepts the JobInfo object. 239 mJobScheduler.schedule(ji); 240 } 241 testMinimumLatency()242 public void testMinimumLatency() { 243 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 244 .setMinimumLatency(1337) 245 .build(); 246 assertEquals(1337, ji.getMinLatencyMillis()); 247 // Confirm JobScheduler accepts the JobInfo object. 248 mJobScheduler.schedule(ji); 249 } 250 testOverrideDeadline()251 public void testOverrideDeadline() { 252 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 253 .setOverrideDeadline(7357) 254 .build(); 255 // ...why are the set/get methods named differently?? >.> 256 assertEquals(7357, ji.getMaxExecutionDelayMillis()); 257 } 258 testPeriodic()259 public void testPeriodic() { 260 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 261 .setPeriodic(60 * 60 * 1000L) 262 .build(); 263 assertTrue(ji.isPeriodic()); 264 assertEquals(60 * 60 * 1000L, ji.getIntervalMillis()); 265 assertEquals(60 * 60 * 1000L, ji.getFlexMillis()); 266 // Confirm JobScheduler accepts the JobInfo object. 267 mJobScheduler.schedule(ji); 268 269 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 270 .setPeriodic(120 * 60 * 1000L, 20 * 60 * 1000L) 271 .build(); 272 assertTrue(ji.isPeriodic()); 273 assertEquals(120 * 60 * 1000L, ji.getIntervalMillis()); 274 assertEquals(20 * 60 * 1000L, ji.getFlexMillis()); 275 // Confirm JobScheduler accepts the JobInfo object. 276 mJobScheduler.schedule(ji); 277 } 278 testPersisted()279 public void testPersisted() { 280 // Assert the default value is false 281 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 282 .build(); 283 assertFalse(ji.isPersisted()); 284 // Confirm JobScheduler accepts the JobInfo object. 285 mJobScheduler.schedule(ji); 286 287 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 288 .setPersisted(true) 289 .build(); 290 assertTrue(ji.isPersisted()); 291 // Confirm JobScheduler accepts the JobInfo object. 292 mJobScheduler.schedule(ji); 293 294 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 295 .setPersisted(false) 296 .build(); 297 assertFalse(ji.isPersisted()); 298 // Confirm JobScheduler accepts the JobInfo object. 299 mJobScheduler.schedule(ji); 300 } 301 testPrefetch()302 public void testPrefetch() { 303 // Assert the default value is false 304 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 305 .build(); 306 assertFalse(ji.isPrefetch()); 307 // Confirm JobScheduler accepts the JobInfo object. 308 mJobScheduler.schedule(ji); 309 310 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 311 .setPrefetch(true) 312 .build(); 313 assertTrue(ji.isPrefetch()); 314 // Confirm JobScheduler accepts the JobInfo object. 315 mJobScheduler.schedule(ji); 316 317 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 318 .setPrefetch(false) 319 .build(); 320 assertFalse(ji.isPrefetch()); 321 // Confirm JobScheduler accepts the JobInfo object. 322 mJobScheduler.schedule(ji); 323 } 324 testRequiredNetwork()325 public void testRequiredNetwork() { 326 final NetworkRequest nr = new NetworkRequest.Builder() 327 .addCapability(NET_CAPABILITY_INTERNET) 328 .addCapability(NET_CAPABILITY_VALIDATED) 329 .build(); 330 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 331 .setRequiredNetwork(nr) 332 .build(); 333 assertEquals(nr, ji.getRequiredNetwork()); 334 // Confirm JobScheduler accepts the JobInfo object. 335 mJobScheduler.schedule(ji); 336 337 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 338 .setRequiredNetwork(null) 339 .build(); 340 assertNull(ji.getRequiredNetwork()); 341 // Confirm JobScheduler accepts the JobInfo object. 342 mJobScheduler.schedule(ji); 343 } 344 345 @SuppressWarnings("deprecation") testRequiredNetworkType()346 public void testRequiredNetworkType() { 347 // Assert the default value is NONE 348 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 349 .build(); 350 assertEquals(JobInfo.NETWORK_TYPE_NONE, ji.getNetworkType()); 351 // Confirm JobScheduler accepts the JobInfo object. 352 mJobScheduler.schedule(ji); 353 354 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 355 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) 356 .build(); 357 assertEquals(JobInfo.NETWORK_TYPE_ANY, ji.getNetworkType()); 358 // Confirm JobScheduler accepts the JobInfo object. 359 mJobScheduler.schedule(ji); 360 361 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 362 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED) 363 .build(); 364 assertEquals(JobInfo.NETWORK_TYPE_UNMETERED, ji.getNetworkType()); 365 // Confirm JobScheduler accepts the JobInfo object. 366 mJobScheduler.schedule(ji); 367 368 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 369 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NOT_ROAMING) 370 .build(); 371 assertEquals(JobInfo.NETWORK_TYPE_NOT_ROAMING, ji.getNetworkType()); 372 // Confirm JobScheduler accepts the JobInfo object. 373 mJobScheduler.schedule(ji); 374 375 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 376 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_CELLULAR) 377 .build(); 378 assertEquals(JobInfo.NETWORK_TYPE_CELLULAR, ji.getNetworkType()); 379 // Confirm JobScheduler accepts the JobInfo object. 380 mJobScheduler.schedule(ji); 381 382 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 383 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE) 384 .build(); 385 assertEquals(JobInfo.NETWORK_TYPE_NONE, ji.getNetworkType()); 386 // Confirm JobScheduler accepts the JobInfo object. 387 mJobScheduler.schedule(ji); 388 } 389 testStorageNotLow()390 public void testStorageNotLow() { 391 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 392 .setRequiresStorageNotLow(true) 393 .build(); 394 assertTrue(ji.isRequireStorageNotLow()); 395 // Confirm JobScheduler accepts the JobInfo object. 396 mJobScheduler.schedule(ji); 397 398 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 399 .setRequiresStorageNotLow(false) 400 .build(); 401 assertFalse(ji.isRequireStorageNotLow()); 402 // Confirm JobScheduler accepts the JobInfo object. 403 mJobScheduler.schedule(ji); 404 } 405 testTransientExtras()406 public void testTransientExtras() { 407 final Bundle b = new Bundle(); 408 b.putBoolean("random_bool", true); 409 assertBuildFails("Successfully built a persisted JobInfo object with transient extras", 410 new JobInfo.Builder(JOB_ID, kJobServiceComponent) 411 .setPersisted(true) 412 .setTransientExtras(b)); 413 414 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 415 .setTransientExtras(b) 416 .build(); 417 assertEquals(b.size(), ji.getTransientExtras().size()); 418 for (String key : b.keySet()) { 419 assertEquals(b.get(key), ji.getTransientExtras().get(key)); 420 } 421 // Confirm JobScheduler accepts the JobInfo object. 422 mJobScheduler.schedule(ji); 423 } 424 testTriggerContentMaxDelay()425 public void testTriggerContentMaxDelay() { 426 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 427 .setTriggerContentMaxDelay(1337) 428 .build(); 429 assertEquals(1337, ji.getTriggerContentMaxDelay()); 430 // Confirm JobScheduler accepts the JobInfo object. 431 mJobScheduler.schedule(ji); 432 } 433 testTriggerContentUpdateDelay()434 public void testTriggerContentUpdateDelay() { 435 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 436 .setTriggerContentUpdateDelay(1337) 437 .build(); 438 assertEquals(1337, ji.getTriggerContentUpdateDelay()); 439 // Confirm JobScheduler accepts the JobInfo object. 440 mJobScheduler.schedule(ji); 441 } 442 testTriggerContentUri()443 public void testTriggerContentUri() { 444 final Uri u = Uri.parse("content://" + MediaStore.AUTHORITY + "/"); 445 final JobInfo.TriggerContentUri tcu = new JobInfo.TriggerContentUri( 446 u, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS); 447 assertEquals(u, tcu.getUri()); 448 assertEquals(JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS, tcu.getFlags()); 449 JobInfo ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 450 .addTriggerContentUri(tcu) 451 .build(); 452 assertEquals(1, ji.getTriggerContentUris().length); 453 assertEquals(tcu, ji.getTriggerContentUris()[0]); 454 // Confirm JobScheduler accepts the JobInfo object. 455 mJobScheduler.schedule(ji); 456 457 final Uri u2 = Uri.parse("content://" + ContactsContract.AUTHORITY + "/"); 458 final JobInfo.TriggerContentUri tcu2 = new JobInfo.TriggerContentUri(u2, 0); 459 assertEquals(u2, tcu2.getUri()); 460 assertEquals(0, tcu2.getFlags()); 461 ji = new JobInfo.Builder(JOB_ID, kJobServiceComponent) 462 .addTriggerContentUri(tcu) 463 .addTriggerContentUri(tcu2) 464 .build(); 465 assertEquals(2, ji.getTriggerContentUris().length); 466 assertEquals(tcu, ji.getTriggerContentUris()[0]); 467 assertEquals(tcu2, ji.getTriggerContentUris()[1]); 468 // Confirm JobScheduler accepts the JobInfo object. 469 mJobScheduler.schedule(ji); 470 } 471 assertBuildFails(String message, JobInfo.Builder builder)472 private void assertBuildFails(String message, JobInfo.Builder builder) { 473 try { 474 builder.build(); 475 fail(message); 476 } catch (IllegalArgumentException e) { 477 // Expected 478 } 479 } 480 } 481