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