Merge "Added app launch test." into jb-mr1.1-dev
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 521e686..85972c3 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -309,13 +309,15 @@
     }
 
     private void setErrorIcon(Drawable icon) {
-        final Drawables dr = mTextView.mDrawables;
-        if (dr != null) {
-            mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
-                    dr.mDrawableBottom);
-        } else {
-            mTextView.setCompoundDrawables(null, null, icon, null);
+        Drawables dr = mTextView.mDrawables;
+        if (dr == null) {
+            mTextView.mDrawables = dr = new Drawables();
         }
+        dr.setErrorDrawable(icon, mTextView);
+
+        mTextView.resetResolvedDrawables();
+        mTextView.invalidate();
+        mTextView.requestLayout();
     }
 
     private void hideError() {
@@ -329,7 +331,7 @@
     }
 
     /**
-     * Returns the Y offset to make the pointy top of the error point
+     * Returns the X offset to make the pointy top of the error point
      * at the middle of the error icon.
      */
     private int getErrorX() {
@@ -340,8 +342,23 @@
         final float scale = mTextView.getResources().getDisplayMetrics().density;
 
         final Drawables dr = mTextView.mDrawables;
-        return mTextView.getWidth() - mErrorPopup.getWidth() - mTextView.getPaddingRight() -
-                (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f);
+
+        final int layoutDirection = mTextView.getLayoutDirection();
+        int errorX;
+        int offset;
+        switch (layoutDirection) {
+            default:
+            case View.LAYOUT_DIRECTION_LTR:
+                offset = - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f);
+                errorX = mTextView.getWidth() - mErrorPopup.getWidth() -
+                        mTextView.getPaddingRight() + offset;
+                break;
+            case View.LAYOUT_DIRECTION_RTL:
+                offset = (dr != null ? dr.mDrawableSizeLeft : 0) / 2 - (int) (25 * scale + 0.5f);
+                errorX = mTextView.getPaddingLeft() + offset;
+                break;
+        }
+        return errorX;
     }
 
     /**
@@ -358,16 +375,27 @@
                 mTextView.getCompoundPaddingBottom() - compoundPaddingTop;
 
         final Drawables dr = mTextView.mDrawables;
-        int icontop = compoundPaddingTop +
-                (vspace - (dr != null ? dr.mDrawableHeightRight : 0)) / 2;
+
+        final int layoutDirection = mTextView.getLayoutDirection();
+        int height;
+        switch (layoutDirection) {
+            default:
+            case View.LAYOUT_DIRECTION_LTR:
+                height = (dr != null ? dr.mDrawableHeightRight : 0);
+                break;
+            case View.LAYOUT_DIRECTION_RTL:
+                height = (dr != null ? dr.mDrawableHeightLeft : 0);
+                break;
+        }
+
+        int icontop = compoundPaddingTop + (vspace - height) / 2;
 
         /*
          * The "2" is the distance between the point and the top edge
          * of the background.
          */
         final float scale = mTextView.getResources().getDisplayMetrics().density;
