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, &paramSetLen);
+        } 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, &paramSetLen);
+        } 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;