Merge "Preload libchromium_net in addition to libwebcore."
diff --git a/api/current.txt b/api/current.txt
index eb79f30..4f50bf3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -8648,6 +8648,7 @@
     method public void setEmpty();
     method public boolean setIntersect(android.graphics.RectF, android.graphics.RectF);
     method public void sort();
+    method public java.lang.String toShortString();
     method public void union(float, float, float, float);
     method public void union(android.graphics.RectF);
     method public void union(float, float);
@@ -10680,8 +10681,8 @@
     method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener);
     method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
     method public void setScreenOnWhilePlaying(boolean);
-    method public void setTexture(android.graphics.SurfaceTexture);
     method public void setSurface(android.view.Surface);
+    method public void setTexture(android.graphics.SurfaceTexture);
     method public void setVolume(float, float);
     method public void setWakeMode(android.content.Context, int);
     method public void start() throws java.lang.IllegalStateException;
@@ -25786,6 +25787,17 @@
     ctor public DigitalClock(android.content.Context, android.util.AttributeSet);
   }
 
+  public class EdgeEffect {
+    ctor public EdgeEffect(android.content.Context);
+    method public boolean draw(android.graphics.Canvas);
+    method public void finish();
+    method public boolean isFinished();
+    method public void onAbsorb(int);
+    method public void onPull(float);
+    method public void onRelease();
+    method public void setSize(int, int);
+  }
+
   public class EditText extends android.widget.TextView {
     ctor public EditText(android.content.Context);
     ctor public EditText(android.content.Context, android.util.AttributeSet);
@@ -26371,6 +26383,7 @@
     method public void fling(int, int, int, int, int, int, int, int);
     method public void fling(int, int, int, int, int, int, int, int, int, int);
     method public final void forceFinished(boolean);
+    method public float getCurrVelocity();
     method public final int getCurrX();
     method public final int getCurrY();
     method public final int getFinalX();
@@ -26713,6 +26726,7 @@
     method public void extendDuration(int);
     method public void fling(int, int, int, int, int, int, int, int);
     method public final void forceFinished(boolean);
+    method public float getCurrVelocity();
     method public final int getCurrX();
     method public final int getCurrY();
     method public final int getDuration();
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 8d6cee1..c7698bf 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5627,6 +5627,10 @@
             if (scheme != null) {
                 if (scheme.equalsIgnoreCase("tel")) {
                     b.append("tel:xxx-xxx-xxxx");
+                } else if (scheme.equalsIgnoreCase("sip")) {
+                    b.append("sip:xxxxxxxxxx");
+                } else if (scheme.equalsIgnoreCase("sms")) {
+                    b.append("sms:xxx-xxx-xxxx");
                 } else if (scheme.equalsIgnoreCase("smsto")) {
                     b.append("smsto:xxx-xxx-xxxx");
                 } else {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index bc45945..63f2244 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1468,6 +1468,7 @@
         private static final String KEY_MAX_NUM_DETECTED_FACES_HW = "max-num-detected-faces-hw";
         private static final String KEY_MAX_NUM_DETECTED_FACES_SW = "max-num-detected-faces-sw";
         private static final String KEY_RECORDING_HINT = "recording-hint";
+        private static final String KEY_VIDEO_SNAPSHOT_SUPPORTED = "video-snapshot-supported";
 
         // Parameter key suffix for supported values.
         private static final String SUPPORTED_VALUES_SUFFIX = "-values";
@@ -3210,6 +3211,35 @@
             set(KEY_RECORDING_HINT, hint ? TRUE : FALSE);
         }
 
+        /**
+         * Returns true if video snapshot is supported. That is, applications
+         * can call {@link #takePicture(Camera.ShutterCallback,
+         * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}
+         * during recording. Applications do not need to call {@link
+         * #startPreview()} after taking a picture. The preview will be still
+         * active. Other than that, taking a picture during recording is
+         * identical to taking a picture normally. All settings and methods
+         * related to takePicture work identically. Ex: {@link
+         * #getPictureSize()}, {@link #getSupportedPictureSizes()}, {@link
+         * #setJpegQuality(int)}, {@link #setRotation(int)}, and etc. The
+         * picture will have an EXIF header. {@link #FLASH_MODE_AUTO} and {@link
+         * #FLASH_MODE_ON} also still work, but the video will record the flash.
+         *
+         * Applications can set shutter callback as null to avoid the shutter
+         * sound. It is also recommended to set raw picture and post view
+         * callbacks to null to avoid the interrupt of preview display.
+         *
+         * Field-of-view of the recorded video may be different from that of the
+         * captured pictures.
+         *
+         * @return true if video snapshot is supported.
+         * @hide
+         */
+        public boolean isVideoSnapshotSupported() {
+            String str = get(KEY_VIDEO_SNAPSHOT_SUPPORTED);
+            return TRUE.equals(str);
+        }
+
         // Splits a comma delimited string to an ArrayList of String.
         // Return null if the passing string is null or the size is 0.
         private ArrayList<String> split(String str) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3441217..530122c 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -814,4 +814,22 @@
         } catch (RemoteException e) {
         }
     }
+
+    /**
+     * Returns true if the hardware supports the given network type
+     * else it returns false.  This doesn't indicate we have coverage
+     * or are authorized onto a network, just whether or not the
+     * hardware supports it.  For example a gsm phone without a sim
+     * should still return true for mobile data, but a wifi only tablet
+     * would return false.
+     * @param networkType The nework type we'd like to check
+     * @return true if supported, else false
+     * @hide
+     */
+    public boolean isNetworkSupported(int networkType) {
+        try {
+            return mService.isNetworkSupported(networkType);
+        } catch (RemoteException e) {}
+        return false;
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index c9553c0..eef658e 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -43,6 +43,8 @@
     NetworkInfo getNetworkInfo(int networkType);
     NetworkInfo[] getAllNetworkInfo();
 
+    boolean isNetworkSupported(int networkType);
+
     LinkProperties getActiveLinkProperties();
     LinkProperties getLinkProperties(int networkType);
 
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 421e995..768071f 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1296,7 +1296,10 @@
                     float h1 = getHorizontal(st, false, line);
                     float h2 = getHorizontal(en, true, line);
 
-                    dest.addRect(h1, top, h2, bottom, Path.Direction.CW);
+                    float left = Math.min(h1, h2);
+                    float right = Math.max(h1, h2);
+
+                    dest.addRect(left, top, right, bottom, Path.Direction.CW);
                 }
             }
         }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 1b4edf1..a963e10 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -20,6 +20,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.IBinder;
 import android.os.LocalPowerManager;
 import android.view.animation.Animation;
@@ -165,7 +166,7 @@
          * 
          * @return Rect The rectangle holding the shown window frame.
          */
-        public Rect getShownFrameLw();
+        public RectF getShownFrameLw();
 
         /**
          * Retrieve the frame of the display that this window was last
diff --git a/core/java/android/webkit/OverScrollGlow.java b/core/java/android/webkit/OverScrollGlow.java
index ff5b30b..e906f7f 100644
--- a/core/java/android/webkit/OverScrollGlow.java
+++ b/core/java/android/webkit/OverScrollGlow.java
@@ -22,7 +22,7 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.view.View;
-import android.widget.EdgeGlow;
+import android.widget.EdgeEffect;
 
 /**
  * This class manages the edge glow effect when a WebView is flung or pulled beyond the edges.
@@ -31,10 +31,10 @@
 public class OverScrollGlow {
     private WebView mHostView;
 
-    private EdgeGlow mEdgeGlowTop;
-    private EdgeGlow mEdgeGlowBottom;
-    private EdgeGlow mEdgeGlowLeft;
-    private EdgeGlow mEdgeGlowRight;
+    private EdgeEffect mEdgeGlowTop;
+    private EdgeEffect mEdgeGlowBottom;
+    private EdgeEffect mEdgeGlowLeft;
+    private EdgeEffect mEdgeGlowRight;
 
     private int mOverScrollDeltaX;
     private int mOverScrollDeltaY;
@@ -42,13 +42,10 @@
     public OverScrollGlow(WebView host) {
         mHostView = host;
         Context context = host.getContext();
-        final Resources res = context.getResources();
-        final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-        final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-        mEdgeGlowTop = new EdgeGlow(context, edge, glow);
-        mEdgeGlowBottom = new EdgeGlow(context, edge, glow);
-        mEdgeGlowLeft = new EdgeGlow(context, edge, glow);
-        mEdgeGlowRight = new EdgeGlow(context, edge, glow);
+        mEdgeGlowTop = new EdgeEffect(context);
+        mEdgeGlowBottom = new EdgeEffect(context);
+        mEdgeGlowLeft = new EdgeEffect(context);
+        mEdgeGlowRight = new EdgeEffect(context);
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4748522..5200b12 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -34,7 +34,6 @@
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.CornerPathEffect;
 import android.graphics.DrawFilter;
 import android.graphics.Paint;
 import android.graphics.PaintFlagsDrawFilter;
@@ -58,8 +57,6 @@
 import android.os.StrictMode;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
-import android.text.Selection;
-import android.text.Spannable;
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.util.Log;
@@ -606,9 +603,15 @@
     // know to handle Shift and arrows natively first
     private boolean mAccessibilityScriptInjected;
 
+    static final boolean USE_JAVA_TEXT_SELECTION = true;
+    private Region mTextSelectionRegion = new Region();
+    private Paint mTextSelectionPaint;
+    private Drawable mSelectHandleLeft;
+    private Drawable mSelectHandleRight;
+
     static final boolean USE_WEBKIT_RINGS = true;
     // the color used to highlight the touch rectangles
-    private static final int mHightlightColor = 0x6633b5e5;
+    private static final int HIGHLIGHT_COLOR = 0x6633b5e5;
     // the round corner for the highlight path
     private static final float TOUCH_HIGHLIGHT_ARC = 5.0f;
     // the region indicating where the user touched on the screen
@@ -4050,8 +4053,10 @@
 
     @Override
     protected void onDraw(Canvas canvas) {
-        // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
+        // if mNativeClass is 0, the WebView is either destroyed or not
+        // initialized. In either case, just draw the background color and return
         if (mNativeClass == 0) {
+            canvas.drawColor(mBackgroundColor);
             return;
         }
 
@@ -4105,7 +4110,7 @@
             } else {
                 if (mTouchHightlightPaint == null) {
                     mTouchHightlightPaint = new Paint();
-                    mTouchHightlightPaint.setColor(mHightlightColor);
+                    mTouchHightlightPaint.setColor(HIGHLIGHT_COLOR);
                 }
                 RegionIterator iter = new RegionIterator(mTouchHighlightRegion);
                 Rect r = new Rect();
@@ -4400,7 +4405,7 @@
         int extras = DRAW_EXTRAS_NONE;
         if (mFindIsUp) {
             extras = DRAW_EXTRAS_FIND;
-        } else if (mSelectingText) {
+        } else if (mSelectingText && !USE_JAVA_TEXT_SELECTION) {
             extras = DRAW_EXTRAS_SELECTION;
             nativeSetSelectionPointer(mDrawSelectionPointer,
                     mZoomManager.getInvScale(),
@@ -4427,6 +4432,10 @@
                 nativeUseHardwareAccelSkia(mHardwareAccelSkia);
             }
 
+            if (mSelectingText && USE_JAVA_TEXT_SELECTION) {
+                drawTextSelectionHandles(canvas);
+            }
+
         } else {
             DrawFilter df = null;
             if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
@@ -4461,6 +4470,56 @@
         }
     }
 
+    private void drawTextSelectionHandles(Canvas canvas) {
+        if (mTextSelectionPaint == null) {
+            mTextSelectionPaint = new Paint();
+            mTextSelectionPaint.setColor(HIGHLIGHT_COLOR);
+        }
+        mTextSelectionRegion.setEmpty();
+        nativeGetTextSelectionRegion(mTextSelectionRegion);
+        Rect r = new Rect();
+        RegionIterator iter = new RegionIterator(mTextSelectionRegion);
+        int start_x = -1;
+        int start_y = -1;
+        int end_x = -1;
+        int end_y = -1;
+        while (iter.next(r)) {
+            r = new Rect(
+                    contentToViewDimension(r.left),
+                    contentToViewDimension(r.top),
+                    contentToViewDimension(r.right),
+                    contentToViewDimension(r.bottom));
+            // Regions are in order. First one is where selection starts,
+            // last one is where it ends
+            if (start_x < 0 || start_y < 0) {
+                start_x = r.left;
+                start_y = r.bottom;
+            }
+            end_x = r.right;
+            end_y = r.bottom;
+            canvas.drawRect(r, mTextSelectionPaint);
+        }
+        if (mSelectHandleLeft == null) {
+            mSelectHandleLeft = mContext.getResources().getDrawable(
+                    com.android.internal.R.drawable.text_select_handle_left);
+        }
+        // Magic formula copied from TextView
+        start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4;
+        mSelectHandleLeft.setBounds(start_x, start_y,
+                start_x + mSelectHandleLeft.getIntrinsicWidth(),
+                start_y + mSelectHandleLeft.getIntrinsicHeight());
+        if (mSelectHandleRight == null) {
+            mSelectHandleRight = mContext.getResources().getDrawable(
+                    com.android.internal.R.drawable.text_select_handle_right);
+        }
+        end_x -= mSelectHandleRight.getIntrinsicWidth() / 4;
+        mSelectHandleRight.setBounds(end_x, end_y,
+                end_x + mSelectHandleRight.getIntrinsicWidth(),
+                end_y + mSelectHandleRight.getIntrinsicHeight());
+        mSelectHandleLeft.draw(canvas);
+        mSelectHandleRight.draw(canvas);
+    }
+
     // draw history
     private boolean mDrawHistory = false;
     private Picture mHistoryPicture = null;
@@ -9329,4 +9388,5 @@
     private native int      nativeGetBackgroundColor();
     native boolean  nativeSetProperty(String key, String value);
     native String   nativeGetProperty(String key);
+    private native void     nativeGetTextSelectionRegion(Region region);
 }
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 80cbbfb..05d4f05 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -594,12 +594,12 @@
     /**
      * Tracks the state of the top edge glow.
      */
-    private EdgeGlow mEdgeGlowTop;
+    private EdgeEffect mEdgeGlowTop;
 
     /**
      * Tracks the state of the bottom edge glow.
      */
-    private EdgeGlow mEdgeGlowBottom;
+    private EdgeEffect mEdgeGlowBottom;
 
     /**
      * An estimate of how many pixels are between the top of the list and
@@ -788,11 +788,8 @@
         if (mode != OVER_SCROLL_NEVER) {
             if (mEdgeGlowTop == null) {
                 Context context = getContext();
-                final Resources res = context.getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowTop = new EdgeGlow(context, edge, glow);
-                mEdgeGlowBottom = new EdgeGlow(context, edge, glow);
+                mEdgeGlowTop = new EdgeEffect(context);
+                mEdgeGlowBottom = new EdgeEffect(context);
             }
         } else {
             mEdgeGlowTop = null;
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeEffect.java
similarity index 77%
rename from core/java/android/widget/EdgeGlow.java
rename to core/java/android/widget/EdgeEffect.java
index 75cef38..fd2abc2 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -16,7 +16,10 @@
 
 package android.widget;
 
+import com.android.internal.R;
+
 import android.content.Context;
+import android.content.res.Resources;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.view.animation.AnimationUtils;
@@ -24,19 +27,33 @@
 import android.view.animation.Interpolator;
 
 /**
- * This class performs the glow effect used at the edges of scrollable widgets.
- * @hide
+ * This class performs the graphical effect used at the edges of scrollable widgets
+ * when the user scrolls beyond the content bounds in 2D space.
+ *
+ * <p>EdgeEffect is stateful. Custom widgets using EdgeEffect should create an
+ * instance for each edge that should show the effect, feed it input data using
+ * the methods {@link #onAbsorb(int)}, {@link #onPull(float)}, and {@link #onRelease()},
+ * and draw the effect using {@link #draw(Canvas)} in the widget's overridden
+ * {@link android.view.View#draw(Canvas)} method. If {@link #isFinished()} returns
+ * false after drawing, the edge effect's animation is not yet complete and the widget
+ * should schedule another drawing pass to continue the animation.</p>
+ *
+ * <p>When drawing, widgets should draw their main content and child views first,
+ * usually by invoking <code>super.draw(canvas)</code> from an overridden <code>draw</code>
+ * method. (This will invoke onDraw and dispatch drawing to child views as needed.)
+ * The edge effect may then be drawn on top of the view's content using the
+ * {@link #draw(Canvas)} method.</p>
  */
