/** Instantiates a new GooglePlayDriver. */ public GooglePlayDriver(@NonNull Context context) { this.context = context; token = PendingIntent.getBroadcast(context, 0, new Intent(), 0); writer = new GooglePlayJobWriter(); validator = new DefaultJobValidator(context); }
/** * Attempts to validate the provided {@code JobParameters}. If the JobParameters is valid, null * will be returned. If the JobParameters has errors, a list of those errors will be returned. */ @Nullable @Override @CallSuper public List<String> validate(@NonNull JobParameters job) { List<String> errors; errors = mergeErrorLists(null, validate(job.getTrigger())); errors = mergeErrorLists(errors, validate(job.getRetryStrategy())); if (job.isRecurring() && job.getTrigger() == Trigger.NOW) { errors = addError(errors, "ImmediateTriggers can't be used with recurring jobs"); } errors = mergeErrorLists(errors, validateForTransport(job.getExtras())); if (job.getLifetime() > Lifetime.UNTIL_NEXT_BOOT) { // noinspection ConstantConditions errors = mergeErrorLists(errors, validateForPersistence(job.getExtras())); } errors = mergeErrorLists(errors, validateTag(job.getTag())); errors = mergeErrorLists(errors, validateService(job.getService())); return errors; }
@Nullable private static List<String> validateForTransport(@Nullable Bundle extras) { if (extras == null) { return null; } int bundleSizeInBytes = measureBundleSize(extras); if (bundleSizeInBytes > MAX_EXTRAS_SIZE_BYTES) { return getMutableSingletonList( String.format( Locale.US, "Extras too large: %d bytes is > the max (%d bytes)", bundleSizeInBytes, MAX_EXTRAS_SIZE_BYTES)); } return null; }
@Nullable private static List<String> validateForPersistence(@Nullable Bundle extras) { List<String> errors = null; if (extras != null) { // check the types to make sure they're persistable for (String k : extras.keySet()) { errors = addError(errors, validateExtrasType(extras, k)); } } return errors; }
@Test public void validateService_packageManagerIsNotAvailable() { when(mockContext.getPackageManager()).thenReturn(null); List<String> errors = validator.validateService("service"); assertTrue(errors.contains("PackageManager is null, can't validate service")); }
@Test public void testValidate_trigger() throws Exception { Map<JobTrigger, String> testCases = new HashMap<>(); testCases.put(Trigger.NOW, null); testCases.put(Trigger.executionWindow(0, 100), null); ContentUriTrigger contentUriTrigger = Trigger.contentUriTrigger( Arrays.asList( new ObservedUri( ContactsContract.AUTHORITY_URI, Flags.FLAG_NOTIFY_FOR_DESCENDANTS))); testCases.put(contentUriTrigger, null); for (Entry<JobTrigger, String> testCase : testCases.entrySet()) { List<String> validationErrors = validator.validate(testCase.getKey()); if (testCase.getValue() == null) { assertNull("Expected no validation errors for trigger", validationErrors); } else { assertTrue( "Expected validation errors to contain \"" + testCase.getValue() + "\"", validationErrors.contains(testCase.getValue())); } } }
private static List<String> validateTag(String tag) { if (tag == null) { return getMutableSingletonList("Tag can't be null"); } if (tag.length() > MAX_TAG_LENGTH) { return getMutableSingletonList("Tag must be shorter than " + MAX_TAG_LENGTH); } return null; }
@Nullable private static List<String> addErrorsIf(boolean condition, List<String> errors, String newErr) { if (condition) { return addError(errors, newErr); } return errors; }
/** * Attempts to validate the provided RetryStrategy. If valid, null is returned. Otherwise a list * of errors will be returned. */ @Nullable @Override @CallSuper public List<String> validate(@NonNull RetryStrategy retryStrategy) { List<String> errors; int policy = retryStrategy.getPolicy(); int initial = retryStrategy.getInitialBackoff(); int maximum = retryStrategy.getMaximumBackoff(); errors = addErrorsIf( policy != RETRY_POLICY_EXPONENTIAL && policy != RETRY_POLICY_LINEAR, null, "Unknown retry policy provided"); errors = addErrorsIf( maximum < initial, errors, "Maximum backoff must be greater than or equal to initial backoff"); errors = addErrorsIf(300 > maximum, errors, "Maximum backoff must be greater than 300s (5 minutes)"); errors = addErrorsIf(initial < 30, errors, "Initial backoff must be at least 30s"); return errors; }
@Test public void validateService_noSuchService() { when(mockContext.getPackageManager()).thenReturn(packageManagerMock); when(packageManagerMock.queryIntentServices(any(Intent.class), eq(0))) .thenReturn(Collections.<ResolveInfo>emptyList()); when(mockContext.getPackageName()).thenReturn("package"); List<String> errors = validator.validateService("service"); assertNull(errors); verify(packageManagerMock).queryIntentServices(intentCaptor.capture(), eq(0)); Intent intent = intentCaptor.getValue(); assertEquals(JobService.ACTION_EXECUTE, intent.getAction()); assertEquals(new ComponentName("package", "service"), intent.getComponent()); }
List<String> validationErrors = validator.validate(testCase.getKey()); assertNotNull("Expected validation errors, but got null", validationErrors);
@Nullable private static List<String> addError(@Nullable List<String> errors, String newError) { if (newError == null) { return errors; } if (errors == null) { return getMutableSingletonList(newError); } Collections.addAll(errors, newError); return errors; }
@Test public void validateService_noActiveService() { when(mockContext.getPackageManager()).thenReturn(packageManagerMock); ResolveInfo resolveInfo = new ResolveInfo(); resolveInfo.serviceInfo = new ServiceInfo(); resolveInfo.serviceInfo.enabled = false; when(packageManagerMock.queryIntentServices(any(Intent.class), eq(0))) .thenReturn(Arrays.asList(resolveInfo)); when(mockContext.getPackageName()).thenReturn("package"); List<String> errors = validator.validateService("service"); assertTrue(errors.contains("service is disabled.")); }
@Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); validator = new DefaultJobValidator(mockContext); }
/** * Attempts to validate the provided Trigger. If valid, null is returned. Otherwise a list of * errors will be returned. * * <p>Note that a Trigger that passes validation here is not necessarily valid in all permutations * of a JobParameters. For example, an Immediate is never valid for a recurring job. */ @Nullable @Override @CallSuper public List<String> validate(@NonNull JobTrigger trigger) { if (trigger != Trigger.NOW && !(trigger instanceof JobTrigger.ExecutionWindowTrigger) && !(trigger instanceof JobTrigger.ContentUriTrigger)) { return getMutableSingletonList("Unknown trigger provided"); } return null; }
@Test public void validateService_empty() { List<String> errors = validator.validateService(""); assertTrue(errors.contains("Service can't be empty")); }
@VisibleForTesting List<String> validateService(String service) { if (service == null || service.isEmpty()) { return getMutableSingletonList("Service can't be empty"); return getMutableSingletonList("Context is null, can't query PackageManager"); return getMutableSingletonList("PackageManager is null, can't validate service"); return getMutableSingletonList(service + " is disabled.");
@Test public void validateService_null() { List<String> errors = validator.validateService(null); assertTrue(errors.contains("Service can't be empty")); }
@Test public void validateService() { when(mockContext.getPackageManager()).thenReturn(packageManagerMock); ResolveInfo resolveInfo = new ResolveInfo(); resolveInfo.serviceInfo = new ServiceInfo(); resolveInfo.serviceInfo.enabled = true; when(packageManagerMock.queryIntentServices(any(Intent.class), eq(0))) .thenReturn(Arrays.asList(resolveInfo)); when(mockContext.getPackageName()).thenReturn("package"); List<String> errors = validator.validateService("service"); assertNull(errors); } }