-        return icontop + (dr != null ? dr.mDrawableHeightRight : 0) - mTextView.getHeight() -
-                (int) (2 * scale + 0.5f);
+        return icontop + height - mTextView.getHeight() - (int) (2 * scale + 0.5f);
     }
 
     void createInputContentTypeIfNeeded() {
@@ -3726,7 +3754,7 @@
             super(v, width, height);
             mView = v;
             // Make sure the TextView has a background set as it will be used the first time it is
-            // shown and positionned. Initialized with below background, which should have
+            // shown and positioned. Initialized with below background, which should have
             // dimensions identical to the above version for this to work (and is more likely).
             mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
                     com.android.internal.R.styleable.Theme_errorMessageBackground);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 267b22a..0a16a66 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -284,15 +284,144 @@
     private TextUtils.TruncateAt mEllipsize;
 
     static class Drawables {
+        final static int DRAWABLE_NONE = -1;
+        final static int DRAWABLE_RIGHT = 0;
+        final static int DRAWABLE_LEFT = 1;
+
         final Rect mCompoundRect = new Rect();
+
         Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight,
-                mDrawableStart, mDrawableEnd;
+                mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp;
+
         int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight,
-                mDrawableSizeStart, mDrawableSizeEnd;
+                mDrawableSizeStart, mDrawableSizeEnd, mDrawableSizeError, mDrawableSizeTemp;
+
         int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight,
-                mDrawableHeightStart, mDrawableHeightEnd;
+                mDrawableHeightStart, mDrawableHeightEnd, mDrawableHeightError, mDrawableHeightTemp;
+
         int mDrawablePadding;
+
+        int mDrawableSaved = DRAWABLE_NONE;
+
+        public void resolveWithLayoutDirection(int layoutDirection) {
+            switch(layoutDirection) {
+                case LAYOUT_DIRECTION_RTL:
+                    if (mDrawableStart != null) {
+                        mDrawableRight = mDrawableStart;
+
+                        mDrawableSizeRight = mDrawableSizeStart;
+                        mDrawableHeightRight = mDrawableHeightStart;
+                    }
+                    if (mDrawableEnd != null) {
+                        mDrawableLeft = mDrawableEnd;
+
+                        mDrawableSizeLeft = mDrawableSizeEnd;
+                        mDrawableHeightLeft = mDrawableHeightEnd;
+                    }
+                    break;
+
+                case LAYOUT_DIRECTION_LTR:
+                default:
+                    if (mDrawableStart != null) {
+                        mDrawableLeft = mDrawableStart;
+
+                        mDrawableSizeLeft = mDrawableSizeStart;
+                        mDrawableHeightLeft = mDrawableHeightStart;
+                    }
+                    if (mDrawableEnd != null) {
+                        mDrawableRight = mDrawableEnd;
+
+                        mDrawableSizeRight = mDrawableSizeEnd;
+                        mDrawableHeightRight = mDrawableHeightEnd;
+                    }
+                    break;
+            }
+            applyErrorDrawableIfNeeded(layoutDirection);
+            updateDrawablesLayoutDirection(layoutDirection);
+        }
+
+        private void updateDrawablesLayoutDirection(int layoutDirection) {
+            if (mDrawableLeft != null) {
+                mDrawableLeft.setLayoutDirection(layoutDirection);
+            }
+            if (mDrawableRight != null) {
+                mDrawableRight.setLayoutDirection(layoutDirection);
+            }
+            if (mDrawableTop != null) {
+                mDrawableTop.setLayoutDirection(layoutDirection);
+            }
+            if (mDrawableBottom != null) {
+                mDrawableBottom.setLayoutDirection(layoutDirection);
+            }
+        }
+
+        public void setErrorDrawable(Drawable dr, TextView tv) {
+            if (mDrawableError != dr && mDrawableError != null) {
+                mDrawableError.setCallback(null);
+            }
+            mDrawableError = dr;
+
+            final Rect compoundRect = mCompoundRect;
+            int[] state = tv.getDrawableState();
+
+            if (mDrawableError != null) {
+                mDrawableError.setState(state);
+                mDrawableError.copyBounds(compoundRect);
+                mDrawableError.setCallback(tv);
+                mDrawableSizeError = compoundRect.width();
+                mDrawableHeightError = compoundRect.height();
+            } else {
+                mDrawableSizeError = mDrawableHeightError = 0;
+            }
+        }
+
+        private void applyErrorDrawableIfNeeded(int layoutDirection) {
+            // first restore the initial state if needed
+            switch (mDrawableSaved) {
+                case DRAWABLE_LEFT:
+                    mDrawableLeft = mDrawableTemp;
+                    mDrawableSizeLeft = mDrawableSizeTemp;
+                    mDrawableHeightLeft = mDrawableHeightTemp;
+                    break;
+                case DRAWABLE_RIGHT:
+                    mDrawableRight = mDrawableTemp;
+                    mDrawableSizeRight = mDrawableSizeTemp;
+                    mDrawableHeightRight = mDrawableHeightTemp;
+                    break;
+                case DRAWABLE_NONE:
+                default:
+            }
+            // then, if needed, assign the Error drawable to the correct location
+            if (mDrawableError != null) {
+                switch(layoutDirection) {
+                    case LAYOUT_DIRECTION_RTL:
+                        mDrawableSaved = DRAWABLE_LEFT;
+
+                        mDrawableTemp = mDrawableLeft;
+                        mDrawableSizeTemp = mDrawableSizeLeft;
+                        mDrawableHeightTemp = mDrawableHeightLeft;
+
+                        mDrawableLeft = mDrawableError;
+                        mDrawableSizeLeft = mDrawableSizeError;
+                        mDrawableHeightLeft = mDrawableHeightError;
+                        break;
+                    case LAYOUT_DIRECTION_LTR:
+                    default:
+                        mDrawableSaved = DRAWABLE_RIGHT;
+
+                        mDrawableTemp = mDrawableRight;
+                        mDrawableSizeTemp = mDrawableSizeRight;
+                        mDrawableHeightTemp = mDrawableHeightRight;
+
+                        mDrawableRight = mDrawableError;
+                        mDrawableSizeRight = mDrawableSizeError;
+                        mDrawableHeightRight = mDrawableHeightError;
+                        break;
+                }
+            }
+        }
     }
+
     Drawables mDrawables;
 
     private CharWrapper mCharWrapper;
