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, true); 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 144 setFirstExecutionInstruction(R.string.snsr_significant_motion_test_deactivation); 145 146 TriggerVerifier verifier = new TriggerVerifier(); 147 mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion); 148 getTestLogger().logWaitForSound(); 149 150 String result; 151 try { 152 mPartialWakeLock.acquire(); 153 154 // wait for the first event to trigger 155 verifier.verifyEventTriggered(); 156 157 // wait for a second event not to trigger 158 result = verifier.verifyEventNotTriggered(); 159 } finally { 160 playSound(); 161 mScreenManipulator.turnScreenOn(); 162 mPartialWakeLock.release(); 163 } 164 return result; 165 } 166 167 public static class AlarmReceiver extends BroadcastReceiver { 168 @Override onReceive(Context context, Intent intent)169 public void onReceive(Context context, Intent intent) { 170 Intent alarm_intent = new Intent(context, SignificantMotionTestActivity.class); 171 alarm_intent.setAction(SignificantMotionTestActivity.ACTION_ALARM); 172 LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent); 173 } 174 } 175 176 public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() { 177 @Override 178 public void onReceive(Context context, Intent intent) { 179 mVerifier.releaseLatch(); 180 mScreenManipulator.turnScreenOn(); 181 try { 182 playSound(); 183 } catch (InterruptedException e) { 184 // Ignore ... 185 } 186 } 187 }; 188 189 @SuppressWarnings("unused") testAPWakeUpOnSMDTrigger()190 public String testAPWakeUpOnSMDTrigger() throws Throwable { 191 192 setFirstExecutionInstruction(R.string.snsr_significant_motion_ap_suspend); 193 194 mVerifier = new TriggerVerifier(); 195 mSensorManager.requestTriggerSensor(mVerifier, mSensorSignificantMotion); 196 long testStartTimeNs = SystemClock.elapsedRealtimeNanos(); 197 Handler handler = new Handler(Looper.getMainLooper()); 198 SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor(); 199 200 Intent intent = new Intent(this, AlarmReceiver.class); 201 PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); 202 203 AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE); 204 am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 205 SystemClock.elapsedRealtime() + ALARM_WAKE_TIME_DELAY_MS, pendingIntent); 206 try { 207 // Wait for the first event to trigger. Device is expected to go into suspend here. 208 mVerifier.verifyEventTriggered(); 209 long eventTimeStampNs = mVerifier.getTimeStampForTriggerEvent(); 210 long endTimeNs = SystemClock.elapsedRealtimeNanos(); 211 long lastWakeupTimeNs = TimeUnit.MILLISECONDS.toNanos( 212 suspendStateMonitor.getLastWakeUpTime()); 213 Assert.assertTrue(getString(R.string.snsr_device_did_not_go_into_suspend), 214 testStartTimeNs < lastWakeupTimeNs && lastWakeupTimeNs < endTimeNs); 215 long timestampDelta = Math.abs(lastWakeupTimeNs - eventTimeStampNs); 216 Assert.assertTrue( 217 String.format(getString(R.string.snsr_device_did_not_wake_up_at_trigger), 218 TimeUnit.NANOSECONDS.toMillis(lastWakeupTimeNs), 219 TimeUnit.NANOSECONDS.toMillis(eventTimeStampNs)), 220 timestampDelta < MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS); 221 } finally { 222 am.cancel(pendingIntent); 223 suspendStateMonitor.cancel(); 224 mScreenManipulator.turnScreenOn(); 225 playSound(); 226 } 227 return null; 228 } 229 230 /** 231 * @param instructionsResId Instruction to be shown to testers 232 * @param isMotionExpected Should the device detect significant motion event 233 * for this test? 234 * @param cancelEventNotification If TRUE, motion notifications will be 235 * requested first and request will be cancelled 236 * @param vibrate If TRUE, vibration will be concurrent with the test 237 * @throws Throwable 238 */ 239 private String runTest( 240 int instructionsResId, 241 boolean isMotionExpected, 242 boolean cancelEventNotification, 243 boolean vibrate) throws Throwable { 244 245 setFirstExecutionInstruction(instructionsResId); 246 247 if (vibrate) { 248 vibrate(VIBRATE_DURATION_MILLIS); 249 } 250 251 TriggerVerifier verifier = new TriggerVerifier(); 252 boolean success = mSensorManager.requestTriggerSensor(verifier, mSensorSignificantMotion); 253 Assert.assertTrue( 254 getString(R.string.snsr_significant_motion_registration, success), 255 success); 256 if (cancelEventNotification) { 257 Assert.assertTrue( 258 getString(R.string.snsr_significant_motion_cancelation), 259 mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion)); 260 } 261 getTestLogger().logWaitForSound(); 262 263 String result; 264 try { 265 mPartialWakeLock.acquire(); 266 267 if (isMotionExpected) { 268 result = verifier.verifyEventTriggered(); 269 } else { 270 result = verifier.verifyEventNotTriggered(); 271 } 272 } finally { 273 mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion); 274 275 // notify user test finished 276 playSound(); 277 mScreenManipulator.turnScreenOn(); 278 mPartialWakeLock.release(); 279 } 280 return result; 281 } 282 283 @Override 284 protected void activitySetUp() { 285 mSensorManager = (SensorManager) getApplicationContext() 286 .getSystemService(Context.SENSOR_SERVICE); 287 mSensorSignificantMotion = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION); 288 if (mSensorSignificantMotion == null) { 289 throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION); 290 } 291 292 mScreenManipulator = new SensorTestScreenManipulator(this); 293 try { 294 mScreenManipulator.initialize(this); 295 } catch (InterruptedException e) { 296 } 297 PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); 298 mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SignificantMotionTestActivity"); 299 LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver, 300 new IntentFilter(ACTION_ALARM)); 301 } 302 303 @Override 304 protected void activityCleanUp() { 305 if (mScreenManipulator != null) { 306 // after this screen does not have to be on constantly 307 mScreenManipulator.releaseScreenOn(); 308 } 309 if (mPartialWakeLock != null && mPartialWakeLock.isHeld()) { 310 mPartialWakeLock.release(); 311 } 312 LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver); 313 } 314 315 @Override 316 protected void onDestroy() { 317 super.onDestroy(); 318 if (mScreenManipulator != null){ 319 mScreenManipulator.close(); 320 } 321 } 322 323 /** 324 * Helper Trigger listener for testing. 325 * It cannot be reused. 326 */ 327 private class TriggerVerifier extends TriggerEventListener { 328 private volatile CountDownLatch mCountDownLatch; 329 private volatile TriggerEventRegistry mEventRegistry; 330 private volatile long mTimestampForTriggeredEvent = 0; 331 332 // TODO: refactor out if needed 333 private class TriggerEventRegistry { 334 public final TriggerEvent triggerEvent; 335 public final long realtimeTimestampNanos; 336 337 public TriggerEventRegistry(TriggerEvent event, long realtimeTimestampNanos) { 338 this.triggerEvent = event; 339 this.realtimeTimestampNanos = realtimeTimestampNanos; 340 } 341 } 342 343 public void onTrigger(TriggerEvent event) { 344 long elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); 345 mEventRegistry = new TriggerEventRegistry(event, elapsedRealtimeNanos); 346 mCountDownLatch.countDown(); 347 } 348 349 public void releaseLatch() { 350 if (mCountDownLatch != null) { 351 mCountDownLatch.countDown(); 352 } 353 } 354 355 public long getTimeStampForTriggerEvent() { 356 return mTimestampForTriggeredEvent; 357 } 358 359 public String verifyEventTriggered() throws Throwable { 360 TriggerEventRegistry registry = awaitForEvent(); 361 362 // verify an event arrived, and it is indeed a Significant Motion event 363 TriggerEvent event = registry.triggerEvent; 364 String eventArrivalMessage = 365 getString(R.string.snsr_significant_motion_event_arrival, event != null); 366 Assert.assertNotNull(eventArrivalMessage, event); 367 368 int eventType = event.sensor.getType(); 369 String eventTypeMessage = getString( 370 R.string.snsr_significant_motion_event_type, 371 Sensor.TYPE_SIGNIFICANT_MOTION, 372 eventType); 373 Assert.assertEquals(eventTypeMessage, Sensor.TYPE_SIGNIFICANT_MOTION, eventType); 374 375 String sensorName = event.sensor.getName(); 376 int valuesLength = event.values.length; 377 String valuesLengthMessage = getString( 378 R.string.snsr_event_length, 379 EVENT_VALUES_LENGTH, 380 valuesLength, 381 sensorName); 382 Assert.assertEquals(valuesLengthMessage, EVENT_VALUES_LENGTH, valuesLength); 383 384 float value = event.values[0]; 385 String valuesMessage = getString( 386 R.string.snsr_event_value, 387 EXPECTED_EVENT_VALUE, 388 value, 389 sensorName); 390 Assert.assertEquals(valuesMessage, EXPECTED_EVENT_VALUE, value); 391 392 long deltaThreshold = MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS 393 + TestSensorEnvironment.getSensorMaxDetectionLatencyNs(event.sensor); 394 return assertTimestampSynchronization( 395 event.timestamp, 396 registry.realtimeTimestampNanos, 397 deltaThreshold, 398 sensorName); 399 } 400 401 public String verifyEventNotTriggered() throws Throwable { 402 TriggerEventRegistry registry = awaitForEvent(); 403 404 TriggerEvent event = registry.triggerEvent; 405 String eventMessage = 406 getString(R.string.snsr_significant_motion_event_unexpected, event != null); 407 Assert.assertNull(eventMessage, event); 408 return eventMessage; 409 } 410 411 private TriggerEventRegistry awaitForEvent() throws InterruptedException { 412 mCountDownLatch = new CountDownLatch(1); 413 mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS); 414 TriggerEventRegistry registry = mEventRegistry; 415 416 // Save the last timestamp when the event triggered. 417 if (mEventRegistry != null && mEventRegistry.triggerEvent != null) { 418 mTimestampForTriggeredEvent = mEventRegistry.triggerEvent.timestamp; 419 } 420 421 mEventRegistry = null; 422 return registry != null ? registry : new TriggerEventRegistry(null, 0); 423 } 424 } 425 } 426