blob: d4d10b056c5c1cf306dd1d017a57f95141faddb6 [file] [log] [blame]
/*
* Copyright (C) 2016 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.os.health;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import java.util.Map;
/**
* Class to write the health stats data into a parcel, so it can then be
* retrieved via a {@link HealthStats} object.
*
* There is an attempt to keep this class as low overhead as possible, for
* example storing an int[] and a long[] instead of a TimerStat[].
*
* @hide
*/
@TestApi
public class HealthStatsWriter {
private final HealthKeys.Constants mConstants;
// TimerStat fields
private final boolean[] mTimerFields;
private final int[] mTimerCounts;
private final long[] mTimerTimes;
// Measurement fields
private final boolean[] mMeasurementFields;
private final long[] mMeasurementValues;
// Stats fields
private final ArrayMap<String,HealthStatsWriter>[] mStatsValues;
// Timers fields
private final ArrayMap<String,TimerStat>[] mTimersValues;
// Measurements fields
private final ArrayMap<String,Long>[] mMeasurementsValues;
/**
* Construct a HealthStatsWriter object with the given constants.
*
* The "getDataType()" of the resulting HealthStats object will be the
* short name of the java class that the Constants object was initalized
* with.
*/
public HealthStatsWriter(HealthKeys.Constants constants) {
mConstants = constants;
// TimerStat
final int timerCount = constants.getSize(HealthKeys.TYPE_TIMER);
mTimerFields = new boolean[timerCount];
mTimerCounts = new int[timerCount];
mTimerTimes = new long[timerCount];
// Measurement
final int measurementCount = constants.getSize(HealthKeys.TYPE_MEASUREMENT);
mMeasurementFields = new boolean[measurementCount];
mMeasurementValues = new long[measurementCount];
// Stats
final int statsCount = constants.getSize(HealthKeys.TYPE_STATS);
mStatsValues = new ArrayMap[statsCount];
// Timers
final int timersCount = constants.getSize(HealthKeys.TYPE_TIMERS);
mTimersValues = new ArrayMap[timersCount];
// Measurements
final int measurementsCount = constants.getSize(HealthKeys.TYPE_MEASUREMENTS);
mMeasurementsValues = new ArrayMap[measurementsCount];
}
/**
* Add a timer for the given key.
*/
public void addTimer(int timerId, int count, long time) {
final int index = mConstants.getIndex(HealthKeys.TYPE_TIMER, timerId);
mTimerFields[index] = true;
mTimerCounts[index] = count;
mTimerTimes[index] = time;
}
/**
* Add a measurement for the given key.
*/
public void addMeasurement(int measurementId, long value) {
final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENT, measurementId);
mMeasurementFields[index] = true;
mMeasurementValues[index] = value;
}
/**
* Add a recursive HealthStats object for the given key and string name. The value
* is stored as a HealthStatsWriter until this object is written to a parcel, so
* don't attempt to reuse the HealthStatsWriter.
*
* The value field should not be null.
*/
public void addStats(int key, String name, HealthStatsWriter value) {
final int index = mConstants.getIndex(HealthKeys.TYPE_STATS, key);
ArrayMap<String,HealthStatsWriter> map = mStatsValues[index];
if (map == null) {
map = mStatsValues[index] = new ArrayMap<String,HealthStatsWriter>(1);
}
map.put(name, value);
}
/**
* Add a TimerStat for the given key and string name.
*
* The value field should not be null.
*/
public void addTimers(int key, String name, TimerStat value) {
final int index = mConstants.getIndex(HealthKeys.TYPE_TIMERS, key);
ArrayMap<String,TimerStat> map = mTimersValues[index];
if (map == null) {
map = mTimersValues[index] = new ArrayMap<String,TimerStat>(1);
}
map.put(name, value);
}
/**
* Add a measurement for the given key and string name.
*/
public void addMeasurements(int key, String name, long value) {
final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENTS, key);
ArrayMap<String,Long> map = mMeasurementsValues[index];
if (map == null) {
map = mMeasurementsValues[index] = new ArrayMap<String,Long>(1);
}
map.put(name, value);
}
/**
* Flattens the data in this HealthStatsWriter to the Parcel format
* that can be unparceled into a HealthStat.
* @more
* (Called flattenToParcel because this HealthStatsWriter itself is
* not parcelable and we don't flatten all the business about the
* HealthKeys.Constants, only the values that were actually supplied)
*/
public void flattenToParcel(Parcel out) {
int[] keys;
// Header fields
out.writeString(mConstants.getDataType());
// TimerStat fields
out.writeInt(countBooleanArray(mTimerFields));
keys = mConstants.getKeys(HealthKeys.TYPE_TIMER);
for (int i=0; i<keys.length; i++) {
if (mTimerFields[i]) {
out.writeInt(keys[i]);
out.writeInt(mTimerCounts[i]);
out.writeLong(mTimerTimes[i]);
}
}
// Measurement fields
out.writeInt(countBooleanArray(mMeasurementFields));
keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENT);
for (int i=0; i<keys.length; i++) {
if (mMeasurementFields[i]) {
out.writeInt(keys[i]);
out.writeLong(mMeasurementValues[i]);
}
}
// Stats
out.writeInt(countObjectArray(mStatsValues));
keys = mConstants.getKeys(HealthKeys.TYPE_STATS);
for (int i=0; i<keys.length; i++) {
if (mStatsValues[i] != null) {
out.writeInt(keys[i]);
writeHealthStatsWriterMap(out, mStatsValues[i]);
}
}
// Timers
out.writeInt(countObjectArray(mTimersValues));
keys = mConstants.getKeys(HealthKeys.TYPE_TIMERS);
for (int i=0; i<keys.length; i++) {
if (mTimersValues[i] != null) {
out.writeInt(keys[i]);
writeParcelableMap(out, mTimersValues[i]);
}
}
// Measurements
out.writeInt(countObjectArray(mMeasurementsValues));
keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENTS);
for (int i=0; i<keys.length; i++) {
if (mMeasurementsValues[i] != null) {
out.writeInt(keys[i]);
writeLongsMap(out, mMeasurementsValues[i]);
}
}
}
/**
* Count how many of the fields have been set.
*/
private static int countBooleanArray(boolean[] fields) {
int count = 0;
final int N = fields.length;
for (int i=0; i<N; i++) {
if (fields[i]) {
count++;
}
}
return count;
}
/**
* Count how many of the fields have been set.
*/
private static <T extends Object> int countObjectArray(T[] fields) {
int count = 0;
final int N = fields.length;
for (int i=0; i<N; i++) {
if (fields[i] != null) {
count++;
}
}
return count;
}
/**
* Write a map of String to HealthStatsWriter to the Parcel.
*/
private static void writeHealthStatsWriterMap(Parcel out,
ArrayMap<String,HealthStatsWriter> map) {
final int N = map.size();
out.writeInt(N);
for (int i=0; i<N; i++) {
out.writeString(map.keyAt(i));
map.valueAt(i).flattenToParcel(out);
}
}
/**
* Write a map of String to Parcelables to the Parcel.
*/
private static <T extends Parcelable> void writeParcelableMap(Parcel out,
ArrayMap<String,T> map) {
final int N = map.size();
out.writeInt(N);
for (int i=0; i<N; i++) {
out.writeString(map.keyAt(i));
map.valueAt(i).writeToParcel(out, 0);
}
}
/**
* Write a map of String to Longs to the Parcel.
*/
private static void writeLongsMap(Parcel out, ArrayMap<String,Long> map) {
final int N = map.size();
out.writeInt(N);
for (int i=0; i<N; i++) {
out.writeString(map.keyAt(i));
out.writeLong(map.valueAt(i));
}
}
}