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