AP Suspend tests.

Change-Id: Iee0bc35b7e832d0e4f683ba8e33f61ffe103215f
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 364f13e..dd85a19 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -34,6 +34,7 @@
                                androidplot \
                                ctsverifier-opencv \
                                core-tests \
+                               android-support-v4  \
                                mockito-target \
                                mockwebserver \
                                compatibility-device-util_v2 \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 5137761..0f4cb6b 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1200,6 +1200,23 @@
                        android:value="android.hardware.type.television:android.software.leanback" />
         </activity>
 -->
+          <activity
+                android:name="com.android.cts.verifier.sensors.DeviceSuspendTestActivity"
+                android:label="@string/snsr_device_suspend_test"
+                android:screenOrientation="nosensor" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_sensors" />
+        </activity>
+
+        <receiver android:name="com.android.cts.verifier.sensors.DeviceSuspendTestActivity$AlarmReceiver">
+        </receiver>
+
+        <receiver android:name="com.android.cts.verifier.sensors.SignificantMotionTestActivity$AlarmReceiver">
+        </receiver>
+
         <activity
             android:name="com.android.cts.verifier.sensors.SignificantMotionTestActivity"
             android:label="@string/snsr_significant_motion_test"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 6b35084..800deb6 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -649,6 +649,14 @@
     <string name="snsr_step_counter_event">%1$d | Step Counter event. count=%2$d.</string>
     <string name="snsr_step_detector_event">%1$d | Step Detector event.</string>
 
+    <!-- Device suspend tests -->
+    <string name="snsr_device_suspend_test">Device Suspend Tests</string>
+    <string name="snsr_device_did_not_go_into_suspend">Device did not go into suspend mode during test execution </string>
+    <string name="snsr_batch_did_not_arrive_at_expected_time">Batch did not arrive at the expected time estimatedBatchArrivalMs=%1$d
+    firstEventReceivedMs=%2$d diffMs=%3$d toleranceMs=%4$d </string>
+    <string name="snsr_device_suspend_test_instr">One you begin the test, disconnect USB, turn off the display and allow
+    the device to go into suspend mode. The screen will turn on and a sound will be played once all the tests are completed.</string>
+
     <!-- Significant Motion -->
     <string name="snsr_significant_motion_test">Significant Motion Tests</string>
     <string name="snsr_significant_motion_event_arrival">Event expected to trigger. Triggered=%1$s.</string>
@@ -663,6 +671,9 @@
     <string name="snsr_significant_motion_test_deactivation">Once you begin the test, you will need to walk to ensure Significant Motion triggers only once.</string>
     <string name="snsr_significant_motion_registration">Expected to be able to register for TriggerSensor. Found=%1$b.</string>
     <string name="snsr_significant_motion_cancelation">Expected to be able to cancel TriggerSensor. Found=%b.</string>
+    <string name="snsr_significant_motion_ap_suspend">One you begin the test, disconnect USB, turn off the display and allow the device to go into suspend.
+    You will need to walk to ensure that Significant Motion triggers. The screen will turn on and a sound will be played once the test completes.</string>
+    <string name="snsr_device_did_not_wake_up_at_trigger">Device did not wakeup at tigger time. wakeTime=%1$d ms triggerTime=%2$d ms</string>
 
     <!-- Strings for Sensor CTS tests inside CtsVerifier -->
     <string name="snsr_single_sensor_tests">CTS Single Sensor Tests</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
