1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.cts.verifier.sensors; 18 19 import java.util.Timer; 20 import java.util.TimerTask; 21 import java.util.concurrent.CountDownLatch; 22 import java.util.concurrent.TimeUnit; 23 24 import com.android.cts.verifier.R; 25 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity; 26 import com.android.cts.verifier.sensors.helpers.SensorTestScreenManipulator; 27 28 import android.app.AlarmManager; 29 import android.app.PendingIntent; 30 import android.content.BroadcastReceiver; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.hardware.cts.helpers.SensorNotSupportedException; 35 import android.hardware.cts.helpers.SuspendStateMonitor; 36 import android.hardware.cts.helpers.TestSensorEnvironment; 37 import android.hardware.Sensor; 38 import android.hardware.SensorManager; 39 import android.hardware.TriggerEvent; 40 import android.hardware.TriggerEventListener; 41 import android.os.Handler; 42 import android.os.Looper; 43 import android.os.PowerManager; 44 import android.os.PowerManager.WakeLock; 45 import android.os.SystemClock; 46 import androidx.localbroadcastmanager.content.LocalBroadcastManager; 47 import android.util.Log; 48 49 import junit.framework.Assert; 50 51 /** 52 * Test cases for Significant Motion sensor. 53 * They use walking motion to change the location and trigger Significant Motion. 54 */ 55 public class SignificantMotionTestActivity extends SensorCtsVerifierTestActivity { SignificantMotionTestActivity()56 public SignificantMotionTestActivity() { 57 super(SignificantMotionTestActivity.class); 58 } 59 60 // acceptable time difference between event time and system time 61 private static final long MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS = 62 TimeUnit.MILLISECONDS.toNanos(500); 63 64 // acceptable time difference between event time and AP wake up time. 65 private static final long MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS = 66 TimeUnit.MILLISECONDS.toNanos(2000); 67 68 // time to wait for SMD after the device has gone into suspend. Even after 69 // 45 secs if SMD does not trigger, the test will fail. 70 private static final long ALARM_WAKE_TIME_DELAY_MS = TimeUnit.SECONDS.toMillis(45); 71 72 // time for the test to wait for a trigger 73 private static final int TRIGGER_MAX_DELAY_SECONDS = 30; 74 private static final int VIBRATE_DURATION_MILLIS = 10000; 75 76 private static final int EVENT_VALUES_LENGTH = 1; 77 private static final float EXPECTED_EVENT_VALUE = 1.0f; 78 private static String ACTION_ALARM = "SignificantMotionTestActivity.ACTION_ALARM"; 79 80 private SensorManager mSensorManager; 81 private Sensor mSensorSignificantMotion; 82 private TriggerVerifier mVerifier; 83 private SensorTestScreenManipulator mScreenManipulator; 84 private WakeLock mPartialWakeLock; 85 86 /** 87 * Test cases. 88 */ 89 @SuppressWarnings("unused") testTrigger()90 public String testTrigger() throws Throwable { 91 return runTest( 92 R.string.snsr_significant_motion_test_trigger, 93 true /* isMotionExpected */, 94 false /* cancelEventNotification */, 95 false /* vibrate */); 96 } 97 98 @SuppressWarnings("unused") testNotTriggerAfterCancel()99 public String testNotTriggerAfterCancel() throws Throwable { 100 return runTest( 101 R.string.snsr_significant_motion_test_cancel, 102 false /* isMotionExpected */, 103 true /* cancelEventNotification */, 104 false /* vibrate */); 105 } 106 107 /** 108 * Verifies that Significant Motion is not trigger by the vibrator motion. 109 */ 110 @SuppressWarnings("unused") testVibratorDoesNotTrigger()111 public String testVibratorDoesNotTrigger() throws Throwable { 112 return runTest( 113 R.string.snsr_significant_motion_test_vibration, 114 false /* isMotionExpected */, 115 false /* cancelEventNotification */, 116 true /* vibrate */); 117 } 118 119 /** 120 * Verifies that the natural motion of keeping the device in hand does not change the location. 121 * It ensures that Significant Motion will not trigger in that scenario. 122 */ 123 @SuppressWarnings("unused") testInHandDoesNotTrigger()124 public String testInHandDoesNotTrigger() throws Throwable { 125 return runTest( 126 R.string.snsr_significant_motion_test_in_hand, 127 false /* isMotionExpected */, 128 false /* cancelEventNotification */, 129 false /* vibrate */); 130 } 131 132 @SuppressWarnings("unused") testSittingDoesNotTrigger()133 public String testSittingDoesNotTrigger() throws Throwable { 134 return runTest( 135 R.string.snsr_significant_motion_test_sitting, 136 false /* isMotionExpected */, 137 false /* cancelEventNotification */, 138 false /* vibrate */); 139 } 140 141 @SuppressWarnings("unused") testTriggerDeactivation()142 public String testTriggerDeactivation() throws Throwable { 143 SensorTestLogger logger = getTestLogger(); 144 logger.logInstructions(R.string.snsr_significant_motion_test_deactivation); 145 waitForUserToBegin(); 146 147 TriggerVerifier verifier = new TriggerVerifier(); 148 mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion); 149 logger.logWaitForSound(); 150 151 String result; 152 try { 153 mPartialWakeLock.acquire(); 154 155 // wait for the first event to trigger 156 verifier.verifyEventTriggered(); 157 158 // wait for a second event not to trigger 159 result = verifier.verifyEventNotTriggered(); 160 } finally { 161 playSound(); 162 mScreenManipulator.turnScreenOn(); 163 mPartialWakeLock.release(); 164 } 165 return result; 166 } 167 168 public static class AlarmReceiver extends BroadcastReceiver { 169 @Override onReceive(Context context, Intent intent)170 public void onReceive(Context context, Intent intent) { 171 Intent alarm_intent = new Intent(context, SignificantMotionTestActivity.class); 172 alarm_intent.setAction(SignificantMotionTestActivity.ACTION_ALARM); 173 LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent); 174 } 175 } 176 177 public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() { 178 @Override 179 public void onReceive(Context context, Intent intent) { 180 mVerifier.releaseLatch(); 181 mScreenManipulator.turnScreenOn(); 182 try { 183 playSound(); 184 } catch (InterruptedException e) { 185 // Ignore ... 186 } 187 } 188 }; 189 190 @SuppressWarnings("unused") testAPWakeUpOnSMDTrigger()191 public String testAPWakeUpOnSMDTrigger() throws Throwable { 192 SensorTestLogger logger = getTestLogger(); 193 logger.logInstructions(R.string.snsr_significant_motion_ap_suspend); 194 waitForUserToBegin(); 195 mVerifier = new TriggerVerifier(); 196 mSensorManager.requestTriggerSensor(mVerifier, mSensorSignificantMotion); 197 long testStartTimeNs = SystemClock.elapsedRealtimeNanos(); 198 Handler handler = new Handler(Looper.getMainLooper()); 199 SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor(); 200 201 Intent intent = new Intent(this, AlarmReceiver.class); 202 PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); 203 204 AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); 205 am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 206 SystemClock.elapsedRealtime() + ALARM_WAKE_TIME_DELAY_MS, pendingIntent); 207 try { 208 // Wait for the first event to trigger. Device is expected to go into suspend here. 209 mVerifier.verifyEventTriggered(); 210 long eventTimeStampNs = mVerifier.getTimeStampForTriggerEvent(); 211 long endTimeNs = SystemClock.elapsedRealtimeNanos(); 212 long lastWakeupTimeNs = TimeUnit.MILLISECONDS.toNanos( 213 suspendStateMonitor.getLastWakeUpTime()); 214 Assert.assertTrue(getString(R.string.snsr_device_did_not_go_into_suspend), 215 testStartTimeNs < lastWakeupTimeNs && lastWakeupTimeNs < endTimeNs); 216 long timestampDelta = Math.abs(lastWakeupTimeNs - eventTimeStampNs); 217 Assert.assertTrue( 218 String.format(getString(R.string.snsr_device_did_not_wake_up_at_trigger), 219 TimeUnit.NANOSECONDS.toMillis(lastWakeupTimeNs), 220 TimeUnit.NANOSECONDS.toMillis(eventTimeStampNs)), 221 timestampDelta < MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS); 222 } finally { 223 am.cancel(pendingIntent); 224 suspendStateMonitor.cancel(); 225 mScreenManipulator.turnScreenOn(); 226 playSound(); 227 } 228 return null; 229 } 230 231 /** 232 * @param instructionsResId Instruction to be shown to testers 233 * @param isMotionExpected Should the device detect significant motion event 234 * for this test? 235 * @param cancelEventNotification If TRUE, motion notifications will be 236 * requested first and request will be cancelled 237 * @param vibrate If TRUE, vibration will be concurrent with the test 238 * @throws Throwable 239 */ 240 private String runTest( 241 int instructionsResId, 242 boolean isMotionExpected, 243 boolean cancelEventNotification, 244 boolean vibrate) throws Throwable { 245 SensorTestLogger logger = getTestLogger(); 246 logger.logInstructions(instructionsResId); 247 waitForUserToBegin(); 248 249 if (vibrate) { 250 vibrate(VIBRATE_DURATION_MILLIS); 251 } 252 253 TriggerVerifier verifier = new TriggerVerifier(); 254 boolean success = mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion); 255 Assert.assertTrue( 256 getString(R.string.snsr_significant_motion_registration, success), 257 success); 258 if (cancelEventNotification) { 259 Assert.assertTrue( 260 getString(R.string.snsr_significant_motion_cancelation), 261 mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion)); 262 } 263 logger.logWaitForSound(); 264 265 String result; 266 try { 267 mPartialWakeLock.acquire(); 268 269 if (isMotionExpected) { 270 result = verifier.verifyEventTriggered(); 271 } else { 272 result = verifier.verifyEventNotTriggered(); 273 } 274 } finally { 275 mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion); 276 277 // notify user test finished 278 playSound(); 279 mScreenManipulator.turnScreenOn(); 280 mPartialWakeLock.release(); 281 } 282 return result; 283 } 284 285 @Override 286 protected void activitySetUp() { 287 mSensorManager = (SensorManager) getApplicationContext() 288 .getSystemService(Context.SENSOR_SERVICE); 289 mSensorSignificantMotion = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION); 290 if (mSensorSignificantMotion == null) { 291 throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION); 292 } 293 294 mScreenManipulator = new SensorTestScreenManipulator(this); 295 try { 296 mScreenManipulator.initialize(this); 297 } catch (InterruptedException e) { 298 } 299 PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); 300 mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SignificantMotionTestActivity"); 301 LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver, 302 new IntentFilter(ACTION_ALARM)); 303 } 304 305 @Override 306 protected void activityCleanUp() { 307 if (mScreenManipulator != null) { 308 // after this screen does not have to be on constantly 309 mScreenManipulator.releaseScreenOn(); 310 } 311 if (mPartialWakeLock != null && mPartialWakeLock.isHeld()) { 312 mPartialWakeLock.release(); 313 } 314 LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver); 315 } 316 317 @Override 318 protected void onDestroy() { 319 super.onDestroy(); 320 if (mScreenManipulator != null){ 321 mScreenManipulator.close(); 322 } 323 } 324 325 /** 326 * Helper Trigger listener for testing. 327 * It cannot be reused. 328 */ 329 private class TriggerVerifier extends TriggerEventListener { 330 private volatile CountDownLatch mCountDownLatch; 331 private volatile TriggerEventRegistry mEventRegistry; 332 private volatile long mTimestampForTriggeredEvent = 0; 333 334 // TODO: refactor out if needed 335 private class TriggerEventRegistry { 336 public final TriggerEvent triggerEvent; 337 public final long realtimeTimestampNanos; 338 339 public TriggerEventRegistry(TriggerEvent event, long realtimeTimestampNanos) { 340 this.triggerEvent = event; 341 this.realtimeTimestampNanos = realtimeTimestampNanos; 342 } 343 } 344 345 public void onTrigger(TriggerEvent event) { 346 long elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); 347 mEventRegistry = new TriggerEventRegistry(event, elapsedRealtimeNanos); 348 mCountDownLatch.countDown(); 349 } 350 351 public void releaseLatch() { 352 if (mCountDownLatch != null) { 353 mCountDownLatch.countDown(); 354 } 355 } 356 357 public long getTimeStampForTriggerEvent() { 358 return mTimestampForTriggeredEvent; 359 } 360 361 public String verifyEventTriggered() throws Throwable { 362 TriggerEventRegistry registry = awaitForEvent(); 363 364 // verify an event arrived, and it is indeed a Significant Motion event 365 TriggerEvent event = registry.triggerEvent; 366 String eventArrivalMessage = 367 getString(R.string.snsr_significant_motion_event_arrival, event != null); 368 Assert.assertNotNull(eventArrivalMessage, event); 369 370 int eventType = event.sensor.getType(); 371 String eventTypeMessage = getString( 372 R.string.snsr_significant_motion_event_type, 373 Sensor.TYPE_SIGNIFICANT_MOTION, 374 eventType); 375 Assert.assertEquals(eventTypeMessage, Sensor.TYPE_SIGNIFICANT_MOTION, eventType); 376 377 String sensorName = event.sensor.getName(); 378 int valuesLength = event.values.length; 379 String valuesLengthMessage = getString( 380 R.string.snsr_event_length, 381 EVENT_VALUES_LENGTH, 382 valuesLength, 383 sensorName); 384 Assert.assertEquals(valuesLengthMessage, EVENT_VALUES_LENGTH, valuesLength); 385 386 float value = event.values[0]; 387 String valuesMessage = getString( 388 R.string.snsr_event_value, 389 EXPECTED_EVENT_VALUE, 390 value, 391 sensorName); 392 Assert.assertEquals(valuesMessage, EXPECTED_EVENT_VALUE, value); 393 394 long deltaThreshold = MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS 395 + TestSensorEnvironment.getSensorMaxDetectionLatencyNs(event.sensor); 396 return assertTimestampSynchronization( 397 event.timestamp, 398 registry.realtimeTimestampNanos, 399 deltaThreshold, 400 sensorName); 401 } 402 403 public String verifyEventNotTriggered() throws Throwable { 404 TriggerEventRegistry registry = awaitForEvent(); 405 406 TriggerEvent event = registry.triggerEvent; 407 String eventMessage = 408 getString(R.string.snsr_significant_motion_event_unexpected, event != null); 409 Assert.assertNull(eventMessage, event); 410 return eventMessage; 411 } 412 413 private TriggerEventRegistry awaitForEvent() throws InterruptedException { 414 mCountDownLatch = new CountDownLatch(1); 415 mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS); 416 TriggerEventRegistry registry = mEventRegistry; 417 418 // Save the last timestamp when the event triggered. 419 if (mEventRegistry != null && mEventRegistry.triggerEvent != null) { 420 mTimestampForTriggeredEvent = mEventRegistry.triggerEvent.timestamp; 421 } 422 423 mEventRegistry = null; 424 return registry != null ? registry : new TriggerEventRegistry(null, 0); 425 } 426 } 427 } 428