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