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