private static Bundle encodeJob(JobParameters job) { return getJobCoder().encode(job, new Bundle()); }
void sendResult(@JobResult int result) { try { remoteCallback.jobFinished(getJobCoder().encode(job, new Bundle()), result); } catch (RemoteException remoteException) { Log.e(TAG, "Failed to send result to driver", remoteException); } } }
public Bundle writeToBundle(JobParameters job, Bundle b) { b.putString(REQUEST_PARAM_TAG, job.getTag()); b.putBoolean(REQUEST_PARAM_UPDATE_CURRENT, job.shouldReplaceCurrent()); boolean persisted = job.getLifetime() == Lifetime.FOREVER; b.putBoolean(REQUEST_PARAM_PERSISTED, persisted); b.putString(REQUEST_PARAM_SERVICE, GooglePlayReceiver.class.getName()); writeTriggerToBundle(job, b); writeConstraintsToBundle(job, b); writeRetryStrategyToBundle(job, b); // Embed the job spec into the extras (under a prefix). Bundle userExtras = job.getExtras(); if (userExtras == null) { userExtras = new Bundle(); } b.putBundle(REQUEST_PARAM_EXTRAS, jobCoder.encode(job, userExtras)); return b; }
@Test(expected = IllegalArgumentException.class) public void testEncode_throwsOnNullBundle() { coder.encode(builder.build(), null); }
@NonNull static Bundle encodeContentUriJob(ContentUriTrigger trigger, JobCoder coder) { Job job = getBuilderWithNoopValidator() .setTag(TAG) .setTrigger(trigger) .setService(TestJobService.class) .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .build(); return coder.encode(job, new Bundle()); }
@Test(expected = IllegalArgumentException.class) public void testDecode_failsUnsupportedTrigger() { coder.decode( coder.encode(setValidBuilderDefaults(builder).setTrigger(null).build(), new Bundle())); }
@Test public void testCodingIsLossless() { for (JobParameters input : TestUtil.getJobCombinations(builder)) { TestUtil.assertJobsEqual(input, coder.decode(coder.encode(input, new Bundle())).build()); } }
@Test public void testCodingForExtras() { Bundle extras = new Bundle(); extras.putString("foo", "bar"); builder.setExtras(extras); Bundle deserializedExtras = coder .decode(coder.encode(setValidBuilderDefaults(builder).build(), new Bundle())) .build() .getExtras(); assertBundlesEqual(extras, deserializedExtras); }
@NonNull static Bundle encodeRecurringContentUriJob(ContentUriTrigger trigger, JobCoder coder) { Job job = getBuilderWithNoopValidator() .setTag(TAG) .setTrigger(trigger) .setService(TestJobService.class) .setReplaceCurrent(true) .setRecurring(true) .build(); return coder.encode(job, new Bundle()); }
@Test public void testDecode_failsWhenMissingFields() { assertNull( "Expected null tag to cause decoding to fail", coder.decode( coder.encode(setValidBuilderDefaults(builder).setTag(null).build(), new Bundle()))); assertNull( "Expected null service to cause decoding to fail", coder.decode( coder.encode(setValidBuilderDefaults(builder).setService(null).build(), new Bundle()))); }
@Test public void testDecode_ignoresMissingRetryStrategy() { assertNotNull( "Expected null retry strategy to cause decode to use a default", coder.decode( coder.encode( setValidBuilderDefaults(builder).setRetryStrategy(null).build(), new Bundle()))); }
@Test public void stop_noCallback_finished() throws Exception { JobService service = spy(new StoppableJobService(/* shouldReschedule= */ false)); JobInvocation job = new Builder() .setTag("Tag") .setTrigger(Trigger.NOW) .setService(StoppableJobService.class.getName()) .build(); IRemoteJobService.Stub.asInterface(service.onBind(null)) .stop(getJobCoder().encode(job, new Bundle()), true); flush(service); verify(service, never()).onStopJob(job); }
@Test public void onStartCommand_executeAction() { JobInvocation job = new JobInvocation.Builder() .setTag("tag") .setService("com.example.foo.FooService") .setTrigger(Trigger.NOW) .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .setLifetime(Lifetime.UNTIL_NEXT_BOOT) .setConstraints(new int[] {Constraint.DEVICE_IDLE}) .build(); Intent execIntent = new Intent("com.google.android.gms.gcm.ACTION_TASK_READY") .putExtra( "extras", new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX).encode(job, new Bundle())) .putExtra("callback", new InspectableBinder().toPendingCallback()); assertResultWasStartNotSticky(receiver.onStartCommand(execIntent, 0, 101)); verify(receiver, never()).stopSelf(anyInt()); verify(executionDelegatorMock).executeJob(any(JobInvocation.class)); receiver.onJobFinished(job, JobService.RESULT_SUCCESS); verify(receiver).stopSelf(101); }
@Test public void testOnStartCommand_handlesStartJob_validRequest() throws Exception { JobService service = spy(new ExampleJobService()); Job jobSpec = TestUtil.getBuilderWithNoopValidator() .setTag("tag") .setService(ExampleJobService.class) .setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL) .setTrigger(Trigger.NOW) .setLifetime(Lifetime.FOREVER) .build(); countDownLatch = new CountDownLatch(1); Bundle jobSpecData = getJobCoder().encode(jobSpec, new Bundle()); IRemoteJobService remoteJobService = IRemoteJobService.Stub.asInterface(service.onBind(new Intent(JobService.ACTION_EXECUTE))); remoteJobService.start(jobSpecData, noopCallback); flush(service); assertTrue("Expected job to run to completion", countDownLatch.await(5, TimeUnit.SECONDS)); }
void verifyCalledWithJobAndResult(JobParameters job, int result) throws Exception { Pair<Bundle, Integer> jobFinishedResult = getJobFinishedFuture().get(TIMEOUT_MS, TimeUnit.MILLISECONDS); assertNotNull(jobFinishedResult); JobCoder jc = getJobCoder(); assertEquals( // re-encode so they're the same class jc.decode(jc.encode(job, new Bundle())).build(), jc.decode(jobFinishedResult.first).build()); assertEquals(result, (int) jobFinishedResult.second); }
@Test public void stop_withCallback_done() throws Exception { StoppableJobService service = spy(new StoppableJobService(/* shouldReschedule= */ true)); JobInvocation job = new Builder() .setTag("Tag") .setTrigger(Trigger.NOW) .setService(StoppableJobService.class.getName()) .build(); Bundle jobSpecData = getJobCoder().encode(job, new Bundle()); FutureSettingJobCallback callback = new FutureSettingJobCallback(); IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, callback); IRemoteJobService.Stub.asInterface(service.onBind(null)).stop(jobSpecData, true); flush(service); assertEquals(1, service.getNumberOfStopRequestsReceived()); callback.verifyCalledWithJobAndResult(job, JobService.RESULT_FAIL_RETRY); }
@Test public void onStartJob_jobFinishedReschedule() throws Exception { // Verify that a retry request from within onStartJob will cause the retry result to be sent // to the bouncer service's handler, regardless of what value is ultimately returned from // onStartJob. JobService reschedulingService = new JobService() { @Override public boolean onStartJob(@NonNull JobParameters job) { // Reschedules job. jobFinished(job, true /* retry this job */); return false; } @Override public boolean onStopJob(@NonNull JobParameters job) { return false; } }; Job jobSpec = TestUtil.getBuilderWithNoopValidator() .setTag("tag") .setService(reschedulingService.getClass()) .setTrigger(Trigger.NOW) .build(); FutureSettingJobCallback callback = new FutureSettingJobCallback(); IRemoteJobService.Stub.asInterface(reschedulingService.onBind(null)) .start(getJobCoder().encode(jobSpec, new Bundle()), callback); flush(reschedulingService); callback.verifyCalledWithJobAndResult(jobSpec, JobService.RESULT_FAIL_RETRY); }
@Test public void stop_withCallback_retry() throws Exception { StoppableJobService service = spy(new StoppableJobService(/* shouldReschedule= */ false)); JobInvocation job = new Builder() .setTag("Tag") .setTrigger(Trigger.NOW) .setService(StoppableJobService.class.getName()) .build(); Bundle jobSpecData = getJobCoder().encode(job, new Bundle()); FutureSettingJobCallback callback = new FutureSettingJobCallback(); // start the service IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, callback); IRemoteJobService.Stub.asInterface(service.onBind(null)).stop(jobSpecData, true); flush(service); assertEquals(1, service.getNumberOfStopRequestsReceived()); callback.verifyCalledWithJobAndResult(job, JobService.RESULT_SUCCESS); }
private static void verifyOnUnbindCausesResult(JobService service, int expectedResult) throws Exception { Job jobSpec = TestUtil.getBuilderWithNoopValidator() .setTag("tag") .setService(service.getClass()) .setTrigger(Trigger.NOW) .build(); Bundle jobSpecData = getJobCoder().encode(jobSpec, new Bundle()); FutureSettingJobCallback callback = new FutureSettingJobCallback(); // start the service IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, callback); // shouldn't have sent a result message yet (still doing background work) assertFalse(callback.getJobFinishedFuture().isDone()); // manually trigger the onUnbind hook service.onUnbind(new Intent()); flush(service); callback.verifyCalledWithJobAndResult(jobSpec, expectedResult); // Calling jobFinished should not attempt to send a second message callback.reset(); service.jobFinished(jobSpec, false); assertFalse(callback.getJobFinishedFuture().isDone()); }
@Test public void testOnStartCommand_handlesStartJob_doNotStartRunningJobAgain() throws Exception { StoppableJobService service = new StoppableJobService(/* shouldReschedule= */ false); Job jobSpec = TestUtil.getBuilderWithNoopValidator() .setTag("tag") .setService(StoppableJobService.class) .setTrigger(Trigger.NOW) .build(); Bundle jobSpecData = getJobCoder().encode(jobSpec, new Bundle()); IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, null); IRemoteJobService.Stub.asInterface(service.onBind(null)).start(jobSpecData, null); flush(service); assertEquals(1, service.getNumberOfStartRequestsReceived()); }