@Before public void setUp() { MockitoAnnotations.initMocks(this); when(mockContext.getPackageName()).thenReturn("com.example.foo"); when(constraintChecker.areConstraintsSatisfied(any(JobInvocation.class))).thenReturn(true); receiver = new TestJobReceiver(); executionDelegator = new ExecutionDelegator(mockContext, receiver, constraintChecker); ExecutionDelegator.cleanServiceConnections(); noopBinder = new IRemoteJobService.Stub() { @Override public void start(Bundle invocationData, IJobCallback callback) {} @Override public void stop(Bundle invocationData, boolean needToSendResult) {} }; }
private void handleStartMessage(Message message) { final Bundle data = message.getData(); final Messenger replyTo = message.replyTo; String tag = data.getString(REQUEST_PARAM_TAG); if (replyTo == null || tag == null) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Invalid start execution message."); } return; } GooglePlayMessengerCallback messengerCallback = new GooglePlayMessengerCallback(replyTo, tag); JobInvocation jobInvocation = googlePlayReceiver.prepareJob(messengerCallback, data); googlePlayReceiver.getExecutionDelegator().executeJob(jobInvocation); }
private void handleStopMessage(Message message) { Builder builder = GooglePlayReceiver.getJobCoder().decode(message.getData()); if (builder == null) { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Invalid stop execution message."); } return; } JobInvocation job = builder.build(); ExecutionDelegator.stopJob(job, true); } }
@Test public void onReschedule_stopJob() { Bundle bundle = TestUtil.getBundleForContentJobExecution(); JobCoder prefixedCoder = new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX); JobInvocation invocation = prefixedCoder.decodeIntentBundle(bundle); Job job = TestUtil.getBuilderWithNoopValidator() .setService(TestJobService.class) .setTrigger(invocation.getTrigger()) .setTag(invocation.getTag()) .build(); receiver.prepareJob(jobCallbackMock, bundle); when(contextMock.bindService( any(Intent.class), any(JobServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); new ExecutionDelegator(contextMock, mock(JobFinishedCallback.class), contraintCheckerMock) .executeJob(invocation); verify(contextMock) .bindService(any(Intent.class), jobServiceConnectionCaptor.capture(), eq(BIND_AUTO_CREATE)); assertTrue(jobServiceConnectionCaptor.getValue().hasJobInvocation(invocation)); GooglePlayReceiver.onSchedule(job); assertFalse(jobServiceConnectionCaptor.getValue().hasJobInvocation(invocation)); assertNull( "JobServiceConnection should be removed.", ExecutionDelegator.getJobServiceConnection(invocation.getService())); }
@Test public void executeJob_constraintsUnsatisfied_schedulesJobRetry() throws RemoteException { // Simulate job constraints not being met. JobInvocation jobInvocation = new JobInvocation.Builder() .setTag("tag") .setService("service") .setTrigger(Trigger.NOW) .build(); when(constraintChecker.areConstraintsSatisfied(eq(jobInvocation))).thenReturn(false); executionDelegator.executeJob(jobInvocation); // Confirm that service not bound verify(mockContext, never()) .bindService(any(Intent.class), any(JobServiceConnection.class), anyInt()); assertThat(ExecutionDelegator.getJobServiceConnection("service")).isNull(); // Verify that job is set for a retry later. assertThat(receiver.lastResult).isEqualTo(JobService.RESULT_FAIL_RETRY); }
private void verifyExecuteJob(JobInvocation input) throws Exception { when(mockContext.getPackageName()).thenReturn("com.example.foo"); receiver.setLatch(new CountDownLatch(1)); when(mockContext.bindService( any(Intent.class), any(JobServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); executionDelegator.executeJob(input); verify(mockContext) .bindService(intentCaptor.capture(), connCaptor.capture(), eq(BIND_AUTO_CREATE)); final Intent result = intentCaptor.getValue(); // verify the intent was sent to the right place assertEquals(input.getService(), result.getComponent().getClassName()); assertEquals(JobService.ACTION_EXECUTE, result.getAction()); final JobServiceConnection connection = connCaptor.getValue(); final AtomicReference<JobParameters> jobParametersAtomicReference = new AtomicReference<>(); IRemoteJobService.Stub jobServiceBinder = new IRemoteJobService.Stub() { @Override public void start(Bundle invocationData, IJobCallback callback) { jobParametersAtomicReference.set( GooglePlayReceiver.getJobCoder().decode(invocationData).build()); } @Override public void stop(Bundle invocationData, boolean needToSendResult) {} }; connection.onServiceConnected(null, jobServiceBinder); TestUtil.assertJobsEqual(input, jobParametersAtomicReference.get()); // Clean up started job. Otherwise new job won't be started. ExecutionDelegator.stopJob(input, false); }
/* package */ synchronized ExecutionDelegator getExecutionDelegator() { if (executionDelegator == null) { executionDelegator = new ExecutionDelegator(this, this, new ConstraintChecker(getApplicationContext())); } return executionDelegator; }
@After public void tearDown() { ExecutionDelegator.cleanServiceConnections(); }
boolean successfullyBound = tryBindingToJobService(jobInvocation, jobServiceConnection);
@Test public void schedule_whenRunning_onStopIsCalled() { // simulate running job Bundle bundle = TestUtil.getBundleForContentJobExecution(); JobCoder prefixedCoder = new JobCoder(BundleProtocol.PACKED_PARAM_BUNDLE_PREFIX); JobInvocation invocation = prefixedCoder.decodeIntentBundle(bundle); googlePlayReceiver.prepareJob(jobCallbackMock, bundle); when(mMockContext.bindService( any(Intent.class), serviceConnectionCaptor.capture(), eq(BIND_AUTO_CREATE))) .thenReturn(true); new ExecutionDelegator(mMockContext, callbackMock, constraintCheckerMock) .executeJob(invocation); Job job = TestUtil.getBuilderWithNoopValidator() .setService(TestJobService.class) .setTrigger(invocation.getTrigger()) .setTag(invocation.getTag()) .build(); googlePlayDriver.schedule(job); // reschedule request during the execution verify(mMockContext).sendBroadcast(any(Intent.class)); assertTrue(serviceConnectionCaptor.getValue().wasUnbound()); assertNull( "JobServiceConnection should be removed.", ExecutionDelegator.getJobServiceConnection(invocation.getService())); }
@Test public void jobFinished() throws RemoteException { JobInvocation jobInvocation = new JobInvocation.Builder() .setTag("tag") .setService("service") .setTrigger(Trigger.NOW) .build(); when(mockContext.bindService( any(Intent.class), any(ServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); executionDelegator.executeJob(jobInvocation); verify(mockContext) .bindService(intentCaptor.capture(), connCaptor.capture(), eq(BIND_AUTO_CREATE)); JobServiceConnection connection = connCaptor.getValue(); when(iBinderMock.queryLocalInterface(IRemoteJobService.class.getName())) .thenReturn(jobServiceMock); connection.onServiceConnected(null, iBinderMock); verify(jobServiceMock).start(bundleCaptor.capture(), jobCallbackCaptor.capture()); jobCallbackCaptor .getValue() .jobFinished(bundleCaptor.getValue(), JobService.RESULT_FAIL_NORETRY); assertNull(ExecutionDelegator.getJobServiceConnection("service")); assertEquals(JobService.RESULT_FAIL_NORETRY, receiver.lastResult); assertTrue(connection.wasUnbound()); }
@Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); handler = new GooglePlayMessageHandler(looper, receiverMock); when(constraintCheckerMock.areConstraintsSatisfied(any(JobInvocation.class))).thenReturn(true); executionDelegator = new ExecutionDelegator(contextMock, jobFinishedCallbackMock, constraintCheckerMock); when(receiverMock.getExecutionDelegator()).thenReturn(executionDelegator); when(receiverMock.getApplicationContext()).thenReturn(contextMock); when(contextMock.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(appOpsManager); }
@After public void tearDown() { ExecutionDelegator.cleanServiceConnections(); }
@Test public void testExecuteJob_handlesNull() { executionDelegator.executeJob(null); verifyZeroInteractions(mockContext); }
.thenReturn(true); executionDelegator.executeJob(jobInvocation); verify(contextMock) .bindService(any(Intent.class), jobServiceConnectionCaptor.capture(), eq(BIND_AUTO_CREATE)); serviceConnection, ExecutionDelegator.getJobServiceConnection(jobInvocation.getService())); assertFalse(serviceConnection.wasUnbound());
/** * Stops the job if it is running. * * <p>Needed to avoid possibility of sending job result before the reschedule request is received * by Google Play services. */ static void onSchedule(Job job) { // Stop if running synchronized (callbacks) { SimpleArrayMap<String, JobCallback> jobs = callbacks.get(job.getService()); if (jobs == null) { // not running return; } JobCallback jobCallback = jobs.get(job.getTag()); if (jobCallback == null) { // not running return; } } JobInvocation key = new JobInvocation.Builder() .setTag(job.getTag()) .setService(job.getService()) .setTrigger(job.getTrigger()) .build(); ExecutionDelegator.stopJob(key, false /* must not send the result */); } }
@After public void tearDown() { GooglePlayReceiver.clearCallbacks(); ExecutionDelegator.cleanServiceConnections(); }
@Override public final int onStartCommand(Intent intent, int flags, int startId) { try { super.onStartCommand(intent, flags, startId); if (intent == null) { Log.w(TAG, ERROR_NULL_INTENT); return START_NOT_STICKY; } String action = intent.getAction(); if (ACTION_EXECUTE.equals(action)) { getExecutionDelegator().executeJob(prepareJob(intent)); return START_NOT_STICKY; } else if (ACTION_INITIALIZE.equals(action)) { return START_NOT_STICKY; } Log.e(TAG, ERROR_UNKNOWN_ACTION); return START_NOT_STICKY; } finally { synchronized (callbacks) { latestStartId = startId; if (callbacks.isEmpty()) { stopSelf(latestStartId); } } } }
@After public void tearDown() { GooglePlayReceiver.clearCallbacks(); ExecutionDelegator.cleanServiceConnections(); }
@Test public void executeJob_startedButNotConnected_doNotStartAgain() { JobInvocation jobInvocation = new JobInvocation.Builder() .setTag("tag") .setService("service") .setTrigger(Trigger.NOW) .build(); when(mockContext.bindService( any(Intent.class), any(ServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); executionDelegator.executeJob(jobInvocation); verify(mockContext) .bindService(intentCaptor.capture(), connCaptor.capture(), eq(BIND_AUTO_CREATE)); reset(mockContext); executionDelegator.executeJob(jobInvocation); verify(mockContext, never()) .bindService(any(Intent.class), any(JobServiceConnection.class), eq(BIND_AUTO_CREATE)); }