blob: a233250d410db9dca42d508f92bc7bd0b028b4b5 [file] [log] [blame]
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.IntentService;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
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.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.SystemClock;
import androidx.localbroadcastmanager.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");
// Launch a foreground service to ensure that the test remains in the foreground and is
// able to be woken-up when sensor data is delivered.
startForegroundService(new Intent(this, DeviceSuspendTestService.class));
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();
}
stopService(new Intent(this, DeviceSuspendTestService.class));
}
@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 static class DeviceSuspendTestService extends Service {
private static final String NOTIFICATION_CHANNEL_ID =
"com.android.cts.verifier.sensors.DeviceSuspendTestActivity.Notification";
private static final String NOTIFICATION_CHANNEL_NAME = "Device Suspend Test";
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationChannel channel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT);
NotificationManager notificationManager =
getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
Notification notification =
new Notification.Builder(getApplicationContext(), NOTIFICATION_CHANNEL_ID)
.setContentTitle(getString(R.string.snsr_device_suspend_service_active))
.setContentText(getString(
R.string.snsr_device_suspend_service_notification))
.setSmallIcon(R.drawable.icon)
.setAutoCancel(true)
.build();
startForeground(1, notification);
return START_NOT_STICKY;
}
}
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 the device is able to suspend
*/
public void verifyDeviceCanSuspend() throws Throwable {
// 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);
}
/**
* 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.");
}
boolean needToVerifySuspend = true;
for (Sensor sensor : sensorList) {
if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
Log.i(TAG, "testTimestampClockSource skipping non-continuous sensor: '" + sensor.getName());
continue;
}
if (sensor.getType() >= Sensor.TYPE_DEVICE_PRIVATE_BASE) {
Log.i(TAG, "testTimestampClockSource skipping vendor specific sensor: '" + sensor.getName());
continue;
}
if (needToVerifySuspend) {
verifyDeviceCanSuspend();
needToVerifySuspend = false;
}
try {
string = runVerifySensorTimestampClockbase(sensor, false);
if (string != null) {
return string;
}
} catch(Throwable e) {
Log.e(TAG, e.getMessage());
error_occurred = true;
}
}
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;
}
}