Merge "Making UI a bit more responsive."
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 5f573bb..d39e741 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -261,6 +261,7 @@
      * @return a string propertly trimmed
      */
     public static String trimV4AddrZeros(String addr) {
+        if (addr == null) return null;
         String[] octets = addr.split("\\.");
         if (octets.length != 4) return addr;
         StringBuilder builder = new StringBuilder(16);
diff --git a/core/java/android/os/storage/package.html b/core/java/android/os/storage/package.html
new file mode 100644
index 0000000..a5f1e1c
--- /dev/null
+++ b/core/java/android/os/storage/package.html
@@ -0,0 +1,8 @@
+<HTML>
+<BODY>
+<p>
+Contains classes for the system storage service, which manages binary asset filesystems
+known as Opaque Binary Blobs (OBBs).
+</p>
+</BODY>
+</HTML>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index caff53b..c44f23b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2779,10 +2779,10 @@
         public static final String ACCESSIBILITY_SPEAK_PASSWORD = "speak_password";
 
         /**
-         * If injection of accessibility enhancing JavaScript scripts
+         * If injection of accessibility enhancing JavaScript screen-reader
          * is enabled.
          * <p>
-         *   Note: Accessibility injecting scripts are served by the
+         *   Note: The JavaScript based screen-reader is served by the
          *   Google infrastructure and enable users with disabilities to
          *   efficiantly navigate in and explore web content.
          * </p>
@@ -2795,6 +2795,22 @@
             "accessibility_script_injection";
 
         /**
+         * The URL for the injected JavaScript based screen-reader used
+         * for providing accessiblity of content in WebView.
+         * <p>
+         *   Note: The JavaScript based screen-reader is served by the
+         *   Google infrastructure and enable users with disabilities to
+         *   efficiently navigate in and explore web content.
+         * </p>
+         * <p>
+         *   This property represents a string value.
+         * </p>
+         * @hide
+         */
+        public static final String ACCESSIBILITY_SCREEN_READER_URL =
+            "accessibility_script_injection_url";
+
+        /**
          * Key bindings for navigation in built-in accessibility support for web content.
          * <p>
          *   Note: These key bindings are for the built-in accessibility navigation for
diff --git a/core/java/android/speech/tts/AudioPlaybackQueueItem.java b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
index 668b459..1a1fda81 100644
--- a/core/java/android/speech/tts/AudioPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/AudioPlaybackQueueItem.java
@@ -15,27 +15,91 @@
  */
 package android.speech.tts;
 
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.ConditionVariable;
 import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
 import android.util.Log;
 
 class AudioPlaybackQueueItem extends PlaybackQueueItem {
-    private final BlockingMediaPlayer mPlayer;
+    private static final String TAG = "TTS.AudioQueueItem";
+
+    private final Context mContext;
+    private final Uri mUri;
+    private final int mStreamType;
+
+    private final ConditionVariable mDone;
+    private MediaPlayer mPlayer;
+    private volatile boolean mFinished;
 
     AudioPlaybackQueueItem(UtteranceProgressDispatcher dispatcher,
-            Object callerIdentity, BlockingMediaPlayer player) {
+            Object callerIdentity,
+            Context context, Uri uri, int streamType) {
         super(dispatcher, callerIdentity);
-        mPlayer = player;
+
+        mContext = context;
+        mUri = uri;
+        mStreamType = streamType;
+
+        mDone = new ConditionVariable();
+        mPlayer = null;
+        mFinished = false;
     }
     @Override
     public void run() {
-        getDispatcher().dispatchOnStart();
-        // TODO: This can be avoided. Will be fixed later in this CL.
-        mPlayer.startAndWait();
-        getDispatcher().dispatchOnDone();
+        final UtteranceProgressDispatcher dispatcher = getDispatcher();
+
+        dispatcher.dispatchOnStart();
+        mPlayer = MediaPlayer.create(mContext, mUri);
+        if (mPlayer == null) {
+            dispatcher.dispatchOnError();
+            return;
+        }
+
+        try {
+            mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+                @Override
+                public boolean onError(MediaPlayer mp, int what, int extra) {
+                    Log.w(TAG, "Audio playback error: " + what + ", " + extra);
+                    mDone.open();
+                    return true;
+                }
+            });
+            mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+                @Override
+                public void onCompletion(MediaPlayer mp) {
+                    mFinished = true;
+                    mDone.open();
+                }
+            });
+            mPlayer.setAudioStreamType(mStreamType);
+            mPlayer.start();
+            mDone.block();
+            finish();
+        } catch (IllegalArgumentException ex) {
+            Log.w(TAG, "MediaPlayer failed", ex);
+            mDone.open();
+        }
+
+        if (mFinished) {
+            dispatcher.dispatchOnDone();
+        } else {
+            dispatcher.dispatchOnError();
+        }
+    }
+
+    private void finish() {
+        try {
+            mPlayer.stop();
+        } catch (IllegalStateException ex) {
+            // Do nothing, the player is already stopped
+        }
+        mPlayer.release();
     }
 
     @Override
     void stop(boolean isError) {
-        mPlayer.stop();
+        mDone.open();
     }
 }
diff --git a/core/java/android/speech/tts/BlockingMediaPlayer.java b/core/java/android/speech/tts/BlockingMediaPlayer.java
deleted file mode 100644
index 1ccc6e4..0000000
--- a/core/java/android/speech/tts/BlockingMediaPlayer.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.speech.tts;
-
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.net.Uri;
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.util.Log;
-
-/**
- * A media player that allows blocking to wait for it to finish.
- */
-class BlockingMediaPlayer {
-
-    private static final String TAG = "BlockMediaPlayer";
-
-    private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer";
-
-    private final Context mContext;
-    private final Uri mUri;
-    private final int mStreamType;
-    private final ConditionVariable mDone;
-    // Only accessed on the Handler thread
-    private MediaPlayer mPlayer;
-    private volatile boolean mFinished;
-
-    /**
-     * Creates a new blocking media player.
-     * Creating a blocking media player is a cheap operation.
-     *
-     * @param context
-     * @param uri
-     * @param streamType
-     */
-    public BlockingMediaPlayer(Context context, Uri uri, int streamType) {
-        mContext = context;
-        mUri = uri;
-        mStreamType = streamType;
-        mDone = new ConditionVariable();
-    }
-
-    /**
-     * Starts playback and waits for it to finish.
-     * Can be called from any thread.
-     *
-     * @return {@code true} if the playback finished normally, {@code false} if the playback
-     *         failed or {@link #stop} was called before the playback finished.
-     */
-    public boolean startAndWait() {
-        HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME);
-        thread.start();
-        Handler handler = new Handler(thread.getLooper());
-        mFinished = false;
-        handler.post(new Runnable() {
-            @Override
-            public void run() {
-                startPlaying();
-            }
-        });
-        mDone.block();
-        handler.post(new Runnable() {
-            @Override
-            public void run() {
-                finish();
-                // No new messages should get posted to the handler thread after this
-                Looper.myLooper().quit();
-            }
-        });
-        return mFinished;
-    }
-
-    /**
-     * Stops playback. Can be called multiple times.
-     * Can be called from any thread.
-     */
-    public void stop() {
-        mDone.open();
-    }
-
-    /**
-     * Starts playback.
-     * Called on the handler thread.
-     */
-    private void startPlaying() {
-        mPlayer = MediaPlayer.create(mContext, mUri);
-        if (mPlayer == null) {
-            Log.w(TAG, "Failed to play " + mUri);
-            mDone.open();
-            return;
-        }
-        try {
-            mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
-                @Override
-                public boolean onError(MediaPlayer mp, int what, int extra) {
-                    Log.w(TAG, "Audio playback error: " + what + ", " + extra);
-                    mDone.open();
-                    return true;
-                }
-            });
-            mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
-                @Override
-                public void onCompletion(MediaPlayer mp) {
-                    mFinished = true;
-                    mDone.open();
-                }
-            });
-            mPlayer.setAudioStreamType(mStreamType);
-            mPlayer.start();
-        } catch (IllegalArgumentException ex) {
-            Log.w(TAG, "MediaPlayer failed", ex);
-            mDone.open();
-        }
-    }
-
-    /**
-     * Stops playback and release the media player.
-     * Called on the handler thread.
-     */
-    private void finish() {
-        try {
-            mPlayer.stop();
-        } catch (IllegalStateException ex) {
-            // Do nothing, the player is already stopped
-        }
-        mPlayer.release();
-    }
-
-}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java
deleted file mode 100644
index f83b793..0000000
--- a/core/java/android/speech/tts/MessageParams.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.speech.tts;
-
-import android.speech.tts.TextToSpeechService.UtteranceProgressDispatcher;
-
-abstract class MessageParams {
-    static final int TYPE_SYNTHESIS = 1;
-    static final int TYPE_AUDIO = 2;
-    static final int TYPE_SILENCE = 3;
-
-    private final UtteranceProgressDispatcher mDispatcher;
-    private final Object mCallerIdentity;
-
-    MessageParams(UtteranceProgressDispatcher dispatcher, Object callerIdentity) {
-        mDispatcher = dispatcher;
-        mCallerIdentity = callerIdentity;
-    }
-
-    UtteranceProgressDispatcher getDispatcher() {
-        return mDispatcher;
-    }
-
-    Object getCallerIdentity() {
-        return mCallerIdentity;
-    }
-
-    @Override
-    public String toString() {
-        return "MessageParams[" + hashCode() + "]";
-    }
-
-    abstract int getType();
-}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index ba8485a..4c1a0af 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -694,12 +694,12 @@
     }
 
     private class AudioSpeechItem extends SpeechItem {
-        private final BlockingMediaPlayer mPlayer;
-
+        private final AudioPlaybackQueueItem mItem;
         public AudioSpeechItem(Object callerIdentity, int callerUid, int callerPid,
                 Bundle params, Uri uri) {
             super(callerIdentity, callerUid, callerPid, params);
-            mPlayer = new BlockingMediaPlayer(TextToSpeechService.this, uri, getStreamType());
+            mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(),
+                    TextToSpeechService.this, uri, getStreamType());
         }
 
         @Override
@@ -709,8 +709,7 @@
 
         @Override
         protected int playImpl() {
-            mAudioPlaybackHandler.enqueue(new AudioPlaybackQueueItem(
-                    this, getCallerIdentity(), mPlayer));
+            mAudioPlaybackHandler.enqueue(mItem);
             return TextToSpeech.SUCCESS;
         }
 
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 05c5daa..b455ad5 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -149,7 +149,7 @@
      * It may be appropriate to tweak this on a device-specific basis in an overlay based on
      * the characteristics of the touch panel and firmware.
      */