-public class EdgeGlow {
-    private static final String TAG = "EdgeGlow";
+public class EdgeEffect {
+    private static final String TAG = "EdgeEffect";
 
     // Time it will take the effect to fully recede in ms
     private static final int RECEDE_TIME = 1000;
 
-    // Time it will take before a pulled glow begins receding
+    // Time it will take before a pulled glow begins receding in ms
     private static final int PULL_TIME = 167;
 
-    // Time it will take for a pulled glow to decay to partial strength before release
+    // Time it will take in ms for a pulled glow to decay to partial strength before release
     private static final int PULL_DECAY_TIME = 1000;
 
     private static final float MAX_ALPHA = 0.8f;
@@ -103,31 +120,58 @@
 
     private float mPullDistance;
 
-    public EdgeGlow(Context context, Drawable edge, Drawable glow) {
-        mEdge = edge;
-        mGlow = glow;
+    /**
+     * Construct a new EdgeEffect with a theme appropriate for the provided context.
+     * @param context Context used to provide theming and resource information for the EdgeEffect
+     */
+    public EdgeEffect(Context context) {
+        final Resources res = context.getResources();
+        mEdge = res.getDrawable(R.drawable.overscroll_edge);
+        mGlow = res.getDrawable(R.drawable.overscroll_glow);
 
         mMinWidth = (int) (context.getResources().getDisplayMetrics().density * MIN_WIDTH + 0.5f);
         mInterpolator = new DecelerateInterpolator();
     }
 
+    /**
+     * Set the size of this edge effect in pixels.
+     *
+     * @param width Effect width in pixels
+     * @param height Effect height in pixels
+     */
     public void setSize(int width, int height) {
         mWidth = width;
         mHeight = height;
     }
 
+    /**
+     * Reports if this EdgeEffect's animation is finished. If this method returns false
+     * after a call to {@link #draw(Canvas)} the host widget should schedule another
+     * drawing pass to continue the animation.
+     *
+     * @return true if animation is finished, false if drawing should continue on the next frame.
+     */
     public boolean isFinished() {
         return mState == STATE_IDLE;
     }
 
+    /**
+     * Immediately finish the current animation.
+     * After this call {@link #isFinished()} will return true.
+     */
     public void finish() {
         mState = STATE_IDLE;
     }
 
     /**
-     * Call when the object is pulled by the user.
+     * A view should call this when content is pulled away from an edge by the user.
+     * This will update the state of the current visual effect and its associated animation.
+     * The host view should always {@link android.view.View#invalidate()} after this
+     * and draw the results accordingly.
      *
-     * @param deltaDistance Change in distance since the last call
+     * @param deltaDistance Change in distance since the last call. Values may be 0 (no change) to
+     *                      1.f (full length of the view) or negative values to express change
+     *                      back toward the edge reached to initiate the effect.
      */
     public void onPull(float deltaDistance) {
         final long now = AnimationUtils.currentAnimationTimeMillis();
@@ -173,6 +217,9 @@
 
     /**
      * Call when the object is released after being pulled.
+     * This will begin the "decay" phase of the effect. After calling this method
+     * the host view should {@link android.view.View#invalidate()} and thereby
+     * draw the results accordingly.
      */
     public void onRelease() {
         mPullDistance = 0;
@@ -198,6 +245,11 @@
 
     /**
      * Call when the effect absorbs an impact at the given velocity.
+     * Used when a fling reaches the scroll boundary.
+     *
+     * <p>When using a {@link android.widget.Scroller} or {@link android.widget.OverScroller},
+     * the method <code>getCurrVelocity</code> will provide a reasonable approximation
+     * to use here.</p>
      *
      * @param velocity Velocity at impact in pixels per second.
      */
@@ -238,7 +290,7 @@
     /**
      * Draw into the provided canvas. Assumes that the canvas has been rotated
      * accordingly and the size has been set. The effect will be drawn the full
-     * width of X=0 to X=width, emitting from Y=0 and extending to some factor <
+     * width of X=0 to X=width, beginning from Y=0 and extending to some factor <
      * 1.f of height.
      *
      * @param canvas Canvas to draw into
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index d638732..a7a05be 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -75,8 +75,8 @@
 
     private final Rect mTempRect = new Rect();
     private OverScroller mScroller;
-    private EdgeGlow mEdgeGlowLeft;
-    private EdgeGlow mEdgeGlowRight;
+    private EdgeEffect mEdgeGlowLeft;
+    private EdgeEffect mEdgeGlowRight;
 
     /**
      * Position of the last motion event.
@@ -1477,11 +1477,8 @@
         if (mode != OVER_SCROLL_NEVER) {
             if (mEdgeGlowLeft == null) {
                 Context context = getContext();
-                final Resources res = context.getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowLeft = new EdgeGlow(context, edge, glow);
-                mEdgeGlowRight = new EdgeGlow(context, edge, glow);
+                mEdgeGlowLeft = new EdgeEffect(context);
+                mEdgeGlowRight = new EdgeEffect(context);
             }
         } else {
             mEdgeGlowLeft = null;
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index 0ba7889..542a1ef 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -166,7 +166,6 @@
     }
 
     /**
-     * @hide
      * Returns the absolute value of the current velocity.
      *
      * @return The original velocity less the deceleration, norm of the X and Y velocity vector.
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 09c875b..6a6bc23 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -69,8 +69,8 @@
 
     private final Rect mTempRect = new Rect();
     private OverScroller mScroller;
-    private EdgeGlow mEdgeGlowTop;
-    private EdgeGlow mEdgeGlowBottom;
+    private EdgeEffect mEdgeGlowTop;
+    private EdgeEffect mEdgeGlowBottom;
 
     /**
      * Position of the last motion event.
@@ -1511,11 +1511,8 @@
         if (mode != OVER_SCROLL_NEVER) {
             if (mEdgeGlowTop == null) {
                 Context context = getContext();
-                final Resources res = context.getResources();
-                final Drawable edge = res.getDrawable(R.drawable.overscroll_edge);
-                final Drawable glow = res.getDrawable(R.drawable.overscroll_glow);
-                mEdgeGlowTop = new EdgeGlow(context, edge, glow);
-                mEdgeGlowBottom = new EdgeGlow(context, edge, glow);
+                mEdgeGlowTop = new EdgeEffect(context);
+                mEdgeGlowBottom = new EdgeEffect(context);
             }
         } else {
             mEdgeGlowTop = null;
diff --git a/core/java/android/widget/Scroller.java b/core/java/android/widget/Scroller.java
index f00640e..a6e83f0 100644
--- a/core/java/android/widget/Scroller.java
+++ b/core/java/android/widget/Scroller.java
@@ -196,7 +196,6 @@
     }
     
     /**
-     * @hide
      * Returns the current velocity.
      *
      * @return The original velocity less the deceleration. Result may be
diff --git a/core/jni/android_database_CursorWindow.cpp b/core/jni/android_database_CursorWindow.cpp
index ddac820..419e464 100644
--- a/core/jni/android_database_CursorWindow.cpp
+++ b/core/jni/android_database_CursorWindow.cpp
@@ -352,7 +352,6 @@
             char buf[32];
             int len;
             snprintf(buf, sizeof(buf), "%lld", value);
-            jchar* dst = env->GetCharArrayElements(buffer, NULL);
             sizeCopied = charToJchar(buf, dst, bufferSize);
          }
     } else if (type == FIELD_TYPE_FLOAT) {
@@ -360,7 +359,6 @@
         if (window->getDouble(row, column, &value)) {
             char tempbuf[32];
             snprintf(tempbuf, sizeof(tempbuf), "%g", value);
-            jchar* dst = env->GetCharArrayElements(buffer, NULL);
             sizeCopied = charToJchar(tempbuf, dst, bufferSize);
         }
     } else if (type == FIELD_TYPE_NULL) {
diff --git a/core/res/res/drawable/switch_inner_holo_dark.xml b/core/res/res/drawable/switch_inner_holo_dark.xml
index 3eb55ee..67584bc 100644
--- a/core/res/res/drawable/switch_inner_holo_dark.xml
+++ b/core/res/res/drawable/switch_inner_holo_dark.xml
@@ -17,5 +17,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false" android:drawable="@drawable/switch_thumb_disabled_holo_dark" />
     <item android:state_pressed="true"  android:drawable="@drawable/switch_thumb_pressed_holo_dark" />
+    <item android:state_checked="true"  android:drawable="@drawable/switch_thumb_activated_holo_dark" />
     <item                               android:drawable="@drawable/switch_thumb_holo_dark" />
 </selector>
diff --git a/core/res/res/drawable/switch_inner_holo_light.xml b/core/res/res/drawable/switch_inner_holo_light.xml
index 9b287cf..95df0e88 100644
--- a/core/res/res/drawable/switch_inner_holo_light.xml
+++ b/core/res/res/drawable/switch_inner_holo_light.xml
@@ -17,5 +17,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
     <item android:state_enabled="false" android:drawable="@drawable/switch_thumb_disabled_holo_light" />
     <item android:state_pressed="true"  android:drawable="@drawable/switch_thumb_pressed_holo_light" />
+    <item android:state_checked="true"  android:drawable="@drawable/switch_thumb_activated_holo_light" />
     <item                               android:drawable="@drawable/switch_thumb_holo_light" />
 </selector>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index cacbdaa..56bd2ce 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -14,15 +14,14 @@
 
 # Warning: this is actually a product definition, to be inherited from
 
+# On space-constrained devices, we include a subset of fonts:
+# First, core/required fonts
 PRODUCT_COPY_FILES := \
     frameworks/base/data/fonts/Roboto-Regular.ttf:system/fonts/Roboto-Regular.ttf \
     frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \
     frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \
     frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \
-    frameworks/base/data/fonts/DroidSans.ttf:system/fonts/DroidSans.ttf \
-    frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.ttf \
     frameworks/base/data/fonts/DroidNaskh-Regular.ttf:system/fonts/DroidNaskh-Regular.ttf \
-    frameworks/base/data/fonts/DroidSansEthiopic-Regular.ttf:system/fonts/DroidSansEthiopic-Regular.ttf \
     frameworks/base/data/fonts/DroidSansHebrew-Regular.ttf:system/fonts/DroidSansHebrew-Regular.ttf \
     frameworks/base/data/fonts/DroidSansHebrew-Bold.ttf:system/fonts/DroidSansHebrew-Bold.ttf \
     frameworks/base/data/fonts/DroidSansThai.ttf:system/fonts/DroidSansThai.ttf \
@@ -33,9 +32,22 @@
     frameworks/base/data/fonts/DroidSansMono.ttf:system/fonts/DroidSansMono.ttf \
     frameworks/base/data/fonts/Lohit_Hindi.ttf:system/fonts/Lohit_Hindi.ttf \
     frameworks/base/data/fonts/Clockopia.ttf:system/fonts/Clockopia.ttf \
-    frameworks/base/data/fonts/DroidSansFallback.ttf:system/fonts/DroidSansFallback.ttf \
     frameworks/base/data/fonts/AndroidClock.ttf:system/fonts/AndroidClock.ttf \
     frameworks/base/data/fonts/AndroidClock_Highlight.ttf:system/fonts/AndroidClock_Highlight.ttf \
     frameworks/base/data/fonts/AndroidClock_Solid.ttf:system/fonts/AndroidClock_Solid.ttf \
     frameworks/base/data/fonts/system_fonts.xml:system/etc/system_fonts.xml \
     frameworks/base/data/fonts/fallback_fonts.xml:system/etc/fallback_fonts.xml
+
+# Next, include additional fonts, depending on how much space we have
+ifeq ($(SMALLER_FONT_FOOTPRINT),true)
+# Smaller fonts alternatives
+PRODUCT_COPY_FILES += \
+    frameworks/base/data/fonts/DroidSansFallback.ttf:system/fonts/DroidSansFallback.ttf
+else
+# Full font set alternatives
+PRODUCT_COPY_FILES += \
+    frameworks/base/data/fonts/DroidSansFallbackFull.ttf:system/fonts/DroidSansFallback.ttf \
+    frameworks/base/data/fonts/DroidSans.ttf:system/fonts/DroidSans.ttf \
+    frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.ttf \
+    frameworks/base/data/fonts/DroidSansEthiopic-Regular.ttf:system/fonts/DroidSansEthiopic-Regular.ttf
+endif
diff --git a/graphics/java/android/graphics/RectF.java b/graphics/java/android/graphics/RectF.java
index 2b3aa33..00e9609 100644
--- a/graphics/java/android/graphics/RectF.java
+++ b/graphics/java/android/graphics/RectF.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import java.io.PrintWriter;
+
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.FloatMath;
@@ -81,8 +83,37 @@
         return "RectF(" + left + ", " + top + ", "
                       + right + ", " + bottom + ")";
     }
+
+    /**
+     * Return a string representation of the rectangle in a compact form.
+     */
+    public String toShortString() {
+        return toShortString(new StringBuilder(32));
+    }
     
     /**
+     * Return a string representation of the rectangle in a compact form.
+     * @hide
+     */
+    public String toShortString(StringBuilder sb) {
+        sb.setLength(0);
+        sb.append('['); sb.append(left); sb.append(',');
+        sb.append(top); sb.append("]["); sb.append(right);
+        sb.append(','); sb.append(bottom); sb.append(']');
+        return sb.toString();
+    }
+    
+    /**
+     * Print short representation to given writer.
+     * @hide
+     */
+    public void printShortString(PrintWriter pw) {
+        pw.print('['); pw.print(left); pw.print(',');
+        pw.print(top); pw.print("]["); pw.print(right);
+        pw.print(','); pw.print(bottom); pw.print(']');
+    }
+
+    /**
      * Returns true if the rectangle is empty (left >= right or top >= bottom)
      */
     public final boolean isEmpty() {
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 6c91dfc..4a4bcfb 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -495,6 +495,25 @@
     // Example value: "true" or "false". Read/write.
     static const char KEY_RECORDING_HINT[];
 
+    // Returns true if video snapshot is supported. That is, applications
+    // can call Camera.takePicture during recording. Applications do not need to
+    // call Camera.startPreview after taking a picture. The preview will be
+    // still active. Other than that, taking a picture during recording is
+    // identical to taking a picture normally. All settings and methods related
+    // to takePicture work identically. Ex: KEY_PICTURE_SIZE,
+    // KEY_SUPPORTED_PICTURE_SIZES, KEY_JPEG_QUALITY, KEY_ROTATION, and etc.
+    // The picture will have an EXIF header. FLASH_MODE_AUTO and FLASH_MODE_ON
+    // also still work, but the video will record the flash.
+    //
+    // Applications can set shutter callback as null to avoid the shutter
+    // sound. It is also recommended to set raw picture and post view callbacks
+    // to null to avoid the interrupt of preview display.
+    //
+    // Field-of-view of the recorded video may be different from that of the
+    // captured pictures.
+    // Example value: "true" or "false". Read only.
+    static const char KEY_VIDEO_SNAPSHOT_SUPPORTED[];
+
     // Value for KEY_ZOOM_SUPPORTED or KEY_SMOOTH_ZOOM_SUPPORTED.
     static const char TRUE[];
     static const char FALSE[];
diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h
index 74d54d1..d0940bb 100644
--- a/include/media/stagefright/SurfaceMediaSource.h
+++ b/include/media/stagefright/SurfaceMediaSource.h
@@ -34,7 +34,7 @@
 class SurfaceMediaSource : public BnSurfaceTexture, public MediaSource,
                                             public MediaBufferObserver {
 public:
-    enum { MIN_UNDEQUEUED_BUFFERS = 3 };
+    enum { MIN_UNDEQUEUED_BUFFERS = 4 };
     enum {
         MIN_ASYNC_BUFFER_SLOTS = MIN_UNDEQUEUED_BUFFERS + 1,
         MIN_SYNC_BUFFER_SLOTS  = MIN_UNDEQUEUED_BUFFERS
@@ -347,6 +347,13 @@
     // encoder
     int mNumFramesEncoded;
 
+    // mFirstFrameTimestamp is the timestamp of the first received frame.
+    // It is used to offset the output timestamps so recording starts at time 0.
+    int64_t mFirstFrameTimestamp;
+    // mStartTimeNs is the start time passed into the source at start, used to
+    // offset timestamps.
+    int64_t mStartTimeNs;
+
     // mFrameAvailableCondition condition used to indicate whether there
     // is a frame available for dequeuing
     Condition mFrameAvailableCondition;
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index 0eb5d50..0dcab6b 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -87,6 +87,7 @@
 const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_HW[] = "max-num-detected-faces-hw";
 const char CameraParameters::KEY_MAX_NUM_DETECTED_FACES_SW[] = "max-num-detected-faces-sw";
 const char CameraParameters::KEY_RECORDING_HINT[] = "recording-hint";
+const char CameraParameters::KEY_VIDEO_SNAPSHOT_SUPPORTED[] = "video-snapshot-supported";
 
 const char CameraParameters::TRUE[] = "true";
 const char CameraParameters::FALSE[] = "false";
diff --git a/libs/rs/scriptc/rs_cl.rsh b/libs/rs/scriptc/rs_cl.rsh
index e402b86..98b0b1d 100644
--- a/libs/rs/scriptc/rs_cl.rsh
+++ b/libs/rs/scriptc/rs_cl.rsh
@@ -15,7 +15,7 @@
  */
 
 /** @file rs_cl.rsh
- *  \brief Additional compute routines
+ *  \brief Basic math functions
  *
  *
  */
@@ -111,221 +111,552 @@
         fnc(float4 v1, float4 v2, int4 *v3);
 
 
+/**
+ * Return the inverse cosine.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) acos(float);
 FN_FUNC_FN(acos)
 
+/**
+ * Return the inverse hyperbolic cosine.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) acosh(float);
 FN_FUNC_FN(acosh)
 
+/**
+ * Return the inverse cosine divided by PI.
+ *
+ * Supports float, float2, float3, float4
+ */
 _RS_RUNTIME float __attribute__((overloadable)) acospi(float v);
-
-
 FN_FUNC_FN(acospi)
 
+/**
+ * Return the inverse sine.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) asin(float);
 FN_FUNC_FN(asin)
 
+/**
+ * Return the inverse hyperbolic sine.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) asinh(float);
 FN_FUNC_FN(asinh)
 
 
+/**
+ * Return the inverse sine divided by PI.
+ *
+ * Supports float, float2, float3, float4
+ */
 _RS_RUNTIME float __attribute__((overloadable)) asinpi(float v);
 FN_FUNC_FN(asinpi)
 
+/**
+ * Return the inverse tangent.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) atan(float);
 FN_FUNC_FN(atan)
 
-extern float __attribute__((overloadable)) atan2(float, float);
+/**
+ * Return the inverse tangent of y / x.
+ *
+ * Supports float, float2, float3, float4.  Both arguments must be of the same
+ * type.
+ *
+ * @param y
+ * @param x
+ */
+extern float __attribute__((overloadable)) atan2(float y, float x);
 FN_FUNC_FN_FN(atan2)
 
+/**
+ * Return the inverse hyperbolic tangent.
+ *
+ * Supports float, float2, float3, float4
+ */
 extern float __attribute__((overloadable)) atanh(float);
 FN_FUNC_FN(atanh)
 
-
+/**
+ * Return the inverse tangent divided by PI.
+ *
+ * Supports float, float2, float3, float4
+ */
 _RS_RUNTIME float __attribute__((overloadable)) atanpi(float v);
 FN_FUNC_FN(atanpi)
 
-
+/**
+ * Return the inverse tangent of y / x, divided by PI.
+ *
+ * Supports float, float2, float3, float4.  Both arguments must be of the same
+ * type.
+ *
+ * @param y
+ * @param x
+ */
 _RS_RUNTIME float __attribute__((overloadable)) atan2pi(float y, float x);
 FN_FUNC_FN_FN(atan2pi)
 
+
+/**
+ * Return the cube root.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) cbrt(float);
 FN_FUNC_FN(cbrt)
 
+/**
+ * Return the smallest integer not less than a value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) ceil(float);
 FN_FUNC_FN(ceil)
 
-extern float __attribute__((overloadable)) copysign(float, float);
+/**
+ * Copy the sign bit from y to x.
+ *
+ * Supports float, float2, float3, float4.  Both arguments must be of the same
+ * type.
+ *
+ * @param x
+ * @param y
+ */
+extern float __attribute__((overloadable)) copysign(float x, float y);
 FN_FUNC_FN_FN(copysign)
 
+/**
+ * Return the cosine.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) cos(float);
 FN_FUNC_FN(cos)
 
+/**
+ * Return the hypebolic cosine.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) cosh(float);
 FN_FUNC_FN(cosh)
 
-
+/**
+ * Return the cosine of the value * PI.
+ *
+ * Supports float, float2, float3, float4.
+ */
 _RS_RUNTIME float __attribute__((overloadable)) cospi(float v);
 FN_FUNC_FN(cospi)
 
+/**
+ * Return the complementary error function.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) erfc(float);
 FN_FUNC_FN(erfc)
 
+/**
+ * Return the error function.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) erf(float);
 FN_FUNC_FN(erf)
 
+/**
+ * Return e ^ value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) exp(float);
 FN_FUNC_FN(exp)
 
+/**
+ * Return 2 ^ value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) exp2(float);
 FN_FUNC_FN(exp2)
 
-extern float __attribute__((overloadable)) pow(float, float);
+/**
+ * Return x ^ y.
+ *
+ * Supports float, float2, float3, float4. Both arguments must be of the same
+ * type.
+ */
+extern float __attribute__((overloadable)) pow(float x, float y);
+FN_FUNC_FN_FN(pow)
 
+/**
+ * Return 10 ^ value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 _RS_RUNTIME float __attribute__((overloadable)) exp10(float v);
 FN_FUNC_FN(exp10)
 
+/**
+ * Return (e ^ value) - 1.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) expm1(float);
 FN_FUNC_FN(expm1)
 
+/**
+ * Return the absolute value of a value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) fabs(float);
 FN_FUNC_FN(fabs)
 
+/**
+ * Return the positive difference between two values.
+ *
+ * Supports float, float2, float3, float4.  Both arguments must be of the same
+ * type.
+ */
 extern float __attribute__((overloadable)) fdim(float, float);
 FN_FUNC_FN_FN(fdim)
 
+/**
+ * Return the smallest integer not greater than a value.
+ *
+ * Supports float, float2, float3, float4.
+ */
 extern float __attribute__((overloadable)) floor(float);
 FN_FUNC_FN(floor)
 
-extern float __attribute__((overloadable)) fma(float, float, float);
+/**
+ * Return a*b + c.
+ *
+ * Supports float, float2, float3, float4.
+ */
+extern float __attribute__((overloadable)) fma(float a, float b, float c);
 FN_FUNC_FN_FN_FN(fma)
 
-extern float __attribute__((overloadable)) fmax(float, float);
+/**
+ * Return (x < y ? y : x)
+ *
+ * Supports float, float2, float3, float4.
+ * @param x: may be float, float2, float3, float4
+ * @param y: may be float or vector.  If vector must match type of x.
+ */
+extern float __attribute__((overloadable)) fmax(float x, float y);
 FN_FUNC_FN_FN(fmax);
 FN_FUNC_FN_F(fmax);
 
-extern float __attribute__((overloadable)) fmin(float, float);
+/**
+ * Return (x > y ? y : x)
+ *
+ * @param x: may be float, float2, float3, float4
+ * @param y: may be float or vector.  If vector must match type of x.
+ */
+extern float __attribute__((overloadable)) fmin(float x, float y);
 FN_FUNC_FN_FN(fmin);
 FN_FUNC_FN_F(fmin);
 
-extern float __attribute__((overloadable)) fmod(float, float);
+/**
+ * Return the remainder from x / y
+ *
+ * Supports float, float2, float3, float4.
+ */
+extern float __attribute__((overloadable)) fmod(float x, float y);
 FN_FUNC_FN_FN(fmod)
 
 
+/**
+ * Return fractional part of v
+ *
+ * @param iptr  iptr[0] will be set to the floor of the input value.
+ * Supports float, float2, float3, float4.
+ */
 _RS_RUNTIME float __attribute__((overloadable)) fract(float v, float *iptr);
 FN_FUNC_FN_PFN(fract)
 
-extern float __attribute__((overloadable)) frexp(float, int *);
+/**
+ * Return the mantissa and place the exponent into iptr[0]
+ *
+ * @param v Supports float, float2, float3, float4.
+ * @param iptr  Must have the same vector size as v.
+ */
+extern float __attribute__((overloadable)) frexp(float v, int *iptr);
 FN_FUNC_FN_PIN(frexp)
 
-extern float __attribute__((overloadable)) hypot(float, float);
+/**
+ * Return sqrt(x*x + y*y)
+ *
+ * Supports float, float2, float3, float4.
+ */
+extern float __attribute__((overloadable)) hypot(float x, float y);
 FN_FUNC_FN_FN(hypot)
 
+/**
+ * Return the integer exponent of a value
+ *
+ * Supports 1,2,3,4 components
+ */
 extern int __attribute__((overloadable)) ilogb(float);
 IN_FUNC_FN(ilogb)
 
-extern float __attribute__((overloadable)) ldexp(float, int);
+/**
+ * Return (x * 2^y)
+ *
+ * @param x Supports 1,2,3,4 components
+ * @param y Supports single component or matching vector.
+ */
+extern float __attribute__((overloadable)) ldexp(float x, int y);
 FN_FUNC_FN_IN(ldexp)
 FN_FUNC_FN_I(ldexp)
 
+/**
+ * Return the log gamma
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) lgamma(float);
 FN_FUNC_FN(lgamma)
-extern float __attribute__((overloadable)) lgamma(float, int*);
+
+/**
+ * Return the log gamma and sign
+ *
+ * @param x Supports 1,2,3,4 components
+ * @param y Supports matching vector.
+ */
+extern float __attribute__((overloadable)) lgamma(float x, int* y);
 FN_FUNC_FN_PIN(lgamma)
 
+/**
+ * Return the natural logarithm
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) log(float);
 FN_FUNC_FN(log)
 
-
+/**
+ * Return the base 10 logarithm
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) log10(float);
 FN_FUNC_FN(log10)
 
-
+/**
+ * Return the base 2 logarithm
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) log2(float v);
 FN_FUNC_FN(log2)
 
-extern float __attribute__((overloadable)) log1p(float);
+/**
+ * Return the natural logarithm of (v + 1.0f)
+ *
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) log1p(float v);
 FN_FUNC_FN(log1p)
 
+/**
+ * Compute the exponent of the value.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) logb(float);
 FN_FUNC_FN(logb)
 
-extern float __attribute__((overloadable)) mad(float, float, float);
+/**
+ * Compute (a * b) + c
+ *
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) mad(float a, float b, float c);
 FN_FUNC_FN_FN_FN(mad)
 
-extern float __attribute__((overloadable)) modf(float, float *);
+/**
+ * Return the integral and fractional components of a number
+ * Supports 1,2,3,4 components
+ *
+ * @param x Source value
+ * @param iret iret[0] will be set to the integral portion of the number.
+ * @return The floating point portion of the value.
+ */
+extern float __attribute__((overloadable)) modf(float x, float *iret);
 FN_FUNC_FN_PFN(modf);
 
 //extern float __attribute__((overloadable)) nan(uint);
 
-extern float __attribute__((overloadable)) nextafter(float, float);
+/**
+ * Return the next floating point number from x towards y.
+ *
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) nextafter(float x, float y);
 FN_FUNC_FN_FN(nextafter)
 
-FN_FUNC_FN_FN(pow)
-
+/**
+ * Return (v ^ p).
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) pown(float v, int p);
-_RS_RUNTIME float2 __attribute__((overloadable)) pown(float2 v, int2 p);
-_RS_RUNTIME float3 __attribute__((overloadable)) pown(float3 v, int3 p);
-_RS_RUNTIME float4 __attribute__((overloadable)) pown(float4 v, int4 p);
+FN_FUNC_FN_IN(pown)
 
+/**
+ * Return (v ^ p).
+ * @param v must be greater than 0.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) powr(float v, float p);
-_RS_RUNTIME float2 __attribute__((overloadable)) powr(float2 v, float2 p);
-_RS_RUNTIME float3 __attribute__((overloadable)) powr(float3 v, float3 p);
-_RS_RUNTIME float4 __attribute__((overloadable)) powr(float4 v, float4 p);
+FN_FUNC_FN_FN(powr)
 
-extern float __attribute__((overloadable)) remainder(float, float);
+/**
+ * Return round x/y to the nearest integer then compute the remander.
+ *
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) remainder(float x, float y);
 FN_FUNC_FN_FN(remainder)
 
+// document once we know the precision of bionic
 extern float __attribute__((overloadable)) remquo(float, float, int *);
 FN_FUNC_FN_FN_PIN(remquo)
 
+/**
+ * Round to the nearest integral value.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) rint(float);
 FN_FUNC_FN(rint)
 
+/**
+ * Compute the Nth root of a value.
+ *
+ * Supports 1,2,3,4 components
+ */
+_RS_RUNTIME float __attribute__((overloadable)) rootn(float v, int n);
+FN_FUNC_FN_IN(rootn)
 
-_RS_RUNTIME float __attribute__((overloadable)) rootn(float v, int r);
-_RS_RUNTIME float2 __attribute__((overloadable)) rootn(float2 v, int2 r);
-_RS_RUNTIME float3 __attribute__((overloadable)) rootn(float3 v, int3 r);
-_RS_RUNTIME float4 __attribute__((overloadable)) rootn(float4 v, int4 r);
-
-
+/**
+ * Round to the nearest integral value.  Half values are rounded away from zero.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) round(float);
 FN_FUNC_FN(round)
 
-
+/**
+ * Return the square root of a value.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) sqrt(float);
+FN_FUNC_FN(sqrt)
+
+/**
+ * Return (1 / sqrt(value)).
+ *
+ * @param v The incoming value in radians
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) rsqrt(float v);
 FN_FUNC_FN(rsqrt)
 
-extern float __attribute__((overloadable)) sin(float);
+/**
+ * Return the sine of a value specified in radians.
+ *
+ * @param v The incoming value in radians
+ * Supports 1,2,3,4 components
+ */
+extern float __attribute__((overloadable)) sin(float v);
 FN_FUNC_FN(sin)
 
+/**
+ * Return the sine and cosine of a value.
+ *
+ * @return sine
+ * @param v The incoming value in radians
+ * @param *cosptr cosptr[0] will be set to the cosine value.
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) sincos(float v, float *cosptr);
-_RS_RUNTIME float2 __attribute__((overloadable)) sincos(float2 v, float2 *cosptr);
-_RS_RUNTIME float3 __attribute__((overloadable)) sincos(float3 v, float3 *cosptr);
-_RS_RUNTIME float4 __attribute__((overloadable)) sincos(float4 v, float4 *cosptr);
+FN_FUNC_FN_PFN(sincos);
 
+/**
+ * Return the hyperbolic sine of a value specified in radians.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) sinh(float);
 FN_FUNC_FN(sinh)
 
+/**
+ * Return the sin(v * PI).
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) sinpi(float v);
 FN_FUNC_FN(sinpi)
 
-FN_FUNC_FN(sqrt)
-
-extern float __attribute__((overloadable)) tan(float);
+/**
+ * Return the tangent of a value.
+ *
+ * Supports 1,2,3,4 components
+ * @param v The incoming value in radians
+ */
+extern float __attribute__((overloadable)) tan(float v);
 FN_FUNC_FN(tan)
 
+/**
+ * Return the hyperbolic tangent of a value.
+ *
+ * Supports 1,2,3,4 components
+ * @param v The incoming value in radians
+ */
 extern float __attribute__((overloadable)) tanh(float);
 FN_FUNC_FN(tanh)
 
+/**
+ * Return tan(v * PI)
+ *
+ * Supports 1,2,3,4 components
+ */
 _RS_RUNTIME float __attribute__((overloadable)) tanpi(float v);
 FN_FUNC_FN(tanpi)
 
-
+/**
+ * Compute the gamma function of a value.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) tgamma(float);
 FN_FUNC_FN(tgamma)
 
+/**
+ * Round to integral using truncation.
+ *
+ * Supports 1,2,3,4 components
+ */
 extern float __attribute__((overloadable)) trunc(float);
 FN_FUNC_FN(trunc)
 
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index ddd9d1f..cd15718 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -2638,7 +2638,7 @@
                 notifyTopOfAudioFocusStack();
                 // there's a new top of the stack, let the remote control know
                 synchronized(mRCStack) {
-                    checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
+                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                 }
             }
         } else {
@@ -2681,7 +2681,7 @@
             notifyTopOfAudioFocusStack();
             // there's a new top of the stack, let the remote control know
             synchronized(mRCStack) {
-                checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
+                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
             }
         }
     }
@@ -2785,7 +2785,7 @@
 
             // there's a new top of the stack, let the remote control know
             synchronized(mRCStack) {
-                checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
+                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
             }
         }//synchronized(mAudioFocusLock)
 
@@ -3183,7 +3183,7 @@
      * Helper function:
      * Called synchronized on mRCStack
      */
-    private void clearRemoteControlDisplay_syncRcs() {
+    private void clearRemoteControlDisplay_syncAfRcs() {
         synchronized(mCurrentRcLock) {
             mCurrentRcClient = null;
         }
@@ -3192,18 +3192,21 @@
     }
 
     /**
-     * Helper function:
-     * Called synchronized on mRCStack
-     * mRCStack.isEmpty() is false
+     * Helper function for code readability: only to be called from
+     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
+     *    this method.
+     * Preconditions:
+     *    - called synchronized mAudioFocusLock then on mRCStack
+     *    - mRCStack.isEmpty() is false
      */
-    private void updateRemoteControlDisplay_syncRcs(int infoChangedFlags) {
+    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
         RemoteControlStackEntry rcse = mRCStack.peek();
         int infoFlagsAboutToBeUsed = infoChangedFlags;
         // this is where we enforce opt-in for information display on the remote controls
         //   with the new AudioManager.registerRemoteControlClient() API
         if (rcse.mRcClient == null) {
             //Log.w(TAG, "Can't update remote control display with null remote control client");
-            clearRemoteControlDisplay_syncRcs();
+            clearRemoteControlDisplay_syncAfRcs();
             return;
         }
         synchronized(mCurrentRcLock) {
@@ -3220,17 +3223,17 @@
 
     /**
      * Helper function:
-     * Called synchronized on mFocusLock, then mRCStack
+     * Called synchronized on mAudioFocusLock, then mRCStack
      * Check whether the remote control display should be updated, triggers the update if required
      * @param infoChangedFlags the flags corresponding to the remote control client information
      *     that has changed, if applicable (checking for the update conditions might trigger a
      *     clear, rather than an update event).
      */
-    private void checkUpdateRemoteControlDisplay_syncRcs(int infoChangedFlags) {
+    private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
         // determine whether the remote control display should be refreshed
         // if either stack is empty, there is a mismatch, so clear the RC display
         if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
-            clearRemoteControlDisplay_syncRcs();
+            clearRemoteControlDisplay_syncAfRcs();
             return;
         }
         // if the top of the two stacks belong to different packages, there is a mismatch, clear
@@ -3238,18 +3241,18 @@
                 && (mFocusStack.peek().mPackageName != null)
                 && !(mRCStack.peek().mCallingPackageName.compareTo(
                         mFocusStack.peek().mPackageName) == 0)) {
-            clearRemoteControlDisplay_syncRcs();
+            clearRemoteControlDisplay_syncAfRcs();
             return;
         }
         // if the audio focus didn't originate from the same Uid as the one in which the remote
         //   control information will be retrieved, clear
         if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
-            clearRemoteControlDisplay_syncRcs();
+            clearRemoteControlDisplay_syncAfRcs();
             return;
         }
         // refresh conditions were verified: update the remote controls
-        // ok to call, mRCStack is not empty
-        updateRemoteControlDisplay_syncRcs(infoChangedFlags);
+        // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
+        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
     }
 
     /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
@@ -3260,7 +3263,7 @@
             synchronized(mRCStack) {
                 pushMediaButtonReceiver(eventReceiver);
                 // new RC client, assume every type of information shall be queried
-                checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
+                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
             }
         }
     }
@@ -3275,7 +3278,7 @@
                 removeMediaButtonReceiver(eventReceiver);
                 if (topOfStackWillChange) {
                     // current RC client will change, assume every type of info needs to be queried
-                    checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
+                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                 }
             }
         }
