diff --git a/api/current.txt b/api/current.txt index b3ef15a350f72..b83a1f27bbc4a 100644 --- a/api/current.txt +++ b/api/current.txt @@ -37341,6 +37341,26 @@ package android.telephony { method public void onSubscriptionsChanged(); } + public final class TelephonyHistogram implements android.os.Parcelable { + ctor public TelephonyHistogram(int, int, int); + ctor public TelephonyHistogram(android.telephony.TelephonyHistogram); + ctor public TelephonyHistogram(android.os.Parcel); + method public void addTimeTaken(int); + method public int describeContents(); + method public int getAverageTime(); + method public int getBucketCount(); + method public int[] getBucketCounters(); + method public int[] getBucketEndPoints(); + method public int getCategory(); + method public int getId(); + method public int getMaxTime(); + method public int getMinTime(); + method public int getSampleCount(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int TELEPHONY_CATEGORY_RIL = 1; // 0x1 + } + public class TelephonyManager { method public boolean canChangeDtmfToneLength(); method public java.util.List getAllCellInfo(); @@ -37384,6 +37404,7 @@ package android.telephony { method public int getSimState(); method public java.lang.String getSubscriberId(); method public java.lang.String getSubscriberId(int); + method public java.util.List getTelephonyHistograms(); method public java.lang.String getVoiceMailAlphaTag(); method public java.lang.String getVoiceMailAlphaTag(int); method public java.lang.String getVoiceMailNumber(); diff --git a/api/system-current.txt b/api/system-current.txt index 3799bc934e311..9c2fd504376cd 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -40274,6 +40274,26 @@ package android.telephony { method public void onSubscriptionsChanged(); } + public final class TelephonyHistogram implements android.os.Parcelable { + ctor public TelephonyHistogram(int, int, int); + ctor public TelephonyHistogram(android.telephony.TelephonyHistogram); + ctor public TelephonyHistogram(android.os.Parcel); + method public void addTimeTaken(int); + method public int describeContents(); + method public int getAverageTime(); + method public int getBucketCount(); + method public int[] getBucketCounters(); + method public int[] getBucketEndPoints(); + method public int getCategory(); + method public int getId(); + method public int getMaxTime(); + method public int getMinTime(); + method public int getSampleCount(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int TELEPHONY_CATEGORY_RIL = 1; // 0x1 + } + public class TelephonyManager { method public void answerRingingCall(); method public void call(java.lang.String, java.lang.String); @@ -40336,6 +40356,7 @@ package android.telephony { method public int getSimState(); method public java.lang.String getSubscriberId(); method public java.lang.String getSubscriberId(int); + method public java.util.List getTelephonyHistograms(); method public java.lang.String getVoiceMailAlphaTag(); method public java.lang.String getVoiceMailAlphaTag(int); method public java.lang.String getVoiceMailNumber(); diff --git a/api/test-current.txt b/api/test-current.txt index 1d9e3213f76b8..62a3d77094afb 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -37419,6 +37419,26 @@ package android.telephony { method public void onSubscriptionsChanged(); } + public final class TelephonyHistogram implements android.os.Parcelable { + ctor public TelephonyHistogram(int, int, int); + ctor public TelephonyHistogram(android.telephony.TelephonyHistogram); + ctor public TelephonyHistogram(android.os.Parcel); + method public void addTimeTaken(int); + method public int describeContents(); + method public int getAverageTime(); + method public int getBucketCount(); + method public int[] getBucketCounters(); + method public int[] getBucketEndPoints(); + method public int getCategory(); + method public int getId(); + method public int getMaxTime(); + method public int getMinTime(); + method public int getSampleCount(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator CREATOR; + field public static final int TELEPHONY_CATEGORY_RIL = 1; // 0x1 + } + public class TelephonyManager { method public boolean canChangeDtmfToneLength(); method public java.util.List getAllCellInfo(); @@ -37462,6 +37482,7 @@ package android.telephony { method public int getSimState(); method public java.lang.String getSubscriberId(); method public java.lang.String getSubscriberId(int); + method public java.util.List getTelephonyHistograms(); method public java.lang.String getVoiceMailAlphaTag(); method public java.lang.String getVoiceMailAlphaTag(int); method public java.lang.String getVoiceMailNumber(); diff --git a/telephony/java/android/telephony/TelephonyHistogram.aidl b/telephony/java/android/telephony/TelephonyHistogram.aidl new file mode 100644 index 0000000000000..8de81cf243db6 --- /dev/null +++ b/telephony/java/android/telephony/TelephonyHistogram.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony; +/** + * @hide + */ +parcelable TelephonyHistogram; diff --git a/telephony/java/android/telephony/TelephonyHistogram.java b/telephony/java/android/telephony/TelephonyHistogram.java new file mode 100644 index 0000000000000..985102c63ada0 --- /dev/null +++ b/telephony/java/android/telephony/TelephonyHistogram.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.telephony; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Parcelable class to store Telephony histogram. + * @hide + */ +@SystemApi +public final class TelephonyHistogram implements Parcelable { + // Type of Telephony histogram Eg: RIL histogram will have all timing data associated with + // RIL calls. Similarly we can have any other Telephony histogram. + private final int category; + + // Unique Id identifying a sample within particular category of histogram + private final int id; + + // Min time taken in ms + private int minTimeMs; + + // Max time taken in ms + private int maxTimeMs; + + // Average time taken in ms + private int averageTimeMs; + + // Total count of samples + private int sampleCount; + + // Array storing time taken for first #RANGE_CALCULATION_COUNT samples of histogram. + private int[] initialTimings; + + // Total number of time ranges expected (must be greater than 1) + private final int bucketCount; + + // Array storing endpoints of range buckets. Calculated based on values of minTime & maxTime + // after totalTimeCount is #RANGE_CALCULATION_COUNT. + private final int[] bucketEndPoints; + + // Array storing counts for each time range starting from smallest value range + private final int[] bucketCounters; + + /** + * Constant for Telephony category + */ + public static final int TELEPHONY_CATEGORY_RIL = 1; + + // Count of Histogram samples after which time buckets are created. + private static final int RANGE_CALCULATION_COUNT = 10; + + + // Constant used to indicate #initialTimings is null while parceling + private static final int ABSENT = 0; + + // Constant used to indicate #initialTimings is not null while parceling + private static final int PRESENT = 1; + + // Throws exception if #totalBuckets is not greater than one. + public TelephonyHistogram (int category, int id, int bucketCount) { + if (bucketCount <= 1) { + throw new IllegalArgumentException("Invalid number of buckets"); + } + this.category = category; + this.id = id; + this.minTimeMs = Integer.MAX_VALUE; + this.maxTimeMs = 0; + this.averageTimeMs = 0; + this.sampleCount = 0; + initialTimings = new int[RANGE_CALCULATION_COUNT]; + this.bucketCount = bucketCount; + bucketEndPoints = new int[bucketCount - 1]; + bucketCounters = new int[bucketCount]; + } + + public TelephonyHistogram(TelephonyHistogram th) { + category = th.getCategory(); + id = th.getId(); + minTimeMs = th.getMinTime(); + maxTimeMs = th.getMaxTime(); + averageTimeMs = th.getAverageTime(); + sampleCount = th.getSampleCount(); + initialTimings = th.getInitialTimings(); + bucketCount = th.getBucketCount(); + bucketEndPoints = th.getBucketEndPoints(); + bucketCounters = th.getBucketCounters(); + } + + public int getCategory() { + return category; + } + + public int getId() { + return id; + } + + public int getMinTime() { + return minTimeMs; + } + + public int getMaxTime() { + return maxTimeMs; + } + + public int getAverageTime() { + return averageTimeMs; + } + + public int getSampleCount () { + return sampleCount; + } + + private int[] getInitialTimings() { + return initialTimings; + } + + public int getBucketCount() { + return bucketCount; + } + + public int[] getBucketEndPoints() { + return getDeepCopyOfArray(bucketEndPoints); + } + + public int[] getBucketCounters() { + return getDeepCopyOfArray(bucketCounters); + } + + private int[] getDeepCopyOfArray(int[] array) { + int[] clone = new int[array.length]; + System.arraycopy(array, 0, clone, 0, array.length); + return clone; + } + + private void addToBucketCounter(int time) { + int i; + for (i = 0; i < bucketEndPoints.length; i++) { + if (time <= bucketEndPoints[i]) { + bucketCounters[i]++; + return; + } + } + bucketCounters[i]++; + } + + // Add new value of time taken + // This function updates minTime, maxTime, averageTime & totalTimeCount every time it is + // called. initialTimings[] is updated if totalTimeCount <= #RANGE_CALCULATION_COUNT. When + // totalTimeCount = RANGE_CALCULATION_COUNT, based on the min, max time & the number of buckets + // expected, bucketEndPoints[] would be calculated. Then bucketCounters[] would be filled up + // using values stored in initialTimings[]. Thereafter bucketCounters[] will always be updated. + public void addTimeTaken(int time) { + // Initialize all fields if its first entry or if integer overflow is going to occur while + // trying to calculate averageTime + if (sampleCount == 0 || (sampleCount == Integer.MAX_VALUE)) { + if (sampleCount == 0) { + minTimeMs = time; + maxTimeMs = time; + averageTimeMs = time; + } else { + initialTimings = new int[RANGE_CALCULATION_COUNT]; + } + sampleCount = 1; + Arrays.fill(initialTimings, 0); + initialTimings[0] = time; + Arrays.fill(bucketEndPoints, 0); + Arrays.fill(bucketCounters, 0); + } else { + if (time < minTimeMs) { + minTimeMs = time; + } + if (time > maxTimeMs) { + maxTimeMs = time; + } + long totalTime = ((long)averageTimeMs) * sampleCount + time; + averageTimeMs = (int)(totalTime/++sampleCount); + + if (sampleCount < RANGE_CALCULATION_COUNT) { + initialTimings[sampleCount - 1] = time; + } else if (sampleCount == RANGE_CALCULATION_COUNT) { + initialTimings[sampleCount - 1] = time; + + // Calculate bucket endpoints based on bucketCount expected + for (int i = 1; i < bucketCount; i++) { + int endPt = minTimeMs + (i * (maxTimeMs - minTimeMs)) / bucketCount; + bucketEndPoints[i - 1] = endPt; + } + + // Use values stored in initialTimings[] to update bucketCounters + for (int j = 0; j < RANGE_CALCULATION_COUNT; j++) { + addToBucketCounter(initialTimings[j]); + } + initialTimings = null; + } else { + addToBucketCounter(time); + } + + } + } + + public String toString() { + String basic = " Histogram id = " + id + " Time(ms): min = " + minTimeMs + " max = " + + maxTimeMs + " avg = " + averageTimeMs + " Count = " + sampleCount; + if (sampleCount < RANGE_CALCULATION_COUNT) { + return basic; + } else { + StringBuffer intervals = new StringBuffer(" Interval Endpoints:"); + for (int i = 0; i < bucketEndPoints.length; i++) { + intervals.append(" " + bucketEndPoints[i]); + } + intervals.append(" Interval counters:"); + for (int i = 0; i < bucketCounters.length; i++) { + intervals.append(" " + bucketCounters[i]); + } + return basic + intervals; + } + } + + public static final Parcelable.Creator CREATOR = + new Parcelable.Creator () { + + @Override + public TelephonyHistogram createFromParcel(Parcel in) { + return new TelephonyHistogram(in); + } + + @Override + public TelephonyHistogram[] newArray(int size) { + return new TelephonyHistogram[size]; + } + }; + + public TelephonyHistogram(Parcel in) { + category = in.readInt(); + id = in.readInt(); + minTimeMs = in.readInt(); + maxTimeMs = in.readInt(); + averageTimeMs = in.readInt(); + sampleCount = in.readInt(); + if (in.readInt() == PRESENT) { + initialTimings = new int[RANGE_CALCULATION_COUNT]; + in.readIntArray(initialTimings); + } + bucketCount = in.readInt(); + bucketEndPoints = new int[bucketCount - 1]; + in.readIntArray(bucketEndPoints); + bucketCounters = new int[bucketCount]; + in.readIntArray(bucketCounters); + } + + public void writeToParcel(Parcel out, int flags) { + out.writeInt(category); + out.writeInt(id); + out.writeInt(minTimeMs); + out.writeInt(maxTimeMs); + out.writeInt(averageTimeMs); + out.writeLong(sampleCount); + if (initialTimings == null) { + out.writeInt(ABSENT); + } else { + out.writeInt(PRESENT); + out.writeIntArray(initialTimings); + } + out.writeInt(bucketCount); + out.writeIntArray(bucketEndPoints); + out.writeIntArray(bucketCounters); + } + + @Override + public int describeContents() { + return 0; + } +} diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 1fa259a7e1145..3f47b61aebef3 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -36,6 +36,7 @@ import android.os.ServiceManager; import android.os.SystemProperties; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; +import android.telephony.TelephonyHistogram; import android.util.Log; import com.android.internal.telecom.ITelecomService; @@ -5437,4 +5438,24 @@ public class TelephonyManager { return null; } + /* + * Get snapshot of Telephony histograms + * @return List of Telephony histograms + * Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * Or the calling app has carrier privileges. + * @hide + */ + @SystemApi + public List getTelephonyHistograms() { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getTelephonyHistograms(); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getTelephonyHistograms", e); + } + return null; + } } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index cf61b0a795193..a44a094e82bc2 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -28,8 +28,10 @@ import android.telephony.ModemActivityInfo; import android.telephony.NeighboringCellInfo; import android.telephony.RadioAccessFamily; import android.telephony.ServiceState; +import android.telephony.TelephonyHistogram; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.OperatorInfo; + import java.util.List; @@ -1107,4 +1109,13 @@ interface ITelephony { * @hide */ String getEsn(int subId); + + /** + * Get snapshot of Telephony histograms + * @return List of Telephony histograms + * Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * Or the calling app has carrier privileges. + */ + List getTelephonyHistograms(); }