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