Merge "Only return default TTS engines from TTS.getEngines()"
diff --git a/api/current.txt b/api/current.txt
index 9d593d8..374c415 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2923,8 +2923,10 @@
     method public abstract android.app.FragmentTransaction add(int, android.app.Fragment);
     method public abstract android.app.FragmentTransaction add(int, android.app.Fragment, java.lang.String);
     method public abstract android.app.FragmentTransaction addToBackStack(java.lang.String);
+    method public abstract android.app.FragmentTransaction attach(android.app.Fragment);
     method public abstract int commit();
     method public abstract int commitAllowingStateLoss();
+    method public abstract android.app.FragmentTransaction detach(android.app.Fragment);
     method public abstract android.app.FragmentTransaction disallowAddToBackStack();
     method public abstract android.app.FragmentTransaction hide(android.app.Fragment);
     method public abstract boolean isAddToBackStackAllowed();
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 850f56a..e5a7980 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -173,6 +173,8 @@
     static final int OP_REMOVE = 3;
     static final int OP_HIDE = 4;
     static final int OP_SHOW = 5;
+    static final int OP_DETACH = 6;
+    static final int OP_ATTACH = 7;
 
     static final class Op {
         Op next;
@@ -416,6 +418,32 @@
         return this;
     }
 
+    public FragmentTransaction detach(Fragment fragment) {
+        //if (fragment.mImmediateActivity == null) {
+        //    throw new IllegalStateException("Fragment not added: " + fragment);
+        //}
+
+        Op op = new Op();
+        op.cmd = OP_DETACH;
+        op.fragment = fragment;
+        addOp(op);
+
+        return this;
+    }
+
+    public FragmentTransaction attach(Fragment fragment) {
+        //if (fragment.mImmediateActivity == null) {
+        //    throw new IllegalStateException("Fragment not added: " + fragment);
+        //}
+
+        Op op = new Op();
+        op.cmd = OP_ATTACH;
+        op.fragment = fragment;
+        addOp(op);
+
+        return this;
+    }
+
     public FragmentTransaction setCustomAnimations(int enter, int exit) {
         return setCustomAnimations(enter, exit, 0, 0);
     }
@@ -589,6 +617,16 @@
                     f.mNextAnim = op.enterAnim;
                     mManager.showFragment(f, mTransition, mTransitionStyle);
                 } break;
+                case OP_DETACH: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.exitAnim;
+                    mManager.detachFragment(f, mTransition, mTransitionStyle);
+                } break;
+                case OP_ATTACH: {
+                    Fragment f = op.fragment;
+                    f.mNextAnim = op.enterAnim;
+                    mManager.attachFragment(f, mTransition, mTransitionStyle);
+                } break;
                 default: {
                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                 }
@@ -655,6 +693,16 @@
                     mManager.hideFragment(f,
                             FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
                 } break;
+                case OP_DETACH: {
+                    Fragment f = op.fragment;
+                    mManager.attachFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+                } break;
+                case OP_ATTACH: {
+                    Fragment f = op.fragment;
+                    mManager.detachFragment(f,
+                            FragmentManagerImpl.reverseTransit(mTransition), mTransitionStyle);
+                } break;
                 default: {
                     throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
                 }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 53dc7c8..a528a1a 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -52,6 +52,7 @@
     final int mContainerId;
     final String mTag;
     final boolean mRetainInstance;
+    final boolean mDetached;
     final Bundle mArguments;
     
     Bundle mSavedFragmentState;
@@ -66,6 +67,7 @@
         mContainerId = frag.mContainerId;
         mTag = frag.mTag;
         mRetainInstance = frag.mRetainInstance;
+        mDetached = frag.mDetached;
         mArguments = frag.mArguments;
     }
     
@@ -77,6 +79,7 @@
         mContainerId = in.readInt();
         mTag = in.readString();
         mRetainInstance = in.readInt() != 0;
+        mDetached = in.readInt() != 0;
         mArguments = in.readBundle();
         mSavedFragmentState = in.readBundle();
     }
@@ -103,6 +106,7 @@
         mInstance.mContainerId = mContainerId;
         mInstance.mTag = mTag;
         mInstance.mRetainInstance = mRetainInstance;
+        mInstance.mDetached = mDetached;
         mInstance.mFragmentManager = activity.mFragments;
         
         return mInstance;
