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