new file mode 100644
index 0000000..d066fce
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
@@ -0,0 +1,263 @@
+package com.android.cts.verifier.sensors;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
+import com.android.cts.verifier.sensors.helpers.SensorTestScreenManipulator;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+import android.hardware.cts.helpers.MovementDetectorHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.hardware.cts.helpers.sensorverification.BatchArrivalVerification;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import junit.framework.Assert;
+
+public class DeviceSuspendTestActivity
+            extends SensorCtsVerifierTestActivity {
+        public DeviceSuspendTestActivity() {
+            super(DeviceSuspendTestActivity.class);
+        }
+
+        private SensorTestScreenManipulator mScreenManipulator;
+        private PowerManager.WakeLock mDeviceSuspendLock;
+        private PendingIntent mPendingIntent;
+        private AlarmManager mAlarmManager;
+        private static String ACTION_ALARM = "DeviceSuspendTestActivity.ACTION_ALARM";
+        private static String TAG = "DeviceSuspendTestActivity";
+        private SensorManager mSensorManager;
+
+        @Override
+        protected void activitySetUp() throws InterruptedException {
+            mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+            mScreenManipulator = new SensorTestScreenManipulator(this);
+            mScreenManipulator.initialize(this);
+            LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver,
+                                            new IntentFilter(ACTION_ALARM));
+
+            Intent intent = new Intent(this, AlarmReceiver.class);
+            mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+
+            mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
+
+            PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+            mDeviceSuspendLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                                                "DeviceSuspendTestActivity");
+            mDeviceSuspendLock.acquire();
+            SensorTestLogger logger = getTestLogger();
+            logger.logInstructions(R.string.snsr_device_suspend_test_instr);
+            waitForUserToBegin();
+        }
+
+        @Override
+        protected void activityCleanUp() {
+            mScreenManipulator.turnScreenOn();
+            try {
+                playSound();
+            } catch(InterruptedException e) {
+              // Ignore.
+            }
+            LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
+        }
+
+        @Override
+        protected void onDestroy() {
+            super.onDestroy();
+            if (mScreenManipulator != null) {
+                mScreenManipulator.releaseScreenOn();
+                mScreenManipulator.close();
+            }
+            if (mDeviceSuspendLock.isHeld()) {
+                mDeviceSuspendLock.release();
+            }
+        }
+
+        public static class AlarmReceiver extends BroadcastReceiver {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                Intent alarm_intent = new Intent(context, DeviceSuspendTestActivity.class);
+                alarm_intent.setAction(DeviceSuspendTestActivity.ACTION_ALARM);
+                LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent);
+            }
+        }
+
+        public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (!mDeviceSuspendLock.isHeld()) {
+                    mDeviceSuspendLock.acquire();
+                }
+            }
+        };
+
+        public String testAPWakeUpWhenReportLatencyExpiresAccel() throws Throwable {
+            Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER, true);
+            if (wakeUpSensor == null) {
+                throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER, true);
+            }
+            return runAPWakeUpWhenReportLatencyExpires(wakeUpSensor);
+        }
+
+        public String testAPWakeUpWhenReportLatencyExpiresGyro() throws Throwable {
+            Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE, true);
+            if (wakeUpSensor == null) {
+                throw new SensorNotSupportedException(Sensor.TYPE_GYROSCOPE, true);
+            }
+            return runAPWakeUpWhenReportLatencyExpires(wakeUpSensor);
+        }
+
+        public String testAPWakeUpWhenReportLatencyExpiresMag() throws Throwable {
+            Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD,true);
+            if (wakeUpSensor == null) {
+                throw new SensorNotSupportedException(Sensor.TYPE_MAGNETIC_FIELD, true);
+            }
+            return runAPWakeUpWhenFIFOFull(wakeUpSensor);
+        }
+
+        public String testAPWakeUpWhenFIFOFullAccel() throws Throwable {
+            Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER, true);
+            if (wakeUpSensor == null) {
+                throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER, true);
+            }
+            return runAPWakeUpWhenFIFOFull(wakeUpSensor);
+        }
+
+        public String testAPWakeUpWhenFIFOFullGyro() throws Throwable {
+            Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE, true);
+            if (wakeUpSensor == null) {
+                throw new SensorNotSupportedException(Sensor.TYPE_GYROSCOPE, true);
+            }
+            return runAPWakeUpWhenFIFOFull(wakeUpSensor);
+        }
+
+        public String testAPWakeUpWhenFIFOFullMag() throws Throwable {
+            Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD,true);
+            if (wakeUpSensor == null) {
+                throw new SensorNotSupportedException(Sensor.TYPE_MAGNETIC_FIELD, true);
+            }
+            return runAPWakeUpWhenFIFOFull(wakeUpSensor);
+        }
+
+        public String runAPWakeUpWhenReportLatencyExpires(Sensor sensor) throws Throwable {
+            int fifoMaxEventCount = sensor.getFifoMaxEventCount();
+            if (fifoMaxEventCount == 0) {
+                throw new SensorTestStateNotSupportedException("Batching not supported.");
+            }
+            int maximumExpectedSamplingPeriodUs = sensor.getMaxDelay();
+            if (maximumExpectedSamplingPeriodUs == 0) {
+                // If maxDelay is not defined, set the value for 5 Hz.
+                maximumExpectedSamplingPeriodUs = 200000;
+            }
+            int fifoBasedReportLatencyUs = fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+
+            // Ensure that FIFO based report latency is at least 20 seconds, we need at least 10
+            // seconds of time to allow the device to be in suspend state.
+            if (fifoBasedReportLatencyUs < 20000000L) {
+                throw new SensorTestStateNotSupportedException("FIFO too small to test reliably");
+            }
+
+            final int MAX_REPORT_LATENCY_US = 15000000; // 15 seconds
+            TestSensorEnvironment environment = new TestSensorEnvironment(
+                    this,
+                    sensor,
+                    false,
+                    maximumExpectedSamplingPeriodUs,
+                    MAX_REPORT_LATENCY_US,
+                    true /*isDeviceSuspendTest*/);
+
+            int numEventsToWaitFor = MAX_REPORT_LATENCY_US/maximumExpectedSamplingPeriodUs;
+            TestSensorOperation op = TestSensorOperation.createOperation(environment,
+                                                                          numEventsToWaitFor,
+                                                                          mDeviceSuspendLock,
+                                                                          false);
+            final int ALARM_WAKE_UP_DELAY_MS = MAX_REPORT_LATENCY_US/1000 +
+                (int)TimeUnit.SECONDS.toMillis(10);
+            op.addVerification(BatchArrivalVerification.getDefault(environment));
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                                    SystemClock.elapsedRealtime() + ALARM_WAKE_UP_DELAY_MS,
+                                    mPendingIntent);
+            try {
+                op.execute(getCurrentTestNode());
+            } finally {
+                mAlarmManager.cancel(mPendingIntent);
+            }
+            return null;
+        }
+
+        public String runAPWakeUpWhenFIFOFull(Sensor sensor) throws Throwable {
+            int fifoMaxEventCount = sensor.getFifoMaxEventCount();
+            if (fifoMaxEventCount == 0) {
+                throw new SensorTestStateNotSupportedException("Batching not supported.");
+            }
+            // Try to fill the FIFO at the fastest rate and check if the time is enough to run
+            // the manual test.
+            int maximumExpectedSamplingPeriodUs = sensor.getMinDelay();
+            int fifoBasedReportLatencyUs = fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+
+            final int MIN_LATENCY_US = (int)TimeUnit.SECONDS.toMicros(20);
+            // Ensure that FIFO based report latency is at least 20 seconds, we need at least 10
+            // seconds of time to allow the device to be in suspend state.
+            if (fifoBasedReportLatencyUs < MIN_LATENCY_US) {
+                maximumExpectedSamplingPeriodUs = MIN_LATENCY_US/fifoMaxEventCount;
+                fifoBasedReportLatencyUs = MIN_LATENCY_US;
+            }
+
+            final int MAX_REPORT_LATENCY_US = Integer.MAX_VALUE;
+            final int ALARM_WAKE_UP_DELAY_MS = fifoBasedReportLatencyUs/1000 +
+                (int)TimeUnit.SECONDS.toMillis(10);
+            TestSensorEnvironment environment = new TestSensorEnvironment(
+                    this,
+                    sensor,
+                    false,
+                    maximumExpectedSamplingPeriodUs,
+                    MAX_REPORT_LATENCY_US,
+                    true /*isDeviceSuspendTest*/);
+
+           int numEventsToWaitFor = fifoMaxEventCount;
+            TestSensorOperation op = TestSensorOperation.createOperation(environment,
+                                                                          numEventsToWaitFor,
+                                                                          mDeviceSuspendLock,
+                                                                         true);
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                                    SystemClock.elapsedRealtime() + ALARM_WAKE_UP_DELAY_MS,
+                                    mPendingIntent);
+            op.addDefaultVerifications();
+            try {
+                op.execute(getCurrentTestNode());
+            } finally {
+                mAlarmManager.cancel(mPendingIntent);
+            }
+            return null;
+        }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
index faba445..f8f1a9a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -16,22 +16,36 @@
 
 package com.android.cts.verifier.sensors;
 
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
+import com.android.cts.verifier.sensors.helpers.SensorTestScreenManipulator;
 
