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