-    private static final int TOUCH_SLOP = 4;
+    private static final int TOUCH_SLOP = 8;
     
     /**
      * Distance the first touch can wander before we stop considering this event a double tap
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 5035cae..1102a47 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2719,13 +2719,6 @@
             return more;
         }
 
-        float alpha = child.getAlpha();
-        // Bail out early if the view does not need to be drawn
-        if (alpha <= ViewConfiguration.ALPHA_THRESHOLD && (child.mPrivateFlags & ALPHA_SET) == 0 &&
-                !(child instanceof SurfaceView)) {
-            return more;
-        }
-
         if (hardwareAccelerated) {
             // Clear INVALIDATED flag to allow invalidation to occur during rendering, but
             // retain the flag's value temporarily in the mRecreateDisplayList flag
@@ -2779,6 +2772,7 @@
             }
         }
 
+        float alpha = child.getAlpha();
         if (transformToApply != null || alpha < 1.0f || !child.hasIdentityMatrix()) {
             if (transformToApply != null || !childHasIdentityMatrix) {
                 int transX = 0;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index b68dec9..a284a17 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -59,6 +59,7 @@
 import android.os.StrictMode;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
+import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.util.Log;
@@ -849,13 +850,12 @@
     // the alias via which accessibility JavaScript interface is exposed
     private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
 
-    // JavaScript to inject the script chooser which will
-    // pick the right script for the current URL
-    private static final String ACCESSIBILITY_SCRIPT_CHOOSER_JAVASCRIPT =
+    // Template for JavaScript that injects a screen-reader.
+    private static final String ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE =
         "javascript:(function() {" +
         "    var chooser = document.createElement('script');" +
         "    chooser.type = 'text/javascript';" +
-        "    chooser.src = 'https://ssl.gstatic.com/accessibility/javascript/android/AndroidScriptChooser.user.js';" +
+        "    chooser.src = '%1s';" +
         "    document.getElementsByTagName('head')[0].appendChild(chooser);" +
         "  })();";
 
@@ -2496,11 +2496,12 @@
     }
 
     /**
-     * Return the reading level scale of the WebView
+     * Compute the reading level scale of the WebView
+     * @param scale The current scale.
      * @return The reading level scale.
      */
-    /*package*/ float getReadingLevelScale() {
-        return mZoomManager.getReadingLevelScale();
+    /*package*/ float computeReadingLevelScale(float scale) {
+        return mZoomManager.computeReadingLevelScale(scale);
     }
 
     /**
@@ -3818,7 +3819,7 @@
             if (onDeviceScriptInjectionEnabled) {
                 ensureAccessibilityScriptInjectorInstance(false);
                 // neither script injected nor script injection opted out => we inject
-                loadUrl(ACCESSIBILITY_SCRIPT_CHOOSER_JAVASCRIPT);
+                loadUrl(getScreenReaderInjectingJs());
                 // TODO: Set this flag after successfull script injection. Maybe upon injection
                 // the chooser should update the meta tag and we check it to declare success
                 mAccessibilityScriptInjected = true;
@@ -3832,7 +3833,7 @@
         } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED) {
             ensureAccessibilityScriptInjectorInstance(false);
             // the URL provides accessibility but we still need to add our generic script
-            loadUrl(ACCESSIBILITY_SCRIPT_CHOOSER_JAVASCRIPT);
+            loadUrl(getScreenReaderInjectingJs());
         } else {
             Log.e(LOGTAG, "Unknown URL value for the \"axs\" URL parameter: " + axsParameterValue);
         }
@@ -3854,6 +3855,17 @@
     }
 
     /**
+     * Gets JavaScript that injects a screen-reader.
+     *
+     * @return The JavaScript snippet.
+     */
+    private String getScreenReaderInjectingJs() {
+        String screenReaderUrl = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL);
+        return String.format(ACCESSIBILITY_SCREEN_READER_JAVASCRIPT_TEMPLATE, screenReaderUrl);
+    }
+
+    /**
      * Gets the "axs" URL parameter value.
      *
      * @param url A url to fetch the paramter from.
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 0f749bc..14da23e 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2514,7 +2514,7 @@
                     if (mSettings.isNarrowColumnLayout()) {
                         // In case of automatic text reflow in fixed view port mode.
                         mInitialViewState.mTextWrapScale =
-                                mWebView.getReadingLevelScale();
+                                mWebView.computeReadingLevelScale(data.mScale);
                     }
                 } else {
                     // Scale is given such as when page is restored, use it.
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 14bdc42..8ffba64 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -316,7 +316,12 @@
      * Returns the zoom scale used for reading text on a double-tap.
      */
     public final float getReadingLevelScale() {
-        return mDisplayDensity * mDoubleTapZoomFactor;
+        return computeScaleWithLimits(computeReadingLevelScale(getZoomOverviewScale()));
+    }
+
+    /* package */ final float computeReadingLevelScale(float scale) {
+        return Math.max(mDisplayDensity * mDoubleTapZoomFactor,
+                scale + MIN_DOUBLE_TAP_SCALE_INCREMENT);
     }
 
     public final float getInvDefaultScale() {
@@ -678,7 +683,7 @@
             }
             zoomToOverview();
         } else {
-            zoomToReadingLevelOrMore();
+            zoomToReadingLevel();
         }
     }
 
@@ -709,9 +714,8 @@
             !mWebView.getSettings().getUseFixedViewport());
     }
 
-    private void zoomToReadingLevelOrMore() {
-        final float zoomScale = Math.max(getReadingLevelScale(),
-                mActualScale + MIN_DOUBLE_TAP_SCALE_INCREMENT);
+    private void zoomToReadingLevel() {
+        final float readingScale = getReadingLevelScale();
 
         int left = mWebView.nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
         if (left != WebView.NO_LEFTEDGE) {
@@ -721,13 +725,13 @@
             // Re-calculate the zoom center so that the new scroll x will be
             // on the left edge.
             if (viewLeft > 0) {
-                mZoomCenterX = viewLeft * zoomScale / (zoomScale - mActualScale);
+                mZoomCenterX = viewLeft * readingScale / (readingScale - mActualScale);
             } else {
                 mWebView.scrollBy(viewLeft, 0);
                 mZoomCenterX = 0;
             }
         }
-        startZoomAnimation(zoomScale,
+        startZoomAnimation(readingScale,
             !mWebView.getSettings().getUseFixedViewport());
     }
 
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 7ba4777..07bd918 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -145,6 +145,9 @@
                 Thread.getDefaultUncaughtExceptionHandler().uncaughtException(t, ex);
             }
         }
+        public synchronized void onDataSetChangedAsync() {
+            onDataSetChanged();
+        }
         public synchronized int getCount() {
             int count = 0;
             try {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 82bcd3e..babd8e7 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9931,6 +9931,7 @@
             if (suggestionInfo.suggestionIndex == ADD_TO_DICTIONARY) {
                 Intent intent = new Intent(Settings.ACTION_USER_DICTIONARY_INSERT);
                 intent.putExtra("word", originalText);
+                intent.putExtra("locale", getTextServicesLocale().toString());
                 intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                 getContext().startActivity(intent);
                 // There is no way to know if the word was indeed added. Re-check.
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 72489a2..61c0c8e 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -1367,10 +1367,12 @@
     /**
      * Get a message and set Message.target = this.
      *
-     * @return message
+     * @return message or null if SM has quit
      */
     public final Message obtainMessage()
     {
+        if (mSmHandler == null) return null;
+
         return Message.obtain(mSmHandler);
     }
 
@@ -1378,9 +1380,11 @@
      * Get a message and set Message.target = this and what
      *
      * @param what is the assigned to Message.what.
-     * @return message
+     * @return message or null if SM has quit
      */
     public final Message obtainMessage(int what) {
+        if (mSmHandler == null) return null;
+
         return Message.obtain(mSmHandler, what);
     }
 
@@ -1390,10 +1394,12 @@
      *
      * @param what is the assigned to Message.what.
      * @param obj is assigned to Message.obj.
-     * @return message
+     * @return message or null if SM has quit
      */
     public final Message obtainMessage(int what, Object obj)
     {
+        if (mSmHandler == null) return null;
+
         return Message.obtain(mSmHandler, what, obj);
     }
 
@@ -1404,10 +1410,13 @@
      * @param what  is assigned to Message.what
      * @param arg1  is assigned to Message.arg1
      * @param arg2  is assigned to Message.arg2
-     * @return  A Message object from the global pool.
+     * @return  A Message object from the global pool or null if
+     *          SM has quit
      */
     public final Message obtainMessage(int what, int arg1, int arg2)
     {
+        if (mSmHandler == null) return null;
+
         return Message.obtain(mSmHandler, what, arg1, arg2);
     }
 
@@ -1419,10 +1428,13 @@
      * @param arg1  is assigned to Message.arg1
      * @param arg2  is assigned to Message.arg2
      * @param obj is assigned to Message.obj
-     * @return  A Message object from the global pool.
+     * @return  A Message object from the global pool or null if
+     *          SM has quit
      */
     public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
     {
+        if (mSmHandler == null) return null;
+
         return Message.obtain(mSmHandler, what, arg1, arg2, obj);
     }
 
@@ -1430,6 +1442,9 @@
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(int what) {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return;
+
         mSmHandler.sendMessage(obtainMessage(what));
     }
 
@@ -1437,6 +1452,9 @@
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(int what, Object obj) {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return;
+
         mSmHandler.sendMessage(obtainMessage(what,obj));
     }
 
@@ -1444,6 +1462,9 @@
      * Enqueue a message to this state machine.
      */
     public final void sendMessage(Message msg) {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return;
+
         mSmHandler.sendMessage(msg);
     }
 
@@ -1451,6 +1472,9 @@
      * Enqueue a message to this state machine after a delay.
      */
     public final void sendMessageDelayed(int what, long delayMillis) {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return;
+
         mSmHandler.sendMessageDelayed(obtainMessage(what), delayMillis);
     }
 
@@ -1458,6 +1482,9 @@
      * Enqueue a message to this state machine after a delay.
      */
     public final void sendMessageDelayed(int what, Object obj, long delayMillis) {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return;
+
         mSmHandler.sendMessageDelayed(obtainMessage(what, obj), delayMillis);
     }
 
@@ -1465,6 +1492,9 @@
      * Enqueue a message to this state machine after a delay.
      */
     public final void sendMessageDelayed(Message msg, long delayMillis) {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return;
+
         mSmHandler.sendMessageDelayed(msg, delayMillis);
     }
 
@@ -1509,6 +1539,9 @@
      * will be processed.
      */
     public final void quit() {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return;
+
         mSmHandler.quit();
     }
 
@@ -1523,6 +1556,9 @@
      * @return if debugging is enabled
      */
     public boolean isDbg() {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return false;
+
         return mSmHandler.isDbg();
     }
 
@@ -1532,6 +1568,9 @@
      * @param dbg is true to enable debugging.
      */
     public void setDbg(boolean dbg) {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return;
+
         mSmHandler.setDbg(dbg);
     }
 
@@ -1539,6 +1578,9 @@
      * Start the state machine.
      */
     public void start() {
+        // mSmHandler can be null if the state machine has quit.
+        if (mSmHandler == null) return;
+
         /** Send the complete construction message */
         mSmHandler.completeConstruction();
     }
diff --git a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
index 18076c4..7317ecf 100644
--- a/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
+++ b/core/java/com/android/internal/widget/IRemoteViewsFactory.aidl
@@ -22,6 +22,7 @@
 /** {@hide} */
 interface IRemoteViewsFactory {
     void onDataSetChanged();
+    oneway void onDataSetChangedAsync();
     oneway void onDestroy(in Intent intent);
     int getCount();
     RemoteViews getViewAt(int position);
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 7724646..feba1e6 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -732,11 +732,7 @@
                                       jcharArray text, int index, int count,
                                       jfloat x, jfloat y, int flags, SkPaint* paint) {
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-#if RTL_USE_HARFBUZZ
         drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint);
-#else
-        TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas);
-#endif
         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
     }
 
@@ -745,11 +741,7 @@
                                           int start, int end,
                                           jfloat x, jfloat y, int flags, SkPaint* paint) {
         const jchar* textArray = env->GetStringChars(text, NULL);
-#if RTL_USE_HARFBUZZ
         drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint);
-#else
-        TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas);
-#endif
         env->ReleaseStringChars(text, textArray);
     }
 