@@ -3284,6 +3287,7 @@
     /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */
     public void registerRemoteControlClient(ComponentName eventReceiver,
             IRemoteControlClient rcClient, String clientName, String callingPackageName) {
+        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
         synchronized(mAudioFocusLock) {
             synchronized(mRCStack) {
                 // store the new display information
@@ -3331,7 +3335,7 @@
                 // if the eventReceiver is at the top of the stack
                 // then check for potential refresh of the remote controls
                 if (isCurrentRcController(eventReceiver)) {
-                    checkUpdateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
+                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
                 }
             }
         }
@@ -3436,35 +3440,35 @@
      */
     public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
         if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
-        synchronized(mRCStack) {
-            if ((mRcDisplay == rcd) || (rcd == null)) {
-                return;
-            }
-            // if we had a display before, stop monitoring its death
-            rcDisplay_stopDeathMonitor_syncRcStack();
-            mRcDisplay = rcd;
-            // new display, start monitoring its death
-            rcDisplay_startDeathMonitor_syncRcStack();
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                if ((mRcDisplay == rcd) || (rcd == null)) {
+                    return;
+                }
+                // if we had a display before, stop monitoring its death
+                rcDisplay_stopDeathMonitor_syncRcStack();
+                mRcDisplay = rcd;
+                // new display, start monitoring its death
+                rcDisplay_startDeathMonitor_syncRcStack();
 
-            // let all the remote control clients there is a new display
-            // no need to unplug the previous because we only support one display
-            // and the clients don't track the death of the display
-            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-            while(stackIterator.hasNext()) {
-                RemoteControlStackEntry rcse = stackIterator.next();
-                if(rcse.mRcClient != null) {
-                    try {
-                        rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error connecting remote control display to client: " + e);
-                        e.printStackTrace();
+                // let all the remote control clients there is a new display
+                // no need to unplug the previous because we only support one display
+                // and the clients don't track the death of the display
+                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                while(stackIterator.hasNext()) {
+                    RemoteControlStackEntry rcse = stackIterator.next();
+                    if(rcse.mRcClient != null) {
+                        try {
+                            rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error connecting remote control display to client: " + e);
+                            e.printStackTrace();
+                        }
                     }
                 }
-            }
 
-            if (!mRCStack.isEmpty()) {
                 // we have a new display, of which all the clients are now aware: have it be updated
-                updateRemoteControlDisplay_syncRcs(RC_INFO_ALL);
+                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
             }
         }
     }
diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index b7d129d..2b4e85f 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -29,6 +29,7 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
+import android.graphics.Matrix;
 import android.media.videoeditor.VideoEditor.ExportProgressListener;
 import android.media.videoeditor.VideoEditor.PreviewProgressListener;
 import android.media.videoeditor.VideoEditor.MediaProcessingProgressListener;
@@ -1050,6 +1051,10 @@
          */
          public int rgbWidth;
          public int rgbHeight;
+         /**
+         * Video rotation degree.
+         */
+         public int rotationDegree;
     }
 
     /**
@@ -1700,6 +1705,11 @@
          */
         public int audioVolumeValue;
 
+        /**
+         * Video rotation degree.
+         */
+        public int videoRotation;
+
         public String Id;
     }
 
@@ -2254,6 +2264,7 @@
         lclipSettings.panZoomTopLeftXEnd = 0;
         lclipSettings.panZoomTopLeftYEnd = 0;
         lclipSettings.mediaRendering = 0;
+        lclipSettings.rotationDegree = 0;
     }
 
 
@@ -3784,7 +3795,8 @@
      **/
     void getPixelsList(String filename, final int width, final int height,
             long startMs, long endMs, int thumbnailCount, int[] indices,
-            final MediaItem.GetThumbnailListCallback callback) {
+            final MediaItem.GetThumbnailListCallback callback,
+            final int videoRotation) {
         /* Make width and height as even */
         final int newWidth = (width + 1) & 0xFFFFFFFE;
         final int newHeight = (height + 1) & 0xFFFFFFFE;
@@ -3799,7 +3811,7 @@
         final int[] rgb888 = new int[thumbnailSize];
         final IntBuffer tmpBuffer = IntBuffer.allocate(thumbnailSize);
         nativeGetPixelsList(filename, rgb888, newWidth, newHeight,
-                thumbnailCount, startMs, endMs, indices,
+                thumbnailCount, videoRotation, startMs, endMs, indices,
                 new NativeGetPixelsListCallback() {
             public void onThumbnail(int index) {
                 Bitmap bitmap = Bitmap.createBitmap(
@@ -3821,7 +3833,21 @@
 
                     canvas.setBitmap(null);
                 }
-                callback.onThumbnail(bitmap, index);
+
+                if (videoRotation == 0) {
+                    callback.onThumbnail(bitmap, index);
+                } else {
+                    Matrix mtx = new Matrix();
+                    mtx.postRotate(videoRotation);
+                    Bitmap rotatedBmp =
+                        Bitmap.createBitmap(bitmap, 0, 0, width, height, mtx, false);
+                    callback.onThumbnail(rotatedBmp, index);
+
+                    if (bitmap != null) {
+                        bitmap.recycle();
+                    }
+                }
+
             }
         });
 
@@ -3943,8 +3969,8 @@
             long timeMS);
 
     private native int nativeGetPixelsList(String fileName, int[] pixelArray,
-            int width, int height, int nosofTN, long startTimeMs, long endTimeMs,
-            int[] indices, NativeGetPixelsListCallback callback);
+            int width, int height, int nosofTN, int videoRotation, long startTimeMs,
+            long endTimeMs, int[] indices, NativeGetPixelsListCallback callback);
 
     /**
      * Releases the JNI and cleans up the core native module.. Should be called
diff --git a/media/java/android/media/videoeditor/MediaImageItem.java b/media/java/android/media/videoeditor/MediaImageItem.java
index b2a279a..65a9e19 100755
--- a/media/java/android/media/videoeditor/MediaImageItem.java
+++ b/media/java/android/media/videoeditor/MediaImageItem.java
@@ -638,7 +638,7 @@
             }
 
             mMANativeHelper.getPixelsList(getGeneratedImageClip(), width,
-                height, startMs, endMs, thumbnailCount, indices, callback);
+                height, startMs, endMs, thumbnailCount, indices, callback, 0);
         }
     }
 
diff --git a/media/java/android/media/videoeditor/MediaVideoItem.java b/media/java/android/media/videoeditor/MediaVideoItem.java
index fea751b..2ce857c 100755
--- a/media/java/android/media/videoeditor/MediaVideoItem.java
+++ b/media/java/android/media/videoeditor/MediaVideoItem.java
@@ -57,6 +57,7 @@
     private String mAudioWaveformFilename;
     private MediaArtistNativeHelper mMANativeHelper;
     private VideoEditorImpl mVideoEditor;
+    private final int mVideoRotationDegree;
     /**
      *  The audio waveform data
      */
