Home | History | Annotate | Download | only in jobscheduler
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.jobscheduler;
     18 
     19 import android.annotation.TargetApi;
     20 import android.app.job.JobInfo;
     21 import android.app.job.JobParameters;
     22 import android.app.job.JobScheduler;
     23 import android.app.job.JobService;
     24 import android.content.Context;
     25 import android.os.Handler;
     26 import android.util.Log;
     27 
     28 import java.util.concurrent.CountDownLatch;
     29 import java.util.concurrent.TimeUnit;
     30 
     31 /**
     32  * Handles callback from the framework {@link android.app.job.JobScheduler}. The behaviour of this
     33  * class is configured through the static
     34  * {@link TestEnvironment}.
     35  */
     36 @TargetApi(21)
     37 public class TriggerContentJobService extends JobService {
     38     private static final String TAG = "TriggerContentJobService";
     39 
     40     /** Wait this long before timing out the test. */
     41     private static final long DEFAULT_TIMEOUT_MILLIS = 30000L; // 30 seconds.
     42 
     43     /** How long to delay before rescheduling the job each time we repeat. */
     44     private static final long REPEAT_INTERVAL = 1000L; // 1 second.
     45 
     46     JobInfo mRunningJobInfo;
     47     JobParameters mRunningParams;
     48 
     49     final Handler mHandler = new Handler();
     50     final Runnable mWorkerReschedule = new Runnable() {
     51         @Override public void run() {
     52             scheduleJob(TriggerContentJobService.this, mRunningJobInfo);
     53             jobFinished(mRunningParams, false);
     54         }
     55     };
     56     final Runnable mWorkerFinishTrue = new Runnable() {
     57         @Override public void run() {
     58             jobFinished(mRunningParams, true);
     59         }
     60     };
     61 
     62     public static void scheduleJob(Context context, JobInfo jobInfo) {
     63         JobScheduler js = context.getSystemService(JobScheduler.class);
     64         js.schedule(jobInfo);
     65     }
     66 
     67     @Override
     68     public void onCreate() {
     69         super.onCreate();
     70         Log.e(TAG, "Created test service.");
     71     }
     72 
     73     @Override
     74     public boolean onStartJob(JobParameters params) {
     75         Log.i(TAG, "Test job executing: " + params.getJobId());
     76 
     77         int mode = TestEnvironment.getTestEnvironment().getMode();
     78         mRunningJobInfo = TestEnvironment.getTestEnvironment().getModeJobInfo();
     79         TestEnvironment.getTestEnvironment().setMode(TestEnvironment.MODE_ONESHOT, null);
     80         TestEnvironment.getTestEnvironment().notifyExecution(params);
     81 
     82         if (mode == TestEnvironment.MODE_ONE_REPEAT_RESCHEDULE) {
     83             mRunningParams = params;
     84             mHandler.postDelayed(mWorkerReschedule, REPEAT_INTERVAL);
     85             return true;
     86         } else if (mode == TestEnvironment.MODE_ONE_REPEAT_FINISH_TRUE) {
     87             mRunningParams = params;
     88             mHandler.postDelayed(mWorkerFinishTrue, REPEAT_INTERVAL);
     89             return true;
     90         } else {
     91             return false;  // No work to do.
     92         }
     93     }
     94 
     95     @Override
     96     public boolean onStopJob(JobParameters params) {
     97         return false;
     98     }
     99 
    100     /**
    101      * Configures the expected behaviour for each test. This object is shared across consecutive
    102      * tests, so to clear state each test is responsible for calling
    103      * {@link TestEnvironment#setUp()}.
    104      */
    105     public static final class TestEnvironment {
    106 
    107         private static TestEnvironment kTestEnvironment;
    108         //public static final int INVALID_JOB_ID = -1;
    109 
    110         private CountDownLatch mLatch;
    111         private JobParameters mExecutedJobParameters;
    112         private int mMode;
    113         private JobInfo mModeJobInfo;
    114 
    115         public static final int MODE_ONESHOT = 0;
    116         public static final int MODE_ONE_REPEAT_RESCHEDULE = 1;
    117         public static final int MODE_ONE_REPEAT_FINISH_TRUE = 2;
    118 
    119         public static TestEnvironment getTestEnvironment() {
    120             if (kTestEnvironment == null) {
    121                 kTestEnvironment = new TestEnvironment();
    122             }
    123             return kTestEnvironment;
    124         }
    125 
    126         public JobParameters getLastJobParameters() {
    127             return mExecutedJobParameters;
    128         }
    129 
    130         /**
    131          * Block the test thread, waiting on the JobScheduler to execute some previously scheduled
    132          * job on this service.
    133          */
    134         public boolean awaitExecution() throws InterruptedException {
    135             final boolean executed = mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
    136             return executed;
    137         }
    138 
    139         public void setMode(int mode, JobInfo jobInfo) {
    140             synchronized (this) {
    141                 mMode = mode;
    142                 mModeJobInfo = jobInfo;
    143             }
    144         }
    145 
    146         public int getMode() {
    147             synchronized (this) {
    148                 return mMode;
    149             }
    150         }
    151 
    152         public JobInfo getModeJobInfo() {
    153             synchronized (this) {
    154                 return mModeJobInfo;
    155             }
    156         }
    157 
    158         /**
    159          * Block the test thread, expecting to timeout but still listening to ensure that no jobs
    160          * land in the interim.
    161          * @return True if the latch timed out waiting on an execution.
    162          */
    163         public boolean awaitTimeout() throws InterruptedException {
    164             return !mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
    165         }
    166 
    167         private void notifyExecution(JobParameters params) {
    168             Log.d(TAG, "Job executed:" + params.getJobId());
    169             mExecutedJobParameters = params;
    170             mLatch.countDown();
    171         }
    172 
    173         public void setExpectedExecutions(int numExecutions) {
    174             // For no executions expected, set count to 1 so we can still block for the timeout.
    175             if (numExecutions == 0) {
    176                 mLatch = new CountDownLatch(1);
    177             } else {
    178                 mLatch = new CountDownLatch(numExecutions);
    179             }
    180         }
    181 
    182         /** Called in each testCase#setup */
    183         public void setUp() {
    184             mLatch = null;
    185             mExecutedJobParameters = null;
    186         }
    187 
    188     }
    189 }