-import junit.framework.Assert;
-
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.hardware.TriggerEvent;
 import android.hardware.TriggerEventListener;
 import android.hardware.cts.helpers.SensorNotSupportedException;
 import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.SuspendStateMonitor;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
 import android.os.SystemClock;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import junit.framework.Assert;
 
 /**
  * Test cases for Significant Motion sensor.
@@ -46,20 +60,30 @@
     private static final long MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS =
             TimeUnit.MILLISECONDS.toNanos(500);
 
+    // acceptable time difference between event time and AP wake up time.
+    private static final long MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS =
+            TimeUnit.MILLISECONDS.toNanos(2000);
+
+    // time to wait for SMD after the device has gone into suspend. Even after
+    // 45 secs if SMD does not trigger, the test will fail.
+    private static final long ALARM_WAKE_TIME_DELAY_MS = TimeUnit.SECONDS.toMillis(45);
+
     // time for the test to wait for a trigger
     private static final int TRIGGER_MAX_DELAY_SECONDS = 30;
     private static final int VIBRATE_DURATION_MILLIS = 10000;
 
     private static final int EVENT_VALUES_LENGTH = 1;
     private static final float EXPECTED_EVENT_VALUE = 1.0f;
+    private static String ACTION_ALARM = "SignificantMotionTestActivity.ACTION_ALARM";
 
     private SensorManager mSensorManager;
     private Sensor mSensorSignificantMotion;
+    private TriggerVerifier mVerifier;
+    private SensorTestScreenManipulator mScreenManipulator;
 
     /**
      * Test cases.
      */
-
     @SuppressWarnings("unused")
     public String testTrigger() throws Throwable {
         return runTest(
@@ -131,6 +155,69 @@
         return result;
     }
 
+    public static class AlarmReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Intent alarm_intent = new Intent(context, SignificantMotionTestActivity.class);
+            alarm_intent.setAction(SignificantMotionTestActivity.ACTION_ALARM);
+            LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent);
+        }
+    }
+
+    public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mVerifier.releaseLatch();
+            mScreenManipulator.turnScreenOn();
+            try {
+                playSound();
+            } catch (InterruptedException e) {
+                // Ignore ...
+            }
+        }
+    };
+
+    @SuppressWarnings("unused")
+    public String testAPWakeUpOnSMDTrigger() throws Throwable {
+        SensorTestLogger logger = getTestLogger();
+        logger.logInstructions(R.string.snsr_significant_motion_ap_suspend);
+        waitForUserToBegin();
+        mVerifier = new TriggerVerifier();
+        mSensorManager.requestTriggerSensor(mVerifier, mSensorSignificantMotion);
+        long testStartTimeNs = SystemClock.elapsedRealtimeNanos();
+        Handler handler = new Handler(Looper.getMainLooper());
+        SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor();
+
+        Intent intent = new Intent(this, AlarmReceiver.class);
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+
+        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
+        am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                     SystemClock.elapsedRealtime() + ALARM_WAKE_TIME_DELAY_MS, pendingIntent);
+        try {
+            // Wait for the first event to trigger. Device is expected to go into suspend here.
+            mVerifier.verifyEventTriggered();
+            long eventTimeStampNs = mVerifier.getTimeStampForTriggerEvent();
+            long endTimeNs = SystemClock.elapsedRealtimeNanos();
+            long lastWakeupTimeNs = TimeUnit.MILLISECONDS.toNanos(
+                    suspendStateMonitor.getLastWakeUpTime());
+            Assert.assertTrue(getString(R.string.snsr_device_did_not_go_into_suspend),
+                              testStartTimeNs < lastWakeupTimeNs && lastWakeupTimeNs < endTimeNs);
+            long timestampDelta = Math.abs(lastWakeupTimeNs - eventTimeStampNs);
+            Assert.assertTrue(
+                    String.format(getString(R.string.snsr_device_did_not_wake_up_at_trigger),
+                              TimeUnit.NANOSECONDS.toMillis(lastWakeupTimeNs),
+                              TimeUnit.NANOSECONDS.toMillis(eventTimeStampNs)),
+                              timestampDelta < MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS);
+        } finally {
+            am.cancel(pendingIntent);
+            suspendStateMonitor.cancel();
+            mScreenManipulator.turnScreenOn();
+            playSound();
+        }
+        return null;
+    }
+
     /**
      * @param instructionsResId Instruction to be shown to testers
      * @param isMotionExpected Should the device detect significant motion event
@@ -187,6 +274,27 @@
         if (mSensorSignificantMotion == null) {
             throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
         }
+
+        mScreenManipulator = new SensorTestScreenManipulator(this);
+        try {
+            mScreenManipulator.initialize(this);
+        } catch (InterruptedException e) {
+        }
+        PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+        LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver,
+                                            new IntentFilter(ACTION_ALARM));
+    }
+
+    @Override
+    protected void activityCleanUp() {
+        mScreenManipulator.turnScreenOff();
+        LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mScreenManipulator.close();
     }
 
     /**
@@ -194,7 +302,7 @@
      * It cannot be reused.
      */
     private class TriggerVerifier extends TriggerEventListener {
-        private volatile CountDownLatch mCountDownLatch;
+        private volatile CountDownLatch mCountDownLatch = new CountDownLatch(1);
         private volatile TriggerEventRegistry mEventRegistry;
 
         // TODO: refactor out if needed
@@ -214,6 +322,19 @@
             mCountDownLatch.countDown();
         }
 