@@ -190,6 +191,7 @@
         } else {
             mWaveformData = null;
         }
+        mVideoRotationDegree = properties.videoRotation;
     }
 
     /**
@@ -317,7 +319,8 @@
         }
 
         mMANativeHelper.getPixelsList(super.getFilename(), width,
-                height, startMs, endMs, thumbnailCount, indices, callback);
+                height, startMs, endMs, thumbnailCount, indices, callback,
+                mVideoRotationDegree);
     }
 
     /*
@@ -425,7 +428,12 @@
      */
     @Override
     public int getWidth() {
-        return mWidth;
+        if (mVideoRotationDegree == 90 ||
+             mVideoRotationDegree == 270) {
+            return mHeight;
+        } else {
+            return mWidth;
+        }
     }
 
     /*
@@ -433,7 +441,12 @@
      */
     @Override
     public int getHeight() {
-        return mHeight;
+        if (mVideoRotationDegree == 90 ||
+             mVideoRotationDegree == 270) {
+            return mWidth;
+        } else {
+            return mHeight;
+        }
     }
 
     /*
@@ -725,6 +738,7 @@
         clipSettings.beginCutTime = (int)getBoundaryBeginTime();
         clipSettings.endCutTime = (int)getBoundaryEndTime();
         clipSettings.mediaRendering = mMANativeHelper.getMediaItemRenderingMode(getRenderingMode());
+        clipSettings.rotationDegree = mVideoRotationDegree;
 
         return clipSettings;
     }
diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp
index 69735ca..4e0e0f2 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.cpp
+++ b/media/jni/mediaeditor/VideoEditorClasses.cpp
@@ -490,7 +490,8 @@
     VIDEOEDIT_JAVA_FIELD_INIT("audioDuration",          "I"),
     VIDEOEDIT_JAVA_FIELD_INIT("audioBitrate",           "I"),
     VIDEOEDIT_JAVA_FIELD_INIT("audioChannels",          "I"),
-    VIDEOEDIT_JAVA_FIELD_INIT("audioSamplingFrequency", "I")
+    VIDEOEDIT_JAVA_FIELD_INIT("audioSamplingFrequency", "I"),
+    VIDEOEDIT_JAVA_FIELD_INIT("videoRotation",          "I")
 };
 
 VIDEOEDIT_JAVA_DEFINE_FIELD_CLASS(Properties, PROPERTIES_CLASS_NAME)
@@ -540,7 +541,8 @@
     VIDEOEDIT_JAVA_FIELD_INIT("panZoomTopLeftYEnd",   "I"                 ),
     VIDEOEDIT_JAVA_FIELD_INIT("mediaRendering",       "I"                 ),
     VIDEOEDIT_JAVA_FIELD_INIT("rgbWidth",           "I"                 ),
-    VIDEOEDIT_JAVA_FIELD_INIT("rgbHeight",          "I"                 )
+    VIDEOEDIT_JAVA_FIELD_INIT("rgbHeight",          "I"                 ),
+    VIDEOEDIT_JAVA_FIELD_INIT("rotationDegree",     "I"                 )
 };
 
 VIDEOEDIT_JAVA_DEFINE_FIELD_CLASS(ClipSettings, CLIP_SETTINGS_CLASS_NAME)
@@ -1402,6 +1404,10 @@
             VIDEOEDIT_LOG_FUNCTION(ANDROID_LOG_INFO, "VIDEO_EDITOR", \
                 "getClipSettings-- rgbFileHeight %d ",
                 pSettings->ClipProperties.uiStillPicHeight);
+
+            // Set the video rotation degree
+            pSettings->ClipProperties.videoRotationDegrees =
+                (M4OSA_UInt32)pEnv->GetIntField(object, fieldIds.rotationDegree);
         }
 
         // Check if settings could be set.
@@ -1513,6 +1519,10 @@
                 pSettings->ClipProperties.uiStillPicWidth ,
                 pSettings->ClipProperties.uiStillPicHeight);
 
+            // Set the video rotation
+            pEnv->SetIntField(object, fieldIds.rotationDegree,
+                pSettings->ClipProperties.videoRotationDegrees);
+
             // Return the object.
             (*pObject) = object;
         }
@@ -1609,6 +1619,9 @@
             pEnv->SetIntField(object, fieldIds.audioSamplingFrequency,
                 pProperties->uiSamplingFrequency);
 
+            // Set the video rotation field.
+            pEnv->SetIntField(object, fieldIds.videoRotation, pProperties->uiRotation);
+
             // Return the object.
             (*pObject) = object;
         }
diff --git a/media/jni/mediaeditor/VideoEditorClasses.h b/media/jni/mediaeditor/VideoEditorClasses.h
index 3c10b1d..a4c82a8 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.h
+++ b/media/jni/mediaeditor/VideoEditorClasses.h
@@ -145,6 +145,7 @@
     M4OSA_UInt32 uiAudioBitrate;
     M4OSA_UInt32 uiNbChannels;
     M4OSA_UInt32 uiSamplingFrequency;
+    M4OSA_UInt32 uiRotation;
 } VideoEditPropClass_Properties;
 
 typedef struct
@@ -166,6 +167,7 @@
     jfieldID audioBitrate;
     jfieldID audioChannels;
     jfieldID audioSamplingFrequency;
+    jfieldID videoRotation;
 } VideoEditJava_PropertiesFieldIds;
 
 
@@ -187,6 +189,7 @@
     jfieldID mediaRendering;
     jfieldID rgbFileWidth;
     jfieldID rgbFileHeight;
+    jfieldID rotationDegree;
 } VideoEditJava_ClipSettingsFieldIds;
 
 typedef struct
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index b737e5d..4e73581 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -185,6 +185,7 @@
                                      M4OSA_UInt32             width,
                                      M4OSA_UInt32             height,
                                      M4OSA_UInt32             noOfThumbnails,
+                                     M4OSA_UInt32             videoRotation,
                                      jlong                    startTime,
                                      jlong                    endTime,
                                      jintArray                indexArray,
@@ -291,7 +292,7 @@
                                 (void *)videoEditor_release            },
     {"nativeGetPixels",         "(Ljava/lang/String;[IIIJ)I",
                                 (void*)videoEditor_getPixels               },
-    {"nativeGetPixelsList",     "(Ljava/lang/String;[IIIIJJ[ILandroid/media/videoeditor/MediaArtistNativeHelper$NativeGetPixelsListCallback;)I",
+    {"nativeGetPixelsList",     "(Ljava/lang/String;[IIIIIJJ[ILandroid/media/videoeditor/MediaArtistNativeHelper$NativeGetPixelsListCallback;)I",
                                 (void*)videoEditor_getPixelsList           },
     {"getMediaProperties",
     "(Ljava/lang/String;)Landroid/media/videoeditor/MediaArtistNativeHelper$Properties;",
@@ -375,6 +376,12 @@
                     pEnv->GetIntField(object,fid);
    M4OSA_TRACE1_1("audioVolumeValue = %d",
                     pSettings->ClipProperties.uiClipAudioVolumePercentage);
+
+   fid = pEnv->GetFieldID(clazz,"videoRotation","I");
+   pSettings->ClipProperties.videoRotationDegrees =
+                    pEnv->GetIntField(object,fid);
+   M4OSA_TRACE1_1("videoRotation = %d",
+                    pSettings->ClipProperties.videoRotationDegrees);
 }
 
 static void jniPreviewProgressCallback (void* cookie, M4OSA_UInt32 msgType,
@@ -789,6 +796,8 @@
             pContext->pEditSettings->pClipList[iCurrentClipIndex]->ClipProperties.uiVideoWidth,
             (M4OSA_Void **)&frameStr.pBuffer);
             tnTimeMs = (M4OSA_UInt32)timeMs;
+
+          frameStr.videoRotationDegree = 0;
     } else {
         /* Handle 3gp/mp4 Clips here */
         /* get thumbnail*/
@@ -913,6 +922,9 @@
 
         /* Fill up the render structure*/
         frameStr.pBuffer = (M4OSA_Void*)yuvPlane[0].pac_data;
+
+        frameStr.videoRotationDegree = pContext->pEditSettings->\
+            pClipList[iCurrentClipIndex]->ClipProperties.videoRotationDegrees;
     }
 
     frameStr.timeMs = timeMs;    /* timestamp on storyboard*/
@@ -976,13 +988,12 @@
     videoEditJava_checkAndThrowRuntimeException(&needToBeLoaded, pEnv,
             (M4NO_ERROR != result), result);
 
-    if (pContext->pEditSettings->pClipList[iCurrentClipIndex]->FileType ==\
-         /*M4VIDEOEDITING_kFileType_JPG */ M4VIDEOEDITING_kFileType_ARGB8888) {
-            free(frameStr.pBuffer);
-    } else {
-        free(yuvPlane[0].pac_data);
+    free(frameStr.pBuffer);
+    if (pContext->pEditSettings->pClipList[iCurrentClipIndex]->FileType !=
+            M4VIDEOEDITING_kFileType_ARGB8888) {
         free(yuvPlane);
     }
+
     return tnTimeMs;
 }
 
@@ -2275,6 +2286,7 @@
                 M4OSA_UInt32            width,
                 M4OSA_UInt32            height,
                 M4OSA_UInt32            noOfThumbnails,
+                M4OSA_UInt32            videoRotation,
                 jlong                   startTime,
                 jlong                   endTime,
                 jintArray               indexArray,
diff --git a/media/jni/mediaeditor/VideoEditorMain.h b/media/jni/mediaeditor/VideoEditorMain.h
index ca4a945..4c3b517 100755
--- a/media/jni/mediaeditor/VideoEditorMain.h
+++ b/media/jni/mediaeditor/VideoEditorMain.h
@@ -71,6 +71,7 @@
     M4OSA_Bool      bApplyEffect;        /* Apply video effects before render*/
     M4OSA_UInt32    clipBeginCutTime;  /* Clip begin cut time relative to storyboard */
     M4OSA_UInt32    clipEndCutTime;    /* Clip end cut time relative to storyboard */
+    M4OSA_UInt32    videoRotationDegree; /* Video rotation degree */
 
 } VideoEditor_renderPreviewFrameStr;
 #endif /*__VIDEO_EDITOR_API_H__*/
diff --git a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
index 2ca3a08..c8fb263 100755
--- a/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorPropertiesMain.cpp
@@ -258,6 +258,8 @@
                 pProperties->uiAudioBitrate      = pClipProperties->uiAudioBitrate;
                 pProperties->uiNbChannels        = pClipProperties->uiNbChannels;
                 pProperties->uiSamplingFrequency = pClipProperties->uiSamplingFrequency;
+                pProperties->uiRotation          = pClipProperties->videoRotationDegrees;
+
             }
 
             // Free the clip properties.
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index 0251baf..605d056 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -124,7 +124,14 @@
                             : ATSParser::DISCONTINUITY_FORMATCHANGE,
                         extra);
             } else {
-                mTSParser->feedTSPacket(buffer, sizeof(buffer));
+                status_t err = mTSParser->feedTSPacket(buffer, sizeof(buffer));
+
+                if (err != OK) {
+                    LOGE("TS Parser returned error %d", err);
+                    mTSParser->signalEOS(err);
+                    mEOS = true;
+                    break;
+                }
             }
 
             mOffset += n;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 1f08a91..ee77f47 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -663,6 +663,19 @@
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
 
+    if (IsFlushingState(audio ? mFlushingAudio : mFlushingVideo)) {
+        // We're currently attempting to flush the decoder, in order
+        // to complete this, the decoder wants all its buffers back,
+        // so we don't want any output buffers it sent us (from before
+        // we initiated the flush) to be stuck in the renderer's queue.
+
+        LOGV("we're still flushing the %s decoder, sending its output buffer"
+             " right back.", audio ? "audio" : "video");
+
+        reply->post();
+        return;
+    }
+
     sp<RefBase> obj;
     CHECK(msg->findObject("buffer", &obj));
 
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index f41e9d2..a741987 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -86,7 +86,15 @@
                             : ATSParser::DISCONTINUITY_FORMATCHANGE,
                         extra);
             } else {
-                mTSParser->feedTSPacket(buffer, sizeof(buffer));
+                status_t err = mTSParser->feedTSPacket(buffer, sizeof(buffer));
+
+                if (err != OK) {
+                    LOGE("TS Parser returned error %d", err);
+
+                    mTSParser->signalEOS(err);
+                    mEOS = true;
+                    break;
+                }
             }
         }
     }
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 7f09319..d5b013d 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1990,7 +1990,7 @@
     CHECK(mIsEncoder);
 
     if (mDecodingTimeList.empty()) {
-        CHECK(mNoMoreOutputData);
+        CHECK(mSignalledEOS || mNoMoreOutputData);
         // No corresponding input frame available.
         // This could happen when EOS is reached.
         return 0;
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index 91b81c2..50dd804 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -46,9 +46,10 @@
                 mSynchronousMode(true),
                 mConnectedApi(NO_CONNECTED_API),
                 mFrameRate(30),
+                mStopped(false),
                 mNumFramesReceived(0),
                 mNumFramesEncoded(0),
-                mStopped(false) {
+                mFirstFrameTimestamp(0) {
     LOGV("SurfaceMediaSource::SurfaceMediaSource");
     sp<ISurfaceComposer> composer(ComposerService::getComposerService());
     mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
@@ -471,10 +472,25 @@
         return -EINVAL;
     }
 
+    if (mNumFramesReceived == 0) {
+        mFirstFrameTimestamp = timestamp;
+        // Initial delay
+        if (mStartTimeNs > 0) {
+            if (timestamp < mStartTimeNs) {
+                // This frame predates start of record, discard
+                mSlots[bufIndex].mBufferState = BufferSlot::FREE;
+                mDequeueCondition.signal();
+                return OK;
+            }
+            mStartTimeNs = timestamp - mStartTimeNs;
+        }
+    }
+    timestamp = mStartTimeNs + (timestamp - mFirstFrameTimestamp);
+
+    mNumFramesReceived++;
     if (mSynchronousMode) {
         // in synchronous mode we queue all buffers in a FIFO
         mQueue.push_back(bufIndex);
-        mNumFramesReceived++;
         LOGV("Client queued buf# %d @slot: %d, Q size = %d, handle = %p, timestamp = %lld",
             mNumFramesReceived, bufIndex, mQueue.size(),
             mSlots[bufIndex].mGraphicBuffer->handle, timestamp);
@@ -684,6 +700,13 @@
 status_t SurfaceMediaSource::start(MetaData *params)
 {
     LOGV("started!");
+
+    mStartTimeNs = 0;
+    int64_t startTimeUs;
+    if (params && params->findInt64(kKeyTime, &startTimeUs)) {
+        mStartTimeNs = startTimeUs * 1000;
+    }
+
     return OK;
 }
 
@@ -753,6 +776,7 @@
     mCurrentBuf = mSlots[mCurrentSlot].mGraphicBuffer;
     int64_t prevTimeStamp = mCurrentTimestamp;
     mCurrentTimestamp = mSlots[mCurrentSlot].mTimestamp;
+
     mNumFramesEncoded++;
     // Pass the data to the MediaBuffer. Pass in only the metadata
     passMetadataBufferLocked(buffer);
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 74a3b32..017d01c 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -48,7 +48,7 @@
 
     bool parsePID(
             unsigned pid, unsigned payload_unit_start_indicator,
-            ABitReader *br);
+            ABitReader *br, status_t *err);
 
     void signalDiscontinuity(
             DiscontinuityType type, const sp<AMessage> &extra);
@@ -77,7 +77,7 @@
     bool mFirstPTSValid;
     uint64_t mFirstPTS;
 
-    void parseProgramMap(ABitReader *br);
+    status_t parseProgramMap(ABitReader *br);
 
     DISALLOW_EVIL_CONSTRUCTORS(Program);
 };
@@ -111,8 +111,6 @@
     sp<ABuffer> mBuffer;
     sp<AnotherPacketSource> mSource;
     bool mPayloadStarted;
-    DiscontinuityType mPendingDiscontinuity;
-    sp<AMessage> mPendingDiscontinuityExtra;
 
     ElementaryStreamQueue *mQueue;
 
@@ -125,9 +123,6 @@
 
     void extractAACFrames(const sp<ABuffer> &buffer);
 
-    void deferDiscontinuity(
-            DiscontinuityType type, const sp<AMessage> &extra);
-
     DISALLOW_EVIL_CONSTRUCTORS(Stream);
 };
 
@@ -145,14 +140,17 @@
 
 bool ATSParser::Program::parsePID(
         unsigned pid, unsigned payload_unit_start_indicator,
-        ABitReader *br) {
+        ABitReader *br, status_t *err) {
+    *err = OK;
+
     if (pid == mProgramMapPID) {
         if (payload_unit_start_indicator) {
             unsigned skip = br->getBits(8);
             br->skipBits(skip * 8);
         }
 
-        parseProgramMap(br);
+        *err = parseProgramMap(br);
+
         return true;
     }
 
@@ -185,7 +183,7 @@
     unsigned mPID;
 };
 
-void ATSParser::Program::parseProgramMap(ABitReader *br) {
+status_t ATSParser::Program::parseProgramMap(ABitReader *br) {
     unsigned table_id = br->getBits(8);
     LOGV("  table_id = %u", table_id);
     CHECK_EQ(table_id, 0x02u);
@@ -288,7 +286,60 @@
     }
 
     if (PIDsChanged) {
-        mStreams.clear();
+#if 0
+        LOGI("before:");
+        for (size_t i = 0; i < mStreams.size(); ++i) {
+            sp<Stream> stream = mStreams.editValueAt(i);
+
+            LOGI("PID 0x%08x => type 0x%02x", stream->pid(), stream->type());
+        }
+
+        LOGI("after:");
+        for (size_t i = 0; i < infos.size(); ++i) {
+            StreamInfo &info = infos.editItemAt(i);
+
+            LOGI("PID 0x%08x => type 0x%02x", info.mPID, info.mType);
+        }
+#endif
+
+        // The only case we can recover from is if we have two streams
+        // and they switched PIDs.
+
+        bool success = false;
+
+        if (mStreams.size() == 2 && infos.size() == 2) {
+            const StreamInfo &info1 = infos.itemAt(0);
+            const StreamInfo &info2 = infos.itemAt(1);
+
+            sp<Stream> s1 = mStreams.editValueAt(0);
+            sp<Stream> s2 = mStreams.editValueAt(1);
+
+            bool caseA =
+                info1.mPID == s1->pid() && info1.mType == s2->type()
+                    && info2.mPID == s2->pid() && info2.mType == s1->type();
+
+            bool caseB =
+                info1.mPID == s2->pid() && info1.mType == s1->type()
+                    && info2.mPID == s1->pid() && info2.mType == s2->type();
+
+            if (caseA || caseB) {
+                unsigned pid1 = s1->pid();
+                unsigned pid2 = s2->pid();
+                s1->setPID(pid2);
+                s2->setPID(pid1);
+
+                mStreams.clear();
+                mStreams.add(s1->pid(), s1);
+                mStreams.add(s2->pid(), s2);
+
+                success = true;
+            }
+        }
+
+        if (!success) {
+            LOGI("Stream PIDs changed and we cannot recover.");
+            return ERROR_MALFORMED;
+        }
     }
 
     for (size_t i = 0; i < infos.size(); ++i) {
@@ -299,13 +350,10 @@
         if (index < 0) {
             sp<Stream> stream = new Stream(this, info.mPID, info.mType);
             mStreams.add(info.mPID, stream);
-
-            if (PIDsChanged) {
-                sp<AMessage> extra;
-                stream->signalDiscontinuity(DISCONTINUITY_FORMATCHANGE, extra);
-            }
         }
     }
+
+    return OK;
 }
 
 sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
