Merge "Use libcorkscrew to format the stack trace."
diff --git a/api/current.txt b/api/current.txt
index 62291f7..4cf23eb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14244,13 +14244,6 @@
     method public static void texSubImage2D(int, int, int, int, android.graphics.Bitmap, int, int);
   }
 
-  public abstract class ManagedEGLContext {
-    ctor public ManagedEGLContext(javax.microedition.khronos.egl.EGLContext);
-    method public javax.microedition.khronos.egl.EGLContext getContext();
-    method public abstract void onTerminate(javax.microedition.khronos.egl.EGLContext);
-    method public void terminate();
-  }
-
   public class Matrix {
     ctor public Matrix();
     method public static void frustumM(float[], int, float, float, float, float, float, float);
@@ -18434,14 +18427,14 @@
     ctor public RSSurfaceView(android.content.Context);
     ctor public RSSurfaceView(android.content.Context, android.util.AttributeSet);
     method public android.renderscript.RenderScriptGL createRenderScriptGL(android.renderscript.RenderScriptGL.SurfaceConfig);
-    method public synchronized void destroyRenderScriptGL();
+    method public void destroyRenderScriptGL();
     method public android.renderscript.RenderScriptGL getRenderScriptGL();
     method public void pause();
     method public void resume();
     method public void setRenderScriptGL(android.renderscript.RenderScriptGL);
-    method public synchronized void surfaceChanged(android.view.SurfaceHolder, int, int, int);
+    method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
     method public void surfaceCreated(android.view.SurfaceHolder);
-    method public synchronized void surfaceDestroyed(android.view.SurfaceHolder);
+    method public void surfaceDestroyed(android.view.SurfaceHolder);
   }
 
   public class RSTextureView extends android.view.TextureView implements android.view.TextureView.SurfaceTextureListener {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 4e38011..7ca6155 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -22,12 +22,9 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.SystemProperties;
 import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceHolder;
@@ -157,7 +154,6 @@
     private boolean mOneShot;
     private boolean mWithBuffer;
     private boolean mFaceDetectionRunning = false;
-    private boolean mReleased = false;
 
     /**
      * Broadcast Action:  A new picture is taken by the camera, and the entry of
@@ -322,15 +318,6 @@
     public final void release() {
         native_release();
         mFaceDetectionRunning = false;
-        if (mCameraSoundPlayers != null) {
-            for (CameraSoundPlayer csp: mCameraSoundPlayers) {
-                if (csp != null) {
-                    csp.release();
-                }
-            }
-            mCameraSoundPlayers = null;
-        }
-        mReleased = true;
     }
 
     /**
@@ -3503,194 +3490,4 @@
             return false;
         }
     };
-
-    /**
-     * <p>The set of default system sounds for camera actions. Use this with
-     * {@link #playSound} to play an appropriate sound when implementing a
-     * custom still or video recording mechanism through the preview
-     * callbacks.</p>
-     *
-     * <p>There is no need to play sounds when using {@link #takePicture} or
-     * {@link android.media.MediaRecorder} for still images or video,
-     * respectively, as these play their own sounds when needed.</p>
-     *
-     * @see #playSound
-     * @hide
-     */
-    public static class Sound {
-        /**
-         * The sound used by {@link android.hardware.Camera#takePicture} to
-         * indicate still image capture.
-         */
-        public static final int SHUTTER_CLICK         = 0;
-
-        /**
-         * A sound to indicate that focusing has completed. Because deciding
-         * when this occurs is application-dependent, this sound is not used by
-         * any methods in the Camera class.
-         */
-        public static final int FOCUS_COMPLETE        = 1;
-
-        /**
-         * The sound used by {@link android.media.MediaRecorder#start} to
-         * indicate the start of video recording.
-         */
-        public static final int START_VIDEO_RECORDING = 2;
-
-        /**
-         * The sound used by {@link android.media.MediaRecorder#stop} to
-         * indicate the end of video recording.
-         */
-        public static final int STOP_VIDEO_RECORDING  = 3;
-
-        private static final int NUM_SOUNDS           = 4;
-    };
-
-    /**
-     * <p>Play one of the predefined platform sounds for camera actions.</p>
-     *
-     * <p>Use this method to play a platform-specific sound for various camera
-     * actions. The sound playing is done asynchronously, with the same behavior
-     * and content as the sounds played by {@link #takePicture takePicture},
-     * {@link android.media.MediaRecorder#start MediaRecorder.start}, and
-     * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p>
-     *
-     * <p>Using this method makes it easy to match the default device sounds
-     * when recording or capturing data through the preview callbacks
-     * ({@link #setPreviewCallback setPreviewCallback},
-     * {@link #setPreviewTexture setPreviewTexture}).</p>
-     *
-     * @param soundId The type of sound to play, selected from the options in
-     *   {@link android.hardware.Camera.Sound}
-     * @see android.hardware.Camera.Sound
-     * @see #takePicture
-     * @see android.media.MediaRecorder
-     * @hide
-     */
-    public void playSound(int soundId) {
-        if (mReleased) return;
-        if (mCameraSoundPlayers == null) {
-            mCameraSoundPlayers = new CameraSoundPlayer[Sound.NUM_SOUNDS];
-        }
-        if (mCameraSoundPlayers[soundId] == null) {
-            mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId);
-        }
-        mCameraSoundPlayers[soundId].play();
-    }
-
-    private CameraSoundPlayer[] mCameraSoundPlayers;
-
-    private static class CameraSoundPlayer implements Runnable {
-        private int mSoundId;
-        private int mAudioStreamType;
-        private MediaPlayer mPlayer;
-        private Thread mThread;
-        private boolean mExit;
-        private int mPlayCount;
-
-        private static final String mShutterSound    =
-                "/system/media/audio/ui/camera_click.ogg";
-        private static final String mFocusSound      =
-                "/system/media/audio/ui/camera_focus.ogg";
-        private static final String mVideoStartSound =
-                "/system/media/audio/ui/VideoRecord.ogg";
-        private static final String mVideoStopSound  =
-                "/system/media/audio/ui/VideoRecord.ogg";
-
-        @Override
-        public void run() {
-            String soundFilePath;
-            switch (mSoundId) {
-                case Sound.SHUTTER_CLICK:
-                    soundFilePath = mShutterSound;
-                    break;
-                case Sound.FOCUS_COMPLETE:
-                    soundFilePath = mFocusSound;
-                    break;
-                case Sound.START_VIDEO_RECORDING:
-                    soundFilePath = mVideoStartSound;
-                    break;
-                case Sound.STOP_VIDEO_RECORDING:
-                    soundFilePath = mVideoStopSound;
-                    break;
-                default:
-                    Log.e(TAG, "Unknown sound " + mSoundId + " requested.");
-                    return;
-            }
-            mPlayer = new MediaPlayer();
-            try {
-                mPlayer.setAudioStreamType(mAudioStreamType);
-                mPlayer.setDataSource(soundFilePath);
-                mPlayer.setLooping(false);
-                mPlayer.prepare();
-            } catch(IOException e) {
-                Log.e(TAG, "Error setting up sound " + mSoundId, e);
-                return;
-            }
-
-            while(true) {
-                try {
-                    synchronized (this) {
-                        while(true) {
-                            if (mExit) {
-                                return;
-                            } else if (mPlayCount <= 0) {
-                                wait();
-                            } else {
-                                mPlayCount--;
-                                break;
-                            }
-                        }
-                    }
-                    mPlayer.start();
-                } catch (Exception e) {
-                    Log.e(TAG, "Error playing sound " + mSoundId, e);
-                }
-            }
-        }
-
-        public CameraSoundPlayer(int soundId) {
-            mSoundId = soundId;
-            if (SystemProperties.get("ro.camera.sound.forced", "0").equals("0")) {
-                mAudioStreamType = AudioManager.STREAM_MUSIC;
-            } else {
-                mAudioStreamType = AudioManager.STREAM_SYSTEM_ENFORCED;
-            }
-        }
-
-        public void play() {
-            if (mThread == null) {
-                mThread = new Thread(this);
-                mThread.start();
-            }
-            synchronized (this) {
-                mPlayCount++;
-                notifyAll();
-            }
-        }
-
-        public void release() {
-            if (mThread != null) {
-                synchronized (this) {
-                    mExit = true;
-                    notifyAll();
-                }
-                try {
-                    mThread.join();
-                } catch (InterruptedException e) {
-                }
-                mThread = null;
-            }
-            if (mPlayer != null) {
-                mPlayer.release();
-                mPlayer = null;
-            }
-        }
-
-        @Override
-        protected void finalize() {
-            release();
-        }
-    }
-
 }
diff --git a/core/java/android/hardware/CameraSound.java b/core/java/android/hardware/CameraSound.java
new file mode 100644
index 0000000..32de0cd
--- /dev/null
+++ b/core/java/android/hardware/CameraSound.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware;
+
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * <p>Use this class to play an appropriate sound when implementing a custom
+ * still or video recording mechanism through the preview callbacks.</p>
+ *
+ * <p>There is no need to play sounds when using {@link #android.hardware.Camera#takePicture}
+ * or {@link android.media.MediaRecorder} for still images or video,
+ * respectively, as these play their own sounds when needed.</p>
+ *
+ * @hide
+ */
+public class CameraSound {
+    private static final String TAG = "CameraSound";
+    /**
+     * The sound used by {@link android.hardware.Camera#takePicture} to
+     * indicate still image capture.
+     */
+    public static final int SHUTTER_CLICK         = 0;
+
+    /**
+     * A sound to indicate that focusing has completed. Because deciding
+     * when this occurs is application-dependent, this sound is not used by
+     * any methods in the Camera class.
+     */
+    public static final int FOCUS_COMPLETE        = 1;
+
+    /**
+     * The sound used by {@link android.media.MediaRecorder#start} to
+     * indicate the start of video recording.
+     */
+    public static final int START_VIDEO_RECORDING = 2;
+
+    /**
+     * The sound used by {@link android.media.MediaRecorder#stop} to
+     * indicate the end of video recording.
+     */
+    public static final int STOP_VIDEO_RECORDING  = 3;
+
+    private static final int NUM_SOUNDS           = 4;
+    private CameraSoundPlayer[] mCameraSoundPlayers;
+
+    public CameraSound() {
+    }
+
+    /**
+     * <p>Play one of the predefined platform sounds for camera actions.</p>
+     *
+     * <p>Use this method to play a platform-specific sound for various camera
+     * actions. The sound playing is done asynchronously, with the same behavior
+     * and content as the sounds played by {@link #takePicture takePicture},
+     * {@link android.media.MediaRecorder#start MediaRecorder.start}, and
+     * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p>
+     *
+     * <p>Using this method makes it easy to match the default device sounds
+     * when recording or capturing data through the preview callbacks.</p>
+     *
+     * @param soundId The type of sound to play, selected from SHUTTER_CLICK,
+     *         FOCUS_COMPLETE, START_VIDEO_RECORDING, or STOP_VIDEO_RECORDING.
+     * @see android.hardware#takePicture
+     * @see android.media.MediaRecorder
+     * @see #SHUTTER_CLICK
+     * @see #FOCUS_COMPLETE
+     * @see #START_VIDEO_RECORDING
+     * @see #STOP_VIDEO_RECORDING
+     */
+    public void playSound(int soundId) {
+        if (mCameraSoundPlayers == null) {
+            mCameraSoundPlayers = new CameraSoundPlayer[NUM_SOUNDS];
+        }
+        if (mCameraSoundPlayers[soundId] == null) {
+            mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId);
+        }
+        mCameraSoundPlayers[soundId].play();
+    }
+
+    public void release() {
+        if (mCameraSoundPlayers != null) {
+            for (CameraSoundPlayer csp: mCameraSoundPlayers) {
+                if (csp != null) {
+                    csp.release();
+                }
+            }
+            mCameraSoundPlayers = null;
+        }
+    }
+
+    private static class CameraSoundPlayer implements Runnable {
+        private int mSoundId;
+        private int mAudioStreamType;
+        private MediaPlayer mPlayer;
+        private Thread mThread;
+        private boolean mExit;
+        private int mPlayCount;
+
+        private static final String mShutterSound    =
+                "/system/media/audio/ui/camera_click.ogg";
+        private static final String mFocusSound      =
+                "/system/media/audio/ui/camera_focus.ogg";
+        private static final String mVideoStartSound =
+                "/system/media/audio/ui/VideoRecord.ogg";
+        private static final String mVideoStopSound  =
+                "/system/media/audio/ui/VideoRecord.ogg";
+
+        @Override
+        public void run() {
+            String soundFilePath;
+            switch (mSoundId) {
+                case SHUTTER_CLICK:
+                    soundFilePath = mShutterSound;
+                    break;
+                case FOCUS_COMPLETE:
+                    soundFilePath = mFocusSound;
+                    break;
+                case START_VIDEO_RECORDING:
+                    soundFilePath = mVideoStartSound;
+                    break;
+                case STOP_VIDEO_RECORDING:
+                    soundFilePath = mVideoStopSound;
+                    break;
+                default:
+                    Log.e(TAG, "Unknown sound " + mSoundId + " requested.");
+                    return;
+            }
+            mPlayer = new MediaPlayer();
+            try {
+                mPlayer.setAudioStreamType(mAudioStreamType);
+                mPlayer.setDataSource(soundFilePath);
+                mPlayer.setLooping(false);
+                mPlayer.prepare();
+            } catch(IOException e) {
+                Log.e(TAG, "Error setting up sound " + mSoundId, e);
+                return;
+            }
+
+            while(true) {
+                try {
+                    synchronized (this) {
+                        while(true) {
+                            if (mExit) {
+                                return;
+                            } else if (mPlayCount <= 0) {
+                                wait();
+                            } else {
+                                mPlayCount--;
+                                break;
+                            }
+                        }
+                    }
+                    mPlayer.start();
+                } catch (Exception e) {
+                    Log.e(TAG, "Error playing sound " + mSoundId, e);
+                }
+            }
+        }
+
+        public CameraSoundPlayer(int soundId) {
+            mSoundId = soundId;
+            if (SystemProperties.get("ro.camera.sound.forced", "0").equals("0")) {
+                mAudioStreamType = AudioManager.STREAM_MUSIC;
+            } else {
+                mAudioStreamType = AudioManager.STREAM_SYSTEM_ENFORCED;
+            }
+        }
+
+        public void play() {
+            if (mThread == null) {
+                mThread = new Thread(this);
+                mThread.start();
+            }
+            synchronized (this) {
+                mPlayCount++;
+                notifyAll();
+            }
+        }
+
+        public void release() {
+            if (mThread != null) {
+                synchronized (this) {
+                    mExit = true;
+                    notifyAll();
+                }
+                try {
+                    mThread.join();
+                } catch (InterruptedException e) {
+                }
+                mThread = null;
+            }
+            if (mPlayer != null) {
+                mPlayer.release();
+                mPlayer = null;
+            }
+        }
+
+        @Override
+        protected void finalize() {
+            release();
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index fc6a44a..397a12a 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -347,21 +347,25 @@
 
         if (success) {
             if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
-           long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion
+            long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion
 
-           //Sanity check for renewal
-           //TODO: would be good to notify the user that his network configuration is
-           //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
-           if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
-               leaseDuration = MIN_RENEWAL_TIME_SECS;
-           }
-           //Do it a bit earlier than half the lease duration time
-           //to beat the native DHCP client and avoid extra packets
-           //48% for one hour lease time = 29 minutes
-           mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                   SystemClock.elapsedRealtime() +
-                   leaseDuration * 480, //in milliseconds
-                   mDhcpRenewalIntent);
+            //Sanity check for renewal
+            if (leaseDuration >= 0) {
+                //TODO: would be good to notify the user that his network configuration is
+                //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
+                if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
+                    leaseDuration = MIN_RENEWAL_TIME_SECS;
+                }
+                //Do it a bit earlier than half the lease duration time
+                //to beat the native DHCP client and avoid extra packets
+                //48% for one hour lease time = 29 minutes
+                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                        SystemClock.elapsedRealtime() +
+                        leaseDuration * 480, //in milliseconds
+                        mDhcpRenewalIntent);
+            } else {
+                //infinite lease time, no renewal needed
+            }
 
             mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal)
                 .sendToTarget();
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 273ee91..02096f2 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -357,8 +357,11 @@
             throw new IllegalArgumentException("context cannot be null");
         }
         context = context.getApplicationContext();