+        public void releaseLatch() {
+            if (mCountDownLatch != null) {
+                mCountDownLatch.countDown();
+            }
+        }
+
+        public long getTimeStampForTriggerEvent() {
+            if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
+                return mEventRegistry.triggerEvent.timestamp;
+            }
+            return 0;
+        }
+
         public String verifyEventTriggered() throws Throwable {
             TriggerEventRegistry registry = awaitForEvent();
 
@@ -267,11 +388,8 @@
         }
 
         private TriggerEventRegistry awaitForEvent() throws InterruptedException {
-            mCountDownLatch = new CountDownLatch(1);
             mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS);
-
             TriggerEventRegistry registry = mEventRegistry;
-            mEventRegistry = null;
 
             playSound();
             return registry != null ? registry : new TriggerEventRegistry(null, 0);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 4450339..6beeec8 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -348,9 +348,9 @@
         TestSensorEventListener listener = new TestSensorEventListener(environment, handler);
 
         CountDownLatch eventLatch = mTestSensorManager.registerListener(listener, 1);
-        listener.waitForEvents(eventLatch, 1);
+        listener.waitForEvents(eventLatch, 1, true);
         CountDownLatch flushLatch = mTestSensorManager.requestFlush();
-        listener.waitForFlushComplete(flushLatch);
+        listener.waitForFlushComplete(flushLatch, true);
         listener.assertEventsReceivedInHandler();
     }
 
@@ -382,9 +382,9 @@
 
         // specifyHandler <= false, use the SensorManager API without Handler parameter
         CountDownLatch eventLatch = mTestSensorManager.registerListener(listener, 1, false);
-        listener.waitForEvents(eventLatch, 1);
+        listener.waitForEvents(eventLatch, 1, true);
         CountDownLatch flushLatch = mTestSensorManager.requestFlush();
-        listener.waitForFlushComplete(flushLatch);
+        listener.waitForFlushComplete(flushLatch, true);
         listener.assertEventsReceivedInHandler();
     }
 
@@ -581,10 +581,10 @@
             try {
                 CountDownLatch eventLatch = sensorManager.registerListener(listener, mEventCount);
                 if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) {
-                    listener.waitForEvents(eventLatch, mEventCount);
+                    listener.waitForEvents(eventLatch, mEventCount, true);
                 }
                 CountDownLatch flushLatch = sensorManager.requestFlush();
-                listener.waitForFlushComplete(flushLatch);
+                listener.waitForFlushComplete(flushLatch, true);
             } finally {
                 sensorManager.unregisterListener();
             }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorNotSupportedException.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorNotSupportedException.java
index e727092..0d90957 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorNotSupportedException.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorNotSupportedException.java
@@ -26,6 +26,11 @@
         super("Sensor '%s' of type %d is not supported.", getSensorName(sensorType), sensorType);
     }
 