@@ -347,12 +395,8 @@
     : mProgram(program),
       mElementaryPID(elementaryPID),
       mStreamType(streamType),
-      mBuffer(new ABuffer(192 * 1024)),
       mPayloadStarted(false),
-      mPendingDiscontinuity(DISCONTINUITY_NONE),
       mQueue(NULL) {
-    mBuffer->setRange(0, 0);
-
     switch (mStreamType) {
         case STREAMTYPE_H264:
             mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::H264);
@@ -382,6 +426,11 @@
     }
 
     LOGV("new stream PID 0x%02x, type 0x%02x", elementaryPID, streamType);
+
+    if (mQueue != NULL) {
+        mBuffer = new ABuffer(192 * 1024);
+        mBuffer->setRange(0, 0);
+    }
 }
 
 ATSParser::Stream::~Stream() {
@@ -391,6 +440,10 @@
 
 void ATSParser::Stream::parse(
         unsigned payload_unit_start_indicator, ABitReader *br) {
+    if (mQueue == NULL) {
+        return;
+    }
+
     if (payload_unit_start_indicator) {
         if (mPayloadStarted) {
             // Otherwise we run the danger of receiving the trailing bytes
@@ -429,6 +482,10 @@
 
 void ATSParser::Stream::signalDiscontinuity(
         DiscontinuityType type, const sp<AMessage> &extra) {
+    if (mQueue == NULL) {
+        return;
+    }
+
     mPayloadStarted = false;
     mBuffer->setRange(0, 0);
 
@@ -453,8 +510,6 @@
 
             if (mSource != NULL) {
                 mSource->queueDiscontinuity(type, extra);
-            } else {
-                deferDiscontinuity(type, extra);
             }
             break;
         }
@@ -465,15 +520,6 @@
     }
 }
 
-void ATSParser::Stream::deferDiscontinuity(
-        DiscontinuityType type, const sp<AMessage> &extra) {
-    if (type > mPendingDiscontinuity) {
-        // Only upgrade discontinuities.
-        mPendingDiscontinuity = type;
-        mPendingDiscontinuityExtra = extra;
-    }
-}
-
 void ATSParser::Stream::signalEOS(status_t finalResult) {
     if (mSource != NULL) {
         mSource->signalEOS(finalResult);
@@ -658,10 +704,6 @@
         const uint8_t *data, size_t size) {
     LOGV("onPayloadData mStreamType=0x%02x", mStreamType);
 
-    if (mQueue == NULL) {
-        return;
-    }
-
     CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
     int64_t timeUs = mProgram->convertPTSToTimestamp(PTS);
 
@@ -681,14 +723,6 @@
                      mElementaryPID, mStreamType);
 
                 mSource = new AnotherPacketSource(meta);
-
-                if (mPendingDiscontinuity != DISCONTINUITY_NONE) {
-                    mSource->queueDiscontinuity(
-                            mPendingDiscontinuity, mPendingDiscontinuityExtra);
-                    mPendingDiscontinuity = DISCONTINUITY_NONE;
-                    mPendingDiscontinuityExtra.clear();
-                }
-
                 mSource->queueAccessUnit(accessUnit);
             }
         } else if (mQueue->getFormat() != NULL) {
@@ -743,11 +777,11 @@
 ATSParser::~ATSParser() {
 }
 
-void ATSParser::feedTSPacket(const void *data, size_t size) {
+status_t ATSParser::feedTSPacket(const void *data, size_t size) {
     CHECK_EQ(size, kTSPacketSize);
 
     ABitReader br((const uint8_t *)data, kTSPacketSize);
-    parseTS(&br);
+    return parseTS(&br);
 }
 
 void ATSParser::signalDiscontinuity(
@@ -825,7 +859,7 @@
     MY_LOGV("  CRC = 0x%08x", br->getBits(32));
 }
 
-void ATSParser::parsePID(
+status_t ATSParser::parsePID(
         ABitReader *br, unsigned PID,
         unsigned payload_unit_start_indicator) {
     if (PID == 0) {
@@ -834,13 +868,18 @@
             br->skipBits(skip * 8);
         }
         parseProgramAssociationTable(br);
-        return;
+        return OK;
     }
 
     bool handled = false;
     for (size_t i = 0; i < mPrograms.size(); ++i) {
+        status_t err;
         if (mPrograms.editItemAt(i)->parsePID(
-                    PID, payload_unit_start_indicator, br)) {
+                    PID, payload_unit_start_indicator, br, &err)) {
+            if (err != OK) {
+                return err;
+            }
+
             handled = true;
             break;
         }
@@ -849,6 +888,8 @@
     if (!handled) {
         LOGV("PID 0x%04x not handled.", PID);
     }
+
+    return OK;
 }
 
 void ATSParser::parseAdaptationField(ABitReader *br) {
@@ -858,7 +899,7 @@
     }
 }
 
-void ATSParser::parseTS(ABitReader *br) {
+status_t ATSParser::parseTS(ABitReader *br) {
     LOGV("---");
 
     unsigned sync_byte = br->getBits(8);
@@ -889,8 +930,10 @@
     }
 
     if (adaptation_field_control == 1 || adaptation_field_control == 3) {
-        parsePID(br, PID, payload_unit_start_indicator);
+        return parsePID(br, PID, payload_unit_start_indicator);
     }
+
+    return OK;
 }
 
 sp<MediaSource> ATSParser::getSource(SourceType type) {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index d12d998..388cb54 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -49,7 +49,7 @@
 
     ATSParser(uint32_t flags = 0);
 
-    void feedTSPacket(const void *data, size_t size);
+    status_t feedTSPacket(const void *data, size_t size);
 
     void signalDiscontinuity(
             DiscontinuityType type, const sp<AMessage> &extra);
@@ -89,12 +89,12 @@
     void parseProgramMap(ABitReader *br);
     void parsePES(ABitReader *br);
 
-    void parsePID(
+    status_t parsePID(
         ABitReader *br, unsigned PID,
         unsigned payload_unit_start_indicator);
 
     void parseAdaptationField(ABitReader *br);
-    void parseTS(ABitReader *br);
+    status_t parseTS(ABitReader *br);
 
     DISALLOW_EVIL_CONSTRUCTORS(ATSParser);
 };
diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
index 8250ad1..17cf45a 100644
--- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
+++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
@@ -210,13 +210,10 @@
 
     if (n < (ssize_t)kTSPacketSize) {
         return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
-    } else {
-        mParser->feedTSPacket(packet, kTSPacketSize);
     }
 
     mOffset += n;
-
-    return OK;
+    return mParser->feedTSPacket(packet, kTSPacketSize);
 }
 
 void MPEG2TSExtractor::setLiveSession(const sp<LiveSession> &liveSession) {
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index d643a0b..d663602 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -106,13 +106,14 @@
             mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
                     window.get(), NULL);
         } else {
-            EGLint pbufferAttribs[] = {
-                EGL_WIDTH, getSurfaceWidth(),
-                EGL_HEIGHT, getSurfaceHeight(),
-                EGL_NONE };
+            LOGV("No actual display. Choosing EGLSurface based on SurfaceMediaSource");
+            sp<SurfaceMediaSource> sms = new SurfaceMediaSource(
+                    getSurfaceWidth(), getSurfaceHeight());
+            sp<SurfaceTextureClient> stc = new SurfaceTextureClient(sms);
+            sp<ANativeWindow> window = stc;
 
-            mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig,
-                    pbufferAttribs);
+            mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
+                    window.get(), NULL);
         }
         ASSERT_EQ(EGL_SUCCESS, eglGetError());
         ASSERT_NE(EGL_NO_SURFACE, mEglSurface);
@@ -408,7 +409,6 @@
         mSTC.clear();
         mANW.clear();
         GLTest::TearDown();
-        eglDestroySurface(mEglDisplay, mSmsEglSurface);
     }
 
     void setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr);
@@ -419,8 +419,6 @@
     sp<SurfaceMediaSource> mSMS;
     sp<SurfaceTextureClient> mSTC;
     sp<ANativeWindow> mANW;
-    EGLConfig  mSMSGlConfig;
-    EGLSurface  mSmsEglSurface;
 };
 
 /////////////////////////////////////////////////////////////////////
@@ -462,7 +460,7 @@
     glClear(GL_COLOR_BUFFER_BIT);
 
     // The following call dequeues and queues the buffer
-    eglSwapBuffers(mEglDisplay, mSmsEglSurface);
+    eglSwapBuffers(mEglDisplay, mEglSurface);
     glDisable(GL_SCISSOR_TEST);
 }
 
@@ -488,19 +486,12 @@
     mSTC = new SurfaceTextureClient(iST);
     mANW = mSTC;
 
-    EGLint numConfigs = 0;
-    EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig,
-            1, &numConfigs));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    LOGV("Native Window = %p, mSTC = %p", mANW.get(), mSTC.get());
-
-    mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig,
+    mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
                                 mANW.get(), NULL);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ;
+    ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ;
 
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface,
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
             mEglContext));
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 }
@@ -778,9 +769,9 @@
 
 // Test to examine whether we can choose the Recordable Android GLConfig
 // DummyRecorder used- no real encoding here
-TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWrite) {
+TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWriter) {
     LOGV("Test # %d", testId++);
-    LOGV("Test to verify creating a surface w/ right config *********");
+    LOGV("Verify creating a surface w/ right config + dummy writer*********");
 
     mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight);
     mSTC = new SurfaceTextureClient(mSMS);
@@ -789,17 +780,12 @@
     DummyRecorder writer(mSMS);
     writer.start();
 
-    EGLint numConfigs = 0;
-    EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig,
-            1, &numConfigs));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig,
+    mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
                                 mANW.get(), NULL);
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ;
+    ASSERT_NE(EGL_NO_SURFACE, mEglSurface) ;
 
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface,
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
             mEglContext));
     ASSERT_EQ(EGL_SUCCESS, eglGetError());
 
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
index 16008a3..42db77e 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_item.xml
@@ -19,70 +19,77 @@
 -->
 
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
-<RelativeLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="wrap_content"
-    android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+    android:layout_height="match_parent"
+    android:layout_width="wrap_content">
 
-    <FrameLayout android:id="@+id/app_thumbnail"
-        android:layout_width="wrap_content"
+    <RelativeLayout android:id="@+id/recent_item"
+        android:layout_gravity="bottom"
         android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
-        android:scaleType="center"
-        android:clickable="true"
-        android:background="@drawable/recents_thumbnail_bg"
-        android:foreground="@drawable/recents_thumbnail_overlay">
-        <ImageView android:id="@+id/app_thumbnail_image"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+        android:paddingBottom="@*android:dimen/status_bar_height">
+
+        <FrameLayout android:id="@+id/app_thumbnail"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
+            android:scaleType="center"
+            android:clickable="true"
+            android:background="@drawable/recents_thumbnail_bg"
+            android:foreground="@drawable/recents_thumbnail_overlay">
+            <ImageView android:id="@+id/app_thumbnail_image"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="invisible"
+            />
+        </FrameLayout>
+
+        <ImageView android:id="@+id/app_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignLeft="@id/app_thumbnail"
+            android:layout_alignTop="@id/app_thumbnail"
+            android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
+            android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
+            android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
+            android:adjustViewBounds="true"
             android:visibility="invisible"
         />
-    </FrameLayout>
 
-    <ImageView android:id="@+id/app_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignLeft="@id/app_thumbnail"
-        android:layout_alignTop="@id/app_thumbnail"
-        android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
-        android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
-        android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
-        android:adjustViewBounds="true"
-        android:visibility="invisible"
-    />
+        <TextView android:id="@+id/app_label"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/status_bar_recents_app_label_text_size"
+            android:fadingEdge="horizontal"
+            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:scrollHorizontally="true"
+            android:layout_alignLeft="@id/app_thumbnail"
+            android:layout_below="@id/app_thumbnail"
+            android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
+            android:layout_marginLeft="@dimen/recents_thumbnail_bg_padding_left"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:visibility="invisible"
+        />
 
-    <TextView android:id="@+id/app_label"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="@dimen/status_bar_recents_app_label_text_size"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
-        android:scrollHorizontally="true"
-        android:layout_alignLeft="@id/app_thumbnail"
-        android:layout_below="@id/app_thumbnail"
-        android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
-        android:layout_marginLeft="@dimen/recents_thumbnail_bg_padding_left"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-        android:visibility="invisible"
-    />
+        <TextView android:id="@+id/app_description"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/status_bar_recents_app_description_text_size"
+            android:fadingEdge="horizontal"
+            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:scrollHorizontally="true"
+            android:layout_alignLeft="@id/app_thumbnail"
+            android:layout_below="@id/app_label"
+            android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
+            android:layout_marginLeft="@dimen/recents_thumbnail_bg_padding_left"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+        />
 
-    <TextView android:id="@+id/app_description"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textSize="@dimen/status_bar_recents_app_description_text_size"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
-        android:scrollHorizontally="true"
-        android:layout_alignLeft="@id/app_thumbnail"
-        android:layout_below="@id/app_label"
-        android:layout_marginTop="@dimen/status_bar_recents_text_description_padding"
-        android:layout_marginLeft="@dimen/recents_thumbnail_bg_padding_left"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-    />
-
-</RelativeLayout>
+    </RelativeLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
index 20ef7cf..f84cc19 100644
--- a/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-land/status_bar_recent_panel.xml
@@ -30,13 +30,12 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:layout_alignParentBottom="true"
-        android:paddingBottom="@*android:dimen/status_bar_height"
         android:clipToPadding="false"
         android:clipChildren="false">
 
         <LinearLayout android:id="@+id/recents_glow"
             android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_height="match_parent"
             android:layout_gravity="bottom|right"
             android:orientation="horizontal"
             android:clipToPadding="false"
@@ -44,7 +43,7 @@
             >
             <com.android.systemui.recent.RecentsHorizontalScrollView android:id="@+id/recents_container"
                 android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
+                android:layout_height="match_parent"
                 android:layout_marginRight="@dimen/status_bar_recents_right_glow_margin"
                 android:divider="@null"
                 android:stackFromBottom="true"
@@ -58,7 +57,7 @@
 
                 <LinearLayout android:id="@+id/recents_linear_layout"
                     android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
+                    android:layout_height="match_parent"
                     android:orientation="horizontal"
                     android:clipToPadding="false"
                     android:clipChildren="false">
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
index c0fce71..f6b72d4 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_item.xml
@@ -19,79 +19,84 @@
 -->
 
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
-<RelativeLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
-    android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+    android:layout_width="match_parent">
 
-    <FrameLayout android:id="@+id/app_thumbnail"
-        android:layout_width="wrap_content"
+    <RelativeLayout android:id="@+id/recent_item"
         android:layout_height="wrap_content"
-        android:layout_alignParentLeft="true"
-        android:layout_alignParentTop="true"
-        android:clickable="true"
-        android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
-        android:scaleType="center"
-        android:background="@drawable/recents_thumbnail_bg"
-        android:foreground="@drawable/recents_thumbnail_overlay">
-        <ImageView android:id="@+id/app_thumbnail_image"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:visibility="invisible"
+        android:layout_width="match_parent">
+
+        <FrameLayout android:id="@+id/app_thumbnail"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:clickable="true"
+            android:layout_marginLeft="@dimen/status_bar_recents_thumbnail_left_margin"
+            android:scaleType="center"
+            android:background="@drawable/recents_thumbnail_bg"
+            android:foreground="@drawable/recents_thumbnail_overlay">
+            <ImageView android:id="@+id/app_thumbnail_image"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="invisible"
+            />
+        </FrameLayout>
+
+        <ImageView android:id="@+id/app_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignLeft="@id/app_thumbnail"
+            android:layout_alignTop="@id/app_thumbnail"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
+            android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
+            android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
+            android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
+            android:adjustViewBounds="true"
         />
-    </FrameLayout>
 
-    <ImageView android:id="@+id/app_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignLeft="@id/app_thumbnail"
-        android:layout_alignTop="@id/app_thumbnail"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_icon_left_margin"
-        android:layout_marginTop="@dimen/status_bar_recents_app_icon_top_margin"
-        android:maxWidth="@dimen/status_bar_recents_thumbnail_max_width"
-        android:maxHeight="@dimen/status_bar_recents_thumbnail_max_height"
-        android:adjustViewBounds="true"
-    />
+        <TextView android:id="@+id/app_label"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/status_bar_recents_app_label_text_size"
+            android:fadingEdge="horizontal"
+            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:scrollHorizontally="true"
+            android:layout_alignParentLeft="true"
+            android:layout_alignTop="@id/app_icon"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+        />
 
-    <TextView android:id="@+id/app_label"
-        android:layout_width="@dimen/status_bar_recents_app_label_width"
-        android:layout_height="wrap_content"
-        android:textSize="@dimen/status_bar_recents_app_label_text_size"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
-        android:scrollHorizontally="true"
-        android:layout_alignParentLeft="true"
-        android:layout_alignTop="@id/app_icon"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-    />
+        <View android:id="@+id/recents_callout_line"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="1dip"
+            android:layout_alignParentLeft="true"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
+            android:layout_toLeftOf="@id/app_thumbnail"
+            android:layout_below="@id/app_label"
+            android:layout_marginRight="3dip"
+            android:layout_marginTop="3dip"
+            android:background="@drawable/recents_callout_line"
+        />
 
-    <View android:id="@+id/recents_callout_line"
-        android:layout_width="@dimen/status_bar_recents_app_label_width"
-        android:layout_height="1dip"
-        android:layout_alignParentLeft="true"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
-        android:layout_toLeftOf="@id/app_thumbnail"
-        android:layout_below="@id/app_label"
-        android:layout_marginRight="3dip"
-        android:layout_marginTop="3dip"
-        android:background="@drawable/recents_callout_line"
-    />
+        <TextView android:id="@+id/app_description"
+            android:layout_width="@dimen/status_bar_recents_app_label_width"
+            android:layout_height="wrap_content"
+            android:textSize="@dimen/status_bar_recents_app_description_text_size"
+            android:fadingEdge="horizontal"
+            android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
+            android:scrollHorizontally="true"
+            android:layout_alignParentLeft="true"
+            android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
+            android:layout_below="@id/recents_callout_line"
+            android:layout_marginTop="3dip"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+        />
 
