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