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