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 android.hardware.cts.helpers.sensoroperations; 18 19 import java.io.IOException; 20 import java.util.HashSet; 21 import java.util.List; 22 import java.util.concurrent.CountDownLatch; 23 import java.util.concurrent.TimeUnit; 24 25 import android.hardware.cts.helpers.SensorCtsHelper; 26 import android.hardware.cts.helpers.SensorStats; 27 import android.hardware.cts.helpers.SensorTestPlatformException; 28 import android.hardware.cts.helpers.TestSensorEnvironment; 29 import android.hardware.cts.helpers.TestSensorEvent; 30 import android.hardware.cts.helpers.TestSensorEventListener; 31 import android.hardware.cts.helpers.TestSensorManager; 32 import android.hardware.cts.helpers.SuspendStateMonitor; 33 import android.hardware.cts.helpers.reporting.ISensorTestNode; 34 import android.hardware.cts.helpers.sensorverification.EventBasicVerification; 35 import android.hardware.cts.helpers.sensorverification.EventGapVerification; 36 import android.hardware.cts.helpers.sensorverification.EventOrderingVerification; 37 import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification; 38 import android.hardware.cts.helpers.sensorverification.FrequencyVerification; 39 import android.hardware.cts.helpers.sensorverification.ISensorVerification; 40 import android.hardware.cts.helpers.sensorverification.JitterVerification; 41 import android.hardware.cts.helpers.sensorverification.MagnitudeVerification; 42 import android.hardware.cts.helpers.sensorverification.MeanVerification; 43 import android.hardware.cts.helpers.sensorverification.InitialValueVerification; 44 import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification; 45 import android.os.Handler; 46 import android.os.SystemClock; 47 import android.os.PowerManager.WakeLock; 48 import android.util.Log; 49 50 import junit.framework.Assert; 51 52 /** 53 * A {@link SensorOperation} used to verify that sensor events and sensor values are correct. 54 * <p> 55 * Provides methods to set test expectations as well as providing a set of default expectations 56 * depending on sensor type. When {{@link #execute(ISensorTestNode)} is called, the sensor will 57 * collect the events and then run all the tests. 58 * </p> 59 */ 60 public class TestSensorOperation extends SensorOperation { 61 private static final String TAG = "TestSensorOperation"; 62 63 private final HashSet<ISensorVerification> mVerifications = new HashSet<>(); 64 65 private final TestSensorManager mSensorManager; 66 private final TestSensorEnvironment mEnvironment; 67 private final Executor mExecutor; 68 private final Handler mHandler; 69 private long mDeviceWakeUpTimeMs = -1; 70 private long mStartTimeMs = -1; 71 private long mStopTimeMs = -1; 72 73 /** 74 * An interface that defines an abstraction for operations to be performed by the 75 * {@link TestSensorOperation}. 76 */ 77 public interface Executor { execute(TestSensorManager sensorManager, TestSensorEventListener listener)78 void execute(TestSensorManager sensorManager, TestSensorEventListener listener) 79 throws Exception; 80 } 81 82 /** 83 * An interface that defines an abstraction for a method that allows {@link TestSensorOperation} 84 * to wait for user interaction before continuing. 85 */ 86 public interface WaitForUserOperation { waitForUser()87 void waitForUser() throws InterruptedException; 88 } 89 90 /** 91 * Create a {@link TestSensorOperation}. 92 */ TestSensorOperation(TestSensorEnvironment environment, Executor executor)93 public TestSensorOperation(TestSensorEnvironment environment, Executor executor) { 94 this(environment, executor, null /* handler */); 95 } 96 97 /** 98 * Create a {@link TestSensorOperation}. 99 */ TestSensorOperation( TestSensorEnvironment environment, Executor executor, Handler handler)100 public TestSensorOperation( 101 TestSensorEnvironment environment, 102 Executor executor, 103 Handler handler) { 104 mEnvironment = environment; 105 mExecutor = executor; 106 mHandler = handler; 107 mSensorManager = new TestSensorManager(mEnvironment); 108 } 109 110 /** 111 * Set all of the default test expectations. 112 */ addDefaultVerifications()113 public void addDefaultVerifications() { 114 addVerification(EventGapVerification.getDefault(mEnvironment)); 115 addVerification(EventOrderingVerification.getDefault(mEnvironment)); 116 addVerification(FrequencyVerification.getDefault(mEnvironment)); 117 addVerification(JitterVerification.getDefault(mEnvironment)); 118 addVerification(MagnitudeVerification.getDefault(mEnvironment)); 119 addVerification(MeanVerification.getDefault(mEnvironment)); 120 addVerification(StandardDeviationVerification.getDefault(mEnvironment)); 121 addVerification(EventTimestampSynchronizationVerification.getDefault(mEnvironment)); 122 addVerification(InitialValueVerification.getDefault(mEnvironment)); 123 } 124 addVerification(ISensorVerification verification)125 public void addVerification(ISensorVerification verification) { 126 if (verification != null) { 127 mVerifications.add(verification); 128 } 129 } 130 131 /** 132 * Collect the specified number of events from the sensor and run all enabled verifications. 133 */ 134 @Override execute(ISensorTestNode parent)135 public void execute(ISensorTestNode parent) throws Exception { 136 getStats().addValue("sensor_name", mEnvironment.getSensor().getName()); 137 TestSensorEventListener listener = new TestSensorEventListener(mEnvironment, mHandler); 138 139 mStartTimeMs = SystemClock.elapsedRealtime(); 140 if (mEnvironment.isDeviceSuspendTest()) { 141 SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor(); 142 // Device should go into suspend here. 143 mExecutor.execute(mSensorManager, listener); 144 mStopTimeMs = SystemClock.elapsedRealtime(); 145 // Check if the device has gone into suspend during test execution. 146 mDeviceWakeUpTimeMs = suspendStateMonitor.getLastWakeUpTime(); 147 suspendStateMonitor.cancel(); 148 Assert.assertTrue("Device did not go into suspend during test execution", 149 mStartTimeMs < mDeviceWakeUpTimeMs && 150 mDeviceWakeUpTimeMs < mStopTimeMs); 151 } else { 152 mExecutor.execute(mSensorManager, listener); 153 mStopTimeMs = SystemClock.elapsedRealtime(); 154 } 155 156 boolean failed = false; 157 StringBuilder sb = new StringBuilder(); 158 List<TestSensorEvent> collectedEvents = listener.getCollectedEvents(); 159 for (ISensorVerification verification : mVerifications) { 160 failed |= evaluateResults(collectedEvents, verification, sb); 161 } 162 163 trySaveCollectedEvents(parent, listener); 164 if (failed) { 165 String msg = SensorCtsHelper 166 .formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString()); 167 getStats().addValue(SensorStats.ERROR, msg); 168 Assert.fail(msg); 169 } 170 } 171 172 /** 173 * {@inheritDoc} 174 */ 175 @Override clone()176 public TestSensorOperation clone() { 177 TestSensorOperation operation = new TestSensorOperation(mEnvironment, mExecutor); 178 for (ISensorVerification verification : mVerifications) { 179 operation.addVerification(verification.clone()); 180 } 181 return operation; 182 } 183 184 /** 185 * Evaluate the results of a test, aggregate the stats, and build the error message. 186 */ evaluateResults( List<TestSensorEvent> events, ISensorVerification verification, StringBuilder sb)187 private boolean evaluateResults( 188 List<TestSensorEvent> events, 189 ISensorVerification verification, 190 StringBuilder sb) { 191 try { 192 // this is an intermediate state in refactoring, at some point verifications might 193 // become stateless 194 verification.addSensorEvents(events); 195 verification.verify(mEnvironment, getStats()); 196 } catch (AssertionError e) { 197 if (sb.length() > 0) { 198 sb.append(", "); 199 } 200 sb.append(e.getMessage()); 201 return true; 202 } 203 return false; 204 } 205 206 /** 207 * Tries to save collected {@link TestSensorEvent}s to a file. 208 * 209 * NOTE: it is more important to handle verifications and its results, than failing if the file 210 * cannot be created. So we silently fail if necessary. 211 */ trySaveCollectedEvents(ISensorTestNode parent, TestSensorEventListener listener)212 private void trySaveCollectedEvents(ISensorTestNode parent, TestSensorEventListener listener) { 213 String sanitizedFileName; 214 try { 215 String fileName = asTestNode(parent).getName(); 216 sanitizedFileName = String.format( 217 "%s-%s-%s_%dus.txt", 218 SensorCtsHelper.sanitizeStringForFileName(fileName), 219 SensorStats.getSanitizedSensorName(mEnvironment.getSensor()), 220 mEnvironment.getFrequencyString(), 221 mEnvironment.getMaxReportLatencyUs()); 222 getStats().addValue(SensorStats.EVENT_LOG_FILENAME, sanitizedFileName); 223 } catch (SensorTestPlatformException e) { 224 Log.w(TAG, "Unable to generate file name to save collected events", e); 225 return; 226 } 227 228 try { 229 listener.logCollectedEventsToFile(sanitizedFileName, mDeviceWakeUpTimeMs, 230 mStartTimeMs, mStopTimeMs); 231 } catch (IOException e) { 232 Log.w(TAG, "Unable to save collected events to file: " + sanitizedFileName, e); 233 } 234 } 235 236 /** 237 * Creates an operation that will wait for a given amount of events to arrive. 238 * 239 * @param environment The test environment. 240 * @param eventCount The number of events to wait for. 241 */ createOperation( TestSensorEnvironment environment, final int eventCount)242 public static TestSensorOperation createOperation( 243 TestSensorEnvironment environment, 244 final int eventCount) { 245 Executor executor = new Executor() { 246 @Override 247 public void execute(TestSensorManager sensorManager, TestSensorEventListener listener) 248 throws InterruptedException { 249 try { 250 CountDownLatch latch = sensorManager.registerListener(listener, eventCount); 251 listener.waitForEvents(latch, eventCount, true); 252 } finally { 253 sensorManager.unregisterListener(); 254 } 255 } 256 }; 257 return new TestSensorOperation(environment, executor); 258 } 259 260 /** 261 * Creates an operation that will wait for a given amount of events to arrive. 262 * 263 * After the execution of this type of test operation, the wakelock passed in will be acquired. 264 * Make sure it is released at clean up. 265 * 266 * @param environment The test environment. 267 * @param eventCount The number of events to wait for. 268 */ createOperation( final TestSensorEnvironment environment, final WakeLock wakeLock, final boolean flushBeforeAfterSuspend)269 public static TestSensorOperation createOperation( 270 final TestSensorEnvironment environment, 271 final WakeLock wakeLock, 272 final boolean flushBeforeAfterSuspend) { 273 Executor executor = new Executor() { 274 @Override 275 public void execute(TestSensorManager sensorManager, TestSensorEventListener listener) 276 throws InterruptedException { 277 try { 278 sensorManager.registerListener(listener); 279 if (flushBeforeAfterSuspend) { 280 int initialNumEvents1 = listener.getCollectedEvents().size(); 281 SensorCtsHelper.sleep(2, TimeUnit.SECONDS); 282 CountDownLatch flushLatch1 = sensorManager.requestFlush(); 283 listener.waitForFlushComplete(flushLatch1, false); 284 Assert.assertTrue("1.No sensor events collected on calling flush " + 285 environment.toString(), 286 listener.getCollectedEvents().size() - initialNumEvents1 > 0); 287 } 288 // acknowledge waitForFlushComplete 289 listener.releaseWakeLock(); 290 291 Log.i(TAG, "Collected sensor events size1=" + 292 listener.getCollectedEvents().size()); 293 int initialNumEvents2 = listener.getCollectedEvents().size(); 294 295 // allow device to go to sleep 296 if (wakeLock.isHeld()) { 297 wakeLock.release(); 298 } 299 300 SuspendStateMonitor suspendMonitor = new SuspendStateMonitor(); 301 long approxStartTimeMs = SystemClock.elapsedRealtime(); 302 // Allow the device to go into suspend. Wait for wake-up. 303 suspendMonitor.waitForWakeUp(15); 304 suspendMonitor.cancel(); 305 306 // keep device awake for processing 307 if (!wakeLock.isHeld()) { 308 wakeLock.acquire(); 309 } 310 311 CountDownLatch flushLatch2 = sensorManager.requestFlush(); 312 listener.waitForFlushComplete(flushLatch2, false); 313 314 Log.i(TAG, "Collected sensor events size2=" + 315 listener.getCollectedEvents().size()); 316 317 if (listener.getCollectedEvents().size() - initialNumEvents2 <= 0 && 318 suspendMonitor.getLastWakeUpTime() > 0) { 319 // Fail 320 String str = String.format("No Sensor events collected by calling flush " + 321 "after device wake up. Approx time after which device went into " + 322 "suspend %dms ,approx AP wake-up time %dms %s", 323 approxStartTimeMs, suspendMonitor.getLastWakeUpTime(), 324 environment.toString()); 325 Assert.fail(str); 326 } 327 if (flushBeforeAfterSuspend) { 328 int initialNumEvents3 = listener.getCollectedEvents().size(); 329 SensorCtsHelper.sleep(2, TimeUnit.SECONDS); 330 CountDownLatch flushLatch3 = sensorManager.requestFlush(); 331 listener.waitForFlushComplete(flushLatch3, false); 332 Assert.assertTrue("3.No sensor events collected on calling flush " + 333 environment.toString(), 334 listener.getCollectedEvents().size() - initialNumEvents3 > 0); 335 } 336 Log.i(TAG, "Collected sensor events size3=" + 337 listener.getCollectedEvents().size()); 338 } finally { 339 // make sure the device can run until the test activity take over. 340 if(!wakeLock.isHeld()) { 341 wakeLock.acquire(); 342 } 343 sensorManager.unregisterListener(); 344 } 345 } 346 }; 347 return new TestSensorOperation(environment, executor); 348 } 349 350 /** 351 * Creates an operation that will wait for a given amount of time to collect events. 352 * 353 * @param environment The test environment. 354 * @param duration The duration to wait for events. 355 * @param timeUnit The time unit for {@code duration}. 356 */ createOperation( TestSensorEnvironment environment, final long duration, final TimeUnit timeUnit)357 public static TestSensorOperation createOperation( 358 TestSensorEnvironment environment, 359 final long duration, 360 final TimeUnit timeUnit) { 361 Executor executor = new Executor() { 362 @Override 363 public void execute(TestSensorManager sensorManager, TestSensorEventListener listener) 364 throws InterruptedException { 365 try { 366 sensorManager.registerListener(listener); 367 listener.waitForEvents(duration, timeUnit); 368 } finally { 369 sensorManager.unregisterListener(); 370 } 371 } 372 }; 373 return new TestSensorOperation(environment, executor); 374 } 375 376 /** 377 * Creates an operation that will wait for user interaction to stop collecting events. 378 * 379 * @param environment The test environment. 380 * @param operation Method that allows waiting for user interaction before continuing execution. 381 */ createOperation( TestSensorEnvironment environment, WaitForUserOperation operation)382 public static TestSensorOperation createOperation( 383 TestSensorEnvironment environment, 384 WaitForUserOperation operation) { 385 Executor executor = new Executor() { 386 @Override 387 public void execute(TestSensorManager sensorManager, TestSensorEventListener listener) 388 throws InterruptedException { 389 try { 390 sensorManager.registerListener(listener); 391 operation.waitForUser(); 392 } finally { 393 sensorManager.unregisterListener(); 394 } 395 } 396 }; 397 return new TestSensorOperation(environment, executor); 398 } 399 400 /** 401 * Creates an operation that will wait for a given amount of time before calling 402 * {@link TestSensorManager#requestFlush()}. 403 * 404 * @param environment The test environment. 405 * @param duration The duration to wait before calling {@link TestSensorManager#requestFlush()}. 406 * @param timeUnit The time unit for {@code duration}. 407 */ createFlushOperation( TestSensorEnvironment environment, final long duration, final TimeUnit timeUnit)408 public static TestSensorOperation createFlushOperation( 409 TestSensorEnvironment environment, 410 final long duration, 411 final TimeUnit timeUnit) { 412 413 return createFlushOperation(environment, new int[] {(int)timeUnit.toMillis(duration)}, -1); 414 } 415 416 /** 417 * Creates an operation that make a series of flush (by calling 418 * {@link TestSensorManager#requestFlush()}) with predefined interval after registerListener. 419 * 420 * @param environment The test environment. 421 * @param flushIntervalMs intervals between calls to {@link TestSensorManager#requestFlush()}. 422 * @param clearEventIndex the index of interval which 423 * {@link TestSensorEventListerner#clearEvent} is called (-1 for never). 424 */ createFlushOperation( TestSensorEnvironment environment, final int [] flushIntervalMs, final int clearEventIndex)425 public static TestSensorOperation createFlushOperation( 426 TestSensorEnvironment environment, 427 final int [] flushIntervalMs, 428 final int clearEventIndex) { 429 430 Assert.assertTrue(clearEventIndex >= -1 && flushIntervalMs.length > clearEventIndex); 431 432 Executor executor = new Executor() { 433 @Override 434 public void execute(TestSensorManager sensorManager, TestSensorEventListener listener) 435 throws InterruptedException { 436 try { 437 sensorManager.registerListener(listener); 438 439 int i = 0; 440 for (int interval: flushIntervalMs) { 441 SensorCtsHelper.sleep(interval, TimeUnit.MILLISECONDS); 442 listener.waitForFlushComplete( 443 sensorManager.requestFlush(), 444 i <= clearEventIndex); 445 ++i; 446 } 447 } finally { 448 sensorManager.unregisterListener(); 449 } 450 } 451 }; 452 return new TestSensorOperation(environment, executor); 453 } 454 } 455