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      * Create a {@link TestSensorOperation}.
84      */
TestSensorOperation(TestSensorEnvironment environment, Executor executor)85     public TestSensorOperation(TestSensorEnvironment environment, Executor executor) {
86         this(environment, executor, null /* handler */);
87     }
88 
89     /**
90      * Create a {@link TestSensorOperation}.
91      */
TestSensorOperation( TestSensorEnvironment environment, Executor executor, Handler handler)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      */
addDefaultVerifications()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 
addVerification(ISensorVerification verification)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
execute(ISensorTestNode parent)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
clone()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      */
evaluateResults( List<TestSensorEvent> events, ISensorVerification verification, StringBuilder sb)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      */
trySaveCollectedEvents(ISensorTestNode parent, TestSensorEventListener listener)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      */
createOperation( TestSensorEnvironment environment, final int eventCount)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      */
createOperation( final TestSensorEnvironment environment, final WakeLock wakeLock, final boolean flushBeforeAfterSuspend)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      */
createOperation( TestSensorEnvironment environment, final long duration, final TimeUnit timeUnit)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      */
createFlushOperation( TestSensorEnvironment environment, final long duration, final TimeUnit timeUnit)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      */
createFlushOperation( TestSensorEnvironment environment, final int [] flushIntervalMs, final int clearEventIndex)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