Merge "Update a comment about when setOrientationHint() should be called" into gingerbread
diff --git a/api/9.xml b/api/9.xml
index 3056e47..27629b7 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -73399,16 +73399,6 @@
  visibility="public"
 >
 </field>
-<field name="CAMERA_ID_DEFAULT"
- type="int"
- transient="false"
- volatile="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <interface name="Camera.AutoFocusCallback"
  abstract="true"
@@ -86967,6 +86957,19 @@
 <parameter name="listener" type="android.media.MediaRecorder.OnInfoListener">
 </parameter>
 </method>
+<method name="setOrientationHint"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="degrees" type="int">
+</parameter>
+</method>
 <method name="setOutputFile"
  return="void"
  abstract="false"
diff --git a/api/current.xml b/api/current.xml
index ac92b7a..fb3b998 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -73399,16 +73399,6 @@
  visibility="public"
 >
 </field>
-<field name="CAMERA_ID_DEFAULT"
- type="int"
- transient="false"
- volatile="false"
- static="true"
- final="false"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
 </class>
 <interface name="Camera.AutoFocusCallback"
  abstract="true"
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 275e2eb..378189e 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -211,8 +211,7 @@
      * blocking the main application UI thread.
      *
      * @param cameraId the hardware camera to access, between 0 and
-     *     {@link #getNumberOfCameras()}-1.  Use {@link #CAMERA_ID_DEFAULT}
-     *     to access the default camera.
+     *     {@link #getNumberOfCameras()}-1.
      * @return a new Camera object, connected, locked and ready for use.
      * @throws RuntimeException if connection to the camera service fails (for
      *     example, if the camera is in use by another process).
@@ -222,18 +221,21 @@
     }
 
     /**
-     * The id for the default camera.
-     * @see #open(int)
-     */
-    public static int CAMERA_ID_DEFAULT = 0;
-
-    /**
-     * Equivalent to Camera.open(Camera.CAMERA_ID_DEFAULT).
-     * Creates a new Camera object to access the default camera.
+     * Creates a new Camera object to access the first back-facing camera on the
+     * device. If the device does not have a back-facing camera, this returns
+     * null.
      * @see #open(int)
      */
     public static Camera open() {
-        return new Camera(CAMERA_ID_DEFAULT);
+        int numberOfCameras = getNumberOfCameras();
+        CameraInfo cameraInfo = new CameraInfo();
+        for (int i = 0; i < numberOfCameras; i++) {
+            getCameraInfo(i, cameraInfo);
+            if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
+                return new Camera(i);
+            }
+        }
+        return null;
     }
 
     Camera(int cameraId) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index acda3e1..6590497 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6533,11 +6533,11 @@
 
             final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
             final boolean opaque = drawingCacheBackgroundColor != 0 || isOpaque();