@@ -120,6 +124,7 @@
         dest.writeInt(mContainerId);
         dest.writeString(mTag);
         dest.writeInt(mRetainInstance ? 1 : 0);
+        dest.writeInt(mDetached ? 1 : 0);
         dest.writeBundle(mArguments);
         dest.writeBundle(mSavedFragmentState);
     }
@@ -404,6 +409,9 @@
     // from the user.
     boolean mHidden;
     
+    // Set to true when the app has requested that this fragment be detached.
+    boolean mDetached;
+
     // If set this fragment would like its instance retained across
     // configuration changes.
     boolean mRetainInstance;
@@ -511,23 +519,27 @@
         }
     }
     
-    void restoreViewState() {
+    final void restoreViewState() {
         if (mSavedViewState != null) {
             mView.restoreHierarchyState(mSavedViewState);
             mSavedViewState = null;
         }
     }
     
-    void setIndex(int index) {
+    final void setIndex(int index) {
         mIndex = index;
         mWho = "android:fragment:" + mIndex;
    }
     
-    void clearIndex() {
+    final void clearIndex() {
         mIndex = -1;
         mWho = null;
     }
     
+    final boolean isInBackStack() {
+        return mBackStackNesting > 0;
+    }
+
     /**
      * Subclasses can not override equals().
      */
@@ -1280,6 +1292,7 @@
                 writer.print(" mFromLayout="); writer.print(mFromLayout);
                 writer.print(" mInLayout="); writer.println(mInLayout);
         writer.print(prefix); writer.print("mHidden="); writer.print(mHidden);
+                writer.print(" mDetached="); writer.print(mDetached);
                 writer.print(" mRetainInstance="); writer.print(mRetainInstance);
                 writer.print(" mRetaining="); writer.print(mRetaining);
                 writer.print(" mHasMenu="); writer.println(mHasMenu);
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index ab60cf0..1eaca3b 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -971,32 +971,36 @@
         if (mAdded == null) {
             mAdded = new ArrayList<Fragment>();
         }
-        mAdded.add(fragment);
-        makeActive(fragment);
         if (DEBUG) Log.v(TAG, "add: " + fragment);
-        fragment.mAdded = true;
-        fragment.mRemoving = false;
-        if (fragment.mHasMenu) {
-            mNeedMenuInvalidate = true;
-        }
-        if (moveToStateNow) {
-            moveToState(fragment);
+        makeActive(fragment);
+        if (!fragment.mDetached) {
+            mAdded.add(fragment);
+            fragment.mAdded = true;
+            fragment.mRemoving = false;
+            if (fragment.mHasMenu) {
+                mNeedMenuInvalidate = true;
+            }
+            if (moveToStateNow) {
+                moveToState(fragment);
+            }
         }
     }
     
     public void removeFragment(Fragment fragment, int transition, int transitionStyle) {
         if (DEBUG) Log.v(TAG, "remove: " + fragment + " nesting=" + fragment.mBackStackNesting);
-        mAdded.remove(fragment);
-        final boolean inactive = fragment.mBackStackNesting <= 0;
-        if (fragment.mHasMenu) {
-            mNeedMenuInvalidate = true;
-        }
-        fragment.mAdded = false;
-        fragment.mRemoving = true;
-        moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
-                transition, transitionStyle);
-        if (inactive) {
-            makeInactive(fragment);
+        final boolean inactive = !fragment.isInBackStack();
+        if (!fragment.mDetached || inactive) {
+            mAdded.remove(fragment);
+            if (fragment.mHasMenu) {
+                mNeedMenuInvalidate = true;
+            }
+            fragment.mAdded = false;
+            fragment.mRemoving = true;
+            moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED,
+                    transition, transitionStyle);
+            if (inactive) {
+                makeInactive(fragment);
+            }
         }
     }
     
@@ -1052,6 +1056,39 @@
         }
     }
     
