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