Merge "SipAudioCall: perform local ops before network op in endCall()" into gingerbread
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 @@
* <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 @@
* {@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 @@
* {@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 @@
* @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 @@
/** @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 @@
/** @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 @@
* <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 @@
* 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 @@
* <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 @@
/**
* 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 @@
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 @@
// 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 @@
* 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 @@
* 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 @@
* 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 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 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 @@
* Reset the velocity tracker back to its initial state.
*/
public void clear() {
- mNumPointers = 0;
+ releasePointerList(mPointerListHead);
+
+ mPointerListHead = null;
mLastTouchIndex = 0;
}
@@ -134,56 +142,62 @@
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;
- }
- } else {
- final PointerData[] newPointers = new PointerData[newPointerIndex * 2];
- System.arraycopy(oldPointers, 0, newPointers, 0, newPointerIndex);
- mPointers = newPointers;
- pointerData = new PointerData();
- newPointers[newPointerIndex] = pointerData;
- }
- pointerData.id = pointerId;
- pointerData.pastTime[lastTouchIndex] = Long.MIN_VALUE;
- mNumPointers += 1;
+
+ // 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 float[] pastX = pointerData.pastX;
- final float[] pastY = pointerData.pastY;
- final long[] pastTime = pointerData.pastTime;
+ 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 {
+ previousPointer.next = pointer;
+ }
+ break;
+ }
+
+ 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 @@
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 @@
* 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 @@
}
// 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 @@
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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* 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 @@
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 @@
* 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 @@
* 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 @@
* 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 @@
/**
* 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 @@
* 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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
* @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 @@
*
* @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 @@
*
* @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 @@
*
* @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 @@
* @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 @@
* @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 @@
* @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 @@
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;
- }
+ profileViewAndChildren(view, out, true);
+ }
- 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));
- }
- }
- }
+ private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root)
+ throws IOException {
- public void run(Void... data) {
- view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
- }
+ long durationMeasure =
+ (root || (view.mPrivateFlags & View.MEASURED_DIMENSION_SET) != 0) ? profileViewOperation(
+ view, new ViewOperation<Void>() {
+ public Void[] pre() {
+ forceLayout(view);
+ return null;
+ }
- public void post(Void... data) {
- }
- });
+ 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));
+ }
+ }
+ }
- final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() {
- public Void[] pre() {
- return null;
- }
+ public void run(Void... data) {
+ view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
+ }
- public void run(Void... data) {
- view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
- }
+ 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;
+ }
- public void post(Void... data) {
- }
- });
+ public void run(Void... data) {
+ view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
+ }
- 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 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 run(Object... data) {
- view.draw((Canvas) data[1]);
- }
+ public void run(Object... data) {
+ view.draw((Canvas) data[1]);
+ }
- public void post(Object... data) {
- ((Bitmap) data[0]).recycle();
- }
- });
-
+ 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 @@
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 @@
});
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 @@
// 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 @@
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 @@
}
}
} 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 @@
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 @@
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 @@
}
}
} 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 @@
// 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 @@
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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* {@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 @@
* 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 @@
* 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 @@
/**
* 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 @@
* 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 @@
* 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 @@
* 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 @@
/**
* The position of the first child displayed
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "scrolling")
int mFirstPosition = 0;
/**
@@ -141,7 +141,7 @@
* 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 @@
/**
* 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 @@
/**
* 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 e27bb4fe..e445180 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -46,27 +46,32 @@
*/
@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 @@
* 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 @@
* 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 @@
* 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 @@
@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 @@
* 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 @@
*
* @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 @@
* 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 @@
*
* @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 @@
* @see #setMax(int)
* @see #getMax()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getProgress() {
return mIndeterminate ? 0 : mProgress;
}
@@ -626,7 +626,7 @@
* @see #setMax(int)
* @see #getMax()
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "progress")
public synchronized int getSecondaryProgress() {
return mIndeterminate ? 0 : mSecondaryProgress;
}
@@ -640,7 +640,7 @@
* @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 @@
* @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 @@
* 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 @@
/**
* <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 @@
/**
* Convenience for {@link Selection#getSelectionStart}.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
public int getSelectionStart() {
return Selection.getSelectionStart(getText());
}
@@ -5741,7 +5741,7 @@
/**
* Convenience for {@link Selection#getSelectionEnd}.
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "text")
public int getSelectionEnd() {
return Selection.getSelectionEnd(getText());
}
@@ -7295,7 +7295,7 @@
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 @@
$(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 @@
mContext.unregisterReceiver(mReceiver);
}
- @LargeTest
public void testEnableDisable() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -172,7 +169,6 @@
}
}
- @LargeTest
public void testDiscoverable() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
enable(adapter);
@@ -186,7 +182,6 @@
disable(adapter);
}
- @LargeTest
public void testScan() {
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
enable(adapter);
@@ -336,7 +331,7 @@
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("undiscoverable(): bluetooth not enabled");
+ fail("undiscoverable() bluetooth not enabled");
}
int scanMode = adapter.getScanMode();
@@ -374,7 +369,7 @@
mReceiver.resetFiredFlags();
if (!adapter.isEnabled()) {
- fail("startScan(): bluetooth not enabled");
+ fail("startScan() bluetooth not enabled");
}
if (adapter.isDiscovering()) {
@@ -404,7 +399,7 @@
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_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 @@
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 @@
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 static final int STATE_INITIALIZED = 1;
// to keep in sync with
- // frameworks/base/media/jni/audioeffect/android_media_AudioEffect.cpp
- /**
- * Event id for engine state change notification.
- */
- public static final int NATIVE_EVENT_ENABLED_STATUS = 0;
+ // frameworks/base/include/media/AudioEffect.h
/**
* Event id for engine control ownership change notification.
*/
- public static final int NATIVE_EVENT_CONTROL_STATUS = 1;
+ public static final int NATIVE_EVENT_CONTROL_STATUS = 0;
+ /**
+ * Event id for engine state change notification.
+ */
+ public static final int NATIVE_EVENT_ENABLED_STATUS = 1;
/**
* Event id for engine parameter change notification.
*/
@@ -795,7 +795,7 @@
// 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 @@
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 @@
// 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 @@
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 @@
}
} 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 @@
// 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 @@
return mIEffect->disable();
}
}
- return INVALID_OPERATION;
+ return NO_ERROR;
}
status_t AudioEffect::command(uint32_t cmdCode,
@@ -231,7 +231,22 @@
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 @@
{
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
};
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 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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_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(AMRWBDecoder)
FACTORY_CREATE(AACDecoder)
FACTORY_CREATE(AVCDecoder)
+FACTORY_CREATE(G711Decoder)
FACTORY_CREATE(M4vH263Decoder)
FACTORY_CREATE(VorbisDecoder)
FACTORY_CREATE(VPXDecoder)
@@ -124,6 +126,7 @@
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 @@
{ 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 @@
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 @@
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 @@
sp<DataSource> mDataSource;
sp<MetaData> mMeta;
+ uint16_t mWaveFormat;
int32_t mSampleRate;
int32_t mNumChannels;
int32_t mBitsPerSample;
@@ -108,7 +114,7 @@
return new WAVSource(
mDataSource, mTrackMeta,
- mBitsPerSample, mDataOffset, mDataSize);
+ mWaveFormat, mBitsPerSample, mDataOffset, mDataSize);
}
sp<MetaData> WAVExtractor::getTrackMetaData(
@@ -160,8 +166,10 @@
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 @@
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 @@
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 @@
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 @@
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->set_range(buffer->range_offset(), 2 * numSamples);
}
-
- 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->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 @@
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 @@
} else {
switch (type) {
case AVC_NALTYPE_SPS:
- case AVC_NALTYPE_PPS:
- LOGV("%s received",
- (type == AVC_NALTYPE_SPS)? "SPS": "PPS");
++mNumInputFrames;
- outputBuffer->set_range(0, dataLength);
+ 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:
+ ++mNumInputFrames;
+ 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 @@
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..2e431205
--- /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 5002442e..1bef0e9 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/M4vH263Encoder.cpp
@@ -292,8 +292,18 @@
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 @@
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 @@
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 @@
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 sockaddr_in mRemoteRTCPAddr;
};
-ARTPConnection::ARTPConnection()
- : mPollEventPending(false),
+ARTPConnection::ARTPConnection(uint32_t flags)
+ : mFlags(flags),
+ mPollEventPending(false),
mLastReceiverReportTimeUs(-1) {
}
@@ -280,7 +279,10 @@
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 @@
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 @@
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 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 @@
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 @@
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 @@
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 @@
}
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.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 @@
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 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 @@
// ----------------------------------------------------------------------------
#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 @@
: \
);
- #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 @@
#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)
+#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(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_LIST( GL_EXTENSION )
-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_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 @@
// 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 @@
// 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 @@
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 @@
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 @@
} catch (RemoteException e) {
// Ignore if process has died.
}
+ notifyFocusChanged();
}
if (lastFocus != null) {
@@ -7867,7 +7868,6 @@
} 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 @@
return mProfile.getProfileName();
}
+ public String getSipUri() {
+ return mProfile.getUriString();
+ }
+
public boolean canTake(Object incomingCall) {
synchronized (SipPhone.class) {
if (!(incomingCall instanceof SipAudioCall)) return false;