+    public void detachFragment(Fragment fragment, int transition, int transitionStyle) {
+        if (DEBUG) Log.v(TAG, "detach: " + fragment);
+        if (!fragment.mDetached) {
+            fragment.mDetached = true;
+            if (fragment.mAdded) {
+                // We are not already in back stack, so need to remove the fragment.
+                mAdded.remove(fragment);
+                if (fragment.mHasMenu) {
+                    mNeedMenuInvalidate = true;
+                }
+                fragment.mAdded = false;
+                fragment.mRemoving = true;
+                moveToState(fragment, Fragment.CREATED, transition, transitionStyle);
+            }
+        }
+    }
+
+    public void attachFragment(Fragment fragment, int transition, int transitionStyle) {
+        if (DEBUG) Log.v(TAG, "attach: " + fragment);
+        if (fragment.mDetached) {
+            fragment.mDetached = false;
+            if (!fragment.mAdded) {
+                mAdded.add(fragment);
+                fragment.mAdded = true;
+                fragment.mRemoving = false;
+                if (fragment.mHasMenu) {
+                    mNeedMenuInvalidate = true;
+                }
+                moveToState(fragment, mCurState, transition, transitionStyle);
+            }
+        }
+    }
+
     public Fragment findFragmentById(int id) {
         if (mActive != null) {
             // First look through added fragments.
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 68600b3..c1f3cd6 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -87,6 +87,31 @@
     public abstract FragmentTransaction show(Fragment fragment);
 
     /**
+     * Detach the given fragment from the UI.  This is the same state as
+     * when it is put on the back stack: the fragment is removed from
+     * the UI, however its state is still being actively managed by the
+     * fragment manager.  When going into this state its view hierarchy
+     * is destroyed.
+     *
+     * @param fragment The fragment to be detached.
+     *
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public abstract FragmentTransaction detach(Fragment fragment);
+
+    /**
+     * Re-attach a fragment after it had previously been deatched from
+     * the UI with {@link #detach(Fragment)}.  This
+     * causes its view hierarchy to be re-created, attached to the UI,
+     * and displayed.
+     *
+     * @param fragment The fragment to be attached.
+     *
+     * @return Returns the same FragmentTransaction instance.
+     */
+    public abstract FragmentTransaction attach(Fragment fragment);
+
+    /**
      * @return <code>true</code> if this transaction contains no operations,
      * <code>false</code> otherwise.
      */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a675618..2f4a4bb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1505,6 +1505,13 @@
         public static final Uri DEFAULT_ALARM_ALERT_URI = getUriFor(ALARM_ALERT);
 
         /**
+         * Persistent store for the system default media button event receiver.
+         *
+         * @hide
+         */
+        public static final String MEDIA_BUTTON_RECEIVER = "media_button_receiver";
+
+        /**
          * Setting to enable Auto Replace (AutoText) in text editors. 1 = On, 0 = Off
          */
         public static final String TEXT_AUTO_REPLACE = "auto_replace";
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index b2caa98..207c72f 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -756,26 +756,36 @@
         env->ReleaseStringChars(text, textArray);
     }
 
+    static void logGlyphs(sp<TextLayoutCacheValue> value) {
+        LOGD("drawTextWithGlyphs -- got glyphs - count=%d", value->getGlyphsCount());
+        for (size_t i = 0; i < value->getGlyphsCount(); i++) {
+            LOGD("                          glyphs[%d]=%d", i, value->getGlyphs()[i]);
+        }
+    }
+
+    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
+            int start, int end,
+            jfloat x, jfloat y, int flags, SkPaint* paint) {
+
+        jint count = end - start;
+        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
+                paint, textArray, start, count, count, flags);
+        if (value == NULL) {
+            LOGE("drawTextWithGlyphs -- cannot get Cache value");
+            return ;
+        }
+#if DEBUG_GLYPHS
+        logGlyphs(value);
+#endif
+        doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
+                x, y, flags, paint);
+    }
+
     static void drawTextWithGlyphs___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
                                       jcharArray text, int index, int count,
                                       jfloat x, jfloat y, int flags, SkPaint* paint) {
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-#if RTL_USE_HARFBUZZ && USE_TEXT_LAYOUT_CACHE
-        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-                paint, textArray + index, 0, count, count, flags);
-        if (value != NULL) {
-#if DEBUG_GLYPHS
-            LOGD("drawTextWithGlyphs -- got glyphs - count=%d", value->getGlyphsCount());
-            for (size_t i = 0; i < value->getGlyphsCount(); i++) {
-                LOGD("                          glyphs[%d]=%d", i, value->getGlyphs()[i]);
-            }
-#endif
-            doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
-                    x, y, flags, paint);
-        }
-#else
-        TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas);
-#endif
+        drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint);
         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
     }
 
