Home | History | Annotate | Download | only in helpers
      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;
     18 
     19 import junit.framework.Assert;
     20 
     21 import android.hardware.Sensor;
     22 import android.hardware.SensorEvent;
     23 import android.hardware.SensorEventListener2;
     24 import android.os.Handler;
     25 import android.os.Looper;
     26 import android.os.SystemClock;
     27 
     28 import java.io.BufferedWriter;
     29 import java.io.File;
     30 import java.io.FileWriter;
     31 import java.io.IOException;
     32 import java.util.ArrayList;
     33 import java.util.Arrays;
     34 import java.util.Collections;
     35 import java.util.List;
     36 import java.util.concurrent.CountDownLatch;
     37 import java.util.concurrent.TimeUnit;
     38 import java.util.concurrent.atomic.AtomicInteger;
     39 
     40 /**
     41  * A {@link SensorEventListener2} which performs operations such as waiting for a specific number of
     42  * events or for a specific time, or waiting for a flush to complete. This class performs
     43  * verifications and will throw {@link AssertionError}s if there are any errors. It may also wrap
     44  * another {@link SensorEventListener2}.
     45  */
     46 public class TestSensorEventListener implements SensorEventListener2 {
     47     public static final String LOG_TAG = "TestSensorEventListener";
     48 
     49     private static final long EVENT_TIMEOUT_US = TimeUnit.SECONDS.toMicros(5);
     50     private static final long FLUSH_TIMEOUT_US = TimeUnit.SECONDS.toMicros(10);
     51 
     52     private final List<TestSensorEvent> mCollectedEvents = new ArrayList<>();
     53     private final List<CountDownLatch> mEventLatches = new ArrayList<>();
     54     private final List<CountDownLatch> mFlushLatches = new ArrayList<>();
     55     private final AtomicInteger mEventsReceivedOutsideHandler = new AtomicInteger();
     56 
     57     private final Handler mHandler;
     58     private final TestSensorEnvironment mEnvironment;
     59 
     60     /**
     61      * @deprecated Use {@link TestSensorEventListener(TestSensorEnvironment)}.
     62      */
     63     @Deprecated
     64     public TestSensorEventListener() {
     65         this(null /* environment */);
     66     }
     67 
     68     /**
     69      * Construct a {@link TestSensorEventListener}.
     70      */
     71     public TestSensorEventListener(TestSensorEnvironment environment) {
     72         this(environment, null /* handler */);
     73     }
     74 
     75     /**
     76      * Construct a {@link TestSensorEventListener}.
     77      */
     78     public TestSensorEventListener(TestSensorEnvironment environment, Handler handler) {
     79         mEnvironment = environment;
     80         mHandler = handler;
     81     }
     82 
     83     /**
     84      * {@inheritDoc}
     85      */
     86     @Override
     87     public void onSensorChanged(SensorEvent event) {
     88         long timestampNs = SystemClock.elapsedRealtimeNanos();
     89         checkHandler();
     90         synchronized (mCollectedEvents) {
     91             mCollectedEvents.add(new TestSensorEvent(event, timestampNs));
     92         }
     93         synchronized (mEventLatches) {
     94             for (CountDownLatch latch : mEventLatches) {
     95                 latch.countDown();
     96             }
     97         }
     98     }
     99 
    100     /**
    101      * {@inheritDoc}
    102      */
    103     @Override
    104     public void onAccuracyChanged(Sensor sensor, int accuracy) {
    105         checkHandler();
    106     }
    107 
    108     /**
    109      * @param eventCount
    110      * @return A CountDownLatch initialzed with eventCount and decremented as sensor events arrive
    111      * for this listerner.
    112      */
    113     public CountDownLatch getLatchForSensorEvents(int eventCount) {
    114         CountDownLatch latch = new CountDownLatch(eventCount);
    115         synchronized (mEventLatches) {
    116             mEventLatches.add(latch);
    117         }
    118         return latch;
    119     }
    120 
    121     /**
    122      * @return A CountDownLatch initialzed with 1 and decremented as a flush complete arrives
    123      * for this listerner.
    124      */
    125     public CountDownLatch getLatchForFlushCompleteEvent() {
    126         CountDownLatch latch = new CountDownLatch(1);
    127         synchronized (mFlushLatches) {
    128             mFlushLatches.add(latch);
    129         }
    130         return latch;
    131     }
    132 
    133     /**
    134      * {@inheritDoc}
    135      */
    136     @Override
    137     public void onFlushCompleted(Sensor sensor) {
    138         checkHandler();
    139         synchronized (mFlushLatches) {
    140             for (CountDownLatch latch : mFlushLatches) {
    141                 latch.countDown();
    142             }
    143         }
    144     }
    145 
    146     /**
    147      * @return The handler (if any) associated with the instance.
    148      */
    149     public Handler getHandler() {
    150         return mHandler;
    151     }
    152 
    153     /**
    154      * @return A list of {@link TestSensorEvent}s collected by the listener.
    155      */
    156     public List<TestSensorEvent> getCollectedEvents() {
    157         synchronized (mCollectedEvents){
    158             return Collections.unmodifiableList(mCollectedEvents);
    159         }
    160     }
    161 
    162     /**
    163      * Clears the internal list of collected {@link TestSensorEvent}s.
    164      */
    165     public void clearEvents() {
    166         synchronized (mCollectedEvents) {
    167             mCollectedEvents.clear();
    168         }
    169     }
    170 
    171 
    172     /**
    173      * Utility method to log the collected events to a file.
    174      * It will overwrite the file if it already exists, the file is created in a relative directory
    175      * named 'events' under the sensor test directory (part of external storage).
    176      */
    177     public void logCollectedEventsToFile(String fileName) throws IOException {
    178         StringBuilder builder = new StringBuilder();
    179         builder.append("Sensor='").append(mEnvironment.getSensor()).append("', ");
    180         builder.append("SamplingRateOverloaded=")
    181                 .append(mEnvironment.isSensorSamplingRateOverloaded()).append(", ");
    182         builder.append("RequestedSamplingPeriod=")
    183                 .append(mEnvironment.getRequestedSamplingPeriodUs()).append("us, ");
    184         builder.append("MaxReportLatency=")
    185                 .append(mEnvironment.getMaxReportLatencyUs()).append("us");
    186 
    187         synchronized (mCollectedEvents) {
    188             for (TestSensorEvent event : mCollectedEvents) {
    189                 builder.append("\n");
    190                 builder.append("Timestamp=").append(event.timestamp).append("ns, ");
    191                 builder.append("ReceivedTimestamp=").append(event.receivedTimestamp).append("ns, ");
    192                 builder.append("Accuracy=").append(event.accuracy).append(", ");
    193                 builder.append("Values=").append(Arrays.toString(event.values));
    194             }
    195         }
    196 
    197         File eventsDirectory = SensorCtsHelper.getSensorTestDataDirectory("events/");
    198         File logFile = new File(eventsDirectory, fileName);
    199         FileWriter fileWriter = new FileWriter(logFile, false /* append */);
    200         try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
    201             writer.write(builder.toString());
    202         }
    203     }
    204 
    205     /**
    206      * Wait for {@link #onFlushCompleted(Sensor)} to be called.
    207      *
    208      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
    209      */
    210     public void waitForFlushComplete(CountDownLatch latch) throws InterruptedException {
    211         clearEvents();
    212         try {
    213             String message = SensorCtsHelper.formatAssertionMessage(
    214                     "WaitForFlush",
    215                     mEnvironment,
    216                     "timeout=%dus",
    217                     FLUSH_TIMEOUT_US);
    218             Assert.assertTrue(message, latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
    219         } finally {
    220             synchronized (mFlushLatches) {
    221                 mFlushLatches.remove(latch);
    222             }
    223         }
    224     }
    225 
    226     /**
    227      * Collect a specific number of {@link TestSensorEvent}s.
    228      *
    229      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
    230      */
    231     public void waitForEvents(CountDownLatch latch, int eventCount) throws InterruptedException {
    232         clearEvents();
    233         try {
    234             long samplingPeriodUs = mEnvironment.getMaximumExpectedSamplingPeriodUs();
    235             // timeout is 2 * event count * expected period + batch timeout + default wait
    236             // we multiply by two as not to raise an error in this function even if the events are
    237             // streaming at a lower rate than expected, as long as it's not streaming twice as slow
    238             // as expected
    239             long timeoutUs = (2 * eventCount * samplingPeriodUs)
    240                     + mEnvironment.getMaxReportLatencyUs()
    241                     + EVENT_TIMEOUT_US;
    242             boolean success = latch.await(timeoutUs, TimeUnit.MICROSECONDS);
    243             if (!success) {
    244                 String message = SensorCtsHelper.formatAssertionMessage(
    245                         "WaitForEvents",
    246                         mEnvironment,
    247                         "requested=%d, received=%d, timeout=%dus",
    248                         eventCount,
    249                         eventCount - latch.getCount(),
    250                         timeoutUs);
    251                 Assert.fail(message);
    252             }
    253         } finally {
    254             synchronized (mEventLatches) {
    255                 mEventLatches.remove(latch);
    256             }
    257         }
    258     }
    259 
    260     /**
    261      * Collect {@link TestSensorEvent} for a specific duration.
    262      */
    263     public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
    264         SensorCtsHelper.sleep(duration, timeUnit);
    265     }
    266 
    267     /**
    268      * Asserts that sensor events arrived in the proper thread if a {@link Handler} was associated
    269      * with the current instance.
    270      *
    271      * If no events were received this assertion will be evaluated to {@code true}.
    272      */
    273     public void assertEventsReceivedInHandler() {
    274         int eventsOutsideHandler = mEventsReceivedOutsideHandler.get();
    275         String message = String.format(
    276                 "Events arrived outside the associated Looper. Expected=0, Found=%d",
    277                 eventsOutsideHandler);
    278         Assert.assertEquals(message, 0 /* expected */, eventsOutsideHandler);
    279     }
    280 
    281     /**
    282      * Keeps track of the number of events that arrived in a different {@link Looper} than the one
    283      * associated with the {@link TestSensorEventListener}.
    284      */
    285     private void checkHandler() {
    286         if (mHandler != null && mHandler.getLooper() != Looper.myLooper()) {
    287             mEventsReceivedOutsideHandler.incrementAndGet();
    288         }
    289     }
    290 }
    291