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.Map;
24 
25 /**
26  * Class to write the health stats data into a parcel, so it can then be
27  * retrieved via a {@link HealthStats} object.
28  *
29  * There is an attempt to keep this class as low overhead as possible, for
30  * example storing an int[] and a long[] instead of a TimerStat[].
31  *
32  * @hide
33  */
34 public class HealthStatsWriter {
35     private final HealthKeys.Constants mConstants;
36 
37     // TimerStat fields
38     private final boolean[] mTimerFields;
39     private final int[] mTimerCounts;
40     private final long[] mTimerTimes;
41 
42     // Measurement fields
43     private final boolean[] mMeasurementFields;
44     private final long[] mMeasurementValues;
45 
46     // Stats fields
47     private final ArrayMap<String,HealthStatsWriter>[] mStatsValues;
48 
49     // Timers fields
50     private final ArrayMap<String,TimerStat>[] mTimersValues;
51 
52     // Measurements fields
53     private final ArrayMap<String,Long>[] mMeasurementsValues;
54 
55     /**
56      * Construct a HealthStatsWriter object with the given constants.
57      *
58      * The "getDataType()" of the resulting HealthStats object will be the
59      * short name of the java class that the Constants object was initalized
60      * with.
61      */
HealthStatsWriter(HealthKeys.Constants constants)62     public HealthStatsWriter(HealthKeys.Constants constants) {
63         mConstants = constants;
64 
65         // TimerStat
66         final int timerCount = constants.getSize(HealthKeys.TYPE_TIMER);
67         mTimerFields = new boolean[timerCount];
68         mTimerCounts = new int[timerCount];
69         mTimerTimes = new long[timerCount];
70 
71         // Measurement
72         final int measurementCount = constants.getSize(HealthKeys.TYPE_MEASUREMENT);
73         mMeasurementFields = new boolean[measurementCount];
74         mMeasurementValues = new long[measurementCount];
75 
76         // Stats
77         final int statsCount = constants.getSize(HealthKeys.TYPE_STATS);
78         mStatsValues = new ArrayMap[statsCount];
79 
80         // Timers
81         final int timersCount = constants.getSize(HealthKeys.TYPE_TIMERS);
82         mTimersValues = new ArrayMap[timersCount];
83 
84         // Measurements
85         final int measurementsCount = constants.getSize(HealthKeys.TYPE_MEASUREMENTS);
86         mMeasurementsValues = new ArrayMap[measurementsCount];
87     }
88 
89     /**
90      * Add a timer for the given key.
91      */
addTimer(int timerId, int count, long time)92     public void addTimer(int timerId, int count, long time) {
93         final int index = mConstants.getIndex(HealthKeys.TYPE_TIMER, timerId);
94 
95         mTimerFields[index] = true;
96         mTimerCounts[index] = count;
97         mTimerTimes[index] = time;
98     }
99 
100     /**
101      * Add a measurement for the given key.
102      */
addMeasurement(int measurementId, long value)103     public void addMeasurement(int measurementId, long value) {
104         final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENT, measurementId);
105 
106         mMeasurementFields[index] = true;
107         mMeasurementValues[index] = value;
108     }
109 
110     /**
111      * Add a recursive HealthStats object for the given key and string name. The value
112      * is stored as a HealthStatsWriter until this object is written to a parcel, so
113      * don't attempt to reuse the HealthStatsWriter.
114      *
115      * The value field should not be null.
116      */
addStats(int key, String name, HealthStatsWriter value)117     public void addStats(int key, String name, HealthStatsWriter value) {
118         final int index = mConstants.getIndex(HealthKeys.TYPE_STATS, key);
119 
120         ArrayMap<String,HealthStatsWriter> map = mStatsValues[index];
121         if (map == null) {
122             map = mStatsValues[index] = new ArrayMap<String,HealthStatsWriter>(1);
123         }
124         map.put(name, value);
125     }
126 
127     /**
128      * Add a TimerStat for the given key and string name.
129      *
130      * The value field should not be null.
131      */
addTimers(int key, String name, TimerStat value)132     public void addTimers(int key, String name, TimerStat value) {
133         final int index = mConstants.getIndex(HealthKeys.TYPE_TIMERS, key);
134 
135         ArrayMap<String,TimerStat> map = mTimersValues[index];
136         if (map == null) {
137             map = mTimersValues[index] = new ArrayMap<String,TimerStat>(1);
138         }
139         map.put(name, value);
140     }
141 
142     /**
143      * Add a measurement for the given key and string name.
144      */
addMeasurements(int key, String name, long value)145     public void addMeasurements(int key, String name, long value) {
146         final int index = mConstants.getIndex(HealthKeys.TYPE_MEASUREMENTS, key);
147 
148         ArrayMap<String,Long> map = mMeasurementsValues[index];
149         if (map == null) {
150             map = mMeasurementsValues[index] = new ArrayMap<String,Long>(1);
151         }
152         map.put(name, value);
153     }
154 
155     /**
156      * Flattens the data in this HealthStatsWriter to the Parcel format
157      * that can be unparceled into a HealthStat.
158      * @more
159      * (Called flattenToParcel because this HealthStatsWriter itself is
160      * not parcelable and we don't flatten all the business about the
161      * HealthKeys.Constants, only the values that were actually supplied)
162      */
flattenToParcel(Parcel out)163     public void flattenToParcel(Parcel out) {
164         int[] keys;
165 
166         // Header fields
167         out.writeString(mConstants.getDataType());
168 
169         // TimerStat fields
170         out.writeInt(countBooleanArray(mTimerFields));
171         keys = mConstants.getKeys(HealthKeys.TYPE_TIMER);
172         for (int i=0; i<keys.length; i++) {
173             if (mTimerFields[i]) {
174                 out.writeInt(keys[i]);
175                 out.writeInt(mTimerCounts[i]);
176                 out.writeLong(mTimerTimes[i]);
177             }
178         }
179 
180         // Measurement fields
181         out.writeInt(countBooleanArray(mMeasurementFields));
182         keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENT);
183         for (int i=0; i<keys.length; i++) {
184             if (mMeasurementFields[i]) {
185                 out.writeInt(keys[i]);
186                 out.writeLong(mMeasurementValues[i]);
187             }
188         }
189 
190         // Stats
191         out.writeInt(countObjectArray(mStatsValues));
192         keys = mConstants.getKeys(HealthKeys.TYPE_STATS);
193         for (int i=0; i<keys.length; i++) {
194             if (mStatsValues[i] != null) {
195                 out.writeInt(keys[i]);
196                 writeHealthStatsWriterMap(out, mStatsValues[i]);
197             }
198         }
199 
200         // Timers
201         out.writeInt(countObjectArray(mTimersValues));
202         keys = mConstants.getKeys(HealthKeys.TYPE_TIMERS);
203         for (int i=0; i<keys.length; i++) {
204             if (mTimersValues[i] != null) {
205                 out.writeInt(keys[i]);
206                 writeParcelableMap(out, mTimersValues[i]);
207             }
208         }
209 
210         // Measurements
211         out.writeInt(countObjectArray(mMeasurementsValues));
212         keys = mConstants.getKeys(HealthKeys.TYPE_MEASUREMENTS);
213         for (int i=0; i<keys.length; i++) {
214             if (mMeasurementsValues[i] != null) {
215                 out.writeInt(keys[i]);
216                 writeLongsMap(out, mMeasurementsValues[i]);
217             }
218         }
219     }
220 
221     /**
222      * Count how many of the fields have been set.
223      */
countBooleanArray(boolean[] fields)224     private static int countBooleanArray(boolean[] fields) {
225         int count = 0;
226         final int N = fields.length;
227         for (int i=0; i<N; i++) {
228             if (fields[i]) {
229                 count++;
230             }
231         }
232         return count;
233     }
234 
235     /**
236      * Count how many of the fields have been set.
237      */
countObjectArray(T[] fields)238     private static <T extends Object> int countObjectArray(T[] fields) {
239         int count = 0;
240         final int N = fields.length;
241         for (int i=0; i<N; i++) {
242             if (fields[i] != null) {
243                 count++;
244             }
245         }
246         return count;
247     }
248 
249     /**
250      * Write a map of String to HealthStatsWriter to the Parcel.
251      */
writeHealthStatsWriterMap(Parcel out, ArrayMap<String,HealthStatsWriter> map)252     private static void writeHealthStatsWriterMap(Parcel out,
253             ArrayMap<String,HealthStatsWriter> map) {
254         final int N = map.size();
255         out.writeInt(N);
256         for (int i=0; i<N; i++) {
257             out.writeString(map.keyAt(i));
258             map.valueAt(i).flattenToParcel(out);
259         }
260     }
261 
262     /**
263      * Write a map of String to Parcelables to the Parcel.
264      */
writeParcelableMap(Parcel out, ArrayMap<String,T> map)265     private static <T extends Parcelable> void writeParcelableMap(Parcel out,
266             ArrayMap<String,T> map) {
267         final int N = map.size();
268         out.writeInt(N);
269         for (int i=0; i<N; i++) {
270             out.writeString(map.keyAt(i));
271             map.valueAt(i).writeToParcel(out, 0);
272         }
273     }
274 
275     /**
276      * Write a map of String to Longs to the Parcel.
277      */
writeLongsMap(Parcel out, ArrayMap<String,Long> map)278     private static void writeLongsMap(Parcel out, ArrayMap<String,Long> map) {
279         final int N = map.size();
280         out.writeInt(N);
281         for (int i=0; i<N; i++) {
282             out.writeString(map.keyAt(i));
283             out.writeLong(map.valueAt(i));
284         }
285     }
286 }
287 
288 
289