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 android.hardware.Sensor;
     20 import android.hardware.cts.helpers.sensoroperations.SensorOperation;
     21 import android.util.Log;
     22 
     23 import java.io.BufferedWriter;
     24 import java.io.File;
     25 import java.io.FileWriter;
     26 import java.io.IOException;
     27 import java.util.ArrayList;
     28 import java.util.Arrays;
     29 import java.util.Collections;
     30 import java.util.HashMap;
     31 import java.util.List;
     32 import java.util.Map;
     33 import java.util.Map.Entry;
     34 import java.util.Set;
     35 
     36 /**
     37  * Class used to store stats related to {@link SensorOperation}s. Sensor stats may be linked
     38  * together so that they form a tree.
     39  */
     40 public class SensorStats {
     41     public static final String DELIMITER = "__";
     42 
     43     public static final String ERROR = "error";
     44     public static final String EVENT_FIFO_LENGTH = "event_fifo_length_observed";
     45     public static final String EVENT_GAP_COUNT_KEY = "event_gap_count";
     46     public static final String EVENT_GAP_POSITIONS_KEY = "event_gap_positions";
     47     public static final String EVENT_OUT_OF_ORDER_COUNT_KEY = "event_out_of_order_count";
     48     public static final String EVENT_OUT_OF_ORDER_POSITIONS_KEY = "event_out_of_order_positions";
     49     public static final String EVENT_TIME_SYNCHRONIZATION_COUNT_KEY =
     50             "event_time_synchronization_count";
     51     public static final String EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY =
     52             "event_time_synchronization_positions";
     53     public static final String EVENT_TIME_WRONG_CLOCKSOURCE_COUNT_KEY =
     54             "event_time_wrong_clocksource_count";
     55     public static final String EVENT_TIME_WRONG_CLOCKSOURCE_POSITIONS_KEY =
     56             "event_time_wrong_clocksource_positions";
     57     public static final String EVENT_COUNT_KEY = "event_count";
     58     public static final String EVENT_COUNT_EXPECTED_KEY = "event_count_expected";
     59     public static final String EVENT_LOG_FILENAME = "event_log_filename";
     60     public static final String WRONG_SENSOR_KEY = "wrong_sensor_observed";
     61     public static final String FREQUENCY_KEY = "frequency";
     62     public static final String JITTER_95_PERCENTILE_PERCENT_KEY = "jitter_95_percentile_percent";
     63     public static final String MEAN_KEY = "mean";
     64     public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
     65     public static final String MAGNITUDE_KEY = "magnitude";
     66     public static final String DELAYED_BATCH_DELIVERY = "delayed_batch_delivery";
     67 
     68     private final Map<String, Object> mValues = new HashMap<>();
     69     private final Map<String, SensorStats> mSensorStats = new HashMap<>();
     70 
     71     /**
     72      * Add a value.
     73      *
     74      * @param key the key.
     75      * @param value the value as an {@link Object}.
     76      */
     77     public synchronized void addValue(String key, Object value) {
     78         if (value == null) {
     79             return;
     80         }
     81         mValues.put(key, value);
     82     }
     83 
     84     /**
     85      * Add a nested {@link SensorStats}. This is useful for keeping track of stats in a
     86      * {@link SensorOperation} tree.
     87      *
     88      * @param key the key
     89      * @param stats the sub {@link SensorStats} object.
     90      */
     91     public synchronized void addSensorStats(String key, SensorStats stats) {
     92         if (stats == null) {
     93             return;
     94         }
     95         mSensorStats.put(key, stats);
     96     }
     97 
     98     /**
     99      * Get the keys from the values table. Will not get the keys from the nested
    100      * {@link SensorStats}.
    101      */
    102     public synchronized Set<String> getKeys() {
    103         return mValues.keySet();
    104     }
    105 
    106     /**
    107      * Get a value from the values table. Will not attempt to get values from nested
    108      * {@link SensorStats}.
    109      */
    110     public synchronized Object getValue(String key) {
    111         return mValues.get(key);
    112     }
    113 
    114     /**
    115      * Flattens the map and all sub {@link SensorStats} objects. Keys will be flattened using
    116      * {@value #DELIMITER}. For example, if a sub {@link SensorStats} is added with key
    117      * {@code "key1"} containing the key value pair {@code \("key2", "value"\)}, the flattened map
    118      * will contain the entry {@code \("key1__key2", "value"\)}.
    119      *
    120      * @return a {@link Map} containing all stats from the value and sub {@link SensorStats}.
    121      */
    122     public synchronized Map<String, Object> flatten() {
    123         final Map<String, Object> flattenedMap = new HashMap<>(mValues);
    124         for (Entry<String, SensorStats> statsEntry : mSensorStats.entrySet()) {
    125             for (Entry<String, Object> valueEntry : statsEntry.getValue().flatten().entrySet()) {
    126                 String key = statsEntry.getKey() + DELIMITER + valueEntry.getKey();
    127                 flattenedMap.put(key, valueEntry.getValue());
    128             }
    129         }
    130         return flattenedMap;
    131     }
    132 
    133     /**
    134      * Utility method to log the stats to the logcat.
    135      */
    136     public void log(String tag) {
    137         final Map<String, Object> flattened = flatten();
    138         for (String key : getSortedKeys(flattened)) {
    139             Object value = flattened.get(key);
    140             Log.v(tag, String.format("%s: %s", key, getValueString(value)));
    141         }
    142     }
    143 
    144     /**
    145      * Utility method to log the stats to a file. Will overwrite the file if it already exists.
    146      */
    147     public void logToFile(String fileName) throws IOException {
    148         File statsDirectory = SensorCtsHelper.getSensorTestDataDirectory("stats/");
    149         File logFile = new File(statsDirectory, fileName);
    150         final Map<String, Object> flattened = flatten();
    151         FileWriter fileWriter = new FileWriter(logFile, false /* append */);
    152         try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
    153             for (String key : getSortedKeys(flattened)) {
    154                 Object value = flattened.get(key);
    155                 writer.write(String.format("%s: %s\n", key, getValueString(value)));
    156             }
    157         }
    158     }
    159 
    160     /**
    161      * Provides a sanitized sensor name, that can be used in file names.
    162      * See {@link #logToFile(String)}.
    163      */
    164     public static String getSanitizedSensorName(Sensor sensor) throws SensorTestPlatformException {
    165         return SensorCtsHelper.sanitizeStringForFileName(sensor.getStringType());
    166     }
    167 
    168     private static List<String> getSortedKeys(Map<String, Object> flattenedStats) {
    169         List<String> keys = new ArrayList<>(flattenedStats.keySet());
    170         Collections.sort(keys);
    171         return keys;
    172     }
    173 
    174     private static String getValueString(Object value) {
    175         if (value == null) {
    176             return "";
    177         } else if (value instanceof boolean[]) {
    178             return Arrays.toString((boolean[]) value);
    179         } else if (value instanceof byte[]) {
    180             return Arrays.toString((byte[]) value);
    181         } else if (value instanceof char[]) {
    182             return Arrays.toString((char[]) value);
    183         } else if (value instanceof double[]) {
    184             return Arrays.toString((double[]) value);
    185         } else if (value instanceof float[]) {
    186             return Arrays.toString((float[]) value);
    187         } else if (value instanceof int[]) {
    188             return Arrays.toString((int[]) value);
    189         } else if (value instanceof long[]) {
    190             return Arrays.toString((long[]) value);
    191         } else if (value instanceof short[]) {
    192             return Arrays.toString((short[]) value);
    193         } else if (value instanceof Object[]) {
    194             return Arrays.toString((Object[]) value);
    195         } else {
    196             return value.toString();
    197         }
    198     }
    199 }
    200