| 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.hardware.cts.helpers.sensorverification.TimestampClockSourceVerification; |
| 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 = "DeviceSuspendSensorTest"; |
| 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); |
| if (mDeviceSuspendLock != null && mDeviceSuspendLock.isHeld()) { |
| mDeviceSuspendLock.release(); |
| } |
| } |
| |
| @Override |
| protected void onDestroy() { |
| super.onDestroy(); |
| if (mScreenManipulator != null) { |
| mScreenManipulator.releaseScreenOn(); |
| mScreenManipulator.close(); |
| } |
| } |
| |
| 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 runAPWakeUpWhenReportLatencyExpires(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 testAccelBatchingInAPSuspendLargeReportLatency() throws Throwable { |
| Sensor accel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); |
| if (accel == null) { |
| throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER, false); |
| } |
| return runAPWakeUpByAlarmNonWakeSensor(accel, (int)TimeUnit.SECONDS.toMicros(1000)); |
| } |
| |
| public String testAccelBatchingInAPSuspendZeroReportLatency() throws Throwable { |
| Sensor accel = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); |
| if (accel == null) { |
| throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER, false); |
| } |
| return runAPWakeUpByAlarmNonWakeSensor(accel, 0); |
| } |
| |
| /** |
| * Verify that each continuous sensor is using the correct |
| * clock source (CLOCK_BOOTTIME) for timestamps. |
| */ |
| public String testTimestampClockSource() throws Throwable { |
| String string = null; |
| boolean error_occurred = false; |
| List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL); |
| if (sensorList == null) { |
| throw new SensorTestStateNotSupportedException( |
| "Sensors are not available in the system."); |
| } |
| |
| // Make sure clocks are different (i.e. kernel has suspended at least once) |
| // so that we can determine if sensors are using correct clocksource timestamp |
| final int MAX_SLEEP_ATTEMPTS = 10; |
| final int SLEEP_DURATION_MS = 2000; |
| int sleep_attempts = 0; |
| boolean device_needs_sleep = true; |
| boolean wakelock_was_held = false; |
| |
| final long ALARM_WAKE_UP_DELAY_MS = TimeUnit.SECONDS.toMillis(20); |
| mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, |
| SystemClock.elapsedRealtime() + ALARM_WAKE_UP_DELAY_MS, |
| mPendingIntent); |
| |
| if (mDeviceSuspendLock != null && mDeviceSuspendLock.isHeld()) { |
| wakelock_was_held = true; |
| mDeviceSuspendLock.release(); |
| } |
| |
| do { |
| try { |
| verifyClockDelta(); |
| device_needs_sleep = false; |
| } catch(Throwable e) { |
| // Delta between clocks too small, must sleep longer |
| if (sleep_attempts++ > MAX_SLEEP_ATTEMPTS) { |
| mAlarmManager.cancel(mPendingIntent); |
| if (wakelock_was_held) { |
| mDeviceSuspendLock.acquire(); |
| } |
| throw e; |
| } |
| Thread.sleep(SLEEP_DURATION_MS); |
| } |
| } while (device_needs_sleep); |
| |
| if (wakelock_was_held) { |
| mDeviceSuspendLock.acquire(); |
| } |
| mAlarmManager.cancel(mPendingIntent); |
| |
| for (Sensor sensor : sensorList) { |
| if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) { |
| try { |
| string = runVerifySensorTimestampClockbase(sensor, false); |
| if (string != null) { |
| return string; |
| } |
| } catch(Throwable e) { |
| Log.e(TAG, e.getMessage()); |
| error_occurred = true; |
| } |
| } else { |
| Log.i(TAG, "testTimestampClockSource skipping non-continuous sensor: '" + sensor.getName()); |
| } |
| } |
| if (error_occurred) { |
| throw new Error("Sensors must use CLOCK_BOOTTIME as clock source for timestamping events"); |
| } |
| return null; |
| } |
| |
| public String runAPWakeUpWhenReportLatencyExpires(Sensor sensor) throws Throwable { |
| |
| verifyBatchingSupport(sensor); |
| |
| int fifoMaxEventCount = sensor.getFifoMaxEventCount(); |
| int samplingPeriodUs = sensor.getMaxDelay(); |
| if (samplingPeriodUs == 0) { |
| // If maxDelay is not defined, set the value for 5 Hz. |
| samplingPeriodUs = 200000; |
| } |
| |
| long fifoBasedReportLatencyUs = maxBatchingPeriod(sensor, samplingPeriodUs); |
| verifyBatchingPeriod(fifoBasedReportLatencyUs); |
| |
| final long MAX_REPORT_LATENCY_US = TimeUnit.SECONDS.toMicros(15); // 15 seconds |
| TestSensorEnvironment environment = new TestSensorEnvironment( |
| this, |
| sensor, |
| false, |
| samplingPeriodUs, |
| (int) MAX_REPORT_LATENCY_US, |
| true /*isDeviceSuspendTest*/); |
| |
| TestSensorOperation op = TestSensorOperation.createOperation(environment, |
| mDeviceSuspendLock, |
| false); |
| final long ALARM_WAKE_UP_DELAY_MS = |
| TimeUnit.MICROSECONDS.toMillis(MAX_REPORT_LATENCY_US) + |
| TimeUnit.SECONDS.toMillis(10); |
| |
| op.addVerification(BatchArrivalVerification.getDefault(environment)); |
| mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, |
| SystemClock.elapsedRealtime() + ALARM_WAKE_UP_DELAY_MS, |
| mPendingIntent); |
| try { |
| Log.i(TAG, "Running .. " + getCurrentTestNode().getName() + " " + sensor.getName()); |
| op.execute(getCurrentTestNode()); |
| } finally { |
| mAlarmManager.cancel(mPendingIntent); |
| } |
| return null; |
| } |
| |
| public String runAPWakeUpWhenFIFOFull(Sensor sensor) throws Throwable { |
| verifyBatchingSupport(sensor); |
| |
| // Try to fill the FIFO at the fastest rate and check if the time is enough to run |
| // the manual test. |
| int samplingPeriodUs = sensor.getMinDelay(); |
| |
| long fifoBasedReportLatencyUs = maxBatchingPeriod(sensor, samplingPeriodUs); |
| |
| final long MIN_LATENCY_US = 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) { |
| int fifoMaxEventCount = sensor.getFifoMaxEventCount(); |
| samplingPeriodUs = (int) MIN_LATENCY_US/fifoMaxEventCount; |
| fifoBasedReportLatencyUs = MIN_LATENCY_US; |
| } |
| |
| final int MAX_REPORT_LATENCY_US = Integer.MAX_VALUE; |
| final long ALARM_WAKE_UP_DELAY_MS = |
| TimeUnit.MICROSECONDS.toMillis(fifoBasedReportLatencyUs) + |
| TimeUnit.SECONDS.toMillis(10); |
| |
| TestSensorEnvironment environment = new TestSensorEnvironment( |
| this, |
| sensor, |
| false, |
| (int) samplingPeriodUs, |
| (int) MAX_REPORT_LATENCY_US, |
| true /*isDeviceSuspendTest*/); |
| |
| TestSensorOperation op = TestSensorOperation.createOperation(environment, |
| mDeviceSuspendLock, |
| true); |
| mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, |
| SystemClock.elapsedRealtime() + ALARM_WAKE_UP_DELAY_MS, |
| mPendingIntent); |
| op.addDefaultVerifications(); |
| try { |
| Log.i(TAG, "Running .. " + getCurrentTestNode().getName() + " " + sensor.getName()); |
| op.execute(getCurrentTestNode()); |
| } finally { |
| mAlarmManager.cancel(mPendingIntent); |
| } |
| return null; |
| } |
| |
| /** |
| * Verify the CLOCK_MONOTONIC and CLOCK_BOOTTIME clock sources are different |
| * by at least 2 seconds. Since delta between these two clock sources represents |
| * time kernel has spent in suspend, device needs to have gone into suspend for |
| * for at least 2 seconds since device was initially booted. |
| */ |
| private void verifyClockDelta() throws Throwable { |
| final int MIN_DELTA_BETWEEN_CLOCKS_MS = 2000; |
| long uptimeMs = SystemClock.uptimeMillis(); |
| long realtimeMs = SystemClock.elapsedRealtime(); |
| long deltaMs = (realtimeMs - uptimeMs); |
| if (deltaMs < MIN_DELTA_BETWEEN_CLOCKS_MS) { |
| throw new Error("Delta between clock sources too small (" |
| + deltaMs + "mS), device must sleep more than " |
| + MIN_DELTA_BETWEEN_CLOCKS_MS/1000 + " seconds"); |
| } |
| Log.i(TAG, "Delta between CLOCK_MONOTONIC and CLOCK_BOOTTIME is " + deltaMs + " mS"); |
| } |
| |
| |
| /** |
| * Verify sensor is using the correct clock source (CLOCK_BOOTTIME) for timestamps. |
| * To tell the clock sources apart, the kernel must have suspended at least once. |
| * |
| * @param sensor - sensor to verify |
| * @param verify_clock_delta |
| * true to verify that clock sources differ before running test |
| * false to skip verification of sufficient delta between clock sources |
| */ |
| public String runVerifySensorTimestampClockbase(Sensor sensor, boolean verify_clock_delta) |
| throws Throwable { |
| Log.i(TAG, "Running .. " + getCurrentTestNode().getName() + " " + sensor.getName()); |
| if (verify_clock_delta) { |
| verifyClockDelta(); |
| } |
| /* Enable a sensor, grab a sample, and then verify timestamp is > realtimeNs |
| * to assure the correct clock source is being used for the sensor timestamp. |
| */ |
| final int MIN_TIMESTAMP_BASE_SAMPLES = 1; |
| int samplingPeriodUs = sensor.getMinDelay(); |
| TestSensorEnvironment environment = new TestSensorEnvironment( |
| this, |
| sensor, |
| false, |
| (int) samplingPeriodUs, |
| 0, |
| false /*isDeviceSuspendTest*/); |
| TestSensorOperation op = TestSensorOperation.createOperation(environment, MIN_TIMESTAMP_BASE_SAMPLES); |
| op.addVerification(TimestampClockSourceVerification.getDefault(environment)); |
| try { |
| op.execute(getCurrentTestNode()); |
| } finally { |
| } |
| return null; |
| } |
| |
| |
| public String runAPWakeUpByAlarmNonWakeSensor(Sensor sensor, int maxReportLatencyUs) |
| throws Throwable { |
| verifyBatchingSupport(sensor); |
| |
| int samplingPeriodUs = sensor.getMaxDelay(); |
| if (samplingPeriodUs == 0 || samplingPeriodUs > 200000) { |
| // If maxDelay is not defined, set the value for 5 Hz. |
| samplingPeriodUs = 200000; |
| } |
| |
| long fifoBasedReportLatencyUs = maxBatchingPeriod(sensor, samplingPeriodUs); |
| verifyBatchingPeriod(fifoBasedReportLatencyUs); |
| |
| TestSensorEnvironment environment = new TestSensorEnvironment( |
| this, |
| sensor, |
| false, |
| (int) samplingPeriodUs, |
| maxReportLatencyUs, |
| true /*isDeviceSuspendTest*/); |
| |
| final long ALARM_WAKE_UP_DELAY_MS = 20000; |
| TestSensorOperation op = TestSensorOperation.createOperation(environment, |
| mDeviceSuspendLock, |
| true); |
| mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, |
| SystemClock.elapsedRealtime() + ALARM_WAKE_UP_DELAY_MS, |
| mPendingIntent); |
| try { |
| Log.i(TAG, "Running .. " + getCurrentTestNode().getName() + " " + sensor.getName()); |
| op.execute(getCurrentTestNode()); |
| } finally { |
| mAlarmManager.cancel(mPendingIntent); |
| } |
| return null; |
| } |
| |
| private void verifyBatchingSupport(Sensor sensor) |
| throws SensorTestStateNotSupportedException { |
| int fifoMaxEventCount = sensor.getFifoMaxEventCount(); |
| if (fifoMaxEventCount == 0) { |
| throw new SensorTestStateNotSupportedException("Batching not supported."); |
| } |
| } |
| |
| private void verifyBatchingPeriod(long periodUs) |
| throws SensorTestStateNotSupportedException { |
| // 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 (periodUs < TimeUnit.SECONDS.toMicros(20)) { |
| throw new SensorTestStateNotSupportedException("FIFO too small to test reliably"); |
| } |
| } |
| |
| private long maxBatchingPeriod (Sensor sensor, long samplePeriod) { |
| long fifoMaxEventCount = sensor.getFifoMaxEventCount(); |
| return fifoMaxEventCount * samplePeriod; |
| } |
| |
| } |