blob: 9d860cd66fc2ebc9a193bfb7fa339737f71eb39f [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware.cts.helpers;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.os.Environment;
import android.util.Log;
import java.io.DataOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Set of static helper methods for CTS tests.
*/
public class SensorCtsHelper {
/**
* Private constructor for static class.
*/
private SensorCtsHelper() {}
/**
* Get the value of the 95th percentile using nearest rank algorithm.
*
* @throws IllegalArgumentException if the collection is null or empty
*/
public static <TValue extends Comparable<? super TValue>> TValue get95PercentileValue(
Collection<TValue> collection) {
validateCollection(collection);
List<TValue> arrayCopy = new ArrayList<TValue>(collection);
Collections.sort(arrayCopy);
// zero-based array index
int arrayIndex = (int) Math.round(arrayCopy.size() * 0.95 + .5) - 1;
return arrayCopy.get(arrayIndex);
}
/**
* Calculates the mean for each of the values in the set of TestSensorEvents.
*
* @throws IllegalArgumentException if there are no events
*/
public static double[] getMeans(TestSensorEvent[] events) {
if (events.length == 0) {
throw new IllegalArgumentException("Events cannot be empty");
}
double[] means = new double[events[0].values.length];
for (TestSensorEvent event : events) {
for (int i = 0; i < means.length; i++) {
means[i] += event.values[i];
}
}
for (int i = 0; i < means.length; i++) {
means[i] /= events.length;
}
return means;
}
/**
* Calculates the variance for each of the values in the set of TestSensorEvents.
*
* @throws IllegalArgumentException if there are no events
*/
public static double[] getVariances(TestSensorEvent[] events) {
double[] means = getMeans(events);
double[] variances = new double[means.length];
for (int i = 0; i < means.length; i++) {
Collection<Double> squaredDiffs = new ArrayList<Double>(events.length);
for (TestSensorEvent event : events) {
double diff = event.values[i] - means[i];
squaredDiffs.add(diff * diff);
}
variances[i] = getMean(squaredDiffs);
}
return variances;
}
/**
* Calculates the standard deviation for each of the values in the set of TestSensorEvents.
*
* @throws IllegalArgumentException if there are no events
*/
public static double[] getStandardDeviations(TestSensorEvent[] events) {
double[] variances = getVariances(events);
double[] stdDevs = new double[variances.length];
for (int i = 0; i < variances.length; i++) {
stdDevs[i] = Math.sqrt(variances[i]);
}
return stdDevs;
}
/**
* Calculate the mean of a collection.
*
* @throws IllegalArgumentException if the collection is null or empty
*/
public static <TValue extends Number> double getMean(Collection<TValue> collection) {
validateCollection(collection);
double sum = 0.0;
for(TValue value : collection) {
sum += value.doubleValue();
}
return sum / collection.size();
}
/**
* Calculate the variance of a collection.
*
* @throws IllegalArgumentException if the collection is null or empty
*/
public static <TValue extends Number> double getVariance(Collection<TValue> collection) {
validateCollection(collection);
double mean = getMean(collection);
ArrayList<Double> squaredDifferences = new ArrayList<Double>();
for(TValue value : collection) {
double difference = mean - value.doubleValue();
squaredDifferences.add(Math.pow(difference, 2));
}
return getMean(squaredDifferences);
}
/**
* Calculate the standard deviation of a collection.
*
* @throws IllegalArgumentException if the collection is null or empty
*/
public static <TValue extends Number> double getStandardDeviation(
Collection<TValue> collection) {
return Math.sqrt(getVariance(collection));
}
/**
* Get a list containing the delay between sensor events.
*
* @param events The array of {@link TestSensorEvent}.
* @return A list containing the delay between sensor events in nanoseconds.
*/
public static List<Long> getTimestampDelayValues(TestSensorEvent[] events) {
if (events.length < 2) {
return new ArrayList<Long>();
}
List<Long> timestampDelayValues = new ArrayList<Long>(events.length - 1);
for (int i = 1; i < events.length; i++) {
timestampDelayValues.add(events[i].timestamp - events[i - 1].timestamp);
}
return timestampDelayValues;
}
/**
* Get a list containing the jitter values for a collection of sensor events.
*
* @param events The array of {@link TestSensorEvent}.
* @return A list containing the jitter values between each event.
* @throws IllegalArgumentException if the number of events is less that 2.
*/
public static List<Double> getJitterValues(TestSensorEvent[] events) {
List<Long> timestampDelayValues = getTimestampDelayValues(events);
double averageTimestampDelay = getMean(timestampDelayValues);
List<Double> jitterValues = new ArrayList<Double>(timestampDelayValues.size());
for (long timestampDelay : timestampDelayValues) {
jitterValues.add(Math.abs(timestampDelay - averageTimestampDelay));
}
return jitterValues;
}
/**
* NOTE:
* - The bug report is usually written to /sdcard/Downloads
* - In order for the test Instrumentation to gather useful data the following permissions are
* required:
* . android.permission.READ_LOGS
* . android.permission.DUMP
*/
public static String collectBugreport(String collectorId)
throws IOException, InterruptedException {
String commands[] = new String[] {
"dumpstate",
"dumpsys",
"logcat -d -v threadtime",
"exit"
};
SimpleDateFormat dateFormat = new SimpleDateFormat("M-d-y_H:m:s.S");
String outputFile = String.format(
"%s/%s_%s",
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
collectorId,
dateFormat.format(new Date()));
DataOutputStream processOutput = null;
try {
Process process = Runtime.getRuntime().exec("/system/bin/sh -");
processOutput = new DataOutputStream(process.getOutputStream());
for(String command : commands) {
processOutput.writeBytes(String.format("%s >> %s\n", command, outputFile));
}
processOutput.flush();
process.waitFor();
Log.d(collectorId, String.format("Bug-Report collected at: %s", outputFile));
} finally {
if(processOutput != null) {
try {
processOutput.close();
} catch(IOException e) {}
}
}
return outputFile;
}
/**
* Get the default sensor for a given type.
*/
public static Sensor getSensor(Context context, int sensorType) {
SensorManager sensorManager = getSensorManager(context);
Sensor sensor = sensorManager.getDefaultSensor(sensorType);
if(sensor == null) {
throw new SensorNotSupportedException(sensorType);
}
return sensor;
}
/**
* Get all the sensors for a given type.
*/
public static List<Sensor> getSensors(Context context, int sensorType) {
SensorManager sensorManager = getSensorManager(context);
List<Sensor> sensors = sensorManager.getSensorList(sensorType);
if (sensors.size() == 0) {
throw new SensorNotSupportedException(sensorType);
}
return sensors;
}
/**
* Convert a period to frequency in Hz.
*/
public static <TValue extends Number> double getFrequency(TValue period, TimeUnit unit) {
return 1000000000 / (TimeUnit.NANOSECONDS.convert(1, unit) * period.doubleValue());
}
/**
* Convert a frequency in Hz into a period.
*/
public static <TValue extends Number> double getPeriod(TValue frequency, TimeUnit unit) {
return 1000000000 / (TimeUnit.NANOSECONDS.convert(1, unit) * frequency.doubleValue());
}
/**
* Convert number of seconds to number of microseconds.
*/
public static int getSecondsAsMicroSeconds(int seconds) {
return (int) TimeUnit.MICROSECONDS.convert(seconds, TimeUnit.SECONDS);
}
/**
* Format an assertion message.
*
* @param verificationName The verification name
* @param sensor The sensor under test
* @param format The additional format string, use "" if blank
* @param params The additional format params
* @return The formatted string.
*/
public static String formatAssertionMessage(
String verificationName,
Sensor sensor,
String format,
Object ... params) {
return formatAssertionMessage(verificationName, null, sensor, format, params);
}
/**
* Format an assertion message.
*
* @param verificationName The verification name
* @param test The test, optional
* @param sensor The sensor under test
* @param format The additional format string, use "" if blank
* @param params The additional format params
* @return The formatted string.
*/
public static String formatAssertionMessage(
String verificationName,
SensorTestOperation test,
Sensor sensor,
String format,
Object ... params) {
StringBuilder builder = new StringBuilder();
// identify the verification
builder.append(verificationName);
builder.append("| ");
// add test context information
if(test != null) {
builder.append(test.toString());
builder.append("| ");
}
// add context information
builder.append(SensorTestInformation.getSensorName(sensor.getType()));
builder.append(", handle:");
builder.append(sensor.getHandle());
builder.append("| ");
// add the custom formatting
builder.append(String.format(format, params));
return builder.toString();
}
/**
* Validate that a collection is not null or empty.
*
* @throws IllegalStateException if collection is null or empty.
*/
private static <T> void validateCollection(Collection<T> collection) {
if(collection == null || collection.size() == 0) {
throw new IllegalStateException("Collection cannot be null or empty");
}
}
/**
* Get the SensorManager.
*
* @throws IllegalStateException if the SensorManager is not present in the system.
*/
private static SensorManager getSensorManager(Context context) {
SensorManager sensorManager = (SensorManager) context.getSystemService(
Context.SENSOR_SERVICE);
if(sensorManager == null) {
throw new IllegalStateException("SensorService is not present in the system.");
}
return sensorManager;
}
}