Home | History | Annotate | Download | only in jobscheduler
      1 /*
      2  * Copyright (C) 2014 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 com.android.cts.verifier.jobscheduler;
     18 
     19 import com.android.cts.verifier.R;
     20 
     21 import android.annotation.TargetApi;
     22 import android.app.job.JobInfo;
     23 import android.content.BroadcastReceiver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.os.AsyncTask;
     28 import android.os.Bundle;
     29 import android.os.PowerManager;
     30 import android.util.Log;
     31 import android.view.View;
     32 import android.widget.Button;
     33 import android.widget.ImageView;
     34 import android.widget.TextView;
     35 
     36 /**
     37  *  Idle constraints:
     38  *      The framework doesn't support turning the screen off. Use the manual tester to
     39  *      turn off the screen to run to run tests that require idle mode to be on.
     40  */
     41 @TargetApi(21)
     42 public class IdleConstraintTestActivity extends ConstraintTestActivity {
     43     private static final String TAG = "IdleModeTestActivity";
     44     /**
     45      * It takes >1hr for idle mode to be triggered. We'll use this secret broadcast to force the
     46      * scheduler into idle. It's not a protected broadcast so that's alright.
     47      */
     48     private static final String ACTION_EXPEDITE_IDLE_MODE =
     49             "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE";
     50 
     51     /**
     52      * Id for the job that we schedule when the device is not in idle mode. This job is expected
     53      * to not execute. Executing means that the verifier test should fail.
     54      */
     55     private static final int IDLE_OFF_JOB_ID = IdleConstraintTestActivity.class.hashCode() + 0;
     56     /**
     57      * Id for the job that we schedule when the device *is* in idle mode. This job is expected to
     58      * execute. Not executing means that the verifier test should fail.
     59      */
     60     private static final int IDLE_ON_JOB_ID = IdleConstraintTestActivity.class.hashCode() + 1;
     61 
     62     private static final int IDLE_ON_TEST_STATE_NOT_IN_PROGRESS = 0;
     63     private static final int IDLE_ON_TEST_STATE_WAITING_FOR_SCREEN_OFF = 1;
     64 
     65     /**
     66      * mTestState stores the state of the tests. It is used to ensure that we only run
     67      * the 'idle on' test if screen is turned off after the user has started tests.
     68      */
     69     private int mTestState = IDLE_ON_TEST_STATE_NOT_IN_PROGRESS;
     70 
     71     private PowerManager mPowerManager;
     72     private TextView mContinueInstructionTextView;
     73 
     74     /**
     75      * Listens for screen off event. Starts an async task to force device into
     76      * idle mode and run the 'idle on' test.
     77      */
     78     private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
     79         @Override
     80         public void onReceive(Context context, Intent intent) {
     81             if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
     82                 if (mTestState == IDLE_ON_TEST_STATE_WAITING_FOR_SCREEN_OFF) {
     83                     mContinueInstructionTextView.setVisibility(View.GONE);
     84                     PowerManager.WakeLock wl = mPowerManager.newWakeLock(
     85                         PowerManager.PARTIAL_WAKE_LOCK, TAG);
     86                     wl.acquire();
     87                     new TestIdleModeTaskIdle().execute(wl);
     88                 }
     89             } else {
     90                 Log.e(TAG, "Invalid broadcast received, was expecting SCREEN_OFF");
     91             }
     92         }
     93     };
     94 
     95     @Override
     96     protected void onCreate(Bundle savedInstanceState) {
     97         super.onCreate(savedInstanceState);
     98 
     99         // Set up the UI.
    100         setContentView(R.layout.js_idle);
    101         setPassFailButtonClickListeners();
    102         setInfoResources(R.string.js_idle_test, R.string.js_idle_instructions, -1);
    103         mStartButton = (Button) findViewById(R.id.js_idle_start_test_button);
    104         mContinueInstructionTextView = (TextView) findViewById(
    105             R.id.js_idle_continue_instruction_view);
    106 
    107         // Register receiver for screen off event.
    108         IntentFilter intentFilter = new IntentFilter();
    109         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
    110 
    111         mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    112 
    113         registerReceiver(mScreenOffReceiver, intentFilter);
    114 
    115     }
    116 
    117     @Override
    118     protected void onResume() {
    119         super.onResume();
    120         // Enable start button only if tests are not in progress.
    121         if (mTestState == IDLE_ON_TEST_STATE_NOT_IN_PROGRESS) {
    122             mStartButton.setEnabled(true);
    123             mContinueInstructionTextView.setVisibility(View.GONE);
    124         }
    125     }
    126 
    127     @Override
    128     protected void onDestroy() {
    129         super.onDestroy();
    130         unregisterReceiver(mScreenOffReceiver);
    131     }
    132 
    133     @Override
    134     protected void startTestImpl() {
    135         mStartButton.setEnabled(false);
    136         new TestIdleModeTaskNotIdle().execute();
    137     }
    138 
    139     /** Background task that will run the 'not idle' test. */
    140     private class TestIdleModeTaskNotIdle extends AsyncTask<Void, Void, Void> {
    141         @Override
    142         protected Void doInBackground(Void... voids) {
    143             testIdleConstraintFails_notIdle();
    144             return null;
    145         }
    146 
    147         @Override
    148         protected void onPostExecute(Void result) {
    149             mTestState = IDLE_ON_TEST_STATE_WAITING_FOR_SCREEN_OFF;
    150             mContinueInstructionTextView.setVisibility(View.VISIBLE);
    151         }
    152     }
    153 
    154     /** Background task that will run the 'idle' test. */
    155     private class TestIdleModeTaskIdle extends AsyncTask<PowerManager.WakeLock, Void, Void> {
    156 
    157         private PowerManager.WakeLock mPartialWakeLock;
    158 
    159         @Override
    160         protected Void doInBackground(PowerManager.WakeLock... wakeLocks) {
    161             mPartialWakeLock = wakeLocks[0];
    162 
    163             if (!sendBroadcastAndBlockForResult(new Intent(ACTION_EXPEDITE_IDLE_MODE))) {
    164                 runOnUiThread(new IdleTestResultRunner(IDLE_ON_JOB_ID, false));
    165             } else {
    166                 testIdleConstraintExecutes_onIdle();
    167             }
    168             notifyTestCompleted();
    169             return null;
    170         }
    171 
    172         @Override
    173         protected void onPostExecute(Void result) {
    174             // Reset test state
    175             mTestState = IDLE_ON_TEST_STATE_NOT_IN_PROGRESS;
    176 
    177             PowerManager.WakeLock fullWakeLock = mPowerManager.newWakeLock(
    178                 PowerManager.FULL_WAKE_LOCK
    179                 | PowerManager.ACQUIRE_CAUSES_WAKEUP
    180                 | PowerManager.ON_AFTER_RELEASE, TAG);
    181             // Turn on screen and release both locks
    182             fullWakeLock.acquire();
    183             fullWakeLock.release();
    184             mPartialWakeLock.release();
    185         }
    186     }
    187 
    188     /**
    189      * The user has just pressed the "Start Test" button, so we know that the device can't be idle.
    190      * Schedule a job with an idle constraint and verify that it doesn't execute.
    191      */
    192     private void testIdleConstraintFails_notIdle() {
    193         mTestEnvironment.setUp();
    194         mJobScheduler.cancelAll();
    195 
    196         mTestEnvironment.setExpectedExecutions(0);
    197 
    198         mJobScheduler.schedule(
    199                 new JobInfo.Builder(IDLE_OFF_JOB_ID, mMockComponent)
    200                         .setRequiresDeviceIdle(true)
    201                         .build());
    202 
    203         boolean testPassed;
    204         try {
    205             testPassed = mTestEnvironment.awaitTimeout();
    206         } catch (InterruptedException e) {
    207             // We'll just indicate that it failed, not why.
    208             testPassed = false;
    209         }
    210         runOnUiThread(new IdleTestResultRunner(IDLE_OFF_JOB_ID, testPassed));
    211     }
    212 
    213     /**
    214      * Called after screen is switched off and device is forced into idle mode.
    215      * Schedule a job with an idle constraint and verify that it executes.
    216      */
    217     private void testIdleConstraintExecutes_onIdle() {
    218         mTestEnvironment.setUp();
    219         mJobScheduler.cancelAll();
    220 
    221         mTestEnvironment.setExpectedExecutions(1);
    222 
    223         mJobScheduler.schedule(
    224                 new JobInfo.Builder(IDLE_ON_JOB_ID, mMockComponent)
    225                 .setRequiresDeviceIdle(true)
    226                 .build());
    227 
    228         boolean testPassed;
    229         try {
    230             testPassed = mTestEnvironment.awaitExecution();
    231         } catch (InterruptedException e) {
    232             // We'll just indicate that it failed, not why.
    233             testPassed = false;
    234         }
    235 
    236         runOnUiThread(new IdleTestResultRunner(IDLE_ON_JOB_ID, testPassed));
    237     }
    238 
    239     /**
    240      * Runnable to update the UI with the outcome of the test. This class only runs two tests, so
    241      * the argument passed into the constructor will indicate which of the tests we are reporting
    242      * for.
    243      */
    244     protected class IdleTestResultRunner extends TestResultRunner {
    245 
    246         IdleTestResultRunner(int jobId, boolean testPassed) {
    247             super(jobId, testPassed);
    248         }
    249 
    250         @Override
    251         public void run() {
    252             ImageView view;
    253             if (mJobId == IDLE_OFF_JOB_ID) {
    254                 view = (ImageView) findViewById(R.id.idle_off_test_image);
    255             } else if (mJobId == IDLE_ON_JOB_ID) {
    256                 view = (ImageView) findViewById(R.id.idle_on_test_image);
    257             } else {
    258                 noteInvalidTest();
    259                 return;
    260             }
    261             view.setImageResource(mTestPassed ? R.drawable.fs_good : R.drawable.fs_error);
    262         }
    263     }
    264 }
    265