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