1 /* 2 * Copyright (C) 2016 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.os.health; 18 19 import android.annotation.TestApi; 20 import android.os.Parcel; 21 import android.os.Parcelable; 22 import android.util.ArrayMap; 23 24 import java.util.Map; 25 26 /** 27 * Class to write the health stats data into a parcel, so it can then be 28 * retrieved via a {@link HealthStats} object. 29 * 30 * There is an attempt to keep this class as low overhead as possible, for 31 * example storing an int[] and a long[] instead of a TimerStat[]. 32 * 33 * @hide 34 */ 35 @TestApi 36 public class HealthStatsWriter { 37 private final HealthKeys.Constants mConstants; 38 39 // TimerStat fields 40 private final boolean[] mTimerFields; 41 private final int[] mTimerCounts; 42 private final long[] mTimerTimes; 43 44 // Measurement fields 45 private final boolean[] mMeasurementFields; 46 private final long[] mMeasurementValues; 47 48 // Stats fields 49 private final ArrayMap<String,HealthStatsWriter>[] mStatsValues; 50 51 // Timers fields 52 private final ArrayMap<String,TimerStat>[] mTimersValues; 53 54 // Measurements fields 55 private final ArrayMap<String,Long>[] mMeasurementsValues; 56 57 /** 58 * Construct a HealthStatsWriter object with the given constants. 59 * 60 * The "getDataType()" of the resulting HealthStats object will be the 61 * short name of the java class that the Constants object was initalized 62 * with. 63 */ HealthStatsWriter(HealthKeys.Constants constants)64 public HealthStatsWriter(HealthKeys.Constants constants) { 65 mConstants = constants; 66 67 // TimerStat 68 final int timerCount = constants.getSize(HealthKeys.TYPE_TIMER); 69 mTimerFields = new boolean[timerCount]; 70 mTimerCounts = new int[timerCount]; 71 mTimerTimes = new long[timerCount]; 72 73 // Measurement 74 final int measurementCount = constants.getSize(HealthKeys.TYPE_MEASUREMENT); 75 mMeasurementFields = new boolean[measurementCount]; 76 mMeasurementValues = new long[measurementCount]; 77 78 // Stats 79 final int statsCount = constants.getSize(HealthKeys.TYPE_STATS); 80 mStatsValues = new ArrayMap[statsCount]; 81 82 // Timers 83 final int timersCount = constants.getSize(HealthKeys.TYPE_TIMERS); 84 mTimersValues = new ArrayMap[timersCount]; 85 86 // Measurements 87 final int measurementsCount = constants.getSize(HealthKeys.TYPE_MEASUREMENTS); 88 mMeasurementsValues = new ArrayMap[measurementsCount]; 89 } 90 91 /** 92 * Add a timer for the given key. 93 */ addTimer(int timerId, int count, long time)94 public void addTimer(int timerId, int count, long time) { 95 final int index = mConstants.getIndex(HealthKeys.TYPE_TIMER, timerId); 96 97 mTimerFields[index] = true; 98 mTimerCounts[index] = count; 99 mTimerTimes[index] = time; 100 } 101 102 /** 103 * Add a measurement for the given key. 104 */ addMeasurement(int measurementId, long value)105 public void addMeasurement(int measurementId, long value) { 106 final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENT, measurementId); 107 108 mMeasurementFields[index] = true; 109 mMeasurementValues[index] = value; 110 } 111 112 /** 113 * Add a recursive HealthStats object for the given key and string name. The value 114 * is stored as a HealthStatsWriter until this object is written to a parcel, so 115 * don't attempt to reuse the HealthStatsWriter. 116 * 117 * The value field should not be null. 118 */ addStats(int key, String name, HealthStatsWriter value)119 public void addStats(int key, String name, HealthStatsWriter value) { 120 final int index = mConstants.getIndex(HealthKeys.TYPE_STATS, key); 121 122 ArrayMap<String,HealthStatsWriter> map = mStatsValues[index]; 123 if (map == null) { 124 map = mStatsValues[index] = new ArrayMap<String,HealthStatsWriter>(1); 125 } 126 map.put(name, value); 127 } 128 129 /** 130 * Add a TimerStat for the given key and string name. 131 * 132 * The value field should not be null. 133 */ addTimers(int key, String name, TimerStat value)134 public void addTimers(int key, String name, TimerStat value) { 135 final int index = mConstants.getIndex(HealthKeys.TYPE_TIMERS, key); 136 137 ArrayMap<String,TimerStat> map = mTimersValues[index]; 138 if (map == null) { 139 map = mTimersValues[index] = new ArrayMap<String,TimerStat>(1); 140 } 141 map.put(name, value); 142 } 143 144 /** 145 * Add a measurement for the given key and string name. 146 */ addMeasurements(int key, String name, long value)147 public void addMeasurements(int key, String name, long value) { 148 final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENTS, key); 149 150 ArrayMap<String,Long> map = mMeasurementsValues[index]; 151 if (map == null) { 152 map = mMeasurementsValues[index] = new ArrayMap<String,Long>(1); 153 } 154 map.put(name, value); 155 } 156 157 /** 158 * Flattens the data in this HealthStatsWriter to the Parcel format 159 * that can be unparceled into a HealthStat. 160 * @more 161 * (Called flattenToParcel because this HealthStatsWriter itself is 162 * not parcelable and we don't flatten all the business about the 163 * HealthKeys.Constants, only the values that were actually supplied) 164 */ flattenToParcel(Parcel out)165 public void flattenToParcel(Parcel out) { 166 int[] keys; 167 168 // Header fields 169 out.writeString(mConstants.getDataType()); 170 171 // TimerStat fields 172 out.writeInt(countBooleanArray(mTimerFields)); 173 keys = mConstants.getKeys(HealthKeys.TYPE_TIMER); 174 for (int i=0; i<keys.length; i++) { 175 if (mTimerFields[i]) { 176 out.writeInt(keys[i]); 177 out.writeInt(mTimerCounts[i]); 178 out.writeLong(mTimerTimes[i]); 179 } 180 } 181 182 // Measurement fields 183 out.writeInt(countBooleanArray(mMeasurementFields)); 184 keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENT); 185 for (int i=0; i<keys.length; i++) { 186 if (mMeasurementFields[i]) { 187 out.writeInt(keys[i]); 188 out.writeLong(mMeasurementValues[i]); 189 } 190 } 191 192 // Stats 193 out.writeInt(countObjectArray(mStatsValues)); 194 keys = mConstants.getKeys(HealthKeys.TYPE_STATS); 195 for (int i=0; i<keys.length; i++) { 196 if (mStatsValues[i] != null) { 197 out.writeInt(keys[i]); 198 writeHealthStatsWriterMap(out, mStatsValues[i]); 199 } 200 } 201 202 // Timers 203 out.writeInt(countObjectArray(mTimersValues)); 204 keys = mConstants.getKeys(HealthKeys.TYPE_TIMERS); 205 for (int i=0; i<keys.length; i++) { 206 if (mTimersValues[i] != null) { 207 out.writeInt(keys[i]); 208 writeParcelableMap(out, mTimersValues[i]); 209 } 210 } 211 212 // Measurements 213 out.writeInt(countObjectArray(mMeasurementsValues)); 214 keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENTS); 215 for (int i=0; i<keys.length; i++) { 216 if (mMeasurementsValues[i] != null) { 217 out.writeInt(keys[i]); 218 writeLongsMap(out, mMeasurementsValues[i]); 219 } 220 } 221 } 222 223 /** 224 * Count how many of the fields have been set. 225 */ countBooleanArray(boolean[] fields)226 private static int countBooleanArray(boolean[] fields) { 227 int count = 0; 228 final int N = fields.length; 229 for (int i=0; i<N; i++) { 230 if (fields[i]) { 231 count++; 232 } 233 } 234 return count; 235 } 236 237 /** 238 * Count how many of the fields have been set. 239 */ countObjectArray(T[] fields)240 private static <T extends Object> int countObjectArray(T[] fields) { 241 int count = 0; 242 final int N = fields.length; 243 for (int i=0; i<N; i++) { 244 if (fields[i] != null) { 245 count++; 246 } 247 } 248 return count; 249 } 250 251 /** 252 * Write a map of String to HealthStatsWriter to the Parcel. 253 */ writeHealthStatsWriterMap(Parcel out, ArrayMap<String,HealthStatsWriter> map)254 private static void writeHealthStatsWriterMap(Parcel out, 255 ArrayMap<String,HealthStatsWriter> map) { 256 final int N = map.size(); 257 out.writeInt(N); 258 for (int i=0; i<N; i++) { 259 out.writeString(map.keyAt(i)); 260 map.valueAt(i).flattenToParcel(out); 261 } 262 } 263 264 /** 265 * Write a map of String to Parcelables to the Parcel. 266 */ writeParcelableMap(Parcel out, ArrayMap<String,T> map)267 private static <T extends Parcelable> void writeParcelableMap(Parcel out, 268 ArrayMap<String,T> map) { 269 final int N = map.size(); 270 out.writeInt(N); 271 for (int i=0; i<N; i++) { 272 out.writeString(map.keyAt(i)); 273 map.valueAt(i).writeToParcel(out, 0); 274 } 275 } 276 277 /** 278 * Write a map of String to Longs to the Parcel. 279 */ writeLongsMap(Parcel out, ArrayMap<String,Long> map)280 private static void writeLongsMap(Parcel out, ArrayMap<String,Long> map) { 281 final int N = map.size(); 282 out.writeInt(N); 283 for (int i=0; i<N; i++) { 284 out.writeString(map.keyAt(i)); 285 out.writeLong(map.valueAt(i)); 286 } 287 } 288 } 289 290 291