@@ -770,12 +762,14 @@
         value = TextLayoutCache::getInstance().getValue(paint, textArray, start, count,
                 contextCount, flags);
         if (value == NULL) {
-            LOGE("Cannot get TextLayoutCache value");
+            LOGE("Cannot get TextLayoutCache value for text = '%s'",
+                    String8(textArray + start, count).string());
             return ;
         }
 #else
-        value = new TextLayoutCacheValue();
-        value->computeValues(paint, textArray, start, count, contextCount, flags);
+        value = new TextLayoutCacheValue(contextCount);
+        TextLayoutEngine::getInstance().computeValues(value.get(), paint,
+                reinterpret_cast<const UChar*>(textArray), start, count, contextCount, flags);
 #endif
         doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(), x, y, flags, paint);
     }
@@ -802,13 +796,8 @@
         jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
 
         jchar* chars = env->GetCharArrayElements(text, NULL);
-#if RTL_USE_HARFBUZZ
         drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
                 count, contextCount, x, y, dirFlags, paint);
-#else
-        TextLayout::drawTextRun(paint, chars + contextIndex, index - contextIndex,
-                count, contextCount, dirFlags, x, y, canvas);
-#endif
         env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
     }
 
@@ -820,13 +809,8 @@
         jint count = end - start;
         jint contextCount = contextEnd - contextStart;
         const jchar* chars = env->GetStringChars(text, NULL);
-#if RTL_USE_HARFBUZZ
         drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
                 count, contextCount, x, y, dirFlags, paint);
-#else
-        TextLayout::drawTextRun(paint, chars + contextStart, start - contextStart,
-                count, contextCount, dirFlags, x, y, canvas);
-#endif
         env->ReleaseStringChars(text, chars);
     }
 
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 883940b..19f53d7 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -350,14 +350,10 @@
         SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
         const jchar* textArray = env->GetCharArrayElements(text, NULL);
         jfloat result = 0;
-#if RTL_USE_HARFBUZZ
+
         TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength,
                 paint->getFlags(), NULL /* dont need all advances */, &result);
-#else
-        // we double count, since measureText wants a byteLength
-        SkScalar width = paint->measureText(textArray + index, count << 1);
-        result = SkScalarToFloat(width);
-#endif
+
         env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
         return result;
     }
@@ -380,13 +376,9 @@
         SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
         jfloat width = 0;
 
-#if RTL_USE_HARFBUZZ
         TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength,
                 paint->getFlags(), NULL /* dont need all advances */, &width);
-#else
 
-        width = SkScalarToFloat(paint->measureText(textArray + start, count << 1));
-#endif
         env->ReleaseStringChars(text, textArray);
         return width;
     }
@@ -404,12 +396,9 @@
         SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
         jfloat width = 0;
 
-#if RTL_USE_HARFBUZZ
         TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength,
                 paint->getFlags(), NULL /* dont need all advances */, &width);
-#else
-        width = SkScalarToFloat(paint->measureText(textArray, textLength << 1));
-#endif
+
         env->ReleaseStringChars(text, textArray);
         return width;
     }
@@ -434,17 +423,9 @@
         AutoJavaFloatArray autoWidths(env, widths, count);
         jfloat* widthsArray = autoWidths.ptr();
 
-#if RTL_USE_HARFBUZZ
         TextLayout::getTextRunAdvances(paint, text, 0, count, count,
                 paint->getFlags(), widthsArray, NULL /* dont need totalAdvance */);
-#else
-        SkScalar* scalarArray = (SkScalar*)widthsArray;
 
-        count = paint->getTextWidths(text, count << 1, scalarArray);
-        for (int i = 0; i < count; i++) {
-            widthsArray[i] = SkScalarToFloat(scalarArray[i]);
-        }
-#endif
         return count;
     }
 
@@ -597,54 +578,11 @@
 
     static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start,
             jint count, jint flags, jint offset, jint opt) {
-#if RTL_USE_HARFBUZZ
         jfloat scalarArray[count];
 
         TextLayout::getTextRunAdvances(paint, text, start, count, count, flags,
                 scalarArray, NULL /* dont need totalAdvance */);
-#else
-        SkScalar scalarArray[count];
-        jchar buffer[count];
 
-        // this is where we'd call harfbuzz
-        // for now we just use ushape.c and widths returned from skia
-
-        int widths;
-        if (flags & 0x1) { // rtl, call arabic shaping in case
-            UErrorCode status = U_ZERO_ERROR;
-            // Use fixed length since we need to keep start and count valid
-            u_shapeArabic(text + start, count, buffer, count,
-                    U_SHAPE_LENGTH_FIXED_SPACES_NEAR | U_SHAPE_TEXT_DIRECTION_LOGICAL |
-                    U_SHAPE_LETTERS_SHAPE | U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
-            // we shouldn't fail unless there's an out of memory condition,
-            // in which case we're hosed anyway
-            for (int i = 0; i < count; ++i) {
-              if (buffer[i] == 0xffff) {
-                buffer[i] = 0x200b; // zero-width-space for skia
-              }
-            }
-            widths = paint->getTextWidths(buffer, count << 1, scalarArray);
-        } else {
-            widths = paint->getTextWidths(text + start, count << 1, scalarArray);
-        }
-
-        if (widths < count) {
-            // Skia operates on code points, not code units, so surrogate pairs return only one
-            // value. Expand the result so we have one value per UTF-16 code unit.
-
-            // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
-            // leaving the remaining widths zero.  Not nice.
-            const jchar *chars = text + start;
-            for (int i = count, p = widths - 1; --i > p;) {
-                if (chars[i] >= 0xdc00 && chars[i] < 0xe000 &&
-                        chars[i-1] >= 0xd800 && chars[i-1] < 0xdc00) {
-                    scalarArray[i] = 0;
-                } else {
-                  scalarArray[i] = scalarArray[--p];
-                }
-            }
-        }
-#endif
         jint pos = offset - start;
         switch (opt) {
         case AFTER:
diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h
index a41c91b..d43745f 100644
--- a/core/jni/android/graphics/RtlProperties.h
+++ b/core/jni/android/graphics/RtlProperties.h
@@ -45,9 +45,6 @@
     return kRtlDebugDisabled;
 }
 
-// Define if we want to use Harfbuzz (1) or not (0)
-#define RTL_USE_HARFBUZZ 1
-
 // Define if we want (1) to have Advances debug values or not (0)
 #define DEBUG_ADVANCES 0
 
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index e1398e9..bc30ace 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "TextLayout"
+
 #include "TextLayout.h"
 #include "TextLayoutCache.h"
 
@@ -46,211 +48,35 @@
     return false;
 }
 
-/**
- * Character-based Arabic shaping.
- *
- * We'll use harfbuzz and glyph-based shaping instead once we're set up for it.
- *
- * @context the text context
- * @start the start of the text to render
- * @count the length of the text to render, start + count  must be <= contextCount
- * @contextCount the length of the context
- * @shaped where to put the shaped text, must have capacity for count uchars
- * @return the length of the shaped text, or -1 if error
- */
-int TextLayout::shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
-                        jchar* shaped, UErrorCode& status) {
-    SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount);
-    jchar* buffer = tempBuffer.get();
-
-    // Use fixed length since we need to keep start and count valid
-    u_shapeArabic(context, contextCount, buffer, contextCount,
-                   U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
-                   U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
-                   U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
-
-    if (U_SUCCESS(status)) {
-        // trim out UNICODE_NOT_A_CHAR following ligatures, if any
-        int end = 0;
-        for (int i = start, e = start + count; i < e; ++i) {
-            if (buffer[i] != UNICODE_NOT_A_CHAR) {
-                buffer[end++] = buffer[i];
-            }
-        }
-        count = end;
-        // ALOG(LOG_INFO, "CSRTL", "start %d count %d ccount %d\n", start, count, contextCount);
-        ubidi_writeReverse(buffer, count, shaped, count, UBIDI_DO_MIRRORING | UBIDI_OUTPUT_REVERSE
-                           | UBIDI_KEEP_BASE_COMBINING, &status);
-        if (U_SUCCESS(status)) {
-            return count;
-        }
-    }
-    return -1;
-}
-
-/**
- * Basic character-based layout supporting rtl and arabic shaping.
- * Runs bidi on the text and generates a reordered, shaped line in buffer, returning
- * the length.
- * @text the text
- * @len the length of the text in uchars
- * @dir receives the resolved paragraph direction
- * @buffer the buffer to receive the reordered, shaped line.  Must have capacity of
- * at least len jchars.
- * @flags line bidi flags
- * @return the length of the reordered, shaped line, or -1 if error
- */
-jint TextLayout::layoutLine(const jchar* text, jint len, jint flags, int& dir, jchar* buffer,
-        UErrorCode& status) {
-    static const int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING |
-            UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE;
-
-    UBiDiLevel bidiReq = 0;
-    switch (flags) {
-    case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
-    case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
-    case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
-    case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
-    case kBidi_Force_LTR: memcpy(buffer, text, len * sizeof(jchar)); return len;
-    case kBidi_Force_RTL: return shapeRtlText(text, 0, len, len, buffer, status);
-    }
-
-    int32_t result = -1;
-
-    UBiDi* bidi = ubidi_open();
-    if (bidi) {
-        ubidi_setPara(bidi, text, len, bidiReq, NULL, &status);
-        if (U_SUCCESS(status)) {
-            dir = ubidi_getParaLevel(bidi) & 0x1; // 0 if ltr, 1 if rtl
-
-            int rc = ubidi_countRuns(bidi, &status);
-            if (U_SUCCESS(status)) {
-                // ALOG(LOG_INFO, "LAYOUT", "para bidiReq=%d dir=%d rc=%d\n", bidiReq, dir, rc);
-
-                int32_t slen = 0;
-                for (int i = 0; i < rc; ++i) {
-                    int32_t start;
-                    int32_t length;
-                    UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &start, &length);
-
-                    if (runDir == UBIDI_RTL) {
-                        slen += shapeRtlText(text + start, 0, length, length, buffer + slen, status);
-                    } else {
-                        memcpy(buffer + slen, text + start, length * sizeof(jchar));
-                        slen += length;
-                    }
-                }
-                if (U_SUCCESS(status)) {
-                    result = slen;
-                }
-            }
-        }
-        ubidi_close(bidi);
-    }
-
-    return result;
-}
-
-bool TextLayout::prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags,
-        const jchar** outText, int32_t* outBytes, jchar** outBuffer) {
-    const jchar *workText = text;
-    jchar *buffer = NULL;
-    int dir = kDirection_LTR;
-    if (needsLayout(text, len, bidiFlags)) {
-        buffer =(jchar *) malloc(len * sizeof(jchar));
-        if (!buffer) {
-            return false;
-        }
-        UErrorCode status = U_ZERO_ERROR;
-        len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir
-        if (!U_SUCCESS(status)) {
-            ALOG(LOG_WARN, "LAYOUT", "drawText error %d\n", status);
-            free(buffer);
-            return false; // can't render
-        }
-        workText = buffer; // use the shaped text
-    }
-
-    bool trimLeft = false;
-    bool trimRight = false;
-
-    SkPaint::Align horiz = paint->getTextAlign();
-    switch (horiz) {
-        case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break;
-        case SkPaint::kCenter_Align: trimLeft = trimRight = true; break;
-        case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask);
-        default: break;
-    }
-    const jchar* workLimit = workText + len;
-
-    if (trimLeft) {
-        while (workText < workLimit && *workText == ' ') {
-            ++workText;
-        }
-    }
-    if (trimRight) {
-        while (workLimit > workText && *(workLimit - 1) == ' ') {
-            --workLimit;
-        }
-    }
-
-    *outBytes = (workLimit - workText) << 1;
-    *outText = workText;
-    *outBuffer = buffer;
-
-    return true;
-}
-
 // Draws or gets the path of a paragraph of text on a single line, running bidi and shaping.
 // This will draw if canvas is not null, otherwise path must be non-null and it will create
 // a path representing the text that would have been drawn.
 void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len,
                             jint bidiFlags, jfloat x, jfloat y,SkCanvas *canvas, SkPath *path) {
-    const jchar *workText;
-    jchar *buffer = NULL;
-    int32_t workBytes;
-    if (prepareText(paint, text, len, bidiFlags, &workText, &workBytes, &buffer)) {
-        SkScalar x_ = SkFloatToScalar(x);
-        SkScalar y_ = SkFloatToScalar(y);
-        if (canvas) {
-            canvas->drawText(workText, workBytes, x_, y_, *paint);
-        } else {
-            paint->getTextPath(workText, workBytes, x_, y_, path);
-        }
-        free(buffer);
+    sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+    // Return advances from the cache. Compute them if needed
+    value = TextLayoutCache::getInstance().getValue(paint, text, 0, len,
+            len, bidiFlags);
+#else
+    value = new TextLayoutCacheValue(len);
+    TextLayoutEngine::getInstance().computeValues(value.get(), paint,
+            reinterpret_cast<const UChar*>(text), 0, len, len, bidiFlags);
+#endif
+    if (value == NULL) {
+        LOGE("Cannot get TextLayoutCache value for text = '%s'",
+                String8(text, len).string());
+        return ;
     }
-}
-
-bool TextLayout::prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
-        jsize contextCount, jchar* shaped) {
-    UErrorCode status = U_ZERO_ERROR;
-    count = shapeRtlText(context, start, count, contextCount, shaped, status);
-    if (U_SUCCESS(status)) {
-        return true;
+    SkScalar x_ = SkFloatToScalar(x);
+    SkScalar y_ = SkFloatToScalar(y);
+    if (canvas) {
+        canvas->drawText(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, *paint);
     } else {
-        LOGW("drawTextRun error %d\n", status);
+        paint->getTextPath(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, path);
     }
-    return false;
 }
 
-void TextLayout::drawTextRun(SkPaint* paint, const jchar* chars,
-                             jint start, jint count, jint contextCount,
-                             int dirFlags, jfloat x, jfloat y, SkCanvas* canvas) {
-
-     SkScalar x_ = SkFloatToScalar(x);
-     SkScalar y_ = SkFloatToScalar(y);
-
-     uint8_t rtl = dirFlags & 0x1;
-     if (rtl) {
-         SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> buffer(contextCount);
-         if (prepareRtlTextRun(chars, start, count, contextCount, buffer.get())) {
-             canvas->drawText(buffer.get(), count << 1, x_, y_, *paint);
-         }
-     } else {
-         canvas->drawText(chars + start, count << 1, x_, y_, *paint);
-     }
- }
-
 void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
                                     jint count, jint contextCount, jint dirFlags,
                                     jfloat* resultAdvances, jfloat* resultTotalAdvance) {
@@ -260,16 +86,20 @@
     value = TextLayoutCache::getInstance().getValue(paint, chars, start, count,
             contextCount, dirFlags);
 #else
-    value = new TextLayoutCacheValue();
-    value->computeValues(paint, chars, start, count, contextCount, dirFlags);
+    value = new TextLayoutCacheValue(contextCount);
+    TextLayoutEngine::getInstance().computeValues(value.get(), paint,
+            reinterpret_cast<const UChar*>(chars), start, count, contextCount, dirFlags);
 #endif
-    if (value != NULL) {
-        if (resultAdvances) {
-            memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat));
-        }
-        if (resultTotalAdvance) {
-            *resultTotalAdvance = value->getTotalAdvance();
-        }
+    if (value == NULL) {
+        LOGE("Cannot get TextLayoutCache value for text = '%s'",
+                String8(chars + start, count).string());
+        return ;
+    }
+    if (resultAdvances) {
+        memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat));
+    }
+    if (resultTotalAdvance) {
+        *resultTotalAdvance = value->getTotalAdvance();
     }
 }
 