@@ -8263,63 +8392,10 @@
             return;
         }
         mLastLayoutDirection = layoutDirection;
-        // No drawable to resolve
-        if (mDrawables == null) {
-            return;
-        }
-        // No relative drawable to resolve
-        if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) {
-            return;
-        }
 
-        Drawables dr = mDrawables;
-        switch(layoutDirection) {
-            case LAYOUT_DIRECTION_RTL:
-                if (dr.mDrawableStart != null) {
-                    dr.mDrawableRight = dr.mDrawableStart;
-
-                    dr.mDrawableSizeRight = dr.mDrawableSizeStart;
-                    dr.mDrawableHeightRight = dr.mDrawableHeightStart;
-                }
-                if (dr.mDrawableEnd != null) {
-                    dr.mDrawableLeft = dr.mDrawableEnd;
-
-                    dr.mDrawableSizeLeft = dr.mDrawableSizeEnd;
-                    dr.mDrawableHeightLeft = dr.mDrawableHeightEnd;
-                }
-                break;
-
-            case LAYOUT_DIRECTION_LTR:
-            default:
-                if (dr.mDrawableStart != null) {
-                    dr.mDrawableLeft = dr.mDrawableStart;
-
-                    dr.mDrawableSizeLeft = dr.mDrawableSizeStart;
-                    dr.mDrawableHeightLeft = dr.mDrawableHeightStart;
-                }
-                if (dr.mDrawableEnd != null) {
-                    dr.mDrawableRight = dr.mDrawableEnd;
-
-                    dr.mDrawableSizeRight = dr.mDrawableSizeEnd;
-                    dr.mDrawableHeightRight = dr.mDrawableHeightEnd;
-                }
-                break;
-        }
-        updateDrawablesLayoutDirection(dr, layoutDirection);
-    }
-
-    private void updateDrawablesLayoutDirection(Drawables dr, int layoutDirection) {
-        if (dr.mDrawableLeft != null) {
-            dr.mDrawableLeft.setLayoutDirection(layoutDirection);
-        }
-        if (dr.mDrawableRight != null) {
-            dr.mDrawableRight.setLayoutDirection(layoutDirection);
-        }
-        if (dr.mDrawableTop != null) {
-            dr.mDrawableTop.setLayoutDirection(layoutDirection);
-        }
-        if (dr.mDrawableBottom != null) {
-            dr.mDrawableBottom.setLayoutDirection(layoutDirection);
+        // Resolve drawables
+        if (mDrawables != null) {
+            mDrawables.resolveWithLayoutDirection(layoutDirection);
         }
     }
 
@@ -8327,6 +8403,7 @@
      * @hide
      */
     protected void resetResolvedDrawables() {
+        super.resetResolvedDrawables();
         mLastLayoutDirection = -1;
     }
 
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error.9.png
new file mode 100644
index 0000000..8b43f4e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above.9.png
new file mode 100644
index 0000000..20e9002
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_dark.9.png
new file mode 100644
index 0000000..b5f397c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_light.9.png
new file mode 100644
index 0000000..a04d695
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_dark.9.png
new file mode 100644
index 0000000..8567b1f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_light.9.png
new file mode 100644
index 0000000..7d1754c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/popup_inline_error_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error.9.png
new file mode 100644
index 0000000..d2efb62
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-ldpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error_above.9.png
new file mode 100644
index 0000000..04d200d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-ldpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error.9.png
new file mode 100644
index 0000000..27e8d4f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above.9.png
new file mode 100644
index 0000000..4ae2b91
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_dark.9.png
new file mode 100644
index 0000000..8cc3b69
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_light.9.png
new file mode 100644
index 0000000..7a84200
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_dark.9.png
new file mode 100644
index 0000000..8fc2e2e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_light.9.png
new file mode 100644
index 0000000..687a691
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/popup_inline_error_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error.9.png
new file mode 100644
index 0000000..db91a56
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above.9.png
new file mode 100644
index 0000000..90820b5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_dark.9.png
new file mode 100644
index 0000000..5989975
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_light.9.png
new file mode 100644
index 0000000..3b3f87d3
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_above_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_dark.9.png
new file mode 100644
index 0000000..75baba2
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_light.9.png
new file mode 100644
index 0000000..6c0203d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/popup_inline_error_holo_light.9.png
Binary files differ
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 22f699f..56abed4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -5088,18 +5088,23 @@
         // top of the stack for the media button event receivers : simply using the top of the
         // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
         // notifications playing during music playback.
-        // crawl the AudioFocus stack until an entry is found with the following characteristics:
+        // Crawl the AudioFocus stack from the top until an entry is found with the following
+        // characteristics:
         // - focus gain on STREAM_MUSIC stream
         // - non-transient focus gain on a stream other than music
         FocusStackEntry af = null;
-        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
-        while(stackIterator.hasNext()) {
-            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
-            if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
-                    || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
-                af = fse;
-                break;
+        try {
+            for (int index = mFocusStack.size()-1; index >= 0; index--) {
+                FocusStackEntry fse = mFocusStack.elementAt(index);
+                if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
+                        || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
+                    af = fse;
+                    break;
+                }
             }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
+            af = null;
         }
         if (af == null) {
             clearRemoteControlDisplay_syncAfRcs();
@@ -5120,6 +5125,7 @@
             clearRemoteControlDisplay_syncAfRcs();
             return;
         }
+
         // refresh conditions were verified: update the remote controls
         // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
         updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
index dbd9999..762711d 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/CameraWidgetFrame.java
@@ -18,10 +18,9 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.Color;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.Log;
@@ -53,17 +52,20 @@
     private final Handler mHandler = new Handler();
     private final KeyguardActivityLauncher mActivityLauncher;
     private final Callbacks mCallbacks;
+    private final CameraWidgetInfo mWidgetInfo;
     private final WindowManager mWindowManager;
     private final Point mRenderedSize = new Point();
-    private final int[] mScreenLocation = new int[2];
+    private final int[] mTmpLoc = new int[2];
+    private final Rect mTmpRect = new Rect();
 
-    private View mWidgetView;
     private long mLaunchCameraStart;
     private boolean mActive;
     private boolean mTransitioning;
-    private boolean mRecovering;
     private boolean mDown;
 
+    private FixedSizeFrameLayout mPreview;
+    private View mFullscreenPreview;
+
     private final Runnable mTransitionToCameraRunnable = new Runnable() {
         @Override
         public void run() {
@@ -81,21 +83,18 @@
             mActivityLauncher.launchCamera(worker, mSecureCameraActivityStartedRunnable);
         }};
 
+    private final Runnable mPostTransitionToCameraEndAction = new Runnable() {
+        @Override
+        public void run() {
+            mHandler.post(mTransitionToCameraEndAction);
+        }};
+
     private final Runnable mRecoverRunnable = new Runnable() {
         @Override
         public void run() {
             recover();
         }};
 
-    private final Runnable mRecoverEndAction = new Runnable() {
-        @Override
-        public void run() {
-            if (!mRecovering)
-                return;
-            mCallbacks.onCameraLaunchedUnsuccessfully();
-            reset();
-        }};
-
     private final Runnable mRenderRunnable = new Runnable() {
         @Override
         public void run() {
@@ -119,13 +118,43 @@
         };
     };
 
+    private static final class FixedSizeFrameLayout extends FrameLayout {
+        int width;
+        int height;
+
+        FixedSizeFrameLayout(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            measureChildren(
+                    MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+            setMeasuredDimension(width, height);
+        }
+    }
+
     private CameraWidgetFrame(Context context, Callbacks callbacks,
-            KeyguardActivityLauncher activityLauncher) {
+            KeyguardActivityLauncher activityLauncher,
+            CameraWidgetInfo widgetInfo, View previewWidget) {
         super(context);
         mCallbacks = callbacks;
         mActivityLauncher = activityLauncher;
+        mWidgetInfo = widgetInfo;
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         KeyguardUpdateMonitor.getInstance(context).registerCallback(mCallback);
+
+        mPreview = new FixedSizeFrameLayout(context);
+        mPreview.addView(previewWidget);
+        addView(mPreview);
+
+        View clickBlocker = new View(context);
+        clickBlocker.setBackgroundColor(Color.TRANSPARENT);
+        clickBlocker.setOnClickListener(this);
+        addView(clickBlocker);
+
+        setContentDescription(context.getString(R.string.keyguard_accessibility_camera));
         if (DEBUG) Log.d(TAG, "new CameraWidgetFrame instance " + instanceId());
     }
 
@@ -137,24 +166,17 @@
         CameraWidgetInfo widgetInfo = launcher.getCameraWidgetInfo();
         if (widgetInfo == null)
             return null;
-        View widgetView = widgetInfo.layoutId > 0 ?
-                inflateWidgetView(context, widgetInfo) :
-                inflateGenericWidgetView(context);
-        if (widgetView == null)
+        View previewWidget = getPreviewWidget(context, widgetInfo);
+        if (previewWidget == null)
             return null;
 
-        ImageView preview = new ImageView(context);
-        preview.setLayoutParams(new FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.MATCH_PARENT,
-                FrameLayout.LayoutParams.MATCH_PARENT));
-        preview.setScaleType(ScaleType.FIT_CENTER);
-        preview.setContentDescription(preview.getContext().getString(
-                R.string.keyguard_accessibility_camera));
-        CameraWidgetFrame cameraWidgetFrame = new CameraWidgetFrame(context, callbacks, launcher);
-        cameraWidgetFrame.addView(preview);
-        cameraWidgetFrame.mWidgetView = widgetView;
-        preview.setOnClickListener(cameraWidgetFrame);
-        return cameraWidgetFrame;
+        return new CameraWidgetFrame(context, callbacks, launcher, widgetInfo, previewWidget);
+    }
+
+    private static View getPreviewWidget(Context context, CameraWidgetInfo widgetInfo) {
+        return widgetInfo.layoutId > 0 ?
+                inflateWidgetView(context, widgetInfo) :
+                inflateGenericWidgetView(context);
     }
 
     private static View inflateWidgetView(Context context, CameraWidgetInfo widgetInfo) {
@@ -188,63 +210,45 @@
         return iv;
     }
 
