@Override public synchronized void onServiceDisconnected(ComponentName name) { unbind(); }
/** Removes provided {@link JobInvocation job} and unbinds itself if no other jobs are running. */ synchronized void onJobFinished(JobInvocation jobInvocation) { jobStatuses.remove(jobInvocation); if (jobStatuses.isEmpty()) { unbind(); } }
private synchronized void stopJob(boolean needToSendResult, JobInvocation jobInvocation) { try { binder.stop(encodeJob(jobInvocation), needToSendResult); } catch (RemoteException remoteException) { Log.e(TAG, "Failed to stop a job", remoteException); unbind(); } }
/** * Stops provided {@link JobInvocation job}. * * <p>Unbinds the service if {@code needToSendResult} is {@code false} and no other jobs are * running. */ synchronized void onStop(JobInvocation jobInvocation, boolean needToSendResult) { if (!wasUnbound()) { boolean isRunning = Boolean.TRUE.equals(jobStatuses.remove(jobInvocation)); if (isRunning && isConnected()) { stopJob(needToSendResult, jobInvocation); } // Need to keep the connection open to receive the result. if (!needToSendResult && jobStatuses.isEmpty()) { unbind(); } } else { Log.w(TAG, "Can't send stop request because service was unbound."); } }
@Override public synchronized void onServiceConnected(ComponentName name, IBinder service) { if (wasUnbound()) { Log.w(TAG, "Connection have been used already."); return; } binder = IRemoteJobService.Stub.asInterface(service); Set<JobInvocation> startedJobs = new HashSet<>(); for (Entry<JobInvocation, Boolean> entry : jobStatuses.entrySet()) { if (Boolean.FALSE.equals(entry.getValue())) { try { binder.start(encodeJob(entry.getKey()), callback); startedJobs.add(entry.getKey()); } catch (RemoteException remoteException) { Log.e(TAG, "Failed to start job " + entry.getKey(), remoteException); unbind(); // TODO(user) notify a driver about the fail and release a wakelock. return; } } } // Mark jobs as started. for (JobInvocation invocation : startedJobs) { jobStatuses.put(invocation, true); } }
/** Returns {@code true} if the job was started. */ synchronized boolean startJob(JobInvocation jobInvocation) { if (wasUnbound()) { requestRetryForJob(jobInvocation); } boolean connected = isConnected(); if (connected) { // Need to stop running job Boolean isRunning = jobStatuses.get(jobInvocation); if (Boolean.TRUE.equals(isRunning)) { Log.w(TAG, "Received an execution request for already running job " + jobInvocation); stopJob(/* Do not send result because it is new execution request. */ false, jobInvocation); } try { binder.start(encodeJob(jobInvocation), callback); } catch (RemoteException e) { Log.e(TAG, "Failed to start the job " + jobInvocation, e); unbind(); return false; } } jobStatuses.put(jobInvocation, connected); return connected; }
@Test public void unbind_throws_noException() throws Exception { connection.onServiceConnected(null, binderMock); binderMock.verifyStartArguments(jobData, noopCallback); assertFalse(connection.wasUnbound()); doThrow(IllegalArgumentException.class).when(contextMock).unbindService(connection); connection.unbind(); assertTrue(connection.wasUnbound()); verify(contextMock).unbindService(connection); }
@Test public void unbind() throws Exception { connection.onServiceConnected(null, binderMock); binderMock.verifyStartArguments(jobData, noopCallback); assertFalse(connection.wasUnbound()); connection.unbind(); assertTrue(connection.wasUnbound()); verify(contextMock).unbindService(connection); }
jobServiceConnection.unbind();
@Test public void executeJob_wasStartedButDisconnected_startAgain() { 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 jobServiceConnection = connCaptor.getValue(); jobServiceConnection.onServiceConnected(null, noopBinder); jobServiceConnection.unbind(); verify(mockContext).unbindService(jobServiceConnection); reset(mockContext); executionDelegator.executeJob(jobInvocation); verify(mockContext) .bindService(any(Intent.class), any(JobServiceConnection.class), eq(BIND_AUTO_CREATE)); }