Merge change 8858

* changes:
  resolved conflicts for merge of ac38dfc5 to master
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
index 12bdead..d8db8b3 100644
--- a/cmds/stagefright/record.cpp
+++ b/cmds/stagefright/record.cpp
@@ -119,7 +119,8 @@
     enc_meta->setInt32(kKeyWidth, width);
     enc_meta->setInt32(kKeyHeight, height);
 
-    OMXDecoder *encoder = OMXDecoder::CreateEncoder(&client, enc_meta);
+    OMXDecoder *encoder =
+        OMXDecoder::Create(&client, enc_meta, true /* createEncoder */);
 
     encoder->setSource(decoder);
     // encoder->setSource(meta, new DummySource(width, height));
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 961942a..7e23574 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -102,6 +102,7 @@
 int main(int argc, char **argv) {
     android::ProcessState::self()->startThreadPool();
 
+    bool audioOnly = false;
     if (argc > 1 && !strcmp(argv[1], "--list")) {
         sp<IServiceManager> sm = defaultServiceManager();
         sp<IBinder> binder = sm->getService(String16("media.player"));
@@ -121,6 +122,10 @@
         }
 
         return 0;
+    } else if (argc > 1 && !strcmp(argv[1], "--audio")) {
+        audioOnly = true;
+        ++argv;
+        --argc;
     }
 
 #if 0
@@ -149,7 +154,11 @@
         const char *mime;
         meta->findCString(kKeyMIMEType, &mime);
 
-        if (!strncasecmp(mime, "video/", 6)) {
+        if (audioOnly && !strncasecmp(mime, "audio/", 6)) {
+            break;
+        }
+
+        if (!audioOnly && !strncasecmp(mime, "video/", 6)) {
             break;
         }
     }
diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl
index 5486920..188e7ff 100644
--- a/core/java/android/os/IPowerManager.aidl
+++ b/core/java/android/os/IPowerManager.aidl
@@ -26,6 +26,7 @@
     void userActivity(long when, boolean noChangeLights);
     void userActivityWithForce(long when, boolean noChangeLights, boolean force);
     void setPokeLock(int pokey, IBinder lock, String tag);
+    int getSupportedWakeLockFlags();
     void setStayOnSetting(int val);
     long getScreenOnTime();
     void preventScreenOn(boolean prevent);
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index bfcf2fc..d5934102 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -114,12 +114,14 @@
     private static final int WAKE_BIT_SCREEN_DIM = 4;
     private static final int WAKE_BIT_SCREEN_BRIGHT = 8;
     private static final int WAKE_BIT_KEYBOARD_BRIGHT = 16;
+    private static final int WAKE_BIT_PROXIMITY_SCREEN_OFF = 32;
     
     private static final int LOCK_MASK = WAKE_BIT_CPU_STRONG
                                         | WAKE_BIT_CPU_WEAK
                                         | WAKE_BIT_SCREEN_DIM
                                         | WAKE_BIT_SCREEN_BRIGHT
-                                        | WAKE_BIT_KEYBOARD_BRIGHT;
+                                        | WAKE_BIT_KEYBOARD_BRIGHT
+                                        | WAKE_BIT_PROXIMITY_SCREEN_OFF;
 
     /**
      * Wake lock that ensures that the CPU is running.  The screen might
@@ -147,6 +149,16 @@
     public static final int SCREEN_DIM_WAKE_LOCK = WAKE_BIT_CPU_WEAK | WAKE_BIT_SCREEN_DIM;
 
     /**
+     * Wake lock that turns the screen off when the proximity sensor activates.
+     * Since not all devices have proximity sensors, use
+     * {@link #getSupportedWakeLockFlags() getSupportedWakeLockFlags()} to determine if
+     * this wake lock mode is supported.
+     *
+     * {@hide}
+     */
+    public static final int PROXIMITY_SCREEN_OFF_WAKE_LOCK = WAKE_BIT_PROXIMITY_SCREEN_OFF;
+
+    /**
      * Normally wake locks don't actually wake the device, they just cause
      * it to remain on once it's already on.  Think of the video player
      * app as the normal behavior.  Notifications that pop up and want
@@ -196,6 +208,7 @@
             case SCREEN_DIM_WAKE_LOCK:
             case SCREEN_BRIGHT_WAKE_LOCK:
             case FULL_WAKE_LOCK:
+            case PROXIMITY_SCREEN_OFF_WAKE_LOCK:
                 break;
             default:
                 throw new IllegalArgumentException();
@@ -365,7 +378,33 @@
         } catch (RemoteException e) {
         }
     }
-    
+
+   /**
+     * Returns the set of flags for {@link #newWakeLock(int, String) newWakeLock()}
+     * that are supported on the device.
+     * For example, to test to see if the {@link #PROXIMITY_SCREEN_OFF_WAKE_LOCK}
+     * is supported:
+     *
+     * {@samplecode
+     * PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+     * int supportedFlags = pm.getSupportedWakeLockFlags();
+     *  boolean proximitySupported = ((supportedFlags & PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK)
+     *                                  == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK);
+     * }
+     *
+     * @return the set of supported WakeLock flags.
+     *
+     * {@hide}
+     */
+    public int getSupportedWakeLockFlags()
+    {
+        try {
+            return mService.getSupportedWakeLockFlags();
+        } catch (RemoteException e) {
+            return 0;
+        }
+    }
+
     private PowerManager()
     {
     }
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 19df87f..358fc9e 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -351,6 +351,9 @@
     VelocityTracker mVelocityTracker;
     private int mMaximumFling;
 
+    // use this flag to control whether enabling the new double tap zoom
+    static final boolean ENABLE_DOUBLETAP_ZOOM = true;
+
     /**
      * Touch mode
      */
@@ -370,6 +373,7 @@
     private static final int SCROLL_ZOOM_OUT = 11;
     private static final int LAST_SCROLL_ZOOM = 11;
     // end of touch mode values specific to scale+scroll
+    private static final int TOUCH_DOUBLE_TAP_MODE = 12;
 
     // Whether to forward the touch events to WebCore
     private boolean mForwardTouchEvents = false;
@@ -394,6 +398,8 @@
      */
     // pre-computed square of ViewConfiguration.getScaledTouchSlop()
     private int mTouchSlopSquare;
+    // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
+    private int mDoubleTapSlopSquare;
     // pre-computed density adjusted navigation slop
     private int mNavSlop;
     // This should be ViewConfiguration.getTapTimeout()
@@ -450,6 +456,7 @@
     private static final int NEVER_REMEMBER_PASSWORD    = 2;
     private static final int SWITCH_TO_SHORTPRESS       = 3;
     private static final int SWITCH_TO_LONGPRESS        = 4;
+    private static final int RELEASE_SINGLE_TAP         = 5;
     private static final int REQUEST_FORM_DATA          = 6;
     private static final int SWITCH_TO_CLICK            = 7;
     private static final int RESUME_WEBCORE_UPDATE      = 8;
@@ -482,7 +489,7 @@
         "NEVER_REMEMBER_PASSWORD", //        = 2;
         "SWITCH_TO_SHORTPRESS", //           = 3;
         "SWITCH_TO_LONGPRESS", //            = 4;
-        "5",
+        "RELEASE_SINGLE_TAP", //             = 5;
         "REQUEST_FORM_DATA", //              = 6;
         "SWITCH_TO_CLICK", //                = 7;
         "RESUME_WEBCORE_UPDATE", //          = 8;
@@ -521,6 +528,16 @@
     // initial scale in percent. 0 means using default.
     private int mInitialScale = 0;
 
+    // while in the zoom overview mode, the page's width is fully fit to the
+    // current window. The page is alive, in another words, you can click to
+    // follow the links. Double tap will toggle between zoom overview mode and
+    // the last zoom scale.
+    boolean mInZoomOverview = false;
+    // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
+    // engadget always have wider mContentWidth no matter what viewport size is.
+    int mZoomOverviewWidth = 0;
+    float mLastScale;
+
     // default scale. Depending on the display density.
     static int DEFAULT_SCALE_PERCENT;
     private float mDefaultScale;
@@ -762,9 +779,11 @@
         setLongClickable(true);
 
         final ViewConfiguration configuration = ViewConfiguration.get(getContext());
-        final int slop = configuration.getScaledTouchSlop();
+        int slop = configuration.getScaledTouchSlop();
         mTouchSlopSquare = slop * slop;
         mMinLockSnapReverseDistance = slop;
+        slop = configuration.getScaledDoubleTapSlop();
+        mDoubleTapSlopSquare = slop * slop;
         final float density = getContext().getResources().getDisplayMetrics().density;
         // use one line height, 16 based on our current default font, for how
         // far we allow a touch be away from the edge of a link
@@ -1124,6 +1143,9 @@
             b.putInt("scrollX", mScrollX);
             b.putInt("scrollY", mScrollY);
             b.putFloat("scale", mActualScale);
+            if (mInZoomOverview) {
+                b.putFloat("lastScale", mLastScale);
+            }
             return true;
         }
         return false;
@@ -1168,6 +1190,13 @@
                 // onSizeChanged() is called, the rest will be set
                 // correctly
                 mActualScale = scale;
+                float lastScale = b.getFloat("lastScale", -1.0f);
+                if (lastScale > 0) {
+                    mInZoomOverview = true;
+                    mLastScale = lastScale;
+                } else {
+                    mInZoomOverview = false;
+                }
                 invalidate();
                 return true;
             }
@@ -3060,6 +3089,11 @@
                 float y = mLastTouchY + (float) (mScrollY - lp.y);
                 mWebTextView.fakeTouchEvent(x, y);
             }
+            if (mInZoomOverview) {
+                // if in zoom overview mode, call doDoubleTap() to bring it back
+                // to normal mode so that user can enter text.
+                doDoubleTap();
+            }
         }
         else { // used by plugins
             imm.showSoftInput(this, 0);
@@ -3634,7 +3668,9 @@
         // update mMinZoomScale if the minimum zoom scale is not fixed
         if (!mMinZoomScaleFixed) {
             mMinZoomScale = (float) getViewWidth()
-                    / Math.max(ZOOM_OUT_WIDTH, mContentWidth);
+                    / Math.max(ZOOM_OUT_WIDTH, mDrawHistory ? mHistoryPicture
+                            .getWidth() : (mZoomOverviewWidth > 0 ?
+                                    mZoomOverviewWidth : mContentWidth));
         }
 
         // we always force, in case our height changed, in which case we still
@@ -3755,6 +3791,15 @@
                     nativeMoveSelection(viewToContent(mSelectX)
                             , viewToContent(mSelectY), false);
                     mTouchSelection = mExtendSelection = true;
+                } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
+                    mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
+                    if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
+                        mTouchMode = TOUCH_DOUBLE_TAP_MODE;
+                    } else {
+                        // commit the short press action for the previous tap
+                        doShortPress();
+                        // continue, mTouchMode should be still TOUCH_INIT_MODE
+                    }
                 } else {
                     mTouchMode = TOUCH_INIT_MODE;
                     mPreventDrag = mForwardTouchEvents;
@@ -3764,7 +3809,8 @@
                     }
                 }
                 // Trigger the link
-                if (mTouchMode == TOUCH_INIT_MODE) {
+                if (mTouchMode == TOUCH_INIT_MODE
+                        || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
                     mPrivateHandler.sendMessageDelayed(mPrivateHandler
                             .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
                 }
@@ -3810,7 +3856,8 @@
                     if (mTouchMode == TOUCH_SHORTPRESS_MODE
                             || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
                         mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
-                    } else if (mTouchMode == TOUCH_INIT_MODE) {
+                    } else if (mTouchMode == TOUCH_INIT_MODE
+                            || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
                         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
                     }
 
@@ -3836,7 +3883,7 @@
                                 .sendMessage(EventHub.SET_SNAP_ANCHOR, 0, 0);
                     }
                     WebSettings settings = getSettings();
-                    if (settings.supportZoom()
+                    if (settings.supportZoom() && !mInZoomOverview
                             && settings.getBuiltInZoomControls()
                             && !mZoomButtonsController.isVisible()
                             && (canZoomScrollOut() ||
@@ -3905,7 +3952,7 @@
                     mUserScroll = true;
                 }
 
-                if (!getSettings().getBuiltInZoomControls()) {
+                if (!getSettings().getBuiltInZoomControls() && !mInZoomOverview) {
                     boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
                     boolean showMagnify = canZoomScrollOut();
                     if (mZoomControls != null && (showPlusMinus || showMagnify)) {
@@ -3929,7 +3976,22 @@
             case MotionEvent.ACTION_UP: {
                 mLastTouchUpTime = eventTime;
                 switch (mTouchMode) {
+                    case TOUCH_DOUBLE_TAP_MODE: // double tap
+                        mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+                        doDoubleTap();
+                        break;
                     case TOUCH_INIT_MODE: // tap
+                        if (ENABLE_DOUBLETAP_ZOOM) {
+                            mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
+                            if (!mPreventDrag) {
+                                mPrivateHandler.sendMessageDelayed(
+                                        mPrivateHandler.obtainMessage(
+                                        RELEASE_SINGLE_TAP),
+                                        ViewConfiguration.getDoubleTapTimeout());
+                            }
+                            break;
+                        }
+                        // fall through
                     case TOUCH_SHORTPRESS_START_MODE:
                     case TOUCH_SHORTPRESS_MODE:
                         mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
@@ -4365,6 +4427,9 @@
             mInvInitialZoomScale = 1.0f / oldScale;
             mInvFinalZoomScale = 1.0f / mActualScale;
             mZoomScale = mActualScale;
+            if (!mInZoomOverview) {
+                mLastScale = scale;
+            }
             invalidate();
             return true;
         } else {
@@ -4470,6 +4535,9 @@
     public boolean zoomIn() {
         // TODO: alternatively we can disallow this during draw history mode
         switchOutDrawHistory();
+        // Center zooming to the center of the screen.
+        mZoomCenterX = getViewWidth() * .5f;
+        mZoomCenterY = getViewHeight() * .5f;
         return zoomWithPreview(mActualScale * 1.25f);
     }
 
@@ -4480,6 +4548,9 @@
     public boolean zoomOut() {
         // TODO: alternatively we can disallow this during draw history mode
         switchOutDrawHistory();
+        // Center zooming to the center of the screen.
+        mZoomCenterX = getViewWidth() * .5f;
+        mZoomCenterY = getViewHeight() * .5f;
         return zoomWithPreview(mActualScale * 0.8f);
     }
 
@@ -4571,6 +4642,50 @@
         }
     }
 
+    private void doDoubleTap() {
+        if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
+            return;
+        }
+        mZoomCenterX = mLastTouchX;
+        mZoomCenterY = mLastTouchY;
+        mInZoomOverview = !mInZoomOverview;
+        if (mInZoomOverview) {
+            float newScale = (float) getViewWidth()
+                    / (mZoomOverviewWidth > 0 ? mZoomOverviewWidth
+                            : mContentWidth);
+            if (Math.abs(newScale - mActualScale) < 0.01) {
+                mInZoomOverview = !mInZoomOverview;
+                // as it is already full screen, do nothing.
+                return;
+            }
+            if (getSettings().getBuiltInZoomControls()) {
+                if (mZoomButtonsController.isVisible()) {
+                    mZoomButtonsController.setVisible(false);
+                }
+            } else {
+                if (mZoomControlRunnable != null) {
+                    mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+                }
+                if (mZoomControls != null) {
+                    mZoomControls.hide();
+                }
+            }
+            zoomWithPreview(newScale);
+        } else {
+            // mLastTouchX and mLastTouchY are the point in the current viewport
+            int contentX = viewToContent((int) mLastTouchX + mScrollX);
+            int contentY = viewToContent((int) mLastTouchY + mScrollY);
+            int left = nativeGetBlockLeftEdge(contentX, contentY);
+            if (left != NO_LEFTEDGE) {
+                // add a 5pt padding to the left edge. Re-calculate the zoom
+                // center so that the new scroll x will be on the left edge.
+                mZoomCenterX = left < 5 ? 0 : (left - 5) * mLastScale
+                        * mActualScale / (mLastScale - mActualScale);
+            }
+            zoomWithPreview(mLastScale);
+        }
+    }
+
     // Called by JNI to handle a touch on a node representing an email address,
     // address, or phone number
     private void overrideLoading(String url) {
@@ -4791,6 +4906,8 @@
                     if (mTouchMode == TOUCH_INIT_MODE) {
                         mTouchMode = TOUCH_SHORTPRESS_START_MODE;
                         updateSelection();
+                    } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
+                        mTouchMode = TOUCH_DONE_MODE;
                     }
                     break;
                 }
@@ -4802,6 +4919,13 @@
                     }
                     break;
                 }
+                case RELEASE_SINGLE_TAP: {
+                    if (!mPreventDrag) {
+                        mTouchMode = TOUCH_DONE_MODE;
+                        doShortPress();
+                    }
+                    break;
+                }
                 case SWITCH_TO_CLICK:
                     // The user clicked with the trackball, and did not click a
                     // second time, so perform the action of a trackball single
@@ -4854,6 +4978,7 @@
                     break;
                 case NEW_PICTURE_MSG_ID:
                     // called for new content
+                    final int viewWidth = getViewWidth();
                     final WebViewCore.DrawData draw =
                             (WebViewCore.DrawData) msg.obj;
                     final Point viewSize = draw.mViewPoint;
@@ -4861,16 +4986,12 @@
                         // use the same logic in sendViewSizeZoom() to make sure
                         // the mZoomScale has matched the viewSize so that we
                         // can clear mZoomScale
-                        if (Math.round(getViewWidth() / mZoomScale) == viewSize.x) {
+                        if (Math.round(viewWidth / mZoomScale) == viewSize.x) {
                             mZoomScale = 0;
                             mWebViewCore.sendMessage(EventHub.SET_SNAP_ANCHOR,
                                     0, 0);
                         }
                     }
-                    if (!mMinZoomScaleFixed) {
-                        mMinZoomScale = (float) getViewWidth()
-                                / Math.max(ZOOM_OUT_WIDTH, draw.mWidthHeight.x);
-                    }
                     // We update the layout (i.e. request a layout from the
                     // view system) if the last view size that we sent to
                     // WebCore matches the view size of the picture we just
@@ -4888,6 +5009,25 @@
                     if (mPictureListener != null) {
                         mPictureListener.onNewPicture(WebView.this, capturePicture());
                     }
+                    if (mWebViewCore.getSettings().getUseWideViewPort()) {
+                        mZoomOverviewWidth = Math.max(draw.mMinPrefWidth,
+                                draw.mViewPoint.x);
+                    }
+                    if (!mMinZoomScaleFixed) {
+                        mMinZoomScale = (float) viewWidth
+                                / Math.max(ZOOM_OUT_WIDTH,
+                                mZoomOverviewWidth > 0 ? mZoomOverviewWidth
+                                        : mContentWidth);
+                    }
+                    if (!mDrawHistory && mInZoomOverview) {
+                        // fit the content width to the current view. Ignore
+                        // the rounding error case.
+                        if (Math.abs((viewWidth * mInvActualScale)
+                                - mZoomOverviewWidth) > 1) {
+                            zoomWithPreview((float) viewWidth
+                                    / mZoomOverviewWidth);
+                        }
+                    }
                     break;
                 case WEBCORE_INITIALIZED_MSG_ID:
                     // nativeCreate sets mNativeClass to a non-zero value
@@ -4916,7 +5056,7 @@
                         }
                     }
                     break;