-            final boolean translucentWindow = attachInfo != null && attachInfo.mTranslucentWindow;
+            final boolean use32BitCache = attachInfo != null && attachInfo.mUse32BitDrawingCache;
 
             if (width <= 0 || height <= 0 ||
                      // Projected bitmap size in bytes
-                    (width * height * (opaque && !translucentWindow ? 2 : 4) >
+                    (width * height * (opaque && !use32BitCache ? 2 : 4) >
                             ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
                 destroyDrawingCache();
                 return;
@@ -6567,7 +6567,7 @@
                 } else {
                     // Optimization for translucent windows
                     // If the window is translucent, use a 32 bits bitmap to benefit from memcpy()
-                    quality = translucentWindow ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
+                    quality = use32BitCache ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565;
                 }
 
                 // Try to cleanup memory
@@ -6581,7 +6581,7 @@
                     } else {
                         mUnscaledDrawingCache = new SoftReference<Bitmap>(bitmap);
                     }
-                    if (opaque && translucentWindow) bitmap.setHasAlpha(false);
+                    if (opaque && use32BitCache) bitmap.setHasAlpha(false);
                 } catch (OutOfMemoryError e) {
                     // If there is not enough memory to create the bitmap cache, just
                     // ignore the issue as bitmap caches are not required to draw the
@@ -9315,9 +9315,9 @@
         int mWindowTop;
 
         /**
-         * Indicates whether the window is translucent/transparent
+         * Indicates whether views need to use 32-bit drawing caches
          */
-        boolean mTranslucentWindow;        
+        boolean mUse32BitDrawingCache;
 
         /**
          * For windows that are full-screen but using insets to layout inside
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 9c249ce..c58207e 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -754,7 +754,8 @@
             // object is not initialized to its backing store, but soon it
             // will be (assuming the window is visible).
             attachInfo.mSurface = mSurface;
-            attachInfo.mTranslucentWindow = PixelFormat.formatHasAlpha(lp.format);
+            attachInfo.mUse32BitDrawingCache = PixelFormat.formatHasAlpha(lp.format) ||
+                    lp.format == PixelFormat.RGBX_8888;
             attachInfo.mHasWindowFocus = false;
             attachInfo.mWindowVisibility = viewVisibility;
             attachInfo.mRecomputeGlobalAttributes = false;
@@ -1301,6 +1302,7 @@
             // Need to make sure we re-evaluate the window attributes next
             // time around, to ensure the window has the correct format.
             mWindowAttributesChanged = true;
+            requestLayout();
         }
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index b30b6b1..41c9736 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3758,7 +3758,7 @@
         // - ExtractEditText does not call onFocus when it is displayed. Fixing this issue would
         //   allow to test for hasSelection in onFocusChanged, which would trigger a
         //   startTextSelectionMode here. TODO
-        if (selectionController != null && hasSelection()) {
+        if (this instanceof ExtractEditText && selectionController != null && hasSelection()) {
             startTextSelectionMode();
         }
 
@@ -4819,6 +4819,7 @@
     }
 
     public void beginBatchEdit() {
+        mInBatchEditControllers = true;
         final InputMethodState ims = mInputMethodState;
         if (ims != null) {
             int nesting = ++ims.mBatchEditNesting;
@@ -4841,6 +4842,7 @@
     }
     
     public void endBatchEdit() {
+        mInBatchEditControllers = false;
         final InputMethodState ims = mInputMethodState;
         if (ims != null) {
             int nesting = --ims.mBatchEditNesting;
@@ -6747,10 +6749,9 @@
                 // Restore previous selection
                 Selection.setSelection((Spannable)mText, prevStart, prevEnd);
 
-                if (mSelectionModifierCursorController != null &&
-                        !mSelectionModifierCursorController.isShowing()) {
+                if (hasSelectionController() && !getSelectionController().isShowing()) {
                     // If the anchors aren't showing, revive them.
-                    mSelectionModifierCursorController.show();
+                    getSelectionController().show();
                 } else {
                     // Tapping inside the selection displays the cut/copy/paste context menu
                     // as long as the anchors are already showing.
@@ -6761,12 +6762,12 @@
                 // Tapping outside stops selection mode, if any
                 stopTextSelectionMode();
 
-                if (mInsertionPointCursorController != null && mText.length() > 0) {
-                    mInsertionPointCursorController.show();
+                if (hasInsertionController() && mText.length() > 0) {
+                    getInsertionController().show();
                 }
             }
-        } else if (hasSelection() && mSelectionModifierCursorController != null) {
-            mSelectionModifierCursorController.show();
+        } else if (hasSelection() && hasSelectionController()) {
+            getSelectionController().show();
         }
     }
 
@@ -6800,11 +6801,11 @@
     public boolean onTouchEvent(MotionEvent event) {
         final int action = event.getActionMasked();
         if (action == MotionEvent.ACTION_DOWN) {
-            if (mInsertionPointCursorController != null) {
-                mInsertionPointCursorController.onTouchEvent(event);
+            if (hasInsertionController()) {
+                getInsertionController().onTouchEvent(event);
             }
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.onTouchEvent(event);
+            if (hasSelectionController()) {
+                getSelectionController().onTouchEvent(event);
             }
 
             // Reset this state; it will be re-set if super.onTouchEvent
@@ -6826,11 +6827,11 @@
         }
 
         if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && mLayout != null) {
-            if (mInsertionPointCursorController != null) {
-                mInsertionPointCursorController.onTouchEvent(event);
+            if (hasInsertionController()) {
+                getInsertionController().onTouchEvent(event);
             }
-            if (mSelectionModifierCursorController != null) {
-                mSelectionModifierCursorController.onTouchEvent(event);
+            if (hasSelectionController()) {
+                getSelectionController().onTouchEvent(event);
             }
 
             boolean handled = false;
@@ -6892,19 +6893,15 @@
         }
 
         // TODO Add an extra android:cursorController flag to disable the controller?
-        if (windowSupportsHandles && mCursorVisible && mLayout != null) {
-            if (mInsertionPointCursorController == null) {
-                mInsertionPointCursorController = new InsertionPointCursorController();
-            }
-        } else {
+        mInsertionControllerEnabled = windowSupportsHandles && mCursorVisible && mLayout != null;
+        mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() &&
+                mLayout != null;
+
+        if (!mInsertionControllerEnabled) {
             mInsertionPointCursorController = null;
         }
 
-        if (windowSupportsHandles && textCanBeSelected() && mLayout != null) {
-            if (mSelectionModifierCursorController == null) {
-                mSelectionModifierCursorController = new SelectionModifierCursorController();
-            }
-        } else {
+        if (!mSelectionControllerEnabled) {
             // Stop selection mode if the controller becomes unavailable.
             stopTextSelectionMode();
             mSelectionModifierCursorController = null;
@@ -7535,10 +7532,12 @@
             case ID_SELECT_ALL:
                 Selection.setSelection((Spannable) mText, 0, mText.length());
                 startTextSelectionMode();
+                getSelectionController().show();
                 return true;
 
             case ID_START_SELECTING_TEXT:
                 startTextSelectionMode();
+                getSelectionController().show();
                 return true;
 
             case ID_CUT:                
@@ -7648,7 +7647,7 @@
 
     private void startTextSelectionMode() {
         if (!mIsInTextSelectionMode) {
-            if (mSelectionModifierCursorController == null) {
+            if (!hasSelectionController()) {
                 Log.w(LOG_TAG, "TextView has no selection controller. Action mode cancelled.");
                 return;
             }
@@ -7658,7 +7657,6 @@
             }
 
             selectCurrentWord();
-            mSelectionModifierCursorController.show();
             mIsInTextSelectionMode = true;
         }
     }
@@ -7837,6 +7835,10 @@
                 return true;
             }
 
+            if (isInBatchEditMode()) {
+                return false;
+            }
+
             final int extendedPaddingTop = getExtendedPaddingTop();
             final int extendedPaddingBottom = getExtendedPaddingBottom();
             final int compoundPaddingLeft = getCompoundPaddingLeft();
@@ -7876,7 +7878,7 @@
             mPositionY = y - TextView.this.mScrollY;
             if (isPositionVisible()) {
                 int[] coords = null;
-                if (mContainer.isShowing()){
+                if (mContainer.isShowing()) {
                     coords = mTempCoords;
                     TextView.this.getLocationInWindow(coords);
                     mContainer.update(coords[0] + mPositionX, coords[1] + mPositionY,
@@ -8065,6 +8067,10 @@
         }
 
         public void show() {
+            if (isInBatchEditMode()) {
+                return;
+            }
+
             mIsShowing = true;
             updatePosition();
             mStartHandle.show();
@@ -8128,6 +8134,10 @@
         }
 
         public void updatePosition() {
+            if (!isShowing()) {
+                return;
+            }
+
             final int selectionStart = getSelectionStart();
             final int selectionEnd = getSelectionEnd();
 
@@ -8288,6 +8298,62 @@
         return getOffsetForHorizontal(line, x);
     }
 
+    /**
+     * @return True if this view supports insertion handles.
+     */
+    boolean hasInsertionController() {
+        return mInsertionControllerEnabled;
+    }
+
+    /**
+     * @return True if this view supports selection handles.
+     */
+    boolean hasSelectionController() {
+        return mSelectionControllerEnabled;
+    }
+
+    CursorController getInsertionController() {
+        if (!mInsertionControllerEnabled) {
+            return null;
+        }
+
+        if (mInsertionPointCursorController == null) {
+            mInsertionPointCursorController = new InsertionPointCursorController();
+
+            final ViewTreeObserver observer = getViewTreeObserver();
+            if (observer != null) {
+                observer.addOnTouchModeChangeListener(mInsertionPointCursorController);
+            }
+        }
+
+        return mInsertionPointCursorController;
+    }
+
+    CursorController getSelectionController() {
+        if (!mSelectionControllerEnabled) {
+            return null;
+        }
+
+        if (mSelectionModifierCursorController == null) {
+            mSelectionModifierCursorController = new SelectionModifierCursorController();
+
+            final ViewTreeObserver observer = getViewTreeObserver();
+            if (observer != null) {
+                observer.addOnTouchModeChangeListener(mSelectionModifierCursorController);
+            }
+        }
+
+        return mSelectionModifierCursorController;
+    }
+
+    boolean isInBatchEditMode() {
+        final InputMethodState ims = mInputMethodState;
+        if (ims != null) {
+            return ims.mBatchEditNesting > 0;
+        }
+        return mInBatchEditControllers;
+    }
+
     @ViewDebug.ExportedProperty
     private CharSequence            mText;
     private CharSequence            mTransformed;
@@ -8319,6 +8385,9 @@
     // Cursor Controllers. Null when disabled.
     private CursorController        mInsertionPointCursorController;
     private CursorController        mSelectionModifierCursorController;
+    private boolean                 mInsertionControllerEnabled;
+    private boolean                 mSelectionControllerEnabled;
+    private boolean                 mInBatchEditControllers;
     private boolean                 mIsInTextSelectionMode = false;
     // These are needed to desambiguate a long click. If the long click comes from ones of these, we
     // select from the current cursor position. Otherwise, select from long pressed position.
diff --git a/core/res/res/drawable-hdpi/textfield_pressed.9.png b/core/res/res/drawable-hdpi/textfield_pressed.9.png
old mode 100755
new mode 100644
index 296d3da..6537be0
--- a/core/res/res/drawable-hdpi/textfield_pressed.9.png
+++ b/core/res/res/drawable-hdpi/textfield_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/textfield_pressed.9.png b/core/res/res/drawable-mdpi/textfield_pressed.9.png
index 6d03e1e..9524fec 100644
--- a/core/res/res/drawable-mdpi/textfield_pressed.9.png
+++ b/core/res/res/drawable-mdpi/textfield_pressed.9.png
Binary files differ
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 2812ae8..3dda5ee 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -19,52 +19,15 @@
 
 @jd:body
 
+<p>Here's an overview of the steps you must follow to set up the Android SDK:</p>
 
-<h2 id="quickstart">Quick Start</h2>
+<ol>
+  <li>Prepare your development computer and ensure it meets the system requirements.</li>
+  <li>Install the SDK starter package from the table above.</li>
+  <li>Install the ADT Plugin for Eclipse (if you'll be developing in Eclipse).</li>
+  <li>Add Android platforms and other components to your SDK.</li>
+  <li>Explore the contents of the Android SDK (optional).</li>
+</ol>
 
-<p><strong>1. Prepare your development computer</strong></p>
-
-<p>Read the <a href="{@docRoot}sdk/requirements.html">System Requirements</a>
-document and make sure that your development computer meets the hardware and
-software requirements for the Android SDK. Install any additional software
-needed before downloading the Android SDK. In particular, you may need to
-install the <a href="http://java.sun.com/javase/downloads/index.jsp">JDK</a>
- (version 5 or 6 required) and <a href="http://www.eclipse.org/downloads/">Eclipse</a>
- (version 3.4 or 3.5, needed only if you want develop using the ADT Plugin).
-
-<p><strong>2. Download and install the SDK starter package</strong></p>
-
-<p>Download a starter package from the table above onto your development computer.
-If you're using Windows, we recommend that you download the installer (the {@code .exe} file),
-which will launch a Wizard to guide you through the installation and check your computer for
-required software. Otherwise, download the SDK starter package ({@code .zip} or {@code .tgz})
-appropriate for your system, unpack it to a safe location, then add the location to your PATH
-environment variable. </p>
-
-<p><strong>3. Install the ADT Plugin for Eclipse</strong></p>
-
-<p>If you are developing in Eclipse, add a new remote update site with the URL
-<code>https://dl-ssl.google.com/android/eclipse/</code>. Install the Android
-Development Tools (ADT) Plugin from that site, restart Eclipse, and set the "Android"
-preferences in Eclipse to point to the Android SDK directory (installed in the previous step). For
-detailed instructions to setup Eclipse, see <a href="{@docRoot}sdk/eclipse-adt.html">ADT Plugin
-for Eclipse</a>.</p>
-
-<p><strong>4. Add Android platforms and other components to your SDK</strong></p>
-
-<p>Launch the <em>Android SDK and AVD Manager</em> by executing {@code SDK Manager.exe} (Windows) or
-{@code android} (Mac/Linux) from the SDK's {@code tools/} directory (if you used the Windows
-installer, this is launched for you when the Wizard is complete). Add some Android platforms
-(such as Android 1.6 and Android 2.3) and other components (such as documentation) to your SDK. If
-you aren't sure what to add, see <a
-href="installing.html#which">Recommended Components</a></p>
-
-<p><strong>Done!</strong></p>
-
-<p>To write your first Android application, see the <a
-href="{@docRoot}resources/tutorials/hello-world.html">Hello World</a> tutorial. Also see <a
-href="{@docRoot}sdk/installing.html#NextSteps">Next
-Steps</a> for other suggestions about how to get started.</p>
-
-<p>For a more detailed guide to installing and setting up the SDK, read <a
+<p>To get started, download the appropriate package from the table above, then read the guide to <a
 href="installing.html">Installing the SDK</a>.</p>
diff --git a/docs/html/sdk/installing.jd b/docs/html/sdk/installing.jd
index 5b5c4f4..2f19181 100644
--- a/docs/html/sdk/installing.jd
+++ b/docs/html/sdk/installing.jd
@@ -58,7 +58,7 @@
       <ol>
         <li><a href="#which">Recommended Components</a></li>
       </ol></li>
-    <li><a href="#sdkContents">5. Exploring the SDK</a></li>
+    <li><a href="#sdkContents">5. Exploring the SDK (Optional)</a></li>
     <li><a href="#NextSteps">Next Steps</a></li>
     <li><a href="#troubleshooting">Troubleshooting</a></li>
   </ol>
@@ -112,7 +112,7 @@
 development environment&mdash;it includes only the core SDK Tools, which you can
 use to download the rest of the SDK components (such as the latest Android platform).</p>
 
-<p>You can get the latest version of the SDK starter package from the <a
+<p>If you haven't already, get the latest version of the SDK starter package from the <a
 href="{@docRoot}sdk/index.html">SDK download page</a>.</p>
 
 <p>If you downloaded a {@code .zip} or {@code .tgz} package (instead of the SDK installer), unpack
@@ -379,10 +379,10 @@
 href="{@docRoot}sdk/adding-components.html">Adding SDK Components</a> document. </p>
 
 
-<h2 id="sdkContents">Step 5. Exploring the SDK</h2>
+<h2 id="sdkContents">Step 5. Exploring the SDK (Optional)</h2>
 
 <p>Once you've installed the SDK and downloaded the platforms, documentation,
-and add-ons that you need, open the SDK directory and take a look at what's
+and add-ons that you need, we suggest that you open the SDK directory and take a look at what's
 inside.</p>
 
 <p>The table below describes the full SDK directory contents, with components
diff --git a/include/private/media/VideoFrame.h b/include/private/media/VideoFrame.h
index 9c35274ba..3aff0c6 100644
--- a/include/private/media/VideoFrame.h
+++ b/include/private/media/VideoFrame.h
@@ -120,6 +120,7 @@
     uint32_t mDisplayHeight;
     uint32_t mSize;            // Number of bytes in mData
     uint8_t* mData;            // Actual binary data
+    int32_t  mRotationAngle;   // rotation angle, clockwise
 };
 
 }; // namespace android
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 12db908..1994f6a 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -289,7 +289,7 @@
     void flushSpan() {
         bool merge = false;
         if (tail-head == ssize_t(span.size())) {
-            Rect const* p = cur;
+            Rect const* p = span.editArray();
             Rect const* q = head;
             if (p->top == q->bottom) {
                 merge = true;
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index efea802..63e9dc8 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -36,6 +36,7 @@
     jfieldID context;
     jclass bitmapClazz;
     jmethodID bitmapConstructor;
+    jmethodID createBitmapMethod;
 };
 
 static fields_t fields;
@@ -174,6 +175,41 @@
         return NULL;
     }
 
+    jobject matrix = NULL;
+    if (videoFrame->mRotationAngle != 0) {
+        LOGD("Create a rotation matrix: %d degrees", videoFrame->mRotationAngle);
+        jclass matrixClazz = env->FindClass("android/graphics/Matrix");
+        if (matrixClazz == NULL) {
+            jniThrowException(env, "java/lang/RuntimeException",
+                "Can't find android/graphics/Matrix");
+            return NULL;
+        }
+        jmethodID matrixConstructor =
+            env->GetMethodID(matrixClazz, "<init>", "()V");
+        if (matrixConstructor == NULL) {
+            jniThrowException(env, "java/lang/RuntimeException",
+                "Can't find Matrix constructor");
+            return NULL;
+        }
+        matrix =
+            env->NewObject(matrixClazz, matrixConstructor);
+        if (matrix == NULL) {
+            LOGE("Could not create a Matrix object");
+            return NULL;
+        }
+
+        LOGV("Rotate the matrix: %d degrees", videoFrame->mRotationAngle);
+        jmethodID setRotateMethod =
+                env->GetMethodID(matrixClazz, "setRotate", "(F)V");
+        if (setRotateMethod == NULL) {
+            jniThrowException(env, "java/lang/RuntimeException",
+                "Can't find Matrix setRotate method");
+            return NULL;
+        }
+        env->CallVoidMethod(matrix, setRotateMethod, 1.0 * videoFrame->mRotationAngle);
+        env->DeleteLocalRef(matrixClazz);
+    }
+
     // Create a SkBitmap to hold the pixels
     SkBitmap *bitmap = new SkBitmap();
     if (bitmap == NULL) {
@@ -191,7 +227,19 @@
     // Since internally SkBitmap uses reference count to manage the reference to
     // its pixels, it is important that the pixels (along with SkBitmap) be
     // available after creating the Bitmap is returned to Java app.
-    return env->NewObject(fields.bitmapClazz, fields.bitmapConstructor, (int) bitmap, true, NULL, -1);
+    jobject jSrcBitmap = env->NewObject(fields.bitmapClazz,
+            fields.bitmapConstructor, (int) bitmap, true, NULL, -1);
+
+    LOGV("Return a new bitmap constructed with the rotation matrix");
+    return env->CallStaticObjectMethod(
+                fields.bitmapClazz, fields.createBitmapMethod,
+                jSrcBitmap,                     // source Bitmap
+                0,                              // x
+                0,                              // y
+                videoFrame->mDisplayWidth,      // width
+                videoFrame->mDisplayHeight,     // height
+                matrix,                         // transform matrix
+                false);                         // filter
 }
 
 static jbyteArray android_media_MediaMetadataRetriever_extractAlbumArt(JNIEnv *env, jobject thiz)
@@ -291,6 +339,15 @@
         jniThrowException(env, "java/lang/RuntimeException", "Can't find Bitmap constructor");
         return;
     }