-        /* use getSystemService() instead of just instantiating to take
-         * advantage of the context's cached NfcManager & NfcAdapter */
+        if (context == null) {
+            throw new IllegalArgumentException(
+                    "context not associated with any application (using a mock context?)");
+        }
+        /* use getSystemService() for consistency */
         NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
         if (manager == null) {
             // NFC not available
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
index 6ec2e21..2bbed57 100644
--- a/core/java/android/nfc/NfcManager.java
+++ b/core/java/android/nfc/NfcManager.java
@@ -40,6 +40,10 @@
     public NfcManager(Context context) {
         NfcAdapter adapter;
         context = context.getApplicationContext();
+        if (context == null) {
+            throw new IllegalArgumentException(
+                    "context not associated with any application (using a mock context?)");
+        }
         try {
             adapter = NfcAdapter.getNfcAdapter(context);
         } catch (UnsupportedOperationException e) {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 3362575..e1bc275 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -101,7 +101,7 @@
      * Defines the UID/GID for the NFC service process.
      * @hide
      */
-    public static final int NFC_UID = 1025;
+    public static final int NFC_UID = 1027;
 
     /**
      * Defines the GID for the group that allows write access to the internal media storage.
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 28251a6..5282e61 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -138,6 +138,25 @@
         }
 
         /**
+         * @hide
+         * The default implementation returns an array of SuggestionsInfo by simply calling
+         * onGetSuggestions().
+         * When you override this method, make sure that suggestionsLimit is applied to suggestions
+         * that share the same start position and length.
+         */
+        public SuggestionsInfo[] onGetSuggestionsMultipleForSentence(TextInfo[] textInfos,
+                int suggestionsLimit) {
+            final int length = textInfos.length;
+            final SuggestionsInfo[] retval = new SuggestionsInfo[length];
+            for (int i = 0; i < length; ++i) {
+                retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
+                retval[i].setCookieAndSequence(
+                        textInfos[i].getCookie(), textInfos[i].getSequence());
+            }
+            return retval;
+        }
+
+        /**
          * Request to abort all tasks executed in SpellChecker.
          * This function will run on the incoming IPC thread.
          * So, this is not called on the main thread,
@@ -196,6 +215,16 @@
         }
 
         @Override
+        public void onGetSuggestionsMultipleForSentence(
+                TextInfo[] textInfos, int suggestionsLimit) {
+            try {
+                mListener.onGetSuggestionsForSentence(
+                        mSession.onGetSuggestionsMultipleForSentence(textInfos, suggestionsLimit));
+            } catch (RemoteException e) {
+            }
+        }
+
+        @Override
         public void onCancel() {
             mSession.onCancel();
         }
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 8e39d6e..f77cf7e 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -162,13 +162,21 @@
     abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException;
 
     /**
-     * Destoys the layers used by the specified view hierarchy.
+     * Destroys the layers used by the specified view hierarchy.
      * 
      * @param view The root of the view hierarchy
      */
     abstract void destroyLayers(View view);
 
     /**
+     * Destroys all hardware rendering resources associated with the specified
+     * view hierarchy.
+     * 
+     * @param view The root of the view hierarchy
+     */
+    abstract void destroyHardwareResources(View view);
+    
+    /**
      * This method should be invoked whenever the current hardware renderer
      * context should be reset.
      * 
@@ -348,15 +356,6 @@
     }
 
     /**
-     * Invoke this method when the system needs to clean up all resources
-     * associated with hardware rendering.
-     */
-    static void terminate() {
-        Log.d(LOG_TAG, "Terminating hardware rendering");
-        Gl20Renderer.terminate();
-    }    
-    
-    /**
      * Indicates whether hardware acceleration is currently enabled.
      * 
      * @return True if hardware acceleration is in use, false otherwise.
@@ -412,8 +411,8 @@
         static final Object[] sEglLock = new Object[0];
         int mWidth = -1, mHeight = -1;
 
-        static final ThreadLocal<Gl20Renderer.MyEGLContext> sEglContextStorage
-                = new ThreadLocal<Gl20Renderer.MyEGLContext>();
+        static final ThreadLocal<Gl20Renderer.Gl20RendererEglContext> sEglContextStorage
+                = new ThreadLocal<Gl20Renderer.Gl20RendererEglContext>();
 
         EGLContext mEglContext;
         Thread mEglThread;
@@ -565,13 +564,13 @@
                 }
             }
 
-            Gl20Renderer.MyEGLContext managedContext = sEglContextStorage.get();
+            Gl20Renderer.Gl20RendererEglContext managedContext = sEglContextStorage.get();
             mEglContext = managedContext != null ? managedContext.getContext() : null;
             mEglThread = Thread.currentThread();
 
             if (mEglContext == null) {
                 mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
-                sEglContextStorage.set(new Gl20Renderer.MyEGLContext(mEglContext));
+                sEglContextStorage.set(new Gl20Renderer.Gl20RendererEglContext(mEglContext));
             }
         }
 
@@ -909,10 +908,10 @@
         private static EGLSurface sPbuffer;
         private static final Object[] sPbufferLock = new Object[0];
 
-        static class MyEGLContext extends ManagedEGLContext {
+        static class Gl20RendererEglContext extends ManagedEGLContext {
             final Handler mHandler = new Handler();
 
-            public MyEGLContext(EGLContext context) {
+            public Gl20RendererEglContext(EGLContext context) {
                 super(context);
             }
 
@@ -939,7 +938,8 @@
                         sEglContextStorage.remove();
 
                         sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
-                        sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+                        sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
+                                EGL_NO_SURFACE, EGL_NO_CONTEXT);
 
                         sEgl.eglReleaseThread();
                         sEgl.eglTerminate(sEglDisplay);
@@ -1046,10 +1046,9 @@
             }
         }
 
-        private void destroyHardwareLayer(View view) {
-            if (view.destroyLayer()) {
-                view.invalidate(true);
-            }
+        private static void destroyHardwareLayer(View view) {
+            view.destroyLayer();
+
             if (view instanceof ViewGroup) {
                 ViewGroup group = (ViewGroup) view;
 
@@ -1059,6 +1058,36 @@
                 }
             }
         }
+        
+        @Override
+        void destroyHardwareResources(View view) {
+            if (view != null) {
+                boolean needsContext = true;
+                if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false;
+
+                if (needsContext) {
+                    Gl20RendererEglContext managedContext = sEglContextStorage.get();
+                    if (managedContext == null) return;
+                    usePbufferSurface(managedContext.getContext());
+                }
+
+                destroyResources(view);
+                GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+            }
+        }
+        
+        private static void destroyResources(View view) {
+            view.destroyHardwareResources();
+
+            if (view instanceof ViewGroup) {
+                ViewGroup group = (ViewGroup) view;
+
+                int count = group.getChildCount();
+                for (int i = 0; i < count; i++) {
+                    destroyResources(group.getChildAt(i));
+                }
+            }
+        }
 
         static HardwareRenderer create(boolean translucent) {
             if (GLES20Canvas.isAvailable()) {
@@ -1070,7 +1099,7 @@
         static void trimMemory(int level) {
             if (sEgl == null || sEglConfig == null) return;
 
-            Gl20Renderer.MyEGLContext managedContext = sEglContextStorage.get();
+            Gl20RendererEglContext managedContext = sEglContextStorage.get();
             // We do not have OpenGL objects
             if (managedContext == null) {
                 return;
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 1697382..74916f0 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -203,7 +203,10 @@
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
+        destroySurface();
+    }
 
+    private void destroySurface() {
         if (mLayer != null) {
             boolean shouldRelease = true;
             if (mListener != null) {
@@ -300,6 +303,17 @@
         return false;
     }
 
+    /**
+     * @hide
+     */
+    @Override
+    protected void destroyHardwareResources() {
+        super.destroyHardwareResources();
+        destroySurface();
+        invalidateParentCaches();
+        invalidate(true);
+    }
+
     @Override
     HardwareLayer getHardwareLayer() {
         if (mLayer == null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 65b1bd0..74c1ce8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10129,7 +10129,7 @@
         switch (mLayerType) {
             case LAYER_TYPE_HARDWARE:
                 destroyLayer();
-                // fall through - unaccelerated views may use software layer mechanism instead
+                // fall through - non-accelerated views may use software layer mechanism instead
             case LAYER_TYPE_SOFTWARE:
                 destroyDrawingCache();
                 break;
@@ -10196,7 +10196,11 @@
 
         switch (mLayerType) {
             case LAYER_TYPE_HARDWARE:
-                getHardwareLayer();
+                if (mAttachInfo.mHardwareRenderer != null &&
+                        mAttachInfo.mHardwareRenderer.isEnabled() &&
+                        mAttachInfo.mHardwareRenderer.validate()) {
+                    getHardwareLayer();
+                }
                 break;
             case LAYER_TYPE_SOFTWARE:
                 buildDrawingCache(true);
@@ -10291,12 +10295,31 @@
         if (mHardwareLayer != null) {
             mHardwareLayer.destroy();
             mHardwareLayer = null;
+
+            invalidate(true);
+            invalidateParentCaches();
+
             return true;
         }
         return false;
     }
 
     /**
+     * Destroys all hardware rendering resources. This method is invoked
+     * when the system needs to reclaim resources. Upon execution of this
+     * method, you should free any OpenGL resources created by the view.
+     * 
+     * Note: you <strong>must</strong> call
+     * <code>super.destroyHardwareResources()</code> when overriding
+     * this method.
+     * 
+     * @hide
+     */
+    protected void destroyHardwareResources() {
+        destroyLayer();
+    }
+
+    /**
      * <p>Enables or disables the drawing cache. When the drawing cache is enabled, the next call
      * to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a
      * bitmap. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 723846a..18d5c40 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -577,6 +577,13 @@
         }
     }
 
+    void terminateHardwareResources() {
+        if (mAttachInfo.mHardwareRenderer != null) {
+            mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
+            mAttachInfo.mHardwareRenderer.destroy(false);
+        }
+    }
+
     void destroyHardwareLayers() {
         if (mThread != Thread.currentThread()) {
             if (mAttachInfo.mHardwareRenderer != null &&
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 660e3f4..dfd1d55 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -425,7 +425,7 @@
                             if (mViews == null) return;
                             int count = mViews.length;
                             for (int i = 0; i < count; i++) {
-                                mRoots[i].destroyHardwareResources();
+                                mRoots[i].terminateHardwareResources();
                             }
                         }
                         // Terminate the hardware renderer to free all resources
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 489587e..1d66cbe 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -88,14 +88,17 @@
      * This meta-data must reference an XML resource.
      **/
     public static final String SERVICE_META_DATA = "android.view.textservice.scs";
+    private static final String SUPPORT_SENTENCE_SPELL_CHECK = "SupportSentenceSpellCheck";
 
 
     private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
+    private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2;
 
     private final InternalListener mInternalListener;
     private final ITextServicesManager mTextServicesManager;
     private final SpellCheckerInfo mSpellCheckerInfo;
     private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl;
+    private final SpellCheckerSubtype mSubtype;
 
     private boolean mIsUsed;
     private SpellCheckerSessionListener mSpellCheckerSessionListener;
@@ -108,6 +111,9 @@
                 case MSG_ON_GET_SUGGESTION_MULTIPLE:
                     handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj);
                     break;
+                case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE:
+                    handleOnGetSuggestionsMultipleForSentence((SuggestionsInfo[]) msg.obj);
+                    break;
             }
         }
     };
@@ -117,7 +123,8 @@
      * @hide
      */
     public SpellCheckerSession(
-            SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener) {
+            SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener,
+            SpellCheckerSubtype subtype) {
         if (info == null || listener == null || tsm == null) {
             throw new NullPointerException();
         }
@@ -127,6 +134,7 @@
         mTextServicesManager = tsm;
         mIsUsed = true;
         mSpellCheckerSessionListener = listener;
+        mSubtype = subtype;
     }
 
     /**
@@ -167,6 +175,14 @@
     }
 
     /**
+     * @hide
+     */
+    public void getSuggestionsForSentence(TextInfo textInfo, int suggestionsLimit) {
+        mSpellCheckerSessionListenerImpl.getSuggestionsMultipleForSentence(
+                new TextInfo[] {textInfo}, suggestionsLimit);
+    }
+
+    /**
      * Get candidate strings for a substring of the specified text.
      * @param textInfo text metadata for a spell checker
      * @param suggestionsLimit the number of limit of suggestions returned
@@ -195,10 +211,15 @@
         mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos);
     }
 
+    private void handleOnGetSuggestionsMultipleForSentence(SuggestionsInfo[] suggestionInfos) {
+        mSpellCheckerSessionListener.onGetSuggestionsForSentence(suggestionInfos);
+    }
+
     private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub {
         private static final int TASK_CANCEL = 1;
         private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
         private static final int TASK_CLOSE = 3;
+        private static final int TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE = 4;
         private final Queue<SpellCheckerParams> mPendingTasks =
                 new LinkedList<SpellCheckerParams>();
         private Handler mHandler;
@@ -236,6 +257,9 @@
                 case TASK_CLOSE:
                     processClose();
                     break;
+                case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
+                    processGetSuggestionsMultipleForSentence(scp);
+                    break;
             }
         }
 
@@ -266,6 +290,15 @@
                             suggestionsLimit, sequentialWords));
         }
 
+        public void getSuggestionsMultipleForSentence(TextInfo[] textInfos, int suggestionsLimit) {
+            if (DBG) {
+                Log.w(TAG, "getSuggestionsMultipleForSentence");
+            }
+            processOrEnqueueTask(
+                    new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE,
+                            textInfos, suggestionsLimit, false));
+        }
+
         public void close() {
             if (DBG) {
                 Log.w(TAG, "close");
@@ -355,10 +388,34 @@
             }
         }
 
+        private void processGetSuggestionsMultipleForSentence(SpellCheckerParams scp) {
+            if (!checkOpenConnection()) {
+                return;
+            }
+            if (DBG) {
+                Log.w(TAG, "Get suggestions from the spell checker.");
+            }
+            if (scp.mTextInfos.length != 1) {
+                throw new IllegalArgumentException();
+            }
+            try {
+                mISpellCheckerSession.onGetSuggestionsMultipleForSentence(
+                        scp.mTextInfos, scp.mSuggestionsLimit);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to get suggestions " + e);
+            }
+        }
+
         @Override
         public void onGetSuggestions(SuggestionsInfo[] results) {
             mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results));
         }
+
+        @Override
+        public void onGetSuggestionsForSentence(SuggestionsInfo[] results) {
+            mHandler.sendMessage(
+                    Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results));
+        }
     }
 
     /**
@@ -370,6 +427,10 @@
          * @param results an array of results of getSuggestions
          */
         public void onGetSuggestions(SuggestionsInfo[] results);
+        /**
+         * @hide
+         */
+        public void onGetSuggestionsForSentence(SuggestionsInfo[] results);
     }
 
     private static class InternalListener extends ITextServicesSessionListener.Stub {
@@ -411,4 +472,11 @@
     public ISpellCheckerSessionListener getSpellCheckerSessionListener() {
         return mSpellCheckerSessionListenerImpl;
     }
+
+    /**
+     * @hide
+     */
+    public boolean isSentenceSpellCheckSupported() {
+        return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK);
+    }
 }
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index aeb3ba6..1bbaf6c 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -21,9 +21,11 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.Slog;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
@@ -33,11 +35,15 @@
  * Subtype can describe locale (e.g. en_US, fr_FR...) used for settings.
  */
 public final class SpellCheckerSubtype implements Parcelable {
+    private static final String TAG = SpellCheckerSubtype.class.getSimpleName();
+    private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
+    private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
 
     private final int mSubtypeHashCode;
     private final int mSubtypeNameResId;
     private final String mSubtypeLocale;
     private final String mSubtypeExtraValue;
+    private HashMap<String, String> mExtraValueHashMapCache;
 
     /**
      * Constructor
@@ -83,6 +89,48 @@
         return mSubtypeExtraValue;
     }
 
+    private HashMap<String, String> getExtraValueHashMap() {
+        if (mExtraValueHashMapCache == null) {
+            mExtraValueHashMapCache = new HashMap<String, String>();
+            final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR);
+            final int N = pairs.length;
+            for (int i = 0; i < N; ++i) {
+                final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR);
+                if (pair.length == 1) {
+                    mExtraValueHashMapCache.put(pair[0], null);
+                } else if (pair.length > 1) {
+                    if (pair.length > 2) {
+                        Slog.w(TAG, "ExtraValue has two or more '='s");
+                    }
+                    mExtraValueHashMapCache.put(pair[0], pair[1]);
+                }
+            }
+        }
+        return mExtraValueHashMapCache;
+    }
+
+    /**
+     * @hide
+     * The string of ExtraValue in subtype should be defined as follows:
+     * example: key0,key1=value1,key2,key3,key4=value4
+     * @param key the key of extra value
+     * @return the subtype contains specified the extra value
+     */
+    public boolean containsExtraValueKey(String key) {
+        return getExtraValueHashMap().containsKey(key);
+    }
+
+    /**
+     * @hide
+     * The string of ExtraValue in subtype should be defined as follows:
+     * example: key0,key1=value1,key2,key3,key4=value4
+     * @param key the key of extra value
+     * @return the value of the specified key
+     */
+    public String getExtraValueOf(String key) {
+        return getExtraValueHashMap().get(key);
+    }
+
     @Override
     public int hashCode() {
         return mSubtypeHashCode;
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
index ddd0361..9b99770 100644
--- a/core/java/android/view/textservice/SuggestionsInfo.java
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -21,11 +21,14 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Arrays;
+
 /**
  * This class contains a metadata of suggestions from the text service
  */
 public final class SuggestionsInfo implements Parcelable {
     private static final String[] EMPTY = ArrayUtils.emptyArray(String.class);
+    private static final int NOT_A_LENGTH = -1;
 
     /**
      * Flag of the attributes of the suggestions that can be obtained by
@@ -47,6 +50,8 @@
     public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 0x0004;
     private final int mSuggestionsAttributes;
     private final String[] mSuggestions;
+    private final int[] mStartPosArray;
+    private final int[] mLengthArray;
     private final boolean mSuggestionsAvailable;
     private int mCookie;
     private int mSequence;
@@ -57,16 +62,7 @@
      * @param suggestions from the text service
      */
     public SuggestionsInfo(int suggestionsAttributes, String[] suggestions) {
-        mSuggestionsAttributes = suggestionsAttributes;
-        if (suggestions == null) {
-            mSuggestions = EMPTY;
-            mSuggestionsAvailable = false;
-        } else {
-            mSuggestions = suggestions;
-            mSuggestionsAvailable = true;
-        }
-        mCookie = 0;
-        mSequence = 0;
+        this(suggestionsAttributes, suggestions, 0, 0);
     }
 
     /**
@@ -78,12 +74,46 @@
      */
     public SuggestionsInfo(
             int suggestionsAttributes, String[] suggestions, int cookie, int sequence) {
+        this(suggestionsAttributes, suggestions, cookie, sequence, null, null);
+    }
+
+    /**
+     * @hide
+     * Constructor.
+     * @param suggestionsAttributes from the text service
+     * @param suggestions from the text service
+     * @param cookie the cookie of the input TextInfo
+     * @param sequence the cookie of the input TextInfo
+     * @param startPosArray the array of start positions of suggestions
+     * @param lengthArray the array of length of suggestions
+     */
+    public SuggestionsInfo(
+            int suggestionsAttributes, String[] suggestions, int cookie, int sequence,
+            int[] startPosArray, int[] lengthArray) {
+        final int suggestsLen;
         if (suggestions == null) {
             mSuggestions = EMPTY;
             mSuggestionsAvailable = false;
+            suggestsLen = 0;
+            mStartPosArray = new int[0];
+            mLengthArray = new int[0];
         } else {
             mSuggestions = suggestions;
             mSuggestionsAvailable = true;
+            suggestsLen = suggestions.length;
+            if (startPosArray == null || lengthArray == null) {
+                mStartPosArray = new int[suggestsLen];
+                mLengthArray = new int[suggestsLen];
+                for (int i = 0; i < suggestsLen; ++i) {
+                    mStartPosArray[i] = 0;
+                    mLengthArray[i] = NOT_A_LENGTH;
+                }
+            } else if (suggestsLen != startPosArray.length || suggestsLen != lengthArray.length) {
+                throw new IllegalArgumentException();
+            } else {
+                mStartPosArray = Arrays.copyOf(startPosArray, suggestsLen);
+                mLengthArray = Arrays.copyOf(lengthArray, suggestsLen);
+            }
         }
         mSuggestionsAttributes = suggestionsAttributes;
         mCookie = cookie;
@@ -96,6 +126,10 @@
         mCookie = source.readInt();
         mSequence = source.readInt();
         mSuggestionsAvailable = source.readInt() == 1;
+        mStartPosArray = new int[mSuggestions.length];
+        mLengthArray = new int[mSuggestions.length];
+        source.readIntArray(mStartPosArray);
+        source.readIntArray(mLengthArray);
     }
 
     /**
@@ -111,6 +145,8 @@
         dest.writeInt(mCookie);
         dest.writeInt(mSequence);
         dest.writeInt(mSuggestionsAvailable ? 1 : 0);
+        dest.writeIntArray(mStartPosArray);
+        dest.writeIntArray(mLengthArray);
     }
 
     /**
@@ -191,4 +227,24 @@
     public int describeContents() {
         return 0;
     }
+
+    /**
+     * @hide
+     */
+    public int getSuggestionStartPosAt(int i) {
+        if (i >= 0 && i < mStartPosArray.length) {
+            return mStartPosArray[i];
+        }
+        return -1;
+    }
+
+    /**
+     * @hide
+     */
+    public int getSuggestionLengthAt(int i) {
+        if (i >= 0 && i < mLengthArray.length) {
+            return mLengthArray[i];
+        }
+        return -1;
+    }
 }
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 69f88a5..fc59e6e 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -157,7 +157,8 @@
         if (subtypeInUse == null) {
             return null;
         }
-        final SpellCheckerSession session = new SpellCheckerSession(sci, sService, listener);
+        final SpellCheckerSession session = new SpellCheckerSession(
+                sci, sService, listener, subtypeInUse);
         try {
             sService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
                     session.getTextServicesSessionListener(),
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index f29aff2..cb555ea 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -261,6 +261,8 @@
         mLayout.addView(getSurfaceView(), layoutParams);
 
         mLayout.setVisibility(View.VISIBLE);
+        mLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
+                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
 
         WebChromeClient client = webView.getWebChromeClient();
         if (client != null) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 3ec7e2f..7bbad78 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -17,6 +17,7 @@
 package android.webkit;
 
 import android.annotation.Widget;
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.content.BroadcastReceiver;
 import android.content.ClipboardManager;
@@ -61,6 +62,7 @@
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.util.Log;
+import android.view.Display;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
 import android.view.HardwareCanvas;
@@ -77,6 +79,7 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.view.ViewTreeObserver;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -739,6 +742,7 @@
     static final int SCREEN_ON                          = 136;
     static final int ENTER_FULLSCREEN_VIDEO             = 137;
     static final int UPDATE_SELECTION                   = 138;
+    static final int UPDATE_ZOOM_DENSITY                = 139;
 
     private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
     private static final int LAST_PACKAGE_MSG_ID = SET_TOUCH_HIGHLIGHT_RECTS;
@@ -794,7 +798,9 @@
         "AUTOFILL_COMPLETE", //              = 134;
         "SELECT_AT", //                      = 135;
         "SCREEN_ON", //                      = 136;
-        "ENTER_FULLSCREEN_VIDEO" //          = 137;
+        "ENTER_FULLSCREEN_VIDEO", //         = 137;
+        "UPDATE_SELECTION", //               = 138;
+        "UPDATE_ZOOM_DENSITY" //             = 139;
     };
 
     // If the site doesn't use the viewport meta tag to specify the viewport,
@@ -8432,6 +8438,11 @@
                     mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
                     break;
                 }
+                case UPDATE_ZOOM_DENSITY: {
+                    final float density = (Float) msg.obj;
+                    mZoomManager.updateDefaultZoomDensity(density);
+                    break;
+                }
                 case REPLACE_BASE_CONTENT: {
                     nativeReplaceBaseContent(msg.arg1);
                     break;
@@ -8446,7 +8457,11 @@
                     // nativeCreate sets mNativeClass to a non-zero value
                     String drawableDir = BrowserFrame.getRawResFilename(
                             BrowserFrame.DRAWABLEDIR, mContext);
-                    nativeCreate(msg.arg1, drawableDir);
+                    WindowManager windowManager =
+                            (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+                    Display display = windowManager.getDefaultDisplay();
+                    nativeCreate(msg.arg1, drawableDir,
+                            ActivityManager.isHighEndGfx(display));
                     if (mDelaySetPicture != null) {
                         setNewPicture(mDelaySetPicture, true);
                         mDelaySetPicture = null;
@@ -9475,7 +9490,7 @@
     private native Rect nativeCacheHitNodeBounds();
     private native int nativeCacheHitNodePointer();
     /* package */ native void nativeClearCursor();
-    private native void     nativeCreate(int ptr, String drawableDir);
+    private native void     nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx);
     private native int      nativeCursorFramePointer();
     private native Rect     nativeCursorNodeBounds();
     private native int nativeCursorNodePointer();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 278bd6e..42b9eab 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2330,7 +2330,8 @@
                     / mViewportDensityDpi;
         }
         if (adjust != mWebView.getDefaultZoomScale()) {
-            mWebView.updateDefaultZoomDensity(adjust);
+            Message.obtain(mWebView.mPrivateHandler,
+                    WebView.UPDATE_ZOOM_DENSITY, adjust).sendToTarget();
         }
         int defaultScale = (int) (adjust * 100);
 
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 2184297..d03db10 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -249,6 +249,12 @@
     }
 
     @Override
+    public void onGetSuggestionsForSentence(SuggestionsInfo[] results) {
+        // TODO: Handle the position and length for each suggestion
+        onGetSuggestions(results);
+    }
+
+    @Override
     public void onGetSuggestions(SuggestionsInfo[] results) {
         Editable editable = (Editable) mTextView.getText();
 
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3e96c81..fec4cbc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -1275,7 +1275,7 @@
             // record changes to the battery level.
             if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
                     (dataSize >= MAX_MAX_HISTORY_BUFFER
-                            || ((mHistoryEnd.states^mHistoryCur.states)
+                            || ((mHistoryLastWritten.states^mHistoryCur.states)
                                     & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                 return;
             }
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
index 3c61968..ba0aa1a 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
@@ -24,6 +24,7 @@
 oneway interface ISpellCheckerSession {
     void onGetSuggestionsMultiple(
             in TextInfo[] textInfos, int suggestionsLimit, boolean multipleWords);
+    void onGetSuggestionsMultipleForSentence(in TextInfo[] textInfos, int suggestionsLimit);
     void onCancel();
     void onClose();
 }
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
index 796b06e..b44dbc8 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
@@ -23,4 +23,5 @@
  */
 oneway interface ISpellCheckerSessionListener {
     void onGetSuggestions(in SuggestionsInfo[] results);
+    void onGetSuggestionsForSentence(in SuggestionsInfo[] results);
 }
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index b7bc366..25b0065 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -29,6 +29,7 @@
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
@@ -392,7 +393,11 @@
             final ActionBar.Tab tab = mTab;
             final View custom = tab.getCustomView();
             if (custom != null) {
-                addView(custom);
+                final ViewParent customParent = custom.getParent();
+                if (customParent != this) {
+                    if (customParent != null) ((ViewGroup) customParent).removeView(custom);
+                    addView(custom);
+                }
                 mCustomView = custom;
                 if (mTextView != null) mTextView.setVisibility(GONE);
                 if (mIconView != null) {
diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp
index e02070d..d78081e 100644
--- a/core/jni/android/graphics/HarfbuzzSkia.cpp
+++ b/core/jni/android/graphics/HarfbuzzSkia.cpp
@@ -47,25 +47,14 @@
 
 namespace android {
 
-static void setupPaintWithFontData(SkPaint* paint, FontData* data) {
-    paint->setTypeface(data->typeFace);
-    paint->setTextSize(data->textSize);
-    paint->setTextSkewX(data->textSkewX);
-    paint->setTextScaleX(data->textScaleX);
-    paint->setFlags(data->flags);
-    paint->setHinting(data->hinting);
-}
-
 static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
         HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
 {
-    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
-    SkPaint paint;
-    setupPaintWithFontData(&paint, data);
+    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+    paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
 
-    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
     uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs);
-    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
+    int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
 
     // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
     // |glyphs| array needs to be converted.
@@ -80,11 +69,8 @@
 static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs,
         HB_Fixed* advances, int flags)
 {
-    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
-    SkPaint paint;
-    setupPaintWithFontData(&paint, data);
-
-    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
     uint16_t* glyphs16 = new uint16_t[numGlyphs];
     if (!glyphs16)
@@ -92,7 +78,7 @@
     for (unsigned i = 0; i < numGlyphs; ++i)
         glyphs16[i] = glyphs[i];
     SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances);
-    paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
+    paint->getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
 
     // The |advances| values which Skia outputs are SkScalars, which are floats
     // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
@@ -108,14 +94,11 @@
 
 static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
 {
-    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
-    SkPaint paint;
-    setupPaintWithFontData(&paint, data);
-
-    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+    paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
 
     uint16_t* glyphs16 = new uint16_t[length];
-    int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
+    int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
 
     bool result = true;
     for (int i = 0; i < numGlyphs; ++i) {
@@ -131,22 +114,20 @@
 static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point,
         HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
 {
-    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
-    SkPaint paint;
-    setupPaintWithFontData(&paint, data);
-
     if (flags & HB_ShaperFlag_UseDesignMetrics)
         // This is requesting pre-hinted positions. We can't support this.
         return HB_Err_Invalid_Argument;
 
-    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
     uint16_t glyph16 = glyph;
     SkPath path;
-    paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
+    paint->getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
     uint32_t numPoints = path.getPoints(0, 0);
     if (point >= numPoints)
         return HB_Err_Invalid_SubTable;
-    SkPoint* points = reinterpret_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
+    SkPoint* points = static_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
     if (!points)
         return HB_Err_Invalid_SubTable;
     // Skia does let us get a single point from the path.
@@ -161,15 +142,13 @@
 
 static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
 {
-    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
-    SkPaint paint;
-    setupPaintWithFontData(&paint, data);
+    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
-    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
     uint16_t glyph16 = glyph;
     SkScalar width;
     SkRect bounds;
-    paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
+    paint->getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
 
     metrics->x = SkScalarToHBFixed(bounds.fLeft);
     metrics->y = SkScalarToHBFixed(bounds.fTop);
@@ -185,12 +164,10 @@
 
 static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
 {
-    FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
-    SkPaint paint;
-    setupPaintWithFontData(&paint, data);
+    SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
 
     SkPaint::FontMetrics skiaMetrics;
-    paint.getFontMetrics(&skiaMetrics);
+    paint->getFontMetrics(&skiaMetrics);
 
     switch (metric) {
     case HB_FontAscent:
@@ -211,10 +188,9 @@
     getFontMetric,
 };
 
-HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
+HB_Error harfbuzzSkiaGetTable(void* font, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
 {
-    FontData* data = reinterpret_cast<FontData*>(voidface);
-    SkTypeface* typeface = data->typeFace;
+    SkTypeface* typeface = static_cast<SkTypeface*>(font);
 
     if (!typeface) {
         LOGD("Typeface cannot be null");
diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfbuzzSkia.h
index 99b389a..2772f4d 100644
--- a/core/jni/android/graphics/HarfbuzzSkia.h
+++ b/core/jni/android/graphics/HarfbuzzSkia.h
@@ -47,15 +47,6 @@
     return SkScalarToFloat(value) * 64.0f;
 }
 
-typedef struct {
-    SkTypeface* typeFace;
-    SkScalar textSize;
-    SkScalar textSkewX;
-    SkScalar textScaleX;
-    uint32_t flags;
-    SkPaint::Hinting hinting;
-} FontData;
-
 HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
 extern const HB_FontClass harfbuzzSkiaClass;
 
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 2ab7da9..883940b 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -484,8 +484,8 @@
 
         jchar* glyphsArray = env->GetCharArrayElements(glyphs, NULL);
 
-        TextLayoutCacheValue value;
-        value.computeValues(paint, text, start, count, contextCount, flags);
+        TextLayoutCacheValue value(contextCount);
+        TextLayoutEngine::getInstance().computeValues(&value, paint, text, start, count, contextCount, flags);
         const jchar* shapedGlyphs = value.getGlyphs();
         size_t glyphsCount = value.getGlyphsCount();
         memcpy(glyphsArray, shapedGlyphs, sizeof(jchar) * glyphsCount);
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 5fed2f3..17492aa 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -34,15 +34,10 @@
 #if USE_TEXT_LAYOUT_CACHE
 
     ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutCache);
-
-    static SkTypeface* gDefaultTypeface = SkFontHost::CreateTypeface(
-            NULL, NULL, NULL, 0, SkTypeface::kNormal);
-
-    static SkTypeface* gArabicTypeface = NULL;
-    static SkTypeface* gHebrewRegularTypeface = NULL;
-    static SkTypeface* gHebrewBoldTypeface = NULL;
+    ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
 
 #endif
+
 //--------------------------------------------------------------------------------------------------
 
 TextLayoutCache::TextLayoutCache() :
@@ -113,10 +108,12 @@
             startTime = systemTime(SYSTEM_TIME_MONOTONIC);
         }
 
-        value = new TextLayoutCacheValue();
+        value = new TextLayoutCacheValue(contextCount);
 
         // Compute advances and store them
-        value->computeValues(paint, text, start, count, contextCount, dirFlags);
+        TextLayoutEngine::getInstance().computeValues(value.get(), paint,
+                reinterpret_cast<const UChar*>(text), start, count,
+                size_t(contextCount), int(dirFlags));
 
         if (mDebugEnabled) {
             value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime);
@@ -193,7 +190,7 @@
                         value->getElapsedTime() * 0.000001f,
                         elapsedTimeThruCacheGet * 0.000001f,
                         deltaPercent,
-                        String8(text, count).string());
+                        String8(text + start, count).string());
             }
             if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) {
                 dumpCacheStats();
@@ -311,8 +308,16 @@
 /**
  * TextLayoutCacheValue
  */
-TextLayoutCacheValue::TextLayoutCacheValue() :
+TextLayoutCacheValue::TextLayoutCacheValue(size_t contextCount) :
         mTotalAdvance(0), mElapsedTime(0) {
+    // Give a hint for advances and glyphs vectors size
+    mAdvances.setCapacity(contextCount);
+    mGlyphs.setCapacity(contextCount);
+}
+
+size_t TextLayoutCacheValue::getSize() const {
+    return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvances.capacity() +
+            sizeof(jchar) * mGlyphs.capacity();
 }
 
 void TextLayoutCacheValue::setElapsedTime(uint32_t time) {
@@ -323,176 +328,51 @@
     return mElapsedTime;
 }
 
-void TextLayoutCacheValue::computeValues(SkPaint* paint, const UChar* chars,
+//HB_ShaperItem TextLayoutEngine::mShaperItem;
+//HB_FontRec TextLayoutEngine::mFontRec;
+//SkPaint TextLayoutEngine::mShapingPaint;
+
+TextLayoutEngine::TextLayoutEngine() : mShaperItemGlyphArraySize(0),
+        mShaperItemLogClustersArraySize(0) {
+    mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal);
+    mArabicTypeface = NULL;
+    mHebrewRegularTypeface = NULL;
+    mHebrewBoldTypeface = NULL;
+
+    mFontRec.klass = &harfbuzzSkiaClass;
+    mFontRec.userData = 0;
+
+    // The values which harfbuzzSkiaClass returns are already scaled to
+    // pixel units, so we just set all these to one to disable further
+    // scaling.
+    mFontRec.x_ppem = 1;
+    mFontRec.y_ppem = 1;
+    mFontRec.x_scale = 1;
+    mFontRec.y_scale = 1;
+
+    memset(&mShaperItem, 0, sizeof(mShaperItem));
+
+    mShaperItem.font = &mFontRec;
+    mShaperItem.font->userData = &mShapingPaint;
+}
+
+TextLayoutEngine::~TextLayoutEngine() {
+    // FIXME should free fonts and caches but since this class is a singleton,
+    // we don't bother at the moment
+}
+
+void TextLayoutEngine::computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars,
         size_t start, size_t count, size_t contextCount, int dirFlags) {
-    // Give a hint for advances, glyphs and log clusters vectors size
-    mAdvances.setCapacity(contextCount);
-    mGlyphs.setCapacity(contextCount);
 
     computeValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
-            &mAdvances, &mTotalAdvance, &mGlyphs);
+            &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs);
 #if DEBUG_ADVANCES
     LOGD("Advances - start=%d, count=%d, contextCount=%d, totalAdvance=%f", start, count,
             contextCount, mTotalAdvance);
 #endif
 }
 
-size_t TextLayoutCacheValue::getSize() const {
-    return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvances.capacity() +
-            sizeof(jchar) * mGlyphs.capacity();
-}
-
-void TextLayoutCacheValue::initShaperItem(HB_ShaperItem& shaperItem, HB_FontRec* font,
-        FontData* fontData, SkPaint* paint, const UChar* chars, size_t count) {
-    font->klass = &harfbuzzSkiaClass;
-    font->userData = 0;
-
-    // The values which harfbuzzSkiaClass returns are already scaled to
-    // pixel units, so we just set all these to one to disable further
-    // scaling.
-    font->x_ppem = 1;
-    font->y_ppem = 1;
-    font->x_scale = 1;
-    font->y_scale = 1;
-
-    // Reset kerning
-    shaperItem.kerning_applied = false;
-
-    // Define font data
-    fontData->textSize = paint->getTextSize();
-    fontData->textSkewX = paint->getTextSkewX();
-    fontData->textScaleX = paint->getTextScaleX();
-    fontData->flags = paint->getFlags();
-    fontData->hinting = paint->getHinting();
-
-    shaperItem.font = font;
-    shaperItem.font->userData = fontData;
-
-    // We cannot know, ahead of time, how many glyphs a given script run
-    // will produce. We take a guess that script runs will not produce more
-    // than twice as many glyphs as there are code points plus a bit of
-    // padding and fallback if we find that we are wrong.
-    createGlyphArrays(shaperItem, (count + 2) * 2);
-
-    // Create log clusters array
-    shaperItem.log_clusters = new unsigned short[count];
-
-    // Set the string properties
-    shaperItem.string = chars;
-    shaperItem.stringLength = count;
-}
-
-void TextLayoutCacheValue::freeShaperItem(HB_ShaperItem& shaperItem) {
-    deleteGlyphArrays(shaperItem);
-    delete[] shaperItem.log_clusters;
-    HB_FreeFace(shaperItem.face);
-}
-
-unsigned TextLayoutCacheValue::shapeFontRun(HB_ShaperItem& shaperItem, SkPaint* paint,
-        size_t count, bool isRTL) {
-    // Update Harfbuzz Shaper
-    shaperItem.item.pos = 0;
-    shaperItem.item.length = count;
-    shaperItem.item.bidiLevel = isRTL;
-
-    // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
-    // This is needed as the Typeface used for shaping can be not the default one
-    // when we are shapping any script that needs to use a fallback Font.
-    // If we are a "common" script we dont need to shift
-    unsigned result = 0;
-    switch(shaperItem.item.script) {
-        case HB_Script_Arabic:
-        case HB_Script_Hebrew: {
-            const uint16_t* text16 = (const uint16_t*)shaperItem.string;
-            SkUnichar firstUnichar = SkUTF16_NextUnichar(&text16);
-            result = paint->getBaseGlyphCount(firstUnichar);
-            break;
-        }
-        default:
-            break;
-    }
-
-    // Set the correct Typeface depending on the script
-    FontData* data = reinterpret_cast<FontData*>(shaperItem.font->userData);
-    switch(shaperItem.item.script) {
-        case HB_Script_Arabic:
-            data->typeFace = getCachedTypeface(&gArabicTypeface, TYPEFACE_ARABIC);
-#if DEBUG_GLYPHS
-            LOGD("Using Arabic Typeface");
-#endif
-            break;
-
-        case HB_Script_Hebrew:
-            if(paint->getTypeface()) {
-                switch(paint->getTypeface()->style()) {
-                    case SkTypeface::kNormal:
-                    case SkTypeface::kItalic:
-                    default:
-                        data->typeFace = getCachedTypeface(&gHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
-#if DEBUG_GLYPHS
-                        LOGD("Using Hebrew Regular/Italic Typeface");
-#endif
-                        break;
-                    case SkTypeface::kBold:
-                    case SkTypeface::kBoldItalic:
-                        data->typeFace = getCachedTypeface(&gHebrewBoldTypeface, TYPE_FACE_HEBREW_BOLD);
-#if DEBUG_GLYPHS
-                        LOGD("Using Hebrew Bold/BoldItalic Typeface");
-#endif
-                        break;
-                }
-            } else {
-                data->typeFace = getCachedTypeface(&gHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
-#if DEBUG_GLYPHS
-                        LOGD("Using Hebrew Regular Typeface");
-#endif
-            }
-            break;
-
-        default:
-            if(paint->getTypeface()) {
-                data->typeFace = paint->getTypeface();
-#if DEBUG_GLYPHS
-            LOGD("Using Paint Typeface");
-#endif
-            } else {
-                data->typeFace = gDefaultTypeface;
-#if DEBUG_GLYPHS
-            LOGD("Using Default Typeface");
-#endif
-            }
-            break;
-    }
-
-    shaperItem.face = HB_NewFace(data, harfbuzzSkiaGetTable);
-
-#if DEBUG_GLYPHS
-    LOGD("Run typeFace = %p", data->typeFace);
-    LOGD("Run typeFace->uniqueID = %d", data->typeFace->uniqueID());
-#endif
-
-    // Shape
-    while (!HB_ShapeItem(&shaperItem)) {
-        // We overflowed our arrays. Resize and retry.
-        // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
-        deleteGlyphArrays(shaperItem);
-        createGlyphArrays(shaperItem, shaperItem.num_glyphs << 1);
-    }
-
-    return result;
-}
-
-SkTypeface* TextLayoutCacheValue::getCachedTypeface(SkTypeface** typeface, const char path[]) {
-    if (!*typeface) {
-        *typeface = SkTypeface::CreateFromFile(path);
-#if DEBUG_GLYPHS
-        LOGD("Created SkTypeface from file: %s", path);
-#endif
-    }
-    return *typeface;
-}
-
-void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+void TextLayoutEngine::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
         size_t start, size_t count, size_t contextCount, int dirFlags,
         Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
         Vector<jchar>* const outGlyphs) {
@@ -510,13 +390,6 @@
             case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
         }
 
-        HB_ShaperItem shaperItem;
-        HB_FontRec font;
-        FontData fontData;
-
-        // Initialize Harfbuzz Shaper
-        initShaperItem(shaperItem, &font, &fontData, paint, chars, contextCount);
-
         bool useSingleRun = false;
         bool isRTL = forceRTL;
         if (forceLTR || forceRTL) {
@@ -627,45 +500,43 @@
     }
 }
 
-void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+void TextLayoutEngine::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
         size_t count, bool isRTL,
         Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
         Vector<jchar>* const outGlyphs) {
 
-    unsigned glyphBaseCount = 0;
-
     *outTotalAdvance = 0;
     jfloat totalAdvance = 0;
 
-    unsigned numCodePoints = 0;
+    // Set the string properties
+    mShaperItem.string = chars;
+    mShaperItem.stringLength = count;
 
-    ssize_t startFontRun = 0;
-    ssize_t endFontRun = 0;
+    // Define shaping paint properties
+    mShapingPaint.setTextSize(paint->getTextSize());
+    mShapingPaint.setTextSkewX(paint->getTextSkewX());
+    mShapingPaint.setTextScaleX(paint->getTextScaleX());
+    mShapingPaint.setFlags(paint->getFlags());
+    mShapingPaint.setHinting(paint->getHinting());
+
+    // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
+    // into the shaperItem
     ssize_t indexFontRun = isRTL ? count - 1 : 0;
-    size_t countFontRun = 0;
-
-    HB_ShaperItem shaperItem;
-    HB_FontRec font;
-    FontData fontData;
-
-    // Zero the Shaper struct
-    memset(&shaperItem, 0, sizeof(shaperItem));
-
-    // Split the BiDi run into Script runs. Harfbuzz will populate the script into the shaperItem
-    while((isRTL) ?
-            hb_utf16_script_run_prev(&numCodePoints, &shaperItem.item, chars,
+    unsigned numCodePoints = 0;
+    while ((isRTL) ?
+            hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, chars,
                     count, &indexFontRun):
-            hb_utf16_script_run_next(&numCodePoints, &shaperItem.item, chars,
+            hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, chars,
                     count, &indexFontRun)) {
 
-        startFontRun = shaperItem.item.pos;
-        countFontRun = shaperItem.item.length;
-        endFontRun = startFontRun + countFontRun;
+        ssize_t startFontRun = mShaperItem.item.pos;
+        size_t countFontRun = mShaperItem.item.length;
+        ssize_t endFontRun = startFontRun + countFontRun;
 
 #if DEBUG_GLYPHS
-        LOGD("Shaped Font Run with");
+        LOGD("Shaping Font Run with");
         LOGD("         -- isRTL=%d", isRTL);
-        LOGD("         -- HB script=%d", shaperItem.item.script);
+        LOGD("         -- HB script=%d", mShaperItem.item.script);
         LOGD("         -- startFontRun=%d", startFontRun);
         LOGD("         -- endFontRun=%d", endFontRun);
         LOGD("         -- countFontRun=%d", countFontRun);
@@ -673,19 +544,17 @@
         LOGD("         -- string='%s'", String8(chars, count).string());
 #endif
 
-        // Initialize Harfbuzz Shaper
-        initShaperItem(shaperItem, &font, &fontData, paint, chars + startFontRun, countFontRun);
-
-        // Shape the Font run and get the base glyph count for offsetting the glyphIDs later on
-        glyphBaseCount = shapeFontRun(shaperItem, paint, countFontRun, isRTL);
+        // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
+        // and shape the Font run
+        size_t glyphBaseCount = shapeFontRun(paint, isRTL);
 
 #if DEBUG_GLYPHS
-        LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs,
-                shaperItem.kerning_applied);
+        LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", mShaperItem.num_glyphs,
+                mShaperItem.kerning_applied);
         LOGD("         -- isDevKernText=%d", paint->isDevKernText());
         LOGD("         -- glyphBaseCount=%d", glyphBaseCount);
 
-        logGlyphs(shaperItem);
+        logGlyphs(mShaperItem);
 #endif
         if (isRTL) {
             endFontRun = startFontRun;
@@ -699,7 +568,7 @@
 #endif
         }
 
-        if (shaperItem.advances == NULL || shaperItem.num_glyphs == 0) {
+        if (mShaperItem.advances == NULL || mShaperItem.num_glyphs == 0) {
 #if DEBUG_GLYPHS
             LOGD("HARFBUZZ -- advances array is empty or num_glypth = 0");
 #endif
@@ -708,16 +577,16 @@
         }
 
         // Get Advances and their total
-        jfloat currentAdvance = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[0]]);
+        jfloat currentAdvance = HBFixedToFloat(mShaperItem.advances[mShaperItem.log_clusters[0]]);
         jfloat totalFontRunAdvance = currentAdvance;
         outAdvances->add(currentAdvance);
         for (size_t i = 1; i < countFontRun; i++) {
-            size_t clusterPrevious = shaperItem.log_clusters[i - 1];
-            size_t cluster = shaperItem.log_clusters[i];
+            size_t clusterPrevious = mShaperItem.log_clusters[i - 1];
+            size_t cluster = mShaperItem.log_clusters[i];
             if (cluster == clusterPrevious) {
                 outAdvances->add(0);
             } else {
-                currentAdvance = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[i]]);
+                currentAdvance = HBFixedToFloat(mShaperItem.advances[mShaperItem.log_clusters[i]]);
                 totalFontRunAdvance += currentAdvance;
                 outAdvances->add(currentAdvance);
             }
@@ -733,38 +602,188 @@
 
         // Get Glyphs and reverse them in place if RTL
         if (outGlyphs) {
-            size_t countGlyphs = shaperItem.num_glyphs;
+            size_t countGlyphs = mShaperItem.num_glyphs;
             for (size_t i = 0; i < countGlyphs; i++) {
                 jchar glyph = glyphBaseCount +
-                        (jchar) shaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
+                        (jchar) mShaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
 #if DEBUG_GLYPHS
                 LOGD("HARFBUZZ  -- glyph[%d]=%d", i, glyph);
 #endif
                 outGlyphs->add(glyph);
             }
         }
-        // Cleaning
-        freeShaperItem(shaperItem);
     }
     *outTotalAdvance = totalAdvance;
 }
 
-void TextLayoutCacheValue::deleteGlyphArrays(HB_ShaperItem& shaperItem) {
-    delete[] shaperItem.glyphs;
-    delete[] shaperItem.attributes;
-    delete[] shaperItem.advances;
-    delete[] shaperItem.offsets;
+
+size_t TextLayoutEngine::shapeFontRun(SkPaint* paint, bool isRTL) {
+    // Reset kerning
+    mShaperItem.kerning_applied = false;
+
+    // Update Harfbuzz Shaper
+    mShaperItem.item.bidiLevel = isRTL;
+
+    SkTypeface* typeface = paint->getTypeface();
+
+    // Set the correct Typeface depending on the script
+    switch (mShaperItem.item.script) {
+    case HB_Script_Arabic:
+        typeface = getCachedTypeface(&mArabicTypeface, TYPEFACE_ARABIC);
+#if DEBUG_GLYPHS
+        LOGD("Using Arabic Typeface");
+#endif
+        break;
+
+    case HB_Script_Hebrew:
+        if (typeface) {
+            switch (typeface->style()) {
+            case SkTypeface::kBold:
+            case SkTypeface::kBoldItalic:
+                typeface = getCachedTypeface(&mHebrewBoldTypeface, TYPE_FACE_HEBREW_BOLD);
+#if DEBUG_GLYPHS
+                LOGD("Using Hebrew Bold/BoldItalic Typeface");
+#endif
+                break;
+
+            case SkTypeface::kNormal:
+            case SkTypeface::kItalic:
+            default:
+                typeface = getCachedTypeface(&mHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
+#if DEBUG_GLYPHS
+                LOGD("Using Hebrew Regular/Italic Typeface");
+#endif
+                break;
+            }
+        } else {
+            typeface = getCachedTypeface(&mHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
+#if DEBUG_GLYPHS
+            LOGD("Using Hebrew Regular Typeface");
+#endif
+        }
+        break;
+
+    default:
+        if (!typeface) {
+            typeface = mDefaultTypeface;
+#if DEBUG_GLYPHS
+            LOGD("Using Default Typeface");
+#endif
+        } else {
+#if DEBUG_GLYPHS
+            LOGD("Using Paint Typeface");
+#endif
+        }
+        break;
+    }
+
+    mShapingPaint.setTypeface(typeface);
+    mShaperItem.face = getCachedHBFace(typeface);
+
+#if DEBUG_GLYPHS
+    LOGD("Run typeFace = %p, uniqueID = %d, hb_face = %p",
+            typeface, typeface->uniqueID(), mShaperItem.face);
+#endif
+
+    // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
+    // This is needed as the Typeface used for shaping can be not the default one
+    // when we are shaping any script that needs to use a fallback Font.
+    // If we are a "common" script we dont need to shift
+    size_t baseGlyphCount = 0;
+    switch (mShaperItem.item.script) {
+    case HB_Script_Arabic:
+    case HB_Script_Hebrew: {
+        const uint16_t* text16 = (const uint16_t*)mShaperItem.string;
+        SkUnichar firstUnichar = SkUTF16_NextUnichar(&text16);
+        baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
+        break;
+    }
+    default:
+        break;
+    }
+
+    // Shape
+    ensureShaperItemLogClustersArray(mShaperItem.item.length);
+    ensureShaperItemGlyphArrays(mShaperItem.item.length * 3 / 2);
+    mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
+    while (!HB_ShapeItem(&mShaperItem)) {
+        // We overflowed our glyph arrays. Resize and retry.
+        // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
+        ensureShaperItemGlyphArrays(mShaperItem.num_glyphs * 2);
+        mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
+    }
+    return baseGlyphCount;
 }
 
-void TextLayoutCacheValue::createGlyphArrays(HB_ShaperItem& shaperItem, int size) {
+void TextLayoutEngine::ensureShaperItemGlyphArrays(size_t size) {
+    if (size > mShaperItemGlyphArraySize) {
+        deleteShaperItemGlyphArrays();
+        createShaperItemGlyphArrays(size);
+    }
+}
+
+void TextLayoutEngine::createShaperItemGlyphArrays(size_t size) {
 #if DEBUG_GLYPHS
     LOGD("createGlyphArrays  -- size=%d", size);
 #endif
-    shaperItem.glyphs = new HB_Glyph[size];
-    shaperItem.attributes = new HB_GlyphAttributes[size];
-    shaperItem.advances = new HB_Fixed[size];
-    shaperItem.offsets = new HB_FixedPoint[size];
-    shaperItem.num_glyphs = size;
+    mShaperItemGlyphArraySize = size;
+    mShaperItem.glyphs = new HB_Glyph[size];
+    mShaperItem.attributes = new HB_GlyphAttributes[size];
+    mShaperItem.advances = new HB_Fixed[size];
+    mShaperItem.offsets = new HB_FixedPoint[size];
+}
+
+void TextLayoutEngine::deleteShaperItemGlyphArrays() {
+    delete[] mShaperItem.glyphs;
+    delete[] mShaperItem.attributes;
+    delete[] mShaperItem.advances;
+    delete[] mShaperItem.offsets;
+}
+
+void TextLayoutEngine::ensureShaperItemLogClustersArray(size_t size) {
+    if (size > mShaperItemLogClustersArraySize) {
+        deleteShaperItemLogClustersArray();
+        createShaperItemLogClustersArray(size);
+    }
+}
+
+void TextLayoutEngine::createShaperItemLogClustersArray(size_t size) {
+#if DEBUG_GLYPHS
+    LOGD("createLogClustersArray  -- size=%d", size);
+#endif
+    mShaperItemLogClustersArraySize = size;
+    mShaperItem.log_clusters = new unsigned short[size];
+}
+
+void TextLayoutEngine::deleteShaperItemLogClustersArray() {
+    delete[] mShaperItem.log_clusters;
+}
+
+SkTypeface* TextLayoutEngine::getCachedTypeface(SkTypeface** typeface, const char path[]) {
+    if (!*typeface) {
+        *typeface = SkTypeface::CreateFromFile(path);
+        (*typeface)->ref();
+#if DEBUG_GLYPHS
+        LOGD("Created SkTypeface from file: %s", path);
+#endif
+    }
+    return *typeface;
+}
+
+HB_Face TextLayoutEngine::getCachedHBFace(SkTypeface* typeface) {
+    SkFontID fontId = typeface->uniqueID();
+    ssize_t index = mCachedHBFaces.indexOfKey(fontId);
+    if (index >= 0) {
+        return mCachedHBFaces.valueAt(index);
+    }
+    HB_Face face = HB_NewFace(typeface, harfbuzzSkiaGetTable);
+    if (face) {
+#if DEBUG_GLYPHS
+        LOGD("Created HB_NewFace %p from paint typeface: %p", face, typeface);
+#endif
+        mCachedHBFaces.add(fontId, face);
+    }
+    return face;
 }
 
 } // namespace android
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 5c938f7..dfdcd03 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -23,6 +23,7 @@
 #include <utils/threads.h>
 #include <utils/String16.h>
 #include <utils/GenerationCache.h>
+#include <utils/KeyedVector.h>
 #include <utils/Compare.h>
 #include <utils/RefBase.h>
 #include <utils/Singleton.h>
@@ -116,14 +117,11 @@
  */
 class TextLayoutCacheValue : public RefBase {
 public:
-    TextLayoutCacheValue();
+    TextLayoutCacheValue(size_t contextCount);
 
     void setElapsedTime(uint32_t time);
     uint32_t getElapsedTime();
 
-    void computeValues(SkPaint* paint, const UChar* chars, size_t start, size_t count,
-            size_t contextCount, int dirFlags);
-
     inline const jfloat* getAdvances() const { return mAdvances.array(); }
     inline size_t getAdvancesCount() const { return mAdvances.size(); }
     inline jfloat getTotalAdvance() const { return mTotalAdvance; }
@@ -131,12 +129,6 @@
     inline size_t getGlyphsCount() const { return mGlyphs.size(); }
 
     /**
-     * Get the size of the Cache entry
-     */
-    size_t getSize() const;
-
-private:
-    /**
      * Advances vector
      */
     Vector<jfloat> mAdvances;
@@ -152,34 +144,16 @@
     Vector<jchar> mGlyphs;
 
     /**
+     * Get the size of the Cache entry
+     */
+    size_t getSize() const;
+
+private:
+    /**
      * Time for computing the values (in milliseconds)
      */
     uint32_t mElapsedTime;
 
-    static void computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
-            size_t start, size_t count, size_t contextCount, int dirFlags,
-            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
-            Vector<jchar>* const outGlyphs);
-
-    static void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
-            size_t count, bool isRTL,
-            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
-            Vector<jchar>* const outGlyphs);
-
-    static void initShaperItem(HB_ShaperItem& shaperItem, HB_FontRec* font, FontData* fontData,
-            SkPaint* paint, const UChar* chars, size_t count);
-
-    static void freeShaperItem(HB_ShaperItem& shaperItem);
-
-    static unsigned shapeFontRun(HB_ShaperItem& shaperItem, SkPaint* paint,
-            size_t count, bool isRTL);
-
-    static SkTypeface* getCachedTypeface(SkTypeface** typeface, const char path[]);
-
-    static void deleteGlyphArrays(HB_ShaperItem& shaperItem);
-
-    static void createGlyphArrays(HB_ShaperItem& shaperItem, int size);
-
 }; // TextLayoutCacheValue
 
 /**
@@ -240,6 +214,72 @@
 
 }; // TextLayoutCache
 
+/**
+ * The TextLayoutEngine is responsible for shaping with Harfbuzz library
+ */
+class TextLayoutEngine : public Singleton<TextLayoutEngine> {
+public:
+    TextLayoutEngine();
+    virtual ~TextLayoutEngine();
+
+    void computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars,
+            size_t start, size_t count, size_t contextCount, int dirFlags);
+
+private:
+    /**
+     * Harfbuzz shaper item
+     */
+    HB_ShaperItem mShaperItem;
+
+    /**
+     * Harfbuzz font
+     */
+    HB_FontRec mFontRec;
+
+    /**
+     * Skia Paint used for shaping
+     */
+    SkPaint mShapingPaint;
+
+    /**
+     * Skia typefaces cached for shaping
+     */
+    SkTypeface* mDefaultTypeface;
+    SkTypeface* mArabicTypeface;
+    SkTypeface* mHebrewRegularTypeface;
+    SkTypeface* mHebrewBoldTypeface;
+
+    KeyedVector<SkFontID, HB_Face> mCachedHBFaces;
+
+    size_t mShaperItemGlyphArraySize;
+    size_t mShaperItemLogClustersArraySize;
+
+    size_t shapeFontRun(SkPaint* paint, bool isRTL);
+
+    void computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+            size_t start, size_t count, size_t contextCount, int dirFlags,
+            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
+            Vector<jchar>* const outGlyphs);
+
+    void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+            size_t count, bool isRTL,
+            Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
+            Vector<jchar>* const outGlyphs);
+
+    SkTypeface* getCachedTypeface(SkTypeface** typeface, const char path[]);
+    HB_Face getCachedHBFace(SkTypeface* typeface);
+
+    void ensureShaperItemGlyphArrays(size_t size);
+    void createShaperItemGlyphArrays(size_t size);
+    void deleteShaperItemGlyphArrays();
+
+    void ensureShaperItemLogClustersArray(size_t size);
+    void createShaperItemLogClustersArray(size_t size);
+    void deleteShaperItemLogClustersArray();
+
+}; // TextLayoutEngine
+
+
 } // namespace android
 #endif /* ANDROID_TEXT_LAYOUT_CACHE_H */
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 25f7d25..326f186 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -314,9 +314,12 @@
     <integer name="config_carDockKeepsScreenOn">1</integer>
 
     <!-- Control whether being in the desk dock should enable accelerometer
