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