-                case DID_FIRST_LAYOUT_MSG_ID:
+                case DID_FIRST_LAYOUT_MSG_ID: {
                     if (mNativeClass == 0) {
                         break;
                     }
@@ -4943,15 +5083,17 @@
                     if (width == 0) {
                         break;
                     }
+                    final WebSettings settings = mWebViewCore.getSettings();
                     int initialScale = msg.arg1;
                     int viewportWidth = msg.arg2;
                     // start a new page with DEFAULT_SCALE zoom scale.
                     float scale = mDefaultScale;
+                    mInZoomOverview = false;
                     if (mInitialScale > 0) {
                         scale = mInitialScale / 100.0f;
                     } else  {
-                        if (initialScale < 0) break;
-                        if (mWebViewCore.getSettings().getUseWideViewPort()) {
+                        if (initialScale == -1) break;
+                        if (settings.getUseWideViewPort()) {
                             // force viewSizeChanged by setting mLastWidthSent
                             // to 0
                             mLastWidthSent = 0;
@@ -4961,11 +5103,21 @@
                             // than the view width, zoom in to fill the view
                             if (viewportWidth > 0 && viewportWidth < width) {
                                 scale = (float) width / viewportWidth;
+                            } else {
+                                if (settings.getUseWideViewPort()) {
+                                    mInZoomOverview = ENABLE_DOUBLETAP_ZOOM;
+                                }
                             }
+                        } else if (initialScale < 0) {
+                            // this should only happen when
+                            // ENABLE_DOUBLETAP_ZOOM is true
+                            mInZoomOverview = true;
+                            scale = -initialScale / 100.0f;
                         } else {
                             scale = initialScale / 100.0f;
                         }
                     }
+                    mLastScale = scale;
                     setNewZoomScale(scale, false);
                     // As we are on a new page, remove the WebTextView.  This
                     // is necessary for page loads driven by webkit, and in
@@ -4973,6 +5125,7 @@
                     // the WebTextView was visible.
                     clearTextEntry();
                     break;
+                }
                 case MOVE_OUT_OF_PLUGIN:
                     if (nativePluginEatsNavKey()) {
                         navHandledKey(msg.arg1, 1, false, 0, true);
@@ -5542,4 +5695,7 @@
     private native void     nativeUpdateCachedTextfield(String updatedText,
             int generation);
     private native void     nativeUpdatePluginReceivesEvents();
+    // return NO_LEFTEDGE means failure.
+    private static final int NO_LEFTEDGE = -1;
+    private native int      nativeGetBlockLeftEdge(int x, int y);
 }
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 4993fcf..36c5f0c8 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -96,7 +96,8 @@
 
     private boolean mViewportUserScalable = true;
 
-    private int mRestoredScale = WebView.DEFAULT_SCALE_PERCENT;
+    private int mRestoredScale = 0;
+    private int mRestoredScreenWidthScale = 0;
     private int mRestoredX = 0;
     private int mRestoredY = 0;
 
@@ -1340,9 +1341,8 @@
             Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
             return;
         }
-        if (mSettings.getUseWideViewPort()
-                && (w < mViewportWidth || mViewportWidth == -1)) {
-            int width = mViewportWidth;
+        if (mSettings.getUseWideViewPort()) {
+            int width;
             if (mViewportWidth == -1) {
                 if (mSettings.getLayoutAlgorithm() ==
                         WebSettings.LayoutAlgorithm.NORMAL) {
@@ -1362,9 +1362,15 @@
                      */
                     width = Math.max(w, nativeGetContentMinPrefWidth());
                 }
+            } else {
+                width = Math.max(w, mViewportWidth);
             }
-            nativeSetSize(width, Math.round((float) width * h / w), w, scale,
-                    w, h);
+            // while in zoom overview mode, the text are wrapped to the screen
+            // width matching mWebView.mLastScale. So that we don't trigger
+            // re-flow while toggling between overview mode and normal mode.
+            nativeSetSize(width, Math.round((float) width * h / w),
+                    Math.round(mWebView.mInZoomOverview ? w * scale
+                            / mWebView.mLastScale : w), scale, w, h);
         } else {
             nativeSetSize(w, h, w, scale, w, h);
         }
@@ -1409,6 +1415,7 @@
         public Region mInvalRegion;
         public Point mViewPoint;
         public Point mWidthHeight;
+        public int mMinPrefWidth;
     }
 
     private void webkitDraw() {
@@ -1424,6 +1431,9 @@
             // Send the native view size that was used during the most recent
             // layout.
             draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
+            if (WebView.ENABLE_DOUBLETAP_ZOOM && mSettings.getUseWideViewPort()) {
+                draw.mMinPrefWidth = nativeGetContentMinPrefWidth();
+            }
             if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
             Message.obtain(mWebView.mPrivateHandler,
                     WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
@@ -1734,9 +1744,10 @@
 
             if (mRestoredScale > 0) {
                 Message.obtain(mWebView.mPrivateHandler,
-                        WebView.DID_FIRST_LAYOUT_MSG_ID, mRestoredScale, 0,
+                        WebView.DID_FIRST_LAYOUT_MSG_ID,
+                        mRestoredScreenWidthScale > 0 ?
+                        -mRestoredScreenWidthScale : mRestoredScale, 0,
                         scaleLimit).sendToTarget();
-                mRestoredScale = 0;
             } else {
                 // if standardLoad is true, use mViewportInitialScale, otherwise
                 // pass -1 to the WebView to indicate no change of the scale.
@@ -1764,8 +1775,8 @@
                 }
             }
 
-            // reset restored offset
-            mRestoredX = mRestoredY = 0;
+            // reset restored offset, scale
+            mRestoredX = mRestoredY = mRestoredScale = mRestoredScreenWidthScale = 0;
         }
     }
 
@@ -1777,6 +1788,17 @@
     }
 
     // called by JNI
+    private void restoreScreenWidthScale(int scale) {
+        if (!WebView.ENABLE_DOUBLETAP_ZOOM || !mSettings.getUseWideViewPort()) {
+            return;
+        }
+
+        if (mBrowserFrame.firstLayoutDone() == false) {
+            mRestoredScreenWidthScale = scale;
+        }
+    }
+
+    // called by JNI
     private void needTouchEvents(boolean need) {
         if (mWebView != null) {
             Message.obtain(mWebView.mPrivateHandler,
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index f829b08..2b9e448 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -96,6 +96,7 @@
     native private int  nAllocationCreatePredefSized(int predef, int count);
     native private int  nAllocationCreateSized(int elem, int count);
     native private int  nAllocationCreateFromBitmap(int dstFmt, boolean genMips, Bitmap bmp);
+    native private int  nAllocationCreateFromBitmapBoxed(int dstFmt, boolean genMips, Bitmap bmp);
 
     native private void nAllocationUploadToTexture(int alloc, int baseMioLevel);
     native private void nAllocationDestroy(int alloc);
@@ -529,7 +530,12 @@
     }
 
     public Allocation allocationCreateFromBitmap(Bitmap b, ElementPredefined dstFmt, boolean genMips) {
-        int id = nAllocationCreateFromBitmap(dstFmt.mID, genMips, b); 
+        int id = nAllocationCreateFromBitmap(dstFmt.mID, genMips, b);
+        return new Allocation(id);
+    }
+
+    public Allocation allocationCreateFromBitmapBoxed(Bitmap b, ElementPredefined dstFmt, boolean genMips) {
+        int id = nAllocationCreateFromBitmapBoxed(dstFmt.mID, genMips, b);
         return new Allocation(id);
     }
 
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 573610c..6f781a0 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -291,6 +291,29 @@
     return 0;
 }
 
+static int
+nAllocationCreateFromBitmapBoxed(JNIEnv *_env, jobject _this, jint dstFmt, jboolean genMips, jobject jbitmap)
+{
+    RsContext con = (RsContext)(_env->GetIntField(_this, gContextId));
+    SkBitmap const * nativeBitmap =
+            (SkBitmap const *)_env->GetIntField(jbitmap, gNativeBitmapID);
+    const SkBitmap& bitmap(*nativeBitmap);
+    SkBitmap::Config config = bitmap.getConfig();
+
+    RsElementPredefined e = SkBitmapToPredefined(config);
+
+    if (e != RS_ELEMENT_USER_U8) {
+        bitmap.lockPixels();
+        const int w = bitmap.width();
+        const int h = bitmap.height();
+        const void* ptr = bitmap.getPixels();
+        jint id = (jint)rsAllocationCreateFromBitmapBoxed(w, h, (RsElementPredefined)dstFmt, e, genMips, ptr);
+        bitmap.unlockPixels();
+        return id;
+    }
+    return 0;
+}
+
 
 static void
 nAllocationDestroy(JNIEnv *_env, jobject _this, jint a)
@@ -994,6 +1017,7 @@
 {"nAllocationCreatePredefSized",   "(II)I",                                (void*)nAllocationCreatePredefSized },
 {"nAllocationCreateSized",         "(II)I",                                (void*)nAllocationCreateSized },
 {"nAllocationCreateFromBitmap",    "(IZLandroid/graphics/Bitmap;)I",       (void*)nAllocationCreateFromBitmap },
+{"nAllocationCreateFromBitmapBoxed","(IZLandroid/graphics/Bitmap;)I",       (void*)nAllocationCreateFromBitmapBoxed },
 {"nAllocationUploadToTexture",     "(II)V",                                (void*)nAllocationUploadToTexture },
 {"nAllocationDestroy",             "(I)V",                                 (void*)nAllocationDestroy },
 {"nAllocationData",                "(I[I)V",                               (void*)nAllocationData_i },
diff --git a/include/media/stagefright/OMXDecoder.h b/include/media/stagefright/OMXDecoder.h
index 0859457..e76fd4c 100644
--- a/include/media/stagefright/OMXDecoder.h
+++ b/include/media/stagefright/OMXDecoder.h
@@ -26,6 +26,8 @@
 #include <utils/List.h>
 #include <utils/threads.h>
 
+#include <OMX_Video.h>
+
 namespace android {
 
 class OMXMediaBuffer;
@@ -35,10 +37,8 @@
                    public MediaBufferObserver {
 public:
     static OMXDecoder *Create(
-            OMXClient *client, const sp<MetaData> &data);
-
-    static OMXDecoder *CreateEncoder(
-            OMXClient *client, const sp<MetaData> &data);
+            OMXClient *client, const sp<MetaData> &data,
+            bool createEncoder = false);
 
     virtual ~OMXDecoder();
 
@@ -68,10 +68,23 @@
     };
 
     enum PortStatus {
-        kPortStatusActive   = 0,
-        kPortStatusDisabled = 1,
-        kPortStatusShutdown = 2,
-        kPortStatusFlushing = 3
+        kPortStatusActive             = 0,
+        kPortStatusDisabled           = 1,
+        kPortStatusShutdown           = 2,
+        kPortStatusFlushing           = 3,
+        kPortStatusFlushingToDisabled = 4,
+        kPortStatusFlushingToShutdown = 5,
+    };
+
+    enum Quirks {
+        kWantsRawNALFrames                   = 1,
+        kDoesntReturnBuffersOnDisable        = 2,
+        kDoesntFlushOnExecutingToIdle        = 4,
+        kDoesntProperlyFlushAllPortsAtOnce   = 8,
+        kRequiresAllocateBufferOnInputPorts  = 16,
+        kRequiresAllocateBufferOnOutputPorts = 32,
+        kRequiresLoadedToIdleAfterAllocation = 64,
+        kMeasuresTimeInMilliseconds          = 128,
     };
 
     OMXClient *mClient;
@@ -79,6 +92,8 @@
     IOMX::node_id mNode;
     char *mComponentName;
     bool mIsMP3;
+    bool mIsAVC;
+    uint32_t mQuirks;
 
     MediaSource *mSource;
     sp<MetaData> mOutputFormat;
@@ -116,7 +131,8 @@
     bool mReachedEndOfInput;
 
     OMXDecoder(OMXClient *client, IOMX::node_id node,
-               const char *mime, const char *codec);
+               const char *mime, const char *codec,
+               uint32_t quirks);
 
     void setPortStatus(OMX_U32 port_index, PortStatus status);
     PortStatus getPortStatus(OMX_U32 port_index) const;
@@ -125,7 +141,13 @@
 
     void setAMRFormat();
     void setAACFormat();
-    void setVideoOutputFormat(OMX_U32 width, OMX_U32 height);
+
+    status_t setVideoPortFormatType(
+            OMX_U32 portIndex,
+            OMX_VIDEO_CODINGTYPE compressionFormat,
+            OMX_COLOR_FORMATTYPE colorFormat);
+
+    void setVideoOutputFormat(const char *mime, OMX_U32 width, OMX_U32 height);
     void setup();
     void dumpPortDefinition(OMX_U32 port_index);
 
@@ -144,6 +166,7 @@
 
     void freeInputBuffer(IOMX::buffer_id buffer);
     void freeOutputBuffer(IOMX::buffer_id buffer);
+    void freePortBuffers(OMX_U32 port_index);
 
     void postStart();
     void postEmptyBufferDone(IOMX::buffer_id buffer);
diff --git a/include/media/stagefright/TIHardwareRenderer.h b/include/media/stagefright/TIHardwareRenderer.h
new file mode 100644
index 0000000..f7fa81b
--- /dev/null
+++ b/include/media/stagefright/TIHardwareRenderer.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#ifndef TI_HARDWARE_RENDERER_H_
+
+#define TI_HARDWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+class ISurface;
+class Overlay;
+
+class TIHardwareRenderer : public VideoRenderer {
+public:
+    TIHardwareRenderer(
+            const sp<ISurface> &surface,
+            size_t displayWidth, size_t displayHeight,
+            size_t decodedWidth, size_t decodedHeight);
+
+    virtual ~TIHardwareRenderer();
+
+    virtual void render(
+            const void *data, size_t size, void *platformPrivate);
+
+private:
+    sp<ISurface> mISurface;
+    size_t mDisplayWidth, mDisplayHeight;
+    size_t mDecodedWidth, mDecodedHeight;
+    size_t mFrameSize;
+    sp<Overlay> mOverlay;
+    Vector<void *> mOverlayAddresses;
+    size_t mIndex;
+
+    TIHardwareRenderer(const TIHardwareRenderer &);
+    TIHardwareRenderer &operator=(const TIHardwareRenderer &);
+};
+
+}  // namespace android
+
+#endif  // TI_HARDWARE_RENDERER_H_
+
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 855ea63..1d14f70 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -146,26 +146,26 @@
 };
 
 enum RsBlendSrcFunc {
-    RS_BLEND_SRC_ZERO, 
-    RS_BLEND_SRC_ONE, 
-    RS_BLEND_SRC_DST_COLOR, 
-    RS_BLEND_SRC_ONE_MINUS_DST_COLOR, 
-    RS_BLEND_SRC_SRC_ALPHA, 
-    RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA, 
-    RS_BLEND_SRC_DST_ALPHA, 
-    RS_BLEND_SRC_ONE_MINUS_DST_ALPHA, 
-    RS_BLEND_SRC_SRC_ALPHA_SATURATE
+    RS_BLEND_SRC_ZERO,                  // 0
+    RS_BLEND_SRC_ONE,                   // 1
+    RS_BLEND_SRC_DST_COLOR,             // 2
+    RS_BLEND_SRC_ONE_MINUS_DST_COLOR,   // 3
+    RS_BLEND_SRC_SRC_ALPHA,             // 4
+    RS_BLEND_SRC_ONE_MINUS_SRC_ALPHA,   // 5
+    RS_BLEND_SRC_DST_ALPHA,             // 6
+    RS_BLEND_SRC_ONE_MINUS_DST_ALPHA,   // 7
+    RS_BLEND_SRC_SRC_ALPHA_SATURATE     // 8
 };
 
 enum RsBlendDstFunc {
-    RS_BLEND_DST_ZERO, 
-    RS_BLEND_DST_ONE, 
-    RS_BLEND_DST_SRC_COLOR, 
-    RS_BLEND_DST_ONE_MINUS_SRC_COLOR, 
-    RS_BLEND_DST_SRC_ALPHA, 
-    RS_BLEND_DST_ONE_MINUS_SRC_ALPHA, 
-    RS_BLEND_DST_DST_ALPHA, 
-    RS_BLEND_DST_ONE_MINUS_DST_ALPHA
+    RS_BLEND_DST_ZERO,                  // 0
+    RS_BLEND_DST_ONE,                   // 1
+    RS_BLEND_DST_SRC_COLOR,             // 2
+    RS_BLEND_DST_ONE_MINUS_SRC_COLOR,   // 3
+    RS_BLEND_DST_SRC_ALPHA,             // 4
+    RS_BLEND_DST_ONE_MINUS_SRC_ALPHA,   // 5
+    RS_BLEND_DST_DST_ALPHA,             // 6
+    RS_BLEND_DST_ONE_MINUS_DST_ALPHA    // 7
 };
 
 enum RsTexEnvMode {
diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h
index 0789301..7a5556e 100644
--- a/libs/rs/RenderScriptEnv.h
+++ b/libs/rs/RenderScriptEnv.h
@@ -30,11 +30,3 @@
 #define RS_PROGRAM_VERTEX_PROJECTION_OFFSET 16
 #define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32
 
-//typedef int (*rsc_RunScript)(uint32_t launchIndex, const rsc_FunctionTable *);
-
-
-/* EnableCap */
-#define GL_LIGHTING                       0x0B50
-
-/* LightName */
-#define GL_LIGHT0                         0x4000
diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c
index 6885251..495fe55 100644
--- a/libs/rs/java/Film/res/raw/filmstrip.c
+++ b/libs/rs/java/Film/res/raw/filmstrip.c
@@ -11,29 +11,29 @@
     int32_t triangleOffsets[64];
     float triangleOffsetsTex[64];
     int32_t triangleOffsetsCount;
-} FilmScriptUserEnv; 
-*/ 
+} FilmScriptUserEnv;
+*/
+
+#define POS_TRANSLATE 0
+#define POS_ROTATE 1
+#define POS_FOCUS 2
+
+#define STATE_TRIANGLE_OFFSET_COUNT 0
+#define STATE_LAST_FOCUS 1
+
 
 // The script enviroment has 3 env allocations.
 // bank0: (r) The enviroment structure
 // bank1: (r) The position information
 // bank2: (rw) The temporary texture state
 
