Fix for Idle Mode Constraint test

This test failed previously because it attempted to
simulate idle mode by broadcasting an intent while the
screen is on. This patch asks the user to switch off
the screen before running the idle mode test.

Bug: 24309062
Change-Id: I999df8ae2d2564c40612708a5497e3dddc4c6533
diff --git a/apps/CtsVerifier/res/layout/js_idle.xml b/apps/CtsVerifier/res/layout/js_idle.xml
index 4277173..5289b98 100644
--- a/apps/CtsVerifier/res/layout/js_idle.xml
+++ b/apps/CtsVerifier/res/layout/js_idle.xml
@@ -15,13 +15,6 @@
                 android:layout_height="wrap_content"
                 android:text="@string/js_test_description"
                 android:layout_margin="@dimen/js_padding"/>
-            <TextView
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:text="@string/js_idle_description_1"
-                android:layout_margin="@dimen/js_padding"
-                android:textStyle="bold"/>
-
             <Button
                 android:id="@+id/js_idle_start_test_button"
                 android:layout_width="wrap_content"
@@ -30,6 +23,14 @@
                 android:text="@string/js_start_test_text"
                 android:onClick="startTest"
                 android:enabled="false"/>
+            <TextView
+                android:id="@+id/js_idle_continue_instruction_view"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/js_idle_continue_instruction"
+                android:layout_margin="@dimen/js_padding"
+                android:textStyle="bold"
+                android:visibility="gone"/>
 
             <LinearLayout
                 android:layout_width="wrap_content"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 874f8cd..c5cf55b 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1871,6 +1871,9 @@
     <string name="js_start_test_text">Start test</string>
     <string name="js_idle_instructions">Verify the behaviour of the JobScheduler API for when the device is in idle mode. Simply follow the on-screen instructions.</string>
     <string name="js_idle_description_1">Turn the screen off and then back on in order to begin.</string>
+    <string name="js_idle_continue_instruction">
+        Switch off screen and wait for it to turn on to continue.
+    </string>
     <string name="js_idle_item_idle_off">Idle job does not execute when device is not idle.</string>
     <string name="js_idle_item_idle_on">Idle job does execute when device is forced into idle.</string>
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java
index a8bd993..05c1a2e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java
@@ -26,15 +26,17 @@
 import android.content.IntentFilter;
 import android.os.AsyncTask;
 import android.os.Bundle;
+import android.os.PowerManager;
 import android.util.Log;
+import android.view.View;
 import android.widget.Button;
 import android.widget.ImageView;
+import android.widget.TextView;
 
 /**
  *  Idle constraints:
- *      The framework doesn't support turning idle mode off. Use the manual tester to ensure that
- *      the device is not in idle mode (by turning the screen off and then back on) before running
- *      the tests.
+ *      The framework doesn't support turning the screen off. Use the manual tester to
+ *      turn off the screen to run to run tests that require idle mode to be on.
  */
 @TargetApi(21)
 public class IdleConstraintTestActivity extends ConstraintTestActivity {
@@ -57,22 +59,35 @@
      */
     private static final int IDLE_ON_JOB_ID = IdleConstraintTestActivity.class.hashCode() + 1;
 
+    private static final int IDLE_ON_TEST_STATE_NOT_IN_PROGRESS = 0;
+    private static final int IDLE_ON_TEST_STATE_WAITING_FOR_SCREEN_OFF = 1;
+
     /**
-     * Listens for idle mode off/on events, namely {@link #ACTION_EXPEDITE_IDLE_MODE} and
-     * {@link Intent#ACTION_SCREEN_ON}.
-     * On ACTION_EXPEDITE_IDLE_MODE, we will disable the {@link #mStartButton}, and on
-     * ACTION_SCREEN_ON we enable it. This is to avoid the start button being clicked when the
-     * device is in idle mode.
+     * mTestState stores the state of the tests. It is used to ensure that we only run
+     * the 'idle on' test if screen is turned off after the user has started tests.
      */
-    private BroadcastReceiver mIdleChangedReceiver = new BroadcastReceiver() {
+    private int mTestState = IDLE_ON_TEST_STATE_NOT_IN_PROGRESS;
+
+    private PowerManager mPowerManager;
+    private TextView mContinueInstructionTextView;
+
+    /**
+     * Listens for screen off event. Starts an async task to force device into
+     * idle mode and run the 'idle on' test.
+     */
+    private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
-                mStartButton.setEnabled(true);
-            } else if (ACTION_EXPEDITE_IDLE_MODE.equals(intent.getAction())) {
-                mStartButton.setEnabled(false);
+            if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
+                if (mTestState == IDLE_ON_TEST_STATE_WAITING_FOR_SCREEN_OFF) {
+                    mContinueInstructionTextView.setVisibility(View.GONE);
+                    PowerManager.WakeLock wl = mPowerManager.newWakeLock(
+                        PowerManager.PARTIAL_WAKE_LOCK, TAG);
+                    wl.acquire();
+                    new TestIdleModeTaskIdle().execute(wl);
+                }
             } else {
-                Log.e(TAG, "Invalid broadcast received, was expecting SCREEN_ON");
+                Log.e(TAG, "Invalid broadcast received, was expecting SCREEN_OFF");
             }
         }
     };