-    public void render() {
-        final Throwable[] thrown = new Throwable[1];
-        final Bitmap[] offscreen = new Bitmap[1];
-        try {
-            final int width = getRootView().getWidth();
-            final int height = getRootView().getHeight();
-            if (mRenderedSize.x == width && mRenderedSize.y == height) {
-                if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s",
-                        width, height));
-                return;
-            }
-            if (width == 0 || height == 0) {
-                return;
-            }
-            final long start = SystemClock.uptimeMillis();
-            offscreen[0] = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-            final Canvas c = new Canvas(offscreen[0]);
-            mWidgetView.measure(
-                    MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
-                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
-            mWidgetView.layout(0, 0, width, height);
-            mWidgetView.draw(c);
-
-            final long end = SystemClock.uptimeMillis();
-            if (DEBUG) Log.d(TAG, String.format(
-                    "Rendered camera widget in %sms size=%sx%s instance=%s at %s",
-                    end - start,
-                    width, height,
-                    instanceId(),
-                    end));
-            mRenderedSize.set(width, height);
-        } catch (Throwable t) {
-            thrown[0] = t;
+    private void render() {
+        final View root = getRootView();
+        final int width = root.getWidth();
+        final int height = root.getHeight();
+        if (mRenderedSize.x == width && mRenderedSize.y == height) {
+            if (DEBUG) Log.d(TAG, String.format("Already rendered at size=%sx%s", width, height));
+            return;
+        }
+        if (width == 0 || height == 0) {
+            return;
         }
 
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                if (thrown[0] == null) {
-                    try {
-                        ((ImageView) getChildAt(0)).setImageBitmap(offscreen[0]);
-                    } catch (Throwable t) {
-                        thrown[0] = t;
-                    }
-                }
-                if (thrown[0] == null)
-                    return;
+        mPreview.width = width;
+        mPreview.height = height;
+        mPreview.requestLayout();
 
-                Log.w(TAG, "Error rendering camera widget", thrown[0]);
-                try {
-                    removeAllViews();
-                    final View genericView = inflateGenericWidgetView(mContext);
-                    addView(genericView);
-                } catch (Throwable t) {
-                    Log.w(TAG, "Error inflating generic camera widget", t);
-                }
-            }});
+        final int thisWidth = getWidth() - getPaddingLeft() - getPaddingRight();
+        final int thisHeight = getHeight() - getPaddingTop() - getPaddingBottom();
+
+        final float pvScaleX = (float) thisWidth / width;
+        final float pvScaleY = (float) thisHeight / height;
+        final float pvScale = Math.min(pvScaleX, pvScaleY);
+
+        final int pvWidth = (int) (pvScale * width);
+        final int pvHeight = (int) (pvScale * height);
+
+        final float pvTransX = pvWidth < thisWidth ? (thisWidth - pvWidth) / 2 : 0;
+        final float pvTransY = pvHeight < thisHeight ? (thisHeight - pvHeight) / 2 : 0;
+
+        mPreview.setPivotX(0);
+        mPreview.setPivotY(0);
+        mPreview.setScaleX(pvScale);
+        mPreview.setScaleY(pvScale);
+        mPreview.setTranslationX(pvTransX);
+        mPreview.setTranslationY(pvTransY);
+
+        mRenderedSize.set(width, height);
+        if (DEBUG) Log.d(TAG, String.format("Rendered camera widget size=%sx%s instance=%s",
+                width, height, instanceId()));
     }
 
     private void transitionToCamera() {
@@ -252,55 +256,53 @@
 
         mTransitioning = true;
 
-        final View child = getChildAt(0);
-        final View root = getRootView();
-
-        final int startWidth = child.getWidth();
-        final int startHeight = child.getHeight();
-
-        final int finishWidth = root.getWidth();
-        final int finishHeight = root.getHeight();
-
-        final float scaleX = (float) finishWidth / startWidth;
-        final float scaleY = (float) finishHeight / startHeight;
-        final float scale = Math.round( Math.max(scaleX, scaleY) * 100) / 100f;
-
-        final int[] loc = new int[2];
-        root.getLocationInWindow(loc);
-        final int finishCenter = loc[1] + finishHeight / 2;
-
-        child.getLocationInWindow(loc);
-        final int startCenter = loc[1] + startHeight / 2;
-
-        if (DEBUG) Log.d(TAG, String.format("Transitioning to camera. " +
-                "(start=%sx%s, finish=%sx%s, scale=%s,%s, startCenter=%s, finishCenter=%s)",
-                startWidth, startHeight,
-                finishWidth, finishHeight,
-                scaleX, scaleY,
-                startCenter, finishCenter));
-
         enableWindowExitAnimation(false);
-        animate()
-            .scaleX(scale)
-            .scaleY(scale)
-            .translationY(finishCenter - startCenter)
-            .setDuration(WIDGET_ANIMATION_DURATION)
-            .withEndAction(mTransitionToCameraEndAction)
-            .start();
 
+        mPreview.getLocationInWindow(mTmpLoc);
+        final float pvHeight = mPreview.getHeight() * mPreview.getScaleY();
+        final float pvCenter = mTmpLoc[1] + pvHeight / 2f;
+
+        final ViewGroup root = (ViewGroup) getRootView();
+        if (mFullscreenPreview == null) {
+            mFullscreenPreview = getPreviewWidget(mContext, mWidgetInfo);
+            mFullscreenPreview.setClickable(false);
+            root.addView(mFullscreenPreview);
+        }
+
+        root.getWindowVisibleDisplayFrame(mTmpRect);
+        final float fsHeight = mTmpRect.height();
+        final float fsCenter = mTmpRect.top + fsHeight / 2;
+
+        final float fsScaleY = pvHeight / fsHeight;
+        final float fsTransY = pvCenter - fsCenter;
+        final float fsScaleX = mPreview.getScaleX();
+
+        mPreview.setVisibility(View.GONE);
+        mFullscreenPreview.setVisibility(View.VISIBLE);
+        mFullscreenPreview.setTranslationY(fsTransY);
+        mFullscreenPreview.setScaleX(fsScaleX);
+        mFullscreenPreview.setScaleY(fsScaleY);
+        mFullscreenPreview
+            .animate()
+            .scaleX(1)
+            .scaleY(1)
+            .translationX(0)
+            .translationY(0)
+            .setDuration(WIDGET_ANIMATION_DURATION)
+            .withEndAction(mPostTransitionToCameraEndAction)
+            .start();
         mCallbacks.onLaunchingCamera();
     }
 
     private void recover() {
         if (DEBUG) Log.d(TAG, "recovering at " + SystemClock.uptimeMillis());
-        mRecovering = true;
-        animate()
-            .scaleX(1)
-            .scaleY(1)
-            .translationY(0)
-            .setDuration(WIDGET_ANIMATION_DURATION)
-            .withEndAction(mRecoverEndAction)
-            .start();
+        mCallbacks.onCameraLaunchedUnsuccessfully();
+        reset();
+    }
+
+    @Override
+    public void setOnLongClickListener(OnLongClickListener l) {
+        // ignore
     }
 
     @Override