-int main(int index) 
+int main(int index)
 {
     int f1,f2,f3,f4, f5,f6,f7,f8, f9,f10,f11,f12, f13,f14,f15,f16;
     int g1,g2,g3,g4, g5,g6,g7,g8, g9,g10,g11,g12, g13,g14,g15,g16;
-    float trans;
-    float rot;
-    int x;
-    float focusPos;  // float
-    int focusID;
-    int lastFocusID;
-    int imgCount;
 
-    trans = loadF(1, 0);
-    rot = loadF(1, 1);
-
+    float trans = loadF(1, POS_TRANSLATE);
+    float rot = loadF(1, POS_ROTATE);
     matrixLoadScale(&f16, 2.f, 2.f, 2.f);
     matrixTranslate(&f16, 0.f, 0.f, trans);
     matrixRotate(&f16, 90.f, 0.f, 0.f, 1.f);
@@ -46,24 +46,18 @@
     drawTriangleMesh(NAMED_mesh);
 
 
-
-    //int imgId = 0;
-
+    // Start of images.
     bindProgramFragmentStore(NAMED_PFImages);
     bindProgramFragment(NAMED_PFSImages);
     bindProgramVertex(NAMED_PVImages);
 
-    //focusPos = loadF(1, 2);
-    //focusID = 0;
-    //lastFocusID = loadI32(2, 0);
-    //imgCount = 13;
+    float focusPos = loadF(1, POS_FOCUS);
+    int focusID = 0;
+    int lastFocusID = loadI32(2, STATE_LAST_FOCUS);
+    int imgCount = 13;
 
-    /*
-    disable(GL_LIGHTING);
-
-
-    if (trans > (-.3)) {
-        focusID = -1.0 - focusPos;
+    if (trans > (-.3f)) {
+        focusID = -1.0f - focusPos;
         if (focusID >= imgCount) {
             focusID = -1;
         }
@@ -71,6 +65,7 @@
         focusID = -1;
     }
 
+    /*
     if (focusID != lastFocusID) {
         if (lastFocusID >= 0) {
             uploadToTexture(con, env->tex[lastFocusID], 1);
@@ -79,36 +74,38 @@
             uploadToTexture(con, env->tex[focusID], 0);
         }
     }
-    storeEnvI32(con, 2, 0, focusID);
+    */
+    storeI32(2, STATE_LAST_FOCUS, focusID);
 
+    int triangleOffsetsCount = loadI32(2, STATE_TRIANGLE_OFFSET_COUNT);
 
+    int imgId = 0;
     for (imgId=1; imgId <= imgCount; imgId++) {
-        float pos = focusPos + imgId + .4f;
-        int offset = (int)floor(pos*2);
-        pos -= 0.75;
-    
-        offset += env->triangleOffsetsCount / 2;
-    
-        if ((offset < 0) || (offset >= env->triangleOffsetsCount)) {
-            continue;
+        float pos = focusPos + imgId + 0.4f;
+        int offset = (int)floorf(pos * 2.f);
+        pos = pos - 0.75f;
+
+        offset = offset + triangleOffsetsCount / 2;
+
+        if (!((offset < 0) || (offset >= triangleOffsetsCount))) {
+            int start = offset -2;
+            int end = offset + 2;
+
+            if (start < 0) {
+                start = 0;
+            }
+            if (end > triangleOffsetsCount) {
+                end = triangleOffsetsCount;
+            }
+
+            bindTexture(NAMED_PFImages, 0, loadI32(0, imgId - 1));
+    /*
+            matrixLoadTranslate(con, &m, -pos - env->triangleOffsetsTex[env->triangleOffsetsCount / 2], 0, 0);
+            storeEnvMatrix(con, 3, RS_PROGRAM_VERTEX_TEXTURE_OFFSET, &m);
+            renderTriangleMeshRange(con, env->mesh, env->triangleOffsets[start], env->triangleOffsets[end] - env->triangleOffsets[start]);
+    */
         }
-    
-        int start = offset -2;
-        int end = offset + 2;
-    
-        if (start < 0) {
-            start = 0;
-        }
-        if (end > env->triangleOffsetsCount) {
-            end = env->triangleOffsetsCount;
-        }
-    
-        programFragmentBindTexture(con, env->fpImages, 0, env->tex[imgId - 1]);
-        matrixLoadTranslate(con, &m, -pos - env->triangleOffsetsTex[env->triangleOffsetsCount / 2], 0, 0); 
-        storeEnvMatrix(con, 3, RS_PROGRAM_VERTEX_TEXTURE_OFFSET, &m);
-        renderTriangleMeshRange(con, env->mesh, env->triangleOffsets[start], env->triangleOffsets[end] - env->triangleOffsets[start]);
-    } 
-*/
+    }
     return 0;
 }
 
diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java
index fca0818..395bd35 100644
--- a/libs/rs/java/Film/src/com/android/film/FilmRS.java
+++ b/libs/rs/java/Film/src/com/android/film/FilmRS.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
@@ -38,6 +39,12 @@
 import android.view.MotionEvent;
 
 public class FilmRS {
+    private final int POS_TRANSLATE = 0;
+    private final int POS_ROTATE = 1;
+    private final int POS_FOCUS = 2;
+
+    private final int STATE_TRIANGLE_OFFSET_COUNT = 0;
+    private final int STATE_LAST_FOCUS = 1;
 
     public FilmRS() {
     }
@@ -56,11 +63,11 @@
         if (x > 270) {
             x = 270;
         }
-    
+
         float anim = ((float)x-50) / 270.f;
-        mBufferPos[0] = 2f * anim + 0.5f;   // translation
-        mBufferPos[1] = (anim * 40);  // rotation
-        mBufferPos[2] = ((float)y) / 16.f - 8;  // focusPos
+        mBufferPos[POS_TRANSLATE] = 2f * anim + 0.5f;   // translation
+        mBufferPos[POS_ROTATE] = (anim * 40);  // rotation
+        mBufferPos[POS_FOCUS] = ((float)y) / 16.f - 8;  // focusPos
         mAllocPos.data(mBufferPos);
     }
 
@@ -80,15 +87,19 @@
     private RenderScript.ProgramVertex mPVImages;
     private ProgramVertexAlloc mPVA;
 
-    private RenderScript.Allocation mAllocEnv;
+    private RenderScript.Allocation mImages[];
+    private RenderScript.Allocation mAllocIDs;
     private RenderScript.Allocation mAllocPos;
     private RenderScript.Allocation mAllocState;
     private RenderScript.Allocation mAllocPV;
     private RenderScript.TriangleMesh mMesh;
     private RenderScript.Light mLight;
 
-    private float[] mBufferPos;
-    private float[] mBufferPV;
+    private FilmStripMesh mFSM;
+
+    private int[] mBufferIDs;
+    private float[] mBufferPos = new float[3];
+    private int[] mBufferState;
 
     private void initSamplers() {
         mRS.samplerBegin();
@@ -112,7 +123,7 @@
         mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.EQUAL);
         mRS.programFragmentStoreDitherEnable(false);
         mRS.programFragmentStoreDepthMask(false);
-        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, 
+        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE,
                                           RenderScript.BlendDstFunc.ONE);
         mPFSImages = mRS.programFragmentStoreCreate();
         mPFSImages.setName("PFSImages");
@@ -148,7 +159,75 @@
     }
 
 
-    int mParams[] = new int[10];
+    private void loadImages() {
+        mBufferIDs = new int[13];
+        mImages = new RenderScript.Allocation[13];
+        mAllocIDs = mRS.allocationCreatePredefSized(
+            RenderScript.ElementPredefined.USER_FLOAT,
+            mBufferIDs.length);
+
+        Bitmap b;
+        BitmapFactory.Options opts = new BitmapFactory.Options();
+        opts.inScaled = false;
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p01, opts);
+        mImages[0] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p02, opts);
+        mImages[1] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p03, opts);
+        mImages[2] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p04, opts);
+        mImages[3] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p05, opts);
+        mImages[4] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p06, opts);
+        mImages[5] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p07, opts);
+        mImages[6] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p08, opts);
+        mImages[7] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p09, opts);
+        mImages[8] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p10, opts);
+        mImages[9] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p11, opts);
+        mImages[10] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p12, opts);
+        mImages[11] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        b = BitmapFactory.decodeResource(mRes, R.drawable.p13, opts);
+        mImages[12] = mRS.allocationCreateFromBitmapBoxed(b, RenderScript.ElementPredefined.RGB_565, true);
+
+        for(int ct=0; ct < mImages.length; ct++) {
+            mImages[ct].uploadToTexture(1);
+            mBufferIDs[ct] = mImages[ct].getID();
+        }
+        mAllocIDs.data(mBufferIDs);
+    }
+
+    private void initState()
+    {
+        mBufferState = new int[10];
+        mAllocState = mRS.allocationCreatePredefSized(
+            RenderScript.ElementPredefined.USER_FLOAT,
+            mBufferState.length);
+
+        mBufferState[STATE_TRIANGLE_OFFSET_COUNT] = mFSM.mTriangleOffsetsCount;
+        mBufferState[STATE_LAST_FOCUS] = -1;
+
+        mAllocState.data(mBufferState);
+    }
 
     private void initRS() {
         mElementVertex = mRS.elementGetPredefined(
@@ -157,8 +236,8 @@
             RenderScript.ElementPredefined.INDEX_16);
 
         mRS.triangleMeshBegin(mElementVertex, mElementIndex);
-        FilmStripMesh fsm = new FilmStripMesh();
-        fsm.init(mRS);
+        mFSM = new FilmStripMesh();
+        mFSM.init(mRS);
         mMesh = mRS.triangleMeshCreate();
         mMesh.setName("mesh");
 
@@ -176,19 +255,22 @@
         mRS.scriptCSetRoot(true);
         mScriptStrip = mRS.scriptCCreate();
 
-        mBufferPos = new float[3];
         mAllocPos = mRS.allocationCreatePredefSized(
-            RenderScript.ElementPredefined.USER_FLOAT, 
+            RenderScript.ElementPredefined.USER_FLOAT,
             mBufferPos.length);
 
+        loadImages();
+        initState();
+
         mPVA = new ProgramVertexAlloc(mRS);
         mPVBackground.bindAllocation(0, mPVA.mAlloc);
         mPVImages.bindAllocation(0, mPVA.mAlloc);
         mPVA.setupProjectionNormalized(320, 480);
 
 
+        mScriptStrip.bindAllocation(mAllocIDs, 0);
         mScriptStrip.bindAllocation(mAllocPos, 1);
-       //mScriptStrip.bindAllocation(gStateAlloc, 2);
+        mScriptStrip.bindAllocation(mAllocState, 2);
         mScriptStrip.bindAllocation(mPVA.mAlloc, 3);
 
 
diff --git a/libs/rs/java/Rollo/res/raw/rollo.c b/libs/rs/java/Rollo/res/raw/rollo.c
index d338d0d..960fdf0 100644
--- a/libs/rs/java/Rollo/res/raw/rollo.c
+++ b/libs/rs/java/Rollo/res/raw/rollo.c
@@ -51,7 +51,7 @@
 
     float touchCut = 1.f;
     if (loadI32(0, STATE_TOUCH)) {
-        touchCut = 5.f;
+        touchCut = 4.f;
     }
 
 
@@ -60,14 +60,17 @@
     storeF(2, SCRATCH_ZOOM, zoom);
 
     float targetRot = loadI32(0, STATE_FIRST_VISIBLE) / 180.0f * 3.14f;
-    float rot = filter(loadF(2, SCRATCH_ROT), targetRot, 0.1f * touchCut);
-    storeF(2, SCRATCH_ROT, rot);
+    float drawRot = filter(loadF(2, SCRATCH_ROT), targetRot, 0.1f * touchCut);
+    storeF(2, SCRATCH_ROT, drawRot);
 
-    float diam = 8.f;// + curve * 2.f;
+    float diam = 10.f;
     float scale = 1.0f / zoom;
 
-    rot = rot * scale;
-    float rotStep = 20.0f / 180.0f * 3.14f * scale;
+    // Bug makes 1.0f alpha fail.
+    color(1.0f, 1.0f, 1.0f, 0.99f);
+
+    float rot = drawRot * scale;
+    float rotStep = 16.0f / 180.0f * 3.14f * scale;
     rowCount = 4;
     int index = 0;
     int iconCount = loadI32(0, STATE_COUNT);
@@ -82,24 +85,55 @@
 
         int y;
         for (y = rowCount -1; (y >= 0) && iconCount; y--) {
-            float ty1 = ((y * 3.0f) - 4.5f) * scale;
+            float ty1 = ((y * 3.5f) - 6.f) * scale;
             float ty2 = ty1 + scale * 2.f;
-            bindTexture(NAMED_PF, 0, loadI32(1, y));
-            color(1.0f, 1.0f, 1.0f, 1.0f);
-            if (done && (index != selectedID)) {
-                color(0.4f, 0.4f, 0.4f, 1.0f);
-            }
+            bindTexture(NAMED_PF, 0, loadI32(1, index));
+            //if (done && (index != selectedID)) {
+                //color(0.4f, 0.4f, 0.4f, 1.0f);
+            //}
             drawQuad(tx1, ty1, tz1,
                      tx2, ty1, tz2,
                      tx2, ty2, tz2,
                      tx1, ty2, tz1);
+
             iconCount--;
             index++;
         }
         rot = rot + rotStep;
     }
 
-    return 0;
+    // Draw the selected icon
+    color(1.0f, 1.0f, 1.0f, 0.9f);
+    rot = drawRot * scale;
+    index = 0;
+    iconCount = loadI32(0, STATE_COUNT);
+    while (iconCount) {
+        int y;
+        for (y = rowCount -1; (y >= 0) && iconCount; y--) {
+            if (index == selectedID) {
+
+                float tmpSin = sinf(rot) * scale;
+                float tmpCos = cosf(rot) * scale;
+                float tx1 = tmpSin * diam * 0.9f - tmpCos * 2.f;
+                float tx2 = tx1 + (tmpCos * 4.f);
+                float tz1 = tmpCos * diam * 0.9f + tmpSin * 2.f;
+                float tz2 = tz1 - (tmpSin * 4.f);
+
+                float ty1 = ((y * 3.5f) - 5.f) * scale;
+                float ty2 = ty1 + scale * 4.f;
+                bindTexture(NAMED_PF, 0, loadI32(1, index));
+                drawQuad(tx1, ty1, tz1,
+                         tx2, ty1, tz2,
+                         tx2, ty2, tz2,
+                         tx1, ty2, tz1);
+            }
+            iconCount--;
+            index++;
+        }
+        rot = rot + rotStep;
+    }
+
+    return 1;
 }
 
 
diff --git a/libs/rs/java/Rollo/res/raw/rollo2.c b/libs/rs/java/Rollo/res/raw/rollo2.c
index b04ea73..256fa3c 100644
--- a/libs/rs/java/Rollo/res/raw/rollo2.c
+++ b/libs/rs/java/Rollo/res/raw/rollo2.c
@@ -3,65 +3,153 @@
 #pragma stateFragment(PF)
 #pragma stateFragmentStore(PFS)
 
-void drawLoop(int x, int y, int z, int rot)
+// Scratch buffer layout
+#define SCRATCH_FADE 0
+#define SCRATCH_ZOOM 1
+#define SCRATCH_ROT 2
+
+//#define STATE_POS_X             0
+#define STATE_DONE              1
+//#define STATE_PRESSURE          2
+#define STATE_ZOOM              3
+//#define STATE_WARP              4
+#define STATE_ORIENTATION       5
+#define STATE_SELECTION         6
+#define STATE_FIRST_VISIBLE     7
+#define STATE_COUNT             8
+#define STATE_TOUCH             9
+
+float filter(float val, float target, float str)
 {
-    int ct;
-    int tx;
-    int ty;
-    int tmpSin;
-    int tmpCos;
-    int sz;
-
-    for (ct = 0; ct < 10; ct ++) {
-        tmpSin = sinx((ct * 36 + rot) * 0x10000);
-        tmpCos = cosx((ct * 36 + rot) * 0x10000);
-
-        ty = y + tmpCos * 4;
-        tx = x + tmpSin * 4;
-        pfBindTexture(NAMED_PF, 0, loadI32(1, ct & 3));
-
-        sz = 0xc000;
-        drawQuad(tx - sz, ty - sz, z,
-                 tx + sz, ty - sz, z,
-                 tx + sz, ty + sz, z,
-                 tx - sz, ty + sz, z);
-    }
+    float delta = (target - val);
+    return val + delta * str;
 }
 
