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