-         based screen orientation.  Note this should probably default to true
-         like car dock, but we haven't had a chance to test it. -->
-    <bool name="config_deskDockEnablesAccelerometer">false</bool>
+         based screen orientation.  This defaults to true because it is
+         common for desk docks to be sold in a variety of form factors
+         with different orientations.  Since we cannot always tell these docks
+         apart and the docks cannot report their true orientation on their own,
+         we rely on gravity to determine the effective orientation. -->
+    <bool name="config_deskDockEnablesAccelerometer">true</bool>
 
     <!-- Control whether being in the car dock should enable accelerometer based
          screen orientation.  This defaults to true because putting a device in
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 27d863d..15c2bab 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -202,6 +202,10 @@
     // getCurrentScalingMode returns the scaling mode of the current buffer
     uint32_t getCurrentScalingMode() const;
 
+    // isSynchronousMode returns whether the SurfaceTexture is currently in
+    // synchronous mode.
+    bool isSynchronousMode() const;
+
     // abandon frees all the buffers and puts the SurfaceTexture into the
     // 'abandoned' state.  Once put in this state the SurfaceTexture can never
     // leave it.  When in the 'abandoned' state, all methods of the
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index 8c1c593..446720b 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -153,6 +153,9 @@
     bool mStarted;
     int32_t mNumFramesEncoded;
 