-    <TextView android:id="@+id/app_description"
-        android:layout_width="@dimen/status_bar_recents_app_label_width"
-        android:layout_height="wrap_content"
-        android:textSize="@dimen/status_bar_recents_app_description_text_size"
-        android:fadingEdge="horizontal"
-        android:fadingEdgeLength="@dimen/status_bar_recents_fading_edge_length"
-        android:scrollHorizontally="true"
-        android:layout_alignParentLeft="true"
-        android:layout_marginLeft="@dimen/status_bar_recents_app_label_left_margin"
-        android:layout_below="@id/recents_callout_line"
-        android:layout_marginTop="3dip"
-        android:singleLine="true"
-        android:ellipsize="marquee"
-    />
-
-</RelativeLayout>
+    </RelativeLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
index c680b8e..dd25cf9 100644
--- a/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
+++ b/packages/SystemUI/res/layout-port/status_bar_recent_panel.xml
@@ -40,7 +40,7 @@
             android:layout_marginTop="@*android:dimen/status_bar_height">
             <com.android.systemui.recent.RecentsVerticalScrollView
                 android:id="@+id/recents_container"
-                android:layout_width="@dimen/status_bar_recents_width"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_marginRight="0dp"
                 android:divider="@null"
@@ -53,7 +53,7 @@
                 android:clipChildren="false">
 
                 <LinearLayout android:id="@+id/recents_linear_layout"
-                    android:layout_width="wrap_content"
+                    android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     android:orientation="vertical"
                     android:clipToPadding="false"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
index 5306508..8dab2e6 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
@@ -19,10 +19,10 @@
 -->
 
 <!--    android:background="@drawable/status_bar_closed_default_background" -->
-<RelativeLayout
+<RelativeLayout android:id="@+id/recent_item"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
-    android:layout_width="@dimen/status_bar_recents_thumbnail_view_width">
+    android:layout_width="wrap_content">
 
     <FrameLayout android:id="@+id/app_thumbnail"
         android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 3919685..656c7c1 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -20,8 +20,6 @@
     <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_width</dimen>
 
     <!-- Recent Applications parameters -->
-    <!-- Width of a recent app view, including all content -->
-    <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
     <!-- How far the thumbnail for a recent app appears from left edge -->
     <dimen name="status_bar_recents_thumbnail_left_margin">8dp</dimen>
     <!-- How far the thumbnail for a recent app appears from top edge -->
@@ -31,7 +29,7 @@
     <!-- Padding for text descriptions -->
     <dimen name="status_bar_recents_text_description_padding">8dp</dimen>
     <!-- Width of application label text -->
-    <dimen name="status_bar_recents_app_label_width">97dip</dimen>
+    <dimen name="status_bar_recents_app_label_width">156dip</dimen>
     <!-- Left margin of application label text -->
     <dimen name="status_bar_recents_app_label_left_margin">16dip</dimen>
     <!-- Margin between recents container and glow on the right -->
diff --git a/packages/SystemUI/res/values-port/dimens.xml b/packages/SystemUI/res/values-port/dimens.xml
index 54c25fa..03b17e9 100644
--- a/packages/SystemUI/res/values-port/dimens.xml
+++ b/packages/SystemUI/res/values-port/dimens.xml
@@ -17,8 +17,6 @@
 -->
 <resources>
     <!-- Recent Applications parameters -->
-    <!-- Width of a recent app view, including all content -->
-    <dimen name="status_bar_recents_thumbnail_view_width">156dp</dimen>
     <!-- How far the thumbnail for a recent app appears from left edge -->
     <dimen name="status_bar_recents_thumbnail_left_margin">110dp</dimen>
     <!-- Width of scrollable area in recents -->
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
new file mode 100644
index 0000000..3e2ec59
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<resources>
+
+    <!-- Whether we're using the tablet-optimized recents interface (we use this
+     value at runtime for some things) -->
+    <bool name="config_recents_interface_for_tablets">true</bool>
+</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index d7d7817..4ac89b2 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -21,6 +21,10 @@
      for different hardware and product builds. -->
 <resources>
 
+    <!-- Whether we're using the tablet-optimized recents interface (we use this
+     value at runtime for some things) -->
+    <bool name="config_recents_interface_for_tablets">false</bool>
+
     <!-- Control whether status bar should distinguish HSPA data icon form UMTS
     data icon on devices -->
     <bool name="config_hspa_data_distinguishable">false</bool>
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index e7ed052..6cfbc1b 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -33,20 +33,19 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_INVALIDATE = false;
     private static final boolean SLOW_ANIMATIONS = false; // DEBUG;
+    private static final boolean CONSTRAIN_SWIPE = true;
+    private static final boolean FADE_OUT_DURING_SWIPE = true;
+    private static final boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
 
     public static final int X = 0;
     public static final int Y = 1;
 
-    private boolean CONSTRAIN_SWIPE = true;
-    private boolean FADE_OUT_DURING_SWIPE = true;
-    private boolean DISMISS_IF_SWIPED_FAR_ENOUGH = true;
-
     private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec
     private int MAX_ESCAPE_ANIMATION_DURATION = 500; // ms
     private int MAX_DISMISS_VELOCITY = 1000; // dp/sec
     private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 250; // ms
 
-    public static float ALPHA_FADE_START = 0.8f; // fraction of thumbnail width
+    public static float ALPHA_FADE_START = 0f; // fraction of thumbnail width
                                                  // where fade starts
     static final float ALPHA_FADE_END = 0.5f; // fraction of thumbnail width
                                               // beyond which alpha->0
@@ -59,6 +58,8 @@
     private float mInitialTouchPos;
     private boolean mDragging;
     private View mCurrView;
+    private View mCurrAnimView;
+    private boolean mCanCurrViewBeDimissed;
     private float mDensityScale;
 
     public SwipeHelper(int swipeDirection, Callback callback, float densityScale,
@@ -82,8 +83,8 @@
         return mSwipeDirection == X ? ev.getX() : ev.getY();
     }
 
-    private float getPos(View v) {
-        return mSwipeDirection == X ? v.getX() : v.getY();
+    private float getTranslation(View v) {
+        return mSwipeDirection == X ? v.getTranslationX() : v.getTranslationY();
     }
 
     private float getVelocity(VelocityTracker vt) {
@@ -115,19 +116,15 @@
                 v.getMeasuredHeight();
     }
 
-    private float getContentSize(View v) {
-        View content = mCallback.getChildContentView(v);
-        return getSize(content);
-    }
-
-    private float getAlphaForOffset(View view, float thumbSize) {
-        final float fadeSize = ALPHA_FADE_END * thumbSize;
+    private float getAlphaForOffset(View view) {
+        float viewSize = getSize(view);
+        final float fadeSize = ALPHA_FADE_END * viewSize;
         float result = 1.0f;
-        float pos = getPos(view);
-        if (pos >= thumbSize * ALPHA_FADE_START) {
-            result = 1.0f - (pos - thumbSize * ALPHA_FADE_START) / fadeSize;
-        } else if (pos < thumbSize * (1.0f - ALPHA_FADE_START)) {
-            result = 1.0f + (thumbSize * ALPHA_FADE_START + pos) / fadeSize;
+        float pos = getTranslation(view);
+        if (pos >= viewSize * ALPHA_FADE_START) {
+            result = 1.0f - (pos - viewSize * ALPHA_FADE_START) / fadeSize;
+        } else if (pos < viewSize * (1.0f - ALPHA_FADE_START)) {
+            result = 1.0f + (viewSize * ALPHA_FADE_START + pos) / fadeSize;
         }
         return result;
     }
@@ -168,6 +165,8 @@
             case MotionEvent.ACTION_DOWN:
                 mDragging = false;
                 mCurrView = mCallback.getChildAtPosition(ev);
+                mCurrAnimView = mCallback.getChildContentView(mCurrView);
+                mCanCurrViewBeDimissed = mCallback.canChildBeDismissed(mCurrView);
                 mVelocityTracker.clear();
                 mVelocityTracker.addMovement(ev);
                 mInitialTouchPos = getPos(ev);
@@ -180,21 +179,24 @@
                     if (Math.abs(delta) > mPagingTouchSlop) {
                         mCallback.onBeginDrag(mCurrView);
                         mDragging = true;
-                        mInitialTouchPos = getPos(ev) - getPos(mCurrView);
+                        mInitialTouchPos = getPos(ev) - getTranslation(mCurrAnimView);
                     }
                 }
                 break;
             case MotionEvent.ACTION_UP:
                 mDragging = false;
                 mCurrView = null;
+                mCurrAnimView = null;
                 break;
         }
         return mDragging;
     }
 
-    public void dismissChild(final View animView, float velocity) {
+    public void dismissChild(final View view, float velocity) {
+        final View animView = mCallback.getChildContentView(view);
+        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(view);
         float newPos;
-        if (velocity < 0 || (velocity == 0 && getPos(animView) < 0)) {
+        if (velocity < 0 || (velocity == 0 && getTranslation(animView) < 0)) {
             newPos = -getSize(animView);
         } else {
             newPos = getSize(animView);
@@ -202,7 +204,7 @@
         int duration = MAX_ESCAPE_ANIMATION_DURATION;
         if (velocity != 0) {
             duration = Math.min(duration,
-                                (int) (Math.abs(newPos - getPos(animView)) * 1000f / Math
+                                (int) (Math.abs(newPos - getTranslation(animView)) * 1000f / Math
                                         .abs(velocity)));
         }
         ObjectAnimator anim = createTranslationAnimation(animView, newPos);
@@ -216,17 +218,17 @@
             }
 
             public void onAnimationEnd(Animator animation) {
-                mCallback.onChildDismissed(animView);
+                mCallback.onChildDismissed(view);
             }
 
             public void onAnimationCancel(Animator animation) {
-                mCallback.onChildDismissed(animView);
+                mCallback.onChildDismissed(view);
             }
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
-                if (FADE_OUT_DURING_SWIPE) {
-                    animView.setAlpha(getAlphaForOffset(animView, getContentSize(animView)));
+                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
+                    animView.setAlpha(getAlphaForOffset(animView));
                 }
                 invalidateGlobalRegion(animView);
             }
@@ -234,14 +236,16 @@
         anim.start();
     }
 
-    public void snapChild(final View animView, float velocity) {
+    public void snapChild(final View view, float velocity) {
+        final View animView = mCallback.getChildContentView(view);
+        final boolean canAnimViewBeDismissed = mCallback.canChildBeDismissed(animView);
         ObjectAnimator anim = createTranslationAnimation(animView, 0);
         int duration = SNAP_ANIM_LEN;
         anim.setDuration(duration);
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
-                if (FADE_OUT_DURING_SWIPE) {
-                    animView.setAlpha(getAlphaForOffset(animView, getContentSize(animView)));
+                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
+                    animView.setAlpha(getAlphaForOffset(animView));
                 }
                 invalidateGlobalRegion(animView);
             }
@@ -264,7 +268,7 @@
                     // don't let items that can't be dismissed be dragged more than
                     // maxScrollDistance
                     if (CONSTRAIN_SWIPE && !mCallback.canChildBeDismissed(mCurrView)) {
-                        float size = getSize(mCurrView);
+                        float size = getSize(mCurrAnimView);
                         float maxScrollDistance = 0.15f * size;
                         if (Math.abs(delta) >= size) {
                             delta = delta > 0 ? maxScrollDistance : -maxScrollDistance;
@@ -272,9 +276,9 @@
                             delta = maxScrollDistance * (float) Math.sin((delta/size)*(Math.PI/2));
                         }
                     }
-                    setTranslation(mCurrView, delta);
-                    if (FADE_OUT_DURING_SWIPE) {
-                        mCurrView.setAlpha(getAlphaForOffset(mCurrView, getContentSize(mCurrView)));
+                    setTranslation(mCurrAnimView, delta);
+                    if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
+                        mCurrAnimView.setAlpha(getAlphaForOffset(mCurrAnimView));
                     }
                     invalidateGlobalRegion(mCurrView);
                 }
@@ -290,10 +294,10 @@
 
                     // Decide whether to dismiss the current view
                     boolean childSwipedFarEnough = DISMISS_IF_SWIPED_FAR_ENOUGH &&
-                            Math.abs(getPos(mCurrView)) > 0.4 * getSize(mCurrView);
+                            Math.abs(getTranslation(mCurrAnimView)) > 0.4 * getSize(mCurrAnimView);
                     boolean childSwipedFastEnough = (Math.abs(velocity) > escapeVelocity) &&
                             (Math.abs(velocity) > Math.abs(perpendicularVelocity)) &&
-                            (velocity > 0) == (getPos(mCurrView) > 0);
+                            (velocity > 0) == (getTranslation(mCurrAnimView) > 0);
 
                     boolean dismissChild = mCallback.canChildBeDismissed(mCurrView) &&
                             (childSwipedFastEnough || childSwipedFarEnough);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
index 5609ead..2de4185 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsCallback.java
@@ -27,4 +27,8 @@
     void handleOnClick(View selectedView);
     void handleSwipe(View selectedView);
     void handleLongPress(View selectedView, View anchorView);
+    void handleShowBackground(boolean show);
+
+    // TODO: find another way to get this info from RecentsPanelView
+    boolean isRecentsVisible();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
index 8da2db6..7ad7484 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.DataSetObserver;
+import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -41,6 +42,8 @@
     private RecentsCallback mCallback;
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
+    private RecentsScrollViewPerformanceHelper mPerformanceHelper;
+
     private OnLongClickListener mOnLongClick = new OnLongClickListener() {
         public boolean onLongClick(View v) {
             final View anchorView = v.findViewById(R.id.app_description);
@@ -49,15 +52,12 @@
         }
     };
 
-    public RecentsHorizontalScrollView(Context context) {
-        this(context, null);
-    }
-
     public RecentsHorizontalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
         float densityScale = getResources().getDisplayMetrics().density;
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.Y, this, densityScale, pagingTouchSlop);
+        mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, false);
     }
 
     private int scrollPositionOfMostRecent() {
@@ -71,7 +71,11 @@
             view.setLongClickable(true);
             view.setOnLongClickListener(mOnLongClick);
 
-            final View thumbnail = getChildContentView(view);
+            if (mPerformanceHelper != null) {
+                mPerformanceHelper.addViewCallback(view);
+            }
+
+            final View thumbnail = view.findViewById(R.id.app_thumbnail);
             // thumbnail is set to clickable in the layout file
             thumbnail.setOnClickListener(new OnClickListener() {
                 public void onClick(View v) {
@@ -139,7 +143,60 @@
     }
 
     public View getChildContentView(View v) {
-        return v.findViewById(R.id.app_thumbnail);
+        return v.findViewById(R.id.recent_item);
+    }
+
+    @Override
+    protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.onLayoutCallback();
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+
+        if (mPerformanceHelper != null) {
+            int paddingLeft = mPaddingLeft;
+            final boolean offsetRequired = isPaddingOffsetRequired();
+            if (offsetRequired) {
+                paddingLeft += getLeftPaddingOffset();
+            }
+
+            int left = mScrollX + paddingLeft;
+            int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
+            int top = mScrollY + getFadeTop(offsetRequired);
+            int bottom = top + getFadeHeight(offsetRequired);
+
+            if (offsetRequired) {
+                right += getRightPaddingOffset();
+                bottom += getBottomPaddingOffset();
+            }
+            mPerformanceHelper.drawCallback(canvas,
+                    left, right, top, bottom, mScrollX, mScrollY,
+                    0, 0,
+                    getLeftFadingEdgeStrength(), getRightFadingEdgeStrength());
+        }
+    }
+
+    @Override
+    public int getVerticalFadingEdgeLength() {
+        if (mPerformanceHelper != null) {
+            return mPerformanceHelper.getVerticalFadingEdgeLengthCallback();
+        } else {
+            return super.getVerticalFadingEdgeLength();
+        }
+    }
+
+    @Override
+    public int getHorizontalFadingEdgeLength() {
+        if (mPerformanceHelper != null) {
+            return mPerformanceHelper.getHorizontalFadingEdgeLengthCallback();
+        } else {
+            return super.getHorizontalFadingEdgeLength();
+        }
     }
 
     @Override
@@ -153,6 +210,14 @@
     }
 
     @Override
+    public void onAttachedToWindow() {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.onAttachedToWindowCallback(
+                    mCallback, mLinearLayout, isHardwareAccelerated());
+        }
+    }
+
+    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         float densityScale = getResources().getDisplayMetrics().density;
@@ -192,6 +257,12 @@
         });
     }
 
+    public void onRecentsVisibilityChanged() {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.updateShowBackground();
+        }
+    }
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -220,6 +291,9 @@
 
     @Override
     public void setLayoutTransition(LayoutTransition transition) {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.setLayoutTransitionCallback(transition);
+        }
         // The layout transition applies to our embedded LinearLayout
         mLinearLayout.setLayoutTransition(transition);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java