@@ -785,23 +795,7 @@
                                           jfloat x, jfloat y, int flags, SkPaint* paint) {
 
         const jchar* textArray = env->GetStringChars(text, NULL);
-#if RTL_USE_HARFBUZZ && USE_TEXT_LAYOUT_CACHE
-        size_t count = end - start;
-        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-                paint, textArray, start, count, count, flags);
-        if (value != NULL) {
-#if DEBUG_GLYPHS
-            LOGD("drawTextWithGlyphs -- got glyphs - count=%d", value->getGlyphsCount());
-            for (size_t i = 0; i < value->getGlyphsCount(); i++) {
-                LOGD("                          glyphs[%d]=%d", i, value->getGlyphs()[i]);
-            }
-#endif
-            doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
-                    x, y, flags, paint);
-        }
-#else
-        TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas);
-#endif
+        drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint);
         env->ReleaseStringChars(text, textArray);
     }
 
diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h
index a41c91b..4fac89a 100644
--- a/core/jni/android/graphics/RtlProperties.h
+++ b/core/jni/android/graphics/RtlProperties.h
@@ -52,7 +52,7 @@
 #define DEBUG_ADVANCES 0
 
 // Define if we want (1) to have Glyphs debug values or not (0)
-#define DEBUG_GLYPHS 0
+#define DEBUG_GLYPHS 1
 
 } // namespace android
 #endif // ANDROID_RTL_PROPERTIES_H
diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h
index f203b75..9bb1b92 100644
--- a/core/jni/android/graphics/TextLayout.h
+++ b/core/jni/android/graphics/TextLayout.h
@@ -46,27 +46,27 @@
     static TextLayoutCache gTextLayoutCache;
 #endif
 
+enum {
+    kBidi_LTR = 0,
+    kBidi_RTL = 1,
+    kBidi_Default_LTR = 2,
+    kBidi_Default_RTL = 3,
+    kBidi_Force_LTR = 4,
+    kBidi_Force_RTL = 5,
+
+    kBidi_Mask = 0x7
+};
+
+enum {
+    kDirection_LTR = 0,
+    kDirection_RTL = 1,
+
+    kDirection_Mask = 0x1
+};
+
 class TextLayout {
 public:
 
-    enum {
-        kDirection_LTR = 0,
-        kDirection_RTL = 1,
-
-        kDirection_Mask = 0x1
-    };
-
-    enum {
-        kBidi_LTR = 0,
-        kBidi_RTL = 1,
-        kBidi_Default_LTR = 2,
-        kBidi_Default_RTL = 3,
-        kBidi_Force_LTR = 4,
-        kBidi_Force_RTL = 5,
-
-        kBidi_Mask = 0x7
-    };
-
     /*
      * Draws a unidirectional run of text.
      */
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 8db768c..088202e 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -15,6 +15,7 @@
  */
 
 #include "TextLayoutCache.h"
+#include "TextLayout.h"
 
 namespace android {
 
@@ -381,13 +382,127 @@
     }
 }
 