@@ -340,8 +342,8 @@
             return true;
         }
 
-        getLocationOnScreen(mScreenLocation);
-        int rawBottom = mScreenLocation[1] + getHeight();
+        getLocationOnScreen(mTmpLoc);
+        int rawBottom = mTmpLoc[1] + getHeight();
         if (event.getRawY() > rawBottom) {
             if (DEBUG) Log.d(TAG, "onUserInteraction eaten: below widget");
             return true;
@@ -388,14 +390,14 @@
         if (DEBUG) Log.d(TAG, "reset at " + SystemClock.uptimeMillis());
         mLaunchCameraStart = 0;
         mTransitioning = false;
-        mRecovering = false;
         mDown = false;
         cancelTransitionToCamera();
         mHandler.removeCallbacks(mRecoverRunnable);
-        animate().cancel();
-        setScaleX(1);
-        setScaleY(1);
-        setTranslationY(0);
+        mPreview.setVisibility(View.VISIBLE);
+        if (mFullscreenPreview != null) {
+            mFullscreenPreview.animate().cancel();
+            mFullscreenPreview.setVisibility(View.GONE);
+        }
         enableWindowExitAnimation(true);
     }
 
@@ -403,11 +405,18 @@
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         if (DEBUG) Log.d(TAG, String.format("onSizeChanged new=%sx%s old=%sx%s at %s",
                 w, h, oldw, oldh, SystemClock.uptimeMillis()));
