blob: 63bdac514477196fb24e8d08aba320fca8ba8d48 [file] [log] [blame]
Sanket Padawe06e1ae12016-05-18 16:51:49 -07001/*
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 */
16package android.telephony;
17
18import android.annotation.SystemApi;
19import android.os.Parcel;
20import android.os.Parcelable;
21
22import java.util.ArrayList;
23import java.util.Arrays;
24import java.util.List;
25
26/**
27 * Parcelable class to store Telephony histogram.
28 * @hide
29 */
30@SystemApi
31public 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.
Sanket Padawe0680f732016-06-15 15:59:25 -070034 private final int mCategory;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070035
36 // Unique Id identifying a sample within particular category of histogram
Sanket Padawe0680f732016-06-15 15:59:25 -070037 private final int mId;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070038
39 // Min time taken in ms
Sanket Padawe0680f732016-06-15 15:59:25 -070040 private int mMinTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070041
42 // Max time taken in ms
Sanket Padawe0680f732016-06-15 15:59:25 -070043 private int mMaxTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070044
45 // Average time taken in ms
Sanket Padawe0680f732016-06-15 15:59:25 -070046 private int mAverageTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070047
48 // Total count of samples
Sanket Padawe0680f732016-06-15 15:59:25 -070049 private int mSampleCount;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070050
51 // Array storing time taken for first #RANGE_CALCULATION_COUNT samples of histogram.
Sanket Padawe0680f732016-06-15 15:59:25 -070052 private int[] mInitialTimings;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070053
54 // Total number of time ranges expected (must be greater than 1)
Sanket Padawe0680f732016-06-15 15:59:25 -070055 private final int mBucketCount;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070056
57 // Array storing endpoints of range buckets. Calculated based on values of minTime & maxTime
58 // after totalTimeCount is #RANGE_CALCULATION_COUNT.
Sanket Padawe0680f732016-06-15 15:59:25 -070059 private final int[] mBucketEndPoints;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070060
61 // Array storing counts for each time range starting from smallest value range
Sanket Padawe0680f732016-06-15 15:59:25 -070062 private final int[] mBucketCounters;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070063
64 /**
65 * Constant for Telephony category
66 */
67 public static final int TELEPHONY_CATEGORY_RIL = 1;
68
69 // Count of Histogram samples after which time buckets are created.
70 private static final int RANGE_CALCULATION_COUNT = 10;
71
72
73 // Constant used to indicate #initialTimings is null while parceling
74 private static final int ABSENT = 0;
75
76 // Constant used to indicate #initialTimings is not null while parceling
77 private static final int PRESENT = 1;
78
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");
83 }
Sanket Padawe0680f732016-06-15 15:59:25 -070084 mCategory = category;
85 mId = id;
86 mMinTimeMs = Integer.MAX_VALUE;
87 mMaxTimeMs = 0;
88 mAverageTimeMs = 0;
89 mSampleCount = 0;
90 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
91 mBucketCount = bucketCount;
92 mBucketEndPoints = new int[bucketCount - 1];
93 mBucketCounters = new int[bucketCount];
Sanket Padawe06e1ae12016-05-18 16:51:49 -070094 }
95
96 public TelephonyHistogram(TelephonyHistogram th) {
Sanket Padawe0680f732016-06-15 15:59:25 -070097 mCategory = th.getCategory();
98 mId = th.getId();
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();
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700107 }
108
109 public int getCategory() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700110 return mCategory;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700111 }
112
113 public int getId() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700114 return mId;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700115 }
116
117 public int getMinTime() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700118 return mMinTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700119 }
120
121 public int getMaxTime() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700122 return mMaxTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700123 }
124
125 public int getAverageTime() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700126 return mAverageTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700127 }
128
129 public int getSampleCount () {
Sanket Padawe0680f732016-06-15 15:59:25 -0700130 return mSampleCount;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700131 }
132
133 private int[] getInitialTimings() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700134 return mInitialTimings;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700135 }
136
137 public int getBucketCount() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700138 return mBucketCount;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700139 }
140
141 public int[] getBucketEndPoints() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700142 if (mSampleCount > 1 && mSampleCount < 10) {
143 int[] tempEndPoints = new int[mBucketCount - 1];
144 calculateBucketEndPoints(tempEndPoints);
145 return tempEndPoints;
146 } else {
147 return getDeepCopyOfArray(mBucketEndPoints);
148 }
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700149 }
150
151 public int[] getBucketCounters() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700152 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]);
158 }
159 return tempBucketCounters;
160 } else {
161 return getDeepCopyOfArray(mBucketCounters);
162 }
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700163 }
164
165 private int[] getDeepCopyOfArray(int[] array) {
166 int[] clone = new int[array.length];
167 System.arraycopy(array, 0, clone, 0, array.length);
168 return clone;
169 }
170
Sanket Padawe0680f732016-06-15 15:59:25 -0700171 private void addToBucketCounter(int[] bucketEndPoints, int[] bucketCounters, int time) {
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700172 int i;
173 for (i = 0; i < bucketEndPoints.length; i++) {
174 if (time <= bucketEndPoints[i]) {
175 bucketCounters[i]++;
176 return;
177 }
178 }
179 bucketCounters[i]++;
180 }
181
Sanket Padawe0680f732016-06-15 15:59:25 -0700182 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;
186 }
187 }
188
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700189 // 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
Sanket Padawe0680f732016-06-15 15:59:25 -0700198 if (mSampleCount == 0 || (mSampleCount == Integer.MAX_VALUE)) {
199 if (mSampleCount == 0) {
200 mMinTimeMs = time;
201 mMaxTimeMs = time;
202 mAverageTimeMs = time;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700203 } else {
Sanket Padawe0680f732016-06-15 15:59:25 -0700204 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700205 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700206 mSampleCount = 1;
207 Arrays.fill(mInitialTimings, 0);
208 mInitialTimings[0] = time;
209 Arrays.fill(mBucketEndPoints, 0);
210 Arrays.fill(mBucketCounters, 0);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700211 } else {
Sanket Padawe0680f732016-06-15 15:59:25 -0700212 if (time < mMinTimeMs) {
213 mMinTimeMs = time;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700214 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700215 if (time > mMaxTimeMs) {
216 mMaxTimeMs = time;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700217 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700218 long totalTime = ((long)mAverageTimeMs) * mSampleCount + time;
219 mAverageTimeMs = (int)(totalTime/++mSampleCount);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700220
Sanket Padawe0680f732016-06-15 15:59:25 -0700221 if (mSampleCount < RANGE_CALCULATION_COUNT) {
222 mInitialTimings[mSampleCount - 1] = time;
223 } else if (mSampleCount == RANGE_CALCULATION_COUNT) {
224 mInitialTimings[mSampleCount - 1] = time;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700225
226 // Calculate bucket endpoints based on bucketCount expected
Sanket Padawe0680f732016-06-15 15:59:25 -0700227 calculateBucketEndPoints(mBucketEndPoints);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700228
229 // Use values stored in initialTimings[] to update bucketCounters
230 for (int j = 0; j < RANGE_CALCULATION_COUNT; j++) {
Sanket Padawe0680f732016-06-15 15:59:25 -0700231 addToBucketCounter(mBucketEndPoints, mBucketCounters, mInitialTimings[j]);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700232 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700233 mInitialTimings = null;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700234 } else {
Sanket Padawe0680f732016-06-15 15:59:25 -0700235 addToBucketCounter(mBucketEndPoints, mBucketCounters, time);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700236 }
237
238 }
239 }
240
241 public String toString() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700242 String basic = " Histogram id = " + mId + " Time(ms): min = " + mMinTimeMs + " max = "
243 + mMaxTimeMs + " avg = " + mAverageTimeMs + " Count = " + mSampleCount;
244 if (mSampleCount < RANGE_CALCULATION_COUNT) {
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700245 return basic;
246 } else {
247 StringBuffer intervals = new StringBuffer(" Interval Endpoints:");
Sanket Padawe0680f732016-06-15 15:59:25 -0700248 for (int i = 0; i < mBucketEndPoints.length; i++) {
249 intervals.append(" " + mBucketEndPoints[i]);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700250 }
251 intervals.append(" Interval counters:");
Sanket Padawe0680f732016-06-15 15:59:25 -0700252 for (int i = 0; i < mBucketCounters.length; i++) {
253 intervals.append(" " + mBucketCounters[i]);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700254 }
255 return basic + intervals;
256 }
257 }
258
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700259 public static final @android.annotation.NonNull Parcelable.Creator<TelephonyHistogram> CREATOR =
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700260 new Parcelable.Creator<TelephonyHistogram> () {
261
262 @Override
263 public TelephonyHistogram createFromParcel(Parcel in) {
264 return new TelephonyHistogram(in);
265 }
266
267 @Override
268 public TelephonyHistogram[] newArray(int size) {
269 return new TelephonyHistogram[size];
270 }
271 };
272
273 public TelephonyHistogram(Parcel in) {
Sanket Padawe0680f732016-06-15 15:59:25 -0700274 mCategory = in.readInt();
275 mId = in.readInt();
276 mMinTimeMs = in.readInt();
277 mMaxTimeMs = in.readInt();
278 mAverageTimeMs = in.readInt();
279 mSampleCount = in.readInt();
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700280 if (in.readInt() == PRESENT) {
Sanket Padawe0680f732016-06-15 15:59:25 -0700281 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
282 in.readIntArray(mInitialTimings);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700283 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700284 mBucketCount = in.readInt();
285 mBucketEndPoints = new int[mBucketCount - 1];
286 in.readIntArray(mBucketEndPoints);
287 mBucketCounters = new int[mBucketCount];
288 in.readIntArray(mBucketCounters);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700289 }
290
291 public void writeToParcel(Parcel out, int flags) {
Sanket Padawe0680f732016-06-15 15:59:25 -0700292 out.writeInt(mCategory);
293 out.writeInt(mId);
294 out.writeInt(mMinTimeMs);
295 out.writeInt(mMaxTimeMs);
296 out.writeInt(mAverageTimeMs);
297 out.writeInt(mSampleCount);
298 if (mInitialTimings == null) {
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700299 out.writeInt(ABSENT);
300 } else {
301 out.writeInt(PRESENT);
Sanket Padawe0680f732016-06-15 15:59:25 -0700302 out.writeIntArray(mInitialTimings);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700303 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700304 out.writeInt(mBucketCount);
305 out.writeIntArray(mBucketEndPoints);
306 out.writeIntArray(mBucketCounters);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700307 }
308
309 @Override
310 public int describeContents() {
311 return 0;
312 }
313}