+struct GlyphRun {
+    inline GlyphRun() {}
+    inline GlyphRun(jchar* glyphs, size_t glyphsCount, bool isRTL) :
+            glyphs(glyphs), glyphsCount(glyphsCount), isRTL(isRTL) { }
+    jchar* glyphs;
+    size_t glyphsCount;
+    int isRTL;
+};
+
 void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
         size_t start, size_t count, size_t contextCount, int dirFlags,
         jfloat* outAdvances, jfloat* outTotalAdvance,
         jchar** outGlyphs, size_t* outGlyphsCount) {
+
+        UBiDiLevel bidiReq = 0;
+        bool forceLTR = false;
+        bool forceRTL = false;
+
+        switch (dirFlags) {
+            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: forceLTR = true; break; // every char is LTR
+            case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
+        }
+
+        if (forceLTR || forceRTL) {
+#if DEBUG_GLYPHS
+                    LOGD("computeValuesWithHarfbuzz -- forcing run with LTR=%d RTL=%d",
+                            forceLTR, forceRTL);
+#endif
+            computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
+                    outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
+        } else {
+            UBiDi* bidi = ubidi_open();
+            if (bidi) {
+                UErrorCode status = U_ZERO_ERROR;
+                LOGD("computeValuesWithHarfbuzz -- bidiReq=%d", bidiReq);
+                ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
+                if (U_SUCCESS(status)) {
+                    int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
+                    size_t rc = ubidi_countRuns(bidi, &status);
+#if DEBUG_GLYPHS
+                    LOGD("computeValuesWithHarfbuzz -- dirFlags=%d run-count=%d paraDir=%d", dirFlags, rc, paraDir);
+#endif
+
+                    if (rc == 1 || !U_SUCCESS(status)) {
+                        LOGD("HERE !!!");
+                        computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount,
+                                dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
+                        ubidi_close(bidi);
+                        return;
+                    }
+
+                    size_t runIndex = 0;
+                    Vector<GlyphRun> glyphRuns;
+                    for (size_t i = 0; i < rc; ++i) {
+                        int32_t startRun;
+                        int32_t lengthRun;
+                        UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
+
+                        int newFlags = (runDir == UBIDI_RTL) ? kDirection_RTL : kDirection_LTR;
+                        jfloat runTotalAdvance = 0;
+                        jchar* runGlyphs;
+                        size_t runGlyphsCount = 0;
+
+#if DEBUG_GLYPHS
+                        LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d newFlags=%d",
+                                startRun, lengthRun, newFlags);
+#endif
+                        computeRunValuesWithHarfbuzz(paint, chars, startRun,
+                                lengthRun, contextCount, newFlags,
+                                outAdvances + runIndex, &runTotalAdvance,
+                                &runGlyphs, &runGlyphsCount);
+
+                        runIndex += lengthRun;
+
+                        *outTotalAdvance += runTotalAdvance;
+                        *outGlyphsCount += runGlyphsCount;
+
+#if DEBUG_GLYPHS
+                        LOGD("computeValuesWithHarfbuzz -- run=%d run-glyphs-count=%d",
+                                i, runGlyphsCount);
+                        for (size_t j = 0; j < runGlyphsCount; j++) {
+                            LOGD("                          -- glyphs[%d]=%d", j, runGlyphs[j]);
+                        }
+#endif
+                        glyphRuns.push(GlyphRun(runGlyphs, runGlyphsCount, newFlags));
+                    }
+
+#if DEBUG_GLYPHS
+                    LOGD("computeValuesWithHarfbuzz -- total-glyphs-count=%d", *outGlyphsCount);
+#endif
+                    *outGlyphs = new jchar[*outGlyphsCount];
+                    jchar* glyphs = *outGlyphs;
+                    for (size_t i = 0; i < glyphRuns.size(); i++) {
+                        const GlyphRun& glyphRun = glyphRuns.itemAt(i);
+                        if (glyphRun.isRTL) {
+                            for (size_t n = 0; n < glyphRun.glyphsCount; n++) {
+                                glyphs[glyphRun.glyphsCount - n - 1] = glyphRun.glyphs[n];
+                            }
+                        } else {
+                            memcpy(glyphs, glyphRun.glyphs, glyphRun.glyphsCount * sizeof(jchar));
+                        }
+                        glyphs += glyphRun.glyphsCount;
+                        delete[] glyphRun.glyphs;
+                    }
+                }
+                ubidi_close(bidi);
+            }
+        }
+}
+
+void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+        size_t start, size_t count, size_t contextCount, int dirFlags,
+        jfloat* outAdvances, jfloat* outTotalAdvance,
+        jchar** outGlyphs, size_t* outGlyphsCount) {
+
     bool isRTL = dirFlags & 0x1;
 
-    // TODO: need to run BiDi algo here to breakdown the text in several runs
     HB_ShaperItem shaperItem;
     HB_FontRec font;
     FontData fontData;
@@ -397,7 +512,7 @@
 #if DEBUG_GLYPHS
     LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs,
             shaperItem.kerning_applied);
-    LOGD("         -- string= '%s'", String8(chars, contextCount).string());
+    LOGD("         -- string= '%s'", String8(chars + start, count).string());
     LOGD("         -- isDevKernText=%d", paint->isDevKernText());
 #endif
 
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index e6ce68d..62813df 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -178,6 +178,10 @@
     static void createGlyphArrays(HB_ShaperItem* shaperItem, int size);
     static void resetGlyphArrays(HB_ShaperItem* shaperItem);
 
+    static void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start,
+            size_t count, size_t contextCount, int dirFlags,
+            jfloat* outAdvances, jfloat* outTotalAdvance,
+            jchar** outGlyphs, size_t* outGlyphsCount);
 }; // TextLayoutCacheValue
 
 /**
diff --git a/docs/html/guide/webapps/targeting.jd b/docs/html/guide/webapps/targeting.jd
index bdc2d5e..46f769c 100644
--- a/docs/html/guide/webapps/targeting.jd
+++ b/docs/html/guide/webapps/targeting.jd
@@ -368,14 +368,14 @@
 }
 
 &#64;media screen and (-webkit-device-pixel-ratio: 1.5) {
-    // CSS for high-density screens
+    /* CSS for high-density screens */
     #header {
         background:url(high-density-image.png);
     }
 }
 
 &#64;media screen and (-webkit-device-pixel-ratio: 0.75) {
