Merge "Fix for IOOBoundsExc in SpannableStringBuilder"
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java
index e6b1c08..8471df9 100644
--- a/core/java/android/accounts/AccountManagerService.java
+++ b/core/java/android/accounts/AccountManagerService.java
@@ -520,6 +520,18 @@
         if (account == null) throw new IllegalArgumentException("account is null");
         checkManageAccountsPermission();
         long identityToken = clearCallingIdentity();
+
+        cancelNotification(getSigninRequiredNotificationId(account));
+        synchronized(mCredentialsPermissionNotificationIds) {
+            for (Pair<Pair<Account, String>, Integer> pair:
+                mCredentialsPermissionNotificationIds.keySet()) {
+                if (account.equals(pair.first.first)) {
+                    int id = mCredentialsPermissionNotificationIds.get(pair);
+                    cancelNotification(id);
+                }
+            }
+        }
+
         try {
             new RemoveAccountSession(response, account).bind();
         } finally {
diff --git a/core/java/android/content/ContentQueryMap.java b/core/java/android/content/ContentQueryMap.java
index c955094..8aeaa8f 100644
--- a/core/java/android/content/ContentQueryMap.java
+++ b/core/java/android/content/ContentQueryMap.java
@@ -33,7 +33,7 @@
  * The cursor data is accessed by row key and column name via getValue().
  */
 public class ContentQueryMap extends Observable {
-    private Cursor mCursor;
+    private volatile Cursor mCursor;
     private String[] mColumnNames;
     private int mKeyColumn;
 
@@ -71,7 +71,7 @@
         // ContentProvider then read it once into the cache. Otherwise the cache will be filled 
         // automatically.
         if (!keepUpdated) {
-            readCursorIntoCache();
+            readCursorIntoCache(cursor);
         }
     }
 
@@ -128,27 +128,35 @@
 
     /** Requeries the cursor and reads the contents into the cache */
     public void requery() {
-        mDirty = false;
-        if (!mCursor.requery()) {
-            throw new IllegalStateException("trying to requery an already closed cursor");
+        final Cursor cursor = mCursor;
+        if (cursor == null) {
+            // If mCursor is null then it means there was a requery() in flight
+            // while another thread called close(), which nulls out mCursor.
+            // If this happens ignore the requery() since we are closed anyways.
+            return;
         }
-        readCursorIntoCache();
+        mDirty = false;
+        if (!cursor.requery()) {
+            // again, don't do anything if the cursor is already closed
+            return;
+        }
+        readCursorIntoCache(cursor);
         setChanged();
         notifyObservers();
     }
 
-    private synchronized void readCursorIntoCache() {
+    private synchronized void readCursorIntoCache(Cursor cursor) {
         // Make a new map so old values returned by getRows() are undisturbed.
         int capacity = mValues != null ? mValues.size() : 0;
         mValues = new HashMap<String, ContentValues>(capacity);
-        while (mCursor.moveToNext()) {
+        while (cursor.moveToNext()) {
             ContentValues values = new ContentValues();
             for (int i = 0; i < mColumnNames.length; i++) {
                 if (i != mKeyColumn) {
-                    values.put(mColumnNames[i], mCursor.getString(i));
+                    values.put(mColumnNames[i], cursor.getString(i));
                 }
             }
-            mValues.put(mCursor.getString(mKeyColumn), values);
+            mValues.put(cursor.getString(mKeyColumn), values);
         }
     }
 
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 97f015b..1fa5af2 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1276,11 +1276,14 @@
                     state.mActiveHead = mNext;
                 }
 
+                state.mActiveSize--;
+
+                if (LOG_V) Log.d(TAG, "Span finished=" + mName + "; size=" + state.mActiveSize);
+
                 this.mCreateMillis = -1;
                 this.mName = null;
                 this.mPrev = null;
                 this.mNext = null;
-                state.mActiveSize--;
 
                 // Add ourselves to the freeList, if it's not already
                 // too big.
@@ -1367,6 +1370,7 @@
             if (span.mNext != null) {
                 span.mNext.mPrev = span;
             }
+            if (LOG_V) Log.d(TAG, "Span enter=" + name + "; size=" + state.mActiveSize);
         }
         return span;
     }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 06800d5..f987a49 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -4185,9 +4185,8 @@
 
         if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
-            if (pageShouldHandleShiftAndArrows()) {
-                mShiftIsPressed = true;
-            } else if (!nativeCursorWantsKeyEvents() && !mSelectingText) {
+            if (!pageShouldHandleShiftAndArrows() && !nativeCursorWantsKeyEvents()
+                    && !mSelectingText) {
                 setUpSelect();
             }
         }
@@ -4206,7 +4205,7 @@
                 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
             switchOutDrawHistory();
             if (pageShouldHandleShiftAndArrows()) {
-                letPageHandleNavKey(keyCode, event.getEventTime(), true);
+                letPageHandleNavKey(keyCode, event.getEventTime(), true, event.getMetaState());
                 return true;
             }
             if (mSelectingText) {
@@ -4248,7 +4247,6 @@
                 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
             // turn off copy select if a shift-key combo is pressed
             selectionDone();
-            mShiftIsPressed = false;
         }
 
         if (getSettings().getNavDump()) {
@@ -4339,9 +4337,7 @@
 
         if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
                 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
-            if (pageShouldHandleShiftAndArrows()) {
-                mShiftIsPressed = false;
-            } else if (copySelection()) {
+            if (!pageShouldHandleShiftAndArrows() && copySelection()) {
                 selectionDone();
                 return true;
             }
@@ -4350,7 +4346,7 @@
         if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
                 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
             if (pageShouldHandleShiftAndArrows()) {
-                letPageHandleNavKey(keyCode, event.getEventTime(), false);
+                letPageHandleNavKey(keyCode, event.getEventTime(), false, event.getMetaState());
                 return true;
             }
             // always handle the navigation keys in the UI thread
@@ -4592,7 +4588,6 @@
                 mDrawCursorRing = false;
             }
             mGotKeyDown = false;
-            mShiftIsPressed = false;
             mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
             mTouchMode = TOUCH_DONE_MODE;
             if (mNativeClass != 0) {
@@ -5529,7 +5524,6 @@
     private int mSelectX = 0;
     private int mSelectY = 0;
     private boolean mFocusSizeChanged = false;
-    private boolean mShiftIsPressed = false;
     private boolean mTrackballDown = false;
     private long mTrackballUpTime = 0;
     private long mLastCursorTime = 0;
@@ -5600,7 +5594,7 @@
             }
             return false; // let common code in onKeyUp at it
         }
-        if ((mMapTrackballToArrowKeys && mShiftIsPressed == false) ||
+        if ((mMapTrackballToArrowKeys && (ev.getMetaState() & KeyEvent.META_SHIFT_ON) == 0) ||
                 (mAccessibilityInjector != null || mAccessibilityScriptInjected)) {
             if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
             return false;
@@ -5629,7 +5623,7 @@
         }
         mTrackballRemainsX += ev.getX();
         mTrackballRemainsY += ev.getY();
-        doTrackball(time);
+        doTrackball(time, ev.getMetaState());
         return true;
     }
 
@@ -5713,7 +5707,7 @@
                 "KEYCODE_DPAD_LEFT}.");
     }
 
