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 
21 import java.lang.annotation.ElementType;
22 import java.lang.annotation.Retention;
23 import java.lang.annotation.RetentionPolicy;
24 import java.lang.annotation.Target;
25 import java.lang.reflect.Field;
26 import java.util.Arrays;
27 
28 /**
29  * Constants and stuff for the android.os.health package.
30  *
31  * @hide
32  */
33 @TestApi
34 public class HealthKeys {
35 
36     /**
37      * No valid key will ever be 0.
38      */
39     public static final int UNKNOWN_KEY = 0;
40 
41     /*
42      * Base key for each of the different classes. There is
43      * nothing intrinsic to the operation of the value of the
44      * keys. It's just segmented for better debugging. The
45      * classes don't mix them anway.
46      */
47     public static final int BASE_UID = 10000;
48     public static final int BASE_PID = 20000;
49     public static final int BASE_PROCESS = 30000;
50     public static final int BASE_PACKAGE = 40000;
51     public static final int BASE_SERVICE = 50000;
52 
53     /*
54      * The types of values supported by HealthStats.
55      */
56     public static final int TYPE_TIMER = 0;
57     public static final int TYPE_MEASUREMENT = 1;
58     public static final int TYPE_STATS = 2;
59     public static final int TYPE_TIMERS = 3;
60     public static final int TYPE_MEASUREMENTS = 4;
61 
62     public static final int TYPE_COUNT = 5;
63 
64     /**
65      * Annotation to mark public static final int fields that are to be used
66      * as field keys in HealthStats.
67      */
68     @Retention(RetentionPolicy.RUNTIME)
69     @Target({ElementType.FIELD})
70     public @interface Constant {
71         /**
72          * One of the TYPE_* constants above.
73          */
type()74         int type();
75     }
76 
77     /**
78      * Class to gather the constants defined in a class full of constants and
79      * build the key indices used by HealthStatsWriter and HealthStats.
80      *
81      * @hide
82      */
83     @TestApi
84     public static class Constants {
85         private final String mDataType;
86         private final int[][] mKeys = new int[TYPE_COUNT][];
87 
88         /**
89          * Pass in a class to gather the public static final int fields that are
90          * tagged with the @Constant annotation.
91          */
Constants(Class clazz)92         public Constants(Class clazz) {
93             // Save the class name for debugging
94             mDataType = clazz.getSimpleName();
95 
96             // Iterate through the list of fields on this class, and build the
97             // constant arrays for these fields.
98             final Field[] fields = clazz.getDeclaredFields();
99             final Class<Constant> annotationClass = Constant.class;
100 
101             final int N = fields.length;
102 
103             final SortedIntArray[] keys = new SortedIntArray[mKeys.length];
104             for (int i=0; i<keys.length; i++) {
105                 keys[i] = new SortedIntArray(N);
106             }
107 
108             for (int i=0; i<N; i++) {
109                 final Field field = fields[i];
110                 final Constant constant = field.getAnnotation(annotationClass);
111                 if (constant != null) {
112                     final int type = constant.type();
113                     if (type >= keys.length) {
114                         throw new RuntimeException("Unknown Constant type " + type
115                                 + " on " + field);
116                     }
117                     try {
118                         keys[type].addValue(field.getInt(null));
119                     } catch (IllegalAccessException ex) {
120                         throw new RuntimeException("Can't read constant value type=" + type
121                                 + " field=" + field, ex);
122                     }
123                 }
124             }
125 
126             for (int i=0; i<keys.length; i++) {
127                 mKeys[i] = keys[i].getArray();
128             }
129         }
130 
131         /**
132          * Get a string representation of this class. Useful for debugging. It will be the
133          * simple name of the class passed in the constructor.
134          */
getDataType()135         public String getDataType() {
136             return mDataType;
137         }
138 
139         /**
140          * Return how many keys there are for the given field type.
141          *
142          * @see TYPE_TIMER
143          * @see TYPE_MEASUREMENT
144          * @see TYPE_TIMERS
145          * @see TYPE_MEASUREMENTS
146          * @see TYPE_STATS
147          */
getSize(int type)148         public int getSize(int type) {
149             return mKeys[type].length;
150         }
151 
152         /**
153          * Return the index for the given type and key combination in the array of field
154          * keys or values.
155          *
156          * @see TYPE_TIMER
157          * @see TYPE_MEASUREMENT
158          * @see TYPE_TIMERS
159          * @see TYPE_MEASUREMENTS
160          * @see TYPE_STATS
161          */
getIndex(int type, int key)162         public int getIndex(int type, int key) {
163             final int index = Arrays.binarySearch(mKeys[type], key);
164             if (index >= 0) {
165                 return index;
166             } else {
167                 throw new RuntimeException("Unknown Constant " + key + " (of type "
168                         + type + " )");
169             }
170         }
171 
172         /**
173          * Get the array of keys for the given field type.
174          */
getKeys(int type)175         public int[] getKeys(int type) {
176             return mKeys[type];
177         }
178     }
179 
180     /**
181      * An array of fixed size that will be sorted.
182      */
183     private static class SortedIntArray {
184         int mCount;
185         int[] mArray;
186 
187         /**
188          * Construct with the maximum number of values.
189          */
SortedIntArray(int maxCount)190         SortedIntArray(int maxCount) {
191             mArray = new int[maxCount];
192         }
193 
194         /**
195          * Add a value.
196          */
addValue(int value)197         void addValue(int value) {
198             mArray[mCount++] = value;
199         }
200 
201         /**
202          * Get the array of values that have been added, with the values in
203          * numerically increasing order.
204          */
getArray()205         int[] getArray() {
206             if (mCount == mArray.length) {
207                 Arrays.sort(mArray);
208                 return mArray;
209             } else {
210                 final int[] result = new int[mCount];
211                 System.arraycopy(mArray, 0, result, 0, mCount);
212                 Arrays.sort(result);
213                 return result;
214             }
215         }
216     }
217 }
218 
219 
220