-    // CSS for low-density screens
+    /* CSS for low-density screens */
     #header {
         background:url(low-density-image.png);
     }
@@ -426,7 +426,7 @@
 <pre>
 if (window.devicePixelRatio == 1.5) {
   alert("This is a high-density screen");
-} else if (window.devicePixelRation == 0.75) {
+} else if (window.devicePixelRatio == 0.75) {
   alert("This is a low-density screen");
 }
 </pre>
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
index 32c9a1d5..bfe13f0 100644
--- a/include/binder/Parcel.h
+++ b/include/binder/Parcel.h
@@ -53,7 +53,8 @@
     
     status_t            setData(const uint8_t* buffer, size_t len);
 
-    status_t            appendFrom(Parcel *parcel, size_t start, size_t len);
+    status_t            appendFrom(const Parcel *parcel,
+                                   size_t start, size_t len);
 
     bool                hasFileDescriptors() const;
 
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 6ed85d7..a0fc4d0 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -353,12 +353,12 @@
     return err;
 }
 
-status_t Parcel::appendFrom(Parcel *parcel, size_t offset, size_t len)
+status_t Parcel::appendFrom(const Parcel *parcel, size_t offset, size_t len)
 {
     const sp<ProcessState> proc(ProcessState::self());
     status_t err;
-    uint8_t *data = parcel->mData;
-    size_t *objects = parcel->mObjects;
+    const uint8_t *data = parcel->mData;
+    const size_t *objects = parcel->mObjects;
     size_t size = parcel->mObjectsSize;
     int startPos = mDataPos;
     int firstIndex = -1, lastIndex = -2;
diff --git a/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp
index 6c5a55b..20aef49 100644
--- a/libs/rs/driver/rsdBcc.cpp
+++ b/libs/rs/driver/rsdBcc.cpp
@@ -381,7 +381,7 @@
     mtls.zEnd = rsMax((uint32_t)1, mtls.zEnd);
     mtls.arrayEnd = rsMax((uint32_t)1, mtls.arrayEnd);
 
-    rsAssert(ain->getType()->getDimZ() == 0);
+    rsAssert(!ain || (ain->getType()->getDimZ() == 0));
 
     Context *mrsc = (Context *)rsc;
     Script * oldTLS = setTLS(s);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 9d0cba3..504cfde 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -111,6 +111,7 @@
     private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
     private static final int MSG_LOAD_SOUND_EFFECTS = 9;
     private static final int MSG_SET_FORCE_USE = 10;
+    private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
 
 
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -355,6 +356,12 @@
         intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
         context.registerReceiver(mReceiver, intentFilter);
 
+        // Register for package removal intent broadcasts for media button receiver persistence
+        IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        pkgFilter.addDataScheme("package");
+        context.registerReceiver(mReceiver, pkgFilter);
+
         // Register for media button intent broadcasts.
         intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
         intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -444,6 +451,9 @@
         // Broadcast vibrate settings
         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
+
+        // Restore the default media button receiver from the system settings
+        restoreMediaButtonReceiver();
     }
 
     private void setStreamVolumeIndex(int stream, int index) {
@@ -1912,6 +1922,11 @@
             }
         }
 
+        private void persistMediaButtonReceiver(ComponentName receiver) {
+            Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
+                    receiver == null ? "" : receiver.flattenToString());
+        }
+
         private void cleanupPlayer(MediaPlayer mp) {
             if (mp != null) {
                 try {
@@ -2022,6 +2037,10 @@
                 case MSG_SET_FORCE_USE:
                     setForceUse(msg.arg1, msg.arg2);
                     break;
+
+                case MSG_PERSIST_MEDIABUTTONRECEIVER:
+                    persistMediaButtonReceiver( (ComponentName) msg.obj );
+                    break;
             }
         }
     }
@@ -2354,6 +2373,14 @@
                 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
                         AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                 mContext.sendStickyBroadcast(newIntent);