+    fields.createBitmapMethod =
+            env->GetStaticMethodID(fields.bitmapClazz, "createBitmap",
+                    "(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)"
+                    "Landroid/graphics/Bitmap;");
+    if (fields.createBitmapMethod == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "Can't find Bitmap.createBitmap method");
+        return;
+    }
 }
 
 static void android_media_MediaMetadataRetriever_native_setup(JNIEnv *env, jobject thiz)
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index ca229fa..39fce81 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -253,6 +253,8 @@
     frameCopy->mDisplayWidth = frame->mDisplayWidth;
     frameCopy->mDisplayHeight = frame->mDisplayHeight;
     frameCopy->mSize = frame->mSize;
+    frameCopy->mRotationAngle = frame->mRotationAngle;
+    LOGV("rotation: %d", frameCopy->mRotationAngle);
     frameCopy->mData = (uint8_t *)frameCopy + sizeof(VideoFrame);
     memcpy(frameCopy->mData, frame->mData, frame->mSize);
     delete frame;  // Fix memory leakage
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index a800a93..9b2dec9 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -191,6 +191,11 @@
     CHECK(meta->findInt32(kKeyWidth, &width));
     CHECK(meta->findInt32(kKeyHeight, &height));
 
+    int32_t rotationAngle;
+    if (!trackMeta->findInt32(kKeyRotation, &rotationAngle)) {
+        rotationAngle = 0;  // By default, no rotation
+    }
+
     VideoFrame *frame = new VideoFrame;
     frame->mWidth = width;
     frame->mHeight = height;
