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