content: package com.lody.virtual.client.stub;
  
  import android.annotation.TargetApi;
  import android.app.Service;
  import android.app.job.IJobCallback;
  import android.app.job.IJobService;
  import android.app.job.JobParameters;
  import android.app.job.JobScheduler;
  import android.content.ComponentName;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.os.Build;
  import android.os.IBinder;
  import android.os.RemoteException;
  
  import com.lody.virtual.client.core.InvocationStubManager;
  import com.lody.virtual.client.hook.proxies.am.ActivityManagerStub;
  import com.lody.virtual.helper.utils.VLog;
  import com.lody.virtual.helper.collection.SparseArray;
  import com.lody.virtual.os.VUserHandle;
  
  import java.util.Map;
  
  import static com.lody.virtual.server.job.VJobSchedulerService.JobConfig;
  import static com.lody.virtual.server.job.VJobSchedulerService.JobId;
  import static com.lody.virtual.server.job.VJobSchedulerService.get;
  
  /**
   * @author Lody
   *         <p>
   *         This service running on the Server process.
   */
  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public class StubJob extends Service {
  
      private static final String TAG = StubJob.class.getSimpleName();
      private final SparseArray<JobSession> mJobSessions = new SparseArray<>();
      private JobScheduler mScheduler;
      private final IJobService mService = new IJobService.Stub() {
  
          @Override
          public void startJob(JobParameters jobParams) throws RemoteException {
              int jobId = jobParams.getJobId();
              IBinder binder = mirror.android.app.job.JobParameters.callback.get(jobParams);
              IJobCallback callback = IJobCallback.Stub.asInterface(binder);
              Map.Entry<JobId, JobConfig> entry = get().findJobByVirtualJobId(jobId);
              if (entry == null) {
                  emptyCallback(callback, jobId);
                  mScheduler.cancel(jobId);
              } else {
                  JobId key = entry.getKey();
                  JobConfig config = entry.getValue();
                  synchronized (mJobSessions) {
                      JobSession session = mJobSessions.get(jobId);
                      if (session != null) {
                          // Job Session has exist.
                          emptyCallback(callback, jobId);
                      } else {
                          session = new JobSession(jobId, callback, jobParams);
                          mirror.android.app.job.JobParameters.callback.set(jobParams, session.asBinder());
                          mirror.android.app.job.JobParameters.jobId.set(jobParams, key.clientJobId);
                          Intent service = new Intent();
                          service.setComponent(new ComponentName(key.packageName, config.serviceName));
                          service.putExtra("_VA_|_user_id_", VUserHandle.getUserId(key.vuid));
                          boolean bound = false;
                          try {
                              bound = bindService(service, session, 0);
                          } catch (Throwable e) {
                              VLog.e(TAG, e);
                          }
                          if (bound) {
                              mJobSessions.put(jobId, session);
                          } else {
                              emptyCallback(callback, jobId);
                              mScheduler.cancel(jobId);
                              get().cancel(jobId);
                          }
                      }
                  }
              }
          }
  
          @Override
          public void stopJob(JobParameters jobParams) throws RemoteException {
              int jobId = jobParams.getJobId();
              synchronized (mJobSessions) {
                  JobSession session = mJobSessions.get(jobId);
                  if (session != null) {
                      session.stopSession();
                  }
              }
          }
      };
  
      /**
       * Make JobScheduler happy.
       */
      private void emptyCallback(IJobCallback callback, int jobId) {
          try {
              callback.acknowledgeStartMessage(jobId, false);
              callback.jobFinished(jobId, false);
          } catch (RemoteException e) {
              e.printStackTrace();
          }
      }
  
      @Override
      public void onCreate() {
          super.onCreate();
          InvocationStubManager.getInstance().checkEnv(ActivityManagerStub.class);
          mScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
      }
  
      @Override
      public IBinder onBind(Intent intent) {
          return mService.asBinder();
      }
  
      private final class JobSession extends IJobCallback.Stub implements ServiceConnection {
  
          private int jobId;
          private IJobCallback clientCallback;
          private JobParameters jobParams;
          private IJobService clientJobService;
  
          JobSession(int jobId, IJobCallback clientCallback, JobParameters jobParams) {
              this.jobId = jobId;
              this.clientCallback = clientCallback;
              this.jobParams = jobParams;
          }
  
          @Override
          public void acknowledgeStartMessage(int jobId, boolean ongoing) throws RemoteException {
              clientCallback.acknowledgeStartMessage(jobId, ongoing);
          }
  
          @Override
          public void acknowledgeStopMessage(int jobId, boolean reschedule) throws RemoteException {
              clientCallback.acknowledgeStopMessage(jobId, reschedule);
          }
  
          @Override
          public void jobFinished(int jobId, boolean reschedule) throws RemoteException {
              clientCallback.jobFinished(jobId, reschedule);
          }
  
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
              clientJobService = IJobService.Stub.asInterface(service);
              if (clientJobService == null) {
                  emptyCallback(clientCallback, jobId);
                  stopSession();
                  return;
              }
              try {
                  clientJobService.startJob(jobParams);
              } catch (RemoteException e) {
                  forceFinishJob();
                  e.printStackTrace();
              }
          }
  
          @Override
          public void onServiceDisconnected(ComponentName name) {
  
          }
  
          void forceFinishJob() {
              try {
                  clientCallback.jobFinished(jobId, false);
              } catch (RemoteException e) {
                  e.printStackTrace();
              } finally {
                  stopSession();
              }
          }
  
          void stopSession() {
              if (clientJobService != null) {
                  try {
                      clientJobService.stopJob(jobParams);
                  } catch (RemoteException e) {
                      e.printStackTrace();
                  }
              }
              mJobSessions.remove(jobId);
              unbindService(this);
          }
      }
  
  }
  