Merge "Import revised translations."
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index e269c31..ad8c971 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -83,15 +83,61 @@
      */
     private long mSeekTime = -1;
 
+    // TODO: We access the following ThreadLocal variables often, some of them on every update.
+    // If ThreadLocal access is significantly expensive, we may want to put all of these
+    // fields into a structure sot hat we just access ThreadLocal once to get the reference
+    // to that structure, then access the structure directly for each field.
+
     // The static sAnimationHandler processes the internal timing loop on which all animations
     // are based
-    private static AnimationHandler sAnimationHandler;
+    private static ThreadLocal<AnimationHandler> sAnimationHandler =
+            new ThreadLocal<AnimationHandler>();
 
-    // The static list of all active animations
-    private static final ArrayList<ValueAnimator> sAnimations = new ArrayList<ValueAnimator>();
+    // The per-thread list of all active animations
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sAnimations =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
 
-    // The set of animations to be started on the next animation frame
-    private static final ArrayList<ValueAnimator> sPendingAnimations = new ArrayList<ValueAnimator>();
+    // The per-thread set of animations to be started on the next animation frame
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
+
+    /**
+     * Internal per-thread collections used to avoid set collisions as animations start and end
+     * while being processed.
+     */
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sDelayedAnims =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
+
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sEndingAnims =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
+
+    private static final ThreadLocal<ArrayList<ValueAnimator>> sReadyAnims =
+            new ThreadLocal<ArrayList<ValueAnimator>>() {
+                @Override
+                protected ArrayList<ValueAnimator> initialValue() {
+                    return new ArrayList<ValueAnimator>();
+                }
+            };
 
     // The time interpolator to be used if none is set on the animation
     private static final TimeInterpolator sDefaultInterpolator =
@@ -136,14 +182,6 @@
     private int mPlayingState = STOPPED;
 
     /**
-     * Internal collections used to avoid set collisions as animations start and end while being
-     * processed.
-     */
-    private static final ArrayList<ValueAnimator> sEndingAnims = new ArrayList<ValueAnimator>();
-    private static final ArrayList<ValueAnimator> sDelayedAnims = new ArrayList<ValueAnimator>();
-    private static final ArrayList<ValueAnimator> sReadyAnims = new ArrayList<ValueAnimator>();
-
-    /**
      * Flag that denotes whether the animation is set up and ready to go. Used to
      * set up animation that has not yet been started.
      */
