Home | History | Annotate | Download | only in health
      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