@@ -281,12 +111,6 @@
             resultAdvances, &resultTotalAdvance);
 }
 
-// Draws a paragraph of text on a single line, running bidi and shaping
-void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len,
-                          int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas) {
-    handleText(paint, text, len, bidiFlags, x, y, canvas, NULL);
-}
-
 void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len,
                              jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
     handleText(paint, text, len, bidiFlags, x, y, NULL, path);
@@ -305,14 +129,30 @@
         return;
     }
 
-    SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> buffer(count);
-
-    int dir = kDirection_LTR;
-    UErrorCode status = U_ZERO_ERROR;
-    count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status);
-    if (U_SUCCESS(status)) {
-        canvas->drawTextOnPathHV(buffer.get(), count << 1, *path, h_, v_, *paint);
+    sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+    value = TextLayoutCache::getInstance().getValue(paint, text, 0, count,
+            count, bidiFlags);
+#else
+    value = new TextLayoutCacheValue(count);
+    TextLayoutEngine::getInstance().computeValues(value.get(), paint,
+            reinterpret_cast<const UChar*>(text), 0, count, count, bidiFlags);
+#endif
+    if (value == NULL) {
+        LOGE("Cannot get TextLayoutCache value for text = '%s'",
+                String8(text, count).string());
+        return ;
     }
+
+    // Save old text encoding
+    SkPaint::TextEncoding oldEncoding = paint->getTextEncoding();
+    // Define Glyph encoding
+    paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    canvas->drawTextOnPathHV(value->getGlyphs(), value->getGlyphsCount() * 2, *path, h_, v_, *paint);
+
+    // Get back old encoding
+    paint->setTextEncoding(oldEncoding);
 }
 
 void TextLayout::computeAdvancesWithICU(SkPaint* paint, const UChar* chars,
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index 9df3829..1dabe32 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -62,13 +62,6 @@
 class TextLayout {
 public:
 
-    /*
-     * Draws a unidirectional run of text.
-     */
-    static void drawTextRun(SkPaint* paint, const jchar* chars,
-                            jint start, jint count, jint contextCount,
-                            int dirFlags, jfloat x, jfloat y, SkCanvas* canvas);
-
     static void getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
                                    jint count, jint contextCount, jint dirFlags,
                                    jfloat* resultAdvances, jfloat* resultTotalAdvance);
@@ -77,29 +70,16 @@
                                    jint count, jint contextCount, jint dirFlags,
                                    jfloat* resultAdvances, jfloat& resultTotalAdvance);
 
-    static void drawText(SkPaint* paint, const jchar* text, jsize len,
-                         jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas);
-
     static void getTextPath(SkPaint* paint, const jchar* text, jsize len,
                             jint bidiFlags, jfloat x, jfloat y, SkPath* path);
 
     static void drawTextOnPath(SkPaint* paint, const jchar* text, jsize len,
                                int bidiFlags, jfloat hOffset, jfloat vOffset,
                                SkPath* path, SkCanvas* canvas);
-                               
-    static bool prepareText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags,
-        const jchar** outText, int32_t* outBytes, jchar** outBuffer);
-
-    static bool prepareRtlTextRun(const jchar* context, jsize start, jsize& count,
-        jsize contextCount, jchar* shaped);
-        
 
 private:
     static bool needsLayout(const jchar* text, jint len, jint bidiFlags);
-    static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
-                            jchar* shaped, UErrorCode& status);
-    static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer,
-                           UErrorCode &status);
+
     static void handleText(SkPaint* paint, const jchar* text, jsize len,
                            int bidiFlags, jfloat x, jfloat y, SkCanvas* canvas, SkPath* path);
 
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index f1c3101..81bf4d5 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -34,10 +34,11 @@
 #if USE_TEXT_LAYOUT_CACHE
 
     ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutCache);
-    ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
 
 #endif
 
+    ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
+
 //--------------------------------------------------------------------------------------------------
 
 TextLayoutCache::TextLayoutCache() :
@@ -549,8 +550,8 @@
         LOGD("Shaping Script Run with");
         LOGD("         -- isRTL = %d", isRTL);
         LOGD("         -- HB script = %d", mShaperItem.item.script);
-        LOGD("         -- startFontRun = %d", startScriptRun);
-        LOGD("         -- endFontRun = %d", endScriptRun);
+        LOGD("         -- startFontRun = %d", int(startScriptRun));
+        LOGD("         -- endFontRun = %d", int(endScriptRun));
         LOGD("         -- countFontRun = %d", countScriptRun);
         LOGD("         -- run = '%s'", String8(chars + startScriptRun, countScriptRun).string());
         LOGD("         -- string = '%s'", String8(chars, count).string());
@@ -572,12 +573,12 @@
         if (isRTL) {
             endScriptRun = startScriptRun;
 #if DEBUG_GLYPHS
-            LOGD("Updated endScriptRun = %d", endScriptRun);
+            LOGD("Updated endScriptRun = %d", int(endScriptRun));
 #endif
         } else {
             startScriptRun = endScriptRun;
 #if DEBUG_GLYPHS
-            LOGD("Updated startScriptRun = %d", startScriptRun);
+            LOGD("Updated startScriptRun = %d", int(startScriptRun));
 #endif
         }
 
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index 3b45ccd..f61795d 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -107,6 +107,12 @@
             return status_t(n);
         }
 
+        status_t status = mReceiver.requestNextVsync();
+        if (status) {
+            LOGW("Failed to request next vsync, status=%d", status);
+            return status;
+        }
+
         if (!mFdCallbackRegistered) {
             int rc = mLooper->addFd(mReceiver.getFd(), 0, ALOOPER_EVENT_INPUT,
                     handleReceiveCallback, this);
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 9c7b108..5860658 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -44,7 +44,7 @@
 #include <SkiaColorFilter.h>
 #include <Rect.h>
 
-#include <TextLayout.h>
+#include <TextLayoutCache.h>
 
 namespace android {
 
@@ -487,66 +487,45 @@
 
 static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
         jfloat x, jfloat y, int flags, SkPaint* paint) {
-#if RTL_USE_HARFBUZZ
     sp<TextLayoutCacheValue> value;
 #if USE_TEXT_LAYOUT_CACHE
     value = TextLayoutCache::getInstance().getValue(paint, text, 0, count, count, flags);
     if (value == NULL) {
-        LOGE("Cannot get TextLayoutCache value");
-        return ;
+        LOGE("Cannot get TextLayoutCache value for text = '%s'",
+                String8(text, count).string());
+        return;
     }
 #else
-    value = new TextLayoutCacheValue();
-    value->computeValues(paint, text, 0, count, count, flags);
+    value = new TextLayoutCacheValue(count);
+    TextLayoutEngine::getInstance().computeValues(value.get(), paint,
+            text, 0, count, count, flags);
 #endif
     const jchar* glyphs = value->getGlyphs();
     size_t glyphsCount = value->getGlyphsCount();
     int bytesCount = glyphsCount * sizeof(jchar);
     renderer->drawText((const char*) glyphs, bytesCount, glyphsCount, x, y, paint);
-#else
-    const jchar *workText;
-    jchar* buffer = NULL;
-    int32_t workBytes;
-    if (TextLayout::prepareText(paint, text, count, flags, &workText, &workBytes, &buffer)) {
-        renderer->drawText((const char*) workText, workBytes, count, x, y, paint);
-        free(buffer);
-    }
-#endif
 }
 
 static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
         jint start, jint count, jint contextCount, jfloat x, jfloat y,
         int flags, SkPaint* paint) {
-#if RTL_USE_HARFBUZZ
     sp<TextLayoutCacheValue> value;
 #if USE_TEXT_LAYOUT_CACHE
     value = TextLayoutCache::getInstance().getValue(paint, text, start, count, contextCount, flags);
     if (value == NULL) {
-        LOGE("Cannot get TextLayoutCache value");
-        return ;
+        LOGE("Cannot get TextLayoutCache value for text = '%s'",
+                String8(text + start, count).string());
+        return;
     }
 #else
-    value = new TextLayoutCacheValue();
-    value->computeValues(paint, text, start, count, contextCount, flags);
+    value = new TextLayoutCacheValue(count);
+    TextLayoutEngine::getInstance().computeValues(value.get(), paint,
+            text, start, count, contextCount, flags);
 #endif
     const jchar* glyphs = value->getGlyphs();
     size_t glyphsCount = value->getGlyphsCount();
     int bytesCount = glyphsCount * sizeof(jchar);
     renderer->drawText((const char*) glyphs, bytesCount, glyphsCount, x, y, paint);
-#else
-    uint8_t rtl = flags & 0x1;
-    if (rtl) {
-        SkAutoSTMalloc<80, jchar> buffer(contextCount);
-        jchar* shaped = buffer.get();
-        if (TextLayout::prepareRtlTextRun(text, start, count, contextCount, shaped)) {
-            renderer->drawText((const char*) shaped, count << 1, count, x, y, paint);
-        } else {
-            LOGW("drawTextRun error");
-        }
-    } else {
-        renderer->drawText((const char*) (text + start), count << 1, count, x, y, paint);
-    }
-#endif
 }
 
 static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz,