+
 int main(void* con, int ft, int launchID)
 {
     int rowCount;
-    int x;
-    int y;
-    int row;
-    int col;
     int imageID;
-    int tx1;
-    int ty1;
-    int tz1;
-    int tx2;
-    int ty2;
-    int tz2;
-    int tmpSin;
-    int tmpCos;
-    int iconCount;
-    int pressure;
+    int done = loadI32(0, STATE_DONE);
+    int selectedID = loadI32(0, STATE_SELECTION);
+    int iconCount = loadI32(0, STATE_COUNT);
 
-    int ringCount;
+    float f = loadF(2, 0);
 
+    float iconSize = 1.f;
+    float iconSpacing = 0.2f;
+    float z = 4.f;
 
-
-    rotStep = 16 * 0x10000;
-    pressure = loadI32(0, 2);
-    rowCount = 4;
-
-    iconCount = loadI32(0, 1);
-    rot = (-20 + loadI32(0, 0)) * 0x10000;
-
-    for (ringCount = 0; ringCount < 5; ringCount++) {
-        drawLoop(0, 0, 0x90000 + (ringCount * 0x80000));
+    pfClearColor(0.0f, 0.0f, 0.0f, f);
+    if (done) {
+    } else {
+        if (f < 0.8f) {
+            f = f + 0.02f;
+            storeF(2, 0, f);
+        }
     }
 
-    return 0;
+    float touchCut = 1.f;
+    if (loadI32(0, STATE_TOUCH)) {
+        touchCut = 5.f;
+    }
+
+
+    float targetZoom = ((float)loadI32(0, STATE_ZOOM)) / 1000.f;
+    float zoom = filter(loadF(2, SCRATCH_ZOOM), targetZoom, 0.15 * touchCut);
+    storeF(2, SCRATCH_ZOOM, zoom);
+
+    float targetPos = loadI32(0, STATE_FIRST_VISIBLE) / (-20.0f);
+    float pos = filter(loadF(2, SCRATCH_ROT), targetPos, 0.1f * touchCut);
+    storeF(2, SCRATCH_ROT, pos);
+    pos = pos - 1.f;
+
+    color(1.0f, 1.0f, 1.0f, 1.0f);
+
+
+    // Draw flat icons first
+    int index = ((int)pos) * 4;
+    int row;
+    int col;
+    float xoffset = -0.3f;
+    float gridSize = iconSize * 4.f + iconSpacing * 3.f;
+    float yoffset = (pos - ((int)pos));
+    for (row = 0; row < 4; row ++) {
+        float ty1 = (gridSize / 2.f) - ((float)row - yoffset) * (iconSize + iconSpacing) - iconSize;
+        float ty2 = ty1 + iconSize;
+
+        for (col = 0; (col < 4) && (index < iconCount); col ++) {
+            if (index >= 0) {
+                bindTexture(NAMED_PF, 0, loadI32(1, index));
+                float fcol = col;
+                float tx1 = xoffset + (-gridSize / 2.f) + (fcol * (iconSize + iconSpacing));
+                float tx2 = tx1 + iconSize;
+
+                drawQuad(tx1, ty1, z,
+                         tx2, ty1, z,
+                         tx2, ty2, z,
+                         tx1, ty2, z);
+            }
+            index++;
+        }
+    }
+
+    // bottom roller
+    {
+        float roll = (1.f - yoffset) * 0.5f * 3.14f;
+        float tmpSin = sinf(roll);
+        float tmpCos = cosf(roll);
+
+        for (col = 0; (col < 4) && (index < iconCount) && (index >= 0); col ++) {
+            float ty2 = (gridSize / 2.f) - ((float)row - yoffset) * (iconSize + iconSpacing);
+            float ty1 = ty2 - tmpCos * iconSize;
+
+            float tz1 = z + tmpSin * iconSize;
+            float tz2 = z;
+
+            float tx1 = xoffset + (-gridSize / 2.f) + ((float)col * (iconSize + iconSpacing));
+            float tx2 = tx1 + iconSize;
+
+            bindTexture(NAMED_PF, 0, loadI32(1, index));
+            drawQuad(tx1, ty1, tz1,
+                     tx2, ty1, tz1,
+                     tx2, ty2, tz2,
+                     tx1, ty2, tz2);
+            index++;
+        }
+    }
+
+    // Top roller
+    {
+        index = (((int)pos) * 4) - 4;
+        float roll = yoffset * 0.5f * 3.14f;
+        float tmpSin = sinf(roll);
+        float tmpCos = cosf(roll);
+
+        for (col = 0; (col < 4) && (index < iconCount) && (index >= 0); col ++) {
+            float ty1 = (gridSize / 2.f) - ((float)-1.f - yoffset) * (iconSize + iconSpacing) - iconSize;
+            float ty2 = ty1 + tmpCos * iconSize;
+
+            float tz1 = z;
+            float tz2 = z + tmpSin * iconSize;
+
+            float tx1 = xoffset + (-gridSize / 2.f) + ((float)col * (iconSize + iconSpacing));
+            float tx2 = tx1 + iconSize;
+
+            bindTexture(NAMED_PF, 0, loadI32(1, index));
+            drawQuad(tx1, ty1, tz1,
+                     tx2, ty1, tz1,
+                     tx2, ty2, tz2,
+                     tx1, ty2, tz2);
+            index++;
+        }
+    }
+
+
+
+
+    return 1;
 }
 
+
+
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
index 8f48335..14afaf8 100644
--- a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java
@@ -18,6 +18,7 @@
 
 import java.io.Writer;
 
+import android.renderscript.RSSurfaceView;
 import android.renderscript.RenderScript;
 import android.renderscript.ProgramVertexAlloc;
 
@@ -95,8 +96,11 @@
     private RenderScript.ProgramFragment mPFImages;
     private RenderScript.ProgramVertex mPV;
     private ProgramVertexAlloc mPVAlloc;
+    private RenderScript.ProgramVertex mPVOrtho;
+    private ProgramVertexAlloc mPVOrthoAlloc;
     private RenderScript.Allocation[] mIcons;
     private RenderScript.Allocation mIconPlate;
+    private RenderScript.Allocation mBackground;
 
     private int[] mAllocStateBuf;
     private RenderScript.Allocation mAllocState;
@@ -130,23 +134,28 @@
         mRS.programFragmentStoreBegin(null, null);
         mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.LESS);
         mRS.programFragmentStoreDitherEnable(false);
-        mRS.programFragmentStoreDepthMask(false);
-        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, 
-                                          RenderScript.BlendDstFunc.ONE);
+        mRS.programFragmentStoreDepthMask(true);
+        mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA,
+                                          RenderScript.BlendDstFunc.ONE_MINUS_SRC_ALPHA);
         mPFSBackground = mRS.programFragmentStoreCreate();
         mPFSBackground.setName("PFS");
 
         mPVAlloc = new ProgramVertexAlloc(mRS);
         mRS.programVertexBegin(null, null);
-        mRS.programVertexSetTextureMatrixEnable(true);
+        mRS.programVertexSetTextureMatrixEnable(false);
         mPV = mRS.programVertexCreate();
         mPV.setName("PV");
         mPV.bindAllocation(0, mPVAlloc.mAlloc);
-
-
-
         mPVAlloc.setupProjectionNormalized(320, 480);
-        //mPVAlloc.setupOrthoNormalized(320, 480);
+
+        mPVOrthoAlloc = new ProgramVertexAlloc(mRS);
+        mRS.programVertexBegin(null, null);
+        mRS.programVertexSetTextureMatrixEnable(true);
+        mPVOrtho = mRS.programVertexCreate();
+        mPVOrtho.setName("PVOrtho");
+        mPVOrtho.bindAllocation(0, mPVOrthoAlloc.mAlloc);
+        mPVOrthoAlloc.setupOrthoWindow(320, 480);
+
         mRS.contextBindProgramVertex(mPV);
 
         mAllocScratchBuf = new int[32];
@@ -162,16 +171,21 @@
 
 
         {
-            mIcons = new RenderScript.Allocation[4];
+            mIcons = new RenderScript.Allocation[29];
             mAllocIconIDBuf = new int[mIcons.length];
             mAllocIconID = mRS.allocationCreatePredefSized(
                 RenderScript.ElementPredefined.USER_I32, mAllocIconIDBuf.length);
 
-            
+
             Bitmap b;
             BitmapFactory.Options opts = new BitmapFactory.Options();
             opts.inScaled = false;
 
+            b = BitmapFactory.decodeResource(mRes, R.raw.cf_background, opts);
+            mBackground = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+            mBackground.setName("TexBk");
+
+
             b = BitmapFactory.decodeResource(mRes, R.raw.browser, opts);
             mIcons[0] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
 
@@ -184,6 +198,112 @@
             b = BitmapFactory.decodeResource(mRes, R.raw.settings, opts);
             mIcons[3] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
 
+/*
+            b = BitmapFactory.decodeResource(mRes, R.raw.assasins_creed, opts);
+            mIcons[4] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.bankofamerica, opts);
+            mIcons[5] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.chess, opts);
+            mIcons[6] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.dictionary, opts);
+            mIcons[7] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.facebook, opts);
+            mIcons[8] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.flashlight, opts);
+            mIcons[9] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.flight_control, opts);
+            mIcons[10] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.google_earth, opts);
+            mIcons[11] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.harry_potter, opts);
+            mIcons[12] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.movies, opts);
+            mIcons[13] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.nytimes, opts);
+            mIcons[14] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.pandora, opts);
+            mIcons[15] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.public_radio, opts);
+            mIcons[16] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.shazam, opts);
+            mIcons[17] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.skype, opts);
+            mIcons[18] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.solitaire, opts);
+            mIcons[19] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.sudoku, opts);
+            mIcons[20] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.taptaprevenge, opts);
+            mIcons[21] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.tetris, opts);
+            mIcons[22] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.tictactoe, opts);
+            mIcons[23] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.tweetie, opts);
+            mIcons[24] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.urbanspoon, opts);
+            mIcons[25] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.waterslide_extreme, opts);
+            mIcons[26] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.weather_channel, opts);
+            mIcons[27] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+
+            b = BitmapFactory.decodeResource(mRes, R.raw.zippo, opts);
+            mIcons[28] = mRS.allocationCreateFromBitmap(b, RenderScript.ElementPredefined.RGB_565, true);
+*/
+            mIcons[4] =  mIcons[3];
+            mIcons[5] =  mIcons[2];
+            mIcons[6] =  mIcons[1];
+            mIcons[7] =  mIcons[0];
+            mIcons[8] =  mIcons[1];
+            mIcons[9] =  mIcons[2];
+            mIcons[10] = mIcons[3];
+            mIcons[11] = mIcons[2];
+            mIcons[12] = mIcons[1];
+            mIcons[13] = mIcons[0];
+            mIcons[14] = mIcons[1];
+            mIcons[15] = mIcons[2];
+            mIcons[16] = mIcons[3];
+            mIcons[17] = mIcons[2];
+            mIcons[18] = mIcons[1];
+            mIcons[19] = mIcons[0];
+            mIcons[20] = mIcons[1];
+            mIcons[21] = mIcons[2];
+            mIcons[22] = mIcons[3];
+            mIcons[23] = mIcons[2];
+            mIcons[24] = mIcons[1];
+            mIcons[25] = mIcons[0];
+            mIcons[26] = mIcons[1];
+            mIcons[27] = mIcons[2];
+            mIcons[28] = mIcons[3];
+
+
+
             for(int ct=0; ct < mIcons.length; ct++) {
                 mIcons[ct].uploadToTexture(0);
                 mAllocIconIDBuf[ct] = mIcons[ct].getID();
@@ -221,6 +341,11 @@
 
     }
 
+    private void makeTextBitmap() {
+        //Bitmap.createBitmap(width, height, Bitmap.Config);
+        //new Canvas(theBitmap);
+        //canvas.drawText();
+    }
 
 
     private void initRS() {
@@ -232,7 +357,7 @@
         //mRS.scriptCSetClearDepth(0);
         mScript = mRS.scriptCCreate();
 
-        mAllocStateBuf = new int[] {0, 0, 0, 8, 0, 0, 0, 0, 38, 0, 0};
+        mAllocStateBuf = new int[] {0, 0, 0, 8, 0, 0, -1, 0, mAllocIconIDBuf.length, 0, 0};
         mAllocState = mRS.allocationCreatePredefSized(
             RenderScript.ElementPredefined.USER_I32, mAllocStateBuf.length);
         mScript.bindAllocation(mAllocState, 0);
@@ -248,4 +373,3 @@
 }
 
 
-
diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
index b5e02af..27f1584 100644
--- a/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
+++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloView.java
@@ -76,7 +76,7 @@
     float mOldColumn;
     float mZoom = 1;
 
-    int mIconCount = 38;
+    int mIconCount = 29;
     int mRows = 4;
     int mColumns = (mIconCount + mRows - 1) / mRows;
 
@@ -101,7 +101,7 @@
 
     void computeSelection(float x, float y)
     {
-        float col = mColumn + (x - 0.5f) * 3;
+        float col = mColumn + (x - 0.5f) * 4 + 1;
         int iCol = (int)(col + 0.25f);
 
         float row = (y / 0.8f) * mRows;
@@ -158,13 +158,14 @@
                     mZoom = zoom;
                     mFlingX = nx;
                     mRender.setZoom(zoom);
-                } else {
-                    if(mControlMode && (mZoom < 1.01f)) {
+                    if(mZoom < 1.01f) {
                         computeSelection(nx, ny);
                     }
+                } else {
                     mControlMode = false;
                     mColumn = mOldColumn;
                     mRender.setZoom(1.f);
+                    mRender.setSelected(-1);
                 }
             } else {
                 // Do something with corners here....
diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec
index 2f99808..45e6d1b 100644
--- a/libs/rs/rs.spec
+++ b/libs/rs/rs.spec
@@ -99,6 +99,16 @@
 	ret RsAllocation
 	}
 
+AllocationCreateFromBitmapBoxed {
+	param uint32_t width
+	param uint32_t height
+	param RsElementPredefined dstFmt
+	param RsElementPredefined srcFmt
+	param bool genMips
+	param const void * data
+	ret RsAllocation
+	}
+
 
 AllocationUploadToTexture {
 	param RsAllocation alloc
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index c143307..a2e3babc 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -301,14 +301,18 @@
         return elementConverter_cpy_32;
     }
 
-    LOGE("pickConverter, unsuported combo");
+    LOGE("pickConverter, unsuported combo, src %i,  dst %i", srcFmt, dstFmt);
     return 0;
 }
 
 
 RsAllocation rsi_AllocationCreateFromBitmap(Context *rsc, uint32_t w, uint32_t h, RsElementPredefined dstFmt, RsElementPredefined srcFmt,  bool genMips, const void *data)
 {
-    rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, RS_ELEMENT_RGB_565));
+    rsAssert(!(w & (w-1)));
+    rsAssert(!(h & (h-1)));
+
+    //LOGE("rsi_AllocationCreateFromBitmap %i %i %i %i %i", w, h, dstFmt, srcFmt, genMips);
+    rsi_TypeBegin(rsc, rsi_ElementGetPredefined(rsc, dstFmt));
     rsi_TypeAdd(rsc, RS_DIMENSION_X, w);
     rsi_TypeAdd(rsc, RS_DIMENSION_Y, h);
     if (genMips) {
@@ -340,6 +344,42 @@
     return texAlloc;
 }
 
+static uint32_t fmtToBits(RsElementPredefined fmt)
+{
+    return 16;
+}
+
+RsAllocation rsi_AllocationCreateFromBitmapBoxed(Context *rsc, uint32_t w, uint32_t h, RsElementPredefined dstFmt, RsElementPredefined srcFmt, bool genMips, const void *data)
+{
+    uint32_t w2 = rsHigherPow2(w);
+    uint32_t h2 = rsHigherPow2(h);
+
+    if ((w2 == w) && (h2 == h)) {
+        return rsi_AllocationCreateFromBitmap(rsc, w, h, dstFmt, srcFmt, genMips, data);
+    }
+
+    uint32_t bpp = fmtToBits(srcFmt) >> 3;
+    size_t size = w2 * h2 * bpp;
+    uint8_t *tmp = static_cast<uint8_t *>(malloc(size));
+    memset(tmp, 0, size);
+
+    const uint8_t * src = static_cast<const uint8_t *>(data);
+    for (uint32_t y = 0; y < h; y++) {
+        uint8_t * ydst = &tmp[y + ((h2 - h) >> 1)];
+        memcpy(&ydst[(w2 - w) >> 1], src, w * bpp);
+        src += h * bpp;
+    }
+
+    RsAllocation ret = rsi_AllocationCreateFromBitmap(rsc, w2, h2, dstFmt, srcFmt, genMips, tmp);
+    free(tmp);
+    return ret;
+
+
+
+
+}
+
+
 RsAllocation rsi_AllocationCreateFromFile(Context *rsc, const char *file, bool genMips)
 {
     bool use32bpp = false;
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index 78b8bf8..e52b0e0 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -79,8 +79,7 @@
     mFragment.set(frag);
     mVertex.set(vtx);
     mFragmentStore.set(store);
-    return true;
-
+    return ret;
 }
 
 
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 497dbcf..a00b8e8 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -83,6 +83,7 @@
 
     const ProgramFragment * getFragment() {return mFragment.get();}
     const ProgramFragmentStore * getFragmentStore() {return mFragmentStore.get();}
+    const ProgramVertex * getVertex() {return mVertex.get();}
 
     void setupCheck();
 
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 417ba6a..792135d 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -55,8 +55,6 @@
         glLoadIdentity();
     }
 
-
-    LOGE("lights %i ", mLightCount);
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
     if (mLightCount) {
@@ -103,6 +101,25 @@
     }
 }
 
+void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const
+{
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix));
+}
+
+void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const
+{
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix));
+}
+
+void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
+{
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
+    memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix));
+}
+
+
 
 ProgramVertexState::ProgramVertexState()
 {
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index ac15b70..da5ed81 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -41,6 +41,10 @@
     void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;}
     void addLight(const Light *);
 
