1 /* 2 * Copyright 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 androidx.work.impl.background.systemjob; 18 19 import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; 20 21 import static androidx.work.NetworkType.CONNECTED; 22 import static androidx.work.NetworkType.METERED; 23 import static androidx.work.NetworkType.NOT_REQUIRED; 24 import static androidx.work.NetworkType.NOT_ROAMING; 25 import static androidx.work.NetworkType.UNMETERED; 26 27 import static org.hamcrest.CoreMatchers.is; 28 import static org.hamcrest.MatcherAssert.assertThat; 29 import static org.hamcrest.Matchers.arrayContaining; 30 import static org.hamcrest.Matchers.greaterThanOrEqualTo; 31 import static org.junit.Assert.assertEquals; 32 import static org.junit.Assert.assertTrue; 33 34 import android.app.job.JobInfo; 35 import android.net.NetworkCapabilities; 36 import android.net.NetworkRequest; 37 import android.net.Uri; 38 import android.os.Build; 39 40 import androidx.test.core.app.ApplicationProvider; 41 import androidx.test.ext.junit.runners.AndroidJUnit4; 42 import androidx.test.filters.SdkSuppress; 43 import androidx.test.filters.SmallTest; 44 import androidx.work.BackoffPolicy; 45 import androidx.work.Constraints; 46 import androidx.work.NetworkType; 47 import androidx.work.OneTimeWorkRequest; 48 import androidx.work.PeriodicWorkRequest; 49 import androidx.work.SystemClock; 50 import androidx.work.WorkManagerTest; 51 import androidx.work.impl.WorkManagerImpl; 52 import androidx.work.impl.model.WorkSpec; 53 import androidx.work.worker.TestWorker; 54 55 import org.junit.Before; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 59 import java.util.concurrent.TimeUnit; 60 61 @RunWith(AndroidJUnit4.class) 62 @SdkSuppress(minSdkVersion = WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL) 63 public class SystemJobInfoConverterTest extends WorkManagerTest { 64 65 private static final long TEST_INTERVAL_DURATION = 66 PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS + 1232L; 67 private static final long TEST_FLEX_DURATION = 68 PeriodicWorkRequest.MIN_PERIODIC_FLEX_MILLIS + 112L; 69 private static final int JOB_ID = 101; 70 71 private SystemJobInfoConverter mConverter; 72 73 @Before setUp()74 public void setUp() { 75 mConverter = new SystemJobInfoConverter( 76 ApplicationProvider.getApplicationContext(), new SystemClock(), true); 77 } 78 79 @Test 80 @SmallTest testConvert_ids()81 public void testConvert_ids() { 82 final String expectedWorkSpecId = "026e3422-9cd1-11e7-abc4-cec278b6b50a"; 83 WorkSpec workSpec = new WorkSpec(expectedWorkSpecId, TestWorker.class.getName()); 84 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 85 String actualWorkSpecId = jobInfo.getExtras().getString( 86 SystemJobInfoConverter.EXTRA_WORK_SPEC_ID); 87 assertThat(actualWorkSpecId, is(expectedWorkSpecId)); 88 assertThat(jobInfo.getId(), is(JOB_ID)); 89 } 90 91 @Test 92 @SmallTest testConvert_setPersistedByDefault()93 public void testConvert_setPersistedByDefault() { 94 JobInfo jobInfo = mConverter.convert( 95 new WorkSpec("id", TestWorker.class.getName()), JOB_ID); 96 assertThat(jobInfo.isPersisted(), is(false)); 97 } 98 99 /** 100 * Due to b/6771687, calling {@link JobInfo.Builder#build} with no constraints throws an 101 * {@link IllegalArgumentException}. This is testing that {@link SystemJobInfoConverter#convert} 102 * sets some dummy constraint to toggle some internal boolean flags in {@link JobInfo.Builder} 103 * to allow {@link OneTimeWorkRequest} with no constraints to be converted without affecting its 104 * runtime, e.g. calling builder.setMinLatencyMillis(0L). 105 */ 106 @Test 107 @SmallTest testConvert_noConstraints_doesNotThrowException()108 public void testConvert_noConstraints_doesNotThrowException() { 109 mConverter.convert(new WorkSpec("id", TestWorker.class.getName()), JOB_ID); 110 } 111 112 @Test 113 @SmallTest testConvert_retryPolicy()114 public void testConvert_retryPolicy() { 115 long expectedBackoffDelayDuration = 50000; 116 WorkSpec workSpec = new WorkSpec("id", TestWorker.class.getName()); 117 workSpec.setBackoffDelayDuration(expectedBackoffDelayDuration); 118 workSpec.backoffPolicy = BackoffPolicy.LINEAR; 119 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 120 assertThat(jobInfo.getInitialBackoffMillis(), is(expectedBackoffDelayDuration)); 121 assertThat(jobInfo.getBackoffPolicy(), is(JobInfo.BACKOFF_POLICY_LINEAR)); 122 } 123 124 @Test 125 @SmallTest testConvert_initialDelay()126 public void testConvert_initialDelay() { 127 final long expectedInitialDelay = 12123L; 128 WorkSpec workSpec = new WorkSpec("id", TestWorker.class.getName()); 129 workSpec.lastEnqueueTime = System.currentTimeMillis(); 130 workSpec.initialDelay = expectedInitialDelay; 131 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 132 assertCloseValues(jobInfo.getMinLatencyMillis(), expectedInitialDelay); 133 } 134 135 @Test 136 @SmallTest testConvert_periodicWithNoFlex()137 public void testConvert_periodicWithNoFlex() { 138 WorkSpec workSpec = new WorkSpec("id", TestWorker.class.getName()); 139 workSpec.setPeriodic(TEST_INTERVAL_DURATION); 140 workSpec.lastEnqueueTime = System.currentTimeMillis(); 141 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 142 assertThat(jobInfo.getMinLatencyMillis(), is(0L)); 143 } 144 145 @Test 146 @SmallTest testConvert_periodicWithFlex()147 public void testConvert_periodicWithFlex() { 148 WorkSpec workSpec = new WorkSpec("id", TestWorker.class.getName()); 149 workSpec.setPeriodic(TEST_INTERVAL_DURATION, TEST_FLEX_DURATION); 150 workSpec.lastEnqueueTime = System.currentTimeMillis(); 151 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 152 assertCloseValues(jobInfo.getMinLatencyMillis(), 153 TEST_INTERVAL_DURATION - TEST_FLEX_DURATION); 154 } 155 156 @Test 157 @SmallTest testConvert_requireCharging()158 public void testConvert_requireCharging() { 159 final boolean expectedRequireCharging = true; 160 WorkSpec workSpec = getTestWorkSpecWithConstraints(new Constraints.Builder() 161 .setRequiresCharging(expectedRequireCharging).build()); 162 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 163 assertThat(jobInfo.isRequireCharging(), is(expectedRequireCharging)); 164 } 165 166 @Test 167 @SmallTest 168 @SdkSuppress(minSdkVersion = 24) testConvert_requireContentUriTrigger()169 public void testConvert_requireContentUriTrigger() { 170 final Uri expectedUri = Uri.parse("TEST_URI"); 171 final JobInfo.TriggerContentUri expectedTriggerContentUri = 172 new JobInfo.TriggerContentUri( 173 expectedUri, JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS); 174 WorkSpec workSpec = getTestWorkSpecWithConstraints(new Constraints.Builder() 175 .addContentUriTrigger(expectedUri, true) 176 .setTriggerContentUpdateDelay(5, TimeUnit.SECONDS) 177 .setTriggerContentMaxDelay(10, TimeUnit.SECONDS) 178 .build()); 179 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 180 181 JobInfo.TriggerContentUri[] triggerContentUris = jobInfo.getTriggerContentUris(); 182 assertThat(triggerContentUris, is(arrayContaining(expectedTriggerContentUri))); 183 assertThat(jobInfo.getTriggerContentUpdateDelay(), is(TimeUnit.SECONDS.toMillis(5))); 184 assertThat(jobInfo.getTriggerContentMaxDelay(), is(TimeUnit.SECONDS.toMillis(10))); 185 } 186 187 @Test 188 @SmallTest testConvert_requireDeviceIdle()189 public void testConvert_requireDeviceIdle() { 190 final boolean expectedRequireDeviceIdle = true; 191 WorkSpec workSpec = getTestWorkSpecWithConstraints(new Constraints.Builder() 192 .setRequiresDeviceIdle(expectedRequireDeviceIdle).build()); 193 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 194 assertThat(jobInfo.isRequireDeviceIdle(), is(expectedRequireDeviceIdle)); 195 } 196 197 @Test 198 @SmallTest 199 @SdkSuppress(minSdkVersion = 26) testConvert_requireBatteryNotLow()200 public void testConvert_requireBatteryNotLow() { 201 final boolean expectedRequireBatteryNotLow = true; 202 WorkSpec workSpec = getTestWorkSpecWithConstraints(new Constraints.Builder() 203 .setRequiresBatteryNotLow(expectedRequireBatteryNotLow).build()); 204 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 205 assertThat(jobInfo.isRequireBatteryNotLow(), is(expectedRequireBatteryNotLow)); 206 } 207 208 @Test 209 @SmallTest 210 @SdkSuppress(minSdkVersion = 26) testConvert_requireStorageNotLow()211 public void testConvert_requireStorageNotLow() { 212 final boolean expectedRequireStorageNotLow = true; 213 WorkSpec workSpec = getTestWorkSpecWithConstraints(new Constraints.Builder() 214 .setRequiresStorageNotLow(expectedRequireStorageNotLow).build()); 215 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 216 assertThat(jobInfo.isRequireStorageNotLow(), is(expectedRequireStorageNotLow)); 217 } 218 219 @Test 220 @SmallTest testConvert_networkTypeNotRoamingRequiresApi24()221 public void testConvert_networkTypeNotRoamingRequiresApi24() { 222 convertWithRequiredNetworkType(NOT_ROAMING, JobInfo.NETWORK_TYPE_NOT_ROAMING, 24); 223 } 224 225 @Test 226 @SmallTest testConvert_networkTypeMeteredRequiresApi26()227 public void testConvert_networkTypeMeteredRequiresApi26() { 228 convertWithRequiredNetworkType(METERED, JobInfo.NETWORK_TYPE_METERED, 26); 229 } 230 231 @Test 232 @SmallTest 233 @SdkSuppress(minSdkVersion = 29, maxSdkVersion = 34) testConvert_setImportantWhileForeground()234 public void testConvert_setImportantWhileForeground() { 235 // Switch maxSdkVersion to 35 after B gets an official SDK version. 236 // setImportantWhileInForeground() turns into a no-op starting API 36. 237 WorkSpec workSpec = getTestWorkSpecWithConstraints(new Constraints.Builder().build()); 238 workSpec.lastEnqueueTime = System.currentTimeMillis(); 239 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 240 assertThat(jobInfo.isImportantWhileForeground(), is(true)); 241 } 242 243 @Test 244 @SmallTest 245 @SdkSuppress(minSdkVersion = 29) testConvert_setImportantWhileForeground_respectFlag()246 public void testConvert_setImportantWhileForeground_respectFlag() { 247 mConverter = new SystemJobInfoConverter( 248 ApplicationProvider.getApplicationContext(), new SystemClock(), false); 249 WorkSpec workSpec = getTestWorkSpecWithConstraints(new Constraints.Builder().build()); 250 workSpec.lastEnqueueTime = System.currentTimeMillis(); 251 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 252 assertThat(jobInfo.isImportantWhileForeground(), is(false)); 253 } 254 255 @Test 256 @SmallTest 257 @SdkSuppress(minSdkVersion = 29) testConvert_setImportantWhileForeground_withTimingConstraints()258 public void testConvert_setImportantWhileForeground_withTimingConstraints() { 259 WorkSpec workSpec = new WorkSpec("id", TestWorker.class.getName()); 260 workSpec.setPeriodic(TEST_INTERVAL_DURATION, TEST_FLEX_DURATION); 261 workSpec.lastEnqueueTime = System.currentTimeMillis(); 262 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 263 assertThat(jobInfo.isImportantWhileForeground(), is(false)); 264 } 265 266 @Test 267 @SmallTest testConvert_expedited()268 public void testConvert_expedited() { 269 if (Build.VERSION.SDK_INT < 31) { 270 return; 271 } 272 273 WorkSpec workSpec = new WorkSpec("id", TestWorker.class.getName()); 274 workSpec.lastEnqueueTime = System.currentTimeMillis(); 275 workSpec.expedited = true; 276 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 277 assertThat(jobInfo.isExpedited(), is(true)); 278 } 279 280 @Test 281 @SmallTest testConvertExpeditedJobs_retriesAreNotExpedited()282 public void testConvertExpeditedJobs_retriesAreNotExpedited() { 283 if (Build.VERSION.SDK_INT < 31) { 284 return; 285 } 286 287 WorkSpec workSpec = new WorkSpec("id", TestWorker.class.getName()); 288 workSpec.expedited = true; 289 workSpec.runAttemptCount = 1; // retry 290 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 291 assertThat(jobInfo.isExpedited(), is(false)); 292 } 293 294 @Test 295 @SmallTest testConvertExpeditedJobs_delaysAreNotExpedited()296 public void testConvertExpeditedJobs_delaysAreNotExpedited() { 297 if (Build.VERSION.SDK_INT < 31) { 298 return; 299 } 300 301 WorkSpec workSpec = new WorkSpec("id", TestWorker.class.getName()); 302 workSpec.expedited = true; 303 workSpec.lastEnqueueTime = System.currentTimeMillis(); 304 workSpec.initialDelay = 1000L; // delay 305 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 306 assertThat(jobInfo.isExpedited(), is(false)); 307 } 308 convertWithRequiredNetworkType(NetworkType networkType, int jobInfoNetworkType, int minSdkVersion)309 private void convertWithRequiredNetworkType(NetworkType networkType, 310 int jobInfoNetworkType, 311 int minSdkVersion) { 312 WorkSpec workSpec = getTestWorkSpecWithConstraints(new Constraints.Builder() 313 .setRequiredNetworkType(networkType).build()); 314 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 315 if (Build.VERSION.SDK_INT >= minSdkVersion) { 316 assertThat(jobInfo.getNetworkType(), is(jobInfoNetworkType)); 317 } else { 318 assertThat(jobInfo.getNetworkType(), is(JobInfo.NETWORK_TYPE_ANY)); 319 } 320 } 321 322 @Test 323 @SmallTest testConvertNetworkType_none()324 public void testConvertNetworkType_none() { 325 assertThat(SystemJobInfoConverter.convertNetworkType(NOT_REQUIRED), 326 is(JobInfo.NETWORK_TYPE_NONE)); 327 } 328 329 @Test 330 @SmallTest testConvertNetworkType_any()331 public void testConvertNetworkType_any() { 332 assertThat(SystemJobInfoConverter.convertNetworkType(CONNECTED), 333 is(JobInfo.NETWORK_TYPE_ANY)); 334 } 335 336 @Test 337 @SmallTest testConvertNetworkType_unmetered()338 public void testConvertNetworkType_unmetered() { 339 assertThat(SystemJobInfoConverter.convertNetworkType(UNMETERED), 340 is(JobInfo.NETWORK_TYPE_UNMETERED)); 341 } 342 343 @Test 344 @SmallTest 345 @SdkSuppress(minSdkVersion = 23, maxSdkVersion = 23) testConvertNetworkType_notRoaming_returnAnyBeforeApi24()346 public void testConvertNetworkType_notRoaming_returnAnyBeforeApi24() { 347 assertThat(SystemJobInfoConverter.convertNetworkType(NOT_ROAMING), 348 is(JobInfo.NETWORK_TYPE_ANY)); 349 } 350 351 @Test 352 @SmallTest 353 @SdkSuppress(minSdkVersion = 24) testConvertNetworkType_notRoaming_returnsNotRoamingAtOrAfterApi24()354 public void testConvertNetworkType_notRoaming_returnsNotRoamingAtOrAfterApi24() { 355 assertThat(SystemJobInfoConverter.convertNetworkType(NOT_ROAMING), 356 is(JobInfo.NETWORK_TYPE_NOT_ROAMING)); 357 } 358 359 @Test 360 @SmallTest 361 @SdkSuppress(minSdkVersion = WorkManagerImpl.MIN_JOB_SCHEDULER_API_LEVEL, maxSdkVersion = 25) testConvertNetworkType_metered_returnsAnyBeforeApi26()362 public void testConvertNetworkType_metered_returnsAnyBeforeApi26() { 363 assertThat(SystemJobInfoConverter.convertNetworkType(METERED), 364 is(JobInfo.NETWORK_TYPE_ANY)); 365 } 366 367 @Test 368 @SmallTest 369 @SdkSuppress(minSdkVersion = 26) testConvertNetworkType_metered_returnsMeteredAtOrAfterApi26()370 public void testConvertNetworkType_metered_returnsMeteredAtOrAfterApi26() { 371 assertThat(SystemJobInfoConverter.convertNetworkType(METERED), 372 is(JobInfo.NETWORK_TYPE_METERED)); 373 } 374 375 @Test 376 @SmallTest 377 @SdkSuppress(minSdkVersion = 30) testConvertNetworkType_temporarilyMetered()378 public void testConvertNetworkType_temporarilyMetered() { 379 WorkSpec workSpec = getTestWorkSpecWithConstraints(new Constraints.Builder() 380 .setRequiredNetworkType(NetworkType.TEMPORARILY_UNMETERED) 381 .build()); 382 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 383 NetworkRequest networkRequest = jobInfo.getRequiredNetwork(); 384 assertTrue(networkRequest.hasCapability(NET_CAPABILITY_TEMPORARILY_NOT_METERED)); 385 } 386 387 @Test 388 @SmallTest testNetworkRequest()389 public void testNetworkRequest() { 390 NetworkRequest networkRequest = new NetworkRequest.Builder() 391 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 392 .addCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P) 393 .build(); 394 WorkSpec workSpec = new WorkSpec("id", TestWorker.class.getName()); 395 workSpec.constraints = (new Constraints.Builder()) 396 .setRequiredNetworkRequest(networkRequest, METERED) 397 .build(); 398 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 399 if (Build.VERSION.SDK_INT >= 28) { 400 assertEquals(networkRequest, jobInfo.getRequiredNetwork()); 401 } else { 402 assertEquals(jobInfo.getNetworkType(), JobInfo.NETWORK_TYPE_METERED); 403 } 404 } 405 406 @Test 407 @SmallTest testEnsureTraceTags()408 public void testEnsureTraceTags() { 409 if (Build.VERSION.SDK_INT < 35) { 410 return; 411 } 412 413 final String id = "id"; 414 WorkSpec workSpec = new WorkSpec(id, TestWorker.class.getName()); 415 workSpec.setTraceTag(TestWorker.class.getSimpleName()); 416 JobInfo jobInfo = mConverter.convert(workSpec, JOB_ID); 417 assertEquals(jobInfo.getTraceTag(), TestWorker.class.getSimpleName()); 418 } 419 getTestWorkSpecWithConstraints(Constraints constraints)420 private WorkSpec getTestWorkSpecWithConstraints(Constraints constraints) { 421 return new OneTimeWorkRequest.Builder(TestWorker.class) 422 .setConstraints(constraints) 423 .build().getWorkSpec(); 424 } 425 assertCloseValues(long value, long target)426 private void assertCloseValues(long value, long target) { 427 double min = Math.min(value, target); 428 double max = Math.max(value, target); 429 double ratio = min / max; 430 assertThat(ratio, greaterThanOrEqualTo(0.999d)); 431 } 432 } 433