diff --git a/core/res/res/layout/global_actions_silent_mode.xml b/core/res/res/layout/global_actions_silent_mode.xml
index 09b4341..18b4715 100644
--- a/core/res/res/layout/global_actions_silent_mode.xml
+++ b/core/res/res/layout/global_actions_silent_mode.xml
@@ -26,6 +26,8 @@
         android:layout_width="64dp"
         android:layout_height="match_parent"
         android:background="?android:attr/actionBarItemBackground"
+        android:contentDescription="@string/silent_mode_silent"
+        android:focusable="true"
         >
         <ImageView
             android:layout_width="48dp"
@@ -52,6 +54,8 @@
         android:layout_width="64dp"
         android:layout_height="match_parent"
         android:background="?android:attr/actionBarItemBackground"
+        android:contentDescription="@string/silent_mode_vibrate"
+        android:focusable="true"
         >
         <ImageView
             android:layout_width="48dp"
@@ -79,6 +83,8 @@
         android:layout_width="64dp"
         android:layout_height="match_parent"
         android:background="?android:attr/actionBarItemBackground"
+        android:contentDescription="@string/silent_mode_ring"
+        android:focusable="true"
         >
         <ImageView
             android:layout_width="48dp"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c342c20..3ab72f4 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -750,6 +750,6 @@
 
     <!-- Base "touch slop" value used by ViewConfiguration as a
          movement threshold where scrolling should begin. -->
-    <dimen name="config_viewConfigurationTouchSlop">4dp</dimen>
+    <dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
 
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 7ad1d53..6f115b3 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -286,6 +286,12 @@
     <string name="screen_lock">Screen lock</string>
     <!-- Button to turn off the phone, within the Phone Options dialog -->
     <string name="power_off">Power off</string>
+    <!-- Spoken description for ringer silent option. [CHAR LIMIT=NONE] -->
+    <string name="silent_mode_silent">Ringer off</string>
+    <!-- Spoken description for ringer vibrate option. [CHAR LIMIT=NONE] -->
+    <string name="silent_mode_vibrate">Ringer vibrate</string>
+    <!-- Spoken description for ringer normal option. [CHAR LIMIT=NONE] -->
+    <string name="silent_mode_ring">Ringer on</string>
 
     <!-- Shutdown Progress Dialog. This is shown if the user chooses to power off the phone. -->
     <string name="shutdown_progress">Shutting down\u2026</string>
diff --git a/data/fonts/DroidSansArmenian.ttf b/data/fonts/DroidSansArmenian.ttf
index 62f67e0..6fafa54 100644
--- a/data/fonts/DroidSansArmenian.ttf
+++ b/data/fonts/DroidSansArmenian.ttf
Binary files differ
diff --git a/data/fonts/DroidSansGeorgian.ttf b/data/fonts/DroidSansGeorgian.ttf
index 743ae66..3a2e9fb 100644
--- a/data/fonts/DroidSansGeorgian.ttf
+++ b/data/fonts/DroidSansGeorgian.ttf
Binary files differ
diff --git a/docs/html/shareables/adl/2011Q3_Android_Market_for_Developers.pdf b/docs/html/shareables/adl/2011Q3_Android_Market_for_Developers.pdf
new file mode 100644
index 0000000..598a27e
--- /dev/null
+++ b/docs/html/shareables/adl/2011Q3_Android_Market_for_Developers.pdf
Binary files differ
diff --git a/docs/html/shareables/adl/2011Q3_Designing_UIs_for_Phones_and_Tablets.pdf b/docs/html/shareables/adl/2011Q3_Designing_UIs_for_Phones_and_Tablets.pdf
new file mode 100644
index 0000000..6ef41dd
--- /dev/null
+++ b/docs/html/shareables/adl/2011Q3_Designing_UIs_for_Phones_and_Tablets.pdf
Binary files differ
diff --git a/docs/html/shareables/adl/2011Q3_Introduction_to_Honeycomb_APIs.pdf b/docs/html/shareables/adl/2011Q3_Introduction_to_Honeycomb_APIs.pdf
new file mode 100755
index 0000000..e56d2377
--- /dev/null
+++ b/docs/html/shareables/adl/2011Q3_Introduction_to_Honeycomb_APIs.pdf
Binary files differ
diff --git a/include/gui/DisplayEventReceiver.h b/include/gui/DisplayEventReceiver.h
index 8d07c0e..dccc164 100644
--- a/include/gui/DisplayEventReceiver.h
+++ b/include/gui/DisplayEventReceiver.h
@@ -94,6 +94,20 @@
      */
     ssize_t getEvents(Event* events, size_t count);
 
+    /*
+     * setVsyncRate() sets the Event::VSync delivery rate. A value of
+     * 1 returns every Event::VSync. A value of 2 returns every other event,
+     * etc... a value of 0 returns no event unless  requestNextVsync() has
+     * been called.
+     */
+    status_t setVsyncRate(uint32_t count);
+
+    /*
+     * requestNextVsync() schedules the next Event::VSync. It has no effect
+     * if the vsync rate is > 0.
+     */
+    status_t requestNextVsync();
+
 private:
     sp<IDisplayEventConnection> mEventConnection;
     sp<BitTube> mDataChannel;
diff --git a/include/gui/IDisplayEventConnection.h b/include/gui/IDisplayEventConnection.h
index 8728bb5..86247de 100644
--- a/include/gui/IDisplayEventConnection.h
+++ b/include/gui/IDisplayEventConnection.h
@@ -33,9 +33,27 @@
 class IDisplayEventConnection : public IInterface
 {
 public:
+
     DECLARE_META_INTERFACE(DisplayEventConnection);
 
+    /*
+     * getDataChannel() returns a BitTube where to receive the events from
+     */
     virtual sp<BitTube> getDataChannel() const = 0;
+
+    /*
+     * setVsyncRate() sets the vsync event delivery rate. A value of
+     * 1 returns every vsync events. A value of 2 returns every other events,
+     * etc... a value of 0 returns no event unless  requestNextVsync() has
+     * been called.
+     */
+    virtual void setVsyncRate(uint32_t count) = 0;
+
+    /*
+     * requestNextVsync() schedules the next vsync event. It has no effect
+     * if the vsync rate is > 0.
+     */
+    virtual void requestNextVsync() = 0;    // asynchronous
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/utils/GenerationCache.h b/include/utils/GenerationCache.h
index 83cda86..da85a9a 100644
--- a/include/utils/GenerationCache.h
+++ b/include/utils/GenerationCache.h
@@ -88,11 +88,13 @@
 
     void attachToCache(const sp<Entry<K, V> >& entry);
     void detachFromCache(const sp<Entry<K, V> >& entry);
+
+    const V mNullValue;
 }; // class GenerationCache
 
 template<typename K, typename V>
 GenerationCache<K, V>::GenerationCache(uint32_t maxCapacity): mMaxCapacity(maxCapacity),
-    mListener(NULL) {
+    mListener(NULL), mNullValue(NULL) {
 };
 
 template<typename K, typename V>
@@ -154,7 +156,7 @@
         return entry->value;
     }
 
-    return NULL;
+    return mNullValue;
 }
 
 template<typename K, typename V>
diff --git a/libs/gui/DisplayEventReceiver.cpp b/libs/gui/DisplayEventReceiver.cpp
index 3b29a11..fee1feb 100644
--- a/libs/gui/DisplayEventReceiver.cpp
+++ b/libs/gui/DisplayEventReceiver.cpp
@@ -58,6 +58,26 @@
     return mDataChannel->getFd();
 }
 
+status_t DisplayEventReceiver::setVsyncRate(uint32_t count) {
+    if (int32_t(count) < 0)
+        return BAD_VALUE;
+
+    if (mEventConnection != NULL) {
+        mEventConnection->setVsyncRate(count);
+        return NO_ERROR;
+    }
+    return NO_INIT;
+}
+
+status_t DisplayEventReceiver::requestNextVsync() {
+    if (mEventConnection != NULL) {
+        mEventConnection->requestNextVsync();
+        return NO_ERROR;
+    }
+    return NO_INIT;
+}
+
+
 ssize_t DisplayEventReceiver::getEvents(DisplayEventReceiver::Event* events,
         size_t count) {
     ssize_t size = mDataChannel->read(events, sizeof(events[0])*count);
diff --git a/libs/gui/IDisplayEventConnection.cpp b/libs/gui/IDisplayEventConnection.cpp
index 44127fb..887d176 100644
--- a/libs/gui/IDisplayEventConnection.cpp
+++ b/libs/gui/IDisplayEventConnection.cpp
@@ -32,6 +32,8 @@
 
 enum {
     GET_DATA_CHANNEL = IBinder::FIRST_CALL_TRANSACTION,
+    SET_VSYNC_RATE,
+    REQUEST_NEXT_VSYNC
 };
 
 class BpDisplayEventConnection : public BpInterface<IDisplayEventConnection>
@@ -49,6 +51,19 @@
         remote()->transact(GET_DATA_CHANNEL, data, &reply);
         return new BitTube(reply);
     }
+
+    virtual void setVsyncRate(uint32_t count) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
+        data.writeInt32(count);
+        remote()->transact(SET_VSYNC_RATE, data, &reply);
+    }
+
+    virtual void requestNextVsync() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IDisplayEventConnection::getInterfaceDescriptor());
+        remote()->transact(REQUEST_NEXT_VSYNC, data, &reply, IBinder::FLAG_ONEWAY);
+    }
 };
 
 IMPLEMENT_META_INTERFACE(DisplayEventConnection, "android.gui.DisplayEventConnection");
@@ -65,6 +80,16 @@
             channel->writeToParcel(reply);
             return NO_ERROR;
         } break;
