Home | History | Annotate | Download | only in telephony
      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 package android.telephony;
     17 
     18 import android.annotation.SystemApi;
     19 import android.os.Parcel;
     20 import android.os.Parcelable;
     21 
     22 import java.util.ArrayList;
     23 import java.util.Arrays;
     24 import java.util.List;
     25 
     26 /**
     27  * Parcelable class to store Telephony histogram.
     28  * @hide
     29  */
     30 @SystemApi
     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;
     35 
     36     // Unique Id identifying a sample within particular category of histogram
     37     private final int mId;
     38 
     39     // Min time taken in ms
     40     private int mMinTimeMs;
     41 
     42     // Max time taken in ms
     43     private int mMaxTimeMs;
     44 
     45     // Average time taken in ms
     46     private int mAverageTimeMs;
     47 
     48     // Total count of samples
     49     private int mSampleCount;
     50 
     51     // Array storing time taken for first #RANGE_CALCULATION_COUNT samples of histogram.
     52     private int[] mInitialTimings;
     53 
     54     // Total number of time ranges expected (must be greater than 1)
     55     private final int mBucketCount;
     56 
     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;
     60 
     61     // Array storing counts for each time range starting from smallest value range
     62     private final int[] mBucketCounters;
     63 
     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         }
     84         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];
     94     }
     95 
     96     public TelephonyHistogram(TelephonyHistogram th) {
     97         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();
    107     }
    108 
    109     public int getCategory() {
    110         return mCategory;
    111     }
    112 
    113     public int getId() {
    114         return mId;
    115     }
    116 
    117     public int getMinTime() {
    118         return mMinTimeMs;
    119     }
    120 
    121     public int getMaxTime() {
    122         return mMaxTimeMs;
    123     }
    124 
    125     public int getAverageTime() {
    126         return mAverageTimeMs;
    127     }
    128 
    129     public int getSampleCount () {
    130         return mSampleCount;
    131     }
    132 
    133     private int[] getInitialTimings() {
    134         return mInitialTimings;
    135     }
    136 
    137     public int getBucketCount() {
    138         return mBucketCount;
    139     }
    140 
    141     public int[] getBucketEndPoints() {
    142         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         }
    149     }
    150 
    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]);
    158             }
    159             return tempBucketCounters;
    160         } else {
    161             return getDeepCopyOfArray(mBucketCounters);
    162         }
    163     }
    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 
    171     private void addToBucketCounter(int[] bucketEndPoints, int[] bucketCounters, int time) {
    172         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 
    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;
    186         }
    187     }
    188 
    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) {
    200                 mMinTimeMs = time;
    201                 mMaxTimeMs = time;
    202                 mAverageTimeMs = time;
    203             } else {
    204                 mInitialTimings = new int[RANGE_CALCULATION_COUNT];
    205             }
    206             mSampleCount = 1;
    207             Arrays.fill(mInitialTimings, 0);
    208             mInitialTimings[0] = time;
    209             Arrays.fill(mBucketEndPoints, 0);
    210             Arrays.fill(mBucketCounters, 0);
    211         } else {
    212             if (time < mMinTimeMs) {
    213                 mMinTimeMs = time;
    214             }
    215             if (time > mMaxTimeMs) {
    216                 mMaxTimeMs = time;
    217             }
    218             long totalTime = ((long)mAverageTimeMs) * mSampleCount + time;
    219             mAverageTimeMs = (int)(totalTime/++mSampleCount);
    220 
    221             if (mSampleCount < RANGE_CALCULATION_COUNT) {
    222                 mInitialTimings[mSampleCount - 1] = time;
    223             } else if (mSampleCount == RANGE_CALCULATION_COUNT) {
    224                 mInitialTimings[mSampleCount - 1] = time;
    225 
    226                 // Calculate bucket endpoints based on bucketCount expected
    227                 calculateBucketEndPoints(mBucketEndPoints);
    228 
    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]);
    232                 }
    233                 mInitialTimings = null;
    234             } else {
    235                 addToBucketCounter(mBucketEndPoints, mBucketCounters, time);
    236             }
    237 
    238         }
    239     }
    240 
    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) {
    245             return basic;
    246         } else {
    247             StringBuffer intervals = new StringBuffer(" Interval Endpoints:");
    248             for (int i = 0; i < mBucketEndPoints.length; i++) {
    249                 intervals.append(" " + mBucketEndPoints[i]);
    250             }
    251             intervals.append(" Interval counters:");
    252             for (int i = 0; i < mBucketCounters.length; i++) {
    253                 intervals.append(" " + mBucketCounters[i]);
    254             }
    255             return basic + intervals;
    256         }
    257     }
    258 
    259     public static final Parcelable.Creator<TelephonyHistogram> CREATOR =
    260             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) {
    274         mCategory = in.readInt();
    275         mId = 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);
    283         }
    284         mBucketCount = in.readInt();
    285         mBucketEndPoints = new int[mBucketCount - 1];
    286         in.readIntArray(mBucketEndPoints);
    287         mBucketCounters = new int[mBucketCount];
    288         in.readIntArray(mBucketCounters);
    289     }
    290 
    291     public void writeToParcel(Parcel out, int flags) {
    292         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) {
    299             out.writeInt(ABSENT);
    300         } else {
    301             out.writeInt(PRESENT);
    302             out.writeIntArray(mInitialTimings);
    303         }
    304         out.writeInt(mBucketCount);
    305         out.writeIntArray(mBucketEndPoints);
    306         out.writeIntArray(mBucketCounters);
    307     }
    308 
    309     @Override
    310     public int describeContents() {
    311         return 0;
    312     }
    313 }
    314