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
17package android.os.health;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.util.ArrayMap;
22
23import java.util.Arrays;
24import 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 */
91public 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