+        case SET_VSYNC_RATE: {
+            CHECK_INTERFACE(IDisplayEventConnection, data, reply);
+            setVsyncRate(data.readInt32());
+            return NO_ERROR;
+        } break;
+        case REQUEST_NEXT_VSYNC: {
+            CHECK_INTERFACE(IDisplayEventConnection, data, reply);
+            requestNextVsync();
+            return NO_ERROR;
+        } break;
     }
     return BBinder::onTransact(code, data, reply, flags);
 }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 8d71dcf..4c70e9d 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1735,6 +1735,9 @@
         /**
          * Called to indicate the video size
          *
+         * The video size (width and height) could be 0 if there was no video,
+         * no display surface was set, or the value was not determined yet.
+         *
          * @param mp        the MediaPlayer associated with this callback
          * @param width     the width of the video
          * @param height    the height of the video
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 759d05a..964cb1f 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -2021,9 +2021,9 @@
 
                 mLock.unlock();
 
-                // Initially make sure we have at least 128 bytes for the sniff
+                // Initially make sure we have at least 192 KB for the sniff
                 // to complete without blocking.
-                static const size_t kMinBytesForSniffing = 128;
+                static const size_t kMinBytesForSniffing = 192 * 1024;
 
                 off64_t metaDataSize = -1ll;
                 for (;;) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEnvReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEnvReverbTest.java
index 3c8d05a..e788c17 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEnvReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEnvReverbTest.java
@@ -353,6 +353,8 @@
         AudioEffect vc = null;
         MediaPlayer mp = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int ringerMode = am.getRingerMode();
+        am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
@@ -411,6 +413,7 @@
                 probe.release();
             }
             am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+            am.setRingerMode(ringerMode);
         }
         assertTrue(msg, result);
     }
@@ -425,6 +428,8 @@
         MediaPlayer mp = null;
         AudioEffect rvb = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int ringerMode = am.getRingerMode();
+        am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
@@ -495,6 +500,7 @@
                 probe.release();
             }
             am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+            am.setRingerMode(ringerMode);
         }
         assertTrue(msg, result);
     }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaPresetReverbTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaPresetReverbTest.java
index 757bbc5..bc9c48d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaPresetReverbTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaPresetReverbTest.java
@@ -198,6 +198,8 @@
         AudioEffect vc = null;
         MediaPlayer mp = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int ringerMode = am.getRingerMode();
+        am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
@@ -254,6 +256,7 @@
                 probe.release();
             }
             am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+            am.setRingerMode(ringerMode);
         }
         assertTrue(msg, result);
     }
@@ -268,6 +271,8 @@
         MediaPlayer mp = null;
         AudioEffect rvb = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int ringerMode = am.getRingerMode();
+        am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
@@ -336,6 +341,7 @@
                 probe.release();
             }
             am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+            am.setRingerMode(ringerMode);
         }
         assertTrue(msg, result);
     }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVisualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVisualizerTest.java
index e0cf51d..b0bf654 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVisualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVisualizerTest.java
@@ -200,6 +200,8 @@
         AudioEffect vc = null;
         MediaPlayer mp = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int ringerMode = am.getRingerMode();
+        am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
@@ -264,6 +266,7 @@
                 vc.release();
             }
             am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+            am.setRingerMode(ringerMode);
         }
         assertTrue(msg, result);
     }
@@ -276,6 +279,8 @@
         AudioEffect vc = null;
         MediaPlayer mp = null;
         AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
+        int ringerMode = am.getRingerMode();
+        am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC,
                            am.getStreamMaxVolume(AudioManager.STREAM_MUSIC),
@@ -393,6 +398,7 @@
                 vc.release();
             }
             am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
+            am.setRingerMode(ringerMode);
         }
         assertTrue(msg, result);
     }
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 1ebed1f..4ea2c31 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -115,6 +115,11 @@
             0x200000038=0x03000701:0x03010701:0x03020701;
     </string>
 
+    <!-- Default for Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION_URL -->
+    <string name="def_accessibility_screen_reader_url" translatable="false">
+        https://ssl.gstatic.com/accessibility/javascript/android/AndroidScriptChooser.user.js
+    </string>
+
     <!-- Default for Settings.Secure.TOUCH_EXPLORATION_ENABLED -->
     <bool name="def_touch_exploration_enabled">false</bool>
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 1fbe08d..a6a3303 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -63,7 +63,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 73;
+    private static final int DATABASE_VERSION = 74;
 
     private Context mContext;
 
@@ -986,6 +986,22 @@
             upgradeVersion = 73;
         }
 
+        if (upgradeVersion == 73) {
+            // URL from which WebView loads a JavaScript based screen-reader.
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT INTO secure(name,value) VALUES(?,?);");
+                loadStringSetting(stmt, Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL,
+                        R.string.def_accessibility_screen_reader_url);
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 74;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -1526,6 +1542,9 @@
 
             loadBooleanSetting(stmt, Settings.Secure.ACCESSIBILITY_SPEAK_PASSWORD,
                     R.bool.def_accessibility_speak_password);
+
+            loadStringSetting(stmt, Settings.Secure.ACCESSIBILITY_SCREEN_READER_URL,
+                    R.string.def_accessibility_screen_reader_url);
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml
index 6cd8899..d51f9c8 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml
@@ -15,20 +15,29 @@
  * limitations under the License.
 -->
 
-<LinearLayout
+<RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:orientation="horizontal"
-    android:gravity="bottom"
     >
 
+    <View
+        android:layout_height="@*android:dimen/status_bar_height"
+        android:layout_width="match_parent"
+        android:background="@drawable/status_bar_ticker_background"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentBottom="true"
+        android:clickable="false"
+        />
+
     <ImageView
         android:id="@+id/large_icon"
         android:layout_width="@android:dimen/notification_large_icon_height"
         android:layout_height="@android:dimen/notification_large_icon_width"
         android:scaleType="center"
         android:visibility="gone"
+        android:layout_alignParentLeft="true"
+        android:layout_alignParentBottom="true"
         />
 
     <FrameLayout
@@ -36,6 +45,8 @@
         android:layout_weight="1"
         android:layout_height="@*android:dimen/status_bar_height"
         android:layout_width="match_parent"
-        android:background="@drawable/status_bar_ticker_background"
+        android:layout_toRightOf="@id/large_icon"
+        android:layout_alignParentBottom="true"
+        android:layout_alignWithParentIfMissing="true"
         />
-</LinearLayout>
+</RelativeLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 490191a..9919d825 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -522,6 +522,16 @@
         // don't wait for these transitions; we just want icons to fade in/out, not move around
         lt.setDuration(LayoutTransition.CHANGE_APPEARING, 0);
         lt.setDuration(LayoutTransition.CHANGE_DISAPPEARING, 0);
+        lt.addTransitionListener(new LayoutTransition.TransitionListener() {
+            public void endTransition(LayoutTransition transition, ViewGroup container,
+                    View view, int transitionType) {
+                // ensure the menu button doesn't stick around on the status bar after it's been
+                // removed
+                mBarContents.invalidate();
+            }
+            public void startTransition(LayoutTransition transition, ViewGroup container,
+                    View view, int transitionType) {}
+        });
         mNavigationArea.setLayoutTransition(lt);
         // no multi-touch on the nav buttons
         mNavigationArea.setMotionEventSplittingEnabled(false);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
index 6045e31..e93a32b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java
@@ -73,6 +73,8 @@
     private StatusBarNotification[] mQueue = new StatusBarNotification[QUEUE_LENGTH];
     private int mQueuePos;
 
+    private final int mLargeIconHeight;
+
     private TabletStatusBar mBar;
 
     private LayoutTransition mLayoutTransition;
@@ -81,6 +83,9 @@
     public TabletTicker(TabletStatusBar bar) {
         mBar = bar;
         mContext = bar.getContext();
+        final Resources res = mContext.getResources();
+        mLargeIconHeight = res.getDimensionPixelSize(
+                android.R.dimen.notification_large_icon_height);
     }
 
     public void add(IBinder key, StatusBarNotification notification) {
@@ -209,8 +214,6 @@
         final Resources res = mContext.getResources();
         final FrameLayout view = new FrameLayout(mContext);
         final int width = res.getDimensionPixelSize(R.dimen.notification_ticker_width);
-        final int height = res.getDimensionPixelSize(
-                android.R.dimen.notification_large_icon_height);
         int windowFlags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                     | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
@@ -219,7 +222,7 @@
         } else {
             windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
         }
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
+        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, mLargeIconHeight,
                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, windowFlags,
                 PixelFormat.TRANSLUCENT);
         lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
@@ -297,6 +300,16 @@
         if (n.largeIcon != null) {
             largeIcon.setImageBitmap(n.largeIcon);
             largeIcon.setVisibility(View.VISIBLE);
+            final ViewGroup.LayoutParams lp = largeIcon.getLayoutParams();
+            final int statusBarHeight = mBar.getStatusBarHeight();
+            if (n.largeIcon.getHeight() <= statusBarHeight) {
+                // for smallish largeIcons, it looks a little odd to have them floating halfway up
+                // the ticker, so we vertically center them in the status bar area instead
+                lp.height = statusBarHeight;
+            } else {
+                lp.height = mLargeIconHeight;
+            }
+            largeIcon.setLayoutParams(lp);
         }
 
         if (CLICKABLE_TICKER) {
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
index 0e2d2a8..38c85bb 100644
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -195,6 +195,7 @@
                 .setInverseBackgroundForced(true);
 
         final AlertDialog dialog = ab.create();
+        dialog.getListView().setItemsCanFocus(true);
         dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
 
         dialog.setOnDismissListener(this);
@@ -518,8 +519,6 @@
         public View create(Context context, View convertView, ViewGroup parent,
                 LayoutInflater inflater) {
             View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
-            // Handle clicks outside the icons and ignore
-            v.setOnClickListener(this);
 
             int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
             for (int i = 0; i < 3; i++) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index dfb1b07..92260d6 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3213,10 +3213,7 @@
             }
 
             final int preferredRotation;
-            if (mHdmiPlugged) {
-                // Ignore sensor when plugged into HDMI.
-                preferredRotation = mHdmiRotation;
-            } else if (mLidOpen == LID_OPEN && mLidOpenRotation >= 0) {
+            if (mLidOpen == LID_OPEN && mLidOpenRotation >= 0) {
                 // Ignore sensor when lid switch is open and rotation is forced.
                 preferredRotation = mLidOpenRotation;
             } else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
@@ -3235,6 +3232,10 @@
                 // enable 180 degree rotation while docked.
                 preferredRotation = mDeskDockEnablesAccelerometer
                         ? sensorRotation : mDeskDockRotation;
+            } else if (mHdmiPlugged) {
+                // Ignore sensor when plugged into HDMI.
+                // Note that the dock orientation overrides the HDMI orientation.
+                preferredRotation = mHdmiRotation;
             } else if ((mAccelerometerDefault != 0 /* implies not rotation locked */
                             && (orientation == ActivityInfo.SCREEN_ORIENTATION_USER
                                     || orientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED))
@@ -3477,6 +3478,9 @@
                             WindowManager.LayoutParams.FLAG_DIM_BEHIND
                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
                     mBootMsgDialog.getWindow().setDimAmount(1);
