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