-        final Handler worker =  getWorkerHandler();
-        (worker != null ? worker : mHandler).post(mRenderRunnable);
+        mHandler.post(mRenderRunnable);
         super.onSizeChanged(w, h, oldw, oldh);
     }
 
+    @Override
+    public void onBouncerShowing(boolean showing) {
+        if (showing) {
+            mTransitioning = false;
+            mHandler.post(mRecoverRunnable);
+        }
+    }
+
     private void enableWindowExitAnimation(boolean isEnabled) {
         View root = getRootView();
         ViewGroup.LayoutParams lp = root.getLayoutParams();
@@ -427,15 +436,14 @@
         if (DEBUG) Log.d(TAG, "onKeyguardVisibilityChanged " + showing
                 + " at " + SystemClock.uptimeMillis());
         if (mTransitioning && !showing) {
-          mTransitioning = false;
-          mRecovering = false;
-          mHandler.removeCallbacks(mRecoverRunnable);
-          if (mLaunchCameraStart > 0) {
-              long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
-              if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
-              mLaunchCameraStart = 0;
-              onCameraLaunched();
-          }
+            mTransitioning = false;
+            mHandler.removeCallbacks(mRecoverRunnable);
+            if (mLaunchCameraStart > 0) {
+                long launchTime = SystemClock.uptimeMillis() - mLaunchCameraStart;
+                if (DEBUG) Log.d(TAG, String.format("Camera took %sms to launch", launchTime));
+                mLaunchCameraStart = 0;
+                onCameraLaunched();
+            }
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
index 3c79206..53b149c4 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetFrame.java
@@ -512,6 +512,10 @@
         return false;
     }
 
+    public void onBouncerShowing(boolean showing) {
+        // hook for subclasses
+    }
+
     public void setWorkerHandler(Handler workerHandler) {
         mWorkerHandler = workerHandler;
     }
@@ -519,4 +523,5 @@
     public Handler getWorkerHandler() {
         return mWorkerHandler;
     }
+
 }
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
index 85b5472..f62af8e4 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardWidgetPager.java
@@ -775,6 +775,9 @@
             mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
             mZoomInOutAnim.start();
         }
