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 17 package org.drrickorang.loopback; 18 19 import android.os.Bundle; 20 import android.os.Parcel; 21 import android.os.Parcelable; 22 23 import java.util.Iterator; 24 25 /** 26 * Maintains and returns pairs of callback timestamps (in milliseconds since beginning of test) and 27 * lengths (milliseconds between a callback and the previous callback). 28 */ 29 public class BufferCallbackTimes implements Iterable<BufferCallbackTimes.BufferCallback>, 30 Parcelable { 31 private final int[] mTimeStamps; 32 private final short[] mCallbackDurations; 33 private final short mExpectedBufferPeriod; 34 private boolean mExceededCapacity; 35 private int mIndex; 36 BufferCallbackTimes(int maxRecords, int expectedBufferPeriod)37 public BufferCallbackTimes(int maxRecords, int expectedBufferPeriod) { 38 mIndex = 0; 39 mTimeStamps = new int[maxRecords]; 40 mCallbackDurations = new short[maxRecords]; 41 mExceededCapacity = false; 42 mExpectedBufferPeriod = (short) expectedBufferPeriod; 43 } 44 45 /** 46 * Instantiates an iterable object with already recorded callback times and lengths 47 * used for callbacks recorded by native sles callback functions. 48 * 49 * exceededCapacity should be set to true only when there were late callbacks observed but 50 * unable to be recorded because allocated arrays were already at capacity 51 */ BufferCallbackTimes(int[] timeStamps, short[] callbackDurations, boolean exceededCapacity, short expectedBufferPeriod)52 public BufferCallbackTimes(int[] timeStamps, short[] callbackDurations, 53 boolean exceededCapacity, short expectedBufferPeriod) { 54 mTimeStamps = timeStamps; 55 mCallbackDurations = callbackDurations; 56 mExceededCapacity = exceededCapacity; 57 mIndex = mTimeStamps.length; 58 mExpectedBufferPeriod = expectedBufferPeriod; 59 } 60 61 /** Record the length of a late/early callback and the time it occurred. Used by Java Thread. */ recordCallbackTime(int timeStamp, short callbackLength)62 public void recordCallbackTime(int timeStamp, short callbackLength) { 63 if (!mExceededCapacity && callbackLength != mExpectedBufferPeriod 64 && callbackLength != mExpectedBufferPeriod + 1) { 65 //only marked as exceeded if attempting to record a late callback after arrays full 66 if (mIndex == mTimeStamps.length) { 67 mExceededCapacity = true; 68 return; 69 } 70 mTimeStamps[mIndex] = timeStamp; 71 mCallbackDurations[mIndex] = callbackLength; 72 mIndex++; 73 } 74 } 75 76 @Override toString()77 public String toString() { 78 StringBuilder sb = new StringBuilder(); 79 for (BufferCallback callback : this) { 80 sb.append(callback.timeStamp); 81 sb.append(","); 82 sb.append(callback.callbackDuration); 83 sb.append("\n"); 84 } 85 return sb.toString(); 86 } 87 88 // True only if arrays are full and recording more late or early callbacks is attempted. isCapacityExceeded()89 public boolean isCapacityExceeded() { 90 return mExceededCapacity; 91 } 92 getNumLateOrEarlyCallbacks()93 public int getNumLateOrEarlyCallbacks() { 94 return mIndex; 95 } 96 getExpectedBufferPeriod()97 public short getExpectedBufferPeriod() { 98 return mExpectedBufferPeriod; 99 } 100 101 @Override iterator()102 public Iterator<BufferCallback> iterator() { 103 return new Iterator<BufferCallback>() { 104 int mIteratorIndex = 0; 105 106 @Override 107 public boolean hasNext() { 108 return mIteratorIndex < mIndex; 109 } 110 111 @Override 112 public BufferCallback next() { 113 return new BufferCallback(mTimeStamps[mIteratorIndex], 114 mCallbackDurations[mIteratorIndex++]); 115 } 116 117 @Override 118 public void remove() { 119 throw new UnsupportedOperationException("Buffer Time Stamps are Immutable"); 120 } 121 }; 122 } 123 124 @Override describeContents()125 public int describeContents() { 126 return 0; 127 } 128 129 @Override writeToParcel(Parcel dest, int flags)130 public void writeToParcel(Parcel dest, int flags) { 131 Bundle out = new Bundle(); 132 out.putIntArray("mTimeStamps", mTimeStamps); 133 out.putShortArray("mCallbackDurations", mCallbackDurations); 134 out.putShort("mExpectedBufferPeriod", mExpectedBufferPeriod); 135 out.putBoolean("mExceededCapacity", mExceededCapacity); 136 out.putInt("mIndex", mIndex); 137 dest.writeBundle(out); 138 } 139 BufferCallbackTimes(Parcel source)140 private BufferCallbackTimes(Parcel source) { 141 Bundle in = source.readBundle(getClass().getClassLoader()); 142 mTimeStamps = in.getIntArray("mTimeStamps"); 143 mCallbackDurations = in.getShortArray("mCallbackDurations"); 144 mExpectedBufferPeriod = in.getShort("mExpectedBufferPeriod"); 145 mExceededCapacity = in.getBoolean("mExceededCapacity"); 146 mIndex = in.getInt("mIndex"); 147 } 148 149 public static final Parcelable.Creator<BufferCallbackTimes> CREATOR 150 = new Parcelable.Creator<BufferCallbackTimes>() { 151 public BufferCallbackTimes createFromParcel(Parcel in) { 152 return new BufferCallbackTimes(in); 153 } 154 155 public BufferCallbackTimes[] newArray(int size) { 156 return new BufferCallbackTimes[size]; 157 } 158 }; 159 160 /** Wrapper for iteration over timestamp and length pairs */ 161 public class BufferCallback { 162 public final int timeStamp; 163 public final short callbackDuration; 164 BufferCallback(final int ts, final short cd)165 BufferCallback(final int ts, final short cd) { 166 timeStamp = ts; 167 callbackDuration = cd; 168 } 169 } 170 171 } 172