blob: b94cb60ffa007bc862d723880c342ef1ddf086d7 [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
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -070018import android.annotation.NonNull;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070019import android.annotation.SystemApi;
20import android.os.Parcel;
21import android.os.Parcelable;
22
Sanket Padawe06e1ae12016-05-18 16:51:49 -070023import java.util.Arrays;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070024
25/**
26 * Parcelable class to store Telephony histogram.
27 * @hide
28 */
29@SystemApi
30public final class TelephonyHistogram implements Parcelable {
31 // Type of Telephony histogram Eg: RIL histogram will have all timing data associated with
32 // RIL calls. Similarly we can have any other Telephony histogram.
Sanket Padawe0680f732016-06-15 15:59:25 -070033 private final int mCategory;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070034
35 // Unique Id identifying a sample within particular category of histogram
Sanket Padawe0680f732016-06-15 15:59:25 -070036 private final int mId;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070037
38 // Min time taken in ms
Sanket Padawe0680f732016-06-15 15:59:25 -070039 private int mMinTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070040
41 // Max time taken in ms
Sanket Padawe0680f732016-06-15 15:59:25 -070042 private int mMaxTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070043
44 // Average time taken in ms
Sanket Padawe0680f732016-06-15 15:59:25 -070045 private int mAverageTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070046
47 // Total count of samples
Sanket Padawe0680f732016-06-15 15:59:25 -070048 private int mSampleCount;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070049
50 // Array storing time taken for first #RANGE_CALCULATION_COUNT samples of histogram.
Sanket Padawe0680f732016-06-15 15:59:25 -070051 private int[] mInitialTimings;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070052
53 // Total number of time ranges expected (must be greater than 1)
Sanket Padawe0680f732016-06-15 15:59:25 -070054 private final int mBucketCount;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070055
56 // Array storing endpoints of range buckets. Calculated based on values of minTime & maxTime
57 // after totalTimeCount is #RANGE_CALCULATION_COUNT.
Sanket Padawe0680f732016-06-15 15:59:25 -070058 private final int[] mBucketEndPoints;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070059
60 // Array storing counts for each time range starting from smallest value range
Sanket Padawe0680f732016-06-15 15:59:25 -070061 private final int[] mBucketCounters;
Sanket Padawe06e1ae12016-05-18 16:51:49 -070062
63 /**
64 * Constant for Telephony category
65 */
66 public static final int TELEPHONY_CATEGORY_RIL = 1;
67
68 // Count of Histogram samples after which time buckets are created.
69 private static final int RANGE_CALCULATION_COUNT = 10;
70
71
72 // Constant used to indicate #initialTimings is null while parceling
73 private static final int ABSENT = 0;
74
75 // Constant used to indicate #initialTimings is not null while parceling
76 private static final int PRESENT = 1;
77
78 // Throws exception if #totalBuckets is not greater than one.
79 public TelephonyHistogram (int category, int id, int bucketCount) {
80 if (bucketCount <= 1) {
81 throw new IllegalArgumentException("Invalid number of buckets");
82 }
Sanket Padawe0680f732016-06-15 15:59:25 -070083 mCategory = category;
84 mId = id;
85 mMinTimeMs = Integer.MAX_VALUE;
86 mMaxTimeMs = 0;
87 mAverageTimeMs = 0;
88 mSampleCount = 0;
89 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
90 mBucketCount = bucketCount;
91 mBucketEndPoints = new int[bucketCount - 1];
92 mBucketCounters = new int[bucketCount];
Sanket Padawe06e1ae12016-05-18 16:51:49 -070093 }
94
95 public TelephonyHistogram(TelephonyHistogram th) {
Sanket Padawe0680f732016-06-15 15:59:25 -070096 mCategory = th.getCategory();
97 mId = th.getId();
98 mMinTimeMs = th.getMinTime();
99 mMaxTimeMs = th.getMaxTime();
100 mAverageTimeMs = th.getAverageTime();
101 mSampleCount = th.getSampleCount();
102 mInitialTimings = th.getInitialTimings();
103 mBucketCount = th.getBucketCount();
104 mBucketEndPoints = th.getBucketEndPoints();
105 mBucketCounters = th.getBucketCounters();
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700106 }
107
108 public int getCategory() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700109 return mCategory;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700110 }
111
112 public int getId() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700113 return mId;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700114 }
115
116 public int getMinTime() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700117 return mMinTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700118 }
119
120 public int getMaxTime() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700121 return mMaxTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700122 }
123
124 public int getAverageTime() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700125 return mAverageTimeMs;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700126 }
127
128 public int getSampleCount () {
Sanket Padawe0680f732016-06-15 15:59:25 -0700129 return mSampleCount;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700130 }
131
132 private int[] getInitialTimings() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700133 return mInitialTimings;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700134 }
135
136 public int getBucketCount() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700137 return mBucketCount;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700138 }
139
140 public int[] getBucketEndPoints() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700141 if (mSampleCount > 1 && mSampleCount < 10) {
142 int[] tempEndPoints = new int[mBucketCount - 1];
143 calculateBucketEndPoints(tempEndPoints);
144 return tempEndPoints;
145 } else {
146 return getDeepCopyOfArray(mBucketEndPoints);
147 }
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700148 }
149
150 public int[] getBucketCounters() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700151 if (mSampleCount > 1 && mSampleCount < 10) {
152 int[] tempEndPoints = new int[mBucketCount - 1];
153 int[] tempBucketCounters = new int[mBucketCount];
154 calculateBucketEndPoints(tempEndPoints);
155 for (int j = 0; j < mSampleCount; j++) {
156 addToBucketCounter(tempEndPoints, tempBucketCounters, mInitialTimings[j]);
157 }
158 return tempBucketCounters;
159 } else {
160 return getDeepCopyOfArray(mBucketCounters);
161 }
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700162 }
163
164 private int[] getDeepCopyOfArray(int[] array) {
165 int[] clone = new int[array.length];
166 System.arraycopy(array, 0, clone, 0, array.length);
167 return clone;
168 }
169
Sanket Padawe0680f732016-06-15 15:59:25 -0700170 private void addToBucketCounter(int[] bucketEndPoints, int[] bucketCounters, int time) {
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700171 int i;
172 for (i = 0; i < bucketEndPoints.length; i++) {
173 if (time <= bucketEndPoints[i]) {
174 bucketCounters[i]++;
175 return;
176 }
177 }
178 bucketCounters[i]++;
179 }
180
Sanket Padawe0680f732016-06-15 15:59:25 -0700181 private void calculateBucketEndPoints(int[] bucketEndPoints) {
182 for (int i = 1; i < mBucketCount; i++) {
183 int endPt = mMinTimeMs + (i * (mMaxTimeMs - mMinTimeMs)) / mBucketCount;
184 bucketEndPoints[i - 1] = endPt;
185 }
186 }
187
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700188 // Add new value of time taken
189 // This function updates minTime, maxTime, averageTime & totalTimeCount every time it is
190 // called. initialTimings[] is updated if totalTimeCount <= #RANGE_CALCULATION_COUNT. When
191 // totalTimeCount = RANGE_CALCULATION_COUNT, based on the min, max time & the number of buckets
192 // expected, bucketEndPoints[] would be calculated. Then bucketCounters[] would be filled up
193 // using values stored in initialTimings[]. Thereafter bucketCounters[] will always be updated.
194 public void addTimeTaken(int time) {
195 // Initialize all fields if its first entry or if integer overflow is going to occur while
196 // trying to calculate averageTime
Sanket Padawe0680f732016-06-15 15:59:25 -0700197 if (mSampleCount == 0 || (mSampleCount == Integer.MAX_VALUE)) {
198 if (mSampleCount == 0) {
199 mMinTimeMs = time;
200 mMaxTimeMs = time;
201 mAverageTimeMs = time;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700202 } else {
Sanket Padawe0680f732016-06-15 15:59:25 -0700203 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700204 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700205 mSampleCount = 1;
206 Arrays.fill(mInitialTimings, 0);
207 mInitialTimings[0] = time;
208 Arrays.fill(mBucketEndPoints, 0);
209 Arrays.fill(mBucketCounters, 0);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700210 } else {
Sanket Padawe0680f732016-06-15 15:59:25 -0700211 if (time < mMinTimeMs) {
212 mMinTimeMs = time;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700213 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700214 if (time > mMaxTimeMs) {
215 mMaxTimeMs = time;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700216 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700217 long totalTime = ((long)mAverageTimeMs) * mSampleCount + time;
218 mAverageTimeMs = (int)(totalTime/++mSampleCount);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700219
Sanket Padawe0680f732016-06-15 15:59:25 -0700220 if (mSampleCount < RANGE_CALCULATION_COUNT) {
221 mInitialTimings[mSampleCount - 1] = time;
222 } else if (mSampleCount == RANGE_CALCULATION_COUNT) {
223 mInitialTimings[mSampleCount - 1] = time;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700224
225 // Calculate bucket endpoints based on bucketCount expected
Sanket Padawe0680f732016-06-15 15:59:25 -0700226 calculateBucketEndPoints(mBucketEndPoints);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700227
228 // Use values stored in initialTimings[] to update bucketCounters
229 for (int j = 0; j < RANGE_CALCULATION_COUNT; j++) {
Sanket Padawe0680f732016-06-15 15:59:25 -0700230 addToBucketCounter(mBucketEndPoints, mBucketCounters, mInitialTimings[j]);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700231 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700232 mInitialTimings = null;
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700233 } else {
Sanket Padawe0680f732016-06-15 15:59:25 -0700234 addToBucketCounter(mBucketEndPoints, mBucketCounters, time);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700235 }
236
237 }
238 }
239
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -0700240 @NonNull
241 @Override
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700242 public String toString() {
Sanket Padawe0680f732016-06-15 15:59:25 -0700243 String basic = " Histogram id = " + mId + " Time(ms): min = " + mMinTimeMs + " max = "
244 + mMaxTimeMs + " avg = " + mAverageTimeMs + " Count = " + mSampleCount;
245 if (mSampleCount < RANGE_CALCULATION_COUNT) {
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700246 return basic;
247 } else {
248 StringBuffer intervals = new StringBuffer(" Interval Endpoints:");
Sanket Padawe0680f732016-06-15 15:59:25 -0700249 for (int i = 0; i < mBucketEndPoints.length; i++) {
250 intervals.append(" " + mBucketEndPoints[i]);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700251 }
252 intervals.append(" Interval counters:");
Sanket Padawe0680f732016-06-15 15:59:25 -0700253 for (int i = 0; i < mBucketCounters.length; i++) {
254 intervals.append(" " + mBucketCounters[i]);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700255 }
256 return basic + intervals;
257 }
258 }
259
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700260 public static final @android.annotation.NonNull Parcelable.Creator<TelephonyHistogram> CREATOR =
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700261 new Parcelable.Creator<TelephonyHistogram> () {
262
263 @Override
264 public TelephonyHistogram createFromParcel(Parcel in) {
265 return new TelephonyHistogram(in);
266 }
267
268 @Override
269 public TelephonyHistogram[] newArray(int size) {
270 return new TelephonyHistogram[size];
271 }
272 };
273
274 public TelephonyHistogram(Parcel in) {
Sanket Padawe0680f732016-06-15 15:59:25 -0700275 mCategory = in.readInt();
276 mId = in.readInt();
277 mMinTimeMs = in.readInt();
278 mMaxTimeMs = in.readInt();
279 mAverageTimeMs = in.readInt();
280 mSampleCount = in.readInt();
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700281 if (in.readInt() == PRESENT) {
Sanket Padawe0680f732016-06-15 15:59:25 -0700282 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
283 in.readIntArray(mInitialTimings);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700284 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700285 mBucketCount = in.readInt();
286 mBucketEndPoints = new int[mBucketCount - 1];
287 in.readIntArray(mBucketEndPoints);
288 mBucketCounters = new int[mBucketCount];
289 in.readIntArray(mBucketCounters);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700290 }
291
292 public void writeToParcel(Parcel out, int flags) {
Sanket Padawe0680f732016-06-15 15:59:25 -0700293 out.writeInt(mCategory);
294 out.writeInt(mId);
295 out.writeInt(mMinTimeMs);
296 out.writeInt(mMaxTimeMs);
297 out.writeInt(mAverageTimeMs);
298 out.writeInt(mSampleCount);
299 if (mInitialTimings == null) {
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700300 out.writeInt(ABSENT);
301 } else {
302 out.writeInt(PRESENT);
Sanket Padawe0680f732016-06-15 15:59:25 -0700303 out.writeIntArray(mInitialTimings);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700304 }
Sanket Padawe0680f732016-06-15 15:59:25 -0700305 out.writeInt(mBucketCount);
306 out.writeIntArray(mBucketEndPoints);
307 out.writeIntArray(mBucketCounters);
Sanket Padawe06e1ae12016-05-18 16:51:49 -0700308 }
309
310 @Override
311 public int describeContents() {
312 return 0;
313 }
314}