-    private void doTrackball(long time) {
+    private void doTrackball(long time, int metaState) {
         int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
         if (elapsed == 0) {
             elapsed = TRACKBALL_TIMEOUT;
@@ -5771,9 +5765,9 @@
             }
             if (mNativeClass != 0 && nativePageShouldHandleShiftAndArrows()) {
                 for (int i = 0; i < count; i++) {
-                    letPageHandleNavKey(selectKeyCode, time, true);
+                    letPageHandleNavKey(selectKeyCode, time, true, metaState);
                 }
-                letPageHandleNavKey(selectKeyCode, time, false);
+                letPageHandleNavKey(selectKeyCode, time, false, metaState);
             } else if (navHandledKey(selectKeyCode, count, false, time)) {
                 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
             }
@@ -7282,7 +7276,7 @@
      * Pass the key directly to the page.  This assumes that
      * nativePageShouldHandleShiftAndArrows() returned true.
      */
-    private void letPageHandleNavKey(int keyCode, long time, boolean down) {
+    private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
         int keyEventAction;
         int eventHubAction;
         if (down) {
@@ -7293,10 +7287,11 @@
             keyEventAction = KeyEvent.ACTION_UP;
             eventHubAction = EventHub.KEY_UP;
         }
+
         KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
-                1, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
-                | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
-                | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
+                1, (metaState & KeyEvent.META_SHIFT_ON)
+                | (metaState & KeyEvent.META_ALT_ON)
+                | (metaState & KeyEvent.META_SYM_ON)
                 , 0, 0, 0);
         mWebViewCore.sendMessage(eventHubAction, event);
     }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 30b1e5d..466e541 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -29,6 +29,7 @@
 import android.os.Handler;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.StrictMode;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
@@ -460,6 +461,15 @@
     private boolean mFlingProfilingStarted = false;
 
     /**
+     * The StrictMode "critical time span" objects to catch animation
+     * stutters.  Non-null when a time-sensitive animation is
+     * in-flight.  Must call finish() on them when done animating.
+     * These are no-ops on user builds.
+     */
+    private StrictMode.Span mScrollStrictSpan = null;
+    private StrictMode.Span mFlingStrictSpan = null;
+
+    /**
      * The last CheckForLongPress runnable we posted, if any
      */
     private CheckForLongPress mPendingCheckForLongPress;
@@ -2089,6 +2099,16 @@
             mAdapter.unregisterDataSetObserver(mDataSetObserver);
             mDataSetObserver = null;
         }