+        if (currentPage instanceof KeyguardWidgetFrame) {
+            ((KeyguardWidgetFrame)currentPage).onBouncerShowing(false);
+        }
     }
 
     // Zoom out after the bouncer is initiated
@@ -800,6 +803,9 @@
             mZoomInOutAnim.setInterpolator(new DecelerateInterpolator(1.5f));
             mZoomInOutAnim.start();
         }
+        if (currentPage instanceof KeyguardWidgetFrame) {
+            ((KeyguardWidgetFrame)currentPage).onBouncerShowing(true);
+        }
     }
 
     boolean isAddPage(int pageIndex) {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index da6f1fa..c33fc71 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -386,6 +386,7 @@
     private Locale mLastSystemLocale;
     private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor();
     private final IPackageManager mIPackageManager;
+    private boolean mInputBoundToKeyguard;
 
     class SettingsObserver extends ContentObserver {
         SettingsObserver(Handler handler) {
@@ -877,10 +878,12 @@
         final boolean hardKeyShown = haveHardKeyboard
                 && conf.hardKeyboardHidden
                         != Configuration.HARDKEYBOARDHIDDEN_YES;
-        final boolean isScreenLocked = mKeyguardManager != null
-                && mKeyguardManager.isKeyguardLocked()
-                && mKeyguardManager.isKeyguardSecure();
-        mImeWindowVis = (!isScreenLocked && (mInputShown || hardKeyShown)) ?
+        final boolean isScreenLocked =
+                mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+        final boolean isScreenSecurelyLocked =
+                isScreenLocked && mKeyguardManager.isKeyguardSecure();
+        final boolean inputShown = mInputShown && (!isScreenLocked || mInputBoundToKeyguard);
+        mImeWindowVis = (!isScreenSecurelyLocked && (inputShown || hardKeyShown)) ?
                 (InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE) : 0;
         updateImeWindowStatusLocked();
     }
@@ -1124,6 +1127,13 @@
             return mNoBinding;
         }
 
+        if (mCurClient == null) {
+            mInputBoundToKeyguard = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+            if (DEBUG) {
+                Slog.v(TAG, "New bind. keyguard = " +  mInputBoundToKeyguard);
+            }
+        }
+
         if (mCurClient != cs) {
             // If the client is changing, we need to switch over to the new
             // one.
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index fa84f486..e2be577 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -1128,13 +1128,13 @@
                 final boolean hasCustomVibrate = notification.vibrate != null;
 
                 // new in 4.2: if there was supposed to be a sound and we're in vibrate mode,
-                // and no other vibration is specified, we apply the default vibration anyway
+                // and no other vibration is specified, we fall back to vibration
                 final boolean convertSoundToVibration =
                            !hasCustomVibrate
                         && hasValidSound
                         && (audioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE);
 
-                // The DEFAULT_VIBRATE flag trumps any custom vibration.
+                // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback.
                 final boolean useDefaultVibrate =
                         (notification.defaults & Notification.DEFAULT_VIBRATE) != 0;
 
@@ -1147,8 +1147,8 @@
                         // does not have the VIBRATE permission.
                         long identity = Binder.clearCallingIdentity();
                         try {
-                            mVibrator.vibrate(convertSoundToVibration ? mFallbackVibrationPattern
-                                                                      : mDefaultVibrationPattern,
+                            mVibrator.vibrate(useDefaultVibrate ? mDefaultVibrationPattern
+                                                                : mFallbackVibrationPattern,
                                 ((notification.flags & Notification.FLAG_INSISTENT) != 0) ? 0: -1);
                         } finally {
                             Binder.restoreCallingIdentity(identity);
diff --git a/tests/MemoryUsage/Android.mk b/tests/MemoryUsage/Android.mk
index e7bfb4f..0ab793b 100644
--- a/tests/MemoryUsage/Android.mk
+++ b/tests/MemoryUsage/Android.mk
@@ -8,7 +8,8 @@
 
 LOCAL_PACKAGE_NAME := MemoryUsage
 
-LOCAL_SDK_VERSION := 7
+LOCAL_CERTIFICATE := platform
+LOCAL_JAVA_LIBRARIES := android.test.runner
 
 include $(BUILD_PACKAGE)
 
diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
index 5e27ba7..b550957 100644
--- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
+++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java
@@ -18,14 +18,17 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.ProcessErrorStateInfo;
 import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.os.Bundle;
 import android.os.Debug.MemoryInfo;
+import android.os.RemoteException;
+import android.os.UserHandle;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 
@@ -48,8 +51,9 @@
 
     private static final int SLEEP_TIME = 1000;
     private static final int THRESHOLD = 1024;
-    private static final int MAX_ITERATIONS = 10;
-    private static final int MIN_ITERATIONS = 4;
+    private static final int MAX_ITERATIONS = 20;
+    private static final int MIN_ITERATIONS = 6;
+    private static final int JOIN_TIMEOUT = 10000;
 
     private static final String TAG = "MemoryUsageInstrumentation";
     private static final String KEY_APPS = "apps";
@@ -58,10 +62,13 @@
     private Map<String, String> mNameToProcess;
     private Map<String, String> mNameToResultKey;
 
+    private IActivityManager mAm;
+
     public void testMemory() {
         MemoryUsageInstrumentation instrumentation =
-                    (MemoryUsageInstrumentation) getInstrumentation();
+                (MemoryUsageInstrumentation) getInstrumentation();
         Bundle args = instrumentation.getBundle();
+        mAm = ActivityManagerNative.getDefault();
 
         createMappings();
         parseArgs(args);
@@ -136,7 +143,16 @@
 
         String process = mNameToProcess.get(appName);
         Intent startIntent = mNameToIntent.get(appName);
-        getInstrumentation().getContext().startActivity(startIntent);
+
+        AppLaunchRunnable runnable = new AppLaunchRunnable(startIntent);
+        Thread t = new Thread(runnable);
+        t.start();
+        try {
+            t.join(JOIN_TIMEOUT);
+        } catch (InterruptedException e) {
+            // ignore
+        }
+
         return process;
     }
 
@@ -234,7 +250,7 @@
             }
 
             int[] pids = {
-                proc.pid };
+                    proc.pid };
 
             MemoryInfo meminfo = am.getProcessMemoryInfo(pids)[0];
             return meminfo.getTotalPss();
@@ -242,4 +258,29 @@
         }
         return -1;
     }
+
+    private class AppLaunchRunnable implements Runnable {
+        private Intent mLaunchIntent;
+
+        public AppLaunchRunnable(Intent intent) {
+            mLaunchIntent = intent;
+        }
+
+        public void run() {
+            try {
+                String mimeType = mLaunchIntent.getType();
+                if (mimeType == null && mLaunchIntent.getData() != null
+                        && "content".equals(mLaunchIntent.getData().getScheme())) {
+                    mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(),
+                            UserHandle.USER_CURRENT);
+                }
+
+                mAm.startActivityAndWait(null, mLaunchIntent, mimeType,
+                        null, null, 0, mLaunchIntent.getFlags(), null, null, null,
+                        UserHandle.USER_CURRENT_OR_SELF);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Error launching app", e);
+            }
+        }
+    }
 }