+    void setProjectionMatrix(const rsc_Matrix *) const;
+    void setModelviewMatrix(const rsc_Matrix *) const;
+    void setTextureMatrix(const rsc_Matrix *) const;
+
 protected:
     bool mDirty;
     uint32_t mLightCount;
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 129b19f..10d1120 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -135,7 +135,6 @@
 
 
 
-
 //////////////////////////////////////////////////////////////////////////////
 // Matrix routines
 //////////////////////////////////////////////////////////////////////////////
@@ -257,6 +256,24 @@
 }
 
 //////////////////////////////////////////////////////////////////////////////
+// VP
+//////////////////////////////////////////////////////////////////////////////
+
+static void SC_vpLoadModelMatrix(const rsc_Matrix *m)
+{
+    GET_TLS();
+    rsc->getVertex()->setModelviewMatrix(m);
+}
+
+static void SC_vpLoadTextureMatrix(const rsc_Matrix *m)
+{
+    GET_TLS();
+    rsc->getVertex()->setTextureMatrix(m);
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
 // Drawing
 //////////////////////////////////////////////////////////////////////////////
 
@@ -343,20 +360,12 @@
 //
 //////////////////////////////////////////////////////////////////////////////
 
-extern "C" const void * loadVp(uint32_t bank, uint32_t offset)
-{
-    GET_TLS();
-    return &static_cast<const uint8_t *>(sc->mSlots[bank]->getPtr())[offset];
-}
-
-
-
 static void SC_color(float r, float g, float b, float a)
 {
     glColor4f(r, g, b, a);
 }
 
-
+/*
 extern "C" void materialDiffuse(float r, float g, float b, float a)
 {
     float v[] = {r, g, b, a};
@@ -369,35 +378,18 @@
     glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v);
 }
 
-extern "C" void lightPosition(float x, float y, float z, float w)
-{
-    float v[] = {x, y, z, w};
-    glLightfv(GL_LIGHT0, GL_POSITION, v);
-}
-
 extern "C" void materialShininess(float s)
 {
     glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s);
 }
+*/
 
-extern "C" void uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
+static void SC_uploadToTexture(RsAllocation va, uint32_t baseMipLevel)
 {
     GET_TLS();
     rsi_AllocationUploadToTexture(rsc, va, baseMipLevel);
 }
 
-extern "C" void enable(uint32_t p)
-{
-    glEnable(p);
-}
-
-extern "C" void disable(uint32_t p)
-{
-    glDisable(p);
-}
-
-
-
 static void SC_ClearColor(float r, float g, float b, float a)
 {
     //LOGE("c %f %f %f %f", r, g, b, a);
@@ -408,6 +400,16 @@
     sc->mEnviroment.mClearColor[3] = a;
 }
 
+static void SC_debugF(const char *s, float f)
+{
+    LOGE("%s %f", s, f);
+}
+
+static void SC_debugI32(const char *s, int32_t i)
+{
+    LOGE("%s %i", s, i);
+}
+
 
 
 //////////////////////////////////////////////////////////////////////////////
@@ -444,6 +446,10 @@
         "float", "(float)" },
     { "randf", (void *)&SC_randf,
         "float", "(float)" },
+    { "floorf", (void *)&floorf,
+        "float", "(float)" },
+    { "ceilf", (void *)&ceilf,
+        "float", "(float)" },
 
     // matrix
     { "matrixLoadIdentity", (void *)&SC_matrixLoadIdentity,
@@ -481,6 +487,14 @@
     { "bindTexture", (void *)&SC_bindTexture,
         "void", "(int, int, int)" },
 
+    // vp
+    { "vpLoadModelMatrix", (void *)&SC_bindProgramFragment,
+        "void", "(void *)" },
+    { "vpLoadTextureMatrix", (void *)&SC_bindProgramFragmentStore,
+        "void", "(void *)" },
+
+
+
     // drawing
     { "drawQuad", (void *)&SC_drawQuad,
         "void", "(float x1, float y1, float z1, float x2, float y2, float z2, float x3, float y3, float z3, float x4, float y4, float z4)" },
@@ -495,10 +509,19 @@
     // misc
     { "pfClearColor", (void *)&SC_ClearColor,
         "void", "(float, float, float, float)" },
-
     { "color", (void *)&SC_color,
         "void", "(float, float, float, float)" },
 
+    { "uploadToTexture", (void *)&SC_uploadToTexture,
+        "void", "(int, int)" },
+
+
+    { "debugF", (void *)&SC_debugF,
+        "void", "(void *, float)" },
+    { "debugI32", (void *)&SC_debugI32,
+        "void", "(void *, int)" },
+
+
     { NULL, NULL, NULL, NULL }
 };
 
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 1d960c5..95d61cd 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -59,6 +59,8 @@
 #include <media/PVPlayer.h>
 #include "TestPlayerStub.h"
 
+//#undef USE_STAGEFRIGHT
+
 #if USE_STAGEFRIGHT
 #include "StagefrightPlayer.h"
 #endif
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 5944d9c..5be9224 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -26,6 +26,7 @@
         SurfaceRenderer.cpp       \
         TimeSource.cpp            \
         TimedEventQueue.cpp       \
+        TIHardwareRenderer.cpp    \
         Utils.cpp                 \
         AudioPlayer.cpp           \
         ESDS.cpp                  \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 17c72b9..d547556 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -87,7 +87,10 @@
     } else {
         mAudioTrack = new AudioTrack(
                 AudioSystem::MUSIC, mSampleRate, AudioSystem::PCM_16_BIT,
-                numChannels, 8192, 0, &AudioCallback, this, 0);
+                (numChannels == 2)
+                    ? AudioSystem::CHANNEL_OUT_STEREO
+                    : AudioSystem::CHANNEL_OUT_MONO,
+                8192, 0, &AudioCallback, this, 0);
 
         assert(mAudioTrack->initCheck() == OK);
 
@@ -217,8 +220,10 @@
 
             Mutex::Autolock autoLock(mLock);
             mPositionTimeMediaUs = (int64_t)units * 1000000 / scale;
+
             mPositionTimeRealUs =
-                ((mNumFramesPlayed + size_done / 4) * 1000000) / mSampleRate; // XXX
+                ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
+                    / mSampleRate;
         }
 
         if (mInputBuffer->range_length() == 0) {
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 6b47a38..01cb2d9 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -73,8 +73,6 @@
 
     if (bitrate_index == 0 || bitrate_index == 0x0f) {
         // Disallow "free" bitrate.
-
-        LOGE("We disallow 'free' bitrate for now.");
         return false;
     }
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index caaec06..4c883c6 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -73,6 +73,8 @@
 
     bool mNeedsNALFraming;
 
+    uint8_t *mSrcBuffer;
+
     MPEG4Source(const MPEG4Source &);
     MPEG4Source &operator=(const MPEG4Source &);
 };
@@ -743,7 +745,8 @@
       mBuffer(NULL),
       mBufferOffset(0),
       mBufferSizeRemaining(0),
-      mNeedsNALFraming(false) {
+      mNeedsNALFraming(false),
+      mSrcBuffer(NULL) {
     const char *mime;
     bool success = mFormat->findCString(kKeyMIMEType, &mime);
     assert(success);
@@ -777,8 +780,13 @@
     status_t err = mSampleTable->getMaxSampleSize(&max_size);
     assert(err == OK);
 
-    // Add padding for de-framing of AVC content just in case.
-    mGroup->add_buffer(new MediaBuffer(max_size + 2));
+    // Assume that a given buffer only contains at most 10 fragments,
+    // each fragment originally prefixed with a 2 byte length will
+    // have a 4 byte header (0x00 0x00 0x00 0x01) after conversion,
+    // and thus will grow by 2 bytes per fragment.
+    mGroup->add_buffer(new MediaBuffer(max_size + 10 * 2));
+
+    mSrcBuffer = new uint8_t[max_size];
 
     mStarted = true;
 
@@ -793,6 +801,9 @@
         mBuffer = NULL;
     }
 
+    delete[] mSrcBuffer;
+    mSrcBuffer = NULL;
+
     delete mGroup;
     mGroup = NULL;
 
@@ -832,33 +843,31 @@
         // fall through
     }
 
-    if (mBuffer == NULL) {
-        off_t offset;
-        size_t size;
-        status_t err = mSampleTable->getSampleOffsetAndSize(
-                mCurrentSampleIndex, &offset, &size);
+    off_t offset;
+    size_t size;
+    status_t err = mSampleTable->getSampleOffsetAndSize(
+            mCurrentSampleIndex, &offset, &size);
 
-        if (err != OK) {
-            return err;
-        }
+    if (err != OK) {
+        return err;
+    }
 
-        uint32_t dts;
-        err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
+    uint32_t dts;
+    err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
 
-        if (err != OK) {
-            return err;
-        }
+    if (err != OK) {
+        return err;
+    }
 
-        err = mGroup->acquire_buffer(&mBuffer);
-        if (err != OK) {
-            assert(mBuffer == NULL);
-            return err;
-        }
+    err = mGroup->acquire_buffer(&mBuffer);
+    if (err != OK) {
+        assert(mBuffer == NULL);
+        return err;
+    }
 
-        assert(mBuffer->size() + 2 >= size);
-
+    if (!mIsAVC || !mNeedsNALFraming) {
         ssize_t num_bytes_read =
-            mDataSource->read_at(offset, (uint8_t *)mBuffer->data() + 2, size);
+            mDataSource->read_at(offset, (uint8_t *)mBuffer->data(), size);
 
         if (num_bytes_read < (ssize_t)size) {
             mBuffer->release();
@@ -867,50 +876,62 @@
             return err;
         }
 
-        mBuffer->set_range(2, size);
+        mBuffer->set_range(0, size);
         mBuffer->meta_data()->clear();
         mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
         mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
-
         ++mCurrentSampleIndex;
 
-        mBufferOffset = 2;
-        mBufferSizeRemaining = size;
-    }
-
-    if (!mIsAVC) {
         *out = mBuffer;
         mBuffer = NULL;
 
         return OK;
     }
 
-    uint8_t *data = (uint8_t *)mBuffer->data() + mBufferOffset;
-    assert(mBufferSizeRemaining >= 2);
+    ssize_t num_bytes_read =
+        mDataSource->read_at(offset, mSrcBuffer, size);
 
-    size_t nal_length = (data[0] << 8) | data[1];
-    assert(mBufferSizeRemaining >= 2 + nal_length);
-
-    if (mNeedsNALFraming) {
-        // Insert marker.
-        data[-2] = data[-1] = data[0] = 0;
-        data[1] = 1;
-
-        mBuffer->set_range(mBufferOffset - 2, nal_length + 4);
-    } else {
-        mBuffer->set_range(mBufferOffset + 2, nal_length);
-    }
-
-    mBufferOffset += nal_length + 2;
-    mBufferSizeRemaining -= nal_length + 2;
-
-    if (mBufferSizeRemaining > 0) {
-        *out = mBuffer->clone();
-    } else {
-        *out = mBuffer;
+    if (num_bytes_read < (ssize_t)size) {
+        mBuffer->release();
         mBuffer = NULL;
+
+        return err;
     }
 
+    uint8_t *dstData = (uint8_t *)mBuffer->data();
+    size_t srcOffset = 0;
+    size_t dstOffset = 0;
+    while (srcOffset < size) {
+        assert(srcOffset + 1 < size);
+        size_t nalLength =
+            (mSrcBuffer[srcOffset] << 8) | mSrcBuffer[srcOffset + 1];
+        assert(srcOffset + 1 + nalLength < size);
+        srcOffset += 2;
+
+        if (nalLength == 0) {
+            continue;
+        }
+
+        assert(dstOffset + 4 <= mBuffer->size());
+
+        dstData[dstOffset++] = 0;
+        dstData[dstOffset++] = 0;
+        dstData[dstOffset++] = 0;
+        dstData[dstOffset++] = 1;
+        memcpy(&dstData[dstOffset], &mSrcBuffer[srcOffset], nalLength);
+        srcOffset += nalLength;
+        dstOffset += nalLength;
+    }
+
+    mBuffer->set_range(0, dstOffset);
+    mBuffer->meta_data()->clear();
+    mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
+    mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+    ++mCurrentSampleIndex;
+
+    *out = mBuffer;
+    mBuffer = NULL;
+
     return OK;
 }
 
diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp
index 78fcdee..04c9a11 100644
--- a/media/libstagefright/MediaPlayerImpl.cpp
+++ b/media/libstagefright/MediaPlayerImpl.cpp
@@ -40,6 +40,7 @@
 #include <media/stagefright/SoftwareRenderer.h>
 #include <media/stagefright/SurfaceRenderer.h>
 #include <media/stagefright/TimeSource.h>
+#include <media/stagefright/TIHardwareRenderer.h>
 #include <ui/PixelFormat.h>
 #include <ui/Surface.h>
 
@@ -311,6 +312,9 @@
         {
             Mutex::Autolock autoLock(mLock);
             mVideoPosition = pts_us;
+
+            LOGV("now_video = %.2f secs (%lld ms)",
+                 pts_us / 1E6, (pts_us + 500) / 1000);
         }
 
         if (seeking && mAudioPlayer != NULL) {
@@ -344,6 +348,7 @@
         if (mAudioPlayer != NULL
             && mAudioPlayer->getMediaTimeMapping(&realtime_us, &mediatime_us)) {
             mTimeSourceDeltaUs = realtime_us - mediatime_us;
+            LOGV("mTimeSourceDeltaUs = %.2f secs", mTimeSourceDeltaUs / 1E6);
         }
 
         int64_t now_us = mTimeSource->getRealTimeUs();
@@ -436,6 +441,7 @@
 }
 
 void MediaPlayerImpl::setAudioSource(MediaSource *source) {
+    LOGI("setAudioSource");
     mAudioSource = source;
 
     sp<MetaData> meta = source->getFormat();
@@ -646,17 +652,28 @@
     success = success && meta->findInt32(kKeyHeight, &decodedHeight);
     assert(success);
 
+    static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
     if (mSurface.get() != NULL) {
+        LOGW("Using SurfaceRenderer.");
         mRenderer =
             new SurfaceRenderer(
                     mSurface, mVideoWidth, mVideoHeight,
                     decodedWidth, decodedHeight);
-    } else if (format == OMX_COLOR_FormatYUV420Planar
-        && !strncasecmp(component, "OMX.qcom.video.decoder.", 23)) {
+    } else if (format == OMX_QCOM_COLOR_FormatYVU420SemiPlanar
+        && !strncmp(component, "OMX.qcom.video.decoder.", 23)) {
+        LOGW("Using QComHardwareRenderer.");
         mRenderer =
             new QComHardwareRenderer(
                     mISurface, mVideoWidth, mVideoHeight,
                     decodedWidth, decodedHeight);
+    } else if (format == OMX_COLOR_FormatCbYCrY
+            && !strcmp(component, "OMX.TI.Video.Decoder")) {
+        LOGW("Using TIHardwareRenderer.");
+        mRenderer =
+            new TIHardwareRenderer(
+                    mISurface, mVideoWidth, mVideoHeight,
+                    decodedWidth, decodedHeight);
     } else {
         LOGW("Using software renderer.");
         mRenderer = new SoftwareRenderer(
diff --git a/media/libstagefright/OMXDecoder.cpp b/media/libstagefright/OMXDecoder.cpp
index c059a9d..5e44999 100644
--- a/media/libstagefright/OMXDecoder.cpp
+++ b/media/libstagefright/OMXDecoder.cpp
@@ -20,6 +20,7 @@
 
 #undef NDEBUG
 #include <assert.h>
+#include <ctype.h>
 
 #include <OMX_Component.h>
 
@@ -54,14 +55,20 @@
 };
 
 static const CodecInfo kDecoderInfo[] = {
+    { "audio/mpeg", "OMX.TI.MP3.decode" },
     { "audio/mpeg", "OMX.PV.mp3dec" },
+    { "audio/3gpp", "OMX.TI.AMR.decode" },
     { "audio/3gpp", "OMX.PV.amrdec" },
+    { "audio/mp4a-latm", "OMX.TI.AAC.decode" },
     { "audio/mp4a-latm", "OMX.PV.aacdec" },
     { "video/mp4v-es", "OMX.qcom.video.decoder.mpeg4" },
+    { "video/mp4v-es", "OMX.TI.Video.Decoder" },
     { "video/mp4v-es", "OMX.PV.mpeg4dec" },
     { "video/3gpp", "OMX.qcom.video.decoder.h263" },
+    { "video/3gpp", "OMX.TI.Video.Decoder" },
     { "video/3gpp", "OMX.PV.h263dec" },
     { "video/avc", "OMX.qcom.video.decoder.avc" },
+    { "video/avc", "OMX.TI.Video.Decoder" },
     { "video/avc", "OMX.PV.avcdec" },
 };
 
@@ -92,7 +99,9 @@
 }
 
 // static
-OMXDecoder *OMXDecoder::Create(OMXClient *client, const sp<MetaData> &meta) {
+OMXDecoder *OMXDecoder::Create(
+        OMXClient *client, const sp<MetaData> &meta,
+        bool createEncoder) {
     const char *mime;
     bool success = meta->findCString(kKeyMIMEType, &mime);
     assert(success);
@@ -102,9 +111,15 @@
     const char *codec = NULL;
     IOMX::node_id node = 0;
     for (int index = 0;; ++index) {
-        codec = GetCodec(
-                kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
-                mime, index);
+        if (createEncoder) {
+            codec = GetCodec(
+                    kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
+                    mime, index);
+        } else {
+            codec = GetCodec(
+                    kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
+                    mime, index);
+        }
 
         if (!codec) {
             return NULL;
@@ -118,7 +133,33 @@
         }
     }
 
-    OMXDecoder *decoder = new OMXDecoder(client, node, mime, codec);
+    uint32_t quirks = 0;
+    if (!strcmp(codec, "OMX.PV.avcdec")) {
+        quirks |= kWantsRawNALFrames;
+    }
+    if (!strcmp(codec, "OMX.TI.AAC.decode")
+        || !strcmp(codec, "OMX.TI.MP3.decode")) {
+        quirks |= kDoesntReturnBuffersOnDisable;
+    }
+    if (!strcmp(codec, "OMX.TI.AAC.decode")) {
+        quirks |= kDoesntFlushOnExecutingToIdle;
+        quirks |= kDoesntProperlyFlushAllPortsAtOnce;
+    }
+    if (!strncmp(codec, "OMX.qcom.video.encoder.", 23)) {
+        quirks |= kRequiresAllocateBufferOnInputPorts;
+    }
+    if (!strncmp(codec, "OMX.qcom.video.decoder.", 23)) {
+        quirks |= kRequiresAllocateBufferOnOutputPorts;
+    }
+    if (!strncmp(codec, "OMX.qcom.video.", 15)) {
+        quirks |= kRequiresLoadedToIdleAfterAllocation;
+    }
+    if (!strcmp(codec, "OMX.TI.AAC.decode")
+        || !strcmp(codec, "OMX.TI.MP3.decode")) {
+        quirks |= kMeasuresTimeInMilliseconds;
+    }
+
+    OMXDecoder *decoder = new OMXDecoder(client, node, mime, codec, quirks);
 
     uint32_t type;
     const void *data;
@@ -169,52 +210,22 @@
     return decoder;
 }
 
-// static
-OMXDecoder *OMXDecoder::CreateEncoder(
-        OMXClient *client, const sp<MetaData> &meta) {
-    const char *mime;
-    bool success = meta->findCString(kKeyMIMEType, &mime);
-    assert(success);
-
-    sp<IOMX> omx = client->interface();
-
-    const char *codec = NULL;
-    IOMX::node_id node = 0;
-    for (int index = 0;; ++index) {
-        codec = GetCodec(
-                kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
-                mime, index);
-
-        if (!codec) {
-            return NULL;
-        }
-
-        LOGI("Attempting to allocate OMX node '%s'", codec);
-
-        status_t err = omx->allocate_node(codec, &node);
-        if (err == OK) {
-            break;
-        }
-    }
-
-    OMXDecoder *encoder = new OMXDecoder(client, node, mime, codec);
-
-    return encoder;
-}
-
 OMXDecoder::OMXDecoder(OMXClient *client, IOMX::node_id node,
-                       const char *mime, const char *codec)
+                       const char *mime, const char *codec,
+                       uint32_t quirks)
     : mClient(client),
       mOMX(mClient->interface()),
       mNode(node),
       mComponentName(strdup(codec)),
       mIsMP3(!strcasecmp(mime, "audio/mpeg")),
+      mIsAVC(!strcasecmp(mime, "video/avc")),
+      mQuirks(quirks),
       mSource(NULL),
       mCodecSpecificDataIterator(mCodecSpecificData.begin()),
       mState(OMX_StateLoaded),
       mPortStatusMask(kPortStatusActive << 2 | kPortStatusActive),
       mShutdownInitiated(false),
-      mDealer(new MemoryDealer(3 * 1024 * 1024)),
+      mDealer(new MemoryDealer(5 * 1024 * 1024)),
       mSeeking(false),
       mStarted(false),
       mErrorCondition(OK),
@@ -261,7 +272,7 @@
     // mDealer->dump("Decoder Dealer");
 
     sp<MetaData> params = new MetaData;
-    if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) {
+    if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) {
         params->setInt32(kKeyNeedsNALFraming, true);
     }
 
@@ -297,7 +308,7 @@
     }
 
     int attempt = 1;