+
+        if (mScrollStrictSpan != null) {
+            mScrollStrictSpan.finish();
+            mScrollStrictSpan = null;
+        }
+
+        if (mFlingStrictSpan != null) {
+            mFlingStrictSpan.finish();
+            mFlingStrictSpan = null;
+        }
     }
 
     @Override
@@ -2559,6 +2579,11 @@
                     }
                 }
 
+                if (mScrollStrictSpan == null) {
+                    // If it's non-null, we're already in a scroll.
+                    mScrollStrictSpan = StrictMode.enterCriticalSpan("AbsListView-scroll");
+                }
+
                 if (y != mLastY) {
                     // We may be here after stopping a fling and continuing to scroll.
                     // If so, we haven't disallowed intercepting touch events yet.
@@ -2722,6 +2747,11 @@
                     mScrollProfilingStarted = false;
                 }
             }
+
+            if (mScrollStrictSpan != null) {
+                mScrollStrictSpan.finish();
+                mScrollStrictSpan = null;
+            }
             break;
         }
 
@@ -2934,6 +2964,10 @@
                     mFlingProfilingStarted = true;
                 }
             }
+
+            if (mFlingStrictSpan == null) {
+                mFlingStrictSpan = StrictMode.enterCriticalSpan("AbsListView-fling");
+            }
         }
 
         void startScroll(int distance, int duration) {
@@ -3012,6 +3046,11 @@
                             mFlingProfilingStarted = false;
                         }
                     }
+
+                    if (mFlingStrictSpan != null) {
+                        mFlingStrictSpan.finish();
+                        mFlingStrictSpan = null;
+                    }
                 }
                 break;
             }
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 432dd4a..e2d78c0 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -100,6 +100,8 @@
 
     private static final int FRAME_PADDING = 4;
 