deleted file mode 100644
index d8b086b..0000000
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsListView.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.recent;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.widget.ListView;
-
-import com.android.systemui.R;
-
-public class RecentsListView extends ListView {
-    private int mLastVisiblePosition;
-    private RecentsCallback mCallback;
-
-    public RecentsListView(Context context) {
-        this(context, null);
-    }
-
-    public RecentsListView(Context context, AttributeSet attrs) {
-        super(context, attrs, 0);
-    }
-
-    @Override
-    protected void onFinishInflate() {
-        super.onFinishInflate();
-        LayoutInflater inflater = (LayoutInflater)
-                mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
-        View footer = inflater.inflate(R.layout.status_bar_recent_panel_footer, this, false);
-        setScrollbarFadingEnabled(true);
-        addFooterView(footer, null, false);
-        final int leftPadding = mContext.getResources()
-            .getDimensionPixelOffset(R.dimen.status_bar_recents_thumbnail_left_margin);
-        setOverScrollEffectPadding(leftPadding, 0);
-    }
-
-    @Override
-    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-        super.onSizeChanged(w, h, oldw, oldh);
-        // Keep track of the last visible item in the list so we can restore it
-        // to the bottom when the orientation changes.
-        final int childCount = getChildCount();
-        if (childCount > 0) {
-            mLastVisiblePosition = getFirstVisiblePosition() + childCount - 1;
-            View view = getChildAt(childCount - 1);
-            final int distanceFromBottom = getHeight() - view.getTop();
-
-            // This has to happen post-layout, so run it "in the future"
-            post(new Runnable() {
-                public void run() {
-                    setSelectionFromTop(mLastVisiblePosition, getHeight() - distanceFromBottom);
-                }
-            });
-        }
-    }
-
-    @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        // scroll to bottom after reloading
-        int count = getAdapter().getCount();
-        mLastVisiblePosition = count - 1;
-        if (visibility == View.VISIBLE && changedView == this) {
-            post(new Runnable() {
-                public void run() {
-                    setSelection(mLastVisiblePosition);
-                }
-            });
-        }
-    }
-
-    public void setCallback(RecentsCallback callback) {
-        mCallback = callback;
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 9cc2c29..233ed6c 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -56,7 +56,6 @@
 import android.view.ViewGroup;
 import android.view.animation.AnimationUtils;
 import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
 import android.widget.BaseAdapter;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
@@ -64,6 +63,7 @@
 import android.widget.RelativeLayout;
 import android.widget.ScrollView;
 import android.widget.TextView;
+import android.widget.AdapterView.OnItemClickListener;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.StatusBar;
@@ -73,7 +73,7 @@
 
 public class RecentsPanelView extends RelativeLayout
         implements OnItemClickListener, RecentsCallback, StatusBarPanel, Animator.AnimatorListener {
-    static final String TAG = "RecentsListView";
+    static final String TAG = "RecentsPanelView";
     static final boolean DEBUG = TabletStatusBar.DEBUG || PhoneStatusBar.DEBUG || false;
     private static final int DISPLAY_TASKS = 20;
     private static final int MAX_TASKS = DISPLAY_TASKS + 1; // allow extra for non-apps
@@ -178,7 +178,7 @@
         public View getView(int position, View convertView, ViewGroup parent) {
             ViewHolder holder;
             if (convertView == null) {
-                convertView = mInflater.inflate(R.layout.status_bar_recent_item, null);
+                convertView = mInflater.inflate(R.layout.status_bar_recent_item, parent, false);
                 holder = new ViewHolder();
                 holder.thumbnailView = convertView.findViewById(R.id.app_thumbnail);
                 holder.thumbnailViewImage = (ImageView) convertView.findViewById(
@@ -247,6 +247,18 @@
         }
     }
 
+    public void handleShowBackground(boolean show) {
+        if (show) {
+            mRecentsScrim.setBackgroundResource(R.drawable.status_bar_recents_background);
+        } else {
+            mRecentsScrim.setBackgroundDrawable(null);
+        }
+    }
+
+    public boolean isRecentsVisible() {
+        return getVisibility() == VISIBLE;
+    }
+
     public void onAnimationCancel(Animator animation) {
     }
 
@@ -332,12 +344,7 @@
         mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         mRecentsContainer = (ViewGroup) findViewById(R.id.recents_container);
         mListAdapter = new ActivityDescriptionAdapter(mContext);
-        if (mRecentsContainer instanceof RecentsListView) {
-            RecentsListView listView = (RecentsListView) mRecentsContainer;
-            listView.setAdapter(mListAdapter);
-            listView.setOnItemClickListener(this);
-            listView.setCallback(this);
-        } else if (mRecentsContainer instanceof RecentsHorizontalScrollView){
+        if (mRecentsContainer instanceof RecentsHorizontalScrollView){
             RecentsHorizontalScrollView scrollView
                     = (RecentsHorizontalScrollView) mRecentsContainer;
             scrollView.setAdapter(mListAdapter);
@@ -349,7 +356,7 @@
             scrollView.setCallback(this);
         }
         else {
-            throw new IllegalArgumentException("missing RecentsListView/RecentsScrollView");
+            throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
         }
 
 
@@ -382,6 +389,14 @@
         if (visibility == View.VISIBLE && changedView == this) {
             refreshApplicationList();
         }
+
+        if (mRecentsContainer instanceof RecentsHorizontalScrollView) {
+            ((RecentsHorizontalScrollView) mRecentsContainer).onRecentsVisibilityChanged();
+        } else if (mRecentsContainer instanceof RecentsVerticalScrollView) {
+            ((RecentsVerticalScrollView) mRecentsContainer).onRecentsVisibilityChanged();
+        } else {
+            throw new IllegalArgumentException("missing Recents[Horizontal]ScrollView");
+        }
     }
 
     Drawable getFullResDefaultActivityIcon() {
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
new file mode 100644
index 0000000..b7e656e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsScrollViewPerformanceHelper.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recent;
+
+import android.animation.LayoutTransition;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+import com.android.systemui.R;
+
+public class RecentsScrollViewPerformanceHelper {
+    public static final boolean OPTIMIZE_SW_RENDERED_RECENTS = true;
+    public static final boolean USE_DARK_FADE_IN_HW_ACCELERATED_MODE = true;
+    private View mScrollView;
+    private LinearLayout mLinearLayout;
+    private RecentsCallback mCallback;
+
+    private boolean mShowBackground = false;
+    private int mFadingEdgeLength;
+    private Drawable.ConstantState mBackgroundDrawable;
+    private Context mContext;
+    private boolean mIsVertical;
+    private boolean mFirstTime = true;
+    private boolean mSoftwareRendered = false;
+    private boolean mAttachedToWindow = false;
+
+    public static RecentsScrollViewPerformanceHelper create(Context context,
+            AttributeSet attrs, View scrollView, boolean isVertical) {
+        boolean isTablet = context.getResources().
+                getBoolean(R.bool.config_recents_interface_for_tablets);
+        if (!isTablet && (OPTIMIZE_SW_RENDERED_RECENTS || USE_DARK_FADE_IN_HW_ACCELERATED_MODE)) {
+            return new RecentsScrollViewPerformanceHelper(context, attrs, scrollView, isVertical);
+        } else {
+            return null;
+        }
+    }
+
+    public RecentsScrollViewPerformanceHelper(Context context,
+            AttributeSet attrs, View scrollView, boolean isVertical) {
+        mScrollView = scrollView;
+        mContext = context;
+        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View);
+        mFadingEdgeLength = a.getDimensionPixelSize(android.R.styleable.View_fadingEdgeLength,
+                ViewConfiguration.get(context).getScaledFadingEdgeLength());
+        mIsVertical = isVertical;
+    }
+
+    public void onAttachedToWindowCallback(
+            RecentsCallback callback, LinearLayout layout, boolean hardwareAccelerated) {
+        mSoftwareRendered = !hardwareAccelerated;
+        if ((mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS)
+                || USE_DARK_FADE_IN_HW_ACCELERATED_MODE) {
+            mScrollView.setVerticalFadingEdgeEnabled(false);
+            mScrollView.setHorizontalFadingEdgeEnabled(false);
+        }
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            mCallback = callback;
+            mLinearLayout = layout;
+            mAttachedToWindow = true;
+            mBackgroundDrawable = mContext.getResources()
+                .getDrawable(R.drawable.status_bar_recents_background).getConstantState();
+            updateShowBackground();
+        }
+
+    }
+
+    public void addViewCallback(View newLinearLayoutChild) {
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            final View view = newLinearLayoutChild;
+            if (mShowBackground) {
+                view.setBackgroundDrawable(mBackgroundDrawable.newDrawable());
+                view.setDrawingCacheEnabled(true);
+                view.buildDrawingCache();
+            } else {
+                view.setBackgroundDrawable(null);
+                view.setDrawingCacheEnabled(false);
+                view.destroyDrawingCache();
+            }
+        }
+    }
+
+    public void onLayoutCallback() {
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            mScrollView.post(new Runnable() {
+                public void run() {
+                    updateShowBackground();
+                }
+            });
+        }
+    }
+
+    public void drawCallback(Canvas canvas,
+            int left, int right, int top, int bottom, int scrollX, int scrollY,
+            float topFadingEdgeStrength, float bottomFadingEdgeStrength,
+            float leftFadingEdgeStrength, float rightFadingEdgeStrength) {
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            if (mIsVertical) {
+                if (scrollY < 0) {
+                    Drawable d = mBackgroundDrawable.newDrawable().getCurrent();
+                    d.setBounds(0, scrollY, mScrollView.getWidth(), 0);
+                    d.draw(canvas);
+                } else {
+                    final int childHeight = mLinearLayout.getHeight();
+                    if (scrollY + mScrollView.getHeight() > childHeight) {
+                        Drawable d = mBackgroundDrawable.newDrawable().getCurrent();
+                        d.setBounds(0, childHeight, mScrollView.getWidth(),
+                                scrollY + mScrollView.getHeight());
+                        d.draw(canvas);
+                    }
+                }
+            } else {
+                if (scrollX < 0) {
+                    Drawable d = mBackgroundDrawable.newDrawable().getCurrent();
+                    d.setBounds(scrollX, 0, 0, mScrollView.getHeight());
+                    d.draw(canvas);
+                } else {
+                    final int childWidth = mLinearLayout.getWidth();
+                    if (scrollX + mScrollView.getWidth() > childWidth) {
+                        Drawable d = mBackgroundDrawable.newDrawable().getCurrent();
+                        d.setBounds(childWidth, 0,
+                                scrollX + mScrollView.getWidth(), mScrollView.getHeight());
+                        d.draw(canvas);
+                    }
+                }
+            }
+        }
+
+        if ((mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS)
+                || USE_DARK_FADE_IN_HW_ACCELERATED_MODE) {
+            Paint p = new Paint();
+            Matrix matrix = new Matrix();
+            // use use a height of 1, and then wack the matrix each time we
+            // actually use it.
+            Shader fade = new LinearGradient(0, 0, 0, 1, 0xCC000000, 0, Shader.TileMode.CLAMP);
+            // PULL OUT THIS CONSTANT
+
+            p.setShader(fade);
+
+            // draw the fade effect
+            boolean drawTop = false;
+            boolean drawBottom = false;
+            boolean drawLeft = false;
+            boolean drawRight = false;
+
+            float topFadeStrength = 0.0f;
+            float bottomFadeStrength = 0.0f;
+            float leftFadeStrength = 0.0f;
+            float rightFadeStrength = 0.0f;
+
+            final float fadeHeight = mFadingEdgeLength;
+            int length = (int) fadeHeight;
+
+            // clip the fade length if top and bottom fades overlap
+            // overlapping fades produce odd-looking artifacts
+            if (mIsVertical && (top + length > bottom - length)) {
+                length = (bottom - top) / 2;
+            }
+
+            // also clip horizontal fades if necessary
+            if (!mIsVertical && (left + length > right - length)) {
+                length = (right - left) / 2;
+            }
+
+            if (mIsVertical) {
+                topFadeStrength = Math.max(0.0f, Math.min(1.0f, topFadingEdgeStrength));
+                drawTop = topFadeStrength * fadeHeight > 1.0f;
+                bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, bottomFadingEdgeStrength));
+                drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
+            }
+
+            if (!mIsVertical) {
+                leftFadeStrength = Math.max(0.0f, Math.min(1.0f, leftFadingEdgeStrength));
+                drawLeft = leftFadeStrength * fadeHeight > 1.0f;
+                rightFadeStrength = Math.max(0.0f, Math.min(1.0f, rightFadingEdgeStrength));
+                drawRight = rightFadeStrength * fadeHeight > 1.0f;
+            }
+
+            if (drawTop) {
+                matrix.setScale(1, fadeHeight * topFadeStrength);
+                matrix.postTranslate(left, top);
+                fade.setLocalMatrix(matrix);
+                canvas.drawRect(left, top, right, top + length, p);
+            }
+
+            if (drawBottom) {
+                matrix.setScale(1, fadeHeight * bottomFadeStrength);
+                matrix.postRotate(180);
+                matrix.postTranslate(left, bottom);
+                fade.setLocalMatrix(matrix);
+                canvas.drawRect(left, bottom - length, right, bottom, p);
+            }
+
+            if (drawLeft) {
+                matrix.setScale(1, fadeHeight * leftFadeStrength);
+                matrix.postRotate(-90);
+                matrix.postTranslate(left, top);
+                fade.setLocalMatrix(matrix);
+                canvas.drawRect(left, top, left + length, bottom, p);
+            }
+
+            if (drawRight) {
+                matrix.setScale(1, fadeHeight * rightFadeStrength);
+                matrix.postRotate(90);
+                matrix.postTranslate(right, top);
+                fade.setLocalMatrix(matrix);
+                canvas.drawRect(right - length, top, right, bottom, p);
+            }
+        }
+    }
+
+    public int getVerticalFadingEdgeLengthCallback() {
+        return mFadingEdgeLength;
+    }
+
+    public int getHorizontalFadingEdgeLengthCallback() {
+        return mFadingEdgeLength;
+    }
+
+    public void setLayoutTransitionCallback(LayoutTransition transition) {
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            if (transition != null) {
+                transition.addTransitionListener(new LayoutTransition.TransitionListener() {
+                    @Override
+                    public void startTransition(LayoutTransition transition,
+                            ViewGroup container, View view, int transitionType) {
+                        updateShowBackground();
+                    }
+
+                    @Override
+                    public void endTransition(LayoutTransition transition,
+                            ViewGroup container, View view, int transitionType) {
+                        updateShowBackground();
+                    }
+                });
+            }
+        }
+    }
+
+    // Turn on/off drawing the background in our ancestor, and turn on/off drawing
+    // in the items in LinearLayout contained by this scrollview.
+    // Moving the background drawing to our children, and turning on a drawing cache
+    // for each of them, gives us a ~20fps gain when Recents is rendered in software
+    public void updateShowBackground() {
+        if (!mAttachedToWindow) {
+            // We haven't been initialized yet-- we'll get called again when we are
+            return;
+        }
+        if (mSoftwareRendered && OPTIMIZE_SW_RENDERED_RECENTS) {
+            LayoutTransition transition = mLinearLayout.getLayoutTransition();
+            int linearLayoutSize =
+                mIsVertical ? mLinearLayout.getHeight() : mLinearLayout.getWidth();
+            int scrollViewSize =
+                mIsVertical ? mScrollView.getHeight() : mScrollView.getWidth();
+            boolean show = !mScrollView.isHardwareAccelerated() &&
+                (linearLayoutSize > scrollViewSize) &&
+                !(transition != null && transition.isRunning()) &&
+                mCallback.isRecentsVisible();
+
+            if (!mFirstTime && show == mShowBackground) return;
+            mShowBackground = show;
+            mFirstTime = false;
+
+            mCallback.handleShowBackground(!show);
+            for (int i = 0; i < mLinearLayout.getChildCount(); i++) {
+                View v = mLinearLayout.getChildAt(i);
+                if (show) {
+                    v.setBackgroundDrawable(mBackgroundDrawable.newDrawable());
+                    v.setDrawingCacheEnabled(true);
+                    v.buildDrawingCache();
+                } else {
+                    v.setDrawingCacheEnabled(false);
+                    v.destroyDrawingCache();
+                    v.setBackgroundDrawable(null);
+                }
+            }
+        }
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
index b1a30d9..1b6daa5 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.DataSetObserver;
+import android.graphics.Canvas;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.MotionEvent;
@@ -40,6 +41,7 @@
     private RecentsCallback mCallback;
     protected int mLastScrollPosition;
     private SwipeHelper mSwipeHelper;
+    private RecentsScrollViewPerformanceHelper mPerformanceHelper;
 
     private OnLongClickListener mOnLongClick = new OnLongClickListener() {
         public boolean onLongClick(View v) {
@@ -49,15 +51,13 @@
         }
     };
 
-    public RecentsVerticalScrollView(Context context) {
-        this(context, null);
-    }
-
     public RecentsVerticalScrollView(Context context, AttributeSet attrs) {
         super(context, attrs, 0);
         float densityScale = getResources().getDisplayMetrics().density;
         float pagingTouchSlop = ViewConfiguration.get(mContext).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
+
+        mPerformanceHelper = RecentsScrollViewPerformanceHelper.create(context, attrs, this, true);
     }
 
     private int scrollPositionOfMostRecent() {
@@ -77,11 +77,15 @@
             }
             final View view = mAdapter.getView(i, old, mLinearLayout);
 
+            if (mPerformanceHelper != null) {
+                mPerformanceHelper.addViewCallback(view);
+            }
+
             if (old == null) {
                 view.setClickable(true);
                 view.setOnLongClickListener(mOnLongClick);
 
-                final View thumbnail = getChildContentView(view);
+                final View thumbnail = view.findViewById(R.id.app_thumbnail);
                 // thumbnail is set to clickable in the layout file
                 thumbnail.setOnClickListener(new OnClickListener() {
                     public void onClick(View v) {
@@ -155,7 +159,60 @@
     }
 
     public View getChildContentView(View v) {
-        return v.findViewById(R.id.app_thumbnail);
+        return v.findViewById(R.id.recent_item);
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.onLayoutCallback();
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+
+        if (mPerformanceHelper != null) {
+            int paddingLeft = mPaddingLeft;
+            final boolean offsetRequired = isPaddingOffsetRequired();
+            if (offsetRequired) {
+                paddingLeft += getLeftPaddingOffset();
+            }
+
+            int left = mScrollX + paddingLeft;
+            int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
+            int top = mScrollY + getFadeTop(offsetRequired);
+            int bottom = top + getFadeHeight(offsetRequired);
+
+            if (offsetRequired) {
+                right += getRightPaddingOffset();
+                bottom += getBottomPaddingOffset();
+            }
+            mPerformanceHelper.drawCallback(canvas,
+                    left, right, top, bottom, mScrollX, mScrollY,
+                    getTopFadingEdgeStrength(), getBottomFadingEdgeStrength(),
+                    0, 0);
+        }
+    }
+
+    @Override
+    public int getVerticalFadingEdgeLength() {
+        if (mPerformanceHelper != null) {
+            return mPerformanceHelper.getVerticalFadingEdgeLengthCallback();
+        } else {
+            return super.getVerticalFadingEdgeLength();
+        }
+    }
+
+    @Override
+    public int getHorizontalFadingEdgeLength() {
+        if (mPerformanceHelper != null) {
+            return mPerformanceHelper.getHorizontalFadingEdgeLengthCallback();
+        } else {
+            return super.getHorizontalFadingEdgeLength();
+        }
     }
 
     @Override
@@ -169,6 +226,14 @@
     }
 
     @Override
+    public void onAttachedToWindow() {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.onAttachedToWindowCallback(
+                    mCallback, mLinearLayout, isHardwareAccelerated());
+        }
+    }
+
+    @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
         float densityScale = getResources().getDisplayMetrics().density;
@@ -208,6 +273,12 @@
         });
     }
 
