2 * Copyright (C) 2016 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package android.telephony;
18 import android.annotation.SystemApi;
19 import android.os.Parcel;
20 import android.os.Parcelable;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
27 * Parcelable class to store Telephony histogram.
31 public final class TelephonyHistogram implements Parcelable {
32 // Type of Telephony histogram Eg: RIL histogram will have all timing data associated with
33 // RIL calls. Similarly we can have any other Telephony histogram.
34 private final int mCategory;
36 // Unique Id identifying a sample within particular category of histogram
37 private final int mId;
39 // Min time taken in ms
40 private int mMinTimeMs;
42 // Max time taken in ms
43 private int mMaxTimeMs;
45 // Average time taken in ms
46 private int mAverageTimeMs;
48 // Total count of samples
49 private int mSampleCount;
51 // Array storing time taken for first #RANGE_CALCULATION_COUNT samples of histogram.
52 private int[] mInitialTimings;
54 // Total number of time ranges expected (must be greater than 1)
55 private final int mBucketCount;
57 // Array storing endpoints of range buckets. Calculated based on values of minTime & maxTime
58 // after totalTimeCount is #RANGE_CALCULATION_COUNT.
59 private final int[] mBucketEndPoints;
61 // Array storing counts for each time range starting from smallest value range
62 private final int[] mBucketCounters;
65 * Constant for Telephony category
67 public static final int TELEPHONY_CATEGORY_RIL = 1;
69 // Count of Histogram samples after which time buckets are created.
70 private static final int RANGE_CALCULATION_COUNT = 10;
73 // Constant used to indicate #initialTimings is null while parceling
74 private static final int ABSENT = 0;
76 // Constant used to indicate #initialTimings is not null while parceling
77 private static final int PRESENT = 1;
79 // Throws exception if #totalBuckets is not greater than one.
80 public TelephonyHistogram (int category, int id, int bucketCount) {
81 if (bucketCount <= 1) {
82 throw new IllegalArgumentException("Invalid number of buckets");
86 mMinTimeMs = Integer.MAX_VALUE;
90 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
91 mBucketCount = bucketCount;
92 mBucketEndPoints = new int[bucketCount - 1];
93 mBucketCounters = new int[bucketCount];
96 public TelephonyHistogram(TelephonyHistogram th) {
97 mCategory = th.getCategory();
99 mMinTimeMs = th.getMinTime();
100 mMaxTimeMs = th.getMaxTime();
101 mAverageTimeMs = th.getAverageTime();
102 mSampleCount = th.getSampleCount();
103 mInitialTimings = th.getInitialTimings();
104 mBucketCount = th.getBucketCount();
105 mBucketEndPoints = th.getBucketEndPoints();
106 mBucketCounters = th.getBucketCounters();
109 public int getCategory() {
117 public int getMinTime() {
121 public int getMaxTime() {
125 public int getAverageTime() {
126 return mAverageTimeMs;
129 public int getSampleCount () {
133 private int[] getInitialTimings() {
134 return mInitialTimings;
137 public int getBucketCount() {
141 public int[] getBucketEndPoints() {
142 if (mSampleCount > 1 && mSampleCount < 10) {
143 int[] tempEndPoints = new int[mBucketCount - 1];
144 calculateBucketEndPoints(tempEndPoints);
145 return tempEndPoints;
147 return getDeepCopyOfArray(mBucketEndPoints);
151 public int[] getBucketCounters() {
152 if (mSampleCount > 1 && mSampleCount < 10) {
153 int[] tempEndPoints = new int[mBucketCount - 1];
154 int[] tempBucketCounters = new int[mBucketCount];
155 calculateBucketEndPoints(tempEndPoints);
156 for (int j = 0; j < mSampleCount; j++) {
157 addToBucketCounter(tempEndPoints, tempBucketCounters, mInitialTimings[j]);
159 return tempBucketCounters;
161 return getDeepCopyOfArray(mBucketCounters);
165 private int[] getDeepCopyOfArray(int[] array) {
166 int[] clone = new int[array.length];
167 System.arraycopy(array, 0, clone, 0, array.length);
171 private void addToBucketCounter(int[] bucketEndPoints, int[] bucketCounters, int time) {
173 for (i = 0; i < bucketEndPoints.length; i++) {
174 if (time <= bucketEndPoints[i]) {
182 private void calculateBucketEndPoints(int[] bucketEndPoints) {
183 for (int i = 1; i < mBucketCount; i++) {
184 int endPt = mMinTimeMs + (i * (mMaxTimeMs - mMinTimeMs)) / mBucketCount;
185 bucketEndPoints[i - 1] = endPt;
189 // Add new value of time taken
190 // This function updates minTime, maxTime, averageTime & totalTimeCount every time it is
191 // called. initialTimings[] is updated if totalTimeCount <= #RANGE_CALCULATION_COUNT. When
192 // totalTimeCount = RANGE_CALCULATION_COUNT, based on the min, max time & the number of buckets
193 // expected, bucketEndPoints[] would be calculated. Then bucketCounters[] would be filled up
194 // using values stored in initialTimings[]. Thereafter bucketCounters[] will always be updated.
195 public void addTimeTaken(int time) {
196 // Initialize all fields if its first entry or if integer overflow is going to occur while
197 // trying to calculate averageTime
198 if (mSampleCount == 0 || (mSampleCount == Integer.MAX_VALUE)) {
199 if (mSampleCount == 0) {
202 mAverageTimeMs = time;
204 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
207 Arrays.fill(mInitialTimings, 0);
208 mInitialTimings[0] = time;
209 Arrays.fill(mBucketEndPoints, 0);
210 Arrays.fill(mBucketCounters, 0);
212 if (time < mMinTimeMs) {
215 if (time > mMaxTimeMs) {
218 long totalTime = ((long)mAverageTimeMs) * mSampleCount + time;
219 mAverageTimeMs = (int)(totalTime/++mSampleCount);
221 if (mSampleCount < RANGE_CALCULATION_COUNT) {
222 mInitialTimings[mSampleCount - 1] = time;
223 } else if (mSampleCount == RANGE_CALCULATION_COUNT) {
224 mInitialTimings[mSampleCount - 1] = time;
226 // Calculate bucket endpoints based on bucketCount expected
227 calculateBucketEndPoints(mBucketEndPoints);
229 // Use values stored in initialTimings[] to update bucketCounters
230 for (int j = 0; j < RANGE_CALCULATION_COUNT; j++) {
231 addToBucketCounter(mBucketEndPoints, mBucketCounters, mInitialTimings[j]);
233 mInitialTimings = null;
235 addToBucketCounter(mBucketEndPoints, mBucketCounters, time);
241 public String toString() {
242 String basic = " Histogram id = " + mId + " Time(ms): min = " + mMinTimeMs + " max = "
243 + mMaxTimeMs + " avg = " + mAverageTimeMs + " Count = " + mSampleCount;
244 if (mSampleCount < RANGE_CALCULATION_COUNT) {
247 StringBuffer intervals = new StringBuffer(" Interval Endpoints:");
248 for (int i = 0; i < mBucketEndPoints.length; i++) {
249 intervals.append(" " + mBucketEndPoints[i]);
251 intervals.append(" Interval counters:");
252 for (int i = 0; i < mBucketCounters.length; i++) {
253 intervals.append(" " + mBucketCounters[i]);
255 return basic + intervals;
259 public static final Parcelable.Creator<TelephonyHistogram> CREATOR =
260 new Parcelable.Creator<TelephonyHistogram> () {
263 public TelephonyHistogram createFromParcel(Parcel in) {
264 return new TelephonyHistogram(in);
268 public TelephonyHistogram[] newArray(int size) {
269 return new TelephonyHistogram[size];
273 public TelephonyHistogram(Parcel in) {
274 mCategory = in.readInt();
276 mMinTimeMs = in.readInt();
277 mMaxTimeMs = in.readInt();
278 mAverageTimeMs = in.readInt();
279 mSampleCount = in.readInt();
280 if (in.readInt() == PRESENT) {
281 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
282 in.readIntArray(mInitialTimings);
284 mBucketCount = in.readInt();
285 mBucketEndPoints = new int[mBucketCount - 1];
286 in.readIntArray(mBucketEndPoints);
287 mBucketCounters = new int[mBucketCount];
288 in.readIntArray(mBucketCounters);
291 public void writeToParcel(Parcel out, int flags) {
292 out.writeInt(mCategory);
294 out.writeInt(mMinTimeMs);
295 out.writeInt(mMaxTimeMs);
296 out.writeInt(mAverageTimeMs);
297 out.writeInt(mSampleCount);
298 if (mInitialTimings == null) {
299 out.writeInt(ABSENT);
301 out.writeInt(PRESENT);
302 out.writeIntArray(mInitialTimings);
304 out.writeInt(mBucketCount);
305 out.writeIntArray(mBucketEndPoints);
306 out.writeIntArray(mBucketCounters);
310 public int describeContents() {