-    while (mState != OMX_StateLoaded && attempt < 10) {
+    while (mState != OMX_StateLoaded && attempt < 20) {
         usleep(100000);
 
         ++attempt;
@@ -366,7 +377,11 @@
             mOutputBuffers.erase(mOutputBuffers.begin());
         }
 
-        status_t err = mOMX->send_command(mNode, OMX_CommandFlush, -1);
+        // XXX One of TI's decoders appears to ignore a flush if it doesn't
+        // currently hold on to any buffers on the port in question and
+        // never sends the completion event... FIXME
+
+        status_t err = mOMX->send_command(mNode, OMX_CommandFlush, OMX_ALL);
         assert(err == OK);
 
         // Once flushing is completed buffers will again be scheduled to be
@@ -472,9 +487,121 @@
     assert(err == NO_ERROR);
 }
 
-void OMXDecoder::setVideoOutputFormat(OMX_U32 width, OMX_U32 height) {
+status_t OMXDecoder::setVideoPortFormatType(
+        OMX_U32 portIndex,
+        OMX_VIDEO_CODINGTYPE compressionFormat,
+        OMX_COLOR_FORMATTYPE colorFormat) {
+    OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+    format.nSize = sizeof(format);
+    format.nVersion.s.nVersionMajor = 1;
+    format.nVersion.s.nVersionMinor = 1;
+    format.nPortIndex = portIndex;
+    format.nIndex = 0;
+    bool found = false;
+
+    OMX_U32 index = 0;
+    for (;;) {
+        format.nIndex = index;
+        status_t err = mOMX->get_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+
+        if (err != OK) {
+            return err;
+        }
+
+        // The following assertion is violated by TI's video decoder.
+        // assert(format.nIndex == index);
+
+        if (format.eCompressionFormat == compressionFormat
+            && format.eColorFormat == colorFormat) {
+            found = true;
+            break;
+        }
+
+        ++index;
+    }
+
+    if (!found) {
+        return UNKNOWN_ERROR;
+    }
+
+    status_t err = mOMX->set_parameter(
+            mNode, OMX_IndexParamVideoPortFormat,
+            &format, sizeof(format));
+
+    return err;
+}
+
+#if 1
+void OMXDecoder::setVideoOutputFormat(
+        const char *mime, OMX_U32 width, OMX_U32 height) {
     LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
 
+#if 1
+    // Enabling this code appears to be the right thing(tm), but,...
+    // the TI decoder then loses the ability to output YUV420 and only outputs
+    // YCbYCr (16bit)
+    if (!strcasecmp("video/avc", mime)) {
+        OMX_PARAM_COMPONENTROLETYPE role;
+        role.nSize = sizeof(role);
+        role.nVersion.s.nVersionMajor = 1;
+        role.nVersion.s.nVersionMinor = 1;
+        strncpy((char *)role.cRole, "video_decoder.avc",
+                OMX_MAX_STRINGNAME_SIZE - 1);
+        role.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+        status_t err = mOMX->set_parameter(
+                mNode, OMX_IndexParamStandardComponentRole,
+                &role, sizeof(role));
+        assert(err == OK);
+    }
+#endif
+
+    OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+    if (!strcasecmp("video/avc", mime)) {
+        compressionFormat = OMX_VIDEO_CodingAVC;
+    } else if (!strcasecmp("video/mp4v-es", mime)) {
+        compressionFormat = OMX_VIDEO_CodingMPEG4;
+    } else if (!strcasecmp("video/3gpp", mime)) {
+        compressionFormat = OMX_VIDEO_CodingH263;
+    } else {
+        assert(!"Should not be here. Not a supported video mime type.");
+    }
+
+    setVideoPortFormatType(
+            kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
+
+#if 1
+    {
+        OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+        format.nSize = sizeof(format);
+        format.nVersion.s.nVersionMajor = 1;
+        format.nVersion.s.nVersionMinor = 1;
+        format.nPortIndex = kPortIndexOutput;
+        format.nIndex = 0;
+
+        status_t err = mOMX->get_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+        assert(err == OK);
+
+        assert(format.eCompressionFormat == OMX_VIDEO_CodingUnused);
+
+        static const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
+
+        assert(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
+               || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
+               || format.eColorFormat == OMX_COLOR_FormatCbYCrY
+               || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar);
+
+        err = mOMX->set_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+        assert(err == OK);
+    }
+#endif
+
     OMX_PARAM_PORTDEFINITIONTYPE def;
     OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
 
@@ -502,7 +629,7 @@
     
     video_def->nFrameWidth = width;
     video_def->nFrameHeight = height;
-    // video_def.eCompressionFormat = OMX_VIDEO_CodingAVC;
+
     video_def->eColorFormat = OMX_COLOR_FormatUnused;
 
     err = mOMX->set_parameter(
@@ -522,21 +649,189 @@
 
     assert(def.eDomain == OMX_PortDomainVideo);
     
+#if 0
     def.nBufferSize =
         (((width + 15) & -16) * ((height + 15) & -16) * 3) / 2;  // YUV420
+#endif
 
     video_def->nFrameWidth = width;
     video_def->nFrameHeight = height;
-    video_def->nStride = width;
-    // video_def->nSliceHeight = height;
-    video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
-//    video_def->eColorFormat = OMX_COLOR_FormatYUV420Planar;
 
     err = mOMX->set_parameter(
             mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
     assert(err == NO_ERROR);
 }
 
+#else
+static void hexdump(const void *_data, size_t size) {
+    char line[256];
+    char tmp[16];
+
+    const uint8_t *data = (const uint8_t *)_data;
+    size_t offset = 0;
+    while (offset < size) {
+        sprintf(line, "0x%04x  ", offset);
+
+        size_t n = size - offset;
+        if (n > 16) {
+            n = 16;
+        }
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (i == 8) {
+                strcat(line, " ");
+            }
+
+            if (offset + i < size) {
+                sprintf(tmp, "%02x ", data[offset + i]);
+                strcat(line, tmp);
+            } else {
+                strcat(line, "   ");
+            }
+        }
+
+        strcat(line, " ");
+
+        for (size_t i = 0; i < n; ++i) {
+            if (isprint(data[offset + i])) {
+                sprintf(tmp, "%c", data[offset + i]);
+                strcat(line, tmp);
+            } else {
+                strcat(line, ".");
+            }
+        }
+
+        LOGI(line);
+
+        offset += 16;
+    }
+}
+
+static void DumpPortDefinitionType(const void *_param) {
+    OMX_PARAM_PORTDEFINITIONTYPE *param = (OMX_PARAM_PORTDEFINITIONTYPE *)_param;
+
+    LOGI("nPortIndex=%ld eDir=%s nBufferCountActual=%ld nBufferCountMin=%ld nBufferSize=%ld", param->nPortIndex, param->eDir == OMX_DirInput ? "input" : "output",
+        param->nBufferCountActual, param->nBufferCountMin, param->nBufferSize);
+
+    if (param->eDomain == OMX_PortDomainVideo) {
+        OMX_VIDEO_PORTDEFINITIONTYPE *video = &param->format.video;
+        LOGI("nFrameWidth=%ld nFrameHeight=%ld nStride=%ld nSliceHeight=%ld nBitrate=%ld xFramerate=%ld eCompressionFormat=%d eColorFormat=%d",
+            video->nFrameWidth, video->nFrameHeight, video->nStride, video->nSliceHeight, video->nBitrate, video->xFramerate, video->eCompressionFormat, video->eColorFormat);
+    } else {
+        hexdump(param, param->nSize);
+    }
+}
+
+void OMXDecoder::setVideoOutputFormat(
+        const char *mime, OMX_U32 width, OMX_U32 height) {
+    LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
+
+#if 0
+    // Enabling this code appears to be the right thing(tm), but,...
+    // the decoder then loses the ability to output YUV420 and only outputs
+    // YCbYCr (16bit)
+    {
+        OMX_PARAM_COMPONENTROLETYPE role;
+        role.nSize = sizeof(role);
+        role.nVersion.s.nVersionMajor = 1;
+        role.nVersion.s.nVersionMinor = 1;
+        strncpy((char *)role.cRole, "video_decoder.avc",
+                OMX_MAX_STRINGNAME_SIZE - 1);
+        role.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+        status_t err = mOMX->set_parameter(
+                mNode, OMX_IndexParamStandardComponentRole,
+                &role, sizeof(role));
+        assert(err == OK);
+    }
+#endif
+
+    setVideoPortFormatType(
+            kPortIndexInput, OMX_VIDEO_CodingAVC, OMX_COLOR_FormatUnused);
+
+#if 1
+    {
+        OMX_VIDEO_PARAM_PORTFORMATTYPE format;
+        format.nSize = sizeof(format);
+        format.nVersion.s.nVersionMajor = 1;
+        format.nVersion.s.nVersionMinor = 1;
+        format.nPortIndex = kPortIndexOutput;
+        format.nIndex = 0;
+
+        status_t err = mOMX->get_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+        assert(err == OK);
+
+        LOGI("XXX MyOMX_GetParameter OMX_IndexParamVideoPortFormat");
+        hexdump(&format, format.nSize);
+
+        assert(format.eCompressionFormat == OMX_VIDEO_CodingUnused);
+        assert(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
+               || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
+               || format.eColorFormat == OMX_COLOR_FormatCbYCrY);
+
+        err = mOMX->set_parameter(
+                mNode, OMX_IndexParamVideoPortFormat,
+                &format, sizeof(format));
+        assert(err == OK);
+    }
+#endif
+
+    OMX_PORT_PARAM_TYPE ptype;
+    ptype.nSize = sizeof(ptype);
+    ptype.nVersion.s.nVersionMajor = 1;
+    ptype.nVersion.s.nVersionMinor = 1;
+
+    status_t err = mOMX->get_parameter(
+            mNode, OMX_IndexParamVideoInit, &ptype, sizeof(ptype));
+    assert(err == OK);
+
+    LOGI("XXX MyOMX_GetParameter OMX_IndexParamVideoInit");
+    hexdump(&ptype, ptype.nSize);
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
+    def.nPortIndex = kPortIndexInput;
+
+    err = mOMX->get_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == OK);
+
+    LOGI("XXX MyOMX_GetParameter OMX_IndexParamPortDefinition");
+    DumpPortDefinitionType(&def);
+
+    OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+
+    err = mOMX->set_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == OK);
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    def.nPortIndex = kPortIndexOutput;
+
+    err = mOMX->get_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == OK);
+
+    LOGI("XXX MyOMX_GetParameter OMX_IndexParamPortDefinition");
+    DumpPortDefinitionType(&def);
+
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+
+    err = mOMX->set_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == OK);
+}
+
+#endif
+
 void OMXDecoder::setup() {
     const sp<MetaData> &meta = mSource->getFormat();
 
@@ -554,7 +849,7 @@
         success = success && meta->findInt32(kKeyHeight, &height);
         assert(success);
 
-        setVideoOutputFormat(width, height);
+        setVideoOutputFormat(mime, width, height);
     }
 
     // dumpPortDefinition(0);
