@Nullable @VisibleForTesting JobInvocation prepareJob(Intent intent) { Bundle intentExtras = intent.getExtras(); if (intentExtras == null) { Log.e(TAG, ERROR_NO_DATA); return null; } // get the callback first. If we don't have this we can't talk back to the backend. Pair<JobCallback, Bundle> extraction = callbackExtractor.extractCallback(intentExtras); if (extraction == null) { Log.i(TAG, "no callback found"); return null; } return prepareJob(extraction.first, extraction.second); }
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); }
@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); } } } }
@Test public void handleMessage_startExecution_noData() throws Exception { Message message = Message.obtain(); message.what = GooglePlayMessageHandler.MSG_START_EXEC; message.replyTo = messengerMock; handler.handleMessage(message); verify(receiverMock, never()) .prepareJob(any(GooglePlayMessengerCallback.class), any(Bundle.class)); }
@Test public void prepareJob_messenger() { JobInvocation jobInvocation = receiver.prepareJob(callbackMock, new Bundle()); assertNull(jobInvocation); verify(callbackMock).jobFinished(JobService.RESULT_FAIL_NORETRY); }
@Test public void handleMessage_ignoreIfSenderIsNotGcm() throws Exception { Message message = Message.obtain(); message.what = GooglePlayMessageHandler.MSG_START_EXEC; Bundle data = new Bundle(); data.putString(REQUEST_PARAM_TAG, "TAG"); message.setData(data); message.replyTo = messengerMock; doThrow(new SecurityException()) .when(appOpsManager) .checkPackage(message.sendingUid, GooglePlayDriver.BACKEND_PACKAGE); handler.handleMessage(message); verify(receiverMock, never()).prepareJob(any(GooglePlayMessengerCallback.class), eq(data)); }
@Test public void onJobFinished_successRecurringContentJob_reschedule() { JobInvocation jobInvocation = receiver.prepareJob(callbackMock, getBundleForContentJobExecutionRecurring()); receiver.onJobFinished(jobInvocation, JobService.RESULT_SUCCESS); verify(driverMock).schedule(jobArgumentCaptor.capture()); // No need to callback when job finished. // Reschedule request is treated as two events: completion of old job and scheduling of new // job with the same parameters. verifyZeroInteractions(callbackMock); Job rescheduledJob = jobArgumentCaptor.getValue(); TestUtil.assertJobsEqual(jobInvocation, rescheduledJob); }
@Test public void onJobFinished_failWithRetryRecurringContentJob_sendResult() { JobInvocation jobInvocation = receiver.prepareJob(callbackMock, getBundleForContentJobExecutionRecurring()); receiver.onJobFinished(jobInvocation, JobService.RESULT_FAIL_RETRY); // If a job finishes with RESULT_FAIL_RETRY we don't need to send a reschedule request. // Rescheduling will erase previously triggered URIs. verify(callbackMock).jobFinished(JobService.RESULT_FAIL_RETRY); verifyZeroInteractions(driverMock); }
@Test public void handleMessage_startExecution() throws Exception { Message startExecutionMsg = Message.obtain(); startExecutionMsg.what = GooglePlayMessageHandler.MSG_START_EXEC; Bundle data = new Bundle(); data.putString(REQUEST_PARAM_TAG, "TAG"); startExecutionMsg.setData(data); startExecutionMsg.replyTo = messengerMock; JobInvocation jobInvocation = new Builder() .setTag("tag") .setService(TestJobService.class.getName()) .setTrigger(Trigger.NOW) .build(); when(receiverMock.prepareJob(any(GooglePlayMessengerCallback.class), eq(data))) .thenReturn(jobInvocation); when(contextMock.bindService( any(Intent.class), any(JobServiceConnection.class), eq(BIND_AUTO_CREATE))) .thenReturn(true); handler.handleMessage(startExecutionMsg); verify(contextMock) .bindService(any(Intent.class), jobServiceConnectionCaptor.capture(), eq(BIND_AUTO_CREATE)); assertTrue(jobServiceConnectionCaptor.getValue().hasJobInvocation(jobInvocation)); }
@Test public void prepareJob_messenger_noExtras() { Bundle bundle = TestUtil.getBundleForContentJobExecution(); JobInvocation jobInvocation = receiver.prepareJob(callbackMock, bundle); assertEquals(jobInvocation.getTriggerReason().getTriggeredContentUris(), TestUtil.URIS); }
@Test public void onJobFinished_notRecurringContentJob_sendResult() { jobInvocationBuilder.setTrigger( Trigger.contentUriTrigger(Arrays.asList(new ObservedUri(Contacts.CONTENT_URI, 0)))); JobInvocation jobInvocation = receiver.prepareJob(callbackMock, TestUtil.getBundleForContentJobExecution()); receiver.onJobFinished(jobInvocation, JobService.RESULT_SUCCESS); verify(callbackMock).jobFinished(JobService.RESULT_SUCCESS); verifyZeroInteractions(driverMock); }
@Test public void prepareJob() { Intent intent = new Intent(); Bundle encode = encodeContentUriJob(getContentUriTrigger(), TestUtil.JOB_CODER); intent.putExtra(GooglePlayJobWriter.REQUEST_PARAM_EXTRAS, encode); Parcel container = Parcel.obtain(); container.writeStrongBinder(new Binder()); PendingCallback pcb = new PendingCallback(container); intent.putExtra("callback", pcb); ArrayList<Uri> uris = new ArrayList<>(); uris.add(ContactsContract.AUTHORITY_URI); uris.add(Media.EXTERNAL_CONTENT_URI); intent.putParcelableArrayListExtra(BundleProtocol.PACKED_PARAM_TRIGGERED_URIS, uris); JobInvocation jobInvocation = receiver.prepareJob(intent); assertEquals(jobInvocation.getTriggerReason().getTriggeredContentUris(), uris); }
@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 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 onReschedule_notRunningWrongTag_noException() { Bundle bundle = TestUtil.getBundleForContentJobExecution(); Job job = TestUtil.getBuilderWithNoopValidator() .setService(TestJobService.class) .setTrigger(Trigger.NOW) .setTag("TAG") .build(); receiver.prepareJob(jobCallbackMock, bundle); GooglePlayReceiver.onSchedule(job); }