+    // Time between capture of two frames.
+    int64_t mTimeBetweenFrameCaptureUs;
+
     CameraSource(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
                  int32_t cameraId,
                  Size videoSize, int32_t frameRate,
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h
index 0e264c7..b060691 100644
--- a/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/include/media/stagefright/CameraSourceTimeLapse.h
@@ -57,10 +57,6 @@
     int32_t mVideoWidth;
     int32_t mVideoHeight;
 
-    // Time between capture of two frames during time lapse recording
-    // Negative value indicates that timelapse is disabled.
-    int64_t mTimeBetweenTimeLapseFrameCaptureUs;
-
     // Time between two frames in final video (1/frameRate)
     int64_t mTimeBetweenTimeLapseVideoFramesUs;
 
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index c21d19d..84f8282 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -336,6 +336,10 @@
 
     int64_t retrieveDecodingTimeUs(bool isCodecSpecific);
 
+    status_t parseAVCCodecSpecificData(
+            const void *data, size_t size,
+            unsigned *profile, unsigned *level);
+
     OMXCodec(const OMXCodec &);
     OMXCodec &operator=(const OMXCodec &);
 };
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index 2d716c7..b7e3ee3 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -32,6 +32,10 @@
 
 LOCAL_MODULE:= libgui
 
+ifeq ($(TARGET_BOARD_PLATFORM), tegra)
+	LOCAL_CFLAGS += -DALLOW_DEQUEUE_CURRENT_BUFFER
+endif
+
 include $(BUILD_SHARED_LIBRARY)
 
 ifeq (,$(ONE_SHOT_MAKEFILE))
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 175de69..b4d01a9 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -36,8 +36,12 @@
 #include <utils/Log.h>
 #include <utils/String8.h>
 