@@ -644,10 +939,7 @@
 }
 
 void OMXDecoder::onStart() {
-    bool needs_qcom_hack =
-        !strncmp(mComponentName, "OMX.qcom.video.", 15);
-
-    if (!needs_qcom_hack) {
+    if (!(mQuirks & kRequiresLoadedToIdleAfterAllocation)) {
         status_t err =
             mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
         assert(err == NO_ERROR);
@@ -656,7 +948,7 @@
     allocateBuffers(kPortIndexInput);
     allocateBuffers(kPortIndexOutput);
 
-    if (needs_qcom_hack) {
+    if (mQuirks & kRequiresLoadedToIdleAfterAllocation) {
         // XXX this should happen before AllocateBuffers, but qcom's
         // h264 vdec disagrees.
         status_t err =
@@ -691,13 +983,17 @@
 
     for (OMX_U32 i = 0; i < num_buffers; ++i) {
         sp<IMemory> mem = mDealer->allocate(buffer_size);
+        if (mem.get() == NULL) {
+            LOGE("[%s] allocating IMemory of size %ld FAILED.",
+                 mComponentName, buffer_size);
+        }
         assert(mem.get() != NULL);
 
         IOMX::buffer_id buffer;
         status_t err;
 
         if (port_index == kPortIndexInput
-            && !strncmp(mComponentName, "OMX.qcom.video.encoder.", 23)) {
+                && (mQuirks & kRequiresAllocateBufferOnInputPorts)) {
             // qcom's H.263 encoder appears to want to allocate its own input
             // buffers.
             err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer);
@@ -706,7 +1002,7 @@
                      mComponentName, err);
             }
         } else if (port_index == kPortIndexOutput
-            && !strncmp(mComponentName, "OMX.qcom.video.decoder.", 23)) {
+                && (mQuirks & kRequiresAllocateBufferOnOutputPorts)) {
 #if 1
             err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer);
 #else
@@ -817,18 +1113,59 @@
         case OMX_CommandFlush: {
             OMX_U32 port_index = data;
             LOGV("Port %ld flush complete.", port_index);
-            assert(getPortStatus(port_index) == kPortStatusFlushing);
 
-            setPortStatus(port_index, kPortStatusActive);
-            BufferList *buffers = &mBuffers.editItemAt(port_index);
-            while (!buffers->empty()) {
-                IOMX::buffer_id buffer = *buffers->begin();
-                buffers->erase(buffers->begin());
+            PortStatus status = getPortStatus(port_index);
 
-                if (port_index == kPortIndexInput) {
-                    postEmptyBufferDone(buffer);
-                } else {
-                    postInitialFillBuffer(buffer);
+            assert(status == kPortStatusFlushing
+                    || status == kPortStatusFlushingToDisabled
+                    || status == kPortStatusFlushingToShutdown);
+
+            switch (status) {
+                case kPortStatusFlushing:
+                {
+                    // This happens when we're flushing before a seek.
+                    setPortStatus(port_index, kPortStatusActive);
+                    BufferList *buffers = &mBuffers.editItemAt(port_index);
+                    while (!buffers->empty()) {
+                        IOMX::buffer_id buffer = *buffers->begin();
+                        buffers->erase(buffers->begin());
+
+                        if (port_index == kPortIndexInput) {
+                            postEmptyBufferDone(buffer);
+                        } else {
+                            postInitialFillBuffer(buffer);
+                        }
+                    }
+                    break;
+                }
+
+                case kPortStatusFlushingToDisabled:
+                {
+                    // Port settings have changed and the (buggy) OMX component
+                    // does not properly return buffers on disabling, we need to
+                    // do a flush first and _then_ disable the port in question.
+
+                    setPortStatus(port_index, kPortStatusDisabled);
+                    status_t err = mOMX->send_command(
+                            mNode, OMX_CommandPortDisable, port_index);
+                    assert(err == OK);
+
+                    freePortBuffers(port_index);
+                    break;
+                }
+
+                default:
+                {
+                    assert(status == kPortStatusFlushingToShutdown);
+
+                    setPortStatus(port_index, kPortStatusShutdown);
+                    if (getPortStatus(kPortIndexInput) == kPortStatusShutdown
+                        && getPortStatus(kPortIndexOutput) == kPortStatusShutdown) {
+                        status_t err = mOMX->send_command(
+                                mNode, OMX_CommandStateSet, OMX_StateIdle);
+                        assert(err == OK);
+                    }
+                    break;
                 }
             }
             break;
@@ -841,10 +1178,22 @@
 
 void OMXDecoder::onEventPortSettingsChanged(OMX_U32 port_index) {
     assert(getPortStatus(port_index) == kPortStatusActive);
-    setPortStatus(port_index, kPortStatusDisabled);
 
-    status_t err =
-        mOMX->send_command(mNode, OMX_CommandPortDisable, port_index);
+    status_t err;
+
+    if (mQuirks & kDoesntReturnBuffersOnDisable) {
+        // Decoder does not properly return our buffers when disabled...
+        // Need to flush port instead and _then_ disable.
+
+        setPortStatus(port_index, kPortStatusFlushingToDisabled);
+
+        err = mOMX->send_command(mNode, OMX_CommandFlush, port_index);
+    } else {
+        setPortStatus(port_index, kPortStatusDisabled);
+
+        err = mOMX->send_command(mNode, OMX_CommandPortDisable, port_index);
+    }
+
     assert(err == NO_ERROR);
 }
 
@@ -894,19 +1243,8 @@
             mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateLoaded);
         assert(err == NO_ERROR);
 
-        BufferList *ibuffers = &mBuffers.editItemAt(kPortIndexInput);
-        for (BufferList::iterator it = ibuffers->begin();
-             it != ibuffers->end(); ++it) {
-            freeInputBuffer(*it);
-        }
-        ibuffers->clear();
-
-        BufferList *obuffers = &mBuffers.editItemAt(kPortIndexOutput);
-        for (BufferList::iterator it = obuffers->begin();
-             it != obuffers->end(); ++it) {
-            freeOutputBuffer(*it);
-        }
-        obuffers->clear();
+        freePortBuffers(kPortIndexInput);
+        freePortBuffers(kPortIndexOutput);
     }
 }
 
@@ -925,26 +1263,41 @@
 
     mShutdownInitiated = true;
 
-    status_t err =
-        mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
-    assert(err == NO_ERROR);
+    status_t err;
+    if (mQuirks & kDoesntFlushOnExecutingToIdle) {
+        if (mQuirks & kDoesntProperlyFlushAllPortsAtOnce) {
+            err = mOMX->send_command(mNode, OMX_CommandFlush, kPortIndexInput);
+            assert(err == OK);
 
-    setPortStatus(kPortIndexInput, kPortStatusShutdown);
-    setPortStatus(kPortIndexOutput, kPortStatusShutdown);
+            err = mOMX->send_command(mNode, OMX_CommandFlush, kPortIndexOutput);
+        } else {
+            err = mOMX->send_command(mNode, OMX_CommandFlush, OMX_ALL);
+        }
+
+        setPortStatus(kPortIndexInput, kPortStatusFlushingToShutdown);
+        setPortStatus(kPortIndexOutput, kPortStatusFlushingToShutdown);
+    } else {
+        err = mClient->send_command(
+                mNode, OMX_CommandStateSet, OMX_StateIdle);
+
+        setPortStatus(kPortIndexInput, kPortStatusShutdown);
+        setPortStatus(kPortIndexOutput, kPortStatusShutdown);
+    }
+    assert(err == OK);
 }
 
 void OMXDecoder::setPortStatus(OMX_U32 port_index, PortStatus status) {
-    int shift = 2 * port_index;
+    int shift = 3 * port_index;
 
-    mPortStatusMask &= ~(3 << shift);
+    mPortStatusMask &= ~(7 << shift);
     mPortStatusMask |= status << shift;
 }
 
 OMXDecoder::PortStatus OMXDecoder::getPortStatus(
         OMX_U32 port_index) const {
-    int shift = 2 * port_index;
+    int shift = 3 * port_index;
 
-    return static_cast<PortStatus>((mPortStatusMask >> shift) & 3);
+    return static_cast<PortStatus>((mPortStatusMask >> shift) & 7);
 }
 
 void OMXDecoder::onEmptyBufferDone(IOMX::buffer_id buffer) {
@@ -964,6 +1317,8 @@
             break;
 
         case kPortStatusFlushing:
+        case kPortStatusFlushingToDisabled:
+        case kPortStatusFlushingToShutdown:
             LOGV("We're currently flushing, enqueue INPUT buffer %p.", buffer);
             mBuffers.editItemAt(kPortIndexInput).push_back(buffer);
             err = NO_ERROR;
@@ -980,7 +1335,9 @@
 void OMXDecoder::onFillBufferDone(const omx_message &msg) {
     IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
 
-    LOGV("[%s] onFillBufferDone (%p)", mComponentName, buffer);
+    LOGV("[%s] on%sFillBufferDone (%p, size:%ld)", mComponentName,
+         msg.type == omx_message::INITIAL_FILL_BUFFER ? "Initial" : "",
+         buffer, msg.u.extended_buffer_data.range_length);
 
     status_t err;
     switch (getPortStatus(kPortIndexOutput)) {
@@ -995,6 +1352,8 @@
             break;
 
         case kPortStatusFlushing:
+        case kPortStatusFlushingToDisabled:
+        case kPortStatusFlushingToShutdown:
             LOGV("We're currently flushing, enqueue OUTPUT buffer %p.", buffer);
             mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
             err = NO_ERROR;
@@ -1035,7 +1394,7 @@
 
         size_t range_length = 0;
 
-        if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) {
+        if (mIsAVC && !(mQuirks & kWantsRawNALFrames)) {
             assert((*mCodecSpecificDataIterator).size + 4 <= mem->size());
 
             memcpy(mem->pointer(), kNALStartCode, 4);
@@ -1142,15 +1501,18 @@
     OMX_TICKS timestamp = 0;
 
     if (success) {
-        // XXX units should be microseconds but PV treats them as milliseconds.
-        timestamp = ((OMX_S64)units * 1000) / scale;
+        if (mQuirks & kMeasuresTimeInMilliseconds) {
+            timestamp = ((OMX_S64)units * 1000) / scale;
+        } else {
+            timestamp = ((OMX_S64)units * 1000000) / scale;
+        }
     }
 
     input_buffer->release();
     input_buffer = NULL;
 
-    LOGV("[%s] Calling EmptyBuffer on buffer %p",
-         mComponentName, buffer);
+    LOGV("[%s] Calling EmptyBuffer on buffer %p size:%d flags:0x%08lx",
+         mComponentName, buffer, src_length, flags);
 
     status_t err2 = mClient->emptyBuffer(
             mNode, buffer, 0, src_length, flags, timestamp);
@@ -1169,8 +1531,16 @@
 
     media_buffer->meta_data()->clear();
 
-    media_buffer->meta_data()->setInt32(
-            kKeyTimeUnits, msg.u.extended_buffer_data.timestamp);
+    if (mQuirks & kMeasuresTimeInMilliseconds) {
+        media_buffer->meta_data()->setInt32(
+                kKeyTimeUnits,
+                msg.u.extended_buffer_data.timestamp);
+    } else {
+        media_buffer->meta_data()->setInt32(
+                kKeyTimeUnits,
+                (msg.u.extended_buffer_data.timestamp + 500) / 1000);
+    }
+
     media_buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
 
     if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
@@ -1198,7 +1568,9 @@
 
     PortStatus outputStatus = getPortStatus(kPortIndexOutput);
     if (outputStatus == kPortStatusShutdown
-            || outputStatus == kPortStatusFlushing) {
+            || outputStatus == kPortStatusFlushing
+            || outputStatus == kPortStatusFlushingToDisabled
+            || outputStatus == kPortStatusFlushingToShutdown) {
         mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
     } else {
         LOGV("[%s] Calling FillBuffer on buffer %p.", mComponentName, buffer);
@@ -1326,4 +1698,18 @@
     postMessage(msg);
 }
 
+void OMXDecoder::freePortBuffers(OMX_U32 port_index) {
+    BufferList *buffers = &mBuffers.editItemAt(port_index);
+    while (!buffers->empty()) {
+        IOMX::buffer_id buffer = *buffers->begin();
+        buffers->erase(buffers->begin());
+
+        if (port_index == kPortIndexInput) {
+            freeInputBuffer(buffer);
+        } else {
+            freeOutputBuffer(buffer);
+        }
+    }
+}
+
 }  // namespace android
diff --git a/media/libstagefright/TIHardwareRenderer.cpp b/media/libstagefright/TIHardwareRenderer.cpp
new file mode 100644
index 0000000..ba42ef4
--- /dev/null
+++ b/media/libstagefright/TIHardwareRenderer.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#define LOG_TAG "TIHardwareRenderer"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/TIHardwareRenderer.h>
+#include <ui/ISurface.h>
+#include <ui/Overlay.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+TIHardwareRenderer::TIHardwareRenderer(
+        const sp<ISurface> &surface,
+        size_t displayWidth, size_t displayHeight,
+        size_t decodedWidth, size_t decodedHeight)
+    : mISurface(surface),
+      mDisplayWidth(displayWidth),
+      mDisplayHeight(displayHeight),
+      mDecodedWidth(decodedWidth),
+      mDecodedHeight(decodedHeight),
+      mFrameSize((mDecodedWidth * mDecodedHeight * 3) / 2) {
+    assert(mISurface.get() != NULL);
+    assert(mDecodedWidth > 0);
+    assert(mDecodedHeight > 0);
+
+    sp<OverlayRef> ref = mISurface->createOverlay(
+            mDisplayWidth, mDisplayHeight, OVERLAY_FORMAT_CbYCrY_422_I);
+
+    if (ref.get() == NULL) {
+        LOGE("Unable to create the overlay!");
+        return;
+    }
+
+    mOverlay = new Overlay(ref);
+
+    for (size_t i = 0; i < mOverlay->getBufferCount(); ++i) {
+        mOverlayAddresses.push(mOverlay->getBufferAddress((void *)i));
+    }
+    mIndex = mOverlayAddresses.size() - 1;
+}
+
+TIHardwareRenderer::~TIHardwareRenderer() {
+    if (mOverlay.get() != NULL) {
+        mOverlay->destroy();
+        mOverlay.clear();
+
+        // XXX apparently destroying an overlay is an asynchronous process...
+        sleep(1);
+    }
+}
+
+void TIHardwareRenderer::render(
+        const void *data, size_t size, void *platformPrivate) {
+    // assert(size == mFrameSize);
+
+    if (mOverlay.get() == NULL) {
+        return;
+    }
+
+#if 0
+    overlay_buffer_t buffer;
+    if (mOverlay->dequeueBuffer(&buffer) == OK) {
+        void *addr = mOverlay->getBufferAddress(buffer);
+
+        memcpy(addr, data, size);
+
+        mOverlay->queueBuffer(buffer);
+    }
+#else
+    memcpy(mOverlayAddresses[mIndex], data, size);
+    mOverlay->queueBuffer((void *)mIndex);
+
+    if (mIndex-- == 0) {
+        mIndex = mOverlayAddresses.size() - 1;
+    }
+#endif
+}
+
+}  // namespace android
+
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index 79d78ad1..a3c3436 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -29,6 +29,10 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Handler;
@@ -58,7 +62,8 @@
 import java.util.Observable;
 import java.util.Observer;
 
-class PowerManagerService extends IPowerManager.Stub implements LocalPowerManager, Watchdog.Monitor {
+class PowerManagerService extends IPowerManager.Stub
+        implements LocalPowerManager,Watchdog.Monitor, SensorEventListener {
 
     private static final String TAG = "PowerManagerService";
     static final String PARTIAL_NAME = "PowerManagerService";
@@ -72,7 +77,8 @@
     private static final int LOCK_MASK = PowerManager.PARTIAL_WAKE_LOCK
                                         | PowerManager.SCREEN_DIM_WAKE_LOCK
                                         | PowerManager.SCREEN_BRIGHT_WAKE_LOCK
-                                        | PowerManager.FULL_WAKE_LOCK;
+                                        | PowerManager.FULL_WAKE_LOCK
+                                        | PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
 
     //                       time since last state:               time since last event:
     // The short keylight delay comes from Gservices; this is the default.
@@ -138,6 +144,7 @@
     private int[] mBroadcastQueue = new int[] { -1, -1, -1 };
     private int[] mBroadcastWhy = new int[3];
     private int mPartialCount = 0;
+    private int mProximityCount = 0;
     private int mPowerState;
     private boolean mOffBecauseOfUser;
     private int mUserState;
@@ -175,6 +182,8 @@
     private IActivityManager mActivityService;
     private IBatteryStats mBatteryStats;
     private BatteryService mBatteryService;
+    private SensorManager mSensorManager;
+    private Sensor mProximitySensor;
     private boolean mDimScreen = true;
     private long mNextTimeout;
     private volatile int mPokey = 0;
@@ -536,6 +545,7 @@
                     wl.minState = SCREEN_DIM;
                     break;
                 case PowerManager.PARTIAL_WAKE_LOCK:
+                case PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK:
                     break;
                 default:
                     // just log and bail.  we're in the server, so don't
@@ -583,6 +593,11 @@
                 }
             }
             Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);
+        } else if ((flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
+            mProximityCount++;
+            if (mProximityCount == 1) {
+                enableProximityLockLocked();
+            }
         }
         if (newlock) {
             acquireUid = wl.uid;
@@ -639,6 +654,11 @@
                 if (LOG_PARTIAL_WL) EventLog.writeEvent(LOG_POWER_PARTIAL_WAKE_STATE, 0, wl.tag);
                 Power.releaseWakeLock(PARTIAL_NAME);
             }
+        } else if ((wl.flags & LOCK_MASK) == PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK) {
+            mProximityCount--;
+            if (mProximityCount == 0) {
+                disableProximityLockLocked();
+            }
         }
         // Unlink the lock from the binder.
         wl.binder.unlinkToDeath(wl, 0);
@@ -1996,4 +2016,47 @@
     public void monitor() {
         synchronized (mLocks) { }
     }
+
+    public int getSupportedWakeLockFlags() {
+        int result = PowerManager.PARTIAL_WAKE_LOCK
+                   | PowerManager.FULL_WAKE_LOCK
+                   | PowerManager.SCREEN_DIM_WAKE_LOCK;
+
+        // call getSensorManager() to make sure mProximitySensor is initialized
+        getSensorManager();
+        if (mProximitySensor != null) {
+            result |= PowerManager.PROXIMITY_SCREEN_OFF_WAKE_LOCK;
+        }
+
+        return result;
+    }
+
+    private SensorManager getSensorManager() {
+        if (mSensorManager == null) {
+            mSensorManager = new SensorManager(mHandlerThread.getLooper());
+            mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+        }
+        return mSensorManager;
+    }
+
+    private void enableProximityLockLocked() {
+        mSensorManager.registerListener(this, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
+    }
+
+    private void disableProximityLockLocked() {
+        mSensorManager.unregisterListener(this);
+    }
+
+    public void onSensorChanged(SensorEvent event) {
+        long milliseconds = event.timestamp / 1000000;
+        if (event.values[0] == 0.0) {
+            goToSleep(milliseconds);
+        } else {
+            userActivity(milliseconds, false);
+        }
+    }
+
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        // ignore
+    }
 }
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
index a03490d..2eecef8 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java
@@ -16,6 +16,9 @@
 
 package com.android.dumprendertree;
 
+import com.android.dumprendertree.forwarder.AdbUtils;
+import com.android.dumprendertree.forwarder.ForwardServer;
+
 import android.app.Instrumentation;
 import android.content.Intent;
 import android.os.Bundle;
@@ -42,7 +45,7 @@
     private BufferedOutputStream mBufferedOutputFailedStream;
     private BufferedOutputStream mBufferedOutputNoresultStream;
     private BufferedOutputStream mBufferedOutputTimedoutStream;
-    
+
     public void passed(String layout_file) {
         try {
             mBufferedOutputPassedStream.write(layout_file.getBytes());
@@ -52,7 +55,7 @@
             e.printStackTrace();
         }
     }
-    
+
     public void failed(String layout_file) {
         try {
             mBufferedOutputFailedStream.write(layout_file.getBytes());
@@ -62,7 +65,7 @@
             e.printStackTrace();
         }
     }
-    
+
     public void noresult(String layout_file) {
         try {
             mBufferedOutputNoresultStream.write(layout_file.getBytes());
@@ -72,7 +75,7 @@
             e.printStackTrace();
         }
     }
-    
+
     public void timedout(String url) {
         try {
             mBufferedOutputTimedoutStream.write(url.getBytes());
@@ -82,14 +85,14 @@
             e.printStackTrace();
         }
     }
-    
+
     public MyTestRecorder(boolean resume) {
         try {
             File resultsPassedFile = new File("/sdcard/layout_tests_passed.txt");
             File resultsFailedFile = new File("/sdcard/layout_tests_failed.txt");
             File noExpectedResultFile = new File("/sdcard/layout_tests_nontext.txt");
             File resultTimedoutFile = new File("/sdcard/layout_tests_timedout.txt");
-          
+
             mBufferedOutputPassedStream =
                 new BufferedOutputStream(new FileOutputStream(resultsPassedFile, resume));
             mBufferedOutputFailedStream =
@@ -102,7 +105,7 @@
             e.printStackTrace();
         }
     }
