/* * 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.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 */ 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)); } } }