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