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.Arrays; 24 import java.util.Map; 25 26 /** 27 * A HealthStats object contains system health data about an application. 28 * 29 * <p> 30 * <b>Data Types</b><br> 31 * Each of the keys references data in one of five data types: 32 * 33 * <p> 34 * A <b>measurement</b> metric contains a sinlge {@code long} value. That value may 35 * be a count, a time, or some other type of value. The unit for a measurement 36 * (COUNT, MS, etc) will always be in the name of the constant for the key to 37 * retrieve it. For example, the 38 * {@link android.os.health.UidHealthStats#MEASUREMENT_WIFI_TX_MS UidHealthStats.MEASUREMENT_WIFI_TX_MS} 39 * value is the number of milliseconds (ms) that were spent transmitting on wifi by an 40 * application. The 41 * {@link android.os.health.UidHealthStats#MEASUREMENT_MOBILE_RX_PACKETS UidHealthStats.MEASUREMENT_MOBILE_RX_PACKETS} 42 * measurement is the number of packets received on behalf of an application. 43 * The {@link android.os.health.UidHealthStats#MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT 44 * UidHealthStats.MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT} 45 * measurement is the number of times the user touched the screen, causing the 46 * screen to stay awake. 47 * 48 * 49 * <p> 50 * A <b>timer</b> metric contains an {@code int} count and a {@code long} time, 51 * measured in milliseconds. Timers track how many times a resource was used, and 52 * the total duration for that usage. For example, the 53 * {@link android.os.health.UidHealthStats#TIMER_FLASHLIGHT} 54 * timer tracks how many times the application turned on the flashlight, and for 55 * how many milliseconds total it kept it on. 56 * 57 * <p> 58 * A <b>measurement map</b> metric is a mapping of {@link java.lang.String} names to 59 * {@link java.lang.Long} values. The names typically are application provided names. For 60 * example, the 61 * {@link android.os.health.PackageHealthStats#MEASUREMENTS_WAKEUP_ALARMS_COUNT 62 * PackageHealthStats.MEASUREMENTS_WAKEUP_ALARMS_COUNT} 63 * measurement map is a mapping of the tag provided to the 64 * {@link android.app.AlarmManager} when the alarm is scheduled. 65 * 66 * <p> 67 * A <b>timer map</b> metric is a mapping of {@link java.lang.String} names to 68 * {@link android.os.health.TimerStat} objects. The names are typically application 69 * provided names. For example, the 70 * {@link android.os.health.UidHealthStats#TIMERS_WAKELOCKS_PARTIAL UidHealthStats.TIMERS_WAKELOCKS_PARTIAL} 71 * is a mapping of tag provided to the {@link android.os.PowerManager} when the 72 * wakelock is created to the number of times and for how long each wakelock was 73 * active. 74 * 75 * <p> 76 * Lastly, a <b>health stats</b> metric is a mapping of {@link java.lang.String} 77 * names to a recursive {@link android.os.health.HealthStats} object containing 78 * more detailed information. For example, the 79 * {@link android.os.health.UidHealthStats#STATS_PACKAGES UidHealthStats.STATS_PACKAGES} 80 * metric is a mapping of the package names for each of the APKs sharing a uid to 81 * the information recorded for that apk. The returned HealthStats objects will 82 * each be associated with a different set of constants. For the HealthStats 83 * returned for UidHealthStats.STATS_PACKAGES, the keys come from the 84 * {@link android.os.health.PackageHealthStats} class. 85 * 86 * <p> 87 * The keys that are available are subject to change, depending on what a particular 88 * device or software version is capable of recording. Applications must handle the absence of 89 * data without crashing. 90 */ 91 public class HealthStats { 92 // Header fields 93 private String mDataType; 94 95 // TimerStat fields 96 private int[] mTimerKeys; 97 private int[] mTimerCounts; 98 private long[] mTimerTimes; 99 100 // Measurement fields 101 private int[] mMeasurementKeys; 102 private long[] mMeasurementValues; 103 104 // Stats fields 105 private int[] mStatsKeys; 106 private ArrayMap<String,HealthStats>[] mStatsValues; 107 108 // Timers fields 109 private int[] mTimersKeys; 110 private ArrayMap<String,TimerStat>[] mTimersValues; 111 112 // Measurements fields 113 private int[] mMeasurementsKeys; 114 private ArrayMap<String,Long>[] mMeasurementsValues; 115 116 /** 117 * HealthStats empty constructor not implemented because this 118 * class is read-only. 119 */ 120 private HealthStats() { 121 throw new RuntimeException("unsupported"); 122 } 123 124 /** 125 * Construct a health stats object from a parcel. 126 * 127 * @hide 128 */ 129 public HealthStats(Parcel in) { 130 int count; 131 132 // Header fields 133 mDataType = in.readString(); 134 135 // TimerStat fields 136 count = in.readInt(); 137 mTimerKeys = new int[count]; 138 mTimerCounts = new int[count]; 139 mTimerTimes = new long[count]; 140 for (int i=0; i<count; i++) { 141 mTimerKeys[i] = in.readInt(); 142 mTimerCounts[i] = in.readInt(); 143 mTimerTimes[i] = in.readLong(); 144 } 145 146 // Measurement fields 147 count = in.readInt(); 148 mMeasurementKeys = new int[count]; 149 mMeasurementValues = new long[count]; 150 for (int i=0; i<count; i++) { 151 mMeasurementKeys[i] = in.readInt(); 152 mMeasurementValues[i] = in.readLong(); 153 } 154 155 // Stats fields 156 count = in.readInt(); 157 mStatsKeys = new int[count]; 158 mStatsValues = new ArrayMap[count]; 159 for (int i=0; i<count; i++) { 160 mStatsKeys[i] = in.readInt(); 161 mStatsValues[i] = createHealthStatsMap(in); 162 } 163 164 // Timers fields 165 count = in.readInt(); 166 mTimersKeys = new int[count]; 167 mTimersValues = new ArrayMap[count]; 168 for (int i=0; i<count; i++) { 169 mTimersKeys[i] = in.readInt(); 170 mTimersValues[i] = createParcelableMap(in, TimerStat.CREATOR); 171 } 172 173 // Measurements fields 174 count = in.readInt(); 175 mMeasurementsKeys = new int[count]; 176 mMeasurementsValues = new ArrayMap[count]; 177 for (int i=0; i<count; i++) { 178 mMeasurementsKeys[i] = in.readInt(); 179 mMeasurementsValues[i] = createLongsMap(in); 180 } 181 } 182 183 /** 184 * Get a name representing the contents of this object. 185 * 186 * @see UidHealthStats 187 * @see PackageHealthStats 188 * @see PidHealthStats 189 * @see ProcessHealthStats 190 * @see ServiceHealthStats 191 */ 192 public String getDataType() { 193 return mDataType; 194 } 195 196 /** 197 * Return whether this object contains a TimerStat for the supplied key. 198 */ 199 public boolean hasTimer(int key) { 200 return getIndex(mTimerKeys, key) >= 0; 201 } 202 203 /** 204 * Return a TimerStat object for the given key. 205 * 206 * This will allocate a new {@link TimerStat} object, which may be wasteful. Instead, use 207 * {@link #getTimerCount} and {@link #getTimerTime}. 208 * 209 * @throws IndexOutOfBoundsException When the key is not present in this object. 210 * @see #hasTimer hasTimer(int) To check if a value for the given key is present. 211 */ 212 public TimerStat getTimer(int key) { 213 final int index = getIndex(mTimerKeys, key); 214 if (index < 0) { 215 throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType 216 + " key=" + key); 217 } 218 return new TimerStat(mTimerCounts[index], mTimerTimes[index]); 219 } 220 221 /** 222 * Get the count for the timer for the given key. 223 * 224 * @throws IndexOutOfBoundsException When the key is not present in this object. 225 * @see #hasTimer hasTimer(int) To check if a value for the given key is present. 226 */ 227 public int getTimerCount(int key) { 228 final int index = getIndex(mTimerKeys, key); 229 if (index < 0) { 230 throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType 231 + " key=" + key); 232 } 233 return mTimerCounts[index]; 234 } 235 236 /** 237 * Get the time for the timer for the given key, in milliseconds. 238 * 239 * @throws IndexOutOfBoundsException When the key is not present in this object. 240 * @see #hasTimer hasTimer(int) To check if a value for the given key is present. 241 */ 242 public long getTimerTime(int key) { 243 final int index = getIndex(mTimerKeys, key); 244 if (index < 0) { 245 throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType 246 + " key=" + key); 247 } 248 return mTimerTimes[index]; 249 } 250 251 /** 252 * Get the number of timer values in this object. Can be used to iterate through 253 * the available timers. 254 * 255 * @see #getTimerKeyAt 256 */ 257 public int getTimerKeyCount() { 258 return mTimerKeys.length; 259 } 260 261 /** 262 * Get the key for the timer at the given index. Index must be between 0 and the result 263 * of {@link #getTimerKeyCount getTimerKeyCount()}. 264 * 265 * @see #getTimerKeyCount 266 */ 267 public int getTimerKeyAt(int index) { 268 return mTimerKeys[index]; 269 } 270 271 /** 272 * Return whether this object contains a measurement for the supplied key. 273 */ 274 public boolean hasMeasurement(int key) { 275 return getIndex(mMeasurementKeys, key) >= 0; 276 } 277 278 /** 279 * Get the measurement for the given key. 280 * 281 * @throws IndexOutOfBoundsException When the key is not present in this object. 282 * @see #hasMeasurement hasMeasurement(int) To check if a value for the given key is present. 283 */ 284 public long getMeasurement(int key) { 285 final int index = getIndex(mMeasurementKeys, key); 286 if (index < 0) { 287 throw new IndexOutOfBoundsException("Bad measurement key dataType=" + mDataType 288 + " key=" + key); 289 } 290 return mMeasurementValues[index]; 291 } 292 293 /** 294 * Get the number of measurement values in this object. Can be used to iterate through 295 * the available measurements. 296 * 297 * @see #getMeasurementKeyAt 298 */ 299 public int getMeasurementKeyCount() { 300 return mMeasurementKeys.length; 301 } 302 303 /** 304 * Get the key for the measurement at the given index. Index must be between 0 and the result 305 * of {@link #getMeasurementKeyCount getMeasurementKeyCount()}. 306 * 307 * @see #getMeasurementKeyCount 308 */ 309 public int getMeasurementKeyAt(int index) { 310 return mMeasurementKeys[index]; 311 } 312 313 /** 314 * Return whether this object contains a HealthStats map for the supplied key. 315 */ 316 public boolean hasStats(int key) { 317 return getIndex(mStatsKeys, key) >= 0; 318 } 319 320 /** 321 * Get the HealthStats map for the given key. 322 * 323 * @throws IndexOutOfBoundsException When the key is not present in this object. 324 * @see #hasStats hasStats(int) To check if a value for the given key is present. 325 */ 326 public Map<String,HealthStats> getStats(int key) { 327 final int index = getIndex(mStatsKeys, key); 328 if (index < 0) { 329 throw new IndexOutOfBoundsException("Bad stats key dataType=" + mDataType 330 + " key=" + key); 331 } 332 return mStatsValues[index]; 333 } 334 335 /** 336 * Get the number of HealthStat map values in this object. Can be used to iterate through 337 * the available measurements. 338 * 339 * @see #getMeasurementKeyAt 340 */ 341 public int getStatsKeyCount() { 342 return mStatsKeys.length; 343 } 344 345 /** 346 * Get the key for the timer at the given index. Index must be between 0 and the result 347 * of {@link #getStatsKeyCount getStatsKeyCount()}. 348 * 349 * @see #getStatsKeyCount 350 */ 351 public int getStatsKeyAt(int index) { 352 return mStatsKeys[index]; 353 } 354 355 /** 356 * Return whether this object contains a timers map for the supplied key. 357 */ 358 public boolean hasTimers(int key) { 359 return getIndex(mTimersKeys, key) >= 0; 360 } 361 362 /** 363 * Get the TimerStat map for the given key. 364 * 365 * @throws IndexOutOfBoundsException When the key is not present in this object. 366 * @see #hasTimers hasTimers(int) To check if a value for the given key is present. 367 */ 368 public Map<String,TimerStat> getTimers(int key) { 369 final int index = getIndex(mTimersKeys, key); 370 if (index < 0) { 371 throw new IndexOutOfBoundsException("Bad timers key dataType=" + mDataType 372 + " key=" + key); 373 } 374 return mTimersValues[index]; 375 } 376 377 /** 378 * Get the number of timer map values in this object. Can be used to iterate through 379 * the available timer maps. 380 * 381 * @see #getTimersKeyAt 382 */ 383 public int getTimersKeyCount() { 384 return mTimersKeys.length; 385 } 386 387 /** 388 * Get the key for the timer map at the given index. Index must be between 0 and the result 389 * of {@link #getTimersKeyCount getTimersKeyCount()}. 390 * 391 * @see #getTimersKeyCount 392 */ 393 public int getTimersKeyAt(int index) { 394 return mTimersKeys[index]; 395 } 396 397 /** 398 * Return whether this object contains a measurements map for the supplied key. 399 */ 400 public boolean hasMeasurements(int key) { 401 return getIndex(mMeasurementsKeys, key) >= 0; 402 } 403 404 /** 405 * Get the measurements map for the given key. 406 * 407 * @throws IndexOutOfBoundsException When the key is not present in this object. 408 * @see #hasMeasurements To check if a value for the given key is present. 409 */ 410 public Map<String,Long> getMeasurements(int key) { 411 final int index = getIndex(mMeasurementsKeys, key); 412 if (index < 0) { 413 throw new IndexOutOfBoundsException("Bad measurements key dataType=" + mDataType 414 + " key=" + key); 415 } 416 return mMeasurementsValues[index]; 417 } 418 419 /** 420 * Get the number of measurement map values in this object. Can be used to iterate through 421 * the available measurement maps. 422 * 423 * @see #getMeasurementsKeyAt 424 */ 425 public int getMeasurementsKeyCount() { 426 return mMeasurementsKeys.length; 427 } 428 429 /** 430 * Get the key for the measurement map at the given index. 431 * Index must be between 0 and the result 432 * of {@link #getMeasurementsKeyCount getMeasurementsKeyCount()}. 433 * 434 * @see #getMeasurementsKeyCount 435 */ 436 public int getMeasurementsKeyAt(int index) { 437 return mMeasurementsKeys[index]; 438 } 439 440 /** 441 * Get the index in keys of key. 442 */ 443 private static int getIndex(int[] keys, int key) { 444 return Arrays.binarySearch(keys, key); 445 } 446 447 /** 448 * Create an ArrayMap<String,HealthStats> from the given Parcel. 449 */ 450 private static ArrayMap<String,HealthStats> createHealthStatsMap(Parcel in) { 451 final int count = in.readInt(); 452 final ArrayMap<String,HealthStats> result = new ArrayMap<String,HealthStats>(count); 453 for (int i=0; i<count; i++) { 454 result.put(in.readString(), new HealthStats(in)); 455 } 456 return result; 457 } 458 459 /** 460 * Create an ArrayMap<String,T extends Parcelable> from the given Parcel using 461 * the given Parcelable.Creator. 462 */ 463 private static <T extends Parcelable> ArrayMap<String,T> createParcelableMap(Parcel in, 464 Parcelable.Creator<T> creator) { 465 final int count = in.readInt(); 466 final ArrayMap<String,T> result = new ArrayMap<String,T>(count); 467 for (int i=0; i<count; i++) { 468 result.put(in.readString(), creator.createFromParcel(in)); 469 } 470 return result; 471 } 472 473 /** 474 * Create an ArrayMap<String,Long> from the given Parcel. 475 */ 476 private static ArrayMap<String,Long> createLongsMap(Parcel in) { 477 final int count = in.readInt(); 478 final ArrayMap<String,Long> result = new ArrayMap<String,Long>(count); 479 for (int i=0; i<count; i++) { 480 result.put(in.readString(), in.readLong()); 481 } 482 return result; 483 } 484 } 485 486