+    public SensorNotSupportedException(int sensorType, boolean wakeup) {
+        super("Sensor '%s' of type %d and %s is not supported.", getSensorName(sensorType),
+               sensorType, wakeup ? "wake-up" : "non wake-up");
+    }
+
     private static String getSensorName(int sensorType) {
         return String.format("%s (%d)", getSimpleSensorName(sensorType), sensorType);
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index 8067aed..f0f0186 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -54,6 +54,7 @@
     public static final String MEAN_KEY = "mean";
     public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
     public static final String MAGNITUDE_KEY = "magnitude";
+    public static final String DELAYED_BATCH_DELIVERY = "delayed_batch_delivery";
 
     private final Map<String, Object> mValues = new HashMap<>();
     private final Map<String, SensorStats> mSensorStats = new HashMap<>();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SuspendStateMonitor.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SuspendStateMonitor.java
new file mode 100644
index 0000000..4426967
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SuspendStateMonitor.java
@@ -0,0 +1,57 @@
+package android.hardware.cts.helpers;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.hardware.Sensor;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+public class SuspendStateMonitor {
+    private final double firstRealTimeMillis;
+    private final double firstUpTimeMillis;
+    private double lastSleepTimeSeconds = 0;
+    private volatile long lastWakeUpTime = 0;
+    Timer sleepMonitoringTimer = new Timer();
+
+    /**
+     * Returns the time the device slept since the start of the application,
+     * in seconds.
+     */
+    public double getSleepTimeSeconds() {
+        double totalSinceStart = android.os.SystemClock.elapsedRealtime() - firstRealTimeMillis;
+        double upTimeSinceStart = android.os.SystemClock.uptimeMillis() - firstUpTimeMillis;
+        return (totalSinceStart - upTimeSinceStart) / 1000;
+    }
+
+    public long getLastWakeUpTime() {
+        return lastWakeUpTime;
+    }
+
+    public void cancel() {
+        sleepMonitoringTimer.cancel();
+    }
+
+     public SuspendStateMonitor() {
+        firstRealTimeMillis = android.os.SystemClock.elapsedRealtime();
+        firstUpTimeMillis = android.os.SystemClock.uptimeMillis();
+        // Every 100 miliseconds, check whether the device has slept.
+        TimerTask sleepMonitoringTask = new TimerTask() {
+                @Override
+                public void run() {
+                    if (getSleepTimeSeconds() - lastSleepTimeSeconds > 0.1) {
+                        lastSleepTimeSeconds = getSleepTimeSeconds();
+                        lastWakeUpTime = SystemClock.elapsedRealtime();
+                    }
+                }
+        };
+        sleepMonitoringTimer.schedule(sleepMonitoringTask, 0, 100);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 143f0a1..6156d3d 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -40,6 +40,7 @@
     private final boolean mSensorMightHaveMoreListeners;
     private final int mSamplingPeriodUs;
     private final int mMaxReportLatencyUs;
+    private final boolean mIsDeviceSuspendTest;
 
     /**
      * Constructs an environment for sensor testing.
@@ -163,11 +164,27 @@
             boolean sensorMightHaveMoreListeners,
             int samplingPeriodUs,
             int maxReportLatencyUs) {
+        this(context,
+                sensor,
+                sensorMightHaveMoreListeners,
+                samplingPeriodUs,
+                maxReportLatencyUs,
+                false /* isDeviceSuspendTest */);
+    }
+
+    public TestSensorEnvironment(
+            Context context,
+            Sensor sensor,
+            boolean sensorMightHaveMoreListeners,
+            int samplingPeriodUs,
+            int maxReportLatencyUs,
+            boolean isDeviceSuspendTest) {
         mContext = context;
         mSensor = sensor;
         mSensorMightHaveMoreListeners = sensorMightHaveMoreListeners;
         mSamplingPeriodUs = samplingPeriodUs;
         mMaxReportLatencyUs = maxReportLatencyUs;
+        mIsDeviceSuspendTest = isDeviceSuspendTest;
     }
 
     /**
@@ -357,4 +374,9 @@
                 && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_UI
                 && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_NORMAL);
     }
+
+    public boolean isDeviceSuspendTest() {
+        return mIsDeviceSuspendTest;
+    }
 }
+
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 662c3ce..23effb9 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -18,12 +18,16 @@
 
 import junit.framework.Assert;
 
+import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener2;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.util.Log;
 
 import java.io.BufferedWriter;
 import java.io.File;
@@ -50,12 +54,14 @@
     private static final long FLUSH_TIMEOUT_US = TimeUnit.SECONDS.toMicros(10);
 
     private final ArrayList<TestSensorEvent> mCollectedEvents = new ArrayList<>();
+    private final ArrayList<Long> mTimeStampFlushCompleteEvents = new ArrayList<>();
     private final List<CountDownLatch> mEventLatches = new ArrayList<>();
     private final List<CountDownLatch> mFlushLatches = new ArrayList<>();
     private final AtomicInteger mEventsReceivedOutsideHandler = new AtomicInteger();
 
     private final Handler mHandler;
     private final TestSensorEnvironment mEnvironment;
+    private final PowerManager.WakeLock mTestSensorEventListenerWakeLock;
 
     /**
      * @deprecated Use {@link TestSensorEventListener(TestSensorEnvironment)}.
@@ -78,6 +84,10 @@
     public TestSensorEventListener(TestSensorEnvironment environment, Handler handler) {
         mEnvironment = environment;
         mHandler = handler;
+        PowerManager pm = (PowerManager) environment.getContext().getSystemService(
+                Context.POWER_SERVICE);
+        mTestSensorEventListenerWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                                                "TestSensorEventListenerWakeLock");
     }
 
     /**
@@ -93,6 +103,9 @@
         synchronized (mEventLatches) {
             for (CountDownLatch latch : mEventLatches) {
                 latch.countDown();
+                if (latch.getCount() == 0 && !mTestSensorEventListenerWakeLock.isHeld()) {
+                    mTestSensorEventListenerWakeLock.acquire();
+                }
             }
         }
     }
@@ -136,6 +149,10 @@
     @Override
     public void onFlushCompleted(Sensor sensor) {
         checkHandler();
+        long timestampNs = SystemClock.elapsedRealtimeNanos();
+        synchronized (mTimeStampFlushCompleteEvents) {
+           mTimeStampFlushCompleteEvents.add(timestampNs);
+        }
         synchronized (mFlushLatches) {
             for (CountDownLatch latch : mFlushLatches) {
                 latch.countDown();
@@ -175,7 +192,8 @@
      * It will overwrite the file if it already exists, the file is created in a relative directory
      * named 'events' under the sensor test directory (part of external storage).
      */
-    public void logCollectedEventsToFile(String fileName) throws IOException {
+    public void logCollectedEventsToFile(String fileName, long deviceWakeUpTimeMs)
+        throws IOException {
         StringBuilder builder = new StringBuilder();
         builder.append("Sensor='").append(mEnvironment.getSensor()).append("', ");
         builder.append("SamplingRateOverloaded=")
@@ -184,15 +202,54 @@
                 .append(mEnvironment.getRequestedSamplingPeriodUs()).append("us, ");
         builder.append("MaxReportLatency=")
                 .append(mEnvironment.getMaxReportLatencyUs()).append("us");
-
         synchronized (mCollectedEvents) {
-            for (TestSensorEvent event : mCollectedEvents) {
+            int i = 0, j = 0;
+            while (i < mCollectedEvents.size() && j < mTimeStampFlushCompleteEvents.size()) {
+                if (mCollectedEvents.get(i).receivedTimestamp <
+                        mTimeStampFlushCompleteEvents.get(j)) {
+                    TestSensorEvent event = mCollectedEvents.get(i);
+                    if (deviceWakeUpTimeMs != -1 && deviceWakeUpTimeMs <
+                            event.receivedTimestamp/1000000) {
+                        builder.append("\n");
+                        builder.append("AP wake-up time=").append(deviceWakeUpTimeMs).append("ms");
+                        deviceWakeUpTimeMs = -1;
+                    }
+                    builder.append("\n");
+                    builder.append("Timestamp=").append(event.timestamp/1000000).append("ms, ");
+                    builder.append("ReceivedTimestamp=").append(event.receivedTimestamp/1000000).
+                        append("ms, ");
+                    builder.append("Accuracy=").append(event.accuracy).append(", ");
+                    builder.append("Values=").append(Arrays.toString(event.values));
+                    ++i;
+                } else {
+                    builder.append("\n");
+                    builder.append("ReceivedTimestamp=")
+                    .append(mTimeStampFlushCompleteEvents.get(j)/1000000)
+                    .append(" Flush complete Event");
+                    ++j;
+                }
+            }
+            for (;i < mCollectedEvents.size(); ++i) {
+                TestSensorEvent event = mCollectedEvents.get(i);
+                if (deviceWakeUpTimeMs != -1 && deviceWakeUpTimeMs <
+                        event.receivedTimestamp/1000000) {
+                    builder.append("\n");
+                    builder.append("AP wake-up time=").append(deviceWakeUpTimeMs).append("ms");
+                    deviceWakeUpTimeMs = -1;
+                }
                 builder.append("\n");
-                builder.append("Timestamp=").append(event.timestamp).append("ns, ");
-                builder.append("ReceivedTimestamp=").append(event.receivedTimestamp).append("ns, ");
+                builder.append("Timestamp=").append(event.timestamp/1000000).append("ms, ");
+                builder.append("ReceivedTimestamp=").append(event.receivedTimestamp/1000000).
+                    append("ms, ");
                 builder.append("Accuracy=").append(event.accuracy).append(", ");
                 builder.append("Values=").append(Arrays.toString(event.values));
             }
+            for (;j < mTimeStampFlushCompleteEvents.size(); ++j) {
+                builder.append("\n");
+                builder.append("ReceivedTimestamp=")
+                    .append(mTimeStampFlushCompleteEvents.get(j)/1000000)
+                    .append("ms Flush complete Event");
+            }
         }
 
         File eventsDirectory = SensorCtsHelper.getSensorTestDataDirectory("events/");
@@ -208,8 +265,11 @@
      *
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
-    public void waitForFlushComplete(CountDownLatch latch) throws InterruptedException {
-        clearEvents();
+    public void waitForFlushComplete(CountDownLatch latch,
+                                      boolean clearCollectedEvents) throws InterruptedException {
+        if (clearCollectedEvents) {
+            clearEvents();
+        }
         try {
             String message = SensorCtsHelper.formatAssertionMessage(
                     "WaitForFlush",
@@ -229,8 +289,11 @@
      *
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
-    public void waitForEvents(CountDownLatch latch, int eventCount) throws InterruptedException {
-        clearEvents();
+    public void waitForEvents(CountDownLatch latch, int eventCount,
+                               boolean clearCollectedEvents) throws InterruptedException {
+        if (clearCollectedEvents) {
+            clearEvents();
+        }
         try {
             long samplingPeriodUs = mEnvironment.getMaximumExpectedSamplingPeriodUs();
             // timeout is 2 * event count * expected period + batch timeout + default wait
@@ -279,6 +342,12 @@
         Assert.assertEquals(message, 0 /* expected */, eventsOutsideHandler);
     }
 
+    public void releaseWakeLock() {
+        if (mTestSensorEventListenerWakeLock.isHeld()) {
+            mTestSensorEventListenerWakeLock.release();
+        }
+    }
+
     /**
      * Keeps track of the number of events that arrived in a different {@link Looper} than the one
      * associated with the {@link TestSensorEventListener}.
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 3b90b15..d6cc54e 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -16,7 +16,11 @@
 
 package android.hardware.cts.helpers.sensoroperations;
 
-import junit.framework.Assert;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 import android.hardware.cts.helpers.SensorCtsHelper;
 import android.hardware.cts.helpers.SensorStats;
@@ -25,6 +29,7 @@
 import android.hardware.cts.helpers.TestSensorEvent;
 import android.hardware.cts.helpers.TestSensorEventListener;
 import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.SuspendStateMonitor;
 import android.hardware.cts.helpers.reporting.ISensorTestNode;
 import android.hardware.cts.helpers.sensorverification.EventGapVerification;
 import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
@@ -36,13 +41,11 @@
 import android.hardware.cts.helpers.sensorverification.MeanVerification;
 import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
 import android.os.Handler;
+import android.os.SystemClock;
+import android.os.PowerManager.WakeLock;
 import android.util.Log;
 
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import junit.framework.Assert;
 
 /**
  * A {@link SensorOperation} used to verify that sensor events and sensor values are correct.
@@ -61,6 +64,7 @@
     private final TestSensorEnvironment mEnvironment;
     private final Executor mExecutor;
     private final Handler mHandler;
+    private long mDeviceWakeUpTimeMs = -1;
 
     /**
      * An interface that defines an abstraction for operations to be performed by the
@@ -118,7 +122,22 @@
     public void execute(ISensorTestNode parent) throws InterruptedException {
         getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
         TestSensorEventListener listener = new TestSensorEventListener(mEnvironment, mHandler);
-        mExecutor.execute(mSensorManager, listener);
+
+        if (mEnvironment.isDeviceSuspendTest()) {
+            SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor();
+            long startTimeMs = SystemClock.elapsedRealtime();
+            // Device should go into suspend here.
+            mExecutor.execute(mSensorManager, listener);
+            long endTimeMs = SystemClock.elapsedRealtime();
+            // Check if the device has gone into suspend during test execution.
+            mDeviceWakeUpTimeMs = suspendStateMonitor.getLastWakeUpTime();
+            suspendStateMonitor.cancel();
+            Assert.assertTrue("Device did not go into suspend during test execution",
+                                       startTimeMs < mDeviceWakeUpTimeMs &&
+                                       mDeviceWakeUpTimeMs < endTimeMs);
+        } else {
+            mExecutor.execute(mSensorManager, listener);
+        }
 
         boolean failed = false;
         StringBuilder sb = new StringBuilder();
@@ -193,7 +212,7 @@
         }
 
         try {
-            listener.logCollectedEventsToFile(sanitizedFileName);
+            listener.logCollectedEventsToFile(sanitizedFileName, mDeviceWakeUpTimeMs);
         } catch (IOException e) {
             Log.w(TAG, "Unable to save collected events to file: " + sanitizedFileName, e);
         }
@@ -214,7 +233,7 @@
                     throws InterruptedException {
                 try {
                     CountDownLatch latch = sensorManager.registerListener(listener, eventCount);
-                    listener.waitForEvents(latch, eventCount);
+                    listener.waitForEvents(latch, eventCount, true);
                 } finally {
                     sensorManager.unregisterListener();
                 }
@@ -224,6 +243,62 @@
     }
 
     /**
+     * Creates an operation that will wait for a given amount of events to arrive.
+     *
+     * @param environment The test environment.
+     * @param eventCount The number of events to wait for.
+     */
+    public static TestSensorOperation createOperation(
+            final TestSensorEnvironment environment,
+            final int eventCount,
+            final WakeLock wakeLock,
+            final boolean flushRequested) {
+        Executor executor = new Executor() {
+            @Override
+            public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+                    throws InterruptedException {
+                try {
+                    int eventCountForLatch = eventCount;
+                    if (flushRequested) {
+                        eventCountForLatch = eventCount + (int)environment.getFrequencyHz();
+                    }
+                    CountDownLatch latch = sensorManager.registerListener(listener,
+                                                                           eventCountForLatch);
+                    if (flushRequested) {
+                        SensorCtsHelper.sleep(1, TimeUnit.SECONDS);
+                        CountDownLatch flushLatch = sensorManager.requestFlush();
+                        listener.waitForFlushComplete(flushLatch, false);
+                    }
+                    if (wakeLock.isHeld()) {
+                        wakeLock.release();
+                    }
+                    listener.releaseWakeLock();
+                    Log.v("TestSensorOperation", "waitForEvents " +
+                            environment.getSensor().getName()
+                          + " " + latch.getCount());
+                    listener.waitForEvents(latch, eventCount, false);
+                    Log.v("TestSensorOperation", "waitForEvents DONE " + environment.getSensor().getName());
+                    if (!wakeLock.isHeld()) {
+                        wakeLock.acquire();
+                    }
+                    if (flushRequested) {
+                        SensorCtsHelper.sleep(1, TimeUnit.SECONDS);
+                        CountDownLatch flushLatch = sensorManager.requestFlush();
+                        listener.waitForFlushComplete(flushLatch, false);
+                    }
+                } finally {
+                    if(!wakeLock.isHeld()) {
+                        wakeLock.acquire();
+                    }
+                    listener.releaseWakeLock();
+                    sensorManager.unregisterListener();
+                }
+            }
+        };
+        return new TestSensorOperation(environment, executor);
+    }
+
+    /**
      * Creates an operation that will wait for a given amount of time to collect events.
      *
      * @param environment The test environment.
@@ -269,7 +344,7 @@
                     sensorManager.registerListener(listener);
                     SensorCtsHelper.sleep(duration, timeUnit);
                     CountDownLatch latch = sensorManager.requestFlush();
-                    listener.waitForFlushComplete(latch);
+                    listener.waitForFlushComplete(latch, true);
                 } finally {
                     sensorManager.unregisterListener();
                 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/BatchArrivalVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/BatchArrivalVerification.java
new file mode 100644
index 0000000..5e50c3a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/BatchArrivalVerification.java
@@ -0,0 +1,139 @@
+
+package android.hardware.cts.helpers.sensorverification;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.hardware.cts.helpers.sensorverification.AbstractSensorVerification.IndexedEventPair;
+import android.os.SystemClock;
+import android.provider.Settings.System;
+
+import junit.framework.Assert;
+
+/**
+ * A {@link ISensorVerification} which verifies that there are no missing events. This is done by
+ * checking the last received sensor timestamp and checking that it is within 1.8 * the expected
+ * period.
+ */
+public class BatchArrivalVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "missing_event_passed";
+
+    // Batch arrival tolerance is 5 seconds.
+    private static final int BATCH_ARRIVAL_TOLERANCE_US = 5000000;
+
+    // Number of indices to print in assert message before truncating
+    private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+    // Number of events to truncate (discard) from the initial events received
+    private static final int TRUNCATE_EVENTS_COUNT = 100;
+
+    private final long mExpectedBatchArrivalTimeUs;
+
+    private final List<IndexedEventPair> mFailures = new LinkedList<IndexedEventPair>();
+    private TestSensorEvent mFirstEvent = null;
+    private int mIndex = 0;
+
+    /**
+     * Construct a {@link EventGapVerification}
+     *
+     * @param expectedDelayUs the expected period in us.
+     */
+    public BatchArrivalVerification(long expectedBatchArrivalTimeUs) {
+         mExpectedBatchArrivalTimeUs = expectedBatchArrivalTimeUs;
+    }
+
+    /**
+     * Get the default {@link EventGapVerification}.
+     *
+     * @param environment the test environment
+     * @return the verification or null if the verification is not a continuous mode sensor.
+     */
+    public static BatchArrivalVerification getDefault(TestSensorEnvironment environment) {
+        if (environment.getSensor().getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
+            return null;
+        }
+        long fifoMaxEventCount = environment.getSensor().getFifoMaxEventCount();
+        int maximumExpectedSamplingPeriodUs = environment.getMaximumExpectedSamplingPeriodUs();
+        long reportLatencyUs = environment.getMaxReportLatencyUs();
+        if (fifoMaxEventCount > 0 && maximumExpectedSamplingPeriodUs != Integer.MAX_VALUE) {
+            long fifoBasedReportLatencyUs =
+                    fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+            // If the device goes into suspend mode during the test and the sensor under test is
+            // a non wake-up sensor, the FIFO will keep overwriting itself and the reportLatency
+            // of each event will be equal to the time it takes to fill up the FIFO.
+            if (environment.isDeviceSuspendTest() && !environment.getSensor().isWakeUpSensor()) {
+                reportLatencyUs = fifoBasedReportLatencyUs;
+            } else {
+                // In this case the sensor under test is either a wake-up sensor OR it
+                // is a non wake-up sensor but the device does not go into suspend.
+                // So the expected delay of a sensor_event is the minimum of the
+                // fifoBasedReportLatencyUs and the requested latency by the application.
+                reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs);
+            }
+        }
+        long expectedBatchArrivalTimeUs = reportLatencyUs + SystemClock.elapsedRealtime() * 1000;
+        return new BatchArrivalVerification(expectedBatchArrivalTimeUs);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void verify(TestSensorEnvironment environment, SensorStats stats) {
+        final int count = mFailures.size();
+        stats.addValue(PASSED_KEY, count == 0);
+        stats.addValue(SensorStats.DELAYED_BATCH_DELIVERY, count);
+
+        if (count > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(count).append(" batch delayed: ");
+            for (int i = 0; i < Math.min(count, TRUNCATE_MESSAGE_LENGTH); i++) {
+                IndexedEventPair info = mFailures.get(i);
+                sb.append(String.format("expectedBatchArrival=%dms actualBatchArrivalTime=%dms "+
+                                        "diff=%dms tolerance=%dms",
+                                         (mExpectedBatchArrivalTimeUs)/1000,
+                                         info.event.receivedTimestamp/(1000 * 1000),
+                                         (mExpectedBatchArrivalTimeUs -
+                                          info.event.receivedTimestamp/1000)/1000,
+                                         BATCH_ARRIVAL_TOLERANCE_US/1000)
+
+                          );
+
+            }
+            if (count > TRUNCATE_MESSAGE_LENGTH) {
+                sb.append(count - TRUNCATE_MESSAGE_LENGTH).append(" more; ");
+            }
+            Assert.fail(sb.toString());
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public BatchArrivalVerification clone() {
+        return new BatchArrivalVerification(mExpectedBatchArrivalTimeUs);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mFirstEvent == null) {
+            mFirstEvent = event;
+        }
+        if (mIndex == 1) {
+            if (Math.abs(mFirstEvent.receivedTimestamp/1000 - mExpectedBatchArrivalTimeUs) >
+                BATCH_ARRIVAL_TOLERANCE_US) {
+                mFailures.add(new IndexedEventPair(1, mFirstEvent, null));
+            }
+        }
+        ++mIndex;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
index a90725f..3af3f03 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
@@ -75,6 +75,16 @@
                     fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
             reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs) +
                 (long)(2.5 * maximumExpectedSamplingPeriodUs);
+            // of each event will be equal to the time it takes to fill up the FIFO.
+            if (environment.isDeviceSuspendTest() && !environment.getSensor().isWakeUpSensor()) {
+                reportLatencyUs = fifoBasedReportLatencyUs;
+            } else {
+                // In this case the sensor under test is either a wake-up sensor OR it
+                // is a non wake-up sensor but the device does not go into suspend.
+                // So the expected delay of a sensor_event is the minimum of the
+                // fifoBasedReportLatencyUs and the requested latency by the application.
+                reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs);
+            }
         }
         long expectedSyncLatencyNs = TimeUnit.MICROSECONDS.toNanos(reportLatencyUs);
         return new EventTimestampSynchronizationVerification(DEFAULT_THRESHOLD_NS,