-    
+
     public void close() {
         try {
             mBufferedOutputPassedStream.close();
@@ -120,7 +123,7 @@
 
     private static final String LOGTAG = "LayoutTests";
     static final int DEFAULT_TIMEOUT_IN_MILLIS = 5000;
-    
+
     static final String LAYOUT_TESTS_ROOT = "/sdcard/android/layout_tests/";
     static final String LAYOUT_TESTS_RESULT_DIR = "/sdcard/android/layout_tests_results/";
     static final String ANDROID_EXPECTED_RESULT_DIR = "/sdcard/android/expected_results/";
@@ -139,14 +142,35 @@
     static final String LAYOUT_RESULTS_CRASHED_RESULT_FILE = "results/layout_tests_crashed.txt";
     static final String LAYOUT_TESTS_RUNNER = "run_layout_tests.py";
 
+    static final String HTTP_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/";
+    static final String HTTPS_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/ssl/";
+    static final String HTTP_LOCAL_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/local/";
+    static final String HTTP_MEDIA_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/media/";
+    static final String HTTP_WML_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/wml/";
+
+
+    private ForwardServer fs8000, fs8080, fs8443;
+
     private MyTestRecorder mResultRecorder;
     private Vector<String> mTestList;
     private boolean mRebaselineResults;
     private String mTestPathPrefix;
     private boolean mFinished;
-    
+
     public LayoutTestsAutoTest() {
       super("com.android.dumprendertree", TestShellActivity.class);
+
+      int addr = -1;
+      try {
+          addr = AdbUtils.resolve("android-browser-test.mtv.corp.google.com");
+      } catch (IOException ioe) {
+          Log.e(LOGTAG, "failed to resolve server address.", ioe);
+      }
+      if(addr != -1) {
+          fs8000 = new ForwardServer(8000, addr, 8000);
+          fs8080 = new ForwardServer(8080, addr, 8080);
+          fs8443 = new ForwardServer(8443, addr, 8443);
+      }
     }
 
     // This function writes the result of the layout test to
@@ -157,7 +181,7 @@
       bundle.putBoolean(file, result);
       inst.sendStatus(0, bundle);
     }
-    
+
     private void getTestList() {
         // Read test list.
         try {
@@ -174,7 +198,7 @@
             Log.e(LOGTAG, "Error while reading test list : " + e.getMessage());
         }
     }
-  
+
     private void resumeTestList() {
         // read out the test name it stoped last time.
         try {
@@ -189,7 +213,7 @@
             Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE);
         }
     }
-  
+
     private void clearTestStatus() {
         // Delete TEST_STATUS_FILE
         try {
@@ -208,13 +232,13 @@
         // Write actual results to result directory.
         return shortName.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_RESULT_DIR) + "-result.txt";
     }
-    
+
     private String getExpectedResultFile(String test) {
         int pos = test.lastIndexOf('.');
         if(pos == -1)
             return null;
         String shortName = test.substring(0, pos);
-        return shortName + "-expected.txt";          
+        return shortName + "-expected.txt";
     }
 
     private String getAndroidExpectedResultFile(String expectedResultFile) {
@@ -224,7 +248,7 @@
     // Wrap up
     private void failedCase(String file) {
         Log.w("Layout test: ", file + " failed");
-        mResultRecorder.failed(file);    
+        mResultRecorder.failed(file);
     }
 
     private void passedCase(String file) {
@@ -236,7 +260,7 @@
         Log.v("Layout test:", file + " no expected result");
         mResultRecorder.noresult(file);
     }
-     
+
     private void processResult(String testFile, String actualResultFile, String expectedResultFile) {
         Log.v(LOGTAG, "  Processing result: " + testFile);
 
@@ -257,13 +281,13 @@
                         break;
                     }
                 }
-                
+
                 if (passing) {
                     passedCase(testFile);
                 } else {
                     failedCase(testFile);
                 }
-                
+
                 fe.close();
                 fr.close();
             } catch (FileNotFoundException ex) {
@@ -278,7 +302,7 @@
             noresultCase(testFile);
         }
     }
-    
+
     private void runTestAndWaitUntilDone(TestShellActivity activity, String test, int timeout) {
         activity.setCallback(new TestShellCallback() {
             public void finished() {
@@ -287,7 +311,7 @@
                     LayoutTestsAutoTest.this.notifyAll();
                 }
             }
-            
+
             public void timedOut(String url) {
             }
         });
@@ -306,16 +330,16 @@
 
             resultFile = getAndroidExpectedResultFile(expectedResultFile);
         }
-        
+
         mFinished = false;
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setClass(activity, TestShellActivity.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        intent.putExtra(TestShellActivity.TEST_URL, "file://" + test);
+        intent.putExtra(TestShellActivity.TEST_URL, getTestUrl(test));
         intent.putExtra(TestShellActivity.RESULT_FILE, resultFile);
         intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout);
         activity.startActivity(intent);
-      
+
         // Wait until done.
         synchronized (this) {
             while(!mFinished){
@@ -324,18 +348,18 @@
                 } catch (InterruptedException e) { }
             }
         }
-        
+
         if (!mRebaselineResults) {
             String expectedResultFile = getExpectedResultFile(test);
             File f = new File(expectedResultFile);
             if (!f.exists()) {
                 expectedResultFile = getAndroidExpectedResultFile(expectedResultFile);
             }
-            
+
             processResult(test, resultFile, expectedResultFile);
         }
-    } 
-    
+    }
+
     // Invokes running of layout tests
     // and waits till it has finished running.
     public void executeLayoutTests(boolean resume) {
@@ -348,28 +372,28 @@
         }
 
         this.mTestList = new Vector<String>();
-      
+
         // Read settings
         try {
             this.mTestPathPrefix =
                 (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getCanonicalPath();
-        } catch (IOException e) {  
+        } catch (IOException e) {
             Log.e(LOGTAG, "Cannot find test path prefix: " + e.getMessage());
             return;
         }
-        
+
         this.mRebaselineResults = runner.mRebaseline;
-        
+
         int timeout = runner.mTimeoutInMillis;
         if (timeout <= 0) {
             timeout = DEFAULT_TIMEOUT_IN_MILLIS;
         }
-          
+
         this.mResultRecorder = new MyTestRecorder(resume);
-          
+
         if (!resume)
             clearTestStatus();
-          
+
         getTestList();
         if (resume)
             resumeTestList();
@@ -377,6 +401,15 @@
         TestShellActivity activity = (TestShellActivity) getActivity();
 
         // Run tests.
+        int addr = -1;
+        try{
+            addr = AdbUtils.resolve("android-browser-test.mtv.corp.google.com");
+        } catch (IOException ioe) {
+            Log.w(LOGTAG, "error while resolving test host name", ioe);
+        }
+        if(addr == -1) {
+            Log.w(LOGTAG, "failed to resolve test host. http tests will fail.");
+        }
         for (int i = 0; i < mTestList.size(); i++) {
             String s = mTestList.elementAt(i);
             FsUtils.updateTestStatus(TEST_STATUS_FILE, s);
@@ -385,10 +418,48 @@
         }
 
         FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE");
-        
+        if(fs8000 != null)
+            fs8000.stop();
+        if(fs8080 != null)
+            fs8080.stop();
+        if(fs8443 != null)
+            fs8443.stop();
+
         activity.finish();
     }
 
+    private void startForwardServerIfNeeded() {
+        try {
+            if(fs8000 != null)
+                fs8000.start();
+            if(fs8080 != null)
+                fs8080.start();
+            if(fs8443 != null)
+                fs8443.start();
+        } catch (IOException ioe) {
+            Log.w(LOGTAG, "failed to start forwarder. http tests will fail.", ioe);
+        }
+    }
+
+    private String getTestUrl(String path) {
+        String url = null;
+        if (!path.startsWith(HTTP_TESTS_PREFIX)) {
+            url = "file://" + path;
+        } else {
+            startForwardServerIfNeeded();
+            if (path.startsWith(HTTPS_TESTS_PREFIX)) {
+                // still cut the URL after "http/tests/"
+                url = "https://127.0.0.1:8443/" + path.substring(HTTP_TESTS_PREFIX.length());
+            } else if (!path.startsWith(HTTP_LOCAL_TESTS_PREFIX)
+                    && !path.startsWith(HTTP_MEDIA_TESTS_PREFIX)
+                    && !path.startsWith(HTTP_WML_TESTS_PREFIX)) {
+                url = "http://127.0.0.1:8000/" + path.substring(HTTP_TESTS_PREFIX.length());
+            } else {
+                url = "file://" + path;
+            }
+        }
+        return url;
+    }
 
     private String getTestPath() {
         LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation();
@@ -403,10 +474,10 @@
             Log.e("LayoutTestsAutoTest", "Cannot get cannonical path " + e.getMessage());
         }
         Log.v("LayoutTestsAutoTest", " Test path : " + test_path);
-        
+
         return test_path;
     }
-    
+
     public void generateTestList() {
         try {
             File tests_list = new File(LAYOUT_TESTS_LIST_FILE);
@@ -431,7 +502,7 @@
         } catch (Exception e) {
             e.printStackTrace();
         }
-        
+
         executeLayoutTests(false);
     }
 
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index 48c1e5d..4483a8e 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -531,6 +531,12 @@
         }
 
         @Override
+        public boolean onJsTimeout() {
+            Log.v(LOGTAG, "JavaScript timeout");
+            return false;
+        }
+
+        @Override
         public void onExceededDatabaseQuota(String url_str,
                 String databaseIdentifier, long currentQuota,
                 WebStorage.QuotaUpdater callback) {
@@ -614,6 +620,9 @@
         }
 
         WebSettings settings = webview.getSettings();
+        settings.setAppCacheEnabled(true);
+        settings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
+        settings.setAppCacheMaxSize(Long.MAX_VALUE);
         settings.setJavaScriptEnabled(true);
         settings.setJavaScriptCanOpenWindowsAutomatically(true);
         settings.setSupportMultipleWindows(true);
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java
new file mode 100644
index 0000000..9a3e9c2
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java
@@ -0,0 +1,112 @@
+package com.android.dumprendertree.forwarder;
+
+import android.util.Log;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+public class AdbUtils {
+
+    private static final String ADB_OK = "OKAY";
+    private static final int ADB_PORT = 5037;
+    private static final String ADB_HOST = "127.0.0.1";
+    private static final int ADB_RESPONSE_SIZE = 4;
+
+    private static final String LOGTAG = "AdbUtils";
+
+    /**
+     *
+     * Convert integer format IP into xxx.xxx.xxx.xxx format
+     *
+     * @param host IP address in integer format
+     * @return human readable format
+     */
+    public static String convert(int host) {
+        return ((host >> 24) & 0xFF) + "."
+        + ((host >> 16) & 0xFF) + "."
+        + ((host >> 8) & 0xFF) + "."
+        + (host & 0xFF);
+    }
+
+    /**
+     *
+     * Resolve DNS name into IP address
+     *
+     * @param host DNS name
+     * @return IP address in integer format
+     * @throws IOException
+     */
+    public static int resolve(String host)  throws IOException {
+        Socket localSocket = new Socket(ADB_HOST, ADB_PORT);
+        DataInputStream dis = new DataInputStream(localSocket.getInputStream());
+        OutputStream os = localSocket.getOutputStream();
+        int count_read = 0;
+        byte[] buf = new byte[128];
+
+        if (localSocket == null || dis == null || os == null)
+            return -1;
+        String cmd = "dns:" + host;
+
+        if(!sendAdbCmd(dis, os, cmd))
+            return -1;
+
+        count_read = dis.readInt();
+        localSocket.close();
+        return count_read;
+    }
+
+    /**
+     *
+     * Send an ADB command using existing socket connection
+     *
+     * the streams provided must be from a socket connected to adbd already
+     *
+     * @param is input stream of the socket connection
+     * @param os output stream of the socket
+     * @param cmd the adb command to send
+     * @return if adb gave a success response
+     * @throws IOException
+     */
+    public static boolean sendAdbCmd(InputStream is, OutputStream os,
+            String cmd) throws IOException {
+        byte[] buf = new byte[ADB_RESPONSE_SIZE];
+
+        cmd = String.format("%04X", cmd.length()) + cmd;
+        os.write(cmd.getBytes());
+        int read = is.read(buf);
+        if(read != ADB_RESPONSE_SIZE || !ADB_OK.equals(new String(buf))) {
+            Log.w(LOGTAG, "adb cmd faild.");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     *
+     * Get a tcp socket connection to specified IP address and port proxied by adb
+     *
+     * The proxying is transparent, e.g. if a socket is returned, then it can be written to and
+     * read from as if it is directly connected to the target
+     *
+     * @param remoteAddress IP address of the host to connect to
+     * @param remotePort port of the host to connect to
+     * @return a valid Socket instance if successful, null otherwise
+     */
+    public static Socket getForwardedSocket(int remoteAddress, int remotePort) {
+        try {
+            Socket socket = new Socket(ADB_HOST, ADB_PORT);
+            String cmd = "tcp:" + remotePort + ":" + convert(remoteAddress);
+            if(!sendAdbCmd(socket.getInputStream(), socket.getOutputStream(), cmd)) {
+                socket.close();
+                return null;
+            }
+            return socket;
+        } catch (IOException ioe) {
+            Log.w(LOGTAG, "error creating adb socket", ioe);
+            return null;
+        }
+    }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java
new file mode 100644
index 0000000..74e018e
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java
@@ -0,0 +1,117 @@
+package com.android.dumprendertree.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ *
+ * A port forwarding server. Listens at specified local port and forward the tcp communications to
+ * external host/port via adb networking proxy.
+ *
+ */
+public class ForwardServer {
+
+    private static final String LOGTAG = "ForwardServer";
+
+    private int remotePort;
+    private int remoteAddress;
+    private int localPort;
+    private ServerSocket serverSocket;
+    private boolean started;
+
+    private Set<Forwarder> forwarders;
+
+    public ForwardServer(int localPort, int remoteAddress, int remotePort) {
+        this.localPort = localPort;
+        this.remoteAddress = remoteAddress;
+        this.remotePort = remotePort;
+        started = false;
+        forwarders = new HashSet<Forwarder>();
+    }
+
+    public synchronized void start() throws IOException {
+        if(!started) {
+            serverSocket = new ServerSocket(localPort);
+            Thread serverThread = new Thread(new ServerRunner(serverSocket));
+            serverThread.setName(LOGTAG);
+            serverThread.start();
+            started = true;
+        }
+    }
+
+    public synchronized void stop() {
+        if(started) {
+            synchronized (forwarders) {
+                for(Forwarder forwarder : forwarders)
+                    forwarder.stop();
+                forwarders.clear();
+            }
+            try {
+                serverSocket.close();
+            } catch (IOException ioe) {
+                Log.v(LOGTAG, "exception while closing", ioe);
+            } finally {
+                started = false;
+            }
+        }
+    }
+
+    public synchronized boolean isRunning() {
+        return started;
+    }
+
+    private class ServerRunner implements Runnable {
+
+        private ServerSocket socket;
+
+        public ServerRunner(ServerSocket socket) {
+            this.socket = socket;
+        }
+
+        public void run() {
+            try {
+                while (true) {
+                    Socket localSocket = socket.accept();
+                    Socket remoteSocket = AdbUtils.getForwardedSocket(remoteAddress, remotePort);
+                    if(remoteSocket == null) {
+                        try {
+                            localSocket.close();
+                        } catch (IOException ioe) {
+                            Log.w(LOGTAG, "error while closing socket", ioe);
+                        } finally {
+                            Log.w(LOGTAG, "failed to start forwarding from " + localSocket);
+                        }
+                    } else {
+                        Forwarder forwarder = new Forwarder(localSocket, remoteSocket,
+                                ForwardServer.this);
+                        forwarder.start();
+                    }
+                }
+            } catch (IOException ioe) {
+                return;
+            }
+        }
+    }
+
+    public void register(Forwarder forwarder) {
+        synchronized (forwarders) {
+            if(!forwarders.contains(forwarder)) {
+                forwarders.add(forwarder);
+            }
+        }
+    }
+
+    public void unregister(Forwarder recyclable) {
+        synchronized (forwarders) {
+            if(forwarders.contains(recyclable)) {
+                recyclable.stop();
+                forwarders.remove(recyclable);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java
new file mode 100644
index 0000000..e1e04a7
--- /dev/null
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java
@@ -0,0 +1,92 @@
+package com.android.dumprendertree.forwarder;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+
+/**
+ *
+ * Worker class for {@link ForwardServer}. A Forwarder will be created once the ForwardServer
+ * accepts an incoming connection, and it will then forward the incoming/outgoing streams to a
+ * connection already proxied by adb networking (see also {@link AdbUtils}).
+ *
+ */
+public class Forwarder {
+
+    private ForwardServer server;
+    private Socket from, to;
+
+    private static final String LOGTAG = "Forwarder";
+
+    public Forwarder (Socket from, Socket to, ForwardServer server) {
+        this.server = server;
+        this.from = from;
+        this.to = to;
+        server.register(this);
+    }
+
+    public void start() {
+        Thread outgoing = new Thread(new SocketPipe(from, to));
+        Thread incoming = new Thread(new SocketPipe(to, from));
+        outgoing.setName(LOGTAG);
+        incoming.setName(LOGTAG);
+        outgoing.start();
+        incoming.start();
+    }
+
+    public void stop() {
+        shutdown(from);
+        shutdown(to);
+    }
+
+    private void shutdown(Socket socket) {
+        try {
+            socket.shutdownInput();
+        } catch (IOException e) {
+            Log.v(LOGTAG, "Socket#shutdownInput", e);
+        }
+        try {
+            socket.shutdownOutput();
+        } catch (IOException e) {
+            Log.v(LOGTAG, "Socket#shutdownOutput", e);
+        }
+        try {
+            socket.close();
+        } catch (IOException e) {
+            Log.v(LOGTAG, "Socket#close", e);
+        }
+    }
+
+    private class SocketPipe implements Runnable {
+
+        private Socket in, out;
+
+        public SocketPipe(Socket in, Socket out) {
+            this.in = in;
+            this.out = out;
+        }
+
+        public void run() {
+            try {
+                int length;
+                InputStream is = in.getInputStream();
+                OutputStream os = out.getOutputStream();
+                byte[] buffer = new byte[4096];
+                while ((length = is.read(buffer)) > 0) {
+                    os.write(buffer, 0, length);
+                }
+            } catch (IOException ioe) {
+            } finally {
+                server.unregister(Forwarder.this);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "SocketPipe{" + in + "=>" + out  + "}";
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 12abce5..083cda3 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -1467,6 +1467,7 @@
     public synchronized boolean restart() {
         if (mRunState == RUN_STATE_STOPPED) {
             mRunState = RUN_STATE_STARTING;
+            resetInterface(true);
             return WifiNative.startDriverCommand();
         } else if (mRunState == RUN_STATE_STOPPING) {
             mRunState = RUN_STATE_STARTING;