@@ -86,47 +101,88 @@
         setPassFailButtonClickListeners();
         setInfoResources(R.string.js_idle_test, R.string.js_idle_instructions, -1);
         mStartButton = (Button) findViewById(R.id.js_idle_start_test_button);
+        mContinueInstructionTextView = (TextView) findViewById(
+            R.id.js_idle_continue_instruction_view);
 
-        // Register receiver for idle off/on events.
+        // Register receiver for screen off event.
         IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-        intentFilter.addAction(ACTION_EXPEDITE_IDLE_MODE);
+        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
 
-        registerReceiver(mIdleChangedReceiver, intentFilter);
+        mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+
+        registerReceiver(mScreenOffReceiver, intentFilter);
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        // Enable start button only if tests are not in progress.
+        if (mTestState == IDLE_ON_TEST_STATE_NOT_IN_PROGRESS) {
+            mStartButton.setEnabled(true);
+            mContinueInstructionTextView.setVisibility(View.GONE);
+        }
     }
 
     @Override
     protected void onDestroy() {
         super.onDestroy();
-        unregisterReceiver(mIdleChangedReceiver);
+        unregisterReceiver(mScreenOffReceiver);
     }
 
     @Override
     protected void startTestImpl() {
-        new TestIdleModeTask().execute();
+        mStartButton.setEnabled(false);
+        new TestIdleModeTaskNotIdle().execute();
     }
 
-    /** Background task that will run the actual test. */
-    private class TestIdleModeTask extends AsyncTask<Void, Void, Void> {
-
+    /** Background task that will run the 'not idle' test. */
+    private class TestIdleModeTaskNotIdle extends AsyncTask<Void, Void, Void> {
         @Override
         protected Void doInBackground(Void... voids) {
             testIdleConstraintFails_notIdle();
+            return null;
+        }
 
+        @Override
+        protected void onPostExecute(Void result) {
+            mTestState = IDLE_ON_TEST_STATE_WAITING_FOR_SCREEN_OFF;
+            mContinueInstructionTextView.setVisibility(View.VISIBLE);
+        }
+    }
 
-            // Send the {@link #ACTION_EXPEDITE_IDLE_MODE} broadcast as an ordered broadcast, this
-            // function will block until all receivers have processed the broadcast.
+    /** Background task that will run the 'idle' test. */
+    private class TestIdleModeTaskIdle extends AsyncTask<PowerManager.WakeLock, Void, Void> {
+
+        private PowerManager.WakeLock mPartialWakeLock;
+
+        @Override
+        protected Void doInBackground(PowerManager.WakeLock... wakeLocks) {
+            mPartialWakeLock = wakeLocks[0];
+
             if (!sendBroadcastAndBlockForResult(new Intent(ACTION_EXPEDITE_IDLE_MODE))) {
-                // Fail the test if the broadcast wasn't processed.
                 runOnUiThread(new IdleTestResultRunner(IDLE_ON_JOB_ID, false));
+            } else {
+                testIdleConstraintExecutes_onIdle();
             }
-
-            testIdleConstraintExecutes_onIdle();
-
             notifyTestCompleted();
             return null;
         }
 
+        @Override
+        protected void onPostExecute(Void result) {
+            // Reset test state
+            mTestState = IDLE_ON_TEST_STATE_NOT_IN_PROGRESS;
+
+            PowerManager.WakeLock fullWakeLock = mPowerManager.newWakeLock(
+                PowerManager.FULL_WAKE_LOCK
+                | PowerManager.ACQUIRE_CAUSES_WAKEUP
+                | PowerManager.ON_AFTER_RELEASE, TAG);
+            // Turn on screen and release both locks
+            fullWakeLock.acquire();
+            fullWakeLock.release();
+            mPartialWakeLock.release();
+        }
     }
 
     /**
@@ -154,6 +210,10 @@
         runOnUiThread(new IdleTestResultRunner(IDLE_OFF_JOB_ID, testPassed));
     }
 
+    /**
+     * Called after screen is switched off and device is forced into idle mode.
+     * Schedule a job with an idle constraint and verify that it executes.
+     */
     private void testIdleConstraintExecutes_onIdle() {
         mTestEnvironment.setUp();
         mJobScheduler.cancelAll();
@@ -172,6 +232,7 @@
             // We'll just indicate that it failed, not why.
             testPassed = false;
         }
+
         runOnUiThread(new IdleTestResultRunner(IDLE_ON_JOB_ID, testPassed));
     }