+            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
+                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    // a package is being removed, not replaced
+                    String packageName = intent.getData().getSchemeSpecificPart();
+                    if (packageName != null) {
+                        removeMediaButtonReceiverForPackage(packageName);
+                    }
+                }
             }
         }
     }
@@ -2469,7 +2496,7 @@
                 if(fse.mClientId.equals(clientToRemove)) {
                     Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
                             + fse.mClientId);
-                    mFocusStack.remove(fse);
+                    stackIterator.remove();
                 }
             }
         }
@@ -2489,7 +2516,7 @@
             if(fse.mSourceRef.equals(cb)) {
                 Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
                         + fse.mClientId);
-                mFocusStack.remove(fse);
+                stackIterator.remove();
             }
         }
         if (isTopOfStackForClientToRemove) {
@@ -2701,6 +2728,56 @@
 
     /**
      * Helper function:
+     * Remove any entry in the remote control stack that has the same package name as packageName
+     * Pre-condition: packageName != null
+     */
+    private void removeMediaButtonReceiverForPackage(String packageName) {
+        synchronized(mRCStack) {
+            if (mRCStack.empty()) {
+                return;
+            } else {
+                RemoteControlStackEntry oldTop = mRCStack.peek();
+                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                // iterate over the stack entries
+                while(stackIterator.hasNext()) {
+                    RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
+                    if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
+                        // a stack entry is from the package being removed, remove it from the stack
+                        stackIterator.remove();
+                    }
+                }
+                if (mRCStack.empty()) {
+                    // no saved media button receiver
+                    mAudioHandler.sendMessage(
+                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+                                    null));
+                    return;
+                } else if (oldTop != mRCStack.peek()) {
+                    // the top of the stack has changed, save it in the system settings
+                    // by posting a message to persist it
+                    mAudioHandler.sendMessage(
+                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+                                    mRCStack.peek().mReceiverComponent));
+                }
+            }
+        }
+    }
+
+    /**
+     * Helper function:
+     * Restore remote control receiver from the system settings
+     */
+    private void restoreMediaButtonReceiver() {
+        String receiverName = Settings.System.getString(mContentResolver,
+                Settings.System.MEDIA_BUTTON_RECEIVER);
+        if ((null != receiverName) && !receiverName.isEmpty()) {
+            ComponentName receiverComponentName = ComponentName.unflattenFromString(receiverName);
+            registerMediaButtonEventReceiver(receiverComponentName);
+        }
+    }
+
+    /**
+     * Helper function:
      * Set the new remote control receiver at the top of the RC focus stack
      */
     private void pushMediaButtonReceiver(ComponentName newReceiver) {
@@ -2712,11 +2789,15 @@
         while(stackIterator.hasNext()) {
             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
             if(rcse.mReceiverComponent.equals(newReceiver)) {
-                mRCStack.remove(rcse);
+                stackIterator.remove();
                 break;
             }
         }
         mRCStack.push(new RemoteControlStackEntry(newReceiver));
+
+        // post message to persist the default media button receiver
+        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
+                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, newReceiver/*obj*/) );
     }
 
     /**
@@ -2728,7 +2809,7 @@
         while(stackIterator.hasNext()) {
             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
             if(rcse.mReceiverComponent.equals(newReceiver)) {
-                mRCStack.remove(rcse);
+                stackIterator.remove();
                 break;
             }
         }
diff --git a/tests/BiDiTests/AndroidManifest.xml b/tests/BiDiTests/AndroidManifest.xml
index 346ace8..727f980 100644
--- a/tests/BiDiTests/AndroidManifest.xml
+++ b/tests/BiDiTests/AndroidManifest.xml
@@ -25,7 +25,8 @@
     android:versionName="1.0">
 
     <application android:label="BiDiTests">
-        <activity android:name="BiDiTestActivity">
+        <activity android:name="BiDiTestActivity"
+                android:windowSoftInputMode="stateAlwaysHidden">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index 632a02e..d20600e 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -20,9 +20,10 @@
     <string name="normal_long_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
     <string name="normal_long_text_2">nnnnnnnnnnnnnnnnnnnnnnnn</string>
     <string name="normal_long_text_3">Notify me when an open network is available</string>
-    <string name="arabic_text">&#x0644;&#x0627;</string>
+    <string name="arabic_text">&#x0644;&#x0627; &#x0627;&#x0646;&#x0627; hello world</string>
     <string name="chinese_text">利比亚局势或影响美俄关系发展</string>
     <string name="italic_text">Italic String</string>
     <string name="bold_text">Bold String - other text</string>
     <string name="bold_italic_text">Bold Italic String</string>
+    <string name="mixed_text_1">he said in Arabic: &#x0644;&#x0627;. Wow this is cool</string>
 </resources>
\ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
index f00bd06..2f9b026 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java
@@ -49,6 +49,7 @@
     private String BOLD_ITALIC_TEXT;
     private String ARABIC_TEXT;
     private String CHINESE_TEXT;
+    private String MIXED_TEXT_1;
 
     private Typeface typeface;
 
@@ -79,6 +80,7 @@
         BOLD_ITALIC_TEXT = context.getString(R.string.bold_italic_text);
         ARABIC_TEXT = context.getString(R.string.arabic_text);
         CHINESE_TEXT = context.getString(R.string.chinese_text);
+        MIXED_TEXT_1 = context.getString(R.string.mixed_text_1);
 
         typeface = paint.getTypeface();
         paint.setAntiAlias(true);
@@ -124,6 +126,10 @@
         // Test Chinese
         deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 10 * currentTextSize,
                 paint, typeface, false, false,  Paint.DIRECTION_LTR, currentTextSize);
+
+        // Test Mixed (English and Arabic)
+        deltaX = testString(canvas, MIXED_TEXT_1, ORIGIN, ORIGIN + 12 * currentTextSize,
+                paint, typeface, false, false,  Paint.DIRECTION_LTR, currentTextSize);
     }
 
     private int testString(Canvas canvas, String text, int x, int y, Paint paint, Typeface typeface,
@@ -139,12 +145,15 @@
             paint.setTextSkewX(DEFAULT_ITALIC_SKEW_X);
         }
 
-        drawTextWithCanvasDrawText(text, canvas, x, y, textSize, Color.WHITE);
+        Log.v(TAG, "START -- drawTextWithCanvasDrawText");
+        drawTextWithCanvasDrawText(text, canvas, x, y, textSize, Color.WHITE, dir);
+        Log.v(TAG, "END   -- drawTextWithCanvasDrawText");
 
         int length = text.length();
         float[] advances = new float[length];
-        float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, 0, advances, 0);
-        float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, 0, advances, 0);
+        float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0);
+        setPaintDir(paint, dir);
+        float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, dir, advances, 0);
 
         logAdvances(text, textWidthHB, textWidthICU, advances);
         drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN);
@@ -156,7 +165,9 @@
 //        logGlypths(glyphs, count);
 //        drawTextWithDrawGlyph(canvas, glyphs, count, x, y + currentTextSize);
 
+        Log.v(TAG, "START -- drawTextWithGlyphs");
         drawTextWithGlyphs(canvas, text, x, y + currentTextSize, dir);
+        Log.v(TAG, "END   -- drawTextWithGlyphs");
 
         // Restore old paint properties
         paint.setFakeBoldText(oldFakeBold);
@@ -165,12 +176,17 @@
         return (int) Math.ceil(textWidthHB) + TEXT_PADDING;
     }
 
+    private void setPaintDir(Paint paint, int dir) {
+        Log.v(TAG, "Setting Paint dir=" + dir);
+        paint.setBidiFlags(dir);
+    }
+
     private void drawTextWithDrawGlyph(Canvas canvas, char[] glyphs, int count, int x, int y) {
         canvas.drawGlyphs(glyphs, 0, count, x, y, paint);
     }
 
     private void drawTextWithGlyphs(Canvas canvas, String text, int x, int y, int dir) {
-        paint.setBidiFlags(dir);
+        setPaintDir(paint, dir);
         canvas.drawTextWithGlyphs(text, x, y, paint);
     }
 
@@ -182,7 +198,6 @@
     }
 
     private int getGlyphs(String text, char[] glyphs, int dir) {
-//        int dir = 1; // Paint.DIRECTION_LTR;
         return paint.getTextGlypths(text, 0, text.length(), 0, text.length(), dir, glyphs);
     }
 
@@ -195,7 +210,8 @@
     }
 
     private void drawTextWithCanvasDrawText(String text, Canvas canvas,
-            float x, float y, float textSize, int color) {
+            float x, float y, float textSize, int color, int dir) {
+        setPaintDir(paint, dir);
         paint.setColor(color);
         paint.setTextSize(textSize);
         canvas.drawText(text, x, y, paint);