+                    WindowManager.LayoutParams lp = mBootMsgDialog.getWindow().getAttributes();
+                    lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+                    mBootMsgDialog.getWindow().setAttributes(lp);
                     mBootMsgDialog.setCancelable(false);
                     mBootMsgDialog.show();
                 }
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 2af5103..4f81178 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -24,9 +24,9 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.Intent.FilterComparison;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.Intent.FilterComparison;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
@@ -39,6 +39,8 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -74,6 +76,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 class AppWidgetService extends IAppWidgetService.Stub
 {
@@ -805,6 +808,45 @@
                     id.host.callbacks = null;
                 }
             }
+
+            // If the host is unavailable, then we call the associated
+            // RemoteViewsFactory.onDataSetChanged() directly
+            if (id.host.callbacks == null) {
+                Set<FilterComparison> keys = mRemoteViewsServicesAppWidgets.keySet();
+                for (FilterComparison key : keys) {
+                    if (mRemoteViewsServicesAppWidgets.get(key).contains(id.appWidgetId)) {
+                        Intent intent = key.getIntent();
+
+                        final ServiceConnection conn = new ServiceConnection() {
+                            @Override
+                            public void onServiceConnected(ComponentName name, IBinder service) {
+                                IRemoteViewsFactory cb =
+                                    IRemoteViewsFactory.Stub.asInterface(service);
+                                try {
+                                    cb.onDataSetChangedAsync();
+                                } catch (RemoteException e) {
+                                    e.printStackTrace();
+                                } catch (RuntimeException e) {
+                                    e.printStackTrace();
+                                }
+                                mContext.unbindService(this);
+                            }
+                            @Override
+                            public void onServiceDisconnected(android.content.ComponentName name) {
+                                // Do nothing
+                            }
+                        };
+
+                        // Bind to the service and call onDataSetChanged()
+                        final long token = Binder.clearCallingIdentity();
+                        try {
+                            mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE);
+                        } finally {
+                            Binder.restoreCallingIdentity(token);
+                        }
+                    }
+                }
+            }
         }
     }
 
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index b5edc0a..6c11953 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1932,8 +1932,9 @@
                     // should be left as-is.
                     replyChainEnd = -1;
                 }
-                
-            } else if (target.resultTo != null) {
+
+            } else if (target.resultTo != null && (below == null
+                    || below.task == target.task)) {
                 // If this activity is sending a reply to a previous
                 // activity, we can't do anything with it now until
                 // we reach the start of the reply chain.
@@ -1963,6 +1964,8 @@
                         replyChainEnd = targetI;
                     }
                     ActivityRecord p = null;
+                    if (DEBUG_TASKS) Slog.v(TAG, "Finishing task at index "
+                            + targetI + " to " + replyChainEnd);
                     for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) {
                         p = mHistory.get(srcPos);
                         if (p.finishing) {
@@ -1981,6 +1984,8 @@
                     if (replyChainEnd < 0) {
                         replyChainEnd = targetI;
                     }
+                    if (DEBUG_TASKS) Slog.v(TAG, "Reparenting task at index "
+                            + targetI + " to " + replyChainEnd);
                     for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) {
                         ActivityRecord p = mHistory.get(srcPos);
                         if (p.finishing) {
@@ -2002,6 +2007,7 @@
                         p.setTask(task, null, false);
                         mHistory.add(lastReparentPos, p);
                         if (DEBUG_TASKS) Slog.v(TAG, "Pulling activity " + p
+                                + " from " + srcPos + " to " + lastReparentPos
                                 + " in to resetting task " + task);
                         mService.mWindowManager.moveAppToken(lastReparentPos, p.appToken);
                         mService.mWindowManager.setAppGroupId(p.appToken, p.task.taskId);
@@ -2031,6 +2037,11 @@
                         }
                     }
                 }
+
+            } else if (below != null && below.task != target.task) {
+                // We hit the botton of a task; the reply chain can't
+                // pass through it.
+                replyChainEnd = -1;
             }
             
             target = below;
diff --git a/services/surfaceflinger/DisplayEventConnection.cpp b/services/surfaceflinger/DisplayEventConnection.cpp
index a0aa9c0..77ecbd2 100644
--- a/services/surfaceflinger/DisplayEventConnection.cpp
+++ b/services/surfaceflinger/DisplayEventConnection.cpp
@@ -25,6 +25,7 @@
 
 #include "SurfaceFlinger.h"
 #include "DisplayEventConnection.h"
+#include "EventThread.h"
 
 // ---------------------------------------------------------------------------
 
@@ -33,30 +34,38 @@
 // ---------------------------------------------------------------------------
 
 DisplayEventConnection::DisplayEventConnection(
-        const sp<SurfaceFlinger>& flinger)
-    : mFlinger(flinger), mChannel(new BitTube())
+        const sp<EventThread>& eventThread)
+    : mEventThread(eventThread), mChannel(new BitTube())
 {
 }
 
 DisplayEventConnection::~DisplayEventConnection() {
-    mFlinger->cleanupDisplayEventConnection(this);
+    mEventThread->unregisterDisplayEventConnection(this);
 }
 
 void DisplayEventConnection::onFirstRef() {
-    // nothing to do here for now.
+    // NOTE: mEventThread doesn't hold a strong reference on us
+    mEventThread->registerDisplayEventConnection(this);
 }
 
 sp<BitTube> DisplayEventConnection::getDataChannel() const {
     return mChannel;
 }
 
+void DisplayEventConnection::setVsyncRate(uint32_t count) {
+    mEventThread->setVsyncRate(count, this);
+}
+
+void DisplayEventConnection::requestNextVsync() {
+    mEventThread->requestNextVsync(this);
+}
+
 status_t DisplayEventConnection::postEvent(const DisplayEventReceiver::Event& event)
 {
     ssize_t size = mChannel->write(&event, sizeof(DisplayEventReceiver::Event));
     return size < 0 ? status_t(size) : status_t(NO_ERROR);
 }
 
-
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/DisplayEventConnection.h b/services/surfaceflinger/DisplayEventConnection.h
index 46cf64b..cc3ee36 100644
--- a/services/surfaceflinger/DisplayEventConnection.h
+++ b/services/surfaceflinger/DisplayEventConnection.h
@@ -32,13 +32,13 @@
 // ---------------------------------------------------------------------------
 
 class BitTube;
-class SurfaceFlinger;
+class EventThread;
 
 // ---------------------------------------------------------------------------
 
 class DisplayEventConnection : public BnDisplayEventConnection {
 public:
-    DisplayEventConnection(const sp<SurfaceFlinger>& flinger);
+    DisplayEventConnection(const sp<EventThread>& flinger);
 
     status_t postEvent(const DisplayEventReceiver::Event& event);
 
@@ -46,8 +46,10 @@
     virtual ~DisplayEventConnection();
     virtual void onFirstRef();
     virtual sp<BitTube> getDataChannel() const;
+    virtual void setVsyncRate(uint32_t count);
+    virtual void requestNextVsync();    // asynchronous
 
-    sp<SurfaceFlinger> const mFlinger;
+    sp<EventThread> const mEventThread;
     sp<BitTube> const mChannel;
 };
 
diff --git a/services/surfaceflinger/EventThread.cpp b/services/surfaceflinger/EventThread.cpp
index 42477a9..dc39f88 100644
--- a/services/surfaceflinger/EventThread.cpp
+++ b/services/surfaceflinger/EventThread.cpp
@@ -47,7 +47,8 @@
 status_t EventThread::registerDisplayEventConnection(
         const sp<DisplayEventConnection>& connection) {
     Mutex::Autolock _l(mLock);
-    mDisplayEventConnections.add(connection);
+    ConnectionInfo info;
+    mDisplayEventConnections.add(connection, info);
     mCondition.signal();
     return NO_ERROR;
 }
@@ -55,44 +56,97 @@
 status_t EventThread::unregisterDisplayEventConnection(
         const wp<DisplayEventConnection>& connection) {
     Mutex::Autolock _l(mLock);
-    mDisplayEventConnections.remove(connection);
+    mDisplayEventConnections.removeItem(connection);
     mCondition.signal();
     return NO_ERROR;
 }
 
