diff options
66 files changed, 4441 insertions, 577 deletions
diff --git a/api/current.xml b/api/current.xml index 197e2cd..841aa21 100644 --- a/api/current.xml +++ b/api/current.xml @@ -85290,7 +85290,7 @@ type="int" transient="false" volatile="false" - value="1" + value="0" static="true" final="true" deprecated="not deprecated" @@ -85301,7 +85301,7 @@ type="int" transient="false" volatile="false" - value="0" + value="1" static="true" final="true" deprecated="not deprecated" diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 8eda844..03bcadc 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -468,12 +468,17 @@ public final class BluetoothAdapter { * <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however * many remote devices can only display the first 40 characters, and some * may be limited to just 20. + * <p>If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} * * @param name a valid Bluetooth name * @return true if the name was set, false otherwise */ public boolean setName(String name) { + if (getState() != STATE_ON) return false; try { return mService.setName(name); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -488,11 +493,16 @@ public final class BluetoothAdapter { * {@link #SCAN_MODE_NONE}, * {@link #SCAN_MODE_CONNECTABLE}, * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. + * <p>If Bluetooth state is not {@link #STATE_ON}, this API + * will return {@link #SCAN_MODE_NONE}. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. * <p>Requires {@link android.Manifest.permission#BLUETOOTH} * * @return scan mode */ public int getScanMode() { + if (getState() != STATE_ON) return SCAN_MODE_NONE; try { return mService.getScanMode(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -511,6 +521,10 @@ public final class BluetoothAdapter { * {@link #SCAN_MODE_NONE}, * {@link #SCAN_MODE_CONNECTABLE}, * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. + * <p>If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. * <p>Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} * <p>Applications cannot set the scan mode. They should use * <code>startActivityForResult( @@ -524,6 +538,7 @@ public final class BluetoothAdapter { * @hide */ public boolean setScanMode(int mode, int duration) { + if (getState() != STATE_ON) return false; try { return mService.setScanMode(mode, duration); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -532,11 +547,13 @@ public final class BluetoothAdapter { /** @hide */ public boolean setScanMode(int mode) { + if (getState() != STATE_ON) return false; return setScanMode(mode, 120); } /** @hide */ public int getDiscoverableTimeout() { + if (getState() != STATE_ON) return -1; try { return mService.getDiscoverableTimeout(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -545,6 +562,7 @@ public final class BluetoothAdapter { /** @hide */ public void setDiscoverableTimeout(int timeout) { + if (getState() != STATE_ON) return; try { mService.setDiscoverableTimeout(timeout); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -572,11 +590,16 @@ public final class BluetoothAdapter { * <p>Device discovery will only find remote devices that are currently * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are * not discoverable by default, and need to be entered into a special mode. + * <p>If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}. * * @return true on success, false on error */ public boolean startDiscovery() { + if (getState() != STATE_ON) return false; try { return mService.startDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -593,10 +616,15 @@ public final class BluetoothAdapter { * the Activity, but is run as a system service, so an application should * always call cancel discovery even if it did not directly request a * discovery, just to be sure. + * <p>If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. * * @return true on success, false on error */ public boolean cancelDiscovery() { + if (getState() != STATE_ON) return false; try { mService.cancelDiscovery(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -614,11 +642,16 @@ public final class BluetoothAdapter { * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED} * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery * starts or completes. + * <p>If Bluetooth state is not {@link #STATE_ON}, this API + * will return false. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. * <p>Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return true if discovering */ public boolean isDiscovering() { + if (getState() != STATE_ON) return false; try { return mService.isDiscovering(); } catch (RemoteException e) {Log.e(TAG, "", e);} @@ -628,11 +661,18 @@ public final class BluetoothAdapter { /** * Return the set of {@link BluetoothDevice} objects that are bonded * (paired) to the local adapter. + * <p>If Bluetooth state is not {@link #STATE_ON}, this API + * will return an empty set. After turning on Bluetooth, + * wait for {@link #ACTION_STATE_CHANGED} with {@link #STATE_ON} + * to get the updated value. * <p>Requires {@link android.Manifest.permission#BLUETOOTH}. * * @return unmodifiable set of {@link BluetoothDevice}, or null on error */ public Set<BluetoothDevice> getBondedDevices() { + if (getState() != STATE_ON) { + return toDeviceSet(new String[0]); + } try { return toDeviceSet(mService.listBonds()); } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/core/java/android/view/InputQueue.java b/core/java/android/view/InputQueue.java index 13d8104..43c957a 100644 --- a/core/java/android/view/InputQueue.java +++ b/core/java/android/view/InputQueue.java @@ -132,9 +132,9 @@ public final class InputQueue { synchronized (sLock) { FinishedCallback callback = sRecycleHead; if (callback != null) { - callback.mRecycleNext = null; sRecycleHead = callback.mRecycleNext; sRecycleCount -= 1; + callback.mRecycleNext = null; } else { callback = new FinishedCallback(); } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 9223e17..ed10e41 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -156,7 +156,7 @@ public class KeyEvent extends InputEvent implements Parcelable { // those new codes. This is intended to maintain a consistent // set of key code definitions across all Android devices. - private static final int LAST_KEYCODE = KEYCODE_SWITCH_CHARSET; + private static final int LAST_KEYCODE = KEYCODE_BUTTON_MODE; /** * @deprecated There are now more than MAX_KEYCODE keycodes. diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java index ff34f4a..0999598 100644 --- a/core/java/android/view/ScaleGestureDetector.java +++ b/core/java/android/view/ScaleGestureDetector.java @@ -312,7 +312,7 @@ public class ScaleGestureDetector { * MotionEvent has no getRawX(int) method; simulate it pending future API approval. */ private static float getRawX(MotionEvent event, int pointerIndex) { - float offset = event.getX() - event.getRawX(); + float offset = event.getRawX() - event.getX(); return event.getX(pointerIndex) + offset; } @@ -320,7 +320,7 @@ public class ScaleGestureDetector { * MotionEvent has no getRawY(int) method; simulate it pending future API approval. */ private static float getRawY(MotionEvent event, int pointerIndex) { - float offset = event.getY() - event.getRawY(); + float offset = event.getRawY() - event.getY(); return event.getY(pointerIndex) + offset; } diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index 068e7b6..fb88c71 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -33,14 +33,15 @@ import android.util.PoolableManager; * and {@link #getXVelocity()}. */ public final class VelocityTracker implements Poolable<VelocityTracker> { - static final String TAG = "VelocityTracker"; - static final boolean DEBUG = false; - static final boolean localLOGV = DEBUG || Config.LOGV; + private static final String TAG = "VelocityTracker"; + private static final boolean DEBUG = false; + private static final boolean localLOGV = DEBUG || Config.LOGV; - static final int NUM_PAST = 10; - static final int MAX_AGE_MILLISECONDS = 200; + private static final int NUM_PAST = 10; + private static final int MAX_AGE_MILLISECONDS = 200; + + private static final int POINTER_POOL_CAPACITY = 20; - static final VelocityTracker[] mPool = new VelocityTracker[1]; private static final Pool<VelocityTracker> sPool = Pools.synchronizedPool( Pools.finitePool(new PoolableManager<VelocityTracker>() { public VelocityTracker newInstance() { @@ -48,16 +49,19 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } public void onAcquired(VelocityTracker element) { - element.clear(); } public void onReleased(VelocityTracker element) { + element.clear(); } }, 2)); - private static final int INITIAL_POINTERS = 5; + private static Pointer sRecycledPointerListHead; + private static int sRecycledPointerCount; - private static final class PointerData { + private static final class Pointer { + public Pointer next; + public int id; public float xVelocity; public float yVelocity; @@ -65,11 +69,13 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { public final float[] pastX = new float[NUM_PAST]; public final float[] pastY = new float[NUM_PAST]; public final long[] pastTime = new long[NUM_PAST]; // uses Long.MIN_VALUE as a sentinel + + public int generation; } - private PointerData[] mPointers = new PointerData[INITIAL_POINTERS]; - private int mNumPointers; + private Pointer mPointerListHead; // sorted by id in increasing order private int mLastTouchIndex; + private int mGeneration; private VelocityTracker mNext; @@ -115,7 +121,9 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * Reset the velocity tracker back to its initial state. */ public void clear() { - mNumPointers = 0; + releasePointerList(mPointerListHead); + + mPointerListHead = null; mLastTouchIndex = 0; } @@ -134,56 +142,62 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { final int lastTouchIndex = mLastTouchIndex; final int nextTouchIndex = (lastTouchIndex + 1) % NUM_PAST; final int finalTouchIndex = (nextTouchIndex + historySize) % NUM_PAST; + final int generation = mGeneration++; - if (pointerCount < mNumPointers) { - final PointerData[] pointers = mPointers; - int i = mNumPointers; - while (--i >= 0) { - final PointerData pointerData = pointers[i]; - if (ev.findPointerIndex(pointerData.id) == -1) { - // Pointer went up. - // Shuffle pointers down to fill the hole. Place the old pointer data at - // the end so we can recycle it if more pointers are added later. - mNumPointers -= 1; - final int remaining = mNumPointers - i; - if (remaining != 0) { - System.arraycopy(pointers, i + 1, pointers, i, remaining); - pointers[mNumPointers] = pointerData; - } - } - } - } - + mLastTouchIndex = finalTouchIndex; + + // Update pointer data. + Pointer previousPointer = null; for (int i = 0; i < pointerCount; i++){ final int pointerId = ev.getPointerId(i); - PointerData pointerData = getPointerData(pointerId); - if (pointerData == null) { - // Pointer went down. - // Add a new entry. Write a sentinel at the end of the pastTime trace so we - // will be able to tell where the trace started. - final PointerData[] oldPointers = mPointers; - final int newPointerIndex = mNumPointers; - if (newPointerIndex < oldPointers.length) { - pointerData = oldPointers[newPointerIndex]; - if (pointerData == null) { - pointerData = new PointerData(); - oldPointers[newPointerIndex] = pointerData; + + // Find the pointer data for this pointer id. + // This loop is optimized for the common case where pointer ids in the event + // are in sorted order. However, we check for this case explicitly and + // perform a full linear scan from the start if needed. + Pointer nextPointer; + if (previousPointer == null || pointerId < previousPointer.id) { + previousPointer = null; + nextPointer = mPointerListHead; + } else { + nextPointer = previousPointer.next; + } + + final Pointer pointer; + for (;;) { + if (nextPointer != null) { + final int nextPointerId = nextPointer.id; + if (nextPointerId == pointerId) { + pointer = nextPointer; + break; + } + if (nextPointerId < pointerId) { + nextPointer = nextPointer.next; + continue; } + } + + // Pointer went down. Add it to the list. + // Write a sentinel at the end of the pastTime trace so we will be able to + // tell when the trace started. + pointer = obtainPointer(); + pointer.id = pointerId; + pointer.pastTime[lastTouchIndex] = Long.MIN_VALUE; + pointer.next = nextPointer; + if (previousPointer == null) { + mPointerListHead = pointer; } else { - final PointerData[] newPointers = new PointerData[newPointerIndex * 2]; - System.arraycopy(oldPointers, 0, newPointers, 0, newPointerIndex); - mPointers = newPointers; - pointerData = new PointerData(); - newPointers[newPointerIndex] = pointerData; + previousPointer.next = pointer; } - pointerData.id = pointerId; - pointerData.pastTime[lastTouchIndex] = Long.MIN_VALUE; - mNumPointers += 1; + break; } - final float[] pastX = pointerData.pastX; - final float[] pastY = pointerData.pastY; - final long[] pastTime = pointerData.pastTime; + pointer.generation = generation; + previousPointer = pointer; + + final float[] pastX = pointer.pastX; + final float[] pastY = pointer.pastY; + final long[] pastTime = pointer.pastTime; for (int j = 0; j < historySize; j++) { final int touchIndex = (nextTouchIndex + j) % NUM_PAST; @@ -196,7 +210,23 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { pastTime[finalTouchIndex] = ev.getEventTime(); } - mLastTouchIndex = finalTouchIndex; + // Find removed pointers. + previousPointer = null; + for (Pointer pointer = mPointerListHead; pointer != null; ) { + final Pointer nextPointer = pointer.next; + if (pointer.generation != generation) { + // Pointer went up. Remove it from the list. + if (previousPointer == null) { + mPointerListHead = nextPointer; + } else { + previousPointer.next = nextPointer; + } + releasePointer(pointer); + } else { + previousPointer = pointer; + } + pointer = nextPointer; + } } /** @@ -223,13 +253,10 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * must be positive. */ public void computeCurrentVelocity(int units, float maxVelocity) { - final int numPointers = mNumPointers; - final PointerData[] pointers = mPointers; final int lastTouchIndex = mLastTouchIndex; - for (int p = 0; p < numPointers; p++) { - final PointerData pointerData = pointers[p]; - final long[] pastTime = pointerData.pastTime; + for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) { + final long[] pastTime = pointer.pastTime; // Search backwards in time for oldest acceptable time. // Stop at the beginning of the trace as indicated by the sentinel time Long.MIN_VALUE. @@ -253,8 +280,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } // Kind-of stupid. - final float[] pastX = pointerData.pastX; - final float[] pastY = pointerData.pastY; + final float[] pastX = pointer.pastX; + final float[] pastY = pointer.pastY; final float oldestX = pastX[oldestTouchIndex]; final float oldestY = pastY[oldestTouchIndex]; @@ -290,11 +317,11 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { accumY = maxVelocity; } - pointerData.xVelocity = accumX; - pointerData.yVelocity = accumY; + pointer.xVelocity = accumX; + pointer.yVelocity = accumY; if (localLOGV) { - Log.v(TAG, "[" + p + "] Pointer " + pointerData.id + Log.v(TAG, "Pointer " + pointer.id + ": Y velocity=" + accumX +" X velocity=" + accumY + " N=" + numTouches); } } @@ -307,8 +334,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity() { - PointerData pointerData = getPointerData(0); - return pointerData != null ? pointerData.xVelocity : 0; + Pointer pointer = getPointer(0); + return pointer != null ? pointer.xVelocity : 0; } /** @@ -318,8 +345,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity() { - PointerData pointerData = getPointerData(0); - return pointerData != null ? pointerData.yVelocity : 0; + Pointer pointer = getPointer(0); + return pointer != null ? pointer.yVelocity : 0; } /** @@ -330,8 +357,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity(int id) { - PointerData pointerData = getPointerData(id); - return pointerData != null ? pointerData.xVelocity : 0; + Pointer pointer = getPointer(id); + return pointer != null ? pointer.xVelocity : 0; } /** @@ -342,19 +369,68 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity(int id) { - PointerData pointerData = getPointerData(id); - return pointerData != null ? pointerData.yVelocity : 0; + Pointer pointer = getPointer(id); + return pointer != null ? pointer.yVelocity : 0; } - private final PointerData getPointerData(int id) { - final PointerData[] pointers = mPointers; - final int numPointers = mNumPointers; - for (int p = 0; p < numPointers; p++) { - PointerData pointerData = pointers[p]; - if (pointerData.id == id) { - return pointerData; + private final Pointer getPointer(int id) { + for (Pointer pointer = mPointerListHead; pointer != null; pointer = pointer.next) { + if (pointer.id == id) { + return pointer; } } return null; } + + private static final Pointer obtainPointer() { + synchronized (sPool) { + if (sRecycledPointerCount != 0) { + Pointer element = sRecycledPointerListHead; + sRecycledPointerCount -= 1; + sRecycledPointerListHead = element.next; + element.next = null; + return element; + } + } + return new Pointer(); + } + + private static final void releasePointer(Pointer pointer) { + synchronized (sPool) { + if (sRecycledPointerCount < POINTER_POOL_CAPACITY) { + pointer.next = sRecycledPointerListHead; + sRecycledPointerCount += 1; + sRecycledPointerListHead = pointer; + } + } + } + + private static final void releasePointerList(Pointer pointer) { + if (pointer != null) { + synchronized (sPool) { + int count = sRecycledPointerCount; + if (count >= POINTER_POOL_CAPACITY) { + return; + } + + Pointer tail = pointer; + for (;;) { + count += 1; + if (count >= POINTER_POOL_CAPACITY) { + break; + } + + Pointer next = tail.next; + if (next == null) { + break; + } + tail = next; + } + + tail.next = sRecycledPointerListHead; + sRecycledPointerCount = count; + sRecycledPointerListHead = pointer; + } + } + } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index c3f81a2..b8623e7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1358,14 +1358,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * Width as measured during measure pass. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "measurement") protected int mMeasuredWidth; /** * Height as measured during measure pass. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "measurement") protected int mMeasuredHeight; /** @@ -1420,8 +1420,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility static final int MEASURED_DIMENSION_SET = 0x00000800; /** {@hide} */ static final int FORCE_LAYOUT = 0x00001000; - - private static final int LAYOUT_REQUIRED = 0x00002000; + /** {@hide} */ + static final int LAYOUT_REQUIRED = 0x00002000; private static final int PRESSED = 0x00004000; @@ -1575,28 +1575,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * to the left edge of this view. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") protected int mLeft; /** * The distance in pixels from the left edge of this view's parent * to the right edge of this view. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") protected int mRight; /** * The distance in pixels from the top edge of this view's parent * to the top edge of this view. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") protected int mTop; /** * The distance in pixels from the top edge of this view's parent * to the bottom edge of this view. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") protected int mBottom; /** @@ -1604,14 +1604,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * horizontally. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "scrolling") protected int mScrollX; /** * The offset, in pixels, by which the content of this view is scrolled * vertically. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "scrolling") protected int mScrollY; /** @@ -1619,28 +1619,28 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * left edge of this view and the left edge of its content. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "padding") protected int mPaddingLeft; /** * The right padding in pixels, that is the distance in pixels between the * right edge of this view and the right edge of its content. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "padding") protected int mPaddingRight; /** * The top padding in pixels, that is the distance in pixels between the * top edge of this view and the top edge of its content. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "padding") protected int mPaddingTop; /** * The bottom padding in pixels, that is the distance in pixels between the * bottom edge of this view and the bottom edge of its content. * {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "padding") protected int mPaddingBottom; /** @@ -1651,13 +1651,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility /** * Cache the paddingRight set by the user to append to the scrollbar's size. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "padding") int mUserPaddingRight; /** * Cache the paddingBottom set by the user to append to the scrollbar's size. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "padding") int mUserPaddingBottom; /** @@ -1764,14 +1764,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * The minimum height of the view. We'll try our best to have the height * of this view to at least this amount. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "measurement") private int mMinHeight; /** * The minimum width of the view. We'll try our best to have the width * of this view to at least this amount. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "measurement") private int mMinWidth; /** @@ -2602,7 +2602,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @return True if this view has or contains focus, false otherwise. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "focus") public boolean hasFocus() { return (mPrivateFlags & FOCUSED) != 0; } @@ -2780,7 +2780,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @return True if this view has focus, false otherwise. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "focus") public boolean isFocused() { return (mPrivateFlags & FOCUSED) != 0; } @@ -3191,7 +3191,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @return true if this view has nothing to draw, false otherwise */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "drawing") public boolean willNotDraw() { return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW; } @@ -3214,7 +3214,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @return true if this view does not cache its drawing, false otherwise */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "drawing") public boolean willNotCacheDrawing() { return (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING; } @@ -3357,7 +3357,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @return True if this view can take focus, or false otherwise. * @attr ref android.R.styleable#View_focusable */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "focus") public final boolean isFocusable() { return FOCUSABLE == (mViewFlags & FOCUSABLE_MASK); } @@ -4666,7 +4666,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @return The width of your view, in pixels. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public final int getWidth() { return mRight - mLeft; } @@ -4676,7 +4676,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @return The height of your view, in pixels. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public final int getHeight() { return mBottom - mTop; } @@ -5162,7 +5162,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * * @return True if this View is guaranteed to be fully opaque, false otherwise. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "drawing") public boolean isOpaque() { return (mPrivateFlags & OPAQUE_MASK) == OPAQUE_MASK; } @@ -6247,7 +6247,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @see #setDrawingCacheEnabled(boolean) * @see #getDrawingCache() */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "drawing") public boolean isDrawingCacheEnabled() { return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED; } @@ -8116,7 +8116,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @return the offset of the baseline within the widget's bounds or -1 * if baseline alignment is not supported */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public int getBaseline() { return -1; } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 5dd45f9..2ca08ea 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -255,6 +255,14 @@ public class ViewDebug { * @see #deepExport() */ String prefix() default ""; + + /** + * Specifies the category the property falls into, such as measurement, + * layout, drawing, etc. + * + * @return the category as String + */ + String category() default ""; } /** @@ -934,65 +942,76 @@ public class ViewDebug { private static void profileViewAndChildren(final View view, BufferedWriter out) throws IOException { - final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() { - public Void[] pre() { - forceLayout(view); - return null; - } - - private void forceLayout(View view) { - view.forceLayout(); - if (view instanceof ViewGroup) { - ViewGroup group = (ViewGroup) view; - final int count = group.getChildCount(); - for (int i = 0; i < count; i++) { - forceLayout(group.getChildAt(i)); - } - } - } + profileViewAndChildren(view, out, true); + } - public void run(Void... data) { - view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec); - } + private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root) + throws IOException { - public void post(Void... data) { - } - }); + long durationMeasure = + (root || (view.mPrivateFlags & View.MEASURED_DIMENSION_SET) != 0) ? profileViewOperation( + view, new ViewOperation<Void>() { + public Void[] pre() { + forceLayout(view); + return null; + } - final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() { - public Void[] pre() { - return null; - } + private void forceLayout(View view) { + view.forceLayout(); + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + final int count = group.getChildCount(); + for (int i = 0; i < count; i++) { + forceLayout(group.getChildAt(i)); + } + } + } - public void run(Void... data) { - view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom); - } + public void run(Void... data) { + view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec); + } - public void post(Void... data) { - } - }); + public void post(Void... data) { + } + }) + : 0; + long durationLayout = + (root || (view.mPrivateFlags & View.LAYOUT_REQUIRED) != 0) ? profileViewOperation( + view, new ViewOperation<Void>() { + public Void[] pre() { + return null; + } - final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() { - public Object[] pre() { - final DisplayMetrics metrics = view.getResources().getDisplayMetrics(); - final Bitmap bitmap = - Bitmap.createBitmap(metrics.widthPixels, metrics.heightPixels, - Bitmap.Config.RGB_565); - final Canvas canvas = new Canvas(bitmap); - return new Object[] { - bitmap, canvas - }; - } + public void run(Void... data) { + view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom); + } - public void run(Object... data) { - view.draw((Canvas) data[1]); - } + public void post(Void... data) { + } + }) : 0; + long durationDraw = + (root || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(view, + new ViewOperation<Object>() { + public Object[] pre() { + final DisplayMetrics metrics = + view.getResources().getDisplayMetrics(); + final Bitmap bitmap = + Bitmap.createBitmap(metrics.widthPixels, + metrics.heightPixels, Bitmap.Config.RGB_565); + final Canvas canvas = new Canvas(bitmap); + return new Object[] { + bitmap, canvas + }; + } - public void post(Object... data) { - ((Bitmap) data[0]).recycle(); - } - }); + public void run(Object... data) { + view.draw((Canvas) data[1]); + } + public void post(Object... data) { + ((Bitmap) data[0]).recycle(); + } + }) : 0; out.write(String.valueOf(durationMeasure)); out.write(' '); out.write(String.valueOf(durationLayout)); @@ -1003,7 +1022,7 @@ public class ViewDebug { ViewGroup group = (ViewGroup) view; final int count = group.getChildCount(); for (int i = 0; i < count; i++) { - profileViewAndChildren(group.getChildAt(i), out); + profileViewAndChildren(group.getChildAt(i), out, false); } } } @@ -1033,7 +1052,10 @@ public class ViewDebug { }); try { - latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS); + if (!latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS)) { + Log.w("View", "Could not complete the profiling of the view " + view); + return -1; + } } catch (InterruptedException e) { Log.w("View", "Could not complete the profiling of the view " + view); Thread.currentThread().interrupt(); @@ -1354,9 +1376,12 @@ public class ViewDebug { // TODO: This should happen on the UI thread Object methodValue = method.invoke(view, (Object[]) null); final Class<?> returnType = method.getReturnType(); + final ExportedProperty property = sAnnotations.get(method); + String categoryPrefix = + property.category().length() != 0 ? property.category() + ":" : ""; if (returnType == int.class) { - final ExportedProperty property = sAnnotations.get(method); + if (property.resolveId() && context != null) { final int id = (Integer) methodValue; methodValue = resolveId(context, id); @@ -1364,7 +1389,8 @@ public class ViewDebug { final FlagToString[] flagsMapping = property.flagMapping(); if (flagsMapping.length > 0) { final int intValue = (Integer) methodValue; - final String valuePrefix = prefix + method.getName() + '_'; + final String valuePrefix = + categoryPrefix + prefix + method.getName() + '_'; exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix); } @@ -1388,21 +1414,22 @@ public class ViewDebug { } } } else if (returnType == int[].class) { - final ExportedProperty property = sAnnotations.get(method); final int[] array = (int[]) methodValue; - final String valuePrefix = prefix + method.getName() + '_'; + final String valuePrefix = categoryPrefix + prefix + method.getName() + '_'; final String suffix = "()"; exportUnrolledArray(context, out, property, array, valuePrefix, suffix); + + // Probably want to return here, same as for fields. + return; } else if (!returnType.isPrimitive()) { - final ExportedProperty property = sAnnotations.get(method); if (property.deepExport()) { dumpViewProperties(context, methodValue, out, prefix + property.prefix()); continue; } } - writeEntry(out, prefix, method.getName(), "()", methodValue); + writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue); } catch (IllegalAccessException e) { } catch (InvocationTargetException e) { } @@ -1422,9 +1449,12 @@ public class ViewDebug { try { Object fieldValue = null; final Class<?> type = field.getType(); + final ExportedProperty property = sAnnotations.get(field); + String categoryPrefix = + property.category().length() != 0 ? property.category() + ":" : ""; if (type == int.class) { - final ExportedProperty property = sAnnotations.get(field); + if (property.resolveId() && context != null) { final int id = field.getInt(view); fieldValue = resolveId(context, id); @@ -1432,7 +1462,8 @@ public class ViewDebug { final FlagToString[] flagsMapping = property.flagMapping(); if (flagsMapping.length > 0) { final int intValue = field.getInt(view); - final String valuePrefix = prefix + field.getName() + '_'; + final String valuePrefix = + categoryPrefix + prefix + field.getName() + '_'; exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix); } @@ -1454,9 +1485,8 @@ public class ViewDebug { } } } else if (type == int[].class) { - final ExportedProperty property = sAnnotations.get(field); final int[] array = (int[]) field.get(view); - final String valuePrefix = prefix + field.getName() + '_'; + final String valuePrefix = categoryPrefix + prefix + field.getName() + '_'; final String suffix = ""; exportUnrolledArray(context, out, property, array, valuePrefix, suffix); @@ -1464,10 +1494,9 @@ public class ViewDebug { // We exit here! return; } else if (!type.isPrimitive()) { - final ExportedProperty property = sAnnotations.get(field); if (property.deepExport()) { - dumpViewProperties(context, field.get(view), out, - prefix + property.prefix()); + dumpViewProperties(context, field.get(view), out, prefix + + property.prefix()); continue; } } @@ -1476,7 +1505,7 @@ public class ViewDebug { fieldValue = field.get(view); } - writeEntry(out, prefix, field.getName(), "", fieldValue); + writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue); } catch (IllegalAccessException e) { } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index e7b6c50..7159929 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -363,7 +363,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS}, * {@link #FOCUS_BLOCK_DESCENDANTS}. */ - @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.ExportedProperty(category = "focus", mapping = { @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"), @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"), @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS") @@ -2764,7 +2764,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @see #setChildrenDrawnWithCacheEnabled(boolean) * @see View#setDrawingCacheEnabled(boolean) */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "drawing") public boolean isAlwaysDrawnWithCacheEnabled() { return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE; } @@ -2799,7 +2799,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @see #setAlwaysDrawnWithCacheEnabled(boolean) * @see #setChildrenDrawnWithCacheEnabled(boolean) */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "drawing") protected boolean isChildrenDrawnWithCacheEnabled() { return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE; } @@ -2831,7 +2831,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @see #setChildrenDrawingOrderEnabled(boolean) * @see #getChildDrawingOrder(int, int) */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "drawing") protected boolean isChildrenDrawingOrderEnabled() { return (mGroupFlags & FLAG_USE_CHILD_DRAWING_ORDER) == FLAG_USE_CHILD_DRAWING_ORDER; } @@ -2868,7 +2868,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE} * and {@link #PERSISTENT_ALL_CACHES} */ - @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.ExportedProperty(category = "drawing", mapping = { @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE, to = "NONE"), @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES, to = "ANIMATION"), @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"), @@ -3501,7 +3501,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * constants FILL_PARENT (replaced by MATCH_PARENT , * in API Level 8) or WRAP_CONTENT. or an exact size. */ - @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") }) @@ -3512,7 +3512,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * constants FILL_PARENT (replaced by MATCH_PARENT , * in API Level 8) or WRAP_CONTENT. or an exact size. */ - @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") }) @@ -3637,25 +3637,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * The left margin in pixels of the child. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public int leftMargin; /** * The top margin in pixels of the child. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public int topMargin; /** * The right margin in pixels of the child. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public int rightMargin; /** * The bottom margin in pixels of the child. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public int bottomMargin; /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 6cfeb68..c970ae6 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3830,7 +3830,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * View type for this view, as returned by * {@link android.widget.Adapter#getItemViewType(int) } */ - @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.ExportedProperty(category = "list", mapping = { @ViewDebug.IntToString(from = ITEM_VIEW_TYPE_IGNORE, to = "ITEM_VIEW_TYPE_IGNORE"), @ViewDebug.IntToString(from = ITEM_VIEW_TYPE_HEADER_OR_FOOTER, to = "ITEM_VIEW_TYPE_HEADER_OR_FOOTER") }) @@ -3842,7 +3842,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * been added to the list view and whether they should be treated as * recycled views or not. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "list") boolean recycledHeaderFooter; /** @@ -3853,7 +3853,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * view to be attached to the window rather than just attached to the * parent. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "list") boolean forceAdd; public LayoutParams(Context c, AttributeSet attrs) { diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index fe6d91a..10a8729 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -56,7 +56,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { /** * The position of the first child displayed */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "scrolling") int mFirstPosition = 0; /** @@ -141,7 +141,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { * The position within the adapter's data set of the item to select * during the next layout. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "list") int mNextSelectedPosition = INVALID_POSITION; /** @@ -152,7 +152,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { /** * The position within the adapter's data set of the currently selected item. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "list") int mSelectedPosition = INVALID_POSITION; /** @@ -168,7 +168,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { /** * The number of items in the current adapter. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "list") int mItemCount; /** diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index e27bb4f..e445180 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -46,27 +46,32 @@ import android.widget.RemoteViews.RemoteView; */ @RemoteView public class FrameLayout extends ViewGroup { - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "measurement") boolean mMeasureAllChildren = false; - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "drawing") private Drawable mForeground; - @ViewDebug.ExportedProperty + + @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingLeft = 0; - @ViewDebug.ExportedProperty + + @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingTop = 0; - @ViewDebug.ExportedProperty + + @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingRight = 0; - @ViewDebug.ExportedProperty + + @ViewDebug.ExportedProperty(category = "padding") private int mForegroundPaddingBottom = 0; private final Rect mSelfBounds = new Rect(); private final Rect mOverlayBounds = new Rect(); - @ViewDebug.ExportedProperty + + @ViewDebug.ExportedProperty(category = "drawing") private int mForegroundGravity = Gravity.FILL; /** {@hide} */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "drawing") protected boolean mForegroundInPadding = true; boolean mForegroundBoundsChanged = false; diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index bd07e1f..0525891 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -50,7 +50,7 @@ public class LinearLayout extends ViewGroup { * Whether the children of this layout are baseline aligned. Only applicable * if {@link #mOrientation} is horizontal. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") private boolean mBaselineAligned = true; /** @@ -60,7 +60,7 @@ public class LinearLayout extends ViewGroup { * Note: this is orthogonal to {@link #mBaselineAligned}, which is concerned * with whether the children of this layout are baseline aligned. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") private int mBaselineAlignedChildIndex = -1; /** @@ -68,12 +68,13 @@ public class LinearLayout extends ViewGroup { * We'll calculate the baseline of this layout as we measure vertically; for * horizontal linear layouts, the offset of 0 is appropriate. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "measurement") private int mBaselineChildTop = 0; - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "measurement") private int mOrientation; - @ViewDebug.ExportedProperty(mapping = { + + @ViewDebug.ExportedProperty(category = "measurement", mapping = { @ViewDebug.IntToString(from = -1, to = "NONE"), @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), @@ -88,13 +89,14 @@ public class LinearLayout extends ViewGroup { @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") }) private int mGravity = Gravity.LEFT | Gravity.TOP; - @ViewDebug.ExportedProperty + + @ViewDebug.ExportedProperty(category = "measurement") private int mTotalLength; - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") private float mWeightSum; - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") private boolean mUseLargestChild; private int[] mMaxAscent; @@ -1364,7 +1366,7 @@ public class LinearLayout extends ViewGroup { * 0 if the view should not be stretched. Otherwise the extra pixels * will be pro-rated among all views whose weight is greater than 0. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public float weight; /** @@ -1372,7 +1374,7 @@ public class LinearLayout extends ViewGroup { * * @see android.view.Gravity */ - @ViewDebug.ExportedProperty(mapping = { + @ViewDebug.ExportedProperty(category = "layout", mapping = { @ViewDebug.IntToString(from = -1, to = "NONE"), @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 892c44a..ec6dbb7 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1140,7 +1140,7 @@ public class ListView extends AbsListView { * UNSPECIFIED/AT_MOST modes, false otherwise. * @hide */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "list") protected boolean recycleOnMeasure() { return true; } diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java index 8e9eb05..c0a546d 100644 --- a/core/java/android/widget/ProgressBar.java +++ b/core/java/android/widget/ProgressBar.java @@ -336,7 +336,7 @@ public class ProgressBar extends View { * * @return true if the progress bar is in indeterminate mode */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "progress") public synchronized boolean isIndeterminate() { return mIndeterminate; } @@ -609,7 +609,7 @@ public class ProgressBar extends View { * @see #setMax(int) * @see #getMax() */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "progress") public synchronized int getProgress() { return mIndeterminate ? 0 : mProgress; } @@ -626,7 +626,7 @@ public class ProgressBar extends View { * @see #setMax(int) * @see #getMax() */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "progress") public synchronized int getSecondaryProgress() { return mIndeterminate ? 0 : mSecondaryProgress; } @@ -640,7 +640,7 @@ public class ProgressBar extends View { * @see #getProgress() * @see #getSecondaryProgress() */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "progress") public synchronized int getMax() { return mMax; } diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 1aa1df3..64cda49 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -1011,7 +1011,7 @@ public class RelativeLayout extends ViewGroup { * @attr ref android.R.styleable#RelativeLayout_Layout_layout_centerVertical */ public static class LayoutParams extends ViewGroup.MarginLayoutParams { - @ViewDebug.ExportedProperty(resolveId = true, indexMapping = { + @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = { @ViewDebug.IntToString(from = ABOVE, to = "above"), @ViewDebug.IntToString(from = ALIGN_BASELINE, to = "alignBaseline"), @ViewDebug.IntToString(from = ALIGN_BOTTOM, to = "alignBottom"), @@ -1040,7 +1040,7 @@ public class RelativeLayout extends ViewGroup { * When true, uses the parent as the anchor if the anchor doesn't exist or if * the anchor's visibility is GONE. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public boolean alignWithParent; public LayoutParams(Context c, AttributeSet attrs) { diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index 48d12df..b612004 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -387,13 +387,13 @@ public class TableRow extends LinearLayout { /** * <p>The column index of the cell represented by the widget.</p> */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public int column; /** * <p>The number of columns the widgets spans over.</p> */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "layout") public int span; private static final int LOCATION = 0; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 950012c..27e0e94 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -5733,7 +5733,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Convenience for {@link Selection#getSelectionStart}. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "text") public int getSelectionStart() { return Selection.getSelectionStart(getText()); } @@ -5741,7 +5741,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Convenience for {@link Selection#getSelectionEnd}. */ - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "text") public int getSelectionEnd() { return Selection.getSelectionEnd(getText()); } @@ -7295,7 +7295,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - @ViewDebug.ExportedProperty + @ViewDebug.ExportedProperty(category = "text") private CharSequence mText; private CharSequence mTransformed; private BufferType mBufferType = BufferType.NORMAL; diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 8bf5c1b..c442fee 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -739,8 +739,8 @@ <string name="force_close" msgid="3653416315450806396">"Ukončit aplikaci"</string> <string name="report" msgid="4060218260984795706">"Nahlásit"</string> <string name="wait" msgid="7147118217226317732">"Počkat"</string> - <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces<xliff:g id="PROCESS">%2$s</xliff:g>) porušila své samovynucované zásady StrictMode."</string> - <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své samovynucované zásady StrictMode."</string> + <string name="smv_application" msgid="295583804361236288">"Aplikace <xliff:g id="APPLICATION">%1$s</xliff:g> (proces <xliff:g id="PROCESS">%2$s</xliff:g>) porušila své vlastní vynucené zásady StrictMode."</string> + <string name="smv_process" msgid="5120397012047462446">"Proces <xliff:g id="PROCESS">%1$s</xliff:g> porušil své vlastní vynucené zásady StrictMode."</string> <string name="heavy_weight_notification" msgid="9087063985776626166">"Běží aplikace <xliff:g id="APP">%1$s</xliff:g>"</string> <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Tuto možnost vyberte, chcete-li přepnout na aplikaci."</string> <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Přepnout mezi aplikacemi?"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index c236c97..bf3efd6 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -742,7 +742,7 @@ <string name="smv_application" msgid="295583804361236288">"Die Anwendung <xliff:g id="APPLICATION">%1$s</xliff:g> (Prozess <xliff:g id="PROCESS">%2$s</xliff:g>) hat gegen ihre selbsterzwungene StrictMode-Richtlinie verstoßen."</string> <string name="smv_process" msgid="5120397012047462446">"Der Prozess <xliff:g id="PROCESS">%1$s</xliff:g> hat gegen seine selbsterzwungene StrictMode-Richtlinie verstoßen."</string> <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> läuft"</string> - <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswahl zum Wechseln in die Anwendung"</string> + <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Auswählen zum Wechseln in die Anwendung"</string> <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Anwendung wechseln?"</string> <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Eine andere Anwendung wird bereits ausgeführt und muss vor dem Start einer neuen Anwendung beendet werden."</string> <string name="old_app_action" msgid="493129172238566282">"Zu <xliff:g id="OLD_APP">%1$s</xliff:g> zurückkehren"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 7646b57..3ab7814 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -739,8 +739,8 @@ <string name="force_close" msgid="3653416315450806396">"Forcer la fermeture"</string> <string name="report" msgid="4060218260984795706">"Rapport"</string> <string name="wait" msgid="7147118217226317732">"Attendre"</string> - <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles StrictMode."</string> - <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles StrictMode."</string> + <string name="smv_application" msgid="295583804361236288">"L\'application <xliff:g id="APPLICATION">%1$s</xliff:g> (processus <xliff:g id="PROCESS">%2$s</xliff:g>) a enfreint ses propres règles du mode strict."</string> + <string name="smv_process" msgid="5120397012047462446">"Le processus <xliff:g id="PROCESS">%1$s</xliff:g> a enfreint ses propres règles du mode strict."</string> <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> en cours d\'exécution"</string> <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Sélectionner pour changer d\'application"</string> <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Passer d\'une application à l\'autre ?"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 974e848..4176886 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -173,7 +173,7 @@ <string name="permlab_statusBar" msgid="7417192629601890791">"отключать или изменять строку состояния"</string> <string name="permdesc_statusBar" msgid="1365473595331989732">"Позволяет приложению отключать строку состояния или добавлять/удалять системные значки."</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"строка состояния"</string> - <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению быть строкой состояния."</string> + <string name="permdesc_statusBarService" msgid="4097605867643520920">"Позволяет приложению заменять строку состояния."</string> <string name="permlab_expandStatusBar" msgid="1148198785937489264">"разворачивать/сворачивать строку состояния"</string> <string name="permdesc_expandStatusBar" msgid="7088604400110768665">"Позволяет приложению разворачивать или сворачивать строку состояния."</string> <string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"перехватывать исходящие вызовы"</string> @@ -315,7 +315,7 @@ <string name="permlab_recordAudio" msgid="3876049771427466323">"записывать аудио"</string> <string name="permdesc_recordAudio" msgid="6493228261176552356">"Позволяет приложению получать доступ к пути аудиозаписи."</string> <string name="permlab_camera" msgid="3616391919559751192">"снимать фото и видео"</string> - <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры. Это дает приложению возможность в любое время получать изображения с объектива камеры."</string> + <string name="permdesc_camera" msgid="6004878235852154239">"Позволяет приложению делать снимки и видео с помощью камеры в любое время."</string> <string name="permlab_brick" msgid="8337817093326370537">"отключать телефон"</string> <string name="permdesc_brick" msgid="5569526552607599221">"Позволяет данному приложению отключить телефон навсегда. Это очень опасно."</string> <string name="permlab_reboot" msgid="2898560872462638242">"принудительно перезагружать телефон"</string> @@ -742,7 +742,7 @@ <string name="smv_application" msgid="295583804361236288">"Приложение <xliff:g id="APPLICATION">%1$s</xliff:g> (процесс <xliff:g id="PROCESS">%2$s</xliff:g>) нарушило собственную политику StrictMode."</string> <string name="smv_process" msgid="5120397012047462446">"Процесс <xliff:g id="PROCESS">%1$s</xliff:g> нарушил собственную политику StrictMode."</string> <string name="heavy_weight_notification" msgid="9087063985776626166">"Приложение <xliff:g id="APP">%1$s</xliff:g> запущено"</string> - <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы переключиться в приложение"</string> + <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Нажмите, чтобы перейти к приложению"</string> <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Переключить приложения?"</string> <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"Выполняется другое приложение, которое должно быть остановлено прежде, чем запускать новое."</string> <string name="old_app_action" msgid="493129172238566282">"Вернуться к приложению <xliff:g id="OLD_APP">%1$s</xliff:g>"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 6d56991..f2fd734 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -739,8 +739,8 @@ <string name="force_close" msgid="3653416315450806396">"Kapanmaya zorla"</string> <string name="report" msgid="4060218260984795706">"Rapor"</string> <string name="wait" msgid="7147118217226317732">"Bekle"</string> - <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string> - <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden zorunlu StrictMode politikasını ihlal etti."</string> + <string name="smv_application" msgid="295583804361236288">"<xliff:g id="APPLICATION">%1$s</xliff:g> uygulaması (<xliff:g id="PROCESS">%2$s</xliff:g> işlemi) kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string> + <string name="smv_process" msgid="5120397012047462446">"<xliff:g id="PROCESS">%1$s</xliff:g> işlemi kendiliğinden uyguladığı StrictMode politikasını ihlal etti."</string> <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> çalışıyor"</string> <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"Uygulama değiştirmeyi seçin"</string> <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"Uygulamaların arasında geçiş yapılsın mı?"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 486e125..a0fb721 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -315,7 +315,7 @@ <string name="permlab_recordAudio" msgid="3876049771427466323">"录音"</string> <string name="permdesc_recordAudio" msgid="6493228261176552356">"允许应用程序访问录音路径。"</string> <string name="permlab_camera" msgid="3616391919559751192">"拍摄照片和视频"</string> - <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中看到的图片。"</string> + <string name="permdesc_camera" msgid="6004878235852154239">"允许应用程序使用相机拍摄照片和视频,这样应用程序可随时收集进入相机镜头中的图片。"</string> <string name="permlab_brick" msgid="8337817093326370537">"永久停用手机"</string> <string name="permdesc_brick" msgid="5569526552607599221">"允许应用程序永久停用整个手机,这非常危险。"</string> <string name="permlab_reboot" msgid="2898560872462638242">"强行重新启动手机"</string> @@ -740,7 +740,7 @@ <string name="report" msgid="4060218260984795706">"报告"</string> <string name="wait" msgid="7147118217226317732">"等待"</string> <string name="smv_application" msgid="295583804361236288">"应用程序<xliff:g id="APPLICATION">%1$s</xliff:g>(<xliff:g id="PROCESS">%2$s</xliff:g> 进程)违反了自我强制执行的严格模式 (StrictMode) 政策。"</string> - <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策"</string> + <string name="smv_process" msgid="5120397012047462446">"进程 <xliff:g id="PROCESS">%1$s</xliff:g> 违反了自我强制执行的严格模式 (StrictMode) 政策。"</string> <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g>正在运行"</string> <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"选择以切换到该应用程序"</string> <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"要切换应用程序吗?"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 560d407..d5dd857 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -34,7 +34,7 @@ <string name="defaultVoiceMailAlphaTag" msgid="2660020990097733077">"語音留言"</string> <string name="defaultMsisdnAlphaTag" msgid="2850889754919584674">"MSISDN1"</string> <string name="mmiError" msgid="5154499457739052907">"連線發生問題或錯誤的 MMI 碼。"</string> - <string name="mmiFdnError" msgid="5224398216385316471">"僅允許在固定撥號時使用此操作。"</string> + <string name="mmiFdnError" msgid="5224398216385316471">"僅限對固定撥號號碼執行此作業。"</string> <string name="serviceEnabled" msgid="8147278346414714315">"服務已啟用。"</string> <string name="serviceEnabledFor" msgid="6856228140453471041">"已啟用服務:"</string> <string name="serviceDisabled" msgid="1937553226592516411">"服務已停用。"</string> @@ -173,7 +173,7 @@ <string name="permlab_statusBar" msgid="7417192629601890791">"停用或變更狀態列"</string> <string name="permdesc_statusBar" msgid="1365473595331989732">"允許應用程式停用狀態列或新增、移除系統圖示。"</string> <string name="permlab_statusBarService" msgid="7247281911387931485">"狀態列"</string> - <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式成為狀態列。"</string> + <string name="permdesc_statusBarService" msgid="4097605867643520920">"允許應用程式以狀態列顯示。"</string> <string name="permlab_expandStatusBar" msgid="1148198785937489264">"展開/收攏狀態列"</string> <string name="permdesc_expandStatusBar" msgid="7088604400110768665">"允許應用程式展開或收攏狀態列。"</string> <string name="permlab_processOutgoingCalls" msgid="1136262550878335980">"攔截撥出電話"</string> @@ -314,8 +314,8 @@ <string name="permdesc_modifyAudioSettings" msgid="5793461287365991922">"允許應用程式編輯全域音訊設定,例如音量與路由。"</string> <string name="permlab_recordAudio" msgid="3876049771427466323">"錄製音訊"</string> <string name="permdesc_recordAudio" msgid="6493228261176552356">"允許應用程式存取音訊錄製路徑。"</string> - <string name="permlab_camera" msgid="3616391919559751192">"拍照和錄影"</string> - <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影。此功能可讓應用程式隨時透過相機收集圖片。"</string> + <string name="permlab_camera" msgid="3616391919559751192">"拍照和拍攝影片"</string> + <string name="permdesc_camera" msgid="6004878235852154239">"允許應用程式使用相機拍照和錄影,此功能可讓應用程式隨時透過相機收集圖片。"</string> <string name="permlab_brick" msgid="8337817093326370537">"永久停用電話"</string> <string name="permdesc_brick" msgid="5569526552607599221">"允許應用程式永久停用手機。此項操作非常危險。"</string> <string name="permlab_reboot" msgid="2898560872462638242">"強制重開機"</string> @@ -739,12 +739,12 @@ <string name="force_close" msgid="3653416315450806396">"強制關閉"</string> <string name="report" msgid="4060218260984795706">"回報"</string> <string name="wait" msgid="7147118217226317732">"等待"</string> - <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行實施的 StrictMode 政策。"</string> - <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行實施的 StrictMode 政策。"</string> + <string name="smv_application" msgid="295583804361236288">"應用程式 <xliff:g id="APPLICATION">%1$s</xliff:g> (處理程序 <xliff:g id="PROCESS">%2$s</xliff:g>) 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string> + <string name="smv_process" msgid="5120397012047462446">"處理程序 <xliff:g id="PROCESS">%1$s</xliff:g> 已違反其自行強制實施的嚴格模式 (StrictMode) 政策。"</string> <string name="heavy_weight_notification" msgid="9087063985776626166">"<xliff:g id="APP">%1$s</xliff:g> 執行中"</string> - <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取切換應用程式"</string> + <string name="heavy_weight_notification_detail" msgid="2423977499339403402">"選取以切換到該應用程式"</string> <string name="heavy_weight_switcher_title" msgid="1135403633766694316">"切換應用程式?"</string> - <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行,才能啟動新的應用程式。"</string> + <string name="heavy_weight_switcher_text" msgid="4592075610079319667">"其他應用程式已在執行中,您必須停止執行該應用程式,才能啟動新的應用程式。"</string> <string name="old_app_action" msgid="493129172238566282">"返回 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string> <string name="old_app_description" msgid="942967900237208466">"請勿啟動新的應用程式。"</string> <string name="new_app_action" msgid="5472756926945440706">"啟動 <xliff:g id="OLD_APP">%1$s</xliff:g>"</string> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 01faaad..693ef18 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -11,11 +11,9 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, DisabledTestApp/src) \ $(call all-java-files-under, EnabledTestApp/src) -LOCAL_STATIC_JAVA_LIBRARIES += android-common - LOCAL_DX_FLAGS := --core-library -LOCAL_STATIC_JAVA_LIBRARIES := core-tests-supportlib -LOCAL_JAVA_LIBRARIES := android.test.runner android-common +LOCAL_STATIC_JAVA_LIBRARIES := core-tests-supportlib android-common +LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksCoreTests LOCAL_CERTIFICATE := platform diff --git a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java index 0fe83e1..cbd8714 100644 --- a/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java +++ b/core/tests/coretests/src/android/bluetooth/BluetoothStressTest.java @@ -17,13 +17,11 @@ package android.bluetooth; import android.app.Instrumentation; -import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; public class BluetoothStressTest extends InstrumentationTestCase { @@ -161,7 +159,6 @@ public class BluetoothStressTest extends InstrumentationTestCase { mContext.unregisterReceiver(mReceiver); } - @LargeTest public void testEnableDisable() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -172,7 +169,6 @@ public class BluetoothStressTest extends InstrumentationTestCase { } } - @LargeTest public void testDiscoverable() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); enable(adapter); @@ -186,7 +182,6 @@ public class BluetoothStressTest extends InstrumentationTestCase { disable(adapter); } - @LargeTest public void testScan() { BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); enable(adapter); @@ -336,7 +331,7 @@ public class BluetoothStressTest extends InstrumentationTestCase { mReceiver.resetFiredFlags(); if (!adapter.isEnabled()) { - fail("undiscoverable(): bluetooth not enabled"); + fail("undiscoverable() bluetooth not enabled"); } int scanMode = adapter.getScanMode(); @@ -374,7 +369,7 @@ public class BluetoothStressTest extends InstrumentationTestCase { mReceiver.resetFiredFlags(); if (!adapter.isEnabled()) { - fail("startScan(): bluetooth not enabled"); + fail("startScan() bluetooth not enabled"); } if (adapter.isDiscovering()) { @@ -404,7 +399,7 @@ public class BluetoothStressTest extends InstrumentationTestCase { mReceiver.resetFiredFlags(); if (!adapter.isEnabled()) { - fail("stopScan(): bluetooth not enabled"); + fail("stopScan() bluetooth not enabled"); } if (!adapter.isDiscovering()) { diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h index 9cddab2..92ce068 100644 --- a/include/media/stagefright/MediaDefs.h +++ b/include/media/stagefright/MediaDefs.h @@ -34,6 +34,8 @@ extern const char *MEDIA_MIMETYPE_AUDIO_MPEG; extern const char *MEDIA_MIMETYPE_AUDIO_AAC; extern const char *MEDIA_MIMETYPE_AUDIO_QCELP; extern const char *MEDIA_MIMETYPE_AUDIO_VORBIS; +extern const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW; +extern const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW; extern const char *MEDIA_MIMETYPE_AUDIO_RAW; extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4; diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h index f162231..71c6c51 100644 --- a/include/ui/InputReader.h +++ b/include/ui/InputReader.h @@ -454,6 +454,8 @@ public: virtual void reset(); virtual void process(const RawEvent* rawEvent); + virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode); + private: // Amount that trackball needs to move in order to generate a key event. static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6; diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index 6618702..5f5a4ac 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -1080,6 +1080,14 @@ void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEv 1, & pointerId, pointerCoords, mXPrecision, mYPrecision, downTime); } +int32_t TrackballInputMapper::getScanCodeState(uint32_t sourceMask, int32_t scanCode) { + if (scanCode >= BTN_MOUSE && scanCode < BTN_JOYSTICK) { + return getEventHub()->getScanCodeState(getDeviceId(), scanCode); + } else { + return AKEY_STATE_UNKNOWN; + } +} + // --- TouchInputMapper --- diff --git a/media/java/android/media/AudioEffect.java b/media/java/android/media/AudioEffect.java index aed29c3..35038fa 100644 --- a/media/java/android/media/AudioEffect.java +++ b/media/java/android/media/AudioEffect.java @@ -101,15 +101,15 @@ public class AudioEffect { public static final int STATE_INITIALIZED = 1; // to keep in sync with - // frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp + // frameworks/base/include/media/AudioEffect.h /** - * Event id for engine state change notification. + * Event id for engine control ownership change notification. */ - public static final int NATIVE_EVENT_ENABLED_STATUS = 0; + public static final int NATIVE_EVENT_CONTROL_STATUS = 0; /** - * Event id for engine control ownership change notification. + * Event id for engine state change notification. */ - public static final int NATIVE_EVENT_CONTROL_STATUS = 1; + public static final int NATIVE_EVENT_ENABLED_STATUS = 1; /** * Event id for engine parameter change notification. */ @@ -795,7 +795,7 @@ public class AudioEffect { // Interface definitions // -------------------- /** - * The OnParameterChangeListener interface defines a method called by the AudioEffect + * The OnEnableStatusChangeListener interface defines a method called by the AudioEffect * when a the enabled state of the effect engine was changed by the controlling application. */ public interface OnEnableStatusChangeListener { @@ -922,7 +922,6 @@ public class AudioEffect { if (effect == null) { return; } - if (effect.mNativeEventHandler != null) { Message m = effect.mNativeEventHandler.obtainMessage(what, arg1, arg2, obj); diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 9212708..41d2cc5 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -239,6 +239,9 @@ public class AudioService extends IAudioService.Stub { // independently change its priority) private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver(); + // Used to alter media button redirection when the phone is ringing. + private boolean mIsRinging = false; + // Devices currently connected private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>(); @@ -1956,11 +1959,16 @@ public class AudioService extends IAudioService.Stub { private final static Object mAudioFocusLock = new Object(); + private final static Object mRingingLock = new Object(); + private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { @Override public void onCallStateChanged(int state, String incomingNumber) { if (state == TelephonyManager.CALL_STATE_RINGING) { //Log.v(TAG, " CALL_STATE_RINGING"); + synchronized(mRingingLock) { + mIsRinging = true; + } int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING); if (ringVolume > 0) { requestAudioFocus(AudioManager.STREAM_RING, @@ -1970,12 +1978,18 @@ public class AudioService extends IAudioService.Stub { } } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) { //Log.v(TAG, " CALL_STATE_OFFHOOK"); + synchronized(mRingingLock) { + mIsRinging = false; + } requestAudioFocus(AudioManager.STREAM_RING, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, null, null /* both allowed to be null only for this clientId */, IN_VOICE_COMM_FOCUS_ID /*clientId*/); } else if (state == TelephonyManager.CALL_STATE_IDLE) { //Log.v(TAG, " CALL_STATE_IDLE"); + synchronized(mRingingLock) { + mIsRinging = false; + } abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID); } } @@ -2243,9 +2257,11 @@ public class AudioService extends IAudioService.Stub { // if in a call or ringing, do not break the current phone app behavior // TODO modify this to let the phone app specifically get the RC focus // add modify the phone app to take advantage of the new API - if ((getMode() == AudioSystem.MODE_IN_CALL) || - (getMode() == AudioSystem.MODE_RINGTONE)) { - return; + synchronized(mRingingLock) { + if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) || + (getMode() == AudioSystem.MODE_RINGTONE) ) { + return; + } } synchronized(mRCStack) { if (!mRCStack.empty()) { diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index 3cdf48a..0f3e245 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -218,7 +218,7 @@ status_t AudioEffect::setEnabled(bool enabled) return mIEffect->disable(); } } - return INVALID_OPERATION; + return NO_ERROR; } status_t AudioEffect::command(uint32_t cmdCode, @@ -231,7 +231,22 @@ status_t AudioEffect::command(uint32_t cmdCode, return INVALID_OPERATION; } - return mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData); + status_t status = mIEffect->command(cmdCode, cmdSize, cmdData, replySize, replyData); + if (status != NO_ERROR) { + return status; + } + status = *(status_t *)replyData; + if (status != NO_ERROR) { + return status; + } + + if (cmdCode == EFFECT_CMD_ENABLE) { + android_atomic_or(1, &mEnabled); + } + if (cmdCode == EFFECT_CMD_DISABLE) { + android_atomic_and(~1, &mEnabled); + } + return status; } @@ -347,7 +362,11 @@ void AudioEffect::enableStatusChanged(bool enabled) { LOGV("enableStatusChanged %p enabled %d mCbf %p", this, enabled, mCbf); if (mStatus == ALREADY_EXISTS) { - mEnabled = enabled; + if (enabled) { + android_atomic_or(1, &mEnabled); + } else { + android_atomic_and(~1, &mEnabled); + } if (mCbf) { mCbf(EVENT_ENABLE_STATUS_CHANGED, mUserData, &enabled); } diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index fb85287..b8b2f3f 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -79,6 +79,7 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_httplive \ libstagefright_rtsp \ libstagefright_id3 \ + libstagefright_g711dec \ LOCAL_SHARED_LIBRARIES += \ libstagefright_amrnb_common \ diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index e426fca..efdad43 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -854,18 +854,6 @@ void AwesomePlayer::setVideoSource(sp<MediaSource> source) { status_t AwesomePlayer::initVideoDecoder() { uint32_t flags = 0; -#if 0 - if (mRTPSession != NULL) { - // XXX hack. - - const char *mime; - CHECK(mVideoTrack->getFormat()->findCString(kKeyMIMEType, &mime)); - if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { - flags |= OMXCodec::kPreferSoftwareCodecs; - } - } -#endif - mVideoSource = OMXCodec::Create( mClient.interface(), mVideoTrack->getFormat(), false, // createEncoder @@ -1019,6 +1007,12 @@ void AwesomePlayer::onVideoEvent() { int64_t latenessUs = nowUs - timeUs; + if (mRTPSession != NULL) { + // We'll completely ignore timestamps for gtalk videochat + // and we'll play incoming video as fast as we get it. + latenessUs = 0; + } + if (latenessUs > 40000) { // We're more than 40ms late. LOGV("we're late by %lld us (%.2f secs)", latenessUs, latenessUs / 1E6); diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp index 9c99866..ccc6a34 100644 --- a/media/libstagefright/HTTPStream.cpp +++ b/media/libstagefright/HTTPStream.cpp @@ -68,7 +68,7 @@ status_t HTTPStream::connect(const char *server, int port) { return UNKNOWN_ERROR; } - setReceiveTimeout(5); // Time out reads after 5 secs by default + setReceiveTimeout(30); // Time out reads after 30 secs by default mState = CONNECTING; @@ -158,7 +158,7 @@ status_t HTTPStream::send(const char *data) { // The workaround accepts both behaviours but could potentially break // legitimate responses that use a single newline to "fold" headers, which is // why it's not yet on by default. -#define WORKAROUND_FOR_MISSING_CR 0 +#define WORKAROUND_FOR_MISSING_CR 1 status_t HTTPStream::receive_line(char *line, size_t size) { if (mState != CONNECTED) { diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 0d8c3c6..c860c5c 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -38,6 +38,9 @@ namespace android { +static const uint8_t kNalUnitTypeSeqParamSet = 0x07; +static const uint8_t kNalUnitTypePicParamSet = 0x08; + class MPEG4Writer::Track { public: Track(MPEG4Writer *owner, const sp<MediaSource> &source); @@ -111,6 +114,20 @@ private: }; List<SttsTableEntry> mSttsTableEntries; + // Sequence parameter set or picture parameter set + struct AVCParamSet { + AVCParamSet(uint16_t length, const uint8_t *data) + : mLength(length), mData(data) {} + + uint16_t mLength; + const uint8_t *mData; + }; + List<AVCParamSet> mSeqParamSets; + List<AVCParamSet> mPicParamSets; + uint8_t mProfileIdc; + uint8_t mProfileCompatible; + uint8_t mLevelIdc; + void *mCodecSpecificData; size_t mCodecSpecificDataSize; bool mGotAllCodecSpecificData; @@ -124,8 +141,15 @@ private: static void *ThreadWrapper(void *me); void threadEntry(); + const uint8_t *parseParamSet( + const uint8_t *data, size_t length, int type, size_t *paramSetLen); + status_t makeAVCCodecSpecificData( const uint8_t *data, size_t size); + status_t copyAVCCodecSpecificData( + const uint8_t *data, size_t size); + status_t parseAVCCodecSpecificData( + const uint8_t *data, size_t size); // Track authoring progress status void trackProgressStatus(int64_t timeUs, status_t err = OK); @@ -1038,6 +1062,174 @@ static void hexdump(const void *_data, size_t size) { } } +static void getNalUnitType(uint8_t byte, uint8_t* type) { + LOGV("getNalUnitType: %d", byte); + + // nal_unit_type: 5-bit unsigned integer + *type = (byte & 0x1F); +} + +static const uint8_t *findNextStartCode( + const uint8_t *data, size_t length) { + + LOGV("findNextStartCode: %p %d", data, length); + + size_t bytesLeft = length; + while (bytesLeft > 4 && + memcmp("\x00\x00\x00\x01", &data[length - bytesLeft], 4)) { + --bytesLeft; + } + if (bytesLeft <= 4) { + bytesLeft = 0; // Last parameter set + } + return &data[length - bytesLeft]; +} + +const uint8_t *MPEG4Writer::Track::parseParamSet( + const uint8_t *data, size_t length, int type, size_t *paramSetLen) { + + LOGV("parseParamSet"); + CHECK(type == kNalUnitTypeSeqParamSet || + type == kNalUnitTypePicParamSet); + + const uint8_t *nextStartCode = findNextStartCode(data, length); + *paramSetLen = nextStartCode - data; + if (*paramSetLen == 0) { + LOGE("Param set is malformed, since its length is 0"); + return NULL; + } + + AVCParamSet paramSet(*paramSetLen, data); + if (type == kNalUnitTypeSeqParamSet) { + if (*paramSetLen < 4) { + LOGE("Seq parameter set malformed"); + return NULL; + } + if (mSeqParamSets.empty()) { + mProfileIdc = data[1]; + mProfileCompatible = data[2]; + mLevelIdc = data[3]; + } else { + if (mProfileIdc != data[1] || + mProfileCompatible != data[2] || + mLevelIdc != data[3]) { + LOGE("Inconsistent profile/level found in seq parameter sets"); + return NULL; + } + } + mSeqParamSets.push_back(paramSet); + } else { + mPicParamSets.push_back(paramSet); + } + return nextStartCode; +} + +status_t MPEG4Writer::Track::copyAVCCodecSpecificData( + const uint8_t *data, size_t size) { + LOGV("copyAVCCodecSpecificData"); + + // 2 bytes for each of the parameter set length field + // plus the 7 bytes for the header + if (size < 4 + 7) { + LOGE("Codec specific data length too short: %d", size); + return ERROR_MALFORMED; + } + + mCodecSpecificDataSize = size; + mCodecSpecificData = malloc(size); + memcpy(mCodecSpecificData, data, size); + return OK; +} + +status_t MPEG4Writer::Track::parseAVCCodecSpecificData( + const uint8_t *data, size_t size) { + + LOGV("parseAVCCodecSpecificData"); + // Data starts with a start code. + // SPS and PPS are separated with start codes. + // Also, SPS must come before PPS + uint8_t type = kNalUnitTypeSeqParamSet; + bool gotSps = false; + bool gotPps = false; + const uint8_t *tmp = data; + const uint8_t *nextStartCode = data; + size_t bytesLeft = size; + size_t paramSetLen = 0; + mCodecSpecificDataSize = 0; + while (bytesLeft > 4 && !memcmp("\x00\x00\x00\x01", tmp, 4)) { + getNalUnitType(*(tmp + 4), &type); + if (type == kNalUnitTypeSeqParamSet) { + if (gotPps) { + LOGE("SPS must come before PPS"); + return ERROR_MALFORMED; + } + if (!gotSps) { + gotSps = true; + } + nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen); + } else if (type == kNalUnitTypePicParamSet) { + if (!gotSps) { + LOGE("SPS must come before PPS"); + return ERROR_MALFORMED; + } + if (!gotPps) { + gotPps = true; + } + nextStartCode = parseParamSet(tmp + 4, bytesLeft - 4, type, ¶mSetLen); + } else { + LOGE("Only SPS and PPS Nal units are expected"); + return ERROR_MALFORMED; + } + + if (nextStartCode == NULL) { + return ERROR_MALFORMED; + } + + // Move on to find the next parameter set + bytesLeft -= nextStartCode - tmp; + tmp = nextStartCode; + mCodecSpecificDataSize += (2 + paramSetLen); + } + + { + // Check on the number of seq parameter sets + size_t nSeqParamSets = mSeqParamSets.size(); + if (nSeqParamSets == 0) { + LOGE("Cound not find sequence parameter set"); + return ERROR_MALFORMED; + } + + if (nSeqParamSets > 0x1F) { + LOGE("Too many seq parameter sets (%d) found", nSeqParamSets); + return ERROR_MALFORMED; + } + } + + { + // Check on the number of pic parameter sets + size_t nPicParamSets = mPicParamSets.size(); + if (nPicParamSets == 0) { + LOGE("Cound not find picture parameter set"); + return ERROR_MALFORMED; + } + if (nPicParamSets > 0xFF) { + LOGE("Too many pic parameter sets (%d) found", nPicParamSets); + return ERROR_MALFORMED; + } + } + + { + // Check on the profiles + // These profiles requires additional parameter set extensions + if (mProfileIdc == 100 || mProfileIdc == 110 || + mProfileIdc == 122 || mProfileIdc == 144) { + LOGE("Sorry, no support for profile_idc: %d!", mProfileIdc); + return BAD_VALUE; + } + } + + return OK; +} status_t MPEG4Writer::Track::makeAVCCodecSpecificData( const uint8_t *data, size_t size) { @@ -1048,50 +1240,67 @@ status_t MPEG4Writer::Track::makeAVCCodecSpecificData( return ERROR_MALFORMED; } - if (size < 4 || memcmp("\x00\x00\x00\x01", data, 4)) { - LOGE("Must start with a start code"); + if (size < 4) { + LOGE("Codec specific data length too short: %d", size); return ERROR_MALFORMED; } - size_t picParamOffset = 4; - while (picParamOffset + 3 < size - && memcmp("\x00\x00\x00\x01", &data[picParamOffset], 4)) { - ++picParamOffset; + // Data is in the form of AVCCodecSpecificData + if (memcmp("\x00\x00\x00\x01", data, 4)) { + return copyAVCCodecSpecificData(data, size); } - if (picParamOffset + 3 >= size) { - LOGE("Could not find start-code for pictureParameterSet"); + if (parseAVCCodecSpecificData(data, size) != OK) { return ERROR_MALFORMED; } - size_t seqParamSetLength = picParamOffset - 4; - size_t picParamSetLength = size - picParamOffset - 4; - - mCodecSpecificDataSize = - 6 + 1 + seqParamSetLength + 2 + picParamSetLength + 2; - + // ISO 14496-15: AVC file format + mCodecSpecificDataSize += 7; // 7 more bytes in the header mCodecSpecificData = malloc(mCodecSpecificDataSize); uint8_t *header = (uint8_t *)mCodecSpecificData; - header[0] = 1; - header[1] = 0x42; // profile - header[2] = 0x80; - header[3] = 0x1e; // level + header[0] = 1; // version + header[1] = mProfileIdc; // profile indication + header[2] = mProfileCompatible; // profile compatibility + header[3] = mLevelIdc; + // 6-bit '111111' followed by 2-bit to lengthSizeMinuusOne #if USE_NALLEN_FOUR header[4] = 0xfc | 3; // length size == 4 bytes #else header[4] = 0xfc | 1; // length size == 2 bytes #endif - header[5] = 0xe0 | 1; - header[6] = seqParamSetLength >> 8; - header[7] = seqParamSetLength & 0xff; - memcpy(&header[8], &data[4], seqParamSetLength); - header += 8 + seqParamSetLength; - header[0] = 1; - header[1] = picParamSetLength >> 8; - header[2] = picParamSetLength & 0xff; - memcpy(&header[3], &data[picParamOffset + 4], picParamSetLength); + // 3-bit '111' followed by 5-bit numSequenceParameterSets + int nSequenceParamSets = mSeqParamSets.size(); + header[5] = 0xe0 | nSequenceParamSets; + header += 6; + for (List<AVCParamSet>::iterator it = mSeqParamSets.begin(); + it != mSeqParamSets.end(); ++it) { + // 16-bit sequence parameter set length + uint16_t seqParamSetLength = it->mLength; + header[0] = seqParamSetLength >> 8; + header[1] = seqParamSetLength & 0xff; + + // SPS NAL unit (sequence parameter length bytes) + memcpy(&header[2], it->mData, seqParamSetLength); + header += (2 + seqParamSetLength); + } + + // 8-bit nPictureParameterSets + int nPictureParamSets = mPicParamSets.size(); + header[0] = nPictureParamSets; + header += 1; + for (List<AVCParamSet>::iterator it = mPicParamSets.begin(); + it != mPicParamSets.end(); ++it) { + // 16-bit picture parameter set length + uint16_t picParamSetLength = it->mLength; + header[0] = picParamSetLength >> 8; + header[1] = picParamSetLength & 0xff; + + // PPS Nal unit (picture parameter set length bytes) + memcpy(&header[2], it->mData, picParamSetLength); + header += (2 + picParamSetLength); + } return OK; } @@ -1168,91 +1377,6 @@ void MPEG4Writer::Track::threadEntry() { mGotAllCodecSpecificData = true; continue; - } else if (!mGotAllCodecSpecificData && - count == 1 && mIsMPEG4 && mCodecSpecificData == NULL) { - // The TI mpeg4 encoder does not properly set the - // codec-specific-data flag. - - const uint8_t *data = - (const uint8_t *)buffer->data() + buffer->range_offset(); - - const size_t size = buffer->range_length(); - - size_t offset = 0; - while (offset + 3 < size) { - if (data[offset] == 0x00 && data[offset + 1] == 0x00 - && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) { - break; - } - - ++offset; - } - - // CHECK(offset + 3 < size); - if (offset + 3 >= size) { - // XXX assume the entire first chunk of data is the codec specific - // data. - offset = size; - } - - mCodecSpecificDataSize = offset; - mCodecSpecificData = malloc(offset); - memcpy(mCodecSpecificData, data, offset); - - buffer->set_range(buffer->range_offset() + offset, size - offset); - - if (size == offset) { - buffer->release(); - buffer = NULL; - - continue; - } - - mGotAllCodecSpecificData = true; - } else if (!mGotAllCodecSpecificData && mIsAvc && count < 3) { - // The TI video encoder does not flag codec specific data - // as such and also splits up SPS and PPS across two buffers. - - const uint8_t *data = - (const uint8_t *)buffer->data() + buffer->range_offset(); - - size_t size = buffer->range_length(); - - CHECK(count == 2 || mCodecSpecificData == NULL); - - size_t offset = mCodecSpecificDataSize; - mCodecSpecificDataSize += size + 4; - mCodecSpecificData = - realloc(mCodecSpecificData, mCodecSpecificDataSize); - - memcpy((uint8_t *)mCodecSpecificData + offset, - "\x00\x00\x00\x01", 4); - - memcpy((uint8_t *)mCodecSpecificData + offset + 4, data, size); - - buffer->release(); - buffer = NULL; - - if (count == 2) { - void *tmp = mCodecSpecificData; - size = mCodecSpecificDataSize; - mCodecSpecificData = NULL; - mCodecSpecificDataSize = 0; - - status_t err = makeAVCCodecSpecificData( - (const uint8_t *)tmp, size); - free(tmp); - tmp = NULL; - CHECK_EQ(OK, err); - - mGotAllCodecSpecificData = true; - } - - continue; - } - - if (!mGotAllCodecSpecificData) { - mGotAllCodecSpecificData = true; } // Make a deep copy of the MediaBuffer and Metadata and release @@ -1753,6 +1877,8 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt32(samplerate << 16); if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime)) { mOwner->beginBox("esds"); + CHECK(mCodecSpecificData); + CHECK(mCodecSpecificDataSize > 0); mOwner->writeInt32(0); // version=0, flags=0 mOwner->writeInt8(0x03); // ES_DescrTag @@ -1830,6 +1956,8 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->writeInt16(0x18); // depth mOwner->writeInt16(-1); // predefined + CHECK(mCodecSpecificData); + CHECK(mCodecSpecificDataSize > 0); CHECK(23 + mCodecSpecificDataSize < 128); if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { @@ -1877,6 +2005,8 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->endBox(); // d263 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { + CHECK(mCodecSpecificData); + CHECK(mCodecSpecificDataSize > 0); mOwner->beginBox("avcC"); mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); mOwner->endBox(); // avcC diff --git a/media/libstagefright/MediaDefs.cpp b/media/libstagefright/MediaDefs.cpp index 39d264c..7648d42 100644 --- a/media/libstagefright/MediaDefs.cpp +++ b/media/libstagefright/MediaDefs.cpp @@ -32,6 +32,8 @@ const char *MEDIA_MIMETYPE_AUDIO_MPEG = "audio/mpeg"; const char *MEDIA_MIMETYPE_AUDIO_AAC = "audio/mp4a-latm"; const char *MEDIA_MIMETYPE_AUDIO_QCELP = "audio/qcelp"; const char *MEDIA_MIMETYPE_AUDIO_VORBIS = "audio/vorbis"; +const char *MEDIA_MIMETYPE_AUDIO_G711_ALAW = "audio/g711-alaw"; +const char *MEDIA_MIMETYPE_AUDIO_G711_MLAW = "audio/g711-mlaw"; const char *MEDIA_MIMETYPE_AUDIO_RAW = "audio/raw"; const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4"; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 4f3bffd..4741b1d 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -26,6 +26,7 @@ #include "include/AMRWBEncoder.h" #include "include/AVCDecoder.h" #include "include/AVCEncoder.h" +#include "include/G711Decoder.h" #include "include/M4vH263Decoder.h" #include "include/M4vH263Encoder.h" #include "include/MP3Decoder.h" @@ -77,6 +78,7 @@ FACTORY_CREATE(AMRNBDecoder) FACTORY_CREATE(AMRWBDecoder) FACTORY_CREATE(AACDecoder) FACTORY_CREATE(AVCDecoder) +FACTORY_CREATE(G711Decoder) FACTORY_CREATE(M4vH263Decoder) FACTORY_CREATE(VorbisDecoder) FACTORY_CREATE(VPXDecoder) @@ -124,6 +126,7 @@ static sp<MediaSource> InstantiateSoftwareCodec( FACTORY_REF(AMRWBDecoder) FACTORY_REF(AACDecoder) FACTORY_REF(AVCDecoder) + FACTORY_REF(G711Decoder) FACTORY_REF(M4vH263Decoder) FACTORY_REF(VorbisDecoder) FACTORY_REF(VPXDecoder) @@ -155,6 +158,8 @@ static const CodecInfo kDecoderInfo[] = { { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" }, { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" }, // { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" }, + { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "G711Decoder" }, + { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "G711Decoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" }, @@ -1680,6 +1685,14 @@ void OMXCodec::on_message(const omx_message &msg) { MediaBuffer *buffer = info->mMediaBuffer; + if (msg.u.extended_buffer_data.range_offset + + msg.u.extended_buffer_data.range_length + > buffer->size()) { + CODEC_LOGE( + "Codec lied about its buffer size requirements, " + "sending a buffer larger than the originally " + "advertised size in FILL_BUFFER_DONE!"); + } buffer->set_range( msg.u.extended_buffer_data.range_offset, msg.u.extended_buffer_data.range_length); diff --git a/media/libstagefright/WAVExtractor.cpp b/media/libstagefright/WAVExtractor.cpp index 39b1b96..8d820c0 100644 --- a/media/libstagefright/WAVExtractor.cpp +++ b/media/libstagefright/WAVExtractor.cpp @@ -31,7 +31,11 @@ namespace android { -static uint16_t WAVE_FORMAT_PCM = 1; +enum { + WAVE_FORMAT_PCM = 1, + WAVE_FORMAT_ALAW = 6, + WAVE_FORMAT_MULAW = 7, +}; static uint32_t U32_LE_AT(const uint8_t *ptr) { return ptr[3] << 24 | ptr[2] << 16 | ptr[1] << 8 | ptr[0]; @@ -45,6 +49,7 @@ struct WAVSource : public MediaSource { WAVSource( const sp<DataSource> &dataSource, const sp<MetaData> &meta, + uint16_t waveFormat, int32_t bitsPerSample, off_t offset, size_t size); @@ -63,6 +68,7 @@ private: sp<DataSource> mDataSource; sp<MetaData> mMeta; + uint16_t mWaveFormat; int32_t mSampleRate; int32_t mNumChannels; int32_t mBitsPerSample; @@ -108,7 +114,7 @@ sp<MediaSource> WAVExtractor::getTrack(size_t index) { return new WAVSource( mDataSource, mTrackMeta, - mBitsPerSample, mDataOffset, mDataSize); + mWaveFormat, mBitsPerSample, mDataOffset, mDataSize); } sp<MetaData> WAVExtractor::getTrackMetaData( @@ -160,8 +166,10 @@ status_t WAVExtractor::init() { return NO_INIT; } - uint16_t format = U16_LE_AT(formatSpec); - if (format != WAVE_FORMAT_PCM) { + mWaveFormat = U16_LE_AT(formatSpec); + if (mWaveFormat != WAVE_FORMAT_PCM + && mWaveFormat != WAVE_FORMAT_ALAW + && mWaveFormat != WAVE_FORMAT_MULAW) { return ERROR_UNSUPPORTED; } @@ -178,9 +186,17 @@ status_t WAVExtractor::init() { mBitsPerSample = U16_LE_AT(&formatSpec[14]); - if (mBitsPerSample != 8 && mBitsPerSample != 16 - && mBitsPerSample != 24) { - return ERROR_UNSUPPORTED; + if (mWaveFormat == WAVE_FORMAT_PCM) { + if (mBitsPerSample != 8 && mBitsPerSample != 16 + && mBitsPerSample != 24) { + return ERROR_UNSUPPORTED; + } + } else { + CHECK(mWaveFormat == WAVE_FORMAT_MULAW + || mWaveFormat == WAVE_FORMAT_ALAW); + if (mBitsPerSample != 8) { + return ERROR_UNSUPPORTED; + } } mValidFormat = true; @@ -190,7 +206,23 @@ status_t WAVExtractor::init() { mDataSize = chunkSize; mTrackMeta = new MetaData; - mTrackMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + + switch (mWaveFormat) { + case WAVE_FORMAT_PCM: + mTrackMeta->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + break; + case WAVE_FORMAT_ALAW: + mTrackMeta->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW); + break; + default: + CHECK_EQ(mWaveFormat, WAVE_FORMAT_MULAW); + mTrackMeta->setCString( + kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW); + break; + } + mTrackMeta->setInt32(kKeyChannelCount, mNumChannels); mTrackMeta->setInt32(kKeySampleRate, mSampleRate); @@ -217,10 +249,12 @@ const size_t WAVSource::kMaxFrameSize = 32768; WAVSource::WAVSource( const sp<DataSource> &dataSource, const sp<MetaData> &meta, + uint16_t waveFormat, int32_t bitsPerSample, off_t offset, size_t size) : mDataSource(dataSource), mMeta(meta), + mWaveFormat(waveFormat), mSampleRate(0), mNumChannels(0), mBitsPerSample(bitsPerSample), @@ -312,43 +346,45 @@ status_t WAVSource::read( buffer->set_range(0, n); - if (mBitsPerSample == 8) { - // Convert 8-bit unsigned samples to 16-bit signed. + if (mWaveFormat == WAVE_FORMAT_PCM) { + if (mBitsPerSample == 8) { + // Convert 8-bit unsigned samples to 16-bit signed. - MediaBuffer *tmp; - CHECK_EQ(mGroup->acquire_buffer(&tmp), OK); + MediaBuffer *tmp; + CHECK_EQ(mGroup->acquire_buffer(&tmp), OK); - // The new buffer holds the sample number of samples, but each - // one is 2 bytes wide. - tmp->set_range(0, 2 * n); + // The new buffer holds the sample number of samples, but each + // one is 2 bytes wide. + tmp->set_range(0, 2 * n); - int16_t *dst = (int16_t *)tmp->data(); - const uint8_t *src = (const uint8_t *)buffer->data(); - while (n-- > 0) { - *dst++ = ((int16_t)(*src) - 128) * 256; - ++src; - } + int16_t *dst = (int16_t *)tmp->data(); + const uint8_t *src = (const uint8_t *)buffer->data(); + while (n-- > 0) { + *dst++ = ((int16_t)(*src) - 128) * 256; + ++src; + } - buffer->release(); - buffer = tmp; - } else if (mBitsPerSample == 24) { - // Convert 24-bit signed samples to 16-bit signed. - - const uint8_t *src = - (const uint8_t *)buffer->data() + buffer->range_offset(); - int16_t *dst = (int16_t *)src; - - size_t numSamples = buffer->range_length() / 3; - for (size_t i = 0; i < numSamples; ++i) { - int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16); - x = (x << 8) >> 8; // sign extension - - x = x >> 8; - *dst++ = (int16_t)x; - src += 3; - } + buffer->release(); + buffer = tmp; + } else if (mBitsPerSample == 24) { + // Convert 24-bit signed samples to 16-bit signed. + + const uint8_t *src = + (const uint8_t *)buffer->data() + buffer->range_offset(); + int16_t *dst = (int16_t *)src; + + size_t numSamples = buffer->range_length() / 3; + for (size_t i = 0; i < numSamples; ++i) { + int32_t x = (int32_t)(src[0] | src[1] << 8 | src[2] << 16); + x = (x << 8) >> 8; // sign extension - buffer->set_range(buffer->range_offset(), 2 * numSamples); + x = x >> 8; + *dst++ = (int16_t)x; + src += 3; + } + + buffer->set_range(buffer->range_offset(), 2 * numSamples); + } } size_t bytesPerSample = mBitsPerSample >> 3; diff --git a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp index d5eb156..6e74279 100644 --- a/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp +++ b/media/libstagefright/codecs/avc/enc/AVCEncoder.cpp @@ -337,14 +337,18 @@ status_t AVCEncoder::read( MediaBuffer *outputBuffer; CHECK_EQ(OK, mGroup->acquire_buffer(&outputBuffer)); - uint8_t *outPtr = (uint8_t *) outputBuffer->data(); - uint32_t dataLength = outputBuffer->size(); + + // Add 4 bytes for the start code 0x00000001 + uint8_t *outPtr = (uint8_t *) outputBuffer->data() + 4; + uint32_t dataLength = outputBuffer->size() - 4; int32_t type; AVCEnc_Status encoderStatus = AVCENC_SUCCESS; - // Return SPS and PPS for the first two buffers - if (!mSpsPpsHeaderReceived) { + // Combine SPS and PPS and place them in the very first output buffer + // SPS and PPS are separated by start code 0x00000001 + // Assume that we have exactly one SPS and exactly one PPS. + while (!mSpsPpsHeaderReceived && mNumInputFrames <= 0) { encoderStatus = PVAVCEncodeNAL(mHandle, outPtr, &dataLength, &type); if (encoderStatus == AVCENC_WRONG_STATE) { mSpsPpsHeaderReceived = true; @@ -352,11 +356,22 @@ status_t AVCEncoder::read( } else { switch (type) { case AVC_NALTYPE_SPS: + ++mNumInputFrames; + memcpy(outputBuffer->data(), "\x00\x00\x00\x01", 4); + outputBuffer->set_range(0, dataLength + 4); + outPtr += (dataLength + 4); // 4 bytes for next start code + dataLength = outputBuffer->size() - + (outputBuffer->range_length() + 4); + break; case AVC_NALTYPE_PPS: - LOGV("%s received", - (type == AVC_NALTYPE_SPS)? "SPS": "PPS"); ++mNumInputFrames; - outputBuffer->set_range(0, dataLength); + memcpy(((uint8_t *) outputBuffer->data()) + + outputBuffer->range_length(), + "\x00\x00\x00\x01", 4); + outputBuffer->set_range(0, + dataLength + outputBuffer->range_length() + 4); + outputBuffer->meta_data()->setInt32(kKeyIsCodecConfig, 1); + outputBuffer->meta_data()->setInt64(kKeyTime, 0); *out = outputBuffer; return OK; default: @@ -376,8 +391,18 @@ status_t AVCEncoder::read( if (err != OK) { LOGE("Failed to read input video frame: %d", err); outputBuffer->release(); + mInputBuffer->release(); + mInputBuffer = NULL; return err; } + + if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) { + outputBuffer->release(); + mInputBuffer->release(); + mInputBuffer = NULL; + return UNKNOWN_ERROR; + } + int64_t timeUs; CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); outputBuffer->meta_data()->setInt64(kKeyTime, timeUs); diff --git a/media/libstagefright/codecs/g711/Android.mk b/media/libstagefright/codecs/g711/Android.mk new file mode 100644 index 0000000..2e43120 --- /dev/null +++ b/media/libstagefright/codecs/g711/Android.mk @@ -0,0 +1,4 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/media/libstagefright/codecs/g711/dec/Android.mk b/media/libstagefright/codecs/g711/dec/Android.mk new file mode 100644 index 0000000..cfb9fe4 --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/Android.mk @@ -0,0 +1,12 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + G711Decoder.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/media/libstagefright/include \ + +LOCAL_MODULE := libstagefright_g711dec + +include $(BUILD_STATIC_LIBRARY) diff --git a/media/libstagefright/codecs/g711/dec/G711Decoder.cpp b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp new file mode 100644 index 0000000..4414e4e --- /dev/null +++ b/media/libstagefright/codecs/g711/dec/G711Decoder.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2010 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "G711Decoder" +#include <utils/Log.h> + +#include "G711Decoder.h" + +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MetaData.h> + +static const size_t kMaxNumSamplesPerFrame = 16384; + +namespace android { + +G711Decoder::G711Decoder(const sp<MediaSource> &source) + : mSource(source), + mStarted(false), + mBufferGroup(NULL) { +} + +G711Decoder::~G711Decoder() { + if (mStarted) { + stop(); + } +} + +status_t G711Decoder::start(MetaData *params) { + CHECK(!mStarted); + + const char *mime; + CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); + + mIsMLaw = false; + if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) { + mIsMLaw = true; + } else if (strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)) { + return ERROR_UNSUPPORTED; + } + + mBufferGroup = new MediaBufferGroup; + mBufferGroup->add_buffer( + new MediaBuffer(kMaxNumSamplesPerFrame * sizeof(int16_t))); + + mSource->start(); + + mStarted = true; + + return OK; +} + +status_t G711Decoder::stop() { + CHECK(mStarted); + + delete mBufferGroup; + mBufferGroup = NULL; + + mSource->stop(); + + mStarted = false; + + return OK; +} + +sp<MetaData> G711Decoder::getFormat() { + sp<MetaData> srcFormat = mSource->getFormat(); + + int32_t numChannels; + int32_t sampleRate; + + CHECK(srcFormat->findInt32(kKeyChannelCount, &numChannels)); + CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate)); + + sp<MetaData> meta = new MetaData; + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW); + meta->setInt32(kKeyChannelCount, numChannels); + meta->setInt32(kKeySampleRate, sampleRate); + + int64_t durationUs; + if (srcFormat->findInt64(kKeyDuration, &durationUs)) { + meta->setInt64(kKeyDuration, durationUs); + } + + meta->setCString(kKeyDecoderComponent, "G711Decoder"); + + return meta; +} + +status_t G711Decoder::read( + MediaBuffer **out, const ReadOptions *options) { + status_t err; + + *out = NULL; + + int64_t seekTimeUs; + ReadOptions::SeekMode mode; + if (options && options->getSeekTo(&seekTimeUs, &mode)) { + CHECK(seekTimeUs >= 0); + } else { + seekTimeUs = -1; + } + + MediaBuffer *inBuffer; + err = mSource->read(&inBuffer, options); + + if (err != OK) { + return err; + } + + if (inBuffer->range_length() > kMaxNumSamplesPerFrame) { + LOGE("input buffer too large (%d).", inBuffer->range_length()); + + inBuffer->release(); + inBuffer = NULL; + + return ERROR_UNSUPPORTED; + } + + int64_t timeUs; + CHECK(inBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); + + const uint8_t *inputPtr = + (const uint8_t *)inBuffer->data() + inBuffer->range_offset(); + + MediaBuffer *outBuffer; + CHECK_EQ(mBufferGroup->acquire_buffer(&outBuffer), OK); + + if (mIsMLaw) { + DecodeMLaw( + static_cast<int16_t *>(outBuffer->data()), + inputPtr, inBuffer->range_length()); + } else { + DecodeALaw( + static_cast<int16_t *>(outBuffer->data()), + inputPtr, inBuffer->range_length()); + } + + // Each 8-bit byte is converted into a 16-bit sample. + outBuffer->set_range(0, inBuffer->range_length() * 2); + + outBuffer->meta_data()->setInt64(kKeyTime, timeUs); + + inBuffer->release(); + inBuffer = NULL; + + *out = outBuffer; + + return OK; +} + +// static +void G711Decoder::DecodeALaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize-- > 0) { + int32_t x = *in++; + + int32_t ix = x ^ 0x55; + ix &= 0x7f; + + int32_t iexp = ix >> 4; + int32_t mant = ix & 0x0f; + + if (iexp > 0) { + mant += 16; + } + + mant = (mant << 4) + 8; + + if (iexp > 1) { + mant = mant << (iexp - 1); + } + + *out++ = (x > 127) ? mant : -mant; + } +} + +// static +void G711Decoder::DecodeMLaw( + int16_t *out, const uint8_t *in, size_t inSize) { + while (inSize-- > 0) { + int32_t x = *in++; + + int32_t mantissa = ~x; + int32_t exponent = (mantissa >> 4) & 7; + int32_t segment = exponent + 1; + mantissa &= 0x0f; + + int32_t step = 4 << segment; + + int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33; + + *out++ = (x < 0x80) ? -abs : abs; + } +} + +} // namespace android diff --git a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp index 5002442..1bef0e9 100644 --- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp +++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp @@ -292,8 +292,18 @@ status_t M4vH263Encoder::read( if (OK != mSource->read(&mInputBuffer, options)) { LOGE("Failed to read from data source"); outputBuffer->release(); + mInputBuffer->release(); + mInputBuffer = NULL; return UNKNOWN_ERROR; } + + if (mInputBuffer->size() - ((mVideoWidth * mVideoHeight * 3) >> 1) != 0) { + outputBuffer->release(); + mInputBuffer->release(); + mInputBuffer = NULL; + return UNKNOWN_ERROR; + } + int64_t timeUs; CHECK(mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); if (mNextModTimeUs > timeUs) { diff --git a/media/libstagefright/include/G711Decoder.h b/media/libstagefright/include/G711Decoder.h new file mode 100644 index 0000000..8b5143a --- /dev/null +++ b/media/libstagefright/include/G711Decoder.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2010 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. + */ + +#ifndef G711_DECODER_H_ + +#define G711_DECODER_H_ + +#include <media/stagefright/MediaSource.h> + +namespace android { + +struct MediaBufferGroup; + +struct G711Decoder : public MediaSource { + G711Decoder(const sp<MediaSource> &source); + + virtual status_t start(MetaData *params); + virtual status_t stop(); + + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options); + +protected: + virtual ~G711Decoder(); + +private: + sp<MediaSource> mSource; + bool mStarted; + bool mIsMLaw; + + MediaBufferGroup *mBufferGroup; + + static void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize); + static void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize); + + G711Decoder(const G711Decoder &); + G711Decoder &operator=(const G711Decoder &); +}; + +} // namespace android + +#endif // G711_DECODER_H_ diff --git a/media/libstagefright/include/WAVExtractor.h b/media/libstagefright/include/WAVExtractor.h index 9384942..3e847b9 100644 --- a/media/libstagefright/include/WAVExtractor.h +++ b/media/libstagefright/include/WAVExtractor.h @@ -43,6 +43,7 @@ private: sp<DataSource> mDataSource; status_t mInitCheck; bool mValidFormat; + uint16_t mWaveFormat; uint16_t mNumChannels; uint32_t mSampleRate; uint16_t mBitsPerSample; diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp index a577704..395cd28 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -356,24 +356,10 @@ status_t APacketSource::read( if (!mBuffers.empty()) { const sp<ABuffer> buffer = *mBuffers.begin(); - uint64_t ntpTime; - CHECK(buffer->meta()->findInt64( - "ntp-time", (int64_t *)&ntpTime)); - MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size()); - mediaBuffer->meta_data()->setInt64(kKeyNTPTime, ntpTime); - - if (mFirstAccessUnit) { - mFirstAccessUnit = false; - mFirstAccessUnitNTP = ntpTime; - } - if (ntpTime > mFirstAccessUnitNTP) { - ntpTime -= mFirstAccessUnitNTP; - } else { - ntpTime = 0; - } - int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32)); + int64_t timeUs; + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs); @@ -390,10 +376,29 @@ status_t APacketSource::read( void APacketSource::queueAccessUnit(const sp<ABuffer> &buffer) { int32_t damaged; if (buffer->meta()->findInt32("damaged", &damaged) && damaged) { - // LOG(VERBOSE) << "discarding damaged AU"; + LOG(INFO) << "discarding damaged AU"; return; } + uint64_t ntpTime; + CHECK(buffer->meta()->findInt64( + "ntp-time", (int64_t *)&ntpTime)); + + if (mFirstAccessUnit) { + mFirstAccessUnit = false; + mFirstAccessUnitNTP = ntpTime; + } + + if (ntpTime > mFirstAccessUnitNTP) { + ntpTime -= mFirstAccessUnitNTP; + } else { + ntpTime = 0; + } + + int64_t timeUs = (int64_t)(ntpTime * 1E6 / (1ll << 32)); + + buffer->meta()->setInt64("timeUs", timeUs); + Mutex::Autolock autoLock(mLock); mBuffers.push_back(buffer); mCondition.signal(); diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp index 5bd306b..469af3e 100644 --- a/media/libstagefright/rtsp/ARTPConnection.cpp +++ b/media/libstagefright/rtsp/ARTPConnection.cpp @@ -28,8 +28,6 @@ #include <arpa/inet.h> #include <sys/socket.h> -#define IGNORE_RTCP_TIME 0 - namespace android { static const size_t kMaxUDPSize = 1500; @@ -61,8 +59,9 @@ struct ARTPConnection::StreamInfo { struct sockaddr_in mRemoteRTCPAddr; }; -ARTPConnection::ARTPConnection() - : mPollEventPending(false), +ARTPConnection::ARTPConnection(uint32_t flags) + : mFlags(flags), + mPollEventPending(false), mLastReceiverReportTimeUs(-1) { } @@ -280,7 +279,10 @@ void ARTPConnection::onPollStreams() { sp<ARTPSource> source = s->mSources.valueAt(i); source->addReceiverReport(buffer); - source->addFIR(buffer); + + if (mFlags & kRegularlyRequestFIR) { + source->addFIR(buffer); + } } if (buffer->size() > 0) { @@ -405,13 +407,11 @@ status_t ARTPConnection::parseRTP(StreamInfo *s, const sp<ABuffer> &buffer) { buffer->setInt32Data(u16at(&data[2])); buffer->setRange(payloadOffset, size - payloadOffset); -#if IGNORE_RTCP_TIME - if (!source->timeEstablished()) { + if ((mFlags & kFakeTimestamps) && !source->timeEstablished()) { source->timeUpdate(rtpTime, 0); - source->timeUpdate(rtpTime + 20, 0x100000000ll); + source->timeUpdate(rtpTime + 90000, 0x100000000ll); CHECK(source->timeEstablished()); } -#endif source->processRTPPacket(buffer); @@ -533,9 +533,9 @@ status_t ARTPConnection::parseSR( sp<ARTPSource> source = findSource(s, id); -#if !IGNORE_RTCP_TIME - source->timeUpdate(rtpTime, ntpTime); -#endif + if ((mFlags & kFakeTimestamps) == 0) { + source->timeUpdate(rtpTime, ntpTime); + } return 0; } diff --git a/media/libstagefright/rtsp/ARTPConnection.h b/media/libstagefright/rtsp/ARTPConnection.h index 49839ad..c535199 100644 --- a/media/libstagefright/rtsp/ARTPConnection.h +++ b/media/libstagefright/rtsp/ARTPConnection.h @@ -28,7 +28,12 @@ struct ARTPSource; struct ASessionDescription; struct ARTPConnection : public AHandler { - ARTPConnection(); + enum Flags { + kFakeTimestamps = 1, + kRegularlyRequestFIR = 2, + }; + + ARTPConnection(uint32_t flags = 0); void addStream( int rtpSocket, int rtcpSocket, @@ -56,6 +61,8 @@ private: static const int64_t kSelectTimeoutUs; + uint32_t mFlags; + struct StreamInfo; List<StreamInfo> mStreams; diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp index 0e0f45a..e082078 100644 --- a/media/libstagefright/rtsp/ARTPSession.cpp +++ b/media/libstagefright/rtsp/ARTPSession.cpp @@ -40,7 +40,10 @@ status_t ARTPSession::setup(const sp<ASessionDescription> &desc) { mDesc = desc; - mRTPConn = new ARTPConnection; + mRTPConn = new ARTPConnection( + ARTPConnection::kFakeTimestamps + | ARTPConnection::kRegularlyRequestFIR); + looper()->registerHandler(mRTPConn); for (size_t i = 1; i < mDesc->countTracks(); ++i) { diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp index e08183e..225f6e8 100644 --- a/media/libstagefright/rtsp/ARTPSource.cpp +++ b/media/libstagefright/rtsp/ARTPSource.cpp @@ -98,7 +98,7 @@ void ARTPSource::timeUpdate(uint32_t rtpTime, uint64_t ntpTime) { mNTPTime[mNumTimes] = ntpTime; mRTPTime[mNumTimes++] = rtpTime; - if (mNumTimes == 2) { + if (timeEstablished()) { for (List<sp<ABuffer> >::iterator it = mQueue.begin(); it != mQueue.end(); ++it) { sp<AMessage> meta = (*it)->meta(); @@ -112,13 +112,6 @@ void ARTPSource::timeUpdate(uint32_t rtpTime, uint64_t ntpTime) { } bool ARTPSource::queuePacket(const sp<ABuffer> &buffer) { -#if 1 - if (mNumTimes != 2) { - // Drop incoming packets until we've established a time base. - return false; - } -#endif - uint32_t seqNum = (uint32_t)buffer->int32Data(); if (mNumTimes == 2) { diff --git a/media/tests/MediaFrameworkTest/AndroidManifest.xml b/media/tests/MediaFrameworkTest/AndroidManifest.xml index 246f9fc..f70a145 100644 --- a/media/tests/MediaFrameworkTest/AndroidManifest.xml +++ b/media/tests/MediaFrameworkTest/AndroidManifest.xml @@ -51,4 +51,9 @@ android:label="MediaRecorder stress tests InstrumentationRunner"> </instrumentation> + <instrumentation android:name=".MediaFrameworkPowerTestRunner" + android:targetPackage="com.android.mediaframeworktest" + android:label="Media Power tests InstrumentationRunner"> + </instrumentation> + </manifest> diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java new file mode 100755 index 0000000..34db4db --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPowerTestRunner.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 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 com.android.mediaframeworktest; + +import com.android.mediaframeworktest.power.MediaPlayerPowerTest; + +import junit.framework.TestSuite; + +import android.test.InstrumentationTestRunner; +import android.test.InstrumentationTestSuite; + + +/** + * Instrumentation Test Runner for all MediaPlayer tests. + * + * Running all tests: + * + * adb shell am instrument \ + * -w com.android.mediaframeworktest/.MediaFrameworkPowerTestRunner + */ + +public class MediaFrameworkPowerTestRunner extends InstrumentationTestRunner { + + @Override + public TestSuite getAllTests() { + TestSuite suite = new InstrumentationTestSuite(this); + suite.addTestSuite(MediaPlayerPowerTest.class); + return suite; + } + + @Override + public ClassLoader getLoader() { + return MediaFrameworkPowerTestRunner.class.getClassLoader(); + } +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java index 3e33951..c7f461e 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTestRunner.java @@ -25,6 +25,11 @@ import com.android.mediaframeworktest.functional.MediaRecorderTest; import com.android.mediaframeworktest.functional.SimTonesTest; import com.android.mediaframeworktest.functional.MediaPlayerInvokeTest; import com.android.mediaframeworktest.functional.MediaAudioManagerTest; +import com.android.mediaframeworktest.functional.MediaAudioEffectTest; +import com.android.mediaframeworktest.functional.MediaBassBoostTest; +import com.android.mediaframeworktest.functional.MediaEqualizerTest; +import com.android.mediaframeworktest.functional.MediaVirtualizerTest; +import com.android.mediaframeworktest.functional.MediaVisualizerTest; import junit.framework.TestSuite; import android.test.InstrumentationTestRunner; @@ -55,6 +60,11 @@ public class MediaFrameworkTestRunner extends InstrumentationTestRunner { suite.addTestSuite(MediaMimeTest.class); suite.addTestSuite(MediaPlayerInvokeTest.class); suite.addTestSuite(MediaAudioManagerTest.class); + suite.addTestSuite(MediaAudioEffectTest.class); + suite.addTestSuite(MediaBassBoostTest.class); + suite.addTestSuite(MediaEqualizerTest.class); + suite.addTestSuite(MediaVirtualizerTest.class); + suite.addTestSuite(MediaVisualizerTest.class); return suite; } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java index 9a48c92..ca6e999 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java @@ -35,6 +35,7 @@ public class MediaNames { public static final String WAV = "/sdcard/media_api/music/rings_2ch.wav"; public static final String AMR = "/sdcard/media_api/music/test_amr_ietf.amr"; public static final String OGG = "/sdcard/media_api/music/Revelation.ogg"; + public static final String SINE_200_1000 = "/sdcard/media_api/music/sine_200+1000Hz_44K_mo.wav"; public static final int MP3CBR_LENGTH = 71000; public static final int MP3VBR_LENGTH = 71000; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java new file mode 100644 index 0000000..fd939ae --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaAudioEffectTest.java @@ -0,0 +1,1492 @@ +/* + * Copyright (C) 2010 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 com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.content.res.AssetFileDescriptor; +import android.media.AudioEffect; +import android.media.AudioFormat; +import android.media.AudioManager; +import android.media.AudioTrack; +import android.media.EnvironmentalReverb; +import android.media.Equalizer; +import android.media.MediaPlayer; + +import android.os.Looper; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaAudioEffectTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaAudioEffectTest"; + + private AudioEffect mEffect = null; + private boolean mHasControl = false; + private boolean mIsEnabled = false; + private int mParameterChanged = -1; + private MediaPlayer mMediaPlayer = null; + private boolean mInitialized = false; + private Looper mLooper = null; + private int mError = 0; + private final Object lock = new Object(); + + public MediaAudioEffectTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + private static void assumeTrue(String message, boolean cond) { + assertTrue("(assume)"+message, cond); + } + + private void log(String testName, String message) { + Log.v(TAG, "["+testName+"] "+message); + } + + private void loge(String testName, String message) { + Log.e(TAG, "["+testName+"] "+message); + } + + //----------------------------------------------------------------- + // AUDIOEFFECT TESTS: + //---------------------------------- + + //----------------------------------------------------------------- + // 0 - static methods + //---------------------------------- + + //Test case 0.0: test queryEffects() and available effects + @LargeTest + public void test0_0QueryEffects() throws Exception { + + AudioEffect.Descriptor[] desc = AudioEffect.queryEffects(); + + assertTrue("test0_0QueryEffects: number of effects < 4: "+desc.length, (desc.length >= 4)); + + boolean hasEQ = false; + boolean hasBassBoost = false; + boolean hasVirtualizer = false; + boolean hasEnvReverb = false; + + for (int i = 0; i < desc.length; i++) { + if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) { + hasEQ = true; + } if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) { + hasBassBoost = true; + } else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) { + hasVirtualizer = true; + } + else if (desc[i].mType.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) { + hasEnvReverb = true; + } + } + assertTrue("test0_0QueryEffects: equalizer not found", hasEQ); + assertTrue("test0_0QueryEffects: bass boost not found", hasBassBoost); + assertTrue("test0_0QueryEffects: virtualizer not found", hasVirtualizer); + assertTrue("test0_0QueryEffects: environmental reverb not found", hasEnvReverb); + } + + //----------------------------------------------------------------- + // 1 - constructor + //---------------------------------- + + //Test case 1.0: test constructor from effect type and get effect ID + @LargeTest + public void test1_0ConstructorFromType() throws Exception { + boolean result = true; + String msg = "test1_0ConstructorFromType()"; + AudioEffect.Descriptor[] desc = AudioEffect.queryEffects(); + assertTrue(msg+": no effects found", (desc.length != 0)); + try { + AudioEffect effect = new AudioEffect(desc[0].mType, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + try { + assertTrue(msg +": invalid effect ID", (effect.getId() != 0)); + } catch (IllegalStateException e) { + msg = msg.concat(": AudioEffect not initialized"); + result = false; + } finally { + effect.release(); + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Effect not found: "+desc[0].mName); + result = false; + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + result = false; + } + assertTrue(msg, result); + } + + //Test case 1.1: test constructor from effect uuid + @LargeTest + public void test1_1ConstructorFromUuid() throws Exception { + boolean result = true; + String msg = "test1_1ConstructorFromUuid()"; + AudioEffect.Descriptor[] desc = AudioEffect.queryEffects(); + assertTrue(msg+"no effects found", (desc.length != 0)); + try { + AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_NULL, + desc[0].mUuid, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + effect.release(); + } catch (IllegalArgumentException e) { + msg = msg.concat(": Effect not found: "+desc[0].mName); + result = false; + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + result = false; + } + assertTrue(msg, result); + } + + //Test case 1.2: test constructor failure from unknown type + @LargeTest + public void test1_2ConstructorUnknownType() throws Exception { + boolean result = false; + String msg = "test1_2ConstructorUnknownType()"; + + try { + AudioEffect effect = new AudioEffect(UUID.randomUUID(), + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + msg = msg.concat(": could create random AudioEffect"); + if (effect != null) { + effect.release(); + } + } catch (IllegalArgumentException e) { + result = true; + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } + assertTrue(msg, result); + } + + //Test case 1.3: test getEnabled() failure when called on released effect + @LargeTest + public void test1_3GetEnabledAfterRelease() throws Exception { + boolean result = false; + String msg = "test1_3GetEnabledAfterRelease()"; + + try { + AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + effect.release(); + try { + effect.getEnabled(); + } catch (IllegalStateException e) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } + assertTrue(msg, result); + } + + //Test case 1.4: test contructor on mediaPlayer audio session + @LargeTest + public void test1_4InsertOnMediaPlayer() throws Exception { + boolean result = false; + String msg = "test1_4InsertOnMediaPlayer()"; + + try { + MediaPlayer mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SHORTMP3); + + AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + mp.getAudioSessionId()); + assertNotNull(msg + ": could not create AudioEffect", effect); + try { + loge(msg, ": effect.setEnabled"); + effect.setEnabled(true); + } catch (IllegalStateException e) { + msg = msg.concat(": AudioEffect not initialized"); + } + + result = true; + effect.release(); + mp.release(); + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } catch (Exception e){ + loge(msg, "Could not create media player:" + e); + } + assertTrue(msg, result); + } + + //Test case 1.5: test auxiliary effect attachement on MediaPlayer + @LargeTest + public void test1_5AuxiliaryOnMediaPlayer() throws Exception { + boolean result = false; + String msg = "test1_5AuxiliaryOnMediaPlayer()"; + + try { + MediaPlayer mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SHORTMP3); + + AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + mp.attachAuxEffect(effect.getId()); + mp.setAuxEffectSendLevel(1.0f); + result = true; + effect.release(); + mp.release(); + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } catch (Exception e){ + loge(msg, "Could not create media player:" + e); + } + assertTrue(msg, result); + } + + //Test case 1.6: test auxiliary effect attachement failure before setDatasource + @LargeTest + public void test1_6AuxiliaryOnMediaPlayerFailure() throws Exception { + boolean result = false; + String msg = "test1_6AuxiliaryOnMediaPlayerFailure()"; + + try { + createMediaPlayerLooper(); + synchronized(lock) { + try { + lock.wait(1000); + } catch(Exception e) { + Log.e(TAG, "Looper creation: wait was interrupted."); + } + } + assertTrue(mInitialized); // mMediaPlayer has been initialized? + mError = 0; + + AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + synchronized(lock) { + try { + mMediaPlayer.attachAuxEffect(effect.getId()); + lock.wait(1000); + } catch(Exception e) { + Log.e(TAG, "Attach effect: wait was interrupted."); + } + } + assertTrue(msg + ": no error on attachAuxEffect", mError != 0); + result = true; + effect.release(); + terminateMediaPlayerLooper(); + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } catch (Exception e){ + loge(msg, "Could not create media player:" + e); + } + assertTrue(msg, result); + } + + + //Test case 1.7: test auxiliary effect attachement on AudioTrack + @LargeTest + public void test1_7AuxiliaryOnAudioTrack() throws Exception { + boolean result = false; + String msg = "test1_7AuxiliaryOnAudioTrack()"; + + try { + AudioTrack track = new AudioTrack( + AudioManager.STREAM_MUSIC, + 44100, + AudioFormat.CHANNEL_OUT_MONO, + AudioFormat.ENCODING_PCM_16BIT, + AudioTrack.getMinBufferSize(44100, + AudioFormat.CHANNEL_OUT_MONO, + AudioFormat.ENCODING_PCM_16BIT), + AudioTrack.MODE_STREAM); + assertNotNull(msg + ": could not create AudioTrack", track); + AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + + track.attachAuxEffect(effect.getId()); + track.setAuxEffectSendLevel(1.0f); + result = true; + effect.release(); + track.release(); + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 2 - enable/ disable + //---------------------------------- + + + //Test case 2.0: test setEnabled() and getEnabled() in valid state + @LargeTest + public void test2_0SetEnabledGetEnabled() throws Exception { + boolean result = false; + String msg = "test2_0SetEnabledGetEnabled()"; + + try { + AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + try { + effect.setEnabled(true); + assertTrue(msg + ": invalid state from getEnabled", effect.getEnabled()); + effect.setEnabled(false); + assertFalse(msg + ": invalid state to getEnabled", effect.getEnabled()); + result = true; + } catch (IllegalStateException e) { + msg = msg.concat(": setEnabled() in wrong state"); + } finally { + effect.release(); + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } + assertTrue(msg, result); + } + + //Test case 2.1: test setEnabled() throws exception after release + @LargeTest + public void test2_1SetEnabledAfterRelease() throws Exception { + boolean result = false; + String msg = "test2_1SetEnabledAfterRelease()"; + + try { + AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + effect.release(); + try { + effect.setEnabled(true); + } catch (IllegalStateException e) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 3 - set parameters + //---------------------------------- + + //Test case 3.0: test setParameter(byte[], byte[]) + @LargeTest + public void test3_0SetParameterByteArrayByteArray() throws Exception { + boolean result = false; + String msg = "test3_0SetParameterByteArrayByteArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + byte[] param = intToByteArray(Equalizer.PARAM_CURRENT_PRESET); + byte[] value = shortToByteArray((short)0); + if (effect.setParameter(param, value) == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": setParameter() rejected"); + loge(msg, "setParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("setParameter() called in wrong state"); + loge(msg, "setParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 3.1: test setParameter(int, int) + @LargeTest + public void test3_1SetParameterIntInt() throws Exception { + boolean result = false; + String msg = "test3_1SetParameterIntInt()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + if (effect.setParameter(EnvironmentalReverb.PARAM_DECAY_TIME, 0) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": setParameter() rejected"); + loge(msg, "setParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("setParameter() called in wrong state"); + loge(msg, "setParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 3.2: test setParameter(int, short) + @LargeTest + public void test3_2SetParameterIntShort() throws Exception { + boolean result = false; + String msg = "test3_2SetParameterIntShort()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + if (effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": setParameter() rejected"); + loge(msg, "setParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("setParameter() called in wrong state"); + loge(msg, "setParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 3.3: test setParameter(int, byte[]) + @LargeTest + public void test3_3SetParameterIntByteArray() throws Exception { + boolean result = false; + String msg = "test3_3SetParameterIntByteArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + byte[] value = shortToByteArray((short)0); + if (effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": setParameter() rejected"); + loge(msg, "setParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("setParameter() called in wrong state"); + loge(msg, "setParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 3.4: test setParameter(int[], int[]) + @LargeTest + public void test3_4SetParameterIntArrayIntArray() throws Exception { + boolean result = false; + String msg = "test3_4SetParameterIntArrayIntArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + int[] param = new int[1]; + int[] value = new int[1]; + param[0] = EnvironmentalReverb.PARAM_DECAY_TIME; + value[0] = 0; + if (effect.setParameter(param, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": setParameter() rejected"); + loge(msg, "setParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("setParameter() called in wrong state"); + loge(msg, "setParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 3.5: test setParameter(int[], short[]) + @LargeTest + public void test3_5SetParameterIntArrayShortArray() throws Exception { + boolean result = false; + String msg = "test3_5SetParameterIntArrayShortArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + int[] param = new int[1]; + short[] value = new short[1]; + param[0] = Equalizer.PARAM_CURRENT_PRESET; + value[0] = (short)0; + if (effect.setParameter(param, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": setParameter() rejected"); + loge(msg, "setParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("setParameter() called in wrong state"); + loge(msg, "setParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 3.6: test setParameter(int[], byte[]) + @LargeTest + public void test3_6SetParameterIntArrayByteArray() throws Exception { + boolean result = false; + String msg = "test3_6SetParameterIntArrayByteArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + int[] param = new int[1]; + byte[] value = shortToByteArray((short)0); + param[0] = Equalizer.PARAM_CURRENT_PRESET; + if (effect.setParameter(param, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": setParameter() rejected"); + loge(msg, "setParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("setParameter() called in wrong state"); + loge(msg, "setParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 3.7: test setParameter() throws exception after release() + @LargeTest + public void test3_7SetParameterAfterRelease() throws Exception { + boolean result = false; + String msg = "test3_7SetParameterAfterRelease()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + effect.release(); + effect.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0); + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": setParameter() rejected"); + loge(msg, "setParameter() rejected"); + } catch (IllegalStateException e) { + result = true; + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 4 - get parameters + //---------------------------------- + + //Test case 4.0: test getParameter(byte[], byte[]) + @LargeTest + public void test4_0GetParameterByteArrayByteArray() throws Exception { + boolean result = false; + String msg = "test4_0GetParameterByteArrayByteArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + byte[] param = intToByteArray(Equalizer.PARAM_CURRENT_PRESET); + byte[] value = new byte[2]; + if (effect.getParameter(param, value) == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": getParameter() rejected"); + loge(msg, "getParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("getParameter() called in wrong state"); + loge(msg, "getParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 4.1: test getParameter(int, int[]) + @LargeTest + public void test4_1GetParameterIntIntArray() throws Exception { + boolean result = false; + String msg = "test4_1GetParameterIntIntArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + int[] value = new int[1]; + if (effect.getParameter(EnvironmentalReverb.PARAM_DECAY_TIME, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": getParameter() rejected"); + loge(msg, "getParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("getParameter() called in wrong state"); + loge(msg, "getParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 4.2: test getParameter(int, short[]) + @LargeTest + public void test4_2GetParameterIntShortArray() throws Exception { + boolean result = false; + String msg = "test4_2GetParameterIntShortArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + short[] value = new short[1]; + if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": getParameter() rejected"); + loge(msg, "getParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("getParameter() called in wrong state"); + loge(msg, "getParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 4.3: test getParameter(int, byte[]) + @LargeTest + public void test4_3GetParameterIntByteArray() throws Exception { + boolean result = false; + String msg = "test4_3GetParameterIntByteArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + byte[] value = new byte[2]; + if (effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": getParameter() rejected"); + loge(msg, "getParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("getParameter() called in wrong state"); + loge(msg, "getParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 4.4: test getParameter(int[], int[]) + @LargeTest + public void test4_4GetParameterIntArrayIntArray() throws Exception { + boolean result = false; + String msg = "test4_4GetParameterIntArrayIntArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + int[] param = new int[1]; + int[] value = new int[1]; + param[0] = EnvironmentalReverb.PARAM_DECAY_TIME; + if (effect.getParameter(param, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": getParameter() rejected"); + loge(msg, "getParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("getParameter() called in wrong state"); + loge(msg, "getParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 4.5: test getParameter(int[], short[]) + @LargeTest + public void test4_5GetParameterIntArrayShortArray() throws Exception { + boolean result = false; + String msg = "test4_5GetParameterIntArrayShortArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + int[] param = new int[1]; + short[] value = new short[1]; + param[0] = Equalizer.PARAM_CURRENT_PRESET; + if (effect.getParameter(param, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": getParameter() rejected"); + loge(msg, "getParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("getParameter() called in wrong state"); + loge(msg, "getParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 4.6: test getParameter(int[], byte[]) + @LargeTest + public void test4_6GetParameterIntArrayByteArray() throws Exception { + boolean result = false; + String msg = "test4_6GetParameterIntArrayByteArray()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + int[] param = new int[1]; + byte[] value = new byte[2]; + param[0] = Equalizer.PARAM_CURRENT_PRESET; + if (effect.getParameter(param, value) + == AudioEffect.SUCCESS) { + result = true; + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": getParameter() rejected"); + loge(msg, "getParameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("getParameter() called in wrong state"); + loge(msg, "getParameter() called in wrong state"); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //Test case 4.7: test getParameter() throws exception after release() + @LargeTest + public void test4_7GetParameterAfterRelease() throws Exception { + boolean result = false; + String msg = "test4_7GetParameterAfterRelease()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + effect.release(); + short[] value = new short[1]; + effect.getParameter(Equalizer.PARAM_CURRENT_PRESET, value); + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": getParameter() rejected"); + loge(msg, "getParameter() rejected"); + } catch (IllegalStateException e) { + result = true; + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 5 priority and listeners + //---------------------------------- + + //Test case 5.0: test control passed to higher priority client + @LargeTest + public void test5_0setEnabledLowerPriority() throws Exception { + boolean result = false; + String msg = "test5_0setEnabledLowerPriority()"; + AudioEffect effect1 = null; + AudioEffect effect2 = null; + try { + effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 1, + 0); + + assertNotNull(msg + ": could not create AudioEffect", effect1); + assertNotNull(msg + ": could not create AudioEffect", effect2); + + assertTrue(msg + ": Effect2 does not have control", effect2.hasControl()); + assertFalse(msg + ": Effect1 has control", effect1.hasControl()); + assertTrue(msg + ": Effect1 can enable", + effect1.setEnabled(true) == AudioEffect.ERROR_INVALID_OPERATION); + assertFalse(msg + ": Effect1 has enabled", effect2.getEnabled()); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Effect not found"); + result = false; + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + result = false; + } finally { + if (effect1 != null) { + effect1.release(); + } + if (effect2 != null) { + effect2.release(); + } + } + assertTrue(msg, result); + } + + //Test case 5.1: test control passed to higher priority client + @LargeTest + public void test5_1setParameterLowerPriority() throws Exception { + boolean result = false; + String msg = "test5_1setParameterLowerPriority()"; + AudioEffect effect1 = null; + AudioEffect effect2 = null; + try { + effect1 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 1, + 0); + + assertNotNull(msg + ": could not create AudioEffect", effect1); + assertNotNull(msg + ": could not create AudioEffect", effect2); + + int status = effect2.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0); + assertEquals(msg + ": Effect2 setParameter failed", + AudioEffect.SUCCESS, status); + + status = effect1.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)1); + assertEquals(msg + ": Effect1 setParameter did not fail", + AudioEffect.ERROR_INVALID_OPERATION, status); + + short[] value = new short[1]; + status = effect2.getParameter(Equalizer.PARAM_CURRENT_PRESET, value); + assertEquals(msg + ": Effect2 getParameter failed", + AudioEffect.SUCCESS, status); + assertEquals(msg + ": Effect1 changed parameter", + (short)0, value[0]); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Effect not found"); + result = false; + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + result = false; + } finally { + if (effect1 != null) { + effect1.release(); + } + if (effect2 != null) { + effect2.release(); + } + } + assertTrue(msg, result); + } + + //Test case 5.2: test control status listener + @LargeTest + public void test5_2ControlStatusListener() throws Exception { + boolean result = false; + String msg = "test5_2ControlStatusListener()"; + mEffect = null; + AudioEffect effect2 = null; + try { + mHasControl = true; + createListenerLooper(true, false, false); + synchronized(lock) { + try { + lock.wait(1000); + } catch(Exception e) { + Log.e(TAG, "Looper creation: wait was interrupted."); + } + } + assertTrue(mInitialized); + synchronized(lock) { + try { + effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 1, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect2); + lock.wait(1000); + } catch(Exception e) { + Log.e(TAG, "Create second effect: wait was interrupted."); + } + } + assertFalse(msg + ": effect control not lost by effect1", mHasControl); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } catch (Exception e){ + loge(msg, "Could not create media player:" + e); + } finally { + terminateListenerLooper(); + if (effect2 != null) { + effect2.release(); + } + } + assertTrue(msg, result); + } + + //Test case 5.3: test enable status listener + @LargeTest + public void test5_3EnableStatusListener() throws Exception { + boolean result = false; + String msg = "test5_3EnableStatusListener()"; + mEffect = null; + AudioEffect effect2 = null; + try { + createListenerLooper(false, true, false); + synchronized(lock) { + try { + lock.wait(1000); + } catch(Exception e) { + Log.e(TAG, "Looper creation: wait was interrupted."); + } + } + assertTrue(mInitialized); + mEffect.setEnabled(true); + mIsEnabled = true; + effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 1, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect2); + assertTrue(msg + ": effect not enabled", effect2.getEnabled()); + synchronized(lock) { + try { + effect2.setEnabled(false); + lock.wait(1000); + } catch(Exception e) { + Log.e(TAG, "Create second effect: wait was interrupted."); + } + } + assertFalse(msg + ": enable status not updated", mIsEnabled); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } catch (Exception e){ + loge(msg, "Could not create media player:" + e); + } finally { + terminateListenerLooper(); + if (effect2 != null) { + effect2.release(); + } + } + assertTrue(msg, result); + } + + //Test case 5.4: test parameter changed listener + @LargeTest + public void test5_4ParameterChangedListener() throws Exception { + boolean result = false; + String msg = "test5_4ParameterChangedListener()"; + mEffect = null; + AudioEffect effect2 = null; + try { + createListenerLooper(false, false, true); + synchronized(lock) { + try { + lock.wait(1000); + } catch(Exception e) { + Log.e(TAG, "Looper creation: wait was interrupted."); + } + } + assertTrue(mInitialized); + effect2 = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 1, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect2); + synchronized(lock) { + try { + mParameterChanged = -1; + effect2.setParameter(Equalizer.PARAM_CURRENT_PRESET, (short)0); + lock.wait(1000); + } catch(Exception e) { + Log.e(TAG, "Create second effect: wait was interrupted."); + } + } + assertEquals(msg + ": parameter change not received", + Equalizer.PARAM_CURRENT_PRESET, mParameterChanged); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } catch (Exception e){ + loge(msg, "Could not create media player:" + e); + } finally { + terminateListenerLooper(); + if (effect2 != null) { + effect2.release(); + } + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 6 command method + //---------------------------------- + + + //Test case 6.0: test command method + @LargeTest + public void test6_0Command() throws Exception { + boolean result = false; + String msg = "test6_0Command()"; + AudioEffect effect = null; + try { + effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull(msg + ": could not create AudioEffect", effect); + try { + byte[] cmd = new byte[0]; + byte[] reply = new byte[4]; + int status = effect.command(3, cmd, reply); + assertEquals(msg + ": command failed", AudioEffect.SUCCESS, status); + assertTrue(msg + ": effect not enabled", effect.getEnabled()); + result = true; + } catch (IllegalStateException e) { + msg = msg.concat(": command in illegal state"); + } + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + loge(msg, ": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + loge(msg, ": Effect library not loaded"); + } catch (Exception e){ + loge(msg, "Could not create media player:" + e); + } finally { + if (effect != null) { + effect.release(); + } + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // private methods + //---------------------------------- + + /* + * Initializes the message looper so that the MediaPlayer object can + * receive the callback messages. + */ + private void createMediaPlayerLooper() { + new Thread() { + @Override + public void run() { + // Set up a looper to be used by mMediaPlayer. + Looper.prepare(); + + // Save the looper so that we can terminate this thread + // after we are done with it. + mLooper = Looper.myLooper(); + + mMediaPlayer = new MediaPlayer(); + mMediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() { + public boolean onError(MediaPlayer player, int what, int extra) { + synchronized(lock) { + mError = what; + lock.notify(); + } + return true; + } + }); + mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + public void onCompletion(MediaPlayer player) { + synchronized(lock) { + lock.notify(); + } + } + }); + synchronized(lock) { + mInitialized = true; + lock.notify(); + } + Looper.loop(); // Blocks forever until Looper.quit() is called. + } + }.start(); + } + /* + * Terminates the message looper thread. + */ + private void terminateMediaPlayerLooper() { + if (mLooper != null) { + mLooper.quit(); + mLooper = null; + } + if (mMediaPlayer != null) { + mMediaPlayer.release(); + } + } + + /* + * Initializes the message looper fro effect listener + */ + class ListenerThread extends Thread { + boolean mControl; + boolean mEnable; + boolean mParameter; + + public ListenerThread(boolean control, boolean enable, boolean parameter) { + super(); + mControl = control; + mEnable = enable; + mParameter = parameter; + } + } + private void createListenerLooper(boolean control, boolean enable, boolean parameter) { + + new ListenerThread(control, enable, parameter) { + @Override + public void run() { + // Set up a looper to be used by mEffect. + Looper.prepare(); + + // Save the looper so that we can terminate this thread + // after we are done with it. + mLooper = Looper.myLooper(); + + mEffect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER, + AudioEffect.EFFECT_TYPE_NULL, + 0, + 0); + assertNotNull("could not create AudioEffect", mEffect); + + if (mControl) { + mEffect.setControlStatusListener(new AudioEffect.OnControlStatusChangeListener() { + public void onControlStatusChange(AudioEffect effect, boolean controlGranted) { + synchronized(lock) { + if (effect == mEffect) { + mHasControl = controlGranted; + lock.notify(); + } + } + } + }); + } + if (mEnable) { + mEffect.setEnableStatusListener(new AudioEffect.OnEnableStatusChangeListener() { + public void onEnableStatusChange(AudioEffect effect, boolean enabled) { + synchronized(lock) { + if (effect == mEffect) { + mIsEnabled = enabled; + lock.notify(); + } + } + } + }); + } + if (mParameter) { + mEffect.setParameterListener(new AudioEffect.OnParameterChangeListener() { + public void onParameterChange(AudioEffect effect, int status, byte[] param, + byte[] value) { + synchronized(lock) { + if (effect == mEffect) { + mParameterChanged = byteArrayToInt(param); + lock.notify(); + } + } + } + }); + } + + synchronized(lock) { + mInitialized = true; + lock.notify(); + } + Looper.loop(); // Blocks forever until Looper.quit() is called. + } + }.start(); + } + /* + * Terminates the listener looper thread. + */ + private void terminateListenerLooper() { + if (mEffect != null) { + mEffect.release(); + mEffect = null; + } + if (mLooper != null) { + mLooper.quit(); + mLooper = null; + } + } + + protected int byteArrayToInt(byte[] valueBuf) { + return byteArrayToInt(valueBuf, 0); + + } + + protected int byteArrayToInt(byte[] valueBuf, int offset) { + ByteBuffer converter = ByteBuffer.wrap(valueBuf); + converter.order(ByteOrder.nativeOrder()); + return converter.getInt(offset); + + } + + protected byte[] intToByteArray(int value) { + ByteBuffer converter = ByteBuffer.allocate(4); + converter.order(ByteOrder.nativeOrder()); + converter.putInt(value); + return converter.array(); + } + + protected short byteArrayToShort(byte[] valueBuf) { + return byteArrayToShort(valueBuf, 0); + } + + protected short byteArrayToShort(byte[] valueBuf, int offset) { + ByteBuffer converter = ByteBuffer.wrap(valueBuf); + converter.order(ByteOrder.nativeOrder()); + return converter.getShort(offset); + + } + + protected byte[] shortToByteArray(short value) { + ByteBuffer converter = ByteBuffer.allocate(2); + converter.order(ByteOrder.nativeOrder()); + short sValue = (short) value; + converter.putShort(sValue); + return converter.array(); + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java new file mode 100644 index 0000000..8a68c5e --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaBassBoostTest.java @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2010 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 com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioEffect; +import android.media.AudioManager; +import android.media.BassBoost; +import android.media.Visualizer; +import android.media.MediaPlayer; + +import android.os.Looper; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaBassBoostTest"; + private final static int MIN_ENERGY_RATIO_2 = 4; + private final static short TEST_STRENGTH = 500; + + private BassBoost mBassBoost = null; + private int mSession = -1; + + public MediaBassBoostTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + releaseBassBoost(); + } + + private static void assumeTrue(String message, boolean cond) { + assertTrue("(assume)"+message, cond); + } + + private void log(String testName, String message) { + Log.v(TAG, "["+testName+"] "+message); + } + + private void loge(String testName, String message) { + Log.e(TAG, "["+testName+"] "+message); + } + + //----------------------------------------------------------------- + // BASS BOOST TESTS: + //---------------------------------- + + + //----------------------------------------------------------------- + // 0 - constructor + //---------------------------------- + + //Test case 0.0: test constructor and release + @LargeTest + public void test0_0ConstructorAndRelease() throws Exception { + boolean result = false; + String msg = "test1_0ConstructorAndRelease()"; + BassBoost bb = null; + try { + bb = new BassBoost(0, 0); + assertNotNull(msg + ": could not create BassBoost", bb); + try { + assertTrue(msg +": invalid effect ID", (bb.getId() != 0)); + } catch (IllegalStateException e) { + msg = msg.concat(": BassBoost not initialized"); + } + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": BassBoost not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } finally { + if (bb != null) { + bb.release(); + } + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 1 - get/set parameters + //---------------------------------- + + //Test case 1.0: test strength + @LargeTest + public void test1_0Strength() throws Exception { + boolean result = false; + String msg = "test1_0Strength()"; + getBassBoost(0); + try { + if (mBassBoost.getStrengthSupported()) { + mBassBoost.setStrength((short)TEST_STRENGTH); + short strength = mBassBoost.getRoundedStrength(); + // allow 10% difference between set strength and rounded strength + assertTrue(msg +": got incorrect strength", + ((float)strength > (float)TEST_STRENGTH * 0.9f) && + ((float)strength < (float)TEST_STRENGTH * 1.1f)); + } else { + short strength = mBassBoost.getRoundedStrength(); + assertTrue(msg +": got incorrect strength", strength >= 0 && strength <= 1000); + } + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseBassBoost(); + } + assertTrue(msg, result); + } + + //Test case 1.1: test properties + @LargeTest + public void test1_1Properties() throws Exception { + boolean result = false; + String msg = "test1_1Properties()"; + getBassBoost(0); + try { + BassBoost.Settings settings = mBassBoost.getProperties(); + String str = settings.toString(); + settings = new BassBoost.Settings(str); + mBassBoost.setProperties(settings); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseBassBoost(); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 2 - Effect action + //---------------------------------- + + //Test case 2.0: test actual bass boost influence on sound + @LargeTest + public void test2_0SoundModification() throws Exception { + boolean result = false; + String msg = "test2_0SoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + getBassBoost(mp.getAudioSessionId()); + mp.prepare(); + mp.start(); + Thread.sleep(200); + // measure reference energy around 1kHz + int refEnergy200 = probe.capture(200); + int refEnergy1000 = probe.capture(1000); + mBassBoost.setStrength((short)1000); + mBassBoost.setEnabled(true); + Thread.sleep(500); + // measure energy around 1kHz with band level at min + int energy200 = probe.capture(200); + int energy1000 = probe.capture(1000); + // verify that the energy ration between low and high frequencies is at least + // MIN_ENERGY_RATIO_2 times higher with bassboost on. + assertTrue(msg + ": bass boost has no effect", + ((float)energy200/(float)energy1000) > + (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000))); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseBassBoost(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + //----------------------------------------------------------------- + // private methods + //---------------------------------- + + private class EnergyProbe { + Visualizer mVisualizer = null; + private byte[] mFft = new byte[1024]; + + public EnergyProbe(int session) { + mVisualizer = new Visualizer(session); + mVisualizer.setCaptureSize(1024); + } + + public int capture(int freq) throws InterruptedException { + int energy = 0; + int count = 0; + if (mVisualizer != null) { + mVisualizer.setEnabled(true); + for (int i = 0; i < 10; i++) { + if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { + // TODO: check speex FFT as it seems to return only the number of points + // correspondong to valid part of the spectrum (< Fs). + // e.g., if the number of points is 1024, it covers the frequency range + // 0 to 22050 instead of 0 to 44100 as expected from an FFT. + int bin = freq / (22050 / 1024); + int tmp = 0; + for (int j = bin-2; j < bin+3; j++) { + tmp += (int)mFft[j] * (int)mFft[j]; + } + energy += tmp/5; + count++; + } + Thread.sleep(50); + } + mVisualizer.setEnabled(false); + } + if (count == 0) { + return 0; + } + return energy/count; + } + + public void release() { + if (mVisualizer != null) { + mVisualizer.release(); + mVisualizer = null; + } + } + } + + private void getBassBoost(int session) { + if (mBassBoost == null || session != mSession) { + if (session != mSession && mBassBoost != null) { + mBassBoost.release(); + mBassBoost = null; + } + try { + mBassBoost = new BassBoost(0, session); + mSession = session; + } catch (IllegalArgumentException e) { + Log.e(TAG, "getBassBoost() BassBoost not found exception: "+e); + } catch (UnsupportedOperationException e) { + Log.e(TAG, "getBassBoost() Effect library not loaded exception: "+e); + } + } + assertNotNull("could not create mBassBoost", mBassBoost); + } + + private void releaseBassBoost() { + if (mBassBoost != null) { + mBassBoost.release(); + mBassBoost = null; + } + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java new file mode 100644 index 0000000..e46887b --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaEqualizerTest.java @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2010 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 com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioEffect; +import android.media.AudioManager; +import android.media.Equalizer; +import android.media.Visualizer; +import android.media.MediaPlayer; + +import android.os.Looper; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaEqualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaEqualizerTest"; + private final static int MIN_NUMBER_OF_BANDS = 4; + private final static int MIN_BAND_LEVEL = -1500; + private final static int MAX_BAND_LEVEL = 1500; + private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000; + private final static int MIN_NUMBER_OF_PRESETS = 4; + private Equalizer mEqualizer = null; + private int mSession = -1; + + public MediaEqualizerTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + releaseEqualizer(); + } + + private static void assumeTrue(String message, boolean cond) { + assertTrue("(assume)"+message, cond); + } + + private void log(String testName, String message) { + Log.v(TAG, "["+testName+"] "+message); + } + + private void loge(String testName, String message) { + Log.e(TAG, "["+testName+"] "+message); + } + + //----------------------------------------------------------------- + // EQUALIZER TESTS: + //---------------------------------- + + + //----------------------------------------------------------------- + // 0 - constructor + //---------------------------------- + + //Test case 0.0: test constructor and release + @LargeTest + public void test0_0ConstructorAndRelease() throws Exception { + boolean result = false; + String msg = "test1_0ConstructorAndRelease()"; + Equalizer eq = null; + try { + eq = new Equalizer(0, 0); + assertNotNull(msg + ": could not create Equalizer", eq); + try { + assertTrue(msg +": invalid effect ID", (eq.getId() != 0)); + } catch (IllegalStateException e) { + msg = msg.concat(": Equalizer not initialized"); + } + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Equalizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } finally { + if (eq != null) { + eq.release(); + } + } + assertTrue(msg, result); + } + + + //----------------------------------------------------------------- + // 1 - get/set parameters + //---------------------------------- + + //Test case 1.0: test setBandLevel() and getBandLevel() + @LargeTest + public void test1_0BandLevel() throws Exception { + boolean result = false; + String msg = "test1_0BandLevel()"; + getEqualizer(0); + try { + short numBands = mEqualizer.getNumberOfBands(); + assertTrue(msg + ": not enough bands", numBands >= MIN_NUMBER_OF_BANDS); + + short[] levelRange = mEqualizer.getBandLevelRange(); + assertTrue(msg + ": min level too high", levelRange[0] <= MIN_BAND_LEVEL); + assertTrue(msg + ": max level too low", levelRange[1] >= MAX_BAND_LEVEL); + + mEqualizer.setBandLevel((short)0, levelRange[1]); + short level = mEqualizer.getBandLevel((short)0); + // 10% margin on actual level compared to requested level + assertTrue(msg + ": setBandLevel failed", + ((float)level > (float)levelRange[1] * 0.9f) && + ((float)level < (float)levelRange[1] * 1.1f)); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseEqualizer(); + } + assertTrue(msg, result); + } + + //Test case 1.1: test band frequency + @LargeTest + public void test1_1BandFrequency() throws Exception { + boolean result = false; + String msg = "test1_1BandFrequency()"; + getEqualizer(0); + try { + short band = mEqualizer.getBand(TEST_FREQUENCY_MILLIHERTZ); + assertTrue(msg + ": getBand failed", band >= 0); + int[] freqRange = mEqualizer.getBandFreqRange(band); + assertTrue(msg + ": getBandFreqRange failed", + (freqRange[0] <= TEST_FREQUENCY_MILLIHERTZ) && + (freqRange[1] >= TEST_FREQUENCY_MILLIHERTZ)); + int freq = mEqualizer.getCenterFreq(band); + assertTrue(msg + ": getCenterFreq failed", + (freqRange[0] <= freq) && (freqRange[1] >= freq)); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseEqualizer(); + } + assertTrue(msg, result); + } + + //Test case 1.2: test presets + @LargeTest + public void test1_2Presets() throws Exception { + boolean result = false; + String msg = "test1_2Presets()"; + getEqualizer(0); + try { + short numPresets = mEqualizer.getNumberOfPresets(); + assertTrue(msg + ": getNumberOfPresets failed", numPresets >= MIN_NUMBER_OF_PRESETS); + mEqualizer.usePreset((short)(numPresets - 1)); + short preset = mEqualizer.getCurrentPreset(); + assertEquals(msg + ": usePreset failed", preset, (short)(numPresets - 1)); + String name = mEqualizer.getPresetName(preset); + assertNotNull(msg + ": getPresetName failed", name); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseEqualizer(); + } + assertTrue(msg, result); + } + + //Test case 1.3: test properties + @LargeTest + public void test1_3Properties() throws Exception { + boolean result = false; + String msg = "test1_3Properties()"; + getEqualizer(0); + try { + Equalizer.Settings settings = mEqualizer.getProperties(); + String str = settings.toString(); + settings = new Equalizer.Settings(str); + mEqualizer.setProperties(settings); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseEqualizer(); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 2 - Effect action + //---------------------------------- + + //Test case 2.0: test that the equalizer actually alters the sound + @LargeTest + public void test2_0SoundModification() throws Exception { + boolean result = false; + String msg = "test2_0SoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + getEqualizer(mp.getAudioSessionId()); + mp.prepare(); + mp.start(); + Thread.sleep(500); + // measure reference energy around 1kHz + int refEnergy = probe.capture(1000); + short band = mEqualizer.getBand(1000000); + short[] levelRange = mEqualizer.getBandLevelRange(); + mEqualizer.setBandLevel(band, levelRange[0]); + mEqualizer.setEnabled(true); + Thread.sleep(500); + // measure energy around 1kHz with band level at min + int energy = probe.capture(1000); + assertTrue(msg + ": equalizer has no effect at 1kHz", energy < refEnergy/4); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseEqualizer(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // private methods + //---------------------------------- + + private class EnergyProbe { + Visualizer mVisualizer = null; + private byte[] mFft = new byte[1024]; + + public EnergyProbe(int session) { + mVisualizer = new Visualizer(session); + mVisualizer.setCaptureSize(1024); + } + + public int capture(int freq) throws InterruptedException { + int energy = 0; + int count = 0; + if (mVisualizer != null) { + mVisualizer.setEnabled(true); + for (int i = 0; i < 10; i++) { + if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { + // TODO: check speex FFT as it seems to return only the number of points + // correspondong to valid part of the spectrum (< Fs). + // e.g., if the number of points is 1024, it covers the frequency range + // 0 to 22050 instead of 0 to 44100 as expected from an FFT. + int bin = freq / (22050 / 1024); + int tmp = 0; + for (int j = bin-2; j < bin+3; j++) { + tmp += (int)mFft[j] * (int)mFft[j]; + } + energy += tmp/5; + count++; + } + Thread.sleep(50); + } + mVisualizer.setEnabled(false); + } + if (count == 0) { + return 0; + } + return energy/count; + } + + public void release() { + if (mVisualizer != null) { + mVisualizer.release(); + mVisualizer = null; + } + } + } + + private void getEqualizer(int session) { + if (mEqualizer == null || session != mSession) { + if (session != mSession && mEqualizer != null) { + mEqualizer.release(); + mEqualizer = null; + } + try { + mEqualizer = new Equalizer(0, session); + mSession = session; + } catch (IllegalArgumentException e) { + Log.e(TAG, "getEqualizer() Equalizer not found exception: "+e); + } catch (UnsupportedOperationException e) { + Log.e(TAG, "getEqualizer() Effect library not loaded exception: "+e); + } + } + assertNotNull("could not create mEqualizer", mEqualizer); + } + + private void releaseEqualizer() { + if (mEqualizer != null) { + mEqualizer.release(); + mEqualizer = null; + } + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java new file mode 100644 index 0000000..6b8ae44 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVirtualizerTest.java @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2010 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 com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioEffect; +import android.media.AudioManager; +import android.media.Virtualizer; +import android.media.Visualizer; +import android.media.MediaPlayer; + +import android.os.Looper; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaVirtualizerTest"; + private final static int MIN_ENERGY_RATIO_2 = 4; + private final static short TEST_STRENGTH = 500; + + private Virtualizer mVirtualizer = null; + private int mSession = -1; + + public MediaVirtualizerTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + releaseVirtualizer(); + } + + private static void assumeTrue(String message, boolean cond) { + assertTrue("(assume)"+message, cond); + } + + private void log(String testName, String message) { + Log.v(TAG, "["+testName+"] "+message); + } + + private void loge(String testName, String message) { + Log.e(TAG, "["+testName+"] "+message); + } + + //----------------------------------------------------------------- + // VIRTUALIZER TESTS: + //---------------------------------- + + + //----------------------------------------------------------------- + // 0 - constructor + //---------------------------------- + + //Test case 0.0: test constructor and release + @LargeTest + public void test0_0ConstructorAndRelease() throws Exception { + boolean result = false; + String msg = "test1_0ConstructorAndRelease()"; + Virtualizer virtualizer = null; + try { + virtualizer = new Virtualizer(0, 0); + assertNotNull(msg + ": could not create Virtualizer", virtualizer); + try { + assertTrue(msg +": invalid effect ID", (virtualizer.getId() != 0)); + } catch (IllegalStateException e) { + msg = msg.concat(": Virtualizer not initialized"); + } + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Virtualizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } finally { + if (virtualizer != null) { + virtualizer.release(); + } + } + assertTrue(msg, result); + } + + + //----------------------------------------------------------------- + // 1 - get/set parameters + //---------------------------------- + + //Test case 1.0: test strength + @LargeTest + public void test1_0Strength() throws Exception { + boolean result = false; + String msg = "test1_0Strength()"; + getVirtualizer(0); + try { + if (mVirtualizer.getStrengthSupported()) { + mVirtualizer.setStrength((short)TEST_STRENGTH); + short strength = mVirtualizer.getRoundedStrength(); + // allow 10% difference between set strength and rounded strength + assertTrue(msg +": got incorrect strength", + ((float)strength > (float)TEST_STRENGTH * 0.9f) && + ((float)strength < (float)TEST_STRENGTH * 1.1f)); + } else { + short strength = mVirtualizer.getRoundedStrength(); + assertTrue(msg +": got incorrect strength", strength >= 0 && strength <= 1000); + } + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseVirtualizer(); + } + assertTrue(msg, result); + } + + //Test case 1.1: test properties + @LargeTest + public void test1_1Properties() throws Exception { + boolean result = false; + String msg = "test1_1Properties()"; + getVirtualizer(0); + try { + Virtualizer.Settings settings = mVirtualizer.getProperties(); + String str = settings.toString(); + settings = new Virtualizer.Settings(str); + mVirtualizer.setProperties(settings); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseVirtualizer(); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 2 - Effect action + //---------------------------------- + + //Test case 2.0: test actual virtualizer influence on sound + @LargeTest + public void test2_0SoundModification() throws Exception { + boolean result = false; + String msg = "test2_0SoundModification()"; + EnergyProbe probe = null; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + + try { + probe = new EnergyProbe(0); + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + getVirtualizer(mp.getAudioSessionId()); + mp.prepare(); + mp.start(); + Thread.sleep(200); + // measure reference energy around 1kHz + int refEnergy200 = probe.capture(200); + int refEnergy1000 = probe.capture(1000); + mVirtualizer.setStrength((short)1000); + mVirtualizer.setEnabled(true); + Thread.sleep(500); + // measure energy around 1kHz with band level at min + int energy200 = probe.capture(200); + int energy1000 = probe.capture(1000); + // verify that the energy ration between low and high frequencies is at least + // four times higher with virtualizer on. + // NOTE: this is what is observed with current virtualizer implementation and the test + // audio file but is not the primary effect of the virtualizer. A better way would + // be to have a stereo PCM capture and check that a strongly paned input is centered + // when output. However, we cannot capture stereo with the visualizer. + assertTrue(msg + ": virtiualizer has no effect", + ((float)energy200/(float)energy1000) > + (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000))); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseVirtualizer(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + if (probe != null) { + probe.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + //----------------------------------------------------------------- + // private methods + //---------------------------------- + + private class EnergyProbe { + Visualizer mVisualizer = null; + private byte[] mFft = new byte[1024]; + + public EnergyProbe(int session) { + mVisualizer = new Visualizer(session); + mVisualizer.setCaptureSize(1024); + } + + public int capture(int freq) throws InterruptedException { + int energy = 0; + int count = 0; + if (mVisualizer != null) { + mVisualizer.setEnabled(true); + for (int i = 0; i < 10; i++) { + if (mVisualizer.getFft(mFft) == Visualizer.SUCCESS) { + // TODO: check speex FFT as it seems to return only the number of points + // correspondong to valid part of the spectrum (< Fs). + // e.g., if the number of points is 1024, it covers the frequency range + // 0 to 22050 instead of 0 to 44100 as expected from an FFT. + int bin = freq / (22050 / 1024); + int tmp = 0; + for (int j = bin-2; j < bin+3; j++) { + tmp += (int)mFft[j] * (int)mFft[j]; + } + energy += tmp/5; + count++; + } + Thread.sleep(50); + } + mVisualizer.setEnabled(false); + } + if (count == 0) { + return 0; + } + return energy/count; + } + + public void release() { + if (mVisualizer != null) { + mVisualizer.release(); + mVisualizer = null; + } + } + } + + private void getVirtualizer(int session) { + if (mVirtualizer == null || session != mSession) { + if (session != mSession && mVirtualizer != null) { + mVirtualizer.release(); + mVirtualizer = null; + } + try { + mVirtualizer = new Virtualizer(0, session); + mSession = session; + } catch (IllegalArgumentException e) { + Log.e(TAG, "getVirtualizer() Virtualizer not found exception: "+e); + } catch (UnsupportedOperationException e) { + Log.e(TAG, "getVirtualizer() Effect library not loaded exception: "+e); + } + } + assertNotNull("could not create mVirtualizer", mVirtualizer); + } + + private void releaseVirtualizer() { + if (mVirtualizer != null) { + mVirtualizer.release(); + mVirtualizer = null; + } + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java new file mode 100644 index 0000000..26fdbfe --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaVisualizerTest.java @@ -0,0 +1,505 @@ +/* + * Copyright (C) 2010 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 com.android.mediaframeworktest.functional; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.content.Context; +import android.content.res.AssetFileDescriptor; +import android.media.AudioEffect; +import android.media.AudioManager; +import android.media.Visualizer; +import android.media.MediaPlayer; + +import android.os.Looper; +import android.test.suitebuilder.annotation.LargeTest; +import android.test.suitebuilder.annotation.MediumTest; +import android.test.suitebuilder.annotation.Suppress; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.nio.ByteOrder; +import java.nio.ByteBuffer; +import java.util.UUID; + +/** + * Junit / Instrumentation test case for the media AudioTrack api + + */ +public class MediaVisualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaVisualizerTest"; + private final static int MIN_CAPTURE_RATE_MAX = 20000; + private final static int MIN_SAMPLING_RATE = 8000000; + private final static int MAX_SAMPLING_RATE = 48000000; + private final static int MIN_CAPTURE_SIZE_MAX = 1024; + private final static int MAX_CAPTURE_SIZE_MIN = 128; + + private Visualizer mVisualizer = null; + private int mSession = -1; + private boolean mInitialized = false; + private Looper mLooper = null; + private final Object lock = new Object(); + private byte[] mWaveform = null; + private byte[] mFft = null; + private boolean mCaptureWaveform = false; + private boolean mCaptureFft = false; + + public MediaVisualizerTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + releaseVisualizer(); + } + + private static void assumeTrue(String message, boolean cond) { + assertTrue("(assume)"+message, cond); + } + + private void log(String testName, String message) { + Log.v(TAG, "["+testName+"] "+message); + } + + private void loge(String testName, String message) { + Log.e(TAG, "["+testName+"] "+message); + } + + //----------------------------------------------------------------- + // VISUALIZER TESTS: + //---------------------------------- + + + //----------------------------------------------------------------- + // 0 - constructor + //---------------------------------- + + //Test case 0.0: test constructor and release + @LargeTest + public void test0_0ConstructorAndRelease() throws Exception { + boolean result = false; + String msg = "test1_0ConstructorAndRelease()"; + Visualizer visualizer = null; + try { + visualizer = new Visualizer(0); + assertNotNull(msg + ": could not create Visualizer", visualizer); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Visualizer not found"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": Effect library not loaded"); + } finally { + if (visualizer != null) { + visualizer.release(); + } + } + assertTrue(msg, result); + } + + + //----------------------------------------------------------------- + // 1 - get/set parameters + //---------------------------------- + + //Test case 1.0: check capture rate and sampling rate + @LargeTest + public void test1_0CaptureRates() throws Exception { + boolean result = false; + String msg = "test1_0CaptureRates()"; + getVisualizer(0); + try { + int captureRate = mVisualizer.getMaxCaptureRate(); + assertTrue(msg +": insufficient max capture rate", + captureRate >= MIN_CAPTURE_RATE_MAX); + int samplingRate = mVisualizer.getSamplingRate(); + assertTrue(msg +": invalid sampling rate", + samplingRate >= MIN_SAMPLING_RATE && samplingRate <= MAX_SAMPLING_RATE); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseVisualizer(); + } + assertTrue(msg, result); + } + + //Test case 1.1: check capture size + @LargeTest + public void test1_1CaptureSize() throws Exception { + boolean result = false; + String msg = "test1_1CaptureSize()"; + getVisualizer(0); + try { + int[] range = mVisualizer.getCaptureSizeRange(); + assertTrue(msg +": insufficient min capture size", + range[0] <= MAX_CAPTURE_SIZE_MIN); + assertTrue(msg +": insufficient min capture size", + range[1] >= MIN_CAPTURE_SIZE_MAX); + mVisualizer.setCaptureSize(range[0]); + assertEquals(msg +": insufficient min capture size", + range[0], mVisualizer.getCaptureSize()); + mVisualizer.setCaptureSize(range[1]); + assertEquals(msg +": insufficient min capture size", + range[1], mVisualizer.getCaptureSize()); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } finally { + releaseVisualizer(); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // 2 - check capture + //---------------------------------- + + //Test case 2.0: test capture in polling mode + @LargeTest + public void test2_0PollingCapture() throws Exception { + boolean result = false; + String msg = "test2_0PollingCapture()"; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + + try { + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + getVisualizer(mp.getAudioSessionId()); + mVisualizer.setEnabled(true); + // check capture on silence + byte[] data = new byte[mVisualizer.getCaptureSize()]; + mVisualizer.getWaveForm(data); + int energy = computeEnergy(data, true); + assertEquals(msg +": getWaveForm reports energy for silence", + 0, energy); + mVisualizer.getFft(data); + energy = computeEnergy(data, false); + assertEquals(msg +": getFft reports energy for silence", + 0, energy); + mp.prepare(); + mp.start(); + Thread.sleep(500); + // check capture on sound + mVisualizer.getWaveForm(data); + energy = computeEnergy(data, true); + assertTrue(msg +": getWaveForm reads insufficient level", + energy > 0); + mVisualizer.getFft(data); + energy = computeEnergy(data, false); + assertTrue(msg +": getFft reads insufficient level", + energy > 0); + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + releaseVisualizer(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //Test case 2.1: test capture with listener + @LargeTest + public void test2_1ListenerCapture() throws Exception { + boolean result = false; + String msg = "test2_1ListenerCapture()"; + AudioEffect vc = null; + MediaPlayer mp = null; + AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE); + int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC); + am.setStreamVolume(AudioManager.STREAM_MUSIC, + am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), + 0); + + try { + // creating a volume controller on output mix ensures that ro.audio.silent mutes + // audio after the effects and not before + vc = new AudioEffect( + AudioEffect.EFFECT_TYPE_NULL, + UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b"), + 0, + 0); + vc.setEnabled(true); + + mp = new MediaPlayer(); + mp.setDataSource(MediaNames.SINE_200_1000); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + + getVisualizer(mp.getAudioSessionId()); + createListenerLooper(); + synchronized(lock) { + try { + lock.wait(1000); + } catch(Exception e) { + Log.e(TAG, "Looper creation: wait was interrupted."); + } + } + assertTrue(mInitialized); + + mVisualizer.setEnabled(true); + + // check capture on silence + synchronized(lock) { + try { + mCaptureWaveform = true; + lock.wait(1000); + mCaptureWaveform = false; + } catch(Exception e) { + Log.e(TAG, "Capture waveform: wait was interrupted."); + } + } + assertNotNull(msg +": waveform capture failed", mWaveform); + int energy = computeEnergy(mWaveform, true); + assertEquals(msg +": getWaveForm reports energy for silence", + 0, energy); + + synchronized(lock) { + try { + mCaptureFft = true; + lock.wait(1000); + mCaptureFft = false; + } catch(Exception e) { + Log.e(TAG, "Capture FFT: wait was interrupted."); + } + } + assertNotNull(msg +": FFT capture failed", mFft); + energy = computeEnergy(mFft, false); + assertEquals(msg +": getFft reports energy for silence", + 0, energy); + + mp.prepare(); + mp.start(); + Thread.sleep(500); + + // check capture on sound + synchronized(lock) { + try { + mCaptureWaveform = true; + lock.wait(1000); + mCaptureWaveform = false; + } catch(Exception e) { + Log.e(TAG, "Capture waveform: wait was interrupted."); + } + } + assertNotNull(msg +": waveform capture failed", mWaveform); + energy = computeEnergy(mWaveform, true); + assertTrue(msg +": getWaveForm reads insufficient level", + energy > 0); + + synchronized(lock) { + try { + mCaptureFft = true; + lock.wait(1000); + mCaptureFft = false; + } catch(Exception e) { + Log.e(TAG, "Capture FFT: wait was interrupted."); + } + } + assertNotNull(msg +": FFT capture failed", mFft); + energy = computeEnergy(mFft, false); + assertTrue(msg +": getFft reads insufficient level", + energy > 0); + + result = true; + } catch (IllegalArgumentException e) { + msg = msg.concat(": Bad parameter value"); + loge(msg, "Bad parameter value"); + } catch (UnsupportedOperationException e) { + msg = msg.concat(": get parameter() rejected"); + loge(msg, "get parameter() rejected"); + } catch (IllegalStateException e) { + msg = msg.concat("get parameter() called in wrong state"); + loge(msg, "get parameter() called in wrong state"); + } catch (InterruptedException e) { + loge(msg, "sleep() interrupted"); + } + finally { + terminateListenerLooper(); + releaseVisualizer(); + if (mp != null) { + mp.release(); + } + if (vc != null) { + vc.release(); + } + am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0); + } + assertTrue(msg, result); + } + + //----------------------------------------------------------------- + // private methods + //---------------------------------- + + private int computeEnergy(byte[] data, boolean unsigned) { + int energy = 0; + if (data.length != 0) { + for (int i = 0; i < data.length; i++) { + int tmp; + // convert from unsigned 8 bit to signed 16 bit + if (unsigned) { + tmp = ((int)data[i] & 0xFF) - 128; + } else { + tmp = (int)data[i]; + } + energy += tmp*tmp; + } + energy /= data.length; + } + return energy; + } + + private void getVisualizer(int session) { + if (mVisualizer == null || session != mSession) { + if (session != mSession && mVisualizer != null) { + mVisualizer.release(); + mVisualizer = null; + } + try { + mVisualizer = new Visualizer(session); + mSession = session; + } catch (IllegalArgumentException e) { + Log.e(TAG, "getVisualizer() Visualizer not found exception: "+e); + } catch (UnsupportedOperationException e) { + Log.e(TAG, "getVisualizer() Effect library not loaded exception: "+e); + } + } + assertNotNull("could not create mVisualizer", mVisualizer); + } + + private void releaseVisualizer() { + if (mVisualizer != null) { + mVisualizer.release(); + mVisualizer = null; + } + } + + private void createListenerLooper() { + + new Thread() { + @Override + public void run() { + // Set up a looper to be used by mEffect. + Looper.prepare(); + + // Save the looper so that we can terminate this thread + // after we are done with it. + mLooper = Looper.myLooper(); + + if (mVisualizer != null) { + mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() { + public void onWaveFormDataCapture( + Visualizer visualizer, byte[] waveform, int samplingRate) { + synchronized(lock) { + if (visualizer == mVisualizer) { + if (mCaptureWaveform) { + mWaveform = waveform; + lock.notify(); + } + } + } + } + + public void onFftDataCapture( + Visualizer visualizer, byte[] fft, int samplingRate) { + synchronized(lock) { + if (visualizer == mVisualizer) { + if (mCaptureFft) { + mFft = fft; + lock.notify(); + } + } + } + } + }, + 10000, + true, + true); + } + + synchronized(lock) { + mInitialized = true; + lock.notify(); + } + Looper.loop(); // Blocks forever until Looper.quit() is called. + } + }.start(); + } + /* + * Terminates the listener looper thread. + */ + private void terminateListenerLooper() { + if (mLooper != null) { + mLooper.quit(); + mLooper = null; + } + } + +} + diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java new file mode 100644 index 0000000..9e91740 --- /dev/null +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/power/MediaPlayerPowerTest.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 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 com.android.mediaframeworktest.power; + +import com.android.mediaframeworktest.MediaFrameworkTest; +import com.android.mediaframeworktest.MediaNames; +import android.media.MediaPlayer; +import android.os.Environment; +import android.test.ActivityInstrumentationTestCase2; +import android.util.Log; + +import java.io.File; + +/** + * Junit / Instrumentation test case for the power measurment the media player + */ +public class MediaPlayerPowerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> { + private String TAG = "MediaPlayerPowerTest"; + private String MP3_POWERTEST = + Environment.getExternalStorageDirectory().toString() + "/power_sample_mp3.mp3"; + private String MP3_STREAM = "http://75.17.48.204:10088/power_media/power_sample_mp3.mp3"; + private String OGG_STREAM = "http://75.17.48.204:10088/power_media/power_sample_ogg.mp3"; + private String AAC_STREAM = "http://75.17.48.204:10088/power_media/power_sample_aac.mp3"; + + public MediaPlayerPowerTest() { + super("com.android.mediaframeworktest", MediaFrameworkTest.class); + } + + protected void setUp() throws Exception { + getActivity(); + super.setUp(); + + } + + public void audioPlayback(String filePath) { + try { + MediaPlayer mp = new MediaPlayer(); + mp.setDataSource(filePath); + mp.prepare(); + mp.start(); + Thread.sleep(200000); + mp.stop(); + mp.release(); + } catch (Exception e) { + Log.v(TAG, e.toString()); + assertTrue("MP3 Playback", false); + } + } + + // A very simple test case which start the audio player. + // Power measurment will be done in other application. + public void testPowerLocalMP3Playback() throws Exception { + audioPlayback(MP3_POWERTEST); + } + + public void testPowerStreamMP3Playback() throws Exception { + audioPlayback(MP3_STREAM); + } + + public void testPowerStreamOGGPlayback() throws Exception { + audioPlayback(OGG_STREAM); + } + + public void testPowerStreamAACPlayback() throws Exception { + audioPlayback(AAC_STREAM); + } +} diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp index 23837ef..dcf8735c 100644 --- a/opengl/libs/EGL/getProcAddress.cpp +++ b/opengl/libs/EGL/getProcAddress.cpp @@ -27,9 +27,12 @@ namespace android { // ---------------------------------------------------------------------------- #undef API_ENTRY -#undef CALL_GL_API +#undef CALL_GL_EXTENSION_API #undef GL_EXTENSION #undef GL_EXTENSION_NAME +#undef GL_EXTENSION_ARRAY +#undef GL_EXTENSION_LIST +#undef GET_TLS #if defined(__arm__) @@ -60,7 +63,7 @@ namespace android { : \ ); - #define GL_EXTENSION_NAME(_n) __glExtFwd##_n + #define GL_EXTENSION_NAME(_n) __glExtFwd##_n #define GL_EXTENSION(_n) \ void API_ENTRY(GL_EXTENSION_NAME(_n))() { \ @@ -78,97 +81,40 @@ namespace android { #endif -GL_EXTENSION(0) -GL_EXTENSION(1) -GL_EXTENSION(2) -GL_EXTENSION(3) -GL_EXTENSION(4) -GL_EXTENSION(5) -GL_EXTENSION(6) -GL_EXTENSION(7) -GL_EXTENSION(8) -GL_EXTENSION(9) -GL_EXTENSION(10) -GL_EXTENSION(11) -GL_EXTENSION(12) -GL_EXTENSION(13) -GL_EXTENSION(14) -GL_EXTENSION(15) - -GL_EXTENSION(16) -GL_EXTENSION(17) -GL_EXTENSION(18) -GL_EXTENSION(19) -GL_EXTENSION(20) -GL_EXTENSION(21) -GL_EXTENSION(22) -GL_EXTENSION(23) -GL_EXTENSION(24) -GL_EXTENSION(25) -GL_EXTENSION(26) -GL_EXTENSION(27) -GL_EXTENSION(28) -GL_EXTENSION(29) -GL_EXTENSION(30) -GL_EXTENSION(31) - -GL_EXTENSION(32) -GL_EXTENSION(33) -GL_EXTENSION(34) -GL_EXTENSION(35) -GL_EXTENSION(36) -GL_EXTENSION(37) -GL_EXTENSION(38) -GL_EXTENSION(39) -GL_EXTENSION(40) -GL_EXTENSION(41) -GL_EXTENSION(42) -GL_EXTENSION(43) -GL_EXTENSION(44) -GL_EXTENSION(45) -GL_EXTENSION(46) -GL_EXTENSION(47) - -GL_EXTENSION(48) -GL_EXTENSION(49) -GL_EXTENSION(50) -GL_EXTENSION(51) -GL_EXTENSION(52) -GL_EXTENSION(53) -GL_EXTENSION(54) -GL_EXTENSION(55) -GL_EXTENSION(56) -GL_EXTENSION(57) -GL_EXTENSION(58) -GL_EXTENSION(59) -GL_EXTENSION(60) -GL_EXTENSION(61) -GL_EXTENSION(62) -GL_EXTENSION(63) +#define GL_EXTENSION_LIST(name) \ + name(0) name(1) name(2) name(3) \ + name(4) name(5) name(6) name(7) \ + name(8) name(9) name(10) name(11) \ + name(12) name(13) name(14) name(15) \ + name(16) name(17) name(18) name(19) \ + name(20) name(21) name(22) name(23) \ + name(24) name(25) name(26) name(27) \ + name(28) name(29) name(30) name(31) \ + name(32) name(33) name(34) name(35) \ + name(36) name(37) name(38) name(39) \ + name(40) name(41) name(42) name(43) \ + name(44) name(45) name(46) name(47) \ + name(48) name(49) name(50) name(51) \ + name(52) name(53) name(54) name(55) \ + name(56) name(57) name(58) name(59) \ + name(60) name(61) name(62) name(63) + + +GL_EXTENSION_LIST( GL_EXTENSION ) + +#define GL_EXTENSION_ARRAY(_n) GL_EXTENSION_NAME(_n), extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS] = { - GL_EXTENSION_NAME(0), GL_EXTENSION_NAME(1), GL_EXTENSION_NAME(2), GL_EXTENSION_NAME(3), - GL_EXTENSION_NAME(4), GL_EXTENSION_NAME(5), GL_EXTENSION_NAME(6), GL_EXTENSION_NAME(7), - GL_EXTENSION_NAME(8), GL_EXTENSION_NAME(9), GL_EXTENSION_NAME(10), GL_EXTENSION_NAME(11), - GL_EXTENSION_NAME(12), GL_EXTENSION_NAME(13), GL_EXTENSION_NAME(14), GL_EXTENSION_NAME(15), - GL_EXTENSION_NAME(16), GL_EXTENSION_NAME(17), GL_EXTENSION_NAME(18), GL_EXTENSION_NAME(19), - GL_EXTENSION_NAME(20), GL_EXTENSION_NAME(21), GL_EXTENSION_NAME(22), GL_EXTENSION_NAME(23), - GL_EXTENSION_NAME(24), GL_EXTENSION_NAME(25), GL_EXTENSION_NAME(26), GL_EXTENSION_NAME(27), - GL_EXTENSION_NAME(28), GL_EXTENSION_NAME(29), GL_EXTENSION_NAME(30), GL_EXTENSION_NAME(31), - GL_EXTENSION_NAME(32), GL_EXTENSION_NAME(33), GL_EXTENSION_NAME(34), GL_EXTENSION_NAME(35), - GL_EXTENSION_NAME(36), GL_EXTENSION_NAME(37), GL_EXTENSION_NAME(38), GL_EXTENSION_NAME(39), - GL_EXTENSION_NAME(40), GL_EXTENSION_NAME(41), GL_EXTENSION_NAME(42), GL_EXTENSION_NAME(43), - GL_EXTENSION_NAME(44), GL_EXTENSION_NAME(45), GL_EXTENSION_NAME(46), GL_EXTENSION_NAME(47), - GL_EXTENSION_NAME(48), GL_EXTENSION_NAME(49), GL_EXTENSION_NAME(50), GL_EXTENSION_NAME(51), - GL_EXTENSION_NAME(52), GL_EXTENSION_NAME(53), GL_EXTENSION_NAME(54), GL_EXTENSION_NAME(55), - GL_EXTENSION_NAME(56), GL_EXTENSION_NAME(57), GL_EXTENSION_NAME(58), GL_EXTENSION_NAME(59), - GL_EXTENSION_NAME(60), GL_EXTENSION_NAME(61), GL_EXTENSION_NAME(62), GL_EXTENSION_NAME(63) + GL_EXTENSION_LIST( GL_EXTENSION_ARRAY ) }; +#undef GET_TLS +#undef GL_EXTENSION_LIST +#undef GL_EXTENSION_ARRAY #undef GL_EXTENSION_NAME #undef GL_EXTENSION #undef API_ENTRY -#undef CALL_GL_API +#undef CALL_GL_EXTENSION_API // ---------------------------------------------------------------------------- }; // namespace android diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 252b42a..1c7faa4 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1696,7 +1696,10 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track // Delegate master volume control to effect in output mix effect chain if needed sp<EffectChain> chain = getEffectChain_l(AudioSystem::SESSION_OUTPUT_MIX); if (chain != 0) { - uint32_t v = (uint32_t)(masterVolume * (1 << 24)); + uint32_t v = 0; + if (!masterMute) { + v = (uint32_t)(masterVolume * (1 << 24)); + } chain->setVolume_l(&v, &v); masterVolume = (float)((v + (1 << 23)) >> 24); chain.clear(); @@ -1750,7 +1753,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track // compute volume for this track int16_t left, right, aux; - if (track->isMuted() || masterMute || track->isPausing() || + if (track->isMuted() || track->isPausing() || mStreamTypes[track->type()].mute) { left = right = aux = 0; if (track->isPausing()) { @@ -5351,7 +5354,7 @@ void AudioFlinger::EffectModule::process() return; } - if (mState == ACTIVE || mState == STOPPING || mState == STOPPED) { + if (mState == ACTIVE || mState == STOPPING || mState == STOPPED || mState == RESTART) { // do 32 bit to 16 bit conversion for auxiliary effect input buffer if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) { AudioMixer::ditherAndClamp(mConfig.inputCfg.buffer.s32, @@ -6032,8 +6035,8 @@ void AudioFlinger::EffectHandle::dump(char* buffer, size_t size) AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread, int sessionId) : mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mOwnInBuffer(false), - mVolumeCtrlIdx(-1), mLeftVolume(0), mRightVolume(0), - mNewLeftVolume(0), mNewRightVolume(0) + mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX), + mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX) { mStrategy = AudioSystem::getStrategyForStream(AudioSystem::MUSIC); } diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index d666523..0def5f2 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -7858,6 +7858,7 @@ public class WindowManagerService extends IWindowManager.Stub } catch (RemoteException e) { // Ignore if process has died. } + notifyFocusChanged(); } if (lastFocus != null) { @@ -7867,7 +7868,6 @@ public class WindowManagerService extends IWindowManager.Stub } catch (RemoteException e) { // Ignore if process has died. } - notifyFocusChanged(); } } } break; diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java index edc1f8a..a94518f 100755 --- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -102,6 +102,10 @@ public class SipPhone extends SipPhoneBase { return mProfile.getProfileName(); } + public String getSipUri() { + return mProfile.getUriString(); + } + public boolean canTake(Object incomingCall) { synchronized (SipPhone.class) { if (!(incomingCall instanceof SipAudioCall)) return false; |