+    public void onRecentsVisibilityChanged() {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.updateShowBackground();
+        }
+    }
+
     @Override
     protected void onVisibilityChanged(View changedView, int visibility) {
         super.onVisibilityChanged(changedView, visibility);
@@ -236,6 +307,9 @@
 
     @Override
     public void setLayoutTransition(LayoutTransition transition) {
+        if (mPerformanceHelper != null) {
+            mPerformanceHelper.setLayoutTransitionCallback(transition);
+        }
         // The layout transition applies to our embedded LinearLayout
         mLinearLayout.setLayoutTransition(transition);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index e3ea0de..7868626 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -380,7 +380,7 @@
     }
 
     protected WindowManager.LayoutParams getRecentsLayoutParams(LayoutParams layoutParams) {
-        boolean translucent = false;
+        boolean opaque = false;
         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                 layoutParams.width,
                 layoutParams.height,
@@ -388,7 +388,7 @@
                 WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                 | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
                 | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                (translucent ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
+                (opaque ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT));
         if (ActivityManager.isHighEndGfx(mDisplay)) {
             lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 60dfdac..3c85814 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -130,6 +130,8 @@
     int mLastDataTypeIconId = -1;
     String mLastLabel = "";
 
+    private boolean mHasMobileDataFeature;
+
     boolean mDataAndWifiStacked = false;
 
     // yuck -- stop doing this here and put it in the framework
@@ -147,6 +149,10 @@
     public NetworkController(Context context) {
         mContext = context;
 
+        ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        mHasMobileDataFeature = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+
         // set up the default wifi icon, used when no radios have ever appeared
         updateWifiIcons();
 
@@ -229,7 +235,7 @@
                 mWifiIconId,
                 mWifiActivityIconId);
         cluster.setMobileDataIndicators(
-                hasMobileDataFeature(),
+                mHasMobileDataFeature,
                 mPhoneSignalIconId,
                 mMobileActivityIconId,
                 mDataTypeIconId);
@@ -376,12 +382,6 @@
         }
     }
 
-    private boolean hasMobileDataFeature() {
-        // XXX: HAX: replace when a more reliable method is available
-        return (! "wifi-only".equals(SystemProperties.get("ro.carrier")));
-    }
-
-
     private void updateAirplaneMode() {
         mAirplaneMode = (Settings.System.getInt(mContext.getContentResolver(),
             Settings.System.AIRPLANE_MODE_ON, 0) == 1);
@@ -828,8 +828,8 @@
             label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
             // On devices without mobile radios, we want to show the wifi icon
             combinedSignalIconId =
-                hasMobileDataFeature() ? mDataSignalIconId : mWifiIconId;
-            mContentDescriptionCombinedSignal = hasMobileDataFeature()
+                mHasMobileDataFeature ? mDataSignalIconId : mWifiIconId;
+            mContentDescriptionCombinedSignal = mHasMobileDataFeature
                 ? mContentDescriptionDataType : mContentDescriptionWifi;
             mDataTypeIconId = 0;
         }
@@ -866,7 +866,7 @@
                         mWifiIconId,
                         mWifiActivityIconId);
                 cluster.setMobileDataIndicators(
-                        hasMobileDataFeature(),
+                        mHasMobileDataFeature,
                         mPhoneSignalIconId,
                         mMobileActivityIconId,
                         mDataTypeIconId);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index c988846..8b450f6 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -38,6 +38,7 @@
 import android.database.ContentObserver;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -2263,7 +2264,7 @@
             // incorrectly think it does cover it when it doesn't.  We'll revisit
             // this later when we re-do the phone status bar.
             if (mStatusBar != null && mStatusBar.isVisibleLw()) {
-                Rect rect = new Rect(mStatusBar.getShownFrameLw());
+                RectF rect = new RectF(mStatusBar.getShownFrameLw());
                 for (int i=mStatusBarPanels.size()-1; i>=0; i--) {
                     WindowState w = mStatusBarPanels.get(i);
                     if (w.isVisibleLw()) {
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index e193be0..b178fd9 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -769,11 +769,6 @@
     status_t result = checkPidAndHardware();
     if (result != NO_ERROR) return result;
 
-    if (mHardware->recordingEnabled()) {
-        LOGE("Cannot take picture during recording.");
-        return INVALID_OPERATION;
-    }
-
     if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
         (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
         LOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 06dea36..80ee28e 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -78,6 +78,40 @@
     return value ? "true" : "false";
 }
 
+// --- Global Functions ---
+
+uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses) {
+    // Touch devices get dibs on touch-related axes.
+    if (deviceClasses & INPUT_DEVICE_CLASS_TOUCH) {
+        switch (axis) {
+        case ABS_X:
+        case ABS_Y:
+        case ABS_PRESSURE:
+        case ABS_TOOL_WIDTH:
+        case ABS_DISTANCE:
+        case ABS_TILT_X:
+        case ABS_TILT_Y:
+        case ABS_MT_SLOT:
+        case ABS_MT_TOUCH_MAJOR:
+        case ABS_MT_TOUCH_MINOR:
+        case ABS_MT_WIDTH_MAJOR:
+        case ABS_MT_WIDTH_MINOR:
+        case ABS_MT_ORIENTATION:
+        case ABS_MT_POSITION_X:
+        case ABS_MT_POSITION_Y:
+        case ABS_MT_TOOL_TYPE:
+        case ABS_MT_BLOB_ID:
+        case ABS_MT_TRACKING_ID:
+        case ABS_MT_PRESSURE:
+        case ABS_MT_DISTANCE:
+            return INPUT_DEVICE_CLASS_TOUCH;
+        }
+    }
+
+    // Joystick devices get the rest.
+    return deviceClasses & INPUT_DEVICE_CLASS_JOYSTICK;
+}
+
 // --- EventHub::Device ---
 
 EventHub::Device::Device(int fd, int32_t id, const String8& path,
@@ -936,13 +970,17 @@
     }
 
     // See if this device is a joystick.
-    // Ignore touchscreens because they use the same absolute axes for other purposes.
     // Assumes that joysticks always have gamepad buttons in order to distinguish them
     // from other devices such as accelerometers that also have absolute axes.
-    if (haveGamepadButtons
-            && !(device->classes & INPUT_DEVICE_CLASS_TOUCH)
-            && containsNonZeroByte(device->absBitmask, 0, sizeof_bit_array(ABS_MAX + 1))) {
-        device->classes |= INPUT_DEVICE_CLASS_JOYSTICK;
+    if (haveGamepadButtons) {
+        uint32_t assumedClasses = device->classes | INPUT_DEVICE_CLASS_JOYSTICK;
+        for (int i = 0; i <= ABS_MAX; i++) {
+            if (test_bit(i, device->absBitmask)
+                    && (getAbsAxisUsage(i, assumedClasses) & INPUT_DEVICE_CLASS_JOYSTICK)) {
+                device->classes = assumedClasses;
+                break;
+            }
+        }
     }
 
     // Check whether this device has switches.
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index fae5d4f..d37549a 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -112,6 +112,12 @@
 };
 
 /*
+ * Gets the class that owns an axis, in cases where multiple classes might claim
+ * the same axis for different purposes.
+ */
+extern uint32_t getAbsAxisUsage(int32_t axis, uint32_t deviceClasses);
+
+/*
  * Grand Central Station for events.
  *
  * The event hub aggregates input events received across all known input
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index e39712e..88c19a4 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -390,7 +390,7 @@
 
 InputDevice* InputReader::createDeviceLocked(int32_t deviceId,
         const String8& name, uint32_t classes) {
-    InputDevice* device = new InputDevice(&mContext, deviceId, name);
+    InputDevice* device = new InputDevice(&mContext, deviceId, name, classes);
 
     // External devices.
     if (classes & INPUT_DEVICE_CLASS_EXTERNAL) {
@@ -842,9 +842,10 @@
 
 // --- InputDevice ---
 
-InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) :
-        mContext(context), mId(id), mName(name), mSources(0),
-        mIsExternal(false), mDropUntilNextSync(false) {
+InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name,
+        uint32_t classes) :
+        mContext(context), mId(id), mName(name), mClasses(classes),
+        mSources(0), mIsExternal(false), mDropUntilNextSync(false) {
 }
 
 InputDevice::~InputDevice() {
@@ -5759,6 +5760,11 @@
     if (!changes) { // first time only
         // Collect all axes.
         for (int32_t abs = 0; abs <= ABS_MAX; abs++) {
+            if (!(getAbsAxisUsage(abs, getDevice()->getClasses())
+                    & INPUT_DEVICE_CLASS_JOYSTICK)) {
+                continue; // axis must be claimed by a different device
+            }
+
             RawAbsoluteAxisInfo rawAxisInfo;
             getAbsoluteAxisInfo(abs, &rawAxisInfo);
             if (rawAxisInfo.valid) {
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index cd3ea37..a122c97 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -430,12 +430,13 @@
 /* Represents the state of a single input device. */
 class InputDevice {
 public:
-    InputDevice(InputReaderContext* context, int32_t id, const String8& name);
+    InputDevice(InputReaderContext* context, int32_t id, const String8& name, uint32_t classes);
     ~InputDevice();
 
     inline InputReaderContext* getContext() { return mContext; }
     inline int32_t getId() { return mId; }
     inline const String8& getName() { return mName; }
+    inline uint32_t getClasses() { return mClasses; }
     inline uint32_t getSources() { return mSources; }
 
     inline bool isExternal() { return mIsExternal; }
@@ -483,10 +484,11 @@
 private:
     InputReaderContext* mContext;
     int32_t mId;
+    String8 mName;
+    uint32_t mClasses;
 
     Vector<InputMapper*> mMappers;
 
-    String8 mName;
     uint32_t mSources;
     bool mIsExternal;
     bool mDropUntilNextSync;
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 32f948b..a086208 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -852,8 +852,8 @@
         mNextDevice = device;
     }
 
-    InputDevice* newDevice(int32_t deviceId, const String8& name) {
-        return new InputDevice(&mContext, deviceId, name);
+    InputDevice* newDevice(int32_t deviceId, const String8& name, uint32_t classes) {
+        return new InputDevice(&mContext, deviceId, name, classes);
     }
 
 protected:
@@ -912,7 +912,7 @@
     FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId,
             const String8& name, uint32_t classes, uint32_t sources,
             const PropertyMap* configuration) {
-        InputDevice* device = mReader->newDevice(deviceId, name);
+        InputDevice* device = mReader->newDevice(deviceId, name, classes);
         FakeInputMapper* mapper = new FakeInputMapper(device, sources);
         device->addMapper(mapper);
         mReader->setNextDevice(device);
@@ -1211,6 +1211,7 @@
 protected:
     static const char* DEVICE_NAME;
     static const int32_t DEVICE_ID;
+    static const uint32_t DEVICE_CLASSES;
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
@@ -1226,7 +1227,7 @@
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
 
         mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME));
+        mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME), DEVICE_CLASSES);
     }
 
     virtual void TearDown() {
@@ -1241,10 +1242,13 @@
 
 const char* InputDeviceTest::DEVICE_NAME = "device";
 const int32_t InputDeviceTest::DEVICE_ID = 1;
+const uint32_t InputDeviceTest::DEVICE_CLASSES = INPUT_DEVICE_CLASS_KEYBOARD
+        | INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_JOYSTICK;
 
 TEST_F(InputDeviceTest, ImmutableProperties) {
     ASSERT_EQ(DEVICE_ID, mDevice->getId());
     ASSERT_STREQ(DEVICE_NAME, mDevice->getName());
+    ASSERT_EQ(DEVICE_CLASSES, mDevice->getClasses());
 }
 
 TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) {
@@ -1390,6 +1394,7 @@
 protected:
     static const char* DEVICE_NAME;
     static const int32_t DEVICE_ID;
+    static const uint32_t DEVICE_CLASSES;
 
     sp<FakeEventHub> mFakeEventHub;
     sp<FakeInputReaderPolicy> mFakePolicy;
@@ -1402,7 +1407,7 @@
         mFakePolicy = new FakeInputReaderPolicy();
         mFakeListener = new FakeInputListener();
         mFakeContext = new FakeInputReaderContext(mFakeEventHub, mFakePolicy, mFakeListener);
-        mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME));
+        mDevice = new InputDevice(mFakeContext, DEVICE_ID, String8(DEVICE_NAME), DEVICE_CLASSES);
 
         mFakeEventHub->addDevice(DEVICE_ID, String8(DEVICE_NAME), 0);
     }
@@ -1483,6 +1488,7 @@
 
 const char* InputMapperTest::DEVICE_NAME = "device";
 const int32_t InputMapperTest::DEVICE_ID = 1;
+const uint32_t InputMapperTest::DEVICE_CLASSES = 0; // not needed for current tests
 
 
 // --- SwitchInputMapperTest ---
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 3815c3b..2348d76 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -703,6 +703,12 @@
         return result.toArray(new NetworkInfo[result.size()]);
     }
 
+    @Override
+    public boolean isNetworkSupported(int networkType) {
+        enforceAccessPermission();
+        return (isNetworkTypeValid(networkType) && (mNetTrackers[networkType] != null));
+    }
+
     /**
      * Return LinkProperties for the active (i.e., connected) default
      * network interface.  It is assumed that at most one default network
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d0e8b5e..2714fc5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -350,7 +350,6 @@
                 Slog.i(TAG, "Wi-Fi Service");
                 wifi = new WifiService(context);
                 ServiceManager.addService(Context.WIFI_SERVICE, wifi);
-                wifi.checkAndStartWifi();
             } catch (Throwable e) {
                 reportWtf("starting Wi-Fi Service", e);
             }
@@ -361,6 +360,7 @@
                 ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
                 networkStats.bindConnectivityManager(connectivity);
                 networkPolicy.bindConnectivityManager(connectivity);
+                wifi.checkAndStartWifi();
                 wifiP2p.connectivityServiceReady();
             } catch (Throwable e) {
                 reportWtf("starting Connectivity Service", e);
diff --git a/services/java/com/android/server/wm/BlackFrame.java b/services/java/com/android/server/wm/BlackFrame.java
index 36f5dcb..10e294b 100644
--- a/services/java/com/android/server/wm/BlackFrame.java
+++ b/services/java/com/android/server/wm/BlackFrame.java
@@ -51,8 +51,8 @@
             mTmpMatrix.setTranslate(left, top);
             mTmpMatrix.postConcat(matrix);
             mTmpMatrix.getValues(mTmpFloats);
-            surface.setPosition((int)mTmpFloats[Matrix.MTRANS_X],
-                    (int)mTmpFloats[Matrix.MTRANS_Y]);
+            surface.setPosition(mTmpFloats[Matrix.MTRANS_X],
+                    mTmpFloats[Matrix.MTRANS_Y]);
             surface.setMatrix(
                     mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
                     mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
diff --git a/services/java/com/android/server/wm/DragState.java b/services/java/com/android/server/wm/DragState.java
index e622503..dd440bf 100644
--- a/services/java/com/android/server/wm/DragState.java
+++ b/services/java/com/android/server/wm/DragState.java
@@ -273,7 +273,7 @@
         if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION notifyMoveLw");
         Surface.openTransaction();
         try {
-            mSurface.setPosition((int)(x - mThumbOffsetX), (int)(y - mThumbOffsetY));
+            mSurface.setPosition(x - mThumbOffsetX, y - mThumbOffsetY);
             if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DRAG "
                     + mSurface + ": pos=(" +
                     (int)(x - mThumbOffsetX) + "," + (int)(y - mThumbOffsetY) + ")");
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 16af151..3c475a0 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -148,8 +148,8 @@
     void setSnapshotTransform(Matrix matrix, float alpha) {
         if (mSurface != null) {
             matrix.getValues(mTmpFloats);
-            mSurface.setPosition((int)mTmpFloats[Matrix.MTRANS_X],
-                    (int)mTmpFloats[Matrix.MTRANS_Y]);
+            mSurface.setPosition(mTmpFloats[Matrix.MTRANS_X],
+                    mTmpFloats[Matrix.MTRANS_Y]);
             mSurface.setMatrix(
                     mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_Y],
                     mTmpFloats[Matrix.MSKEW_X], mTmpFloats[Matrix.MSCALE_Y]);
diff --git a/services/java/com/android/server/wm/Session.java b/services/java/com/android/server/wm/Session.java
index 50b251f..2e0c9ab 100644
--- a/services/java/com/android/server/wm/Session.java
+++ b/services/java/com/android/server/wm/Session.java
@@ -281,8 +281,8 @@
             if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, ">>> OPEN TRANSACTION performDrag");
             Surface.openTransaction();
             try {
-                surface.setPosition((int)(touchX - thumbCenterX),
-                        (int)(touchY - thumbCenterY));
+                surface.setPosition(touchX - thumbCenterX,
+                        touchY - thumbCenterY);
                 surface.setAlpha(.7071f);
                 surface.setLayer(mService.mDragState.getDragLayerLw());
                 surface.show();
diff --git a/services/java/com/android/server/wm/ViewServer.java b/services/java/com/android/server/wm/ViewServer.java
index 70cb26a..a763e2c 100644
--- a/services/java/com/android/server/wm/ViewServer.java
+++ b/services/java/com/android/server/wm/ViewServer.java
@@ -319,7 +319,7 @@
                     }
                 }
             } catch (Exception e) {
-                Slog.w(LOG_TAG, "Connection error: ", e);
+                // Ignore
             } finally {
                 if (out != null) {
                     try {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 4adf304..f5a5e2e 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -8455,8 +8455,8 @@
                                 + Integer.toHexString(diff));
                     }
                     win.mConfiguration = mCurConfiguration;
-                    win.mClient.resized(win.mSurfaceW, win.mSurfaceH, win.mLastContentInsets,
-                            win.mLastVisibleInsets, win.mDrawPending,
+                    win.mClient.resized((int)win.mSurfaceW, (int)win.mSurfaceH,
+                            win.mLastContentInsets, win.mLastVisibleInsets, win.mDrawPending,
                             configChanged ? win.mConfiguration : null);
                     win.mContentInsetsChanged = false;
                     win.mVisibleInsetsChanged = false;
diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index a384b03..bb36d3a7 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -30,6 +30,7 @@
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.graphics.RectF;
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -110,7 +111,7 @@
      * are in the screen's coordinate space (WITH the compatibility scale
      * applied).
      */
-    final Rect mShownFrame = new Rect();
+    final RectF mShownFrame = new RectF();
 
     /**
      * Set when we have changed the size of the surface, to know that
@@ -267,7 +268,7 @@
 
     // For debugging, this is the last information given to the surface flinger.
     boolean mSurfaceShown;
-    int mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
+    float mSurfaceX, mSurfaceY, mSurfaceW, mSurfaceH;
     int mSurfaceLayer;
     float mSurfaceAlpha;
     
@@ -518,7 +519,7 @@
         return mFrame;
     }
 
-    public Rect getShownFrameLw() {
+    public RectF getShownFrameLw() {
         return mShownFrame;
     }
 
@@ -1128,8 +1129,8 @@
             mDtDx = tmpFloats[Matrix.MSKEW_Y];
             mDsDy = tmpFloats[Matrix.MSKEW_X];
             mDtDy = tmpFloats[Matrix.MSCALE_Y];
-            int x = (int)tmpFloats[Matrix.MTRANS_X];
-            int y = (int)tmpFloats[Matrix.MTRANS_Y];
+            float x = tmpFloats[Matrix.MTRANS_X];
+            float y = tmpFloats[Matrix.MTRANS_Y];
             int w = frame.width();
             int h = frame.height();
             mShownFrame.set(x, y, x+w, y+h);
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 3e13a86..bd35058 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -29,6 +29,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
 import android.os.AsyncResult;
@@ -230,8 +231,6 @@
 
     Object     mLastNITZTimeInfo;
 
-    private static final String WIFI_ONLY_CARRIER = "wifi-only";
-
     //***** Events
 
     static final int EVENT_SEND                 = 1;
@@ -626,10 +625,9 @@
         Looper looper = mSenderThread.getLooper();
         mSender = new RILSender(looper);
 
-        // TODO: Provide a common API for determining if a
-        // device is wifi-only. bug: 3480713
-        String carrier = SystemProperties.get("ro.carrier");
-        if (WIFI_ONLY_CARRIER.equals(carrier)) {
+        ConnectivityManager cm = (ConnectivityManager)context.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false) {
             riljLog("Not starting RILReceiver: wifi-only");
         } else {
             riljLog("Starting RILReceiver");
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 274edae..c52142d 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -36,6 +36,7 @@
 import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.util.Slog;
+import android.util.Log;
 
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
@@ -174,7 +175,7 @@
      * It triggers a disableNetwork call if a DNS check fails.
      */
     public boolean mDisableAPNextFailure = false;
-    private ConnectivityManager mConnectivityManager;
+    private static boolean sWifiOnly = false;
     private boolean mNotificationShown;
     public boolean mHasConnectedWifiManager = false;
 
@@ -219,9 +220,14 @@
 
     public static WifiWatchdogStateMachine makeWifiWatchdogStateMachine(Context context) {
         ContentResolver contentResolver = context.getContentResolver();
+
+        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        sWifiOnly = (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE) == false);
+
         // Disable for wifi only devices.
         if (Settings.Secure.getString(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON) == null &&
-                "wifi-only".equals(SystemProperties.get("ro.carrier"))) {
+                sWifiOnly) {
             putSettingsBoolean(contentResolver, Settings.Secure.WIFI_WATCHDOG_ON, false);
         }
         WifiWatchdogStateMachine wwsm = new WifiWatchdogStateMachine(context);
@@ -508,22 +514,6 @@
         }
     }
 
-    /**
-     * @return true if there is definitely no mobile data (we'll be less aggressive)
-     */
-    private boolean hasNoMobileData() {
-        if (mConnectivityManager == null) {
-            mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
-                    Context.CONNECTIVITY_SERVICE);
-        }
-        NetworkInfo mobileNetInfo = mConnectivityManager.getNetworkInfo(
-                ConnectivityManager.TYPE_MOBILE);
-        if (mobileNetInfo == null || !mobileNetInfo.isAvailable()) {
-            return true;
-        }
-        return false;
-    }
-
     class DefaultState extends State {
         @Override
         public boolean processMessage(Message msg) {
@@ -941,7 +931,7 @@
 
             if (mDisableAPNextFailure || mNumCheckFailures >= mBssids.size()
                     || mNumCheckFailures >= mMaxSsidBlacklists) {
-                if (hasNoMobileData()) {
+                if (sWifiOnly) {
                     Slog.w(WWSM_TAG, "Would disable bad network, but device has no mobile data!" +
                             "  Going idle...");
                     // This state should be called idle -- will be changing flow.