@@ -609,11 +647,14 @@
         @Override
         public void handleMessage(Message msg) {
             boolean callAgain = true;
+            ArrayList<ValueAnimator> animations = sAnimations.get();
+            ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get();
             switch (msg.what) {
                 // TODO: should we avoid sending frame message when starting if we
                 // were already running?
                 case ANIMATION_START:
-                    if (sAnimations.size() > 0 || sDelayedAnims.size() > 0) {
+                    ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get();
+                    if (animations.size() > 0 || delayedAnims.size() > 0) {
                         callAgain = false;
                     }
                     // pendingAnims holds any animations that have requested to be started
@@ -621,10 +662,10 @@
                     // cause more to be added to the pending list (for example, if one animation
                     // starting triggers another starting). So we loop until sPendingAnimations
                     // is empty.
-                    while (sPendingAnimations.size() > 0) {
+                    while (pendingAnimations.size() > 0) {
                         ArrayList<ValueAnimator> pendingCopy =
-                                (ArrayList<ValueAnimator>) sPendingAnimations.clone();
-                        sPendingAnimations.clear();
+                                (ArrayList<ValueAnimator>) pendingAnimations.clone();
+                        pendingAnimations.clear();
                         int count = pendingCopy.size();
                         for (int i = 0; i < count; ++i) {
                             ValueAnimator anim = pendingCopy.get(i);
@@ -633,7 +674,7 @@
                                     anim.mPlayingState == CANCELED) {
                                 anim.startAnimation();
                             } else {
-                                sDelayedAnims.add(anim);
+                                delayedAnims.add(anim);
                             }
                         }
                     }
@@ -642,45 +683,47 @@
                     // currentTime holds the common time for all animations processed
                     // during this frame
                     long currentTime = AnimationUtils.currentAnimationTimeMillis();
+                    ArrayList<ValueAnimator> readyAnims = sReadyAnims.get();
+                    ArrayList<ValueAnimator> endingAnims = sEndingAnims.get();
 
                     // First, process animations currently sitting on the delayed queue, adding
                     // them to the active animations if they are ready
-                    int numDelayedAnims = sDelayedAnims.size();
+                    int numDelayedAnims = delayedAnims.size();
                     for (int i = 0; i < numDelayedAnims; ++i) {
-                        ValueAnimator anim = sDelayedAnims.get(i);
+                        ValueAnimator anim = delayedAnims.get(i);
                         if (anim.delayedAnimationFrame(currentTime)) {
-                            sReadyAnims.add(anim);
+                            readyAnims.add(anim);
                         }
                     }
-                    int numReadyAnims = sReadyAnims.size();
+                    int numReadyAnims = readyAnims.size();
                     if (numReadyAnims > 0) {
                         for (int i = 0; i < numReadyAnims; ++i) {
-                            ValueAnimator anim = sReadyAnims.get(i);
+                            ValueAnimator anim = readyAnims.get(i);
                             anim.startAnimation();
-                            sDelayedAnims.remove(anim);
+                            delayedAnims.remove(anim);
                         }
-                        sReadyAnims.clear();
+                        readyAnims.clear();
                     }
 
                     // Now process all active animations. The return value from animationFrame()
                     // tells the handler whether it should now be ended
-                    int numAnims = sAnimations.size();
+                    int numAnims = animations.size();
                     for (int i = 0; i < numAnims; ++i) {
-                        ValueAnimator anim = sAnimations.get(i);
+                        ValueAnimator anim = animations.get(i);
                         if (anim.animationFrame(currentTime)) {
-                            sEndingAnims.add(anim);
+                            endingAnims.add(anim);
                         }
                     }
-                    if (sEndingAnims.size() > 0) {
-                        for (int i = 0; i < sEndingAnims.size(); ++i) {
-                            sEndingAnims.get(i).endAnimation();
+                    if (endingAnims.size() > 0) {
+                        for (int i = 0; i < endingAnims.size(); ++i) {
+                            endingAnims.get(i).endAnimation();
                         }
-                        sEndingAnims.clear();
+                        endingAnims.clear();
                     }
 
                     // If there are still active or delayed animations, call the handler again
                     // after the frameDelay
-                    if (callAgain && (!sAnimations.isEmpty() || !sDelayedAnims.isEmpty())) {
+                    if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) {
                         sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay -
                             (AnimationUtils.currentAnimationTimeMillis() - currentTime)));
                     }
@@ -935,13 +978,13 @@
         mCurrentIteration = 0;
         mPlayingState = STOPPED;
         mStartedDelay = false;
-        sPendingAnimations.add(this);
-        if (sAnimationHandler == null) {
-            sAnimationHandler = new AnimationHandler();
+        sPendingAnimations.get().add(this);
+        AnimationHandler animationHandler = sAnimationHandler.get();
+        if (animationHandler == null) {
+            animationHandler = new AnimationHandler();
+            sAnimationHandler.set(animationHandler);
         }
-        // TODO: does this put too many messages on the queue if the handler
-        // is already running?
-        sAnimationHandler.sendEmptyMessage(ANIMATION_START);
+        animationHandler.sendEmptyMessage(ANIMATION_START);
     }
 
     @Override
@@ -965,14 +1008,16 @@
 
     @Override
     public void end() {
-        if (!sAnimations.contains(this) && !sPendingAnimations.contains(this)) {
+        if (!sAnimations.get().contains(this) && !sPendingAnimations.get().contains(this)) {
             // Special case if the animation has not yet started; get it ready for ending
             mStartedDelay = false;
-            sPendingAnimations.add(this);
-            if (sAnimationHandler == null) {
-                sAnimationHandler = new AnimationHandler();
+            sPendingAnimations.get().add(this);
+            AnimationHandler animationHandler = sAnimationHandler.get();
+            if (animationHandler == null) {
+                animationHandler = new AnimationHandler();
+                sAnimationHandler.set(animationHandler);
             }
-            sAnimationHandler.sendEmptyMessage(ANIMATION_START);
+            animationHandler.sendEmptyMessage(ANIMATION_START);
         }
         // Just set the ENDED flag - this causes the animation to end the next time a frame
         // is processed.
@@ -1009,7 +1054,7 @@
      * called on the UI thread.
      */
     private void endAnimation() {
-        sAnimations.remove(this);
+        sAnimations.get().remove(this);
         mPlayingState = STOPPED;
         if (mListeners != null) {
             ArrayList<AnimatorListener> tmpListeners =
@@ -1026,7 +1071,7 @@
      */
     private void startAnimation() {
         initAnimation();
-        sAnimations.add(this);
+        sAnimations.get().add(this);
         if (mStartDelay > 0 && mListeners != null) {
             // Listeners were already notified in start() if startDelay is 0; this is
             // just for delayed animations
@@ -1225,6 +1270,6 @@
      * @hide
      */
     public static int getCurrentAnimationsCount() {
-        return sAnimations.size();
+        return sAnimations.get().size();
     }
 }
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 378a8bd..33f88d8 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1228,7 +1228,6 @@
      */
     protected void onPause() {
         mCalled = true;
-        QueuedWork.waitToFinish();
     }
 
     /**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2abe822..c5badaf 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -2347,6 +2347,9 @@
             r.activity.mConfigChangeFlags |= configChanges;
             Bundle state = performPauseActivity(token, finished, true);
 
+            // Make sure any pending writes are now committed.
+            QueuedWork.waitToFinish();
+            
             // Tell the activity manager we have paused.
             try {
                 ActivityManagerNative.getDefault().activityPaused(token, state);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6897537..257dbf0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6903,14 +6903,14 @@
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
-        if (action == MotionEvent.ACTION_DOWN) {
-            if (mInsertionPointCursorController != null) {
-                mInsertionPointCursorController.onTouchEvent(event);
-            }
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.onTouchEvent(event);
-            }
+        if (mInsertionPointCursorController != null) {
+            mInsertionPointCursorController.onTouchEvent(event);
+        }
+        if (mSelectionModifierCursorController != null) {
+            mSelectionModifierCursorController.onTouchEvent(event);
+        }
 
+        if (action == MotionEvent.ACTION_DOWN) {
             // Reset this state; it will be re-set if super.onTouchEvent
             // causes focus to move to the view.
             mTouchFocusSelected = false;
@@ -6931,13 +6931,6 @@
 
         if ((mMovement != null || onCheckIsTextEditor()) && isEnabled()
                 && mText instanceof Spannable && mLayout != null) {
-            if (mInsertionPointCursorController != null) {
-                mInsertionPointCursorController.onTouchEvent(event);
-            }
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.onTouchEvent(event);
-            }
-
             boolean handled = false;
 
             // Save previous selection, in case this event is used to show the IME.
@@ -6946,7 +6939,7 @@
 
             final int oldScrollX = mScrollX;
             final int oldScrollY = mScrollY;
-            
+
             if (mMovement != null) {
                 handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
             }
@@ -7981,7 +7974,7 @@
                 }
                 mDrawable = mSelectHandleLeft;
                 handleWidth = mDrawable.getIntrinsicWidth();
-                mHotspotX = handleWidth / 4 * 3;
+                mHotspotX = (handleWidth * 3) / 4;
                 break;
             }
 
@@ -8264,6 +8257,11 @@
         // Whether selection anchors are active
         private boolean mIsShowing;
 
+        // Double tap detection
+        private long mPreviousTapUpTime = 0;
+        private int mPreviousTapPositionX;
+        private int mPreviousTapPositionY;
+
         private static final int DELAY_BEFORE_FADE_OUT = 4100;
 
         private final Runnable mHider = new Runnable() {
@@ -8369,6 +8367,26 @@
                         // Remember finger down position, to be able to start selection from there
                         mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
 
+                        // Double tap detection
+                        long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
+                        if (duration <= ViewConfiguration.getDoubleTapTimeout()) {
+                            final int deltaX = x - mPreviousTapPositionX;
+                            final int deltaY = y - mPreviousTapPositionY;
+                            final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
+                            final int doubleTapSlop =
+                                ViewConfiguration.get(getContext()).getScaledDoubleTapSlop();
+                            final int slopSquared = doubleTapSlop * doubleTapSlop;
+                            if (distanceSquared < slopSquared) {
+                                startSelectionActionMode();
+                                // Hacky: onTapUpEvent will open a context menu with cut/copy
+                                // Prevent this by hiding handles which will be revived instead.
+                                hide();
+                            }
+                        }
+
+                        mPreviousTapPositionX = x;
+                        mPreviousTapPositionY = y;
+
                         break;
 
                     case MotionEvent.ACTION_POINTER_DOWN:
@@ -8380,6 +8398,10 @@
                             updateMinAndMaxOffsets(event);
                         }
                         break;
+
+                    case MotionEvent.ACTION_UP:
+                        mPreviousTapUpTime = SystemClock.uptimeMillis();
+                        break;
                 }
             }
             return false;
diff --git a/core/res/res/anim/wallpaper_intra_close_enter.xml b/core/res/res/anim/wallpaper_intra_close_enter.xml
index 5c4f5c9..73bf9a3 100644
--- a/core/res/res/anim/wallpaper_intra_close_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_close_enter.xml
@@ -20,12 +20,6 @@
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@anim/decelerate_interpolator"
         android:zAdjustment="top">
-    <scale android:fromXScale="2.0" android:toXScale="1.0"
-           android:fromYScale="2.0" android:toYScale="1.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-	<translate android:fromXDelta="-150%p" android:toXDelta="0%p"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+            android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/core/res/res/anim/wallpaper_intra_close_exit.xml b/core/res/res/anim/wallpaper_intra_close_exit.xml
index acaf773..d43660e 100644
--- a/core/res/res/anim/wallpaper_intra_close_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_close_exit.xml
@@ -18,13 +18,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/accelerate_interpolator">
-    <scale android:fromXScale="1.0" android:toXScale=".5"
-           android:fromYScale="1.0" android:toYScale=".5"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-	<translate android:fromXDelta="0%p" android:toXDelta="100%p"
-        android:duration="@android:integer/config_mediumAnimTime"/>
-    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+        android:interpolator="@anim/decelerate_interpolator">
+    <alpha android:fromAlpha="0.5" android:toAlpha="0.0"
+            android:duration="@android:integer/config_shortAnimTime"/>
 </set>
diff --git a/core/res/res/anim/wallpaper_intra_open_enter.xml b/core/res/res/anim/wallpaper_intra_open_enter.xml
index 81c9991..c85ca4c 100644
--- a/core/res/res/anim/wallpaper_intra_open_enter.xml
+++ b/core/res/res/anim/wallpaper_intra_open_enter.xml
@@ -19,12 +19,6 @@
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
         android:interpolator="@anim/decelerate_interpolator">
-    <scale android:fromXScale=".5" android:toXScale="1.0"
-           android:fromYScale=".5" android:toYScale="1.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <translate android:fromXDelta="100%p" android:toXDelta="0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-    <alpha android:fromAlpha="0.0" android:toAlpha="1.0"
-            android:duration="@android:integer/config_mediumAnimTime" />
+    <alpha android:fromAlpha="0.5" android:toAlpha="1.0"
+            android:duration="@android:integer/config_shortAnimTime" />
 </set>
diff --git a/core/res/res/anim/wallpaper_intra_open_exit.xml b/core/res/res/anim/wallpaper_intra_open_exit.xml
index 28c4287..eaeac22 100644
--- a/core/res/res/anim/wallpaper_intra_open_exit.xml
+++ b/core/res/res/anim/wallpaper_intra_open_exit.xml
@@ -18,14 +18,8 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:interpolator="@anim/accelerate_interpolator"
+        android:interpolator="@anim/decelerate_interpolator"
         android:zAdjustment="top">
-    <scale android:fromXScale="1.0" android:toXScale="2.0"
-           android:fromYScale="1.0" android:toYScale="2.0"
-           android:pivotX="100%p" android:pivotY="50%p"
-           android:duration="@android:integer/config_mediumAnimTime" />
-    <translate android:fromXDelta="0" android:toXDelta="-150%p"
-            android:duration="@android:integer/config_mediumAnimTime"/>
-    <alpha android:fromAlpha="1.0" android:toAlpha="0.0"
-            android:duration="@android:integer/config_mediumAnimTime"/>
+    <alpha android:fromAlpha="0.5" android:toAlpha="0.0"
+            android:duration="@android:integer/config_shortAnimTime"/>
 </set>
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index b0d2c64..bbde516 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -18,18 +18,381 @@
 
 #include "ARTPSource.h"
 
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include <ctype.h>
 
 namespace android {
 
-AMPEG4AudioAssembler::AMPEG4AudioAssembler(const sp<AMessage> &notify)
+static bool GetAttribute(const char *s, const char *key, AString *value) {
+    value->clear();
+
+    size_t keyLen = strlen(key);
+
+    for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
+        const char *colonPos = strchr(s, ';');
+
+        size_t len =
+            (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+            value->setTo(&s[keyLen + 1], len - keyLen - 1);
+            return true;
+        }
+
+        if (colonPos == NULL) {
+            return false;
+        }
+
+        s = colonPos + 1;
+    }
+}
+
+static sp<ABuffer> decodeHex(const AString &s) {
+    if ((s.size() % 2) != 0) {
+        return NULL;
+    }
+
+    size_t outLen = s.size() / 2;
+    sp<ABuffer> buffer = new ABuffer(outLen);
+    uint8_t *out = buffer->data();
+
+    uint8_t accum = 0;
+    for (size_t i = 0; i < s.size(); ++i) {
+        char c = s.c_str()[i];
+        unsigned value;
+        if (c >= '0' && c <= '9') {
+            value = c - '0';
+        } else if (c >= 'a' && c <= 'f') {
+            value = c - 'a' + 10;
+        } else if (c >= 'A' && c <= 'F') {
+            value = c - 'A' + 10;
+        } else {
+            return NULL;
+        }
+
+        accum = (accum << 4) | value;
+
+        if (i & 1) {
+            *out++ = accum;
+
+            accum = 0;
+        }
+    }
+
+    return buffer;
+}
+
+static status_t parseAudioObjectType(
+        ABitReader *bits, unsigned *audioObjectType) {
+    *audioObjectType = bits->getBits(5);
+    if ((*audioObjectType) == 31) {
+        *audioObjectType = 32 + bits->getBits(6);
+    }
+
+    return OK;
+}
+
+static status_t parseGASpecificConfig(
+        ABitReader *bits,
+        unsigned audioObjectType, unsigned channelConfiguration) {
+    unsigned frameLengthFlag = bits->getBits(1);
+    unsigned dependsOnCoreCoder = bits->getBits(1);
+    if (dependsOnCoreCoder) {
+        /* unsigned coreCoderDelay = */bits->getBits(1);
+    }
+    unsigned extensionFlag = bits->getBits(1);
+
+    if (!channelConfiguration) {
+        // program_config_element
+        return ERROR_UNSUPPORTED;  // XXX to be implemented
+    }
+
+    if (audioObjectType == 6 || audioObjectType == 20) {
+        /* unsigned layerNr = */bits->getBits(3);
+    }
+
+    if (extensionFlag) {
+        if (audioObjectType == 22) {
+            /* unsigned numOfSubFrame = */bits->getBits(5);
+            /* unsigned layerLength = */bits->getBits(11);
+        } else if (audioObjectType == 17 || audioObjectType == 19
+                || audioObjectType == 20 || audioObjectType == 23) {
+            /* unsigned aacSectionDataResilienceFlag = */bits->getBits(1);
+            /* unsigned aacScalefactorDataResilienceFlag = */bits->getBits(1);
+            /* unsigned aacSpectralDataResilienceFlag = */bits->getBits(1);
+        }
+
+        unsigned extensionFlag3 = bits->getBits(1);
+        CHECK_EQ(extensionFlag3, 0u);  // TBD in version 3
+    }
+
+    return OK;
+}
+
+static status_t parseAudioSpecificConfig(ABitReader *bits) {
+    unsigned audioObjectType;
+    CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
+
+    unsigned samplingFreqIndex = bits->getBits(4);
+    if (samplingFreqIndex == 0x0f) {
+        /* unsigned samplingFrequency = */bits->getBits(24);
+    }
+
+    unsigned channelConfiguration = bits->getBits(4);
+
+    unsigned extensionAudioObjectType = 0;
+    unsigned sbrPresent = 0;
+
+    if (audioObjectType == 5) {
+        extensionAudioObjectType = audioObjectType;
+        sbrPresent = 1;
+        unsigned extensionSamplingFreqIndex = bits->getBits(4);
+        if (extensionSamplingFreqIndex == 0x0f) {
+            /* unsigned extensionSamplingFrequency = */bits->getBits(24);
+        }
+        CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
+    }
+
+    CHECK((audioObjectType >= 1 && audioObjectType <= 4)
+        || (audioObjectType >= 6 && audioObjectType <= 7)
+        || audioObjectType == 17
+        || (audioObjectType >= 19 && audioObjectType <= 23));
+
+    CHECK_EQ(parseGASpecificConfig(
+                bits, audioObjectType, channelConfiguration), (status_t)OK);
+
+    if (audioObjectType == 17
+            || (audioObjectType >= 19 && audioObjectType <= 27)) {
+        unsigned epConfig = bits->getBits(2);
+        if (epConfig == 2 || epConfig == 3) {
+            // ErrorProtectionSpecificConfig
+            return ERROR_UNSUPPORTED;  // XXX to be implemented
+
+            if (epConfig == 3) {
+                unsigned directMapping = bits->getBits(1);
+                CHECK_EQ(directMapping, 1u);
+            }
+        }
+    }
+
+#if 0
+    // This is not supported here as the upper layers did not explicitly
+    // signal the length of AudioSpecificConfig.
+
+    if (extensionAudioObjectType != 5 && bits->numBitsLeft() >= 16) {
+        unsigned syncExtensionType = bits->getBits(11);
+        if (syncExtensionType == 0x2b7) {
+            CHECK_EQ(parseAudioObjectType(bits, &extensionAudioObjectType),
+                     (status_t)OK);
+
+            sbrPresent = bits->getBits(1);
+
+            if (sbrPresent == 1) {
+                unsigned extensionSamplingFreqIndex = bits->getBits(4);
+                if (extensionSamplingFreqIndex == 0x0f) {
+                    /* unsigned extensionSamplingFrequency = */bits->getBits(24);
+                }
+            }
+        }
+    }
+#endif
+
+    return OK;
+}
+
+static status_t parseStreamMuxConfig(
+        ABitReader *bits,
+        unsigned *numSubFrames,
+        unsigned *frameLengthType,
+        bool *otherDataPresent,
+        unsigned *otherDataLenBits) {
+    unsigned audioMuxVersion = bits->getBits(1);
+
+    unsigned audioMuxVersionA = 0;
+    if (audioMuxVersion == 1) {
+        audioMuxVersionA = bits->getBits(1);
+    }
+
+    CHECK_EQ(audioMuxVersionA, 0u);  // otherwise future spec
+
+    if (audioMuxVersion != 0) {
+        return ERROR_UNSUPPORTED;  // XXX to be implemented;
+    }
+    CHECK_EQ(audioMuxVersion, 0u);  // XXX to be implemented
+
+    unsigned allStreamsSameTimeFraming = bits->getBits(1);
+    CHECK_EQ(allStreamsSameTimeFraming, 1u);  // There's only one stream.
+
+    *numSubFrames = bits->getBits(6);
+    unsigned numProgram = bits->getBits(4);
+    CHECK_EQ(numProgram, 0u);  // disabled in RTP LATM
+
+    unsigned numLayer = bits->getBits(3);
+    CHECK_EQ(numLayer, 0u);  // disabled in RTP LATM
+
+    if (audioMuxVersion == 0) {
+        // AudioSpecificConfig
+        CHECK_EQ(parseAudioSpecificConfig(bits), (status_t)OK);
+    } else {
+        TRESPASS();  // XXX to be implemented
+    }
+
+    *frameLengthType = bits->getBits(3);
+    switch (*frameLengthType) {
+        case 0:
+        {
+            /* unsigned bufferFullness = */bits->getBits(8);
+
+            // The "coreFrameOffset" does not apply since there's only
+            // a single layer.
+            break;
+        }
+
+        case 1:
+        {
+            /* unsigned frameLength = */bits->getBits(9);
+            break;
+        }
+
+        case 3:
+        case 4:
+        case 5:
+        {
+            /* unsigned CELPframeLengthTableIndex = */bits->getBits(6);
+            break;
+        }
+
+        case 6:
+        case 7:
+        {
+            /* unsigned HVXCframeLengthTableIndex = */bits->getBits(1);
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    *otherDataPresent = bits->getBits(1);
+    *otherDataLenBits = 0;
+    if (*otherDataPresent) {
+        if (audioMuxVersion == 1) {
+            TRESPASS();  // XXX to be implemented
+        } else {
+            *otherDataLenBits = 0;
+
+            unsigned otherDataLenEsc;
+            do {
+                (*otherDataLenBits) <<= 8;
+                otherDataLenEsc = bits->getBits(1);
+                unsigned otherDataLenTmp = bits->getBits(8);
+                (*otherDataLenBits) += otherDataLenTmp;
+            } while (otherDataLenEsc);
+        }
+    }
+
+    unsigned crcCheckPresent = bits->getBits(1);
+    if (crcCheckPresent) {
+        /* unsigned crcCheckSum = */bits->getBits(8);
+    }
+
+    return OK;
+}
+
+sp<ABuffer> AMPEG4AudioAssembler::removeLATMFraming(const sp<ABuffer> &buffer) {
+    CHECK(!mMuxConfigPresent);  // XXX to be implemented
+
+    sp<ABuffer> out = new ABuffer(buffer->size());
+    out->setRange(0, 0);
+
+    size_t offset = 0;
+    uint8_t *ptr = buffer->data();
+
+    for (size_t i = 0; i <= mNumSubFrames; ++i) {
+        // parse PayloadLengthInfo
+
+        unsigned payloadLength = 0;
+
+        switch (mFrameLengthType) {
+            case 0:
+            {
+                unsigned muxSlotLengthBytes = 0;
+                unsigned tmp;
+                do {
+                    CHECK_LT(offset, buffer->size());
+                    tmp = ptr[offset++];
+                    muxSlotLengthBytes += tmp;
+                } while (tmp == 0xff);
+
+                payloadLength = muxSlotLengthBytes;
+                break;
+            }
+
+            default:
+                TRESPASS();  // XXX to be implemented
+                break;
+        }
+
+        CHECK_LE(offset + payloadLength, buffer->size());
+
+        memcpy(out->data() + out->size(), &ptr[offset], payloadLength);
+        out->setRange(0, out->size() + payloadLength);
+
+        offset += payloadLength;
+
+        if (mOtherDataPresent) {
+            // We want to stay byte-aligned.
+
+            CHECK((mOtherDataLenBits % 8) == 0);
+            CHECK_LE(offset + (mOtherDataLenBits / 8), buffer->size());
+            offset += mOtherDataLenBits / 8;
+        }
+    }
+
+    CHECK_EQ(offset, buffer->size());
+
+    return out;
+}
+
+AMPEG4AudioAssembler::AMPEG4AudioAssembler(
+        const sp<AMessage> &notify, const AString &params)
     : mNotifyMsg(notify),
+      mMuxConfigPresent(false),
       mAccessUnitRTPTime(0),
       mNextExpectedSeqNoValid(false),
       mNextExpectedSeqNo(0),
       mAccessUnitDamaged(false) {
+    AString val;
+    if (!GetAttribute(params.c_str(), "cpresent", &val)) {
+        mMuxConfigPresent = true;
+    } else if (val == "0") {
+        mMuxConfigPresent = false;
+    } else {
+        CHECK(val == "1");
+        mMuxConfigPresent = true;
+    }
+
+    CHECK(GetAttribute(params.c_str(), "config", &val));
+
+    sp<ABuffer> config = decodeHex(val);
+    CHECK(config != NULL);
+
+    ABitReader bits(config->data(), config->size());
+    status_t err = parseStreamMuxConfig(
+            &bits, &mNumSubFrames, &mFrameLengthType,
+            &mOtherDataPresent, &mOtherDataLenBits);
+
+    CHECK_EQ(err, (status_t)NO_ERROR);
 }
 
 AMPEG4AudioAssembler::~AMPEG4AudioAssembler() {
@@ -108,13 +471,7 @@
     while (it != mPackets.end()) {
         const sp<ABuffer> &unit = *it;
 
-        size_t n = 0;
-        while (unit->data()[n] == 0xff) {
-            ++n;
-        }
-        ++n;
-
-        totalSize += unit->size() - n;
+        totalSize += unit->size();
         ++it;
     }
 
@@ -124,20 +481,13 @@
     while (it != mPackets.end()) {
         const sp<ABuffer> &unit = *it;
 
-        size_t n = 0;
-        while (unit->data()[n] == 0xff) {
-            ++n;
-        }
-        ++n;
-
         memcpy((uint8_t *)accessUnit->data() + offset,
-               unit->data() + n, unit->size() - n);
-
-        offset += unit->size() - n;
+               unit->data(), unit->size());
 
         ++it;
     }
 
+    accessUnit = removeLATMFraming(accessUnit);
     CopyTimes(accessUnit, *mPackets.begin());
 
 #if 0
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
index bf9f204..9cef94c 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
@@ -27,9 +27,11 @@
 namespace android {
 
 struct AMessage;
+struct AString;
 
 struct AMPEG4AudioAssembler : public ARTPAssembler {
-    AMPEG4AudioAssembler(const sp<AMessage> &notify);
+    AMPEG4AudioAssembler(
+            const sp<AMessage> &notify, const AString &params);
 
 protected:
     virtual ~AMPEG4AudioAssembler();
@@ -40,6 +42,13 @@
 
 private:
     sp<AMessage> mNotifyMsg;
+
+    bool mMuxConfigPresent;
+    unsigned mNumSubFrames;
+    unsigned mFrameLengthType;
+    bool mOtherDataPresent;
+    unsigned mOtherDataLenBits;
+
     uint32_t mAccessUnitRTPTime;
     bool mNextExpectedSeqNoValid;
     uint32_t mNextExpectedSeqNo;
@@ -49,6 +58,8 @@
     AssemblyStatus addPacket(const sp<ARTPSource> &source);
     void submitAccessUnit();
 
+    sp<ABuffer> removeLATMFraming(const sp<ABuffer> &buffer);
+
     DISALLOW_EVIL_CONSTRUCTORS(AMPEG4AudioAssembler);
 };
 
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 2518264..5aae4e7 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -57,7 +57,7 @@
         mAssembler = new AAVCAssembler(notify);
         mIssueFIRRequests = true;
     } else if (!strncmp(desc.c_str(), "MP4A-LATM/", 10)) {
-        mAssembler = new AMPEG4AudioAssembler(notify);
+        mAssembler = new AMPEG4AudioAssembler(notify, params);
     } else if (!strncmp(desc.c_str(), "H263-1998/", 10)
             || !strncmp(desc.c_str(), "H263-2000/", 10)) {
         mAssembler = new AH263Assembler(notify);
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index 880aa85..547fbab 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -53,7 +53,6 @@
     mFormats.push(AString("[root]"));
 
     AString desc((const char *)data, size);
-    LOGI("%s", desc.c_str());
 
     size_t i = 0;
     for (;;) {
@@ -76,6 +75,8 @@
             return false;
         }
 
+        LOGI("%s", line.c_str());
+
         switch (line.c_str()[0]) {
             case 'v':
             {
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index e44c485..7c496e7 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -546,7 +546,9 @@
     if (!dirtyRegion.isEmpty()) {
         dirtyRegion.andSelf(Rect(buffer->width, buffer->height));
         if (previousBuffer) {
-            const Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
+            // This was const Region copyBack, but that causes an
+            // internal compile error on simulator builds
+            /*const*/ Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion));
             if (!copyBack.isEmpty()) {
                 void* prevBits;
                 if (lock(previousBuffer,