-status_t EventThread::removeDisplayEventConnection(
+void EventThread::removeDisplayEventConnection(
         const wp<DisplayEventConnection>& connection) {
     Mutex::Autolock _l(mLock);
-    mDisplayEventConnections.remove(connection);
-    return NO_ERROR;
+    mDisplayEventConnections.removeItem(connection);
+}
+
+EventThread::ConnectionInfo* EventThread::getConnectionInfoLocked(
+        const wp<DisplayEventConnection>& connection) {
+    ssize_t index = mDisplayEventConnections.indexOfKey(connection);
+    if (index < 0) return NULL;
+    return &mDisplayEventConnections.editValueAt(index);
+}
+
+void EventThread::setVsyncRate(uint32_t count,
+        const wp<DisplayEventConnection>& connection) {
+    if (int32_t(count) >= 0) { // server must protect against bad params
+        Mutex::Autolock _l(mLock);
+        ConnectionInfo* info = getConnectionInfoLocked(connection);
+        if (info) {
+            info->count = (count == 0) ? -1 : count;
+            mCondition.signal();
+        }
+    }
+}
+
+void EventThread::requestNextVsync(
+        const wp<DisplayEventConnection>& connection) {
+    Mutex::Autolock _l(mLock);
+    ConnectionInfo* info = getConnectionInfoLocked(connection);
+    if (info) {
+        if (info->count < 0) {
+            info->count = 0;
+        }
+        mCondition.signal();
+    }
 }
 
 bool EventThread::threadLoop() {
 
     nsecs_t timestamp;
     DisplayEventReceiver::Event vsync;
-    SortedVector<wp<DisplayEventConnection> > displayEventConnections;
+    KeyedVector< wp<DisplayEventConnection>, ConnectionInfo > displayEventConnections;
 
     { // scope for the lock
         Mutex::Autolock _l(mLock);
         do {
             // wait for listeners
-            while (!mDisplayEventConnections.size()) {
+            do {
+                bool waitForNextVsync = false;
+                size_t count = mDisplayEventConnections.size();
+                for (size_t i=0 ; i<count ; i++) {
+                    const ConnectionInfo& info(
+                            mDisplayEventConnections.valueAt(i));
+                    if (info.count >= 1) {
+                        // continuous mode
+                        waitForNextVsync = true;
+                    } else {
+                        // one-shot event
+                        if (info.count >= -1) {
+                            ConnectionInfo& info(
+                                    mDisplayEventConnections.editValueAt(i));
+                            info.count--;
+                            if (info.count == -1) {
+                                // fired this time around
+                                waitForNextVsync = true;
+                            }
+                        }
+                    }
+                }
+
+                if (waitForNextVsync)
+                    break;
+
                 mCondition.wait(mLock);
-            }
+            } while(true);
 
             // wait for vsync
             mLock.unlock();
             timestamp = mHw.waitForVSync();
             mLock.lock();
+            mDeliveredEvents++;
 
             // make sure we still have some listeners
         } while (!mDisplayEventConnections.size());
 
-
         // dispatch vsync events to listeners...
-        mDeliveredEvents++;
-
         vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC;
         vsync.header.timestamp = timestamp;
         vsync.vsync.count = mDeliveredEvents;
@@ -104,9 +158,30 @@
 
     const size_t count = displayEventConnections.size();
     for (size_t i=0 ; i<count ; i++) {
-        sp<DisplayEventConnection> conn(displayEventConnections.itemAt(i).promote());
+        sp<DisplayEventConnection> conn(displayEventConnections.keyAt(i).promote());
         // make sure the connection didn't die
         if (conn != NULL) {
+
+            const ConnectionInfo& info(
+                    displayEventConnections.valueAt(i));
+
+            if ((info.count > 1) && (mDeliveredEvents % info.count)) {
+                // continuous event, but not time to send this event yet
+                continue;
+            } else if (info.count < -1) {
+                // disabled event
+                continue;
+            } else if (info.count == 0) {
+                // impossible by construction. but we prefer to be safe.
+                continue;
+            }
+
+            // here, either:
+            // count = -1 : one-shot scheduled this time around
+            // count =  1 : continuous not rate-limited
+            // count >  1 : continuous, rate-limited
+            // Note: count == 0 is not possible by construction
+
             status_t err = conn->postEvent(vsync);
             if (err == -EAGAIN || err == -EWOULDBLOCK) {
                 // The destination doesn't accept events anymore, it's probably
@@ -118,12 +193,12 @@
                 // handle any other error on the pipe as fatal. the only
                 // reasonable thing to do is to clean-up this connection.
                 // The most common error we'll get here is -EPIPE.
-                removeDisplayEventConnection(displayEventConnections.itemAt(i));
+                removeDisplayEventConnection(displayEventConnections.keyAt(i));
             }
         } else {
             // somehow the connection is dead, but we still have it in our list
             // just clean the list.
-            removeDisplayEventConnection(displayEventConnections.itemAt(i));
+            removeDisplayEventConnection(displayEventConnections.keyAt(i));
         }
     }
 
diff --git a/services/surfaceflinger/EventThread.h b/services/surfaceflinger/EventThread.h
index 4872c2b..35bd299 100644
--- a/services/surfaceflinger/EventThread.h
+++ b/services/surfaceflinger/EventThread.h
@@ -24,7 +24,7 @@
 
 #include <utils/Errors.h>
 #include <utils/threads.h>
-#include <utils/SortedVector.h>
+#include <utils/KeyedVector.h>
 
 #include "DisplayEventConnection.h"
 
@@ -51,6 +51,11 @@
     status_t unregisterDisplayEventConnection(
             const wp<DisplayEventConnection>& connection);
 
+    void setVsyncRate(uint32_t count,
+            const wp<DisplayEventConnection>& connection);
+
+    void requestNextVsync(const wp<DisplayEventConnection>& connection);
+
     void dump(String8& result, char* buffer, size_t SIZE) const;
 
 private:
@@ -58,7 +63,20 @@
     virtual status_t    readyToRun();
     virtual void        onFirstRef();
 
-    status_t removeDisplayEventConnection(
+    struct ConnectionInfo {
+        ConnectionInfo() : count(-1) { }
+
+        // count >= 1 : continuous event. count is the vsync rate
+        // count == 0 : one-shot event that has not fired
+        // count ==-1 : one-shot event that fired this round / disabled
+        // count ==-2 : one-shot event that fired the round before
+        int32_t count;
+    };
+
+    void removeDisplayEventConnection(
+            const wp<DisplayEventConnection>& connection);
+
+    ConnectionInfo* getConnectionInfoLocked(
             const wp<DisplayEventConnection>& connection);
 
     // constants
@@ -69,7 +87,9 @@
     mutable Condition mCondition;
 
     // protected by mLock
-    SortedVector<wp<DisplayEventConnection> > mDisplayEventConnections;
+    KeyedVector< wp<DisplayEventConnection>, ConnectionInfo > mDisplayEventConnections;
+
+    // main thread only
     size_t mDeliveredEvents;
 };
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 58196d8..014c7e2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -389,16 +389,10 @@
 // ----------------------------------------------------------------------------
 
 sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection() {
-    sp<DisplayEventConnection> result(new DisplayEventConnection(this));
-    mEventThread->registerDisplayEventConnection(result);
+    sp<DisplayEventConnection> result(new DisplayEventConnection(mEventThread));
     return result;
 }
 
-void SurfaceFlinger::cleanupDisplayEventConnection(
-        const wp<DisplayEventConnection>& connection) {
-    mEventThread->unregisterDisplayEventConnection(connection);
-}
-
 // ----------------------------------------------------------------------------
 #if 0
 #pragma mark -
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index e6d2cd9..41caee3 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -335,9 +335,6 @@
             status_t electronBeamOffAnimationImplLocked();
             status_t electronBeamOnAnimationImplLocked();
 
-            void cleanupDisplayEventConnection(
-                    const wp<DisplayEventConnection>& connection);
-
             void        debugFlashRegions();
             void        debugShowFPS() const;
             void        drawWormhole() const;
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index f111dd6..07b6183 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -683,6 +683,13 @@
         mRilConnectedRegistrants.remove(h);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setCurrentPreferredNetworkType() {
+    }
+
     //***** Protected Methods
     /**
      * Store new RadioState and send notification based on the changes
diff --git a/telephony/java/com/android/internal/telephony/CommandsInterface.java b/telephony/java/com/android/internal/telephony/CommandsInterface.java
index 33ead75..d6e6ae0 100644
--- a/telephony/java/com/android/internal/telephony/CommandsInterface.java
+++ b/telephony/java/com/android/internal/telephony/CommandsInterface.java
@@ -1311,6 +1311,12 @@
     void queryAvailableBandMode (Message response);
 
     /**
+     * Set the current preferred network type. This will be the last
+     * networkType that was passed to setPreferredNetworkType.
+     */
+    void setCurrentPreferredNetworkType();
+
+    /**
      *  Requests to set the preferred network type for searching and registering
      * (CS/PS domain, RAT, and operation mode)
      * @param networkType one of  NT_*_TYPE
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 9f93fb8..f2e7f45 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -1823,6 +1823,16 @@
     /**
      * {@inheritDoc}
      */
+    @Override
+    public void setCurrentPreferredNetworkType() {
+        if (RILJ_LOGD) riljLog("setCurrentPreferredNetworkType: " + mSetPreferredNetworkType);
+        setPreferredNetworkType(mSetPreferredNetworkType, null);
+    }
+    private int mSetPreferredNetworkType;
+
+    /**
+     * {@inheritDoc}
+     */
     public void setPreferredNetworkType(int networkType , Message response) {
         RILRequest rr = RILRequest.obtain(
                 RILConstants.RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE, response);
@@ -1830,6 +1840,7 @@
         rr.mp.writeInt(1);
         rr.mp.writeInt(networkType);
 
+        mSetPreferredNetworkType = networkType;
         mPreferredNetworkType = networkType;
 
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index 3d6cd68..d939e98 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -257,6 +257,9 @@
             break;
 
         case EVENT_RUIM_READY:
+            // TODO: Consider calling setCurrentPreferredNetworkType as we do in GsmSST.
+            // cm.setCurrentPreferredNetworkType();
+
             // The RUIM is now ready i.e if it was locked it has been
             // unlocked. At this stage, the radio is already powered on.
             isSubscriptionFromRuim = true;
@@ -277,6 +280,9 @@
             break;
 
         case EVENT_NV_READY:
+            // TODO: Consider calling setCurrentPreferredNetworkType as we do in GsmSST.
+            // cm.setCurrentPreferredNetworkType();
+
             isSubscriptionFromRuim = false;
             // For Non-RUIM phones, the subscription information is stored in
             // Non Volatile. Here when Non-Volatile is ready, we can poll the CDMA
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
index 3799894..16d3129 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmMmiCode.java
@@ -132,7 +132,7 @@
     // See TS 22.030 6.5.2 "Structure of the MMI"
 
     static Pattern sPatternSuppService = Pattern.compile(
-        "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
+        "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)([^#]*)");
 /*       1  2                    3          4  5       6   7         8    9     10  11             12
 
          1 = Full string up to and including #
@@ -141,7 +141,7 @@
          5 = SIA
          7 = SIB
          9 = SIC
-         10 = dialing number
+         10 = dialing number which must not include #, e.g. *SCn*SI#DN format
 */
 
     static final int MATCH_GROUP_POUND_STRING = 1;
@@ -1338,4 +1338,20 @@
      * SpecialCharSequenceMgr class.
      */
 
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder("GsmMmiCode {");
+
+        sb.append("State=" + getState());
+        if (action != null) sb.append(" action=" + action);
+        if (sc != null) sb.append(" sc=" + sc);
+        if (sia != null) sb.append(" sia=" + sia);
+        if (sib != null) sb.append(" sib=" + sib);
+        if (sic != null) sb.append(" sic=" + sic);
+        if (poundString != null) sb.append(" poundString=" + poundString);
+        if (dialingNumber != null) sb.append(" dialingNumber=" + dialingNumber);
+        if (pwd != null) sb.append(" pwd=" + pwd);
+        sb.append("}");
+        return sb.toString();
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index eea2780..84127cf 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -270,6 +270,9 @@
                 break;
 
             case EVENT_SIM_READY:
+                // Set the network type, in case the radio does not restore it.
+                cm.setCurrentPreferredNetworkType();
+
                 // The SIM is now ready i.e if it was locked
                 // it has been unlocked. At this stage, the radio is already
                 // powered on.
diff --git a/tests/StatusBar/res/layout/notification_builder_test.xml b/tests/StatusBar/res/layout/notification_builder_test.xml
index e1199c7..6c384f7 100644
--- a/tests/StatusBar/res/layout/notification_builder_test.xml
+++ b/tests/StatusBar/res/layout/notification_builder_test.xml
@@ -605,6 +605,11 @@
                         style="@style/FieldContents"
                         android:text="pineapple2"
                         />
+                <RadioButton
+                        android:id="@+id/large_icon_small"
+                        style="@style/FieldContents"
+                        android:text="small"
+                        />
             </RadioGroup>
             
 
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
index 5a2ebac..fefd890 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationBuilderTest.java
@@ -287,6 +287,9 @@
             case R.id.large_icon_pineapple2:
                 b.setLargeIcon(loadBitmap(R.drawable.pineapple2));
                 break;
+            case R.id.large_icon_small:
+                b.setLargeIcon(loadBitmap(R.drawable.icon2));
+                break;
         }
 
         // sound TODO
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index 637c27d..d7ac15e 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -625,6 +625,11 @@
             bool actImeService = false;
             bool actWallpaperService = false;
 
+            // These two implement the implicit permissions that are granted
+            // to pre-1.6 applications.
+            bool hasWriteExternalStoragePermission = false;
+            bool hasReadPhoneStatePermission = false;
+
             // This next group of variables is used to implement a group of
             // backward-compatibility heuristics necessitated by the addition of
             // some new uses-feature constants in 2.1 and 2.2. In most cases, the
@@ -986,6 +991,10 @@
                                        name == "android.permission.WRITE_APN_SETTINGS" ||
                                        name == "android.permission.WRITE_SMS") {
                                 hasTelephonyPermission = true;
+                            } else if (name == "android.permission.WRITE_EXTERNAL_STORAGE") {
+                                hasWriteExternalStoragePermission = true;
+                            } else if (name == "android.permission.READ_PHONE_STATE") {
+                                hasReadPhoneStatePermission = true;
                             }
                             printf("uses-permission:'%s'\n", name.string());
                         } else {
@@ -1144,6 +1153,16 @@
                 }
             }
 
+            // Pre-1.6 implicitly granted permission compatibility logic
+            if (targetSdk < 4) {
+                if (!hasWriteExternalStoragePermission) {
+                    printf("uses-permission:'android.permission.WRITE_EXTERNAL_STORAGE'\n");
+                }
+                if (!hasReadPhoneStatePermission) {
+                    printf("uses-permission:'android.permission.READ_PHONE_STATE'\n");
+                }
+            }
+
             /* The following blocks handle printing "inferred" uses-features, based
              * on whether related features or permissions are used by the app.
              * Note that the various spec*Feature variables denote whether the