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;
21
22import java.lang.annotation.Annotation;
23import java.lang.annotation.ElementType;
24import java.lang.annotation.Retention;
25import java.lang.annotation.RetentionPolicy;
26import java.lang.annotation.Target;
27import java.lang.reflect.Field;
28import java.util.Arrays;
29
30/**
31 * Constants and stuff for the android.os.health package.
32 *
33 * @hide
34 */
35public class HealthKeys {
36
37    /**
38     * No valid key will ever be 0.
39     */
40    public static final int UNKNOWN_KEY = 0;
41
42    /*
43     * Base key for each of the different classes. There is
44     * nothing intrinsic to the operation of the value of the
45     * keys. It's just segmented for better debugging. The
46     * classes don't mix them anway.
47     */
48    public static final int BASE_UID = 10000;
49    public static final int BASE_PID = 20000;
50    public static final int BASE_PROCESS = 30000;
51    public static final int BASE_PACKAGE = 40000;
52    public static final int BASE_SERVICE = 50000;
53
54    /*
55     * The types of values supported by HealthStats.
56     */
57    public static final int TYPE_TIMER = 0;
58    public static final int TYPE_MEASUREMENT = 1;
59    public static final int TYPE_STATS = 2;
60    public static final int TYPE_TIMERS = 3;
61    public static final int TYPE_MEASUREMENTS = 4;
62
63    public static final int TYPE_COUNT = 5;
64
65    /**
66     * Annotation to mark public static final int fields that are to be used
67     * as field keys in HealthStats.
68     */
69    @Retention(RetentionPolicy.RUNTIME)
70    @Target({ElementType.FIELD})
71    public @interface Constant {
72        /**
73         * One of the TYPE_* constants above.
74         */
75        int type();
76    }
77
78    /**
79     * Class to gather the constants defined in a class full of constants and
80     * build the key indices used by HealthStatsWriter and HealthStats.
81     *
82     * @hide
83     */
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         */
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         */
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         */
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         */
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         */
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         */
190        SortedIntArray(int maxCount) {
191            mArray = new int[maxCount];
192        }
193
194        /**
195         * Add a value.
196         */
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         */
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