| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.os.health; |
| |
| import android.annotation.TestApi; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.util.ArrayMap; |
| |
| import java.util.Arrays; |
| import java.util.Map; |
| |
| /** |
| * A HealthStats object contains system health data about an application. |
| * |
| * <p> |
| * <b>Data Types</b><br> |
| * Each of the keys references data in one of five data types: |
| * |
| * <p> |
| * A <b>measurement</b> metric contains a sinlge {@code long} value. That value may |
| * be a count, a time, or some other type of value. The unit for a measurement |
| * (COUNT, MS, etc) will always be in the name of the constant for the key to |
| * retrieve it. For example, the |
| * {@link android.os.health.UidHealthStats#MEASUREMENT_WIFI_TX_MS UidHealthStats.MEASUREMENT_WIFI_TX_MS} |
| * value is the number of milliseconds (ms) that were spent transmitting on wifi by an |
| * application. The |
| * {@link android.os.health.UidHealthStats#MEASUREMENT_MOBILE_RX_PACKETS UidHealthStats.MEASUREMENT_MOBILE_RX_PACKETS} |
| * measurement is the number of packets received on behalf of an application. |
| * The {@link android.os.health.UidHealthStats#MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT |
| * UidHealthStats.MEASUREMENT_TOUCH_USER_ACTIVITY_COUNT} |
| * measurement is the number of times the user touched the screen, causing the |
| * screen to stay awake. |
| * |
| * |
| * <p> |
| * A <b>timer</b> metric contains an {@code int} count and a {@code long} time, |
| * measured in milliseconds. Timers track how many times a resource was used, and |
| * the total duration for that usage. For example, the |
| * {@link android.os.health.UidHealthStats#TIMER_FLASHLIGHT} |
| * timer tracks how many times the application turned on the flashlight, and for |
| * how many milliseconds total it kept it on. |
| * |
| * <p> |
| * A <b>measurement map</b> metric is a mapping of {@link java.lang.String} names to |
| * {@link java.lang.Long} values. The names typically are application provided names. For |
| * example, the |
| * {@link android.os.health.PackageHealthStats#MEASUREMENTS_WAKEUP_ALARMS_COUNT |
| * PackageHealthStats.MEASUREMENTS_WAKEUP_ALARMS_COUNT} |
| * measurement map is a mapping of the tag provided to the |
| * {@link android.app.AlarmManager} when the alarm is scheduled. |
| * |
| * <p> |
| * A <b>timer map</b> metric is a mapping of {@link java.lang.String} names to |
| * {@link android.os.health.TimerStat} objects. The names are typically application |
| * provided names. For example, the |
| * {@link android.os.health.UidHealthStats#TIMERS_WAKELOCKS_PARTIAL UidHealthStats.TIMERS_WAKELOCKS_PARTIAL} |
| * is a mapping of tag provided to the {@link android.os.PowerManager} when the |
| * wakelock is created to the number of times and for how long each wakelock was |
| * active. |
| * |
| * <p> |
| * Lastly, a <b>health stats</b> metric is a mapping of {@link java.lang.String} |
| * names to a recursive {@link android.os.health.HealthStats} object containing |
| * more detailed information. For example, the |
| * {@link android.os.health.UidHealthStats#STATS_PACKAGES UidHealthStats.STATS_PACKAGES} |
| * metric is a mapping of the package names for each of the APKs sharing a uid to |
| * the information recorded for that apk. The returned HealthStats objects will |
| * each be associated with a different set of constants. For the HealthStats |
| * returned for UidHealthStats.STATS_PACKAGES, the keys come from the |
| * {@link android.os.health.PackageHealthStats} class. |
| * |
| * <p> |
| * The keys that are available are subject to change, depending on what a particular |
| * device or software version is capable of recording. Applications must handle the absence of |
| * data without crashing. |
| */ |
| public class HealthStats { |
| // Header fields |
| private String mDataType; |
| |
| // TimerStat fields |
| private int[] mTimerKeys; |
| private int[] mTimerCounts; |
| private long[] mTimerTimes; |
| |
| // Measurement fields |
| private int[] mMeasurementKeys; |
| private long[] mMeasurementValues; |
| |
| // Stats fields |
| private int[] mStatsKeys; |
| private ArrayMap<String,HealthStats>[] mStatsValues; |
| |
| // Timers fields |
| private int[] mTimersKeys; |
| private ArrayMap<String,TimerStat>[] mTimersValues; |
| |
| // Measurements fields |
| private int[] mMeasurementsKeys; |
| private ArrayMap<String,Long>[] mMeasurementsValues; |
| |
| /** |
| * HealthStats empty constructor not implemented because this |
| * class is read-only. |
| */ |
| private HealthStats() { |
| throw new RuntimeException("unsupported"); |
| } |
| |
| /** |
| * Construct a health stats object from a parcel. |
| * |
| * @hide |
| */ |
| @TestApi |
| public HealthStats(Parcel in) { |
| int count; |
| |
| // Header fields |
| mDataType = in.readString(); |
| |
| // TimerStat fields |
| count = in.readInt(); |
| mTimerKeys = new int[count]; |
| mTimerCounts = new int[count]; |
| mTimerTimes = new long[count]; |
| for (int i=0; i<count; i++) { |
| mTimerKeys[i] = in.readInt(); |
| mTimerCounts[i] = in.readInt(); |
| mTimerTimes[i] = in.readLong(); |
| } |
| |
| // Measurement fields |
| count = in.readInt(); |
| mMeasurementKeys = new int[count]; |
| mMeasurementValues = new long[count]; |
| for (int i=0; i<count; i++) { |
| mMeasurementKeys[i] = in.readInt(); |
| mMeasurementValues[i] = in.readLong(); |
| } |
| |
| // Stats fields |
| count = in.readInt(); |
| mStatsKeys = new int[count]; |
| mStatsValues = new ArrayMap[count]; |
| for (int i=0; i<count; i++) { |
| mStatsKeys[i] = in.readInt(); |
| mStatsValues[i] = createHealthStatsMap(in); |
| } |
| |
| // Timers fields |
| count = in.readInt(); |
| mTimersKeys = new int[count]; |
| mTimersValues = new ArrayMap[count]; |
| for (int i=0; i<count; i++) { |
| mTimersKeys[i] = in.readInt(); |
| mTimersValues[i] = createParcelableMap(in, TimerStat.CREATOR); |
| } |
| |
| // Measurements fields |
| count = in.readInt(); |
| mMeasurementsKeys = new int[count]; |
| mMeasurementsValues = new ArrayMap[count]; |
| for (int i=0; i<count; i++) { |
| mMeasurementsKeys[i] = in.readInt(); |
| mMeasurementsValues[i] = createLongsMap(in); |
| } |
| } |
| |
| /** |
| * Get a name representing the contents of this object. |
| * |
| * @see UidHealthStats |
| * @see PackageHealthStats |
| * @see PidHealthStats |
| * @see ProcessHealthStats |
| * @see ServiceHealthStats |
| */ |
| public String getDataType() { |
| return mDataType; |
| } |
| |
| /** |
| * Return whether this object contains a TimerStat for the supplied key. |
| */ |
| public boolean hasTimer(int key) { |
| return getIndex(mTimerKeys, key) >= 0; |
| } |
| |
| /** |
| * Return a TimerStat object for the given key. |
| * |
| * This will allocate a new {@link TimerStat} object, which may be wasteful. Instead, use |
| * {@link #getTimerCount} and {@link #getTimerTime}. |
| * |
| * @throws IndexOutOfBoundsException When the key is not present in this object. |
| * @see #hasTimer hasTimer(int) To check if a value for the given key is present. |
| */ |
| public TimerStat getTimer(int key) { |
| final int index = getIndex(mTimerKeys, key); |
| if (index < 0) { |
| throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType |
| + " key=" + key); |
| } |
| return new TimerStat(mTimerCounts[index], mTimerTimes[index]); |
| } |
| |
| /** |
| * Get the count for the timer for the given key. |
| * |
| * @throws IndexOutOfBoundsException When the key is not present in this object. |
| * @see #hasTimer hasTimer(int) To check if a value for the given key is present. |
| */ |
| public int getTimerCount(int key) { |
| final int index = getIndex(mTimerKeys, key); |
| if (index < 0) { |
| throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType |
| + " key=" + key); |
| } |
| return mTimerCounts[index]; |
| } |
| |
| /** |
| * Get the time for the timer for the given key, in milliseconds. |
| * |
| * @throws IndexOutOfBoundsException When the key is not present in this object. |
| * @see #hasTimer hasTimer(int) To check if a value for the given key is present. |
| */ |
| public long getTimerTime(int key) { |
| final int index = getIndex(mTimerKeys, key); |
| if (index < 0) { |
| throw new IndexOutOfBoundsException("Bad timer key dataType=" + mDataType |
| + " key=" + key); |
| } |
| return mTimerTimes[index]; |
| } |
| |
| /** |
| * Get the number of timer values in this object. Can be used to iterate through |
| * the available timers. |
| * |
| * @see #getTimerKeyAt |
| */ |
| public int getTimerKeyCount() { |
| return mTimerKeys.length; |
| } |
| |
| /** |
| * Get the key for the timer at the given index. Index must be between 0 and the result |
| * of {@link #getTimerKeyCount getTimerKeyCount()}. |
| * |
| * @see #getTimerKeyCount |
| */ |
| public int getTimerKeyAt(int index) { |
| return mTimerKeys[index]; |
| } |
| |
| /** |
| * Return whether this object contains a measurement for the supplied key. |
| */ |
| public boolean hasMeasurement(int key) { |
| return getIndex(mMeasurementKeys, key) >= 0; |
| } |
| |
| /** |
| * Get the measurement for the given key. |
| * |
| * @throws IndexOutOfBoundsException When the key is not present in this object. |
| * @see #hasMeasurement hasMeasurement(int) To check if a value for the given key is present. |
| */ |
| public long getMeasurement(int key) { |
| final int index = getIndex(mMeasurementKeys, key); |
| if (index < 0) { |
| throw new IndexOutOfBoundsException("Bad measurement key dataType=" + mDataType |
| + " key=" + key); |
| } |
| return mMeasurementValues[index]; |
| } |
| |
| /** |
| * Get the number of measurement values in this object. Can be used to iterate through |
| * the available measurements. |
| * |
| * @see #getMeasurementKeyAt |
| */ |
| public int getMeasurementKeyCount() { |
| return mMeasurementKeys.length; |
| } |
| |
| /** |
| * Get the key for the measurement at the given index. Index must be between 0 and the result |
| * of {@link #getMeasurementKeyCount getMeasurementKeyCount()}. |
| * |
| * @see #getMeasurementKeyCount |
| */ |
| public int getMeasurementKeyAt(int index) { |
| return mMeasurementKeys[index]; |
| } |
| |
| /** |
| * Return whether this object contains a HealthStats map for the supplied key. |
| */ |
| public boolean hasStats(int key) { |
| return getIndex(mStatsKeys, key) >= 0; |
| } |
| |
| /** |
| * Get the HealthStats map for the given key. |
| * |
| * @throws IndexOutOfBoundsException When the key is not present in this object. |
| * @see #hasStats hasStats(int) To check if a value for the given key is present. |
| */ |
| public Map<String,HealthStats> getStats(int key) { |
| final int index = getIndex(mStatsKeys, key); |
| if (index < 0) { |
| throw new IndexOutOfBoundsException("Bad stats key dataType=" + mDataType |
| + " key=" + key); |
| } |
| return mStatsValues[index]; |
| } |
| |
| /** |
| * Get the number of HealthStat map values in this object. Can be used to iterate through |
| * the available measurements. |
| * |
| * @see #getMeasurementKeyAt |
| */ |
| public int getStatsKeyCount() { |
| return mStatsKeys.length; |
| } |
| |
| /** |
| * Get the key for the timer at the given index. Index must be between 0 and the result |
| * of {@link #getStatsKeyCount getStatsKeyCount()}. |
| * |
| * @see #getStatsKeyCount |
| */ |
| public int getStatsKeyAt(int index) { |
| return mStatsKeys[index]; |
| } |
| |
| /** |
| * Return whether this object contains a timers map for the supplied key. |
| */ |
| public boolean hasTimers(int key) { |
| return getIndex(mTimersKeys, key) >= 0; |
| } |
| |
| /** |
| * Get the TimerStat map for the given key. |
| * |
| * @throws IndexOutOfBoundsException When the key is not present in this object. |
| * @see #hasTimers hasTimers(int) To check if a value for the given key is present. |
| */ |
| public Map<String,TimerStat> getTimers(int key) { |
| final int index = getIndex(mTimersKeys, key); |
| if (index < 0) { |
| throw new IndexOutOfBoundsException("Bad timers key dataType=" + mDataType |
| + " key=" + key); |
| } |
| return mTimersValues[index]; |
| } |
| |
| /** |
| * Get the number of timer map values in this object. Can be used to iterate through |
| * the available timer maps. |
| * |
| * @see #getTimersKeyAt |
| */ |
| public int getTimersKeyCount() { |
| return mTimersKeys.length; |
| } |
| |
| /** |
| * Get the key for the timer map at the given index. Index must be between 0 and the result |
| * of {@link #getTimersKeyCount getTimersKeyCount()}. |
| * |
| * @see #getTimersKeyCount |
| */ |
| public int getTimersKeyAt(int index) { |
| return mTimersKeys[index]; |
| } |
| |
| /** |
| * Return whether this object contains a measurements map for the supplied key. |
| */ |
| public boolean hasMeasurements(int key) { |
| return getIndex(mMeasurementsKeys, key) >= 0; |
| } |
| |
| /** |
| * Get the measurements map for the given key. |
| * |
| * @throws IndexOutOfBoundsException When the key is not present in this object. |
| * @see #hasMeasurements To check if a value for the given key is present. |
| */ |
| public Map<String,Long> getMeasurements(int key) { |
| final int index = getIndex(mMeasurementsKeys, key); |
| if (index < 0) { |
| throw new IndexOutOfBoundsException("Bad measurements key dataType=" + mDataType |
| + " key=" + key); |
| } |
| return mMeasurementsValues[index]; |
| } |
| |
| /** |
| * Get the number of measurement map values in this object. Can be used to iterate through |
| * the available measurement maps. |
| * |
| * @see #getMeasurementsKeyAt |
| */ |
| public int getMeasurementsKeyCount() { |
| return mMeasurementsKeys.length; |
| } |
| |
| /** |
| * Get the key for the measurement map at the given index. |
| * Index must be between 0 and the result |
| * of {@link #getMeasurementsKeyCount getMeasurementsKeyCount()}. |
| * |
| * @see #getMeasurementsKeyCount |
| */ |
| public int getMeasurementsKeyAt(int index) { |
| return mMeasurementsKeys[index]; |
| } |
| |
| /** |
| * Get the index in keys of key. |
| */ |
| private static int getIndex(int[] keys, int key) { |
| return Arrays.binarySearch(keys, key); |
| } |
| |
| /** |
| * Create an ArrayMap<String,HealthStats> from the given Parcel. |
| */ |
| private static ArrayMap<String,HealthStats> createHealthStatsMap(Parcel in) { |
| final int count = in.readInt(); |
| final ArrayMap<String,HealthStats> result = new ArrayMap<String,HealthStats>(count); |
| for (int i=0; i<count; i++) { |
| result.put(in.readString(), new HealthStats(in)); |
| } |
| return result; |
| } |
| |
| /** |
| * Create an ArrayMap<String,T extends Parcelable> from the given Parcel using |
| * the given Parcelable.Creator. |
| */ |
| private static <T extends Parcelable> ArrayMap<String,T> createParcelableMap(Parcel in, |
| Parcelable.Creator<T> creator) { |
| final int count = in.readInt(); |
| final ArrayMap<String,T> result = new ArrayMap<String,T>(count); |
| for (int i=0; i<count; i++) { |
| result.put(in.readString(), creator.createFromParcel(in)); |
| } |
| return result; |
| } |
| |
| /** |
| * Create an ArrayMap<String,Long> from the given Parcel. |
| */ |
| private static ArrayMap<String,Long> createLongsMap(Parcel in) { |
| final int count = in.readInt(); |
| final ArrayMap<String,Long> result = new ArrayMap<String,Long>(count); |
| for (int i=0; i<count; i++) { |
| result.put(in.readString(), in.readLong()); |
| } |
| return result; |
| } |
| } |
| |