+    private final Rect mTouchRect = new Rect();
+
     /**
      * These variables are all related to the current state of touch interaction
      * with the stack
@@ -531,7 +533,6 @@
         return true;
     }
 
-    private final Rect touchRect = new Rect();
     private void onSecondaryPointerUp(MotionEvent ev) {
         final int activePointerIndex = ev.getActionIndex();
         final int pointerId = ev.getPointerId(activePointerIndex);
@@ -552,8 +553,8 @@
                     float x = ev.getX(index);
                     float y = ev.getY(index);
 
-                    touchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
-                    if (touchRect.contains(Math.round(x), Math.round(y))) {
+                    mTouchRect.set(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
+                    if (mTouchRect.contains(Math.round(x), Math.round(y))) {
                         float oldX = ev.getX(activePointerIndex);
                         float oldY = ev.getY(activePointerIndex);
 
@@ -1049,18 +1050,23 @@
         private static final int RES_OUT = 0;
         private static final int CLICK_FEEDBACK = 1;
         private float mDensity;
+        private BlurMaskFilter mSmallBlurMaskFilter;
+        private BlurMaskFilter mLargeBlurMaskFilter;
+        private final Canvas mCanvas = new Canvas();
+        private final Canvas mMaskCanvas = new Canvas();
+        private final int[] mTmpXY = new int[2];
+        private final Matrix mIdentityMatrix = new Matrix();
 
         HolographicHelper(Context context) {
-            initializePaints(context);
-        }
-
-        void initializePaints(Context context) {
             mDensity = context.getResources().getDisplayMetrics().density;
 
             mHolographicPaint.setFilterBitmap(true);
             mHolographicPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
             mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
             mErasePaint.setFilterBitmap(true);
+
+            mSmallBlurMaskFilter = new BlurMaskFilter(2 * mDensity, BlurMaskFilter.Blur.NORMAL);
+            mLargeBlurMaskFilter = new BlurMaskFilter(4 * mDensity, BlurMaskFilter.Blur.NORMAL);
         }
 
         Bitmap createOutline(View v) {
@@ -1070,10 +1076,10 @@
         Bitmap createOutline(View v, int type) {
             if (type == RES_OUT) {
                 mHolographicPaint.setColor(0xff6699ff);
-                mBlurPaint.setMaskFilter(new BlurMaskFilter(2*mDensity, BlurMaskFilter.Blur.NORMAL));
+                mBlurPaint.setMaskFilter(mSmallBlurMaskFilter);
             } else if (type == CLICK_FEEDBACK) {
                 mHolographicPaint.setColor(0x886699ff);
-                mBlurPaint.setMaskFilter(new BlurMaskFilter(4*mDensity, BlurMaskFilter.Blur.NORMAL));
+                mBlurPaint.setMaskFilter(mLargeBlurMaskFilter);
             }
 
             if (v.getMeasuredWidth() == 0 || v.getMeasuredHeight() == 0) {
@@ -1082,7 +1088,7 @@
 
             Bitmap bitmap = Bitmap.createBitmap(v.getMeasuredWidth(), v.getMeasuredHeight(),
                     Bitmap.Config.ARGB_8888);
-            Canvas canvas = new Canvas(bitmap);
+            mCanvas.setBitmap(bitmap);
 
             float rotationX = v.getRotationX();
             float rotation = v.getRotation();
@@ -1090,23 +1096,22 @@
             v.setRotationX(0);
             v.setRotation(0);
             v.setTranslationY(0);
-            v.draw(canvas);
+            v.draw(mCanvas);
             v.setRotationX(rotationX);
             v.setRotation(rotation);
             v.setTranslationY(translationY);
 
-            drawOutline(canvas, bitmap);
+            drawOutline(mCanvas, bitmap);
             return bitmap;
         }
 
-        final Matrix id = new Matrix();
         void drawOutline(Canvas dest, Bitmap src) {
-            int[] xy = new int[2];
+            final int[] xy = mTmpXY;
             Bitmap mask = src.extractAlpha(mBlurPaint, xy);
-            Canvas maskCanvas = new Canvas(mask);
-            maskCanvas.drawBitmap(src, -xy[0], -xy[1], mErasePaint);
+            mMaskCanvas.setBitmap(mask);
+            mMaskCanvas.drawBitmap(src, -xy[0], -xy[1], mErasePaint);
             dest.drawColor(0, PorterDuff.Mode.CLEAR);
-            dest.setMatrix(id);
+            dest.setMatrix(mIdentityMatrix);
             dest.drawBitmap(mask, xy[0], xy[1], mHolographicPaint);
             mask.recycle();
         }
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index fbc8549..aefe8d8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6720,7 +6720,8 @@
                     + " before=" + before + " after=" + after + ": " + buffer);
 
             if (AccessibilityManager.getInstance(mContext).isEnabled()
-                    && !isPasswordInputType(mInputType)) {
+                    && !isPasswordInputType(mInputType)
+                    && !hasPasswordTransformationMethod()) {
                 mBeforeText = buffer.toString();
             }
 
@@ -7599,7 +7600,7 @@
             return false;
         }
 
-        final boolean isPassword = isPasswordInputType(mInputType);
+        final boolean isPassword = hasPasswordTransformationMethod();
 
         if (!isPassword) {
             CharSequence text = getText();
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index ff9f255..538e7bf 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -84,11 +84,13 @@
             OMX_COLOR_FORMATTYPE colorFormat,
             const sp<Surface> &surface,
             size_t displayWidth, size_t displayHeight,
-            size_t decodedWidth, size_t decodedHeight)
+            size_t decodedWidth, size_t decodedHeight,
+            int32_t rotationDegrees)
         : mTarget(NULL) {
             init(colorFormat, surface,
                  displayWidth, displayHeight,
-                 decodedWidth, decodedHeight);
+                 decodedWidth, decodedHeight,
+                 rotationDegrees);
     }
 
     virtual void render(MediaBuffer *buffer) {
@@ -113,7 +115,8 @@
             OMX_COLOR_FORMATTYPE colorFormat,
             const sp<Surface> &surface,
             size_t displayWidth, size_t displayHeight,
-            size_t decodedWidth, size_t decodedHeight);
+            size_t decodedWidth, size_t decodedHeight,
+            int32_t rotationDegrees);
 
     AwesomeLocalRenderer(const AwesomeLocalRenderer &);
     AwesomeLocalRenderer &operator=(const AwesomeLocalRenderer &);;
@@ -123,15 +126,19 @@
         OMX_COLOR_FORMATTYPE colorFormat,
         const sp<Surface> &surface,
         size_t displayWidth, size_t displayHeight,
-        size_t decodedWidth, size_t decodedHeight) {
+        size_t decodedWidth, size_t decodedHeight,
+        int32_t rotationDegrees) {
     mTarget = new SoftwareRenderer(
             colorFormat, surface, displayWidth, displayHeight,
-            decodedWidth, decodedHeight);
+            decodedWidth, decodedHeight, rotationDegrees);
 }
 
 struct AwesomeNativeWindowRenderer : public AwesomeRenderer {
-    AwesomeNativeWindowRenderer(const sp<ANativeWindow> &nativeWindow)
+    AwesomeNativeWindowRenderer(
+            const sp<ANativeWindow> &nativeWindow,
+            int32_t rotationDegrees)
         : mNativeWindow(nativeWindow) {
+        applyRotation(rotationDegrees);
     }
 
     virtual void render(MediaBuffer *buffer) {
@@ -153,6 +160,22 @@
 private:
     sp<ANativeWindow> mNativeWindow;
 
+    void applyRotation(int32_t rotationDegrees) {
+        uint32_t transform;
+        switch (rotationDegrees) {
+            case 0: transform = 0; break;
+            case 90: transform = HAL_TRANSFORM_ROT_90; break;
+            case 180: transform = HAL_TRANSFORM_ROT_180; break;
+            case 270: transform = HAL_TRANSFORM_ROT_270; break;
+            default: transform = 0; break;
+        }
+
+        if (transform) {
+            CHECK_EQ(0, native_window_set_buffers_transform(
+                        mNativeWindow.get(), transform));
+        }
+    }
+
     AwesomeNativeWindowRenderer(const AwesomeNativeWindowRenderer &);
     AwesomeNativeWindowRenderer &operator=(
             const AwesomeNativeWindowRenderer &);
@@ -796,7 +819,19 @@
     CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
     CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
 
-    notifyListener_l(MEDIA_SET_VIDEO_SIZE, decodedWidth, decodedHeight);
+    int32_t rotationDegrees;
+    if (!mVideoTrack->getFormat()->findInt32(
+                kKeyRotation, &rotationDegrees)) {
+        rotationDegrees = 0;
+    }
+
+    if (rotationDegrees == 90 || rotationDegrees == 270) {
+        notifyListener_l(
+                MEDIA_SET_VIDEO_SIZE, decodedHeight, decodedWidth);
+    } else {
+        notifyListener_l(
+                MEDIA_SET_VIDEO_SIZE, decodedWidth, decodedHeight);
+    }
 }
 
 void AwesomePlayer::initRenderer_l() {
@@ -814,6 +849,12 @@
     CHECK(meta->findInt32(kKeyWidth, &decodedWidth));
     CHECK(meta->findInt32(kKeyHeight, &decodedHeight));
 
+    int32_t rotationDegrees;
+    if (!mVideoTrack->getFormat()->findInt32(
+                kKeyRotation, &rotationDegrees)) {
+        rotationDegrees = 0;
+    }
+
     mVideoRenderer.clear();
 
     // Must ensure that mVideoRenderer's destructor is actually executed
@@ -824,7 +865,8 @@
         // Hardware decoders avoid the CPU color conversion by decoding
         // directly to ANativeBuffers, so we must use a renderer that
         // just pushes those buffers to the ANativeWindow.
-        mVideoRenderer = new AwesomeNativeWindowRenderer(mSurface);
+        mVideoRenderer =
+            new AwesomeNativeWindowRenderer(mSurface, rotationDegrees);
     } else {
         // Other decoders are instantiated locally and as a consequence
         // allocate their buffers in local address space.  This renderer
@@ -834,7 +876,8 @@
             (OMX_COLOR_FORMATTYPE)format,
             mSurface,
             mVideoWidth, mVideoHeight,
-            decodedWidth, decodedHeight);
+            decodedWidth, decodedHeight,
+            rotationDegrees);
     }
 }
 
@@ -1819,7 +1862,8 @@
                     state->mVideoWidth,
                     state->mVideoHeight,
                     state->mDecodedWidth,
-                    state->mDecodedHeight);
+                    state->mDecodedHeight,
+                    0);
 
         mVideoRendererIsPreview = true;
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 2e94a12..bb929fd 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -27,11 +27,11 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/DataSource.h>
 #include "include/ESDS.h"
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaBufferGroup.h>
-#include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
@@ -766,55 +766,11 @@
 
         case FOURCC('t', 'k', 'h', 'd'):
         {
-            if (chunk_data_size < 4) {
-                return ERROR_MALFORMED;
+            status_t err;
+            if ((err = parseTrackHeader(data_offset, chunk_data_size)) != OK) {
+                return err;
             }
 
-            uint8_t version;
-            if (mDataSource->readAt(data_offset, &version, 1) < 1) {
-                return ERROR_IO;
-            }
-
-            uint64_t ctime, mtime, duration;
-            int32_t id;
-            uint32_t width, height;
-
-            if (version == 1) {
-                if (chunk_data_size != 36 + 60) {
-                    return ERROR_MALFORMED;
-                }
-
-                uint8_t buffer[36 + 60];
-                if (mDataSource->readAt(
-                            data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
-                    return ERROR_IO;
-                }
-
-                ctime = U64_AT(&buffer[4]);
-                mtime = U64_AT(&buffer[12]);
-                id = U32_AT(&buffer[20]);
-                duration = U64_AT(&buffer[28]);
-                width = U32_AT(&buffer[88]);
-                height = U32_AT(&buffer[92]);
-            } else if (version == 0) {
-                if (chunk_data_size != 24 + 60) {
-                    return ERROR_MALFORMED;
-                }
-
-                uint8_t buffer[24 + 60];
-                if (mDataSource->readAt(
-                            data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
-                    return ERROR_IO;
-                }
-                ctime = U32_AT(&buffer[4]);
-                mtime = U32_AT(&buffer[8]);
-                id = U32_AT(&buffer[12]);
-                duration = U32_AT(&buffer[20]);
-                width = U32_AT(&buffer[76]);
-                height = U32_AT(&buffer[80]);
-            }
-
-            mLastTrack->meta->setInt32(kKeyTrackID, id);
             *offset += chunk_size;
             break;
         }
@@ -1275,6 +1231,93 @@
     return OK;
 }
 
+status_t MPEG4Extractor::parseTrackHeader(
+        off_t data_offset, off_t data_size) {
+    if (data_size < 4) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t version;
+    if (mDataSource->readAt(data_offset, &version, 1) < 1) {
+        return ERROR_IO;
+    }
+
+    size_t dynSize = (version == 1) ? 36 : 24;
+
+    uint8_t buffer[36 + 60];
+
+    if (data_size != (off_t)dynSize + 60) {
+        return ERROR_MALFORMED;
+    }
+
+    if (mDataSource->readAt(
+                data_offset, buffer, data_size) < (ssize_t)data_size) {
+        return ERROR_IO;
+    }
+
+    uint64_t ctime, mtime, duration;
+    int32_t id;
+
+    if (version == 1) {
+        ctime = U64_AT(&buffer[4]);
+        mtime = U64_AT(&buffer[12]);
+        id = U32_AT(&buffer[20]);
+        duration = U64_AT(&buffer[28]);
+    } else {
+        CHECK_EQ((unsigned)version, 0u);
+
+        ctime = U32_AT(&buffer[4]);
+        mtime = U32_AT(&buffer[8]);
+        id = U32_AT(&buffer[12]);
+        duration = U32_AT(&buffer[20]);
+    }
+
+    mLastTrack->meta->setInt32(kKeyTrackID, id);
+
+    size_t matrixOffset = dynSize + 16;
+    int32_t a00 = U32_AT(&buffer[matrixOffset]);
+    int32_t a01 = U32_AT(&buffer[matrixOffset + 4]);
+    int32_t dx = U32_AT(&buffer[matrixOffset + 8]);
+    int32_t a10 = U32_AT(&buffer[matrixOffset + 12]);
+    int32_t a11 = U32_AT(&buffer[matrixOffset + 16]);
+    int32_t dy = U32_AT(&buffer[matrixOffset + 20]);
+
+#if 0
+    LOGI("x' = %.2f * x + %.2f * y + %.2f",
+         a00 / 65536.0f, a01 / 65536.0f, dx / 65536.0f);
+    LOGI("y' = %.2f * x + %.2f * y + %.2f",
+         a10 / 65536.0f, a11 / 65536.0f, dy / 65536.0f);
+#endif
+
+    uint32_t rotationDegrees;
+
+    static const int32_t kFixedOne = 0x10000;
+    if (a00 == kFixedOne && a01 == 0 && a10 == 0 && a11 == kFixedOne) {
+        // Identity, no rotation
+        rotationDegrees = 0;
+    } else if (a00 == 0 && a01 == kFixedOne && a10 == -kFixedOne && a11 == 0) {
+        rotationDegrees = 90;
+    } else if (a00 == 0 && a01 == -kFixedOne && a10 == kFixedOne && a11 == 0) {
+        rotationDegrees = 270;
+    } else if (a00 == -kFixedOne && a01 == 0 && a10 == 0 && a11 == -kFixedOne) {
+        rotationDegrees = 180;
+    } else {
+        LOGW("We only support 0,90,180,270 degree rotation matrices");
+        rotationDegrees = 0;
+    }
+
+    if (rotationDegrees != 0) {
+        mLastTrack->meta->setInt32(kKeyRotation, rotationDegrees);
+    }
+
+#if 0
+    uint32_t width = U32_AT(&buffer[dynSize + 52]);
+    uint32_t height = U32_AT(&buffer[dynSize + 56]);
+#endif
+
+    return OK;
+}
+
 status_t MPEG4Extractor::parseMetaData(off_t offset, size_t size) {
     if (size < 4) {
         return ERROR_MALFORMED;
@@ -1588,7 +1631,7 @@
         const uint8_t *ptr = (const uint8_t *)data;
 
         CHECK(size >= 7);
-        CHECK_EQ(ptr[0], 1);  // configurationVersion == 1
+        CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
 
         // The number of bytes used to encode the length of a NAL unit.
         mNALLengthSize = 1 + (ptr[4] & 3);
@@ -1736,7 +1779,7 @@
         }
 
         uint32_t sampleTime;
-        CHECK_EQ(OK, mSampleTable->getMetaDataForSample(
+        CHECK_EQ((status_t)OK, mSampleTable->getMetaDataForSample(
                     sampleIndex, NULL, NULL, &sampleTime));
 
         if (mode == ReadOptions::SEEK_CLOSEST) {
@@ -1783,7 +1826,7 @@
         err = mGroup->acquire_buffer(&mBuffer);
 
         if (err != OK) {
-            CHECK_EQ(mBuffer, NULL);
+            CHECK(mBuffer == NULL);
             return err;
         }
     }
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 662a84a..3d507ca 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -35,7 +35,8 @@
         OMX_COLOR_FORMATTYPE colorFormat,
         const sp<Surface> &surface,
         size_t displayWidth, size_t displayHeight,
-        size_t decodedWidth, size_t decodedHeight)
+        size_t decodedWidth, size_t decodedHeight,
+        int32_t rotationDegrees)
     : mColorFormat(colorFormat),
       mConverter(NULL),
       mYUVMode(None),
@@ -95,6 +96,20 @@
     CHECK_EQ(0, native_window_set_buffers_geometry(
                 mSurface.get(), mDecodedWidth, mDecodedHeight,
                 halFormat));
+
+    uint32_t transform;
+    switch (rotationDegrees) {
+        case 0: transform = 0; break;
+        case 90: transform = HAL_TRANSFORM_ROT_90; break;
+        case 180: transform = HAL_TRANSFORM_ROT_180; break;
+        case 270: transform = HAL_TRANSFORM_ROT_270; break;
+        default: transform = 0; break;
+    }
+
+    if (transform) {
+        CHECK_EQ(0, native_window_set_buffers_transform(
+                    mSurface.get(), transform));
+    }
 }
 
 SoftwareRenderer::~SoftwareRenderer() {
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index 4e31059..bc2e4dc 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -88,6 +88,8 @@
     bool mIsDrm;
     status_t parseDrmSINF(off_t *offset, off_t data_offset);
 
+    status_t parseTrackHeader(off_t data_offset, off_t data_size);
+
     MPEG4Extractor(const MPEG4Extractor &);
     MPEG4Extractor &operator=(const MPEG4Extractor &);
 };
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 198bfd6..9cafc68 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -31,7 +31,8 @@
             OMX_COLOR_FORMATTYPE colorFormat,
             const sp<Surface> &surface,
             size_t displayWidth, size_t displayHeight,
-            size_t decodedWidth, size_t decodedHeight);
+            size_t decodedWidth, size_t decodedHeight,
+            int32_t rotationDegrees);
 
     ~SoftwareRenderer();
 
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 35a2c19..290f2c1 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -438,9 +438,9 @@
                 + " " + (mCssIndicator ? "CSS supported" : "CSS not supported")
                 + " " + mNetworkId
                 + " " + mSystemId
-                + "RoamInd: " + mCdmaRoamingIndicator
-                + "DefRoamInd: " + mCdmaDefaultRoamingIndicator
-                + "EmergOnly: " + mIsEmergencyOnly);
+                + " RoamInd=" + mCdmaRoamingIndicator
+                + " DefRoamInd=" + mCdmaDefaultRoamingIndicator
+                + " EmergOnly=" + mIsEmergencyOnly);
     }
 
     public void setStateOutOfService() {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 7c652c5..d7ff0c5 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -240,8 +240,8 @@
                     mCdmaPhone.mRuimRecords.getRecordsLoaded())) {
                 reason += " - radioState= " + mPhone.mCM.getRadioState() + " - RUIM not loaded";
             }
-            if (mPhone.getState() != Phone.State.IDLE &&
-                    mCdmaPhone.mSST.isConcurrentVoiceAndData()) {
+            if (!(mCdmaPhone.mSST.isConcurrentVoiceAndData() ||
+                    mPhone.getState() == Phone.State.IDLE)) {
                 reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState();
             }
             if (roaming) reason += " - Roaming";
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 2aca9ad..effb743 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -328,8 +328,8 @@
                 reason += " - PhoneState= " + mPhone.getState();
             }
             if (!mMasterDataEnabled) reason += " - mMasterDataEnabled= false";
-            if (mPhone.getServiceState().getRoaming() && getDataOnRoamingEnabled()) {
-                reason += " - Roaming";
+            if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
+                reason += " - Roaming and data roaming not enabled";
             }
             if (mIsPsRestricted) reason += " - mIsPsRestricted= true";
             if (!desiredPowerState) reason += " - desiredPowerState= false";
@@ -1037,8 +1037,15 @@
         } else {
             GsmDataConnection.FailCause cause;
             cause = (GsmDataConnection.FailCause) (ar.result);
-            if(DBG) log("PDP setup failed " + cause);
-                    // Log this failure to the Event Logs.
+            if (DBG) {
+                String apnString;
+                try {
+                    apnString = mWaitingApns.get(0).apn;
+                } catch (Exception e) {
+                    apnString = "<unknown>";
+                }
+                log(String.format("onDataSetupComplete: error apn=%s cause=%s", apnString, cause));
+            }
             if (cause.isEventLoggable()) {
                 GsmCellLocation loc = ((GsmCellLocation)mPhone.getCellLocation());
                 EventLog.writeEvent(EventLogTags.PDP_SETUP_FAIL,