@@ -198,6 +203,7 @@
     frame->mDisplayHeight = height;
     frame->mSize = width * height * 2;
     frame->mData = new uint8_t[frame->mSize];
+    frame->mRotationAngle = rotationAngle;
 
     int32_t srcFormat;
     CHECK(meta->findInt32(kKeyColorFormat, &srcFormat));
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index e204e04..c9ab992 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -175,10 +175,11 @@
         for (size_t i=0 ; i<mSensorList.size() ; i++) {
             const Sensor& s(mSensorList[i]);
             const sensors_event_t& e(mLastEventSeen.valueFor(s.getHandle()));
-            snprintf(buffer, SIZE, "%s (vendor=%s, handle=%d, last=<%5.1f,%5.1f,%5.1f>)\n",
+            snprintf(buffer, SIZE, "%s (vendor=%s, handle=%d, maxRate=%.2fHz, last=<%5.1f,%5.1f,%5.1f>)\n",
                     s.getName().string(),
                     s.getVendor().string(),
                     s.getHandle(),
+                    s.getMinDelay() ? (1000000.0f / s.getMinDelay()) : 0.0f,
                     e.data[0], e.data[1], e.data[2]);
             result.append(buffer);
         }
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 9f37799..dfb1c0e 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -49,8 +49,8 @@
 {
    friend class BinderService<SensorService>;
 
-   static const nsecs_t MINIMUM_EVENTS_PERIOD = 10000000; // 10ms
-   static const nsecs_t DEFAULT_EVENTS_PERIOD = 200000000; // 200 ms
+   static const nsecs_t MINIMUM_EVENTS_PERIOD =   1000000; // 1000 Hz
+   static const nsecs_t DEFAULT_EVENTS_PERIOD = 200000000; //    5 Hz
 
             SensorService();
     virtual ~SensorService();
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index 42bf983..aea1062 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -27,15 +27,25 @@
     sp<SensorEventQueue> q((SensorEventQueue*)data);
     ssize_t n;
     ASensorEvent buffer[8];
+
+    static nsecs_t oldTimeStamp = 0;
+
     while ((n = q->read(buffer, 8)) > 0) {
         for (int i=0 ; i<n ; i++) {
-            if (buffer[i].type == Sensor::TYPE_ACCELEROMETER) {
+            if (buffer[i].type == Sensor::TYPE_GYROSCOPE) {
                 printf("time=%lld, value=<%5.1f,%5.1f,%5.1f>\n",
                         buffer[i].timestamp,
                         buffer[i].acceleration.x,
                         buffer[i].acceleration.y,
                         buffer[i].acceleration.z);
             }
+
+            if (oldTimeStamp) {
+                float t = float(buffer[i].timestamp - oldTimeStamp) / s2ns(1);
+                printf("%f ms (%f Hz)\n", t*1000, 1.0/t);
+            }
+            oldTimeStamp = buffer[i].timestamp;
+
         }
     }
     if (n<0 && n != -EAGAIN) {
@@ -56,7 +66,7 @@
     sp<SensorEventQueue> q = mgr.createEventQueue();
     printf("queue=%p\n", q.get());
 
-    Sensor const* accelerometer = mgr.getDefaultSensor(Sensor::TYPE_ACCELEROMETER);
+    Sensor const* accelerometer = mgr.getDefaultSensor(Sensor::TYPE_GYROSCOPE);
     printf("accelerometer=%p (%s)\n",
             accelerometer, accelerometer->getName().string());
     q->enableSensor(accelerometer);