-
-#define ALLOW_DEQUEUE_CURRENT_BUFFER    false
+#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
+#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    true
+#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
+#else
+#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER    false
+#endif
 
 // Macros for including the SurfaceTexture name in log messages
 #define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
@@ -325,7 +329,7 @@
             LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
                     "dequeueBuffer: buffer %d is both FREE and current!", i);
 
-            if (ALLOW_DEQUEUE_CURRENT_BUFFER) {
+            if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
                 if (state == BufferSlot::FREE || i == mCurrentTexture) {
                     foundSync = i;
                     if (i != mCurrentTexture) {
@@ -642,8 +646,9 @@
     Mutex::Autolock lock(mMutex);
 
     if (mAbandoned) {
-        ST_LOGE("disconnect: SurfaceTexture has been abandoned!");
-        return NO_INIT;
+        // it is not really an error to disconnect after the surface
+        // has been abandoned, it should just be a no-op.
+        return NO_ERROR;
     }
 
     int err = NO_ERROR;
@@ -1005,6 +1010,11 @@
     return mCurrentScalingMode;
 }
 
+bool SurfaceTexture::isSynchronousMode() const {
+    Mutex::Autolock lock(mMutex);
+    return mSynchronousMode;
+}
+
 int SurfaceTexture::query(int what, int* outValue)
 {
     Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 4268782..c79e69a 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -396,7 +396,8 @@
             1.0f, 1.0f,
         };
 
-        glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices);
+        glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0,
+                triangleVertices);
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
         glEnableVertexAttribArray(mPositionHandle);
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
@@ -410,13 +411,17 @@
         // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as
         // they're setting the defautls for that target, but when hacking things
         // to use GL_TEXTURE_2D they are needed to achieve the same behavior.
-        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
+                GL_LINEAR);
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
-        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER,
+                GL_LINEAR);
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
-        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
+                GL_CLAMP_TO_EDGE);
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
-        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
+                GL_CLAMP_TO_EDGE);
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
 
         GLfloat texMatrix[16];
@@ -640,8 +645,8 @@
 
     for (int i = 0; i < 5; i++) {
         const android_native_rect_t& crop(crops[i]);
-        SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left,
-                crop.top, crop.right, crop.bottom).string());
+        SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }",
+                crop.left, crop.top, crop.right, crop.bottom).string());
 
         ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop));
 
@@ -650,13 +655,15 @@
         ASSERT_TRUE(anb != NULL);
 
         sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
-        ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+        ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(),
+                buf->getNativeBuffer()));
 
         uint8_t* img = NULL;
         buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
         fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop);
         buf->unlock();
-        ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
+        ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(),
+                buf->getNativeBuffer()));
 
         mST->updateTexImage();
 
@@ -708,7 +715,8 @@
 
     class ProducerThread : public Thread {
     public:
-        ProducerThread(const sp<ANativeWindow>& anw, const TestPixel* testPixels):
+        ProducerThread(const sp<ANativeWindow>& anw,
+                const TestPixel* testPixels):
                 mANW(anw),
                 mTestPixels(testPixels) {
         }
@@ -940,82 +948,6 @@
     EXPECT_TRUE(checkPixel( 3, 52,  35, 231,  35,  35));
 }
 
-TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) {
-    const int texWidth = 64;
-    const int texHeight = 64;
-
-    mST->setDefaultBufferSize(texWidth, texHeight);
-
-    // Do the producer side of things
-    EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
-            mANW.get(), NULL);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);
-
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
-            mEglContext));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    glClearColor(0.6, 0.6, 0.6, 0.6);
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    glEnable(GL_SCISSOR_TEST);
-    glScissor(4, 4, 4, 4);
-    glClearColor(1.0, 0.0, 0.0, 1.0);
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    glScissor(24, 48, 4, 4);
-    glClearColor(0.0, 1.0, 0.0, 1.0);
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    glScissor(37, 17, 4, 4);
-    glClearColor(0.0, 0.0, 1.0, 1.0);
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    eglSwapBuffers(mEglDisplay, stcEglSurface);
-
-    // Do the consumer side of things
-    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
-            mEglContext));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    glDisable(GL_SCISSOR_TEST);
-
-    mST->updateTexImage();
-
-    // We must wait until updateTexImage has been called to destroy the
-    // EGLSurface because we're in synchronous mode.
-    eglDestroySurface(mEglDisplay, stcEglSurface);
-
-    glClearColor(0.2, 0.2, 0.2, 0.2);
-    glClear(GL_COLOR_BUFFER_BIT);
-
-    glViewport(0, 0, texWidth, texHeight);
-    drawTexture();
-
-    EXPECT_TRUE(checkPixel( 0,  0, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(63,  0, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153));
-
-    EXPECT_TRUE(checkPixel( 4,  7, 255,   0,   0, 255));
-    EXPECT_TRUE(checkPixel(25, 51,   0, 255,   0, 255));
-    EXPECT_TRUE(checkPixel(40, 19,   0,   0, 255, 255));
-    EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(13,  8, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(46,  3, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153));
-    EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
-}
-
 TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
     class ProducerThread : public Thread {
     public:
@@ -1093,13 +1025,284 @@
             reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError());
 }
 
+TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) {
+    int texHeight = 16;
+    ANativeWindowBuffer* anb;
+
+    GLint maxTextureSize;
+    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+
+    // make sure it works with small textures
+    mST->setDefaultBufferSize(16, texHeight);
+    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+    EXPECT_EQ(16, anb->width);
+    EXPECT_EQ(texHeight, anb->height);
+    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
+    EXPECT_EQ(NO_ERROR, mST->updateTexImage());
+
+    // make sure it works with GL_MAX_TEXTURE_SIZE
+    mST->setDefaultBufferSize(maxTextureSize, texHeight);
+    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+    EXPECT_EQ(maxTextureSize, anb->width);
+    EXPECT_EQ(texHeight, anb->height);
+    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
+    EXPECT_EQ(NO_ERROR, mST->updateTexImage());
+
+    // make sure it fails with GL_MAX_TEXTURE_SIZE+1
+    mST->setDefaultBufferSize(maxTextureSize+1, texHeight);
+    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+    EXPECT_EQ(maxTextureSize+1, anb->width);
+    EXPECT_EQ(texHeight, anb->height);
+    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
+    ASSERT_NE(NO_ERROR, mST->updateTexImage());
+}
+
 /*
- * This test is for testing GL -> GL texture streaming via SurfaceTexture.  It
- * contains functionality to create a producer thread that will perform GL
- * rendering to an ANativeWindow that feeds frames to a SurfaceTexture.
- * Additionally it supports interlocking the producer and consumer threads so
- * that a specific sequence of calls can be deterministically created by the
- * test.
+ * This test fixture is for testing GL -> GL texture streaming.  It creates an
+ * EGLSurface and an EGLContext for the image producer to use.
+ */
+class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
+protected:
+    SurfaceTextureGLToGLTest():
+            mProducerEglSurface(EGL_NO_SURFACE),
+            mProducerEglContext(EGL_NO_CONTEXT) {
+    }
+
+    virtual void SetUp() {
+        SurfaceTextureGLTest::SetUp();
+
+        EGLConfig myConfig = {0};
+        EGLint numConfigs = 0;
+        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
+                1, &numConfigs));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
+                mANW.get(), NULL);
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
+
+        mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
+                EGL_NO_CONTEXT, getContextAttribs());
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
+    }
+
+    virtual void TearDown() {
+        if (mProducerEglContext != EGL_NO_CONTEXT) {
+            eglDestroyContext(mEglDisplay, mProducerEglContext);
+        }
+        if (mProducerEglSurface != EGL_NO_SURFACE) {
+            eglDestroySurface(mEglDisplay, mProducerEglSurface);
+        }
+        SurfaceTextureGLTest::TearDown();
+    }
+
+    EGLSurface mProducerEglSurface;
+    EGLContext mProducerEglContext;
+};
+
+TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) {
+    const int texWidth = 64;
+    const int texHeight = 64;
+
+    mST->setDefaultBufferSize(texWidth, texHeight);
+
+    // Do the producer side of things
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+            mProducerEglSurface, mProducerEglContext));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    // This is needed to ensure we pick up a buffer of the correct size.
+    eglSwapBuffers(mEglDisplay, mProducerEglSurface);
+
+    glClearColor(0.6, 0.6, 0.6, 0.6);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glEnable(GL_SCISSOR_TEST);
+    glScissor(4, 4, 4, 4);
+    glClearColor(1.0, 0.0, 0.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glScissor(24, 48, 4, 4);
+    glClearColor(0.0, 1.0, 0.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glScissor(37, 17, 4, 4);
+    glClearColor(0.0, 0.0, 1.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    eglSwapBuffers(mEglDisplay, mProducerEglSurface);
+
+    // Do the consumer side of things
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+            mEglContext));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    glDisable(GL_SCISSOR_TEST);
+
+    mST->updateTexImage(); // Skip the first frame, which was empty
+    mST->updateTexImage();
+
+    glClearColor(0.2, 0.2, 0.2, 0.2);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glViewport(0, 0, texWidth, texHeight);
+    drawTexture();
+
+    EXPECT_TRUE(checkPixel( 0,  0, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(63,  0, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153));
+
+    EXPECT_TRUE(checkPixel( 4,  7, 255,   0,   0, 255));
+    EXPECT_TRUE(checkPixel(25, 51,   0, 255,   0, 255));
+    EXPECT_TRUE(checkPixel(40, 19,   0,   0, 255, 255));
+    EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(13,  8, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(46,  3, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153));
+    EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
+}
+
+TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) {
+    sp<GraphicBuffer> buffers[3];
+
+    // This test requires async mode to run on a single thread.
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+            mProducerEglSurface, mProducerEglContext));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    for (int i = 0; i < 3; i++) {
+        // Produce a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+                mProducerEglSurface, mProducerEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        glClear(GL_COLOR_BUFFER_BIT);
+        eglSwapBuffers(mEglDisplay, mProducerEglSurface);
+
+        // Consume a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        mST->updateTexImage();
+        buffers[i] = mST->getCurrentBuffer();
+    }
+
+    // Destroy the GL texture object to release its ref on buffers[2].
+    GLuint texID = TEX_ID;
+    glDeleteTextures(1, &texID);
+
+    // Destroy the EGLSurface
+    EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    // Release the ref that the SurfaceTexture has on buffers[2].
+    mST->abandon();
+
+    EXPECT_EQ(1, buffers[0]->getStrongCount());
+    EXPECT_EQ(1, buffers[1]->getStrongCount());
+
+    // Depending on how lazily the GL driver dequeues buffers, we may end up
+    // with either two or three total buffers.  If there are three, make sure
+    // the last one was properly down-ref'd.
+    if (buffers[2] != buffers[0]) {
+        EXPECT_EQ(1, buffers[2]->getStrongCount());
+    }
+}
+
+TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
+    sp<GraphicBuffer> buffers[3];
+
+    // This test requires async mode to run on a single thread.
+    EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+            mProducerEglSurface, mProducerEglContext));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+    EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    for (int i = 0; i < 3; i++) {
+        // Produce a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+                mProducerEglSurface, mProducerEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        glClear(GL_COLOR_BUFFER_BIT);
+        EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        // Consume a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+        buffers[i] = mST->getCurrentBuffer();
+    }
+
+    // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has
+    // on buffers[2].
+    mST->abandon();
+
+    // Destroy the GL texture object to release its ref on buffers[2].
+    GLuint texID = TEX_ID;
+    glDeleteTextures(1, &texID);
+
+    // Destroy the EGLSurface.
+    EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface));
+    ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+    EXPECT_EQ(1, buffers[0]->getStrongCount());
+    EXPECT_EQ(1, buffers[1]->getStrongCount());
+
+    // Depending on how lazily the GL driver dequeues buffers, we may end up
+    // with either two or three total buffers.  If there are three, make sure
+    // the last one was properly down-ref'd.
+    if (buffers[2] != buffers[0]) {
+        EXPECT_EQ(1, buffers[2]->getStrongCount());
+    }
+}
+
+TEST_F(SurfaceTextureGLToGLTest, EglSurfaceDefaultsToSynchronousMode) {
+    // This test requires 3 buffers to run on a single thread.
+    mST->setBufferCountServer(3);
+
+    ASSERT_TRUE(mST->isSynchronousMode());
+
+    for (int i = 0; i < 10; i++) {
+        // Produce a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+                mProducerEglSurface, mProducerEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        glClear(GL_COLOR_BUFFER_BIT);
+        EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+        // Consume a frame
+        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+                mEglContext));
+        ASSERT_EQ(EGL_SUCCESS, eglGetError());
+        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+    }
+
+    ASSERT_TRUE(mST->isSynchronousMode());
+}
+
+/*
+ * This test fixture is for testing GL -> GL texture streaming from one thread
+ * to another.  It contains functionality to create a producer thread that will
+ * perform GL rendering to an ANativeWindow that feeds frames to a
+ * SurfaceTexture.  Additionally it supports interlocking the producer and
+ * consumer threads so that a specific sequence of calls can be
+ * deterministically created by the test.
  *
  * The intended usage is as follows:
  *
@@ -1122,7 +1325,7 @@
  * }
  *
  */
-class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
+class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest {
 protected:
 
     // ProducerThread is an abstract base class to simplify the creation of
@@ -1223,30 +1426,8 @@
         Condition mFrameFinishCondition;
     };
 
-    SurfaceTextureGLToGLTest():
-            mProducerEglSurface(EGL_NO_SURFACE),
-            mProducerEglContext(EGL_NO_CONTEXT) {
-    }
-
     virtual void SetUp() {
-        SurfaceTextureGLTest::SetUp();
-
-        EGLConfig myConfig = {0};
-        EGLint numConfigs = 0;
-        EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
-                1, &numConfigs));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-        mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
-                mANW.get(), NULL);
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
-
-        mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
-                EGL_NO_CONTEXT, getContextAttribs());
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
-
+        SurfaceTextureGLToGLTest::SetUp();
         mFC = new FrameCondition();
         mST->setFrameAvailableListener(mFC);
     }
@@ -1255,15 +1436,9 @@
         if (mProducerThread != NULL) {
             mProducerThread->requestExitAndWait();
         }
-        if (mProducerEglContext != EGL_NO_CONTEXT) {
-            eglDestroyContext(mEglDisplay, mProducerEglContext);
-        }
-        if (mProducerEglSurface != EGL_NO_SURFACE) {
-            eglDestroySurface(mEglDisplay, mProducerEglSurface);
-        }
         mProducerThread.clear();
         mFC.clear();
-        SurfaceTextureGLTest::TearDown();
+        SurfaceTextureGLToGLTest::TearDown();
     }
 
     void runProducerThread(const sp<ProducerThread> producerThread) {
@@ -1274,13 +1449,12 @@
         producerThread->run();
     }
 
-    EGLSurface mProducerEglSurface;
-    EGLContext mProducerEglContext;
     sp<ProducerThread> mProducerThread;
     sp<FrameCondition> mFC;
 };
 
-TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+        UpdateTexImageBeforeFrameFinishedCompletes) {
     class PT : public ProducerThread {
         virtual void render() {
             glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
@@ -1298,7 +1472,8 @@
     // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
 }
 
-TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+        UpdateTexImageAfterFrameFinishedCompletes) {
     class PT : public ProducerThread {
         virtual void render() {
             glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
@@ -1316,7 +1491,8 @@
     // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
 }
 
-TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+        RepeatedUpdateTexImageBeforeFrameFinishedCompletes) {
     enum { NUM_ITERATIONS = 1024 };
 
     class PT : public ProducerThread {
@@ -1344,7 +1520,8 @@
     }
 }
 
-TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+        RepeatedUpdateTexImageAfterFrameFinishedCompletes) {
     enum { NUM_ITERATIONS = 1024 };
 
     class PT : public ProducerThread {
@@ -1373,7 +1550,8 @@
 }
 
 // XXX: This test is disabled because it is currently hanging on some devices.
-TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+        DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) {
     enum { NUM_ITERATIONS = 64 };
 
     class PT : public ProducerThread {
@@ -1438,118 +1616,4 @@
     }
 }
 
-TEST_F(SurfaceTextureGLTest, EglDestroySurfaceUnrefsBuffers) {
-    EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
-            mANW.get(), NULL);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);
-
-    sp<GraphicBuffer> buffers[3];
-
-    for (int i = 0; i < 3; i++) {
-        // Produce a frame
-        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
-                mEglContext));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        glClear(GL_COLOR_BUFFER_BIT);
-        eglSwapBuffers(mEglDisplay, stcEglSurface);
-
-        // Consume a frame
-        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
-                mEglContext));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        mST->updateTexImage();
-        buffers[i] = mST->getCurrentBuffer();
-    }
-
-    // Destroy the GL texture object to release its ref on buffers[2].
-    GLuint texID = TEX_ID;
-    glDeleteTextures(1, &texID);
-
-    // Destroy the EGLSurface
-    EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    // Release the ref that the SurfaceTexture has on buffers[2].
-    mST->abandon();
-
-    EXPECT_EQ(1, buffers[0]->getStrongCount());
-    EXPECT_EQ(1, buffers[1]->getStrongCount());
-    EXPECT_EQ(1, buffers[2]->getStrongCount());
-}
-
-TEST_F(SurfaceTextureGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
-    EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
-            mANW.get(), NULL);
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-    ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);
-
-    sp<GraphicBuffer> buffers[3];
-
-    for (int i = 0; i < 3; i++) {
-        // Produce a frame
-        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
-                mEglContext));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        glClear(GL_COLOR_BUFFER_BIT);
-        EXPECT_TRUE(eglSwapBuffers(mEglDisplay, stcEglSurface));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-        // Consume a frame
-        EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
-                mEglContext));
-        ASSERT_EQ(EGL_SUCCESS, eglGetError());
-        ASSERT_EQ(NO_ERROR, mST->updateTexImage());
-        buffers[i] = mST->getCurrentBuffer();
-    }
-
-    // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has
-    // on buffers[2].
-    mST->abandon();
-
-    // Destroy the GL texture object to release its ref on buffers[2].
-    GLuint texID = TEX_ID;
-    glDeleteTextures(1, &texID);
-
-    // Destroy the EGLSurface.
-    EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface));
-    ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
-    EXPECT_EQ(1, buffers[0]->getStrongCount());
-    EXPECT_EQ(1, buffers[1]->getStrongCount());
-    EXPECT_EQ(1, buffers[2]->getStrongCount());
-}
-
-TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) {
-    int texHeight = 16;
-    ANativeWindowBuffer* anb;
-
-    GLint maxTextureSize;
-    glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
-
-    // make sure it works with small textures
-    mST->setDefaultBufferSize(16, texHeight);
-    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
-    EXPECT_EQ(16, anb->width);
-    EXPECT_EQ(texHeight, anb->height);
-    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
-    EXPECT_EQ(NO_ERROR, mST->updateTexImage());
-
-    // make sure it works with GL_MAX_TEXTURE_SIZE
-    mST->setDefaultBufferSize(maxTextureSize, texHeight);
-    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
-    EXPECT_EQ(maxTextureSize, anb->width);
-    EXPECT_EQ(texHeight, anb->height);
-    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
-    EXPECT_EQ(NO_ERROR, mST->updateTexImage());
-
-    // make sure it fails with GL_MAX_TEXTURE_SIZE+1
-    mST->setDefaultBufferSize(maxTextureSize+1, texHeight);
-    EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
-    EXPECT_EQ(maxTextureSize+1, anb->width);
-    EXPECT_EQ(texHeight, anb->height);
-    EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
-    ASSERT_NE(NO_ERROR, mST->updateTexImage());
-}
-
 } // namespace android
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 70f1b7a..0b5262d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -208,7 +208,7 @@
 
     glDisable(GL_DITHER);
 
-    glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+    glBindFramebuffer(GL_FRAMEBUFFER, mSnapshot->fbo);
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 
     mCaches.blend = true;
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index d51154d..aff7b93 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -213,7 +213,8 @@
     Layer* layer;
 
     /**
-     * Only set when the flag kFlagIsFboLayer is set.
+     * Target FBO used for rendering. Set to 0 when rendering directly
+     * into the framebuffer.
      */
     GLuint fbo;
 
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 092ae23..927e122 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -33,6 +33,8 @@
 
 namespace android {
 
+static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL;
+
 struct CameraSourceListener : public CameraListener {
     CameraSourceListener(const sp<CameraSource> &source);
 
@@ -156,6 +158,7 @@
       mLastFrameTimestampUs(0),
       mStarted(false),
       mNumFramesEncoded(0),
+      mTimeBetweenFrameCaptureUs(0),
       mFirstFrameTimeUs(0),
       mNumFramesDropped(0),
       mNumGlitches(0),
@@ -644,7 +647,8 @@
     releaseQueuedFrames();
     while (!mFramesBeingEncoded.empty()) {
         if (NO_ERROR !=
-            mFrameCompleteCondition.waitRelative(mLock, 3000000000LL)) {
+            mFrameCompleteCondition.waitRelative(mLock,
+                    mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
             LOGW("Timed out waiting for outstanding frames being encoded: %d",
                 mFramesBeingEncoded.size());
         }
@@ -736,7 +740,8 @@
         Mutex::Autolock autoLock(mLock);
         while (mStarted && mFramesReceived.empty()) {
             if (NO_ERROR !=
-                mFrameAvailableCondition.waitRelative(mLock, 1000000000LL)) {
+                mFrameAvailableCondition.waitRelative(mLock,
+                    mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
                 if (mCameraRecordingProxy != 0 &&
                     !mCameraRecordingProxy->asBinder()->isBinderAlive()) {
                     LOGW("camera recording proxy is gone");
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 23e6e35..4f6c8d1 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -39,12 +39,12 @@
         Size videoSize,
         int32_t videoFrameRate,
         const sp<Surface>& surface,
-        int64_t timeBetweenTimeLapseFrameCaptureUs) {
+        int64_t timeBetweenFrameCaptureUs) {
 
     CameraSourceTimeLapse *source = new
             CameraSourceTimeLapse(camera, proxy, cameraId,
                 videoSize, videoFrameRate, surface,
-                timeBetweenTimeLapseFrameCaptureUs);
+                timeBetweenFrameCaptureUs);
 
     if (source != NULL) {
         if (source->initCheck() != OK) {
@@ -62,15 +62,15 @@
         Size videoSize,
         int32_t videoFrameRate,
         const sp<Surface>& surface,
-        int64_t timeBetweenTimeLapseFrameCaptureUs)
+        int64_t timeBetweenFrameCaptureUs)
     : CameraSource(camera, proxy, cameraId, videoSize, videoFrameRate, surface, true),
-      mTimeBetweenTimeLapseFrameCaptureUs(timeBetweenTimeLapseFrameCaptureUs),
       mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate),
       mLastTimeLapseFrameRealTimestampUs(0),
       mSkipCurrentFrame(false) {
 
+    mTimeBetweenFrameCaptureUs = timeBetweenFrameCaptureUs;
     LOGD("starting time lapse mode: %lld us",
-        mTimeBetweenTimeLapseFrameCaptureUs);
+        mTimeBetweenFrameCaptureUs);
 
     mVideoWidth = videoSize.width;
     mVideoHeight = videoSize.height;
@@ -271,14 +271,14 @@
     // The first 2 output frames from the encoder are: decoder specific info and
     // the compressed video frame data for the first input video frame.
     if (mNumFramesEncoded >= 1 && *timestampUs <
-        (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenTimeLapseFrameCaptureUs)) {
+        (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenFrameCaptureUs)) {
         // Skip all frames from last encoded frame until
-        // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed.
+        // sufficient time (mTimeBetweenFrameCaptureUs) has passed.
         // Tell the camera to release its recording frame and return.
         ALOGV("dataCallbackTimestamp timelapse: skipping intermediate frame");
         return true;
     } else {
-        // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time:
+        // Desired frame has arrived after mTimeBetweenFrameCaptureUs time:
         // - Reset mLastTimeLapseFrameRealTimestampUs to current time.
         // - Artificially modify timestampUs to be one frame time (1/framerate) ahead
         // of the last encoded frame's time stamp.
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 97d011e..f9f92d2 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -520,6 +520,85 @@
     return NULL;
 }
 
+status_t OMXCodec::parseAVCCodecSpecificData(
+        const void *data, size_t size,
+        unsigned *profile, unsigned *level) {
+    const uint8_t *ptr = (const uint8_t *)data;
+
+    // verify minimum size and configurationVersion == 1.
+    if (size < 7 || ptr[0] != 1) {
+        return ERROR_MALFORMED;
+    }
+
+    *profile = ptr[1];
+    *level = ptr[3];
+
+    // There is decodable content out there that fails the following
+    // assertion, let's be lenient for now...
+    // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
+
+    size_t lengthSize = 1 + (ptr[4] & 3);
+
+    // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+    // violates it...
+    // CHECK((ptr[5] >> 5) == 7);  // reserved
+
+    size_t numSeqParameterSets = ptr[5] & 31;
+
+    ptr += 6;
+    size -= 6;
+
+    for (size_t i = 0; i < numSeqParameterSets; ++i) {
+        if (size < 2) {
+            return ERROR_MALFORMED;
+        }
+
+        size_t length = U16_AT(ptr);
+
+        ptr += 2;
+        size -= 2;
+
+        if (size < length) {
+            return ERROR_MALFORMED;
+        }
+
+        addCodecSpecificData(ptr, length);
+
+        ptr += length;
+        size -= length;
+    }
+
+    if (size < 1) {
+        return ERROR_MALFORMED;
+    }
+
+    size_t numPictureParameterSets = *ptr;
+    ++ptr;
+    --size;
+
+    for (size_t i = 0; i < numPictureParameterSets; ++i) {
+        if (size < 2) {
+            return ERROR_MALFORMED;
+        }
+
+        size_t length = U16_AT(ptr);
+
+        ptr += 2;
+        size -= 2;
+
+        if (size < length) {
+            return ERROR_MALFORMED;
+        }
+
+        addCodecSpecificData(ptr, length);
+
+        ptr += length;
+        size -= length;
+    }
+
+    return OK;
+}
+
 status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
     ALOGV("configureCodec protected=%d",
          (mFlags & kEnableGrallocUsageProtected) ? 1 : 0);
@@ -542,66 +621,17 @@
         } else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
             // Parse the AVCDecoderConfigurationRecord
 
-            const uint8_t *ptr = (const uint8_t *)data;
-
-            CHECK(size >= 7);
-            CHECK_EQ((unsigned)ptr[0], 1u);  // configurationVersion == 1
-            uint8_t profile = ptr[1];
-            uint8_t level = ptr[3];
-
-            // There is decodable content out there that fails the following
-            // assertion, let's be lenient for now...
-            // CHECK((ptr[4] >> 2) == 0x3f);  // reserved
-
-            size_t lengthSize = 1 + (ptr[4] & 3);
-
-            // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
-            // violates it...
-            // CHECK((ptr[5] >> 5) == 7);  // reserved
-
-            size_t numSeqParameterSets = ptr[5] & 31;
-
-            ptr += 6;
-            size -= 6;
-
-            for (size_t i = 0; i < numSeqParameterSets; ++i) {
-                CHECK(size >= 2);
-                size_t length = U16_AT(ptr);
-
-                ptr += 2;
-                size -= 2;
-
-                CHECK(size >= length);
-
-                addCodecSpecificData(ptr, length);
-
-                ptr += length;
-                size -= length;
-            }
-
-            CHECK(size >= 1);
-            size_t numPictureParameterSets = *ptr;
-            ++ptr;
-            --size;
-
-            for (size_t i = 0; i < numPictureParameterSets; ++i) {
-                CHECK(size >= 2);
-                size_t length = U16_AT(ptr);
-
-                ptr += 2;
-                size -= 2;
-
-                CHECK(size >= length);
-
-                addCodecSpecificData(ptr, length);
-
-                ptr += length;
-                size -= length;
+            unsigned profile, level;
+            status_t err;
+            if ((err = parseAVCCodecSpecificData(
+                            data, size, &profile, &level)) != OK) {
+                LOGE("Malformed AVC codec specific data.");
+                return err;
             }
 
             CODEC_LOGI(
-                    "AVC profile = %d (%s), level = %d",
-                    (int)profile, AVCProfileToString(profile), level);
+                    "AVC profile = %u (%s), level = %u",
+                    profile, AVCProfileToString(profile), level);
 
             if (!strcmp(mComponentName, "OMX.TI.Video.Decoder")
                 && (profile != kAVCProfileBaseline || level > 30)) {
diff --git a/opengl/java/android/opengl/ManagedEGLContext.java b/opengl/java/android/opengl/ManagedEGLContext.java
index d3a3662..61fa565 100644
--- a/opengl/java/android/opengl/ManagedEGLContext.java
+++ b/opengl/java/android/opengl/ManagedEGLContext.java
@@ -43,12 +43,13 @@
  * of the currently created EGL contexts in the process are being managed
  * through this class, then they will all be asked to terminate through the
  * call to {@link #onTerminate}.
+ * 
+ * @hide
  */
 public abstract class ManagedEGLContext {
     static final String TAG = "ManagedEGLContext";
 
-    static final ArrayList<ManagedEGLContext> sActive
-            = new ArrayList<ManagedEGLContext>();
+    static final ArrayList<ManagedEGLContext> sActive = new ArrayList<ManagedEGLContext>();
 
     final EGLContext mContext;
 
@@ -127,7 +128,7 @@
             sActive.clear();
         }
 
-        for (int i=0; i<active.size(); i++) {
+        for (int i = 0; i < active.size(); i++) {
             active.get(i).execTerminate();
         }
 
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 5855b63..9c1a10e 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -44,10 +44,17 @@
   LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
 endif
 
+ifneq ($(MAX_EGL_CACHE_ENTRY_SIZE),)
+  LOCAL_CFLAGS += -DMAX_EGL_CACHE_ENTRY_SIZE=$(MAX_EGL_CACHE_ENTRY_SIZE)
+endif
+
+ifneq ($(MAX_EGL_CACHE_SIZE),)
+  LOCAL_CFLAGS += -DMAX_EGL_CACHE_SIZE=$(MAX_EGL_CACHE_SIZE)
+endif
+
 include $(BUILD_SHARED_LIBRARY)
 installed_libEGL := $(LOCAL_INSTALLED_MODULE)
 
-
 # OpenGL drivers config file
 ifneq ($(BOARD_EGL_CFG),)
 
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 2237eb6..a63d5b0 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -370,6 +370,11 @@
             }
         }
 
+        // the EGL spec requires that a new EGLSurface default to swap interval
+        // 1, so explicitly set that on the window here.
+        ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
+        anw->setSwapInterval(anw, 1);
+
         EGLSurface surface = cnx->egl.eglCreateWindowSurface(
                 iDpy, iConfig, window, attrib_list);
         if (surface != EGL_NO_SURFACE) {
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index fe32d43..c4a7466 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -25,10 +25,18 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#ifndef MAX_EGL_CACHE_ENTRY_SIZE
+#define MAX_EGL_CACHE_ENTRY_SIZE (16 * 1024);
+#endif
+
+#ifndef MAX_EGL_CACHE_SIZE
+#define MAX_EGL_CACHE_SIZE (64 * 1024);
+#endif
+
 // Cache size limits.
 static const size_t maxKeySize = 1024;
-static const size_t maxValueSize = 4096;
-static const size_t maxTotalSize = 64 * 1024;
+static const size_t maxValueSize = MAX_EGL_CACHE_ENTRY_SIZE;
+static const size_t maxTotalSize = MAX_EGL_CACHE_SIZE;
 
 // Cache file header
 static const char* cacheFileMagic = "EGL$";
diff --git a/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java b/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java
index 20a1c50..1db2a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java
+++ b/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java
@@ -30,12 +30,17 @@
                     com.android.internal.R.string.config_defaultDreamComponent);
             }
             if (component != null) {
+                // dismiss the notification shade, recents, etc.
+                context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
                 ComponentName cn = ComponentName.unflattenFromString(component);
                 Intent zzz = new Intent(Intent.ACTION_MAIN)
                     .setComponent(cn)
                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
-                        | Intent.FLAG_ACTIVITY_NO_USER_ACTION
+                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+                            | Intent.FLAG_ACTIVITY_NO_USER_ACTION
+                            | Intent.FLAG_FROM_BACKGROUND
+                            | Intent.FLAG_ACTIVITY_NO_HISTORY
                         );
                 Slog.v(TAG, "Starting screen saver on dock event: " + component);
                 context.startActivity(zzz);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 2837366..72856b8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3478,12 +3478,17 @@
                     component = mContext.getResources().getString(R.string.config_defaultDreamComponent);
                 }
                 if (component != null) {
+                    // dismiss the notification shade, recents, etc.
+                    mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
                     ComponentName cn = ComponentName.unflattenFromString(component);
                     Intent intent = new Intent(Intent.ACTION_MAIN)
                         .setComponent(cn)
                         .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                             | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
                             | Intent.FLAG_ACTIVITY_NO_USER_ACTION
+                            | Intent.FLAG_FROM_BACKGROUND
+                            | Intent.FLAG_ACTIVITY_NO_HISTORY
                             );
                     mContext.startActivity(intent);
                 } else {
diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java
index dc628e0..113f007 100644
--- a/voip/java/com/android/server/sip/SipHelper.java
+++ b/voip/java/com/android/server/sip/SipHelper.java
@@ -73,7 +73,7 @@
  */
 class SipHelper {
     private static final String TAG = SipHelper.class.getSimpleName();
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
     private static final boolean DEBUG_PING = false;
 
     private SipStack mSipStack;
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 119ed54..38a683e 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -68,8 +68,7 @@
  */
 public final class SipService extends ISipService.Stub {
     static final String TAG = "SipService";
-    static final boolean DEBUGV = false;
-    static final boolean DEBUG = true;
+    static final boolean DEBUG = false;
     private static final int EXPIRY_TIME = 3600;
     private static final int SHORT_EXPIRY_TIME = 10;
     private static final int MIN_EXPIRY_TIME = 60;
@@ -581,7 +580,7 @@
         @Override
         public void onRinging(ISipSession s, SipProfile caller,
                 String sessionDescription) {
-            if (DEBUGV) Log.d(TAG, "<<<<< onRinging()");
+            if (DEBUG) Log.d(TAG, "<<<<< onRinging()");
             SipSessionGroup.SipSessionImpl session =
                     (SipSessionGroup.SipSessionImpl) s;
             synchronized (SipService.this) {
@@ -778,7 +777,6 @@
         private void restartLater() {
             synchronized (SipService.this) {
                 int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
-                Log.d(TAG, "Retry measurement " + interval + "s later.");
                 mTimer.cancel(this);
                 mTimer.set(interval * 1000, this);
             }
@@ -788,7 +786,7 @@
     private class AutoRegistrationProcess extends SipSessionAdapter
             implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
         private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
-        private String TAG = "SipAudoReg";
+        private String TAG = "SipAutoReg";
 
         private SipSessionGroup.SipSessionImpl mSession;
         private SipSessionGroup.SipSessionImpl mKeepAliveSession;
@@ -820,13 +818,12 @@
                 // in registration to avoid adding duplicate entries to server
                 mMyWakeLock.acquire(mSession);
                 mSession.unregister();
-                if (DEBUG) TAG = mSession.getLocalProfile().getUriString();
-                if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess");
+                TAG = "SipAutoReg:" + mSession.getLocalProfile().getUriString();
             }
         }
 
         private void startKeepAliveProcess(int interval) {
-            Log.d(TAG, "start keepalive w interval=" + interval);
+            if (DEBUG) Log.d(TAG, "start keepalive w interval=" + interval);
             if (mKeepAliveSession == null) {
                 mKeepAliveSession = mSession.duplicate();
             } else {
@@ -864,9 +861,11 @@
                             mKeepAliveSuccessCount = 0;
                         }
                     } else {
-                        Log.i(TAG, "keep keepalive going with interval "
-                                + interval + ", past success count="
-                                + mKeepAliveSuccessCount);
+                        if (DEBUG) {
+                            Log.i(TAG, "keep keepalive going with interval "
+                                    + interval + ", past success count="
+                                    + mKeepAliveSuccessCount);
+                        }
                         mKeepAliveSuccessCount /= 2;
                     }
                 } else {
@@ -894,7 +893,9 @@
         // SipSessionGroup.KeepAliveProcessCallback
         @Override
         public void onError(int errorCode, String description) {
-            Log.e(TAG, "keepalive error: " + description);
+            if (DEBUG) {
+                Log.e(TAG, "keepalive error: " + description);
+            }
             onResponse(true); // re-register immediately
         }
 
@@ -917,7 +918,7 @@
         public void onKeepAliveIntervalChanged() {
             if (mKeepAliveSession != null) {
                 int newInterval = getKeepAliveInterval();
-                if (DEBUGV) {
+                if (DEBUG) {
                     Log.v(TAG, "restart keepalive w interval=" + newInterval);
                 }
                 mKeepAliveSuccessCount = 0;
@@ -987,7 +988,7 @@
         }
 
         private void restart(int duration) {
-            if (DEBUG) Log.d(TAG, "Refresh registration " + duration + "s later.");
+            Log.d(TAG, "Refresh registration " + duration + "s later.");
             mTimer.cancel(this);
             mTimer.set(duration * 1000, this);
         }
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 06cdaf2..877a0a4 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -89,8 +89,8 @@
  */
 class SipSessionGroup implements SipListener {
     private static final String TAG = "SipSession";
-    private static final boolean DEBUG = true;
-    private static final boolean DEBUG_PING = DEBUG && false;
+    private static final boolean DEBUG = false;
+    private static final boolean DEBUG_PING = false;
     private static final String ANONYMOUS = "anonymous";
     // Limit the size of thread pool to 1 for the order issue when the phone is
     // waken up from sleep and there are many packets to be processed in the SIP
@@ -205,7 +205,9 @@
     }
 
     synchronized void resetExternalAddress() {
-        Log.d(TAG, " reset external addr on " + mSipStack);
+        if (DEBUG) {
+            Log.d(TAG, " reset external addr on " + mSipStack);
+        }
         mExternalIp = null;
         mExternalPort = 0;
     }
@@ -362,7 +364,7 @@
                         + SipSession.State.toString(session.mState));
             }
         } catch (Throwable e) {
-            Log.w(TAG, "event process error: " + event, e);
+            Log.w(TAG, "event process error: " + event, getRootCause(e));
             session.onError(e);
         }
     }
@@ -393,11 +395,22 @@
         if ((rport > 0) && (externalIp != null)) {
             mExternalIp = externalIp;
             mExternalPort = rport;
-            Log.d(TAG, " got external addr " + externalIp + ":" + rport
-                    + " on " + mSipStack);
+            if (DEBUG) {
+                Log.d(TAG, " got external addr " + externalIp + ":" + rport
+                        + " on " + mSipStack);
+            }
         }
     }
 
+    private Throwable getRootCause(Throwable exception) {
+        Throwable cause = exception.getCause();
+        while (cause != null) {
+            exception = cause;
+            cause = exception.getCause();
+        }
+        return exception;
+    }
+
     private SipSessionImpl createNewSession(RequestEvent event,
             ISipSessionListener listener, ServerTransaction transaction,
             int newState) throws SipException {
@@ -890,7 +903,9 @@
             if (expires != null && time < expires.getExpires()) {
                 time = expires.getExpires();
             }
-            Log.v(TAG, "Expiry time = " + time);
+            if (DEBUG) {
+                Log.v(TAG, "Expiry time = " + time);
+            }
             return time;
         }
 
@@ -1409,15 +1424,6 @@
             }
         }
 
-        private Throwable getRootCause(Throwable exception) {
-            Throwable cause = exception.getCause();
-            while (cause != null) {
-                exception = cause;
-                cause = exception.getCause();
-            }
-            return exception;
-        }
-
         private int getErrorCode(Throwable exception) {
             String message = exception.getMessage();
             if (exception instanceof UnknownHostException) {
@@ -1555,8 +1561,10 @@
                     try {
                         sendKeepAlive();
                     } catch (Throwable t) {
-                        Log.w(TAG, "keepalive error: "
-                                + mLocalProfile.getUriString(), getRootCause(t));
+                        if (DEBUG) {
+                            Log.w(TAG, "keepalive error: "
+                                    + mLocalProfile.getUriString(), getRootCause(t));
+                        }
                         // It's possible that the keepalive process is being stopped
                         // during session.sendKeepAlive() so need to check mRunning
                         // again here.
diff --git a/voip/java/com/android/server/sip/SipWakeLock.java b/voip/java/com/android/server/sip/SipWakeLock.java
index 52bc094..0c4d14c 100644
--- a/voip/java/com/android/server/sip/SipWakeLock.java
+++ b/voip/java/com/android/server/sip/SipWakeLock.java
@@ -22,8 +22,8 @@
 import java.util.HashSet;
 
 class SipWakeLock {
-    private static final boolean DEBUGV = SipService.DEBUGV;
-    private static final String TAG = SipService.TAG;
+    private static final boolean DEBUG = false;
+    private static final String TAG = "SipWakeLock";
     private PowerManager mPowerManager;
     private PowerManager.WakeLock mWakeLock;
     private PowerManager.WakeLock mTimerWakeLock;
@@ -34,9 +34,9 @@
     }
 
     synchronized void reset() {
+        if (DEBUG) Log.v(TAG, "reset count=" + mHolders.size());
         mHolders.clear();
         release(null);
-        if (DEBUGV) Log.v(TAG, "~~~ hard reset wakelock");
     }
 
     synchronized void acquire(long timeout) {
@@ -55,8 +55,7 @@
                     PowerManager.PARTIAL_WAKE_LOCK, "SipWakeLock");
         }
         if (!mWakeLock.isHeld()) mWakeLock.acquire();
-        if (DEBUGV) Log.v(TAG, "acquire wakelock: holder count="
-                + mHolders.size());
+        if (DEBUG) Log.v(TAG, "acquire count=" + mHolders.size());
     }
 
     synchronized void release(Object holder) {
@@ -65,7 +64,6 @@
                 && mWakeLock.isHeld()) {
             mWakeLock.release();
         }
-        if (DEBUGV) Log.v(TAG, "release wakelock: holder count="
-                + mHolders.size());
+        if (DEBUG) Log.v(TAG, "release count=" + mHolders.size());
     }
 }