Merge "Merge chromium to 10.0.634.0: Compile fix"
diff --git a/Android.mk b/Android.mk
index 4a994ed..e3406ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -114,6 +114,7 @@
 	core/java/android/nfc/ILlcpConnectionlessSocket.aidl \
 	core/java/android/nfc/ILlcpServiceSocket.aidl \
 	core/java/android/nfc/ILlcpSocket.aidl \
+	core/java/android/nfc/INdefPushCallback.aidl \
 	core/java/android/nfc/INfcAdapter.aidl \
 	core/java/android/nfc/INfcAdapterExtras.aidl \
 	core/java/android/nfc/INfcTag.aidl \
@@ -161,6 +162,7 @@
 	core/java/com/android/internal/view/IInputMethodSession.aidl \
 	core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \
 	core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \
+	keystore/java/android/security/IKeyChainAliasResponse.aidl \
 	keystore/java/android/security/IKeyChainService.aidl \
 	location/java/android/location/ICountryDetector.aidl \
 	location/java/android/location/ICountryListener.aidl \
@@ -362,7 +364,7 @@
 		-werror -hide 113 \
 		-overview $(LOCAL_PATH)/core/java/overview.html
 
-framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= $(call intermediates-dir-for,JAVA_LIBRARIES,framework)
+framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= $(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
 
 framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
     frameworks/base/docs/knowntags.txt
@@ -585,7 +587,7 @@
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
 LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
-LOCAL_ADDITIONAL_JAVA_DIR:=$(call intermediates-dir-for,JAVA_LIBRARIES,framework)
+LOCAL_ADDITIONAL_JAVA_DIR:=$(framework_docs_LOCAL_ADDITIONAL_JAVA_DIR)
 LOCAL_ADDITIONAL_DEPENDENCIES:=$(framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES)
 
 LOCAL_MODULE := hidden
diff --git a/api/current.txt b/api/current.txt
index 3339497..6854965 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -866,6 +866,7 @@
     field public static final int subtitleTextStyle = 16843513; // 0x10102f9
     field public static final int suggestActionMsg = 16843228; // 0x10101dc
     field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd
+    field public static final int suggestionsEnabled = 16843630; // 0x101036e
     field public static final int summary = 16843241; // 0x10101e9
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
@@ -1977,6 +1978,7 @@
     method public boolean isRunning();
     method public void removeChild(android.view.ViewGroup, android.view.View);
     method public void removeTransitionListener(android.animation.LayoutTransition.TransitionListener);
+    method public void setAnimateParentHierarchy(boolean);
     method public void setAnimator(int, android.animation.Animator);
     method public void setDuration(long);
     method public void setDuration(int, long);
@@ -6003,6 +6005,7 @@
     field public static final int UI_MODE_TYPE_DESK = 2; // 0x2
     field public static final int UI_MODE_TYPE_MASK = 15; // 0xf
     field public static final int UI_MODE_TYPE_NORMAL = 1; // 0x1
+    field public static final int UI_MODE_TYPE_TELEVISION = 4; // 0x4
     field public static final int UI_MODE_TYPE_UNDEFINED = 0; // 0x0
     field public float fontScale;
     field public int hardKeyboardHidden;
@@ -9684,7 +9687,8 @@
     method public void unloadSoundEffects();
     method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
     field public static final java.lang.String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";
-    field public static final java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
+    field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
+    field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
     field public static final int ADJUST_LOWER = -1; // 0xffffffff
     field public static final int ADJUST_RAISE = 1; // 0x1
     field public static final int ADJUST_SAME = 0; // 0x0
@@ -9697,6 +9701,7 @@
     field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
     field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
     field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE";
+    field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
     field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE";
     field public static final java.lang.String EXTRA_VIBRATE_SETTING = "android.media.EXTRA_VIBRATE_SETTING";
     field public static final java.lang.String EXTRA_VIBRATE_TYPE = "android.media.EXTRA_VIBRATE_TYPE";
@@ -9733,6 +9738,7 @@
     field public static final deprecated int ROUTE_HEADSET = 8; // 0x8
     field public static final deprecated int ROUTE_SPEAKER = 2; // 0x2
     field public static final int SCO_AUDIO_STATE_CONNECTED = 1; // 0x1
+    field public static final int SCO_AUDIO_STATE_CONNECTING = 2; // 0x2
     field public static final int SCO_AUDIO_STATE_DISCONNECTED = 0; // 0x0
     field public static final int SCO_AUDIO_STATE_ERROR = -1; // 0xffffffff
     field public static final int STREAM_ALARM = 4; // 0x4
@@ -11641,6 +11647,7 @@
     method public void disableForegroundNdefPush(android.app.Activity);
     method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], java.lang.String[][]);
     method public void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage);
+    method public void enableForegroundNdefPush(android.app.Activity, android.nfc.NfcAdapter.NdefPushCallback);
     method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context);
     method public static deprecated android.nfc.NfcAdapter getDefaultAdapter();
     method public boolean isEnabled();
@@ -11652,6 +11659,11 @@
     field public static final java.lang.String EXTRA_TAG = "android.nfc.extra.TAG";
   }
 
+  public static abstract interface NfcAdapter.NdefPushCallback {
+    method public abstract android.nfc.NdefMessage createMessage();
+    method public abstract void onMessagePushed();
+  }
+
   public final class NfcManager {
     method public android.nfc.NfcAdapter getDefaultAdapter();
   }
@@ -21433,6 +21445,8 @@
     method public void setScaleY(float);
     method public void setScrollBarStyle(int);
     method public void setScrollContainer(boolean);
+    method public void setScrollX(int);
+    method public void setScrollY(int);
     method public void setScrollbarFadingEnabled(boolean);
     method public void setSelected(boolean);
     method public void setSoundEffectsEnabled(boolean);
@@ -21850,6 +21864,9 @@
   public class ViewPropertyAnimator {
     method public android.view.ViewPropertyAnimator alpha(float);
     method public android.view.ViewPropertyAnimator alphaBy(float);
+    method public void cancel();
+    method public long getDuration();
+    method public long getStartDelay();
     method public android.view.ViewPropertyAnimator rotation(float);
     method public android.view.ViewPropertyAnimator rotationBy(float);
     method public android.view.ViewPropertyAnimator rotationX(float);
@@ -21863,6 +21880,8 @@
     method public android.view.ViewPropertyAnimator setDuration(long);
     method public android.view.ViewPropertyAnimator setInterpolator(android.animation.TimeInterpolator);
     method public android.view.ViewPropertyAnimator setListener(android.animation.Animator.AnimatorListener);
+    method public android.view.ViewPropertyAnimator setStartDelay(long);
+    method public void start();
     method public android.view.ViewPropertyAnimator translationX(float);
     method public android.view.ViewPropertyAnimator translationXBy(float);
     method public android.view.ViewPropertyAnimator translationY(float);
@@ -25207,6 +25226,7 @@
     method public final android.content.res.ColorStateList getLinkTextColors();
     method public final boolean getLinksClickable();
     method public final android.text.method.MovementMethod getMovementMethod();
+    method public int getOffsetForPosition(float, float);
     method public android.text.TextPaint getPaint();
     method public int getPaintFlags();
     method public java.lang.String getPrivateImeOptions();
@@ -25227,6 +25247,7 @@
     method public android.text.style.URLSpan[] getUrls();
     method public boolean hasSelection();
     method public boolean isInputMethodTarget();
+    method public boolean isSuggestionsEnabled();
     method public boolean isTextSelectable();
     method public int length();
     method public boolean moveCursorToVisibleOffset();
@@ -25298,6 +25319,7 @@
     method public void setSingleLine();
     method public void setSingleLine(boolean);
     method public final void setSpannableFactory(android.text.Spannable.Factory);
+    method public void setSuggestionsEnabled(boolean);
     method public final void setText(java.lang.CharSequence);
     method public void setText(java.lang.CharSequence, android.widget.TextView.BufferType);
     method public final void setText(char[], int, int);
diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp
index 371268f..152a7cb 100644
--- a/cmds/app_process/app_main.cpp
+++ b/cmds/app_process/app_main.cpp
@@ -149,10 +149,7 @@
     mArgLen--;
 
     AppRuntime runtime;
-    const char *arg;
-    const char *argv0;
-
-    argv0 = argv[0];
+    const char* argv0 = argv[0];
 
     // Process command line arguments
     // ignore argv[0]
@@ -163,39 +160,53 @@
 
     int i = runtime.addVmArguments(argc, argv);
 
-    // Next arg is parent directory
-    if (i < argc) {
-        runtime.mParentDir = argv[i++];
+    // Parse runtime arguments.  Stop at first unrecognized option.
+    bool zygote = false;
+    bool startSystemServer = false;
+    bool application = false;
+    const char* parentDir = NULL;
+    const char* niceName = NULL;
+    const char* className = NULL;
+    while (i < argc) {
+        const char* arg = argv[i++];
+        if (!parentDir) {
+            parentDir = arg;
+        } else if (strcmp(arg, "--zygote") == 0) {
+            zygote = true;
+            niceName = "zygote";
+        } else if (strcmp(arg, "--start-system-server") == 0) {
+            startSystemServer = true;
+        } else if (strcmp(arg, "--application") == 0) {
+            application = true;
+        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
+            niceName = arg + 12;
+        } else {
+            className = arg;
+            break;
+        }
     }
 
-    // Next arg is startup classname or "--zygote"
-    if (i < argc) {
-        arg = argv[i++];
-        if (0 == strcmp("--zygote", arg)) {
-            bool startSystemServer = (i < argc) ?
-                    strcmp(argv[i], "--start-system-server") == 0 : false;
-            setArgv0(argv0, "zygote");
-            set_process_name("zygote");
-            runtime.start("com.android.internal.os.ZygoteInit",
-                startSystemServer);
-        } else {
-            set_process_name(argv0);
+    if (niceName && *niceName) {
+        setArgv0(argv0, niceName);
+        set_process_name(niceName);
+    }
 
-            runtime.mClassName = arg;
+    runtime.mParentDir = parentDir;
 
-            // Remainder of args get passed to startup class main()
-            runtime.mArgC = argc-i;
-            runtime.mArgV = argv+i;
-
-            LOGV("App process is starting with pid=%d, class=%s.\n",
-                 getpid(), runtime.getClassName());
-            runtime.start();
-        }
+    if (zygote) {
+        runtime.start("com.android.internal.os.ZygoteInit",
+                startSystemServer ? "start-system-server" : "");
+    } else if (className) {
+        // Remainder of args get passed to startup class main()
+        runtime.mClassName = className;
+        runtime.mArgC = argc - i;
+        runtime.mArgV = argv + i;
+        runtime.start("com.android.internal.os.RuntimeInit",
+                application ? "application" : "tool");
     } else {
         fprintf(stderr, "Error: no class name or --zygote supplied.\n");
         app_usage();
         LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
         return 10;
     }
-
 }
diff --git a/cmds/runtime/main_runtime.cpp b/cmds/runtime/main_runtime.cpp
index 785e4cc..dbff095 100644
--- a/cmds/runtime/main_runtime.cpp
+++ b/cmds/runtime/main_runtime.cpp
@@ -497,7 +497,7 @@
 #ifndef HAVE_ANDROID_OS
         QuickRuntime* runt = new QuickRuntime();
         runt->start("com/android/server/SystemServer",
-                    false /* spontaneously fork system server from zygote */);
+                    "" /* spontaneously fork system server from zygote */);
 #endif
     }
 
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index adfda8e..d25de97 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -18,6 +18,7 @@
 
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewParent;
 import android.view.ViewTreeObserver;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
@@ -70,8 +71,9 @@
  * moving as a result of the layout event) as well as the values that are changing (such as the
  * position and size of that object). The actual values that are pushed to each animation
  * depends on what properties are specified for the animation. For example, the default
- * CHANGE_APPEARING animation animates <code>left</code>, <code>top</code>, <code>right</code>,
- * and <code>bottom</code>. Values for these properties are updated with the pre- and post-layout
+ * CHANGE_APPEARING animation animates the <code>left</code>, <code>top</code>, <code>right</code>,
+ * <code>bottom</code>, <code>scrollX</code>, and <code>scrollY</code> properties.
+ * Values for these properties are updated with the pre- and post-layout
  * values when the transition begins. Custom animations will be similarly populated with
  * the target and values being animated, assuming they use ObjectAnimator objects with
  * property names that are known on the target object.</p>
@@ -210,6 +212,14 @@
      */
     private ArrayList<TransitionListener> mListeners;
 
+    /**
+     * Controls whether changing animations automatically animate the parent hierarchy as well.
+     * This behavior prevents artifacts when wrap_content layouts snap to the end state as the
+     * transition begins, causing visual glitches and clipping.
+     * Default value is true.
+     */
+    private boolean mAnimateParentHierarchy = true;
+
 
     /**
      * Constructs a LayoutTransition object. By default, the object will listen to layout
@@ -223,14 +233,17 @@
             PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
             PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1);
             PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1);
+            PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1);
+            PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1);
             defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder(this,
-                    pvhLeft, pvhTop, pvhRight, pvhBottom);
+                    pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY);
             defaultChangeIn.setDuration(DEFAULT_DURATION);
             defaultChangeIn.setStartDelay(mChangingAppearingDelay);
             defaultChangeIn.setInterpolator(mChangingAppearingInterpolator);
             defaultChangeOut = defaultChangeIn.clone();
             defaultChangeOut.setStartDelay(mChangingDisappearingDelay);
             defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator);
+
             defaultFadeIn = ObjectAnimator.ofFloat(this, "alpha", 0f, 1f);
             defaultFadeIn.setDuration(DEFAULT_DURATION);
             defaultFadeIn.setStartDelay(mAppearingDelay);
@@ -572,122 +585,24 @@
 
             // only animate the views not being added or removed
             if (child != newView) {
-
-
-                // Make a copy of the appropriate animation
-                final Animator anim = baseAnimator.clone();
-
-                // Set the target object for the animation
-                anim.setTarget(child);
-
-                // A ObjectAnimator (or AnimatorSet of them) can extract start values from
-                // its target object
-                anim.setupStartValues();
-
-                // If there's an animation running on this view already, cancel it
-                Animator currentAnimation = pendingAnimations.get(child);
-                if (currentAnimation != null) {
-                    currentAnimation.cancel();
-                    pendingAnimations.remove(child);
-                }
-                // Cache the animation in case we need to cancel it later
-                pendingAnimations.put(child, anim);
-
-                // For the animations which don't get started, we have to have a means of
-                // removing them from the cache, lest we leak them and their target objects.
-                // We run an animator for the default duration+100 (an arbitrary time, but one
-                // which should far surpass the delay between setting them up here and
-                // handling layout events which start them.
-                ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f).
-                        setDuration(duration+100);
-                pendingAnimRemover.addListener(new AnimatorListenerAdapter() {
-                    @Override
-                    public void onAnimationEnd(Animator animation) {
-                        pendingAnimations.remove(child);
-                    }
-                });
-                pendingAnimRemover.start();
-
-                // Add a listener to track layout changes on this view. If we don't get a callback,
-                // then there's nothing to animate.
-                final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() {
-                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
-                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
-
-                        // Tell the animation to extract end values from the changed object
-                        anim.setupEndValues();
-
-                        long startDelay;
-                        if (changeReason == APPEARING) {
-                            startDelay = mChangingAppearingDelay + staggerDelay;
-                            staggerDelay += mChangingAppearingStagger;
-                        } else {
-                            startDelay = mChangingDisappearingDelay + staggerDelay;
-                            staggerDelay += mChangingDisappearingStagger;
-                        }
-                        anim.setStartDelay(startDelay);
-                        anim.setDuration(duration);
-
-                        Animator prevAnimation = currentChangingAnimations.get(child);
-                        if (prevAnimation != null) {
-                            prevAnimation.cancel();
-                        }
-                        Animator pendingAnimation = pendingAnimations.get(child);
-                        if (pendingAnimation != null) {
-                            pendingAnimations.remove(child);
-                        }
-                        // Cache the animation in case we need to cancel it later
-                        currentChangingAnimations.put(child, anim);
-
-                        if (anim instanceof ObjectAnimator) {
-                            ((ObjectAnimator) anim).setCurrentPlayTime(0);
-                        }
-                        anim.start();
-
-                        // this only removes listeners whose views changed - must clear the
-                        // other listeners later
-                        child.removeOnLayoutChangeListener(this);
-                        layoutChangeListenerMap.remove(child);
-                    }
-                };
-                // Remove the animation from the cache when it ends
-                anim.addListener(new AnimatorListenerAdapter() {
-
-                    @Override
-                    public void onAnimationStart(Animator animator) {
-                        if (mListeners != null) {
-                            for (TransitionListener listener : mListeners) {
-                                listener.startTransition(LayoutTransition.this, parent, child,
-                                        changeReason == APPEARING ?
-                                                CHANGE_APPEARING : CHANGE_DISAPPEARING);
-                            }
-                        }
-                    }
-
-                    @Override
-                    public void onAnimationCancel(Animator animator) {
-                        child.removeOnLayoutChangeListener(listener);
-                        layoutChangeListenerMap.remove(child);
-                    }
-
-                    @Override
-                    public void onAnimationEnd(Animator animator) {
-                        currentChangingAnimations.remove(child);
-                        if (mListeners != null) {
-                            for (TransitionListener listener : mListeners) {
-                                listener.endTransition(LayoutTransition.this, parent, child,
-                                        changeReason == APPEARING ?
-                                                CHANGE_APPEARING : CHANGE_DISAPPEARING);
-                            }
-                        }
-                    }
-                });
-
-                child.addOnLayoutChangeListener(listener);
-                // cache the listener for later removal
-                layoutChangeListenerMap.put(child, listener);
+                setupChangeAnimation(parent, changeReason, baseAnimator, duration, child);
             }
         }
+        if (mAnimateParentHierarchy) {
+            ViewGroup tempParent = parent;
+            while (tempParent != null) {
+                ViewParent parentParent = tempParent.getParent();
+                if (parentParent instanceof ViewGroup) {
+                    setupChangeAnimation((ViewGroup)parentParent, changeReason, baseAnimator,
+                            duration, tempParent);
+                    tempParent = (ViewGroup) parentParent;
+                } else {
+                    tempParent = null;
+                }
+
+            }
+        }
+
         // This is the cleanup step. When we get this rendering event, we know that all of
         // the appropriate animations have been set up and run. Now we can clear out the
         // layout listeners.
@@ -706,6 +621,175 @@
     }
 
     /**
+     * This flag controls whether CHANGE_APPEARING or CHANGE_DISAPPEARING animations will
+     * cause the same changing animation to be run on the parent hierarchy as well. This allows
+     * containers of transitioning views to also transition, which may be necessary in situations
+     * where the containers bounds change between the before/after states and may clip their
+     * children during the transition animations. For example, layouts with wrap_content will
+     * adjust their bounds according to the dimensions of their children.
+     *
+     * @param animateParentHierarchy A boolean value indicating whether the parents of
+     * transitioning views should also be animated during the transition. Default value is true.
+     */
+    public void setAnimateParentHierarchy(boolean animateParentHierarchy) {
+        mAnimateParentHierarchy = animateParentHierarchy;
+    }
+
+    /**
+     * Utility function called by runChangingTransition for both the children and the parent
+     * hierarchy.
+     */
+    private void setupChangeAnimation(final ViewGroup parent, final int changeReason,
+            Animator baseAnimator, final long duration, final View child) {
+        // Make a copy of the appropriate animation
+        final Animator anim = baseAnimator.clone();
+
+        // Set the target object for the animation
+        anim.setTarget(child);
+
+        // A ObjectAnimator (or AnimatorSet of them) can extract start values from
+        // its target object
+        anim.setupStartValues();
+
+        // If there's an animation running on this view already, cancel it
+        Animator currentAnimation = pendingAnimations.get(child);
+        if (currentAnimation != null) {
+            currentAnimation.cancel();
+            pendingAnimations.remove(child);
+        }
+        // Cache the animation in case we need to cancel it later
+        pendingAnimations.put(child, anim);
+
+        // For the animations which don't get started, we have to have a means of
+        // removing them from the cache, lest we leak them and their target objects.
+        // We run an animator for the default duration+100 (an arbitrary time, but one
+        // which should far surpass the delay between setting them up here and
+        // handling layout events which start them.
+        ValueAnimator pendingAnimRemover = ValueAnimator.ofFloat(0f, 1f).
+                setDuration(duration + 100);
+        pendingAnimRemover.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                pendingAnimations.remove(child);
+            }
+        });
+        pendingAnimRemover.start();
+
+        // Add a listener to track layout changes on this view. If we don't get a callback,
+        // then there's nothing to animate.
+        final View.OnLayoutChangeListener listener = new View.OnLayoutChangeListener() {
+            public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                    int oldLeft, int oldTop, int oldRight, int oldBottom) {
+
+                // Tell the animation to extract end values from the changed object
+                anim.setupEndValues();
+                if (anim instanceof ValueAnimator) {
+                    boolean valuesDiffer = false;
+                    ValueAnimator valueAnim = (ValueAnimator)anim;
+                    PropertyValuesHolder[] oldValues = valueAnim.getValues();
+                    for (int i = 0; i < oldValues.length; ++i) {
+                        PropertyValuesHolder pvh = oldValues[i];
+                        KeyframeSet keyframeSet = pvh.mKeyframeSet;
+                        if (keyframeSet.mFirstKeyframe == null ||
+                                keyframeSet.mLastKeyframe == null ||
+                                !keyframeSet.mFirstKeyframe.getValue().equals(
+                                keyframeSet.mLastKeyframe.getValue())) {
+                            valuesDiffer = true;
+                        }
+                    }
+                    if (!valuesDiffer) {
+                        return;
+                    }
+                }
+
+                long startDelay;
+                if (changeReason == APPEARING) {
+                    startDelay = mChangingAppearingDelay + staggerDelay;
+                    staggerDelay += mChangingAppearingStagger;
+                } else {
+                    startDelay = mChangingDisappearingDelay + staggerDelay;
+                    staggerDelay += mChangingDisappearingStagger;
+                }
+                anim.setStartDelay(startDelay);
+                anim.setDuration(duration);
+
+                Animator prevAnimation = currentChangingAnimations.get(child);
+                if (prevAnimation != null) {
+                    prevAnimation.cancel();
+                }
+                Animator pendingAnimation = pendingAnimations.get(child);
+                if (pendingAnimation != null) {
+                    pendingAnimations.remove(child);
+                }
+                // Cache the animation in case we need to cancel it later
+                currentChangingAnimations.put(child, anim);
+
+                parent.requestTransitionStart(LayoutTransition.this);
+
+                // this only removes listeners whose views changed - must clear the
+                // other listeners later
+                child.removeOnLayoutChangeListener(this);
+                layoutChangeListenerMap.remove(child);
+            }
+        };
+        // Remove the animation from the cache when it ends
+        anim.addListener(new AnimatorListenerAdapter() {
+
+            @Override
+            public void onAnimationStart(Animator animator) {
+                if (mListeners != null) {
+                    for (TransitionListener listener : mListeners) {
+                        listener.startTransition(LayoutTransition.this, parent, child,
+                                changeReason == APPEARING ?
+                                        CHANGE_APPEARING : CHANGE_DISAPPEARING);
+                    }
+                }
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animator) {
+                child.removeOnLayoutChangeListener(listener);
+                layoutChangeListenerMap.remove(child);
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                currentChangingAnimations.remove(child);
+                if (mListeners != null) {
+                    for (TransitionListener listener : mListeners) {
+                        listener.endTransition(LayoutTransition.this, parent, child,
+                                changeReason == APPEARING ?
+                                        CHANGE_APPEARING : CHANGE_DISAPPEARING);
+                    }
+                }
+            }
+        });
+
+        child.addOnLayoutChangeListener(listener);
+        // cache the listener for later removal
+        layoutChangeListenerMap.put(child, listener);
+    }
+
+    /**
+     * Starts the animations set up for a CHANGING transition. We separate the setup of these
+     * animations from actually starting them, to avoid side-effects that starting the animations
+     * may have on the properties of the affected objects. After setup, we tell the affected parent
+     * that this transition should be started. The parent informs its ViewAncestor, which then
+     * starts the transition after the current layout/measurement phase, just prior to drawing
+     * the view hierarchy.
+     *
+     * @hide
+     */
+    public void startChangingAnimations() {
+        for (Animator anim : currentChangingAnimations.values()) {
+            if (anim instanceof ObjectAnimator) {
+                ((ObjectAnimator) anim).setCurrentPlayTime(0);
+            }
+            anim.start();
+        }
+    }
+
+    /**
      * Returns true if animations are running which animate layout-related properties. This
      * essentially means that either CHANGE_APPEARING or CHANGE_DISAPPEARING animations
      * are running, since these animations operate on layout-related properties.
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 87369ab..3877bd0 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1398,6 +1398,10 @@
     public void onConfigurationChanged(Configuration newConfig) {
         mCalled = true;
 
+        if (mActionBar != null) {
+            mActionBar.onConfigurationChanged(newConfig);
+        }
+
         mFragments.dispatchConfigurationChanged(newConfig);
 
         if (mWindow != null) {
diff --git a/core/java/android/app/IThumbnailRetriever.aidl b/core/java/android/app/IThumbnailRetriever.aidl
index 2a6737d..410cc20 100644
--- a/core/java/android/app/IThumbnailRetriever.aidl
+++ b/core/java/android/app/IThumbnailRetriever.aidl
@@ -18,6 +18,7 @@
 
 /**
  * System private API for retrieving thumbnails
+ * {@hide}
  */
 interface IThumbnailRetriever {
     Bitmap getThumbnail(int index);
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 95451d6..71f6445 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -167,7 +167,8 @@
      * Return the current running mode type.  May be one of
      * {@link Configuration#UI_MODE_TYPE_NORMAL Configuration.UI_MODE_TYPE_NORMAL},
      * {@link Configuration#UI_MODE_TYPE_DESK Configuration.UI_MODE_TYPE_DESK}, or
-     * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR},
+     * {@link Configuration#UI_MODE_TYPE_CAR Configuration.UI_MODE_TYPE_CAR}, or
+     * {@link Configuration#UI_MODE_TYPE_TELEVISION Configuration.UI_MODE_TYPE_TV}.
      */
     public int getCurrentModeType() {
         if (mService != null) {
diff --git a/core/java/android/content/IOnPrimaryClipChangedListener.aidl b/core/java/android/content/IOnPrimaryClipChangedListener.aidl
index fb42a45..46d7f7c 100644
--- a/core/java/android/content/IOnPrimaryClipChangedListener.aidl
+++ b/core/java/android/content/IOnPrimaryClipChangedListener.aidl
@@ -16,6 +16,9 @@
 
 package android.content;
 
+/**
+ * {@hide}
+ */
 oneway interface IOnPrimaryClipChangedListener {
     void dispatchPrimaryClipChanged();
 }
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 12ec258..51a7115 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -226,6 +226,7 @@
     public static final int UI_MODE_TYPE_NORMAL = 0x01;
     public static final int UI_MODE_TYPE_DESK = 0x02;
     public static final int UI_MODE_TYPE_CAR = 0x03;
+    public static final int UI_MODE_TYPE_TELEVISION = 0x04;
 
     public static final int UI_MODE_NIGHT_MASK = 0x30;
     public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
@@ -367,6 +368,7 @@
             case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break;
             case UI_MODE_TYPE_DESK: sb.append(" desk"); break;
             case UI_MODE_TYPE_CAR: sb.append(" car"); break;
+            case UI_MODE_TYPE_TELEVISION: sb.append(" television"); break;
             default: sb.append(" uimode="); sb.append(uiMode&UI_MODE_TYPE_MASK); break;
         }
         switch ((uiMode&UI_MODE_NIGHT_MASK)) {
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl
new file mode 100644
index 0000000..80ba2ed
--- /dev/null
+++ b/core/java/android/nfc/INdefPushCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.nfc;
+
+import android.nfc.NdefMessage;
+
+/**
+ * @hide
+ */
+interface INdefPushCallback
+{
+    NdefMessage onConnect();
+    void onMessagePushed();
+}
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 870127c..d11fea0 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -25,6 +25,7 @@
 import android.nfc.ILlcpSocket;
 import android.nfc.ILlcpServiceSocket;
 import android.nfc.ILlcpConnectionlessSocket;
+import android.nfc.INdefPushCallback;
 import android.nfc.INfcTag;
 import android.nfc.IP2pTarget;
 import android.nfc.IP2pInitiator;
@@ -51,6 +52,7 @@
             in IntentFilter[] filters, in TechListParcel techLists);
     void disableForegroundDispatch(in ComponentName activity);
     void enableForegroundNdefPush(in ComponentName activity, in NdefMessage msg);
+    void enableForegroundNdefPushWithCallback(in ComponentName activity, in INdefPushCallback callback);
     void disableForegroundNdefPush(in ComponentName activity);
 
     // Non-public methods
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 4689804..738e75f 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -124,7 +124,7 @@
      * Intent to start an activity when a tag is discovered.
      *
      * <p>This intent will not be started when a tag is discovered if any activities respond to
-     * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag. 
+     * {@link #ACTION_NDEF_DISCOVERED} or {@link #ACTION_TECH_DISCOVERED} for the current tag.
      */
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
     public static final String ACTION_TAG_DISCOVERED = "android.nfc.action.TAG_DISCOVERED";
@@ -235,6 +235,37 @@
      */
     private static final int DISCOVERY_MODE_CARD_EMULATION = 2;
 
+    /**
+     * Callback passed into {@link #enableForegroundNdefPush(Activity,NdefPushCallback)}. This
+     */
+    public interface NdefPushCallback {
+        /**
+         * Called when a P2P connection is created.
+         */
+        NdefMessage createMessage();
+        /**
+         * Called when the message is pushed.
+         */
+        void onMessagePushed();
+    }
+
+    private static class NdefPushCallbackWrapper extends INdefPushCallback.Stub {
+        private NdefPushCallback mCallback;
+
+        public NdefPushCallbackWrapper(NdefPushCallback callback) {
+            mCallback = callback;
+        }
+
+        @Override
+        public NdefMessage onConnect() {
+            return mCallback.createMessage();
+        }
+
+        @Override
+        public void onMessagePushed() {
+            mCallback.onMessagePushed();
+        }
+    }
 
     // Guarded by NfcAdapter.class
     private static boolean sIsInitialized = false;
@@ -575,6 +606,44 @@
     }
 
     /**
+     * Enable NDEF message push over P2P while this Activity is in the foreground.
+     *
+     * <p>For this to function properly the other NFC device being scanned must
+     * support the "com.android.npp" NDEF push protocol. Support for this
+     * protocol is currently optional for Android NFC devices.
+     *
+     * <p>This method must be called from the main thread.
+     *
+     * <p class="note"><em>NOTE:</em> While foreground NDEF push is active standard tag dispatch is disabled.
+     * Only the foreground activity may receive tag discovered dispatches via
+     * {@link #enableForegroundDispatch}.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param activity the foreground Activity
+     * @param callback is called on when the P2P connection is established
+     * @throws IllegalStateException if the Activity is not currently in the foreground
+     * @throws OperationNotSupportedException if this Android device does not support NDEF push
+     */
+    public void enableForegroundNdefPush(Activity activity, NdefPushCallback callback) {
+        if (activity == null || callback == null) {
+            throw new NullPointerException();
+        }
+        if (!activity.isResumed()) {
+            throw new IllegalStateException("Foregorund NDEF push can only be enabled " +
+                    "when your activity is resumed");
+        }
+        try {
+            ActivityThread.currentActivityThread().registerOnActivityPausedListener(activity,
+                    mForegroundNdefPushListener);
+            sService.enableForegroundNdefPushWithCallback(activity.getComponentName(),
+                    new NdefPushCallbackWrapper(callback));
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
+    }
+
+    /**
      * Disable NDEF message push over P2P.
      *
      * <p>After calling {@link #enableForegroundNdefPush}, an activity
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index f85df6c..d475f36 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -634,6 +634,20 @@
     }
 
     /**
+     * Returns the parent process id for a currently running process.
+     * @param pid the process id
+     * @return the parent process id of the process, or -1 if the process is not running.
+     * @hide
+     */
+    public static final int getParentPid(int pid) {
+        String[] procStatusLabels = { "PPid:" };
+        long[] procStatusValues = new long[1];
+        procStatusValues[0] = -1;
+        Process.readProcLines("/proc/" + pid + "/status", procStatusLabels, procStatusValues);
+        return (int) procStatusValues[0];
+    }
+
+    /**
      * Set the priority of a thread, based on Linux priorities.
      * 
      * @param tid The identifier of the thread/process to change.
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index d68e6fb..bc6e993 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -36,6 +36,11 @@
     private final int mMtpReserveSpace;
     private int mStorageId;
 
+    // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
+    // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
+    // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
+    public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
+
     public StorageVolume(String path, String description,
             boolean removable, boolean emulated, int mtpReserveSpace) {
         mPath = path;
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 4141879..20614dc 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -616,18 +616,6 @@
         public static final String DTEND = "dtend";
 
         /**
-         * The time the event starts with allDay events in a local tz
-         * <P>Type: INTEGER (long; millis since epoch)</P>
-         */
-        public static final String DTSTART2 = "dtstart2";
-
-        /**
-         * The time the event ends with allDay events in a local tz
-         * <P>Type: INTEGER (long; millis since epoch)</P>
-         */
-        public static final String DTEND2 = "dtend2";
-
-        /**
          * The duration of the event
          * <P>Type: TEXT (duration in RFC2445 format)</P>
          */
@@ -734,8 +722,16 @@
         public static final String EXDATE = "exdate";
 
         /**
+         * The _id of the original recurring event for which this event is an
+         * exception.
+         * <P>Type: TEXT</P>
+         */
+        public static final String ORIGINAL_ID = "original_id";
+
+        /**
          * The _sync_id of the original recurring event for which this event is
-         * an exception.
+         * an exception. The provider should keep the original_id in sync when
+         * this is updated.
          * <P>Type: TEXT</P>
          */
         public static final String ORIGINAL_SYNC_ID = "original_sync_id";
@@ -899,6 +895,7 @@
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DTEND);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, DURATION);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_TIMEZONE);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EVENT_END_TIMEZONE);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ALL_DAY);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, AVAILABILITY);
@@ -910,6 +907,7 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXRULE);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, EXDATE);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_SYNC_ID);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORIGINAL_ID);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv,
                         ORIGINAL_INSTANCE_TIME);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ORIGINAL_ALL_DAY);
@@ -925,7 +923,7 @@
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
-                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.SYNC1);
+                DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, SYNC1);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
                         Events.SYNC_DATA1);
 
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
index 6f4c15b..d698b54 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -18,6 +18,7 @@
 import android.media.AudioFormat;
 import android.media.AudioTrack;
 import android.os.Bundle;
+import android.os.Handler;
 import android.util.Log;
 
 /**
@@ -49,16 +50,20 @@
     private final float mPan;
 
     private final Object mStateLock = new Object();
-    private AudioTrack mAudioTrack = null;
+    private final Handler mAudioTrackHandler;
+    private volatile AudioTrack mAudioTrack = null;
     private boolean mStopped = false;
     private boolean mDone = false;
+    private volatile boolean mWriteErrorOccured;
 
     PlaybackSynthesisRequest(String text, Bundle params,
-            int streamType, float volume, float pan) {
+            int streamType, float volume, float pan, Handler audioTrackHandler) {
         super(text, params);
         mStreamType = streamType;
         mVolume = volume;
         mPan = pan;
+        mAudioTrackHandler = audioTrackHandler;
+        mWriteErrorOccured = false;
     }
 
     @Override
@@ -70,14 +75,28 @@
         }
     }
 
+    // Always guarded by mStateLock.
     private void cleanUp() {
         if (DBG) Log.d(TAG, "cleanUp()");
-        if (mAudioTrack != null) {
-            mAudioTrack.flush();
-            mAudioTrack.stop();
-            mAudioTrack.release();
-            mAudioTrack = null;
+        if (mAudioTrack == null) {
+            return;
         }
+
+        final AudioTrack audioTrack = mAudioTrack;
+        mAudioTrack = null;
+
+        // Clean up on the audiotrack handler thread.
+        //
+        // NOTE: It isn't very clear whether AudioTrack is thread safe.
+        // If it is we can clean up on the current (synthesis) thread.
+        mAudioTrackHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                audioTrack.flush();
+                audioTrack.stop();
+                audioTrack.release();
+            }
+        });
     }
 
     @Override
@@ -146,10 +165,15 @@
             Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
                     + offset + "," + length + ")");
         }
-        if (length > getMaxBufferSize()) {
-            throw new IllegalArgumentException("buffer is too large (" + length + " bytes)");
+        if (length > getMaxBufferSize() || length <= 0) {
+            throw new IllegalArgumentException("buffer is too large or of zero length (" +
+                    + length + " bytes)");
         }
         synchronized (mStateLock) {
+            if (mWriteErrorOccured) {
+                if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+                return TextToSpeech.ERROR;
+            }
             if (mStopped) {
                 if (DBG) Log.d(TAG, "Request has been aborted.");
                 return TextToSpeech.ERROR;
@@ -158,22 +182,33 @@
                 Log.e(TAG, "audioAvailable(): Not started");
                 return TextToSpeech.ERROR;
             }
-            int playState = mAudioTrack.getPlayState();
-            if (playState == AudioTrack.PLAYSTATE_STOPPED) {
-                if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
-                mAudioTrack.play();
-            }
-            // TODO: loop until all data is written?
-            if (DBG) Log.d(TAG, "AudioTrack.write()");
-            int count = mAudioTrack.write(buffer, offset, length);
-            if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count);
-            if (count < 0) {
-                Log.e(TAG, "Writing to AudioTrack failed: " + count);
-                cleanUp();
-                return TextToSpeech.ERROR;
-            } else {
-                return TextToSpeech.SUCCESS;
-            }
+            final AudioTrack audioTrack = mAudioTrack;
+            // Sigh, another copy.
+            final byte[] bufferCopy = new byte[length];
+            System.arraycopy(buffer, offset, bufferCopy, 0, length);
+
+            mAudioTrackHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    int playState = audioTrack.getPlayState();
+                    if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+                        if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
+                        audioTrack.play();
+                    }
+                    // TODO: loop until all data is written?
+                    if (DBG) Log.d(TAG, "AudioTrack.write()");
+                    int count = audioTrack.write(bufferCopy, 0, bufferCopy.length);
+                    // The semantics of this change very slightly. Earlier, we would
+                    // report an error immediately, Now we will return an error on
+                    // the next API call, usually done( ) or another audioAvailable( )
+                    // call.
+                    if (count < 0) {
+                        mWriteErrorOccured = true;
+                    }
+                }
+            });
+
+            return TextToSpeech.SUCCESS;
         }
     }
 
@@ -181,6 +216,10 @@
     public int done() {
         if (DBG) Log.d(TAG, "done()");
         synchronized (mStateLock) {
+            if (mWriteErrorOccured) {
+                if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
+                return TextToSpeech.ERROR;
+            }
             if (mStopped) {
                 if (DBG) Log.d(TAG, "Request has been aborted.");
                 return TextToSpeech.ERROR;
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index f32474f..717dde8 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -51,6 +51,7 @@
     private static final String SYNTH_THREAD_NAME = "SynthThread";
 
     private SynthHandler mSynthHandler;
+    private Handler mAudioTrackHandler;
 
     private CallbackMap mCallbacks;
 
@@ -63,6 +64,10 @@
         synthThread.start();
         mSynthHandler = new SynthHandler(synthThread.getLooper());
 
+        HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread");
+        audioTrackThread.start();
+        mAudioTrackHandler = new Handler(audioTrackThread.getLooper());
+
         mCallbacks = new CallbackMap();
 
         // Load default language
@@ -75,6 +80,7 @@
 
         // Tell the synthesizer to stop
         mSynthHandler.quit();
+        mAudioTrackHandler.getLooper().quit();
 
         // Unregister all callbacks.
         mCallbacks.kill();
@@ -444,7 +450,7 @@
 
         protected SynthesisRequest createSynthesisRequest() {
             return new PlaybackSynthesisRequest(mText, mParams,
-                    getStreamType(), getVolume(), getPan());
+                    getStreamType(), getVolume(), getPan(), mAudioTrackHandler);
         }
 
         private void setRequestParams(SynthesisRequest request) {
diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java
index d432dee..fe96565 100644
--- a/core/java/android/text/method/ArrowKeyMovementMethod.java
+++ b/core/java/android/text/method/ArrowKeyMovementMethod.java
@@ -234,7 +234,7 @@
             if (action == MotionEvent.ACTION_DOWN) {
               boolean cap = isSelecting(buffer);
               if (cap) {
-                  int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+                  int offset = widget.getOffsetForPosition(event.getX(), event.getY());
 
                   buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT);
 
@@ -259,7 +259,7 @@
                     // Update selection as we're moving the selection area.
 
                     // Get the current touch position
-                    int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+                    int offset = widget.getOffsetForPosition(event.getX(), event.getY());
 
                     Selection.extendSelection(buffer, offset);
                     return true;
@@ -275,7 +275,7 @@
                     return true;
                 }
 
-                int offset = widget.getOffset((int) event.getX(), (int) event.getY());
+                int offset = widget.getOffsetForPosition(event.getX(), event.getY());
                 if (isSelecting(buffer)) {
                     buffer.removeSpan(LAST_TAP_DOWN);
                     Selection.extendSelection(buffer, offset);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 845fbc3..61a24a0 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -17,7 +17,6 @@
 
 package android.view;
 
-import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
@@ -137,14 +136,14 @@
          * 
          * @param canvas The Canvas used to render the view.
          */
-        void onHardwarePreDraw(Canvas canvas);
+        void onHardwarePreDraw(HardwareCanvas canvas);
 
         /**
          * Invoked after a view is drawn by a hardware renderer.
          * 
          * @param canvas The Canvas used to render the view.
          */
-        void onHardwarePostDraw(Canvas canvas);
+        void onHardwarePostDraw(HardwareCanvas canvas);
     }
 
     /**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 82fd581..3436cd1 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -883,8 +883,8 @@
      * <p>
      * <ul>
      * <li>For a stylus, reports the distance of the stylus from the screen.
-     * The value is normalized to a range from 0.0 (direct contact) to 1.0 (furthest measurable
-     * distance).
+     * The value is nominally measured in millimeters where 0.0 indicates direct contact
+     * and larger values indicate increasing distance from the surface.
      * </ul>
      * </p>
      *
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d5f573c..98d07c4 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -6048,6 +6048,26 @@
     }
 
     /**
+     * Set the horizontal scrolled position of your view. This will cause a call to
+     * {@link #onScrollChanged(int, int, int, int)} and the view will be
+     * invalidated.
+     * @param value the x position to scroll to
+     */
+    public void setScrollX(int value) {
+        scrollTo(value, mScrollY);
+    }
+
+    /**
+     * Set the vertical scrolled position of your view. This will cause a call to
+     * {@link #onScrollChanged(int, int, int, int)} and the view will be
+     * invalidated.
+     * @param value the y position to scroll to
+     */
+    public void setScrollY(int value) {
+        scrollTo(mScrollX, value);
+    }
+
+    /**
      * Return the scrolled left position of this view. This is the left edge of
      * the displayed part of your view. You do not need to draw any pixels
      * farther left, since those are outside of the frame of your view on
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index 8085ea8..bf04502 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.Manifest;
+import android.animation.LayoutTransition;
 import android.app.ActivityManagerNative;
 import android.content.ClipDescription;
 import android.content.ComponentCallbacks;
@@ -25,7 +26,6 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -45,9 +45,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.util.AndroidRuntimeException;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
@@ -230,10 +228,11 @@
     int mScrollY;
     int mCurScrollY;
     Scroller mScroller;
-    Bitmap mResizeBitmap;
-    long mResizeBitmapStartTime;
-    int mResizeBitmapDuration;
+    HardwareLayer mResizeBuffer;
+    long mResizeBufferStartTime;
+    int mResizeBufferDuration;
     static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator();
+    private ArrayList<LayoutTransition> mPendingTransitions;
 
     final ViewConfiguration mViewConfiguration;
 
@@ -672,6 +671,7 @@
         if (!mTraversalScheduled) {
             mTraversalScheduled = true;
 
+            //noinspection ConstantConditions
             if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
                 final long now = System.nanoTime();
                 Log.d(TAG, "Latency: Scheduled traversal, it has been "
@@ -694,10 +694,32 @@
         return mAppVisible ? mView.getVisibility() : View.GONE;
     }
 
-    void disposeResizeBitmap() {
-        if (mResizeBitmap != null) {
-            mResizeBitmap.recycle();
-            mResizeBitmap = null;
+    void disposeResizeBuffer() {
+        if (mResizeBuffer != null) {
+            mResizeBuffer.destroy();
+            mResizeBuffer = null;
+        }
+    }
+
+    /**
+     * Add LayoutTransition to the list of transitions to be started in the next traversal.
+     * This list will be cleared after the transitions on the list are start()'ed. These
+     * transitionsa re added by LayoutTransition itself when it sets up animations. The setup
+     * happens during the layout phase of traversal, which we want to complete before any of the
+     * animations are started (because those animations may side-effect properties that layout
+     * depends upon, like the bounding rectangles of the affected views). So we add the transition
+     * to the list and it is started just prior to starting the drawing phase of traversal.
+     *
+     * @param transition The LayoutTransition to be started on the next traversal.
+     *
+     * @hide
+     */
+    public void requestTransitionStart(LayoutTransition transition) {
+        if (mPendingTransitions == null || !mPendingTransitions.contains(transition)) {
+            if (mPendingTransitions == null) {
+                 mPendingTransitions = new ArrayList<LayoutTransition>();
+            }
+            mPendingTransitions.add(transition);
         }
     }
 
@@ -819,15 +841,25 @@
                             mAttachInfo.mHardwareRenderer.isEnabled() &&
                             lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
 
-                        disposeResizeBitmap();
+                        disposeResizeBuffer();
 
                         boolean completed = false;
+                        HardwareCanvas canvas = null;
                         try {
-                            mResizeBitmap = Bitmap.createBitmap(mWidth, mHeight,
-                                    Bitmap.Config.ARGB_8888);
-                            mResizeBitmap.setHasAlpha(false);
-                            Canvas canvas = new Canvas(mResizeBitmap);
+                            if (mResizeBuffer == null) {
+                                mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer(
+                                        mWidth, mHeight, false);
+                            } else if (mResizeBuffer.getWidth() != mWidth ||
+                                    mResizeBuffer.getHeight() != mHeight) {
+                                mResizeBuffer.resize(mWidth, mHeight);
+                            }
+                            canvas = mResizeBuffer.start(mAttachInfo.mHardwareCanvas);
+                            canvas.setViewport(mWidth, mHeight);
+                            canvas.onPreDraw(null);
+                            final int restoreCount = canvas.save();
+                            
                             canvas.drawColor(0xff000000, PorterDuff.Mode.SRC);
+
                             int yoff;
                             final boolean scrolling = mScroller != null
                                     && mScroller.computeScrollOffset();
@@ -837,22 +869,32 @@
                             } else {
                                 yoff = mScrollY;
                             }
+
                             canvas.translate(0, -yoff);
                             if (mTranslator != null) {
                                 mTranslator.translateCanvas(canvas);
                             }
-                            canvas.setScreenDensity(mAttachInfo.mScalingRequired
-                                    ? DisplayMetrics.DENSITY_DEVICE : 0);
+
                             mView.draw(canvas);
-                            mResizeBitmapStartTime = SystemClock.uptimeMillis();
-                            mResizeBitmapDuration = mView.getResources().getInteger(
+
+                            mResizeBufferStartTime = SystemClock.uptimeMillis();
+                            mResizeBufferDuration = mView.getResources().getInteger(
                                     com.android.internal.R.integer.config_mediumAnimTime);
                             completed = true;
+
+                            canvas.restoreToCount(restoreCount);
                         } catch (OutOfMemoryError e) {
                             Log.w(TAG, "Not enough memory for content change anim buffer", e);
                         } finally {
-                            if (!completed) {
-                                mResizeBitmap = null;
+                            if (canvas != null) {
+                                canvas.onPostDraw();
+                            }
+                            if (mResizeBuffer != null) {
+                                mResizeBuffer.end(mAttachInfo.mHardwareCanvas);
+                                if (!completed) {
+                                    mResizeBuffer.destroy();
+                                    mResizeBuffer = null;
+                                }
                             }
                         }
                     }
@@ -1114,7 +1156,7 @@
                     if (mScroller != null) {
                         mScroller.abortAnimation();
                     }
-                    disposeResizeBitmap();
+                    disposeResizeBuffer();
                 } else if (surfaceGenerationId != mSurface.getGenerationId() &&
                         mSurfaceHolder == null && mAttachInfo.mHardwareRenderer != null) {
                     fullRedrawNeeded = true;
@@ -1396,6 +1438,12 @@
         boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
 
         if (!cancelDraw && !newSurface) {
+            if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+                for (int i = 0; i < mPendingTransitions.size(); ++i) {
+                    mPendingTransitions.get(i).startChangingAnimations();
+                }
+                mPendingTransitions.clear();
+            }
             mFullRedrawNeeded = false;
 
             final long drawStartTime;
@@ -1495,15 +1543,14 @@
     int mResizeAlpha;
     final Paint mResizePaint = new Paint();
 
-    public void onHardwarePreDraw(Canvas canvas) {
+    public void onHardwarePreDraw(HardwareCanvas canvas) {
         canvas.translate(0, -mHardwareYOffset);
     }
 
-    public void onHardwarePostDraw(Canvas canvas) {
-        if (mResizeBitmap != null) {
-            canvas.translate(0, mHardwareYOffset);
+    public void onHardwarePostDraw(HardwareCanvas canvas) {
+        if (mResizeBuffer != null) {
             mResizePaint.setAlpha(mResizeAlpha);
-            canvas.drawBitmap(mResizeBitmap, 0, 0, mResizePaint);
+            canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint);
         }
     }
 
@@ -1559,15 +1606,15 @@
         boolean scalingRequired = mAttachInfo.mScalingRequired;
 
         int resizeAlpha = 0;
-        if (mResizeBitmap != null) {
-            long deltaTime = SystemClock.uptimeMillis() - mResizeBitmapStartTime;
-            if (deltaTime < mResizeBitmapDuration) {
-                float amt = deltaTime/(float)mResizeBitmapDuration;
+        if (mResizeBuffer != null) {
+            long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime;
+            if (deltaTime < mResizeBufferDuration) {
+                float amt = deltaTime/(float) mResizeBufferDuration;
                 amt = mResizeInterpolator.getInterpolation(amt);
                 animating = true;
                 resizeAlpha = 255 - (int)(amt*255);
             } else {
-                disposeResizeBitmap();
+                disposeResizeBuffer();
             }
         }
 
@@ -1579,7 +1626,7 @@
                 if (mScroller != null) {
                     mScroller.abortAnimation();
                 }
-                disposeResizeBitmap();
+                disposeResizeBuffer();
             }
             return;
         }
@@ -1863,7 +1910,7 @@
         if (scrollY != mScrollY) {
             if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
                     + mScrollY + " , new=" + scrollY);
-            if (!immediate && mResizeBitmap == null) {
+            if (!immediate && mResizeBuffer == null) {
                 if (mScroller == null) {
                     mScroller = new Scroller(mView.getContext());
                 }
@@ -1909,20 +1956,22 @@
     public void focusableViewAvailable(View v) {
         checkThread();
 
-        if (mView != null && !mView.hasFocus()) {
-            v.requestFocus();
-        } else {
-            // the one case where will transfer focus away from the current one
-            // is if the current view is a view group that prefers to give focus
-            // to its children first AND the view is a descendant of it.
-            mFocusedView = mView.findFocus();
-            boolean descendantsHaveDibsOnFocus =
-                    (mFocusedView instanceof ViewGroup) &&
-                        (((ViewGroup) mFocusedView).getDescendantFocusability() ==
-                                ViewGroup.FOCUS_AFTER_DESCENDANTS);
-            if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
-                // If a view gets the focus, the listener will be invoked from requestChildFocus()
+        if (mView != null) {
+            if (!mView.hasFocus()) {
                 v.requestFocus();
+            } else {
+                // the one case where will transfer focus away from the current one
+                // is if the current view is a view group that prefers to give focus
+                // to its children first AND the view is a descendant of it.
+                mFocusedView = mView.findFocus();
+                boolean descendantsHaveDibsOnFocus =
+                        (mFocusedView instanceof ViewGroup) &&
+                            (((ViewGroup) mFocusedView).getDescendantFocusability() ==
+                                    ViewGroup.FOCUS_AFTER_DESCENDANTS);
+                if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
+                    // If a view gets the focus, the listener will be invoked from requestChildFocus()
+                    v.requestFocus();
+                }
             }
         }
     }
@@ -1986,9 +2035,7 @@
             // At this point the resources have been updated to
             // have the most recent config, whatever that is.  Use
             // the on in them which may be newer.
-            if (mView != null) {
-                config = mView.getResources().getConfiguration();
-            }
+            config = mView.getResources().getConfiguration();
             if (force || mLastConfiguration.diff(config) != 0) {
                 mLastConfiguration.setTo(config);
                 mView.dispatchConfigurationChanged(config);
@@ -2207,6 +2254,7 @@
             if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) {
                 // The IME is trying to say this event is from the
                 // system!  Bad bad bad!
+                //noinspection UnusedAssignment
                 event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM);
             }
             deliverKeyEventPostIme((KeyEvent)msg.obj, false);
@@ -2240,7 +2288,7 @@
         }
     }
     
-    private void startInputEvent(InputEvent event, InputQueue.FinishedCallback finishedCallback) {
+    private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
         if (mFinishedCallback != null) {
             Slog.w(TAG, "Received a new input event from the input queue but there is "
                     + "already an unfinished input event in progress.");
@@ -2454,9 +2502,6 @@
         if (isDown) {
             ensureTouchMode(true);
         }
-        if(false) {
-            captureMotionLog("captureDispatchPointer", event);
-        }
 
         // Offset the scroll position.
         if (mCurScrollY != 0) {
@@ -2534,6 +2579,7 @@
         if (sendDone) {
             finishInputEvent(event, handled);
         }
+        //noinspection ConstantConditions
         if (LOCAL_LOGV || WATCH_POINTER) {
             if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                 Log.i(TAG, "Done dispatching!");
@@ -2859,52 +2905,6 @@
         return false;
     }
 
-    /**
-     * log motion events
-     */
-    private static void captureMotionLog(String subTag, MotionEvent ev) {
-        //check dynamic switch
-        if (ev == null ||
-                SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
-            return;
-        }
-
-        StringBuilder sb = new StringBuilder(subTag + ": ");
-        sb.append(ev.getDownTime()).append(',');
-        sb.append(ev.getEventTime()).append(',');
-        sb.append(ev.getAction()).append(',');
-        sb.append(ev.getX()).append(',');
-        sb.append(ev.getY()).append(',');
-        sb.append(ev.getPressure()).append(',');
-        sb.append(ev.getSize()).append(',');
-        sb.append(ev.getMetaState()).append(',');
-        sb.append(ev.getXPrecision()).append(',');
-        sb.append(ev.getYPrecision()).append(',');
-        sb.append(ev.getDeviceId()).append(',');
-        sb.append(ev.getEdgeFlags());
-        Log.d(TAG, sb.toString());
-    }
-    /**
-     * log motion events
-     */
-    private static void captureKeyLog(String subTag, KeyEvent ev) {
-        //check dynamic switch
-        if (ev == null ||
-                SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
-            return;
-        }
-        StringBuilder sb = new StringBuilder(subTag + ": ");
-        sb.append(ev.getDownTime()).append(',');
-        sb.append(ev.getEventTime()).append(',');
-        sb.append(ev.getAction()).append(',');
-        sb.append(ev.getKeyCode()).append(',');
-        sb.append(ev.getRepeatCount()).append(',');
-        sb.append(ev.getMetaState()).append(',');
-        sb.append(ev.getDeviceId()).append(',');
-        sb.append(ev.getScanCode());
-        Log.d(TAG, sb.toString());
-    }
-
     int enqueuePendingEvent(Object event, boolean sendDone) {
         int seq = mPendingEventSeq+1;
         if (seq < 0) seq = 0;
@@ -2993,10 +2993,6 @@
             return;
         }
 
-        if (false) {
-            captureKeyLog("captureDispatchKeyEvent", event);
-        }
-
         // Make sure the fallback event policy sees all keys that will be delivered to the
         // view hierarchy.
         mFallbackEventHandler.preDispatchKeyEvent(event);
@@ -3392,12 +3388,12 @@
     
     private final InputHandler mInputHandler = new InputHandler() {
         public void handleKey(KeyEvent event, InputQueue.FinishedCallback finishedCallback) {
-            startInputEvent(event, finishedCallback);
+            startInputEvent(finishedCallback);
             dispatchKey(event, true);
         }
 
         public void handleMotion(MotionEvent event, InputQueue.FinishedCallback finishedCallback) {
-            startInputEvent(event, finishedCallback);
+            startInputEvent(finishedCallback);
             dispatchMotion(event, true);
         }
     };
@@ -3429,10 +3425,6 @@
         sendMessageAtTime(msg, event.getEventTime());
     }
     
-    public void dispatchMotion(MotionEvent event) {
-        dispatchMotion(event, false);
-    }
-
     private void dispatchMotion(MotionEvent event, boolean sendDone) {
         int source = event.getSource();
         if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
@@ -3444,10 +3436,6 @@
         }
     }
 
-    public void dispatchPointer(MotionEvent event) {
-        dispatchPointer(event, false);
-    }
-
     private void dispatchPointer(MotionEvent event, boolean sendDone) {
         Message msg = obtainMessage(DISPATCH_POINTER);
         msg.obj = event;
@@ -3455,10 +3443,6 @@
         sendMessageAtTime(msg, event.getEventTime());
     }
 
-    public void dispatchTrackball(MotionEvent event) {
-        dispatchTrackball(event, false);
-    }
-
     private void dispatchTrackball(MotionEvent event, boolean sendDone) {
         Message msg = obtainMessage(DISPATCH_TRACKBALL);
         msg.obj = event;
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 4aa8727..f014070 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -92,12 +92,6 @@
     public static final boolean TRACE_RECYCLER = false;
 
     /**
-     * The system property of dynamic switch for capturing event information
-     * when it is set, we log key events, touch/motion and trackball events
-     */
-    static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent";
-
-    /**
      * Profiles drawing times in the events log.
      *
      * @hide
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6937573..f504b90 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -4838,6 +4838,20 @@
     }
 
     /**
+     * This method is called by LayoutTransition when there are 'changing' animations that need
+     * to start after the layout/setup phase. The request is forwarded to the ViewAncestor, who
+     * starts all pending transitions prior to the drawing phase in the current traversal.
+     *
+     * @param transition The LayoutTransition to be started on the next traversal.
+     *
+     * @hide
+     */
+    public void requestTransitionStart(LayoutTransition transition) {
+        ViewAncestor viewAncestor = getViewAncestor();
+        viewAncestor.requestTransitionStart(transition);
+    }
+
+    /**
      * Return true if the pressed state should be delayed for children or descendants of this
      * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
      * This prevents the pressed state from appearing when the user is actually trying to scroll
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 1d56e9d..9eddf23 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -67,6 +67,19 @@
     private boolean mDurationSet = false;
 
     /**
+     * The startDelay of the underlying Animator object. By default, we don't set the startDelay
+     * on the Animator and just use its default startDelay. If the startDelay is ever set on this
+     * Animator, then we use the startDelay that it was set to.
+     */
+    private long mStartDelay = 0;
+
+    /**
+     * A flag indicating whether the startDelay has been set on this object. If not, we don't set
+     * the startDelay on the underlying Animator, but instead just use its default startDelay.
+     */
+    private boolean mStartDelaySet = false;
+
+    /**
      * The interpolator of the underlying Animator object. By default, we don't set the interpolator
      * on the Animator and just use its default interpolator. If the interpolator is ever set on
      * this Animator, then we use the interpolator that it was set to.
@@ -233,6 +246,60 @@
     }
 
     /**
+     * Returns the current duration of property animations. If the duration was set on this
+     * object, that value is returned. Otherwise, the default value of the underlying Animator
+     * is returned.
+     *
+     * @see #setDuration(long)
+     * @return The duration of animations, in milliseconds.
+     */
+    public long getDuration() {
+        if (mStartDelaySet) {
+            return mStartDelay;
+        } else {
+            // Just return the default from ValueAnimator, since that's what we'd get if
+            // the value has not been set otherwise
+            return new ValueAnimator().getDuration();
+        }
+    }
+
+    /**
+     * Returns the current startDelay of property animations. If the startDelay was set on this
+     * object, that value is returned. Otherwise, the default value of the underlying Animator
+     * is returned.
+     *
+     * @see #setStartDelay(long)
+     * @return The startDelay of animations, in milliseconds.
+     */
+    public long getStartDelay() {
+        if (mStartDelaySet) {
+            return mStartDelay;
+        } else {
+            // Just return the default from ValueAnimator (0), since that's what we'd get if
+            // the value has not been set otherwise
+            return 0;
+        }
+    }
+
+    /**
+     * Sets the startDelay for the underlying animator that animates the requested properties.
+     * By default, the animator uses the default value for ValueAnimator. Calling this method
+     * will cause the declared value to be used instead.
+     * @param startDelay The delay of ensuing property animations, in milliseconds. The value
+     * cannot be negative.
+     * @return This object, allowing calls to methods in this class to be chained.
+     */
+    public ViewPropertyAnimator setStartDelay(long startDelay) {
+        if (startDelay < 0) {
+            throw new IllegalArgumentException("Animators cannot have negative duration: " +
+                    startDelay);
+        }
+        mStartDelaySet = true;
+        mStartDelay = startDelay;
+        return this;
+    }
+
+    /**
      * Sets the interpolator for the underlying animator that animates the requested properties.
      * By default, the animator uses the default interpolator for ValueAnimator. Calling this method
      * will cause the declared object to be used instead.
@@ -259,6 +326,33 @@
     }
 
     /**
+     * Starts the currently pending property animations immediately. Calling <code>start()</code>
+     * is optional because all animations start automatically at the next opportunity. However,
+     * if the animations are needed to start immediately and synchronously (not at the time when
+     * the next event is processed by the hierarchy, which is when the animations would begin
+     * otherwise), then this method can be used.
+     */
+    public void start() {
+        startAnimation();
+    }
+
+    /**
+     * Cancels all property animations that are currently running or pending.
+     */
+    public void cancel() {
+        if (mAnimatorMap.size() > 0) {
+            HashMap<Animator, PropertyBundle> mAnimatorMapCopy =
+                    (HashMap<Animator, PropertyBundle>)mAnimatorMap.clone();
+            Set<Animator> animatorSet = mAnimatorMapCopy.keySet();
+            for (Animator runningAnim : animatorSet) {
+                runningAnim.cancel();
+            }
+        }
+        mPendingAnimations.clear();
+        mView.getHandler().removeCallbacks(mAnimationStarter);
+    }
+
+    /**
      * This method will cause the View's <code>x</code> property to be animated to the
      * specified value. Animations already running on the property will be canceled.
      *
@@ -598,7 +692,7 @@
                     // on a property will cancel a previous animation on that property, so
                     // there can only ever be one such animation running.
                     if (bundle.mPropertyMask == NONE) {
-                        // the animation is not longer changing anything - cancel it
+                        // the animation is no longer changing anything - cancel it
                         animatorToCancel = runningAnim;
                         break;
                     }
diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java
index 976e786..12391df 100644
--- a/core/java/android/webkit/JWebCoreJavaBridge.java
+++ b/core/java/android/webkit/JWebCoreJavaBridge.java
@@ -39,9 +39,6 @@
     // immediately.
     private boolean mHasInstantTimer;
 
-    // Reference count the pause/resume of timers
-    private int mPauseTimerRefCount;
-
     private boolean mTimerPaused;
     private boolean mHasDeferredTimers;
 
@@ -136,7 +133,7 @@
      * Pause all timers.
      */
     public void pause() {
-        if (--mPauseTimerRefCount == 0) {
+        if (!mTimerPaused) {
             mTimerPaused = true;
             mHasDeferredTimers = false;
         }
@@ -146,7 +143,7 @@
      * Resume all timers.
      */
     public void resume() {
-        if (++mPauseTimerRefCount == 1) {
+        if (mTimerPaused) {
            mTimerPaused = false;
            if (mHasDeferredTimers) {
                mHasDeferredTimers = false;
diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java
index f59d7d0..5b4fb1d 100644
--- a/core/java/android/webkit/L10nUtils.java
+++ b/core/java/android/webkit/L10nUtils.java
@@ -69,7 +69,8 @@
         com.android.internal.R.string.autofill_card_number_re,              // IDS_AUTOFILL_CARD_NUMBER_RE
         com.android.internal.R.string.autofill_expiration_month_re,         // IDS_AUTOFILL_EXPIRATION_MONTH_RE
         com.android.internal.R.string.autofill_expiration_date_re,          // IDS_AUTOFILL_EXPIRATION_DATE_RE
-        com.android.internal.R.string.autofill_card_ignored_re              // IDS_AUTOFILL_CARD_IGNORED_RE
+        com.android.internal.R.string.autofill_card_ignored_re,             // IDS_AUTOFILL_CARD_IGNORED_RE
+        com.android.internal.R.string.autofill_fax_re                       // IDS_AUTOFILL_FAX_RE
     };
 
     private static Context mApplicationContext;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 8ffbda2..66fcc27 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -183,7 +183,6 @@
     private boolean         mJavaScriptCanOpenWindowsAutomatically = false;
     private boolean         mUseDoubleTree = false;
     private boolean         mUseWideViewport = false;
-    private boolean         mUseFixedViewport = false;
     private boolean         mSupportMultipleWindows = false;
     private boolean         mShrinksStandaloneImagesToFit = false;
     private long            mMaximumDecodedImageSize = 0; // 0 means default
@@ -361,10 +360,9 @@
     }
 
     // User agent strings.
-    private static final String DESKTOP_USERAGENT =
-            "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_7; en-us)"
-            + " AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0"
-            + " Safari/530.17";
+    private static final String DESKTOP_USERAGENT = "Mozilla/5.0 (X11; " +
+        "Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) " +
+        "Chrome/11.0.696.34 Safari/534.24";
     private static final String IPHONE_USERAGENT =
             "Mozilla/5.0 (iPhone; U; CPU iPhone OS 3_0 like Mac OS X; en-us)"
             + " AppleWebKit/528.18 (KHTML, like Gecko) Version/4.0"
@@ -383,13 +381,6 @@
         mDefaultTextEncoding = context.getString(com.android.internal.
                                                  R.string.default_text_encoding);
 
-        // Detect tablet device for fixed viewport mode.
-        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
-        final int landscapeWidth = Math.max(metrics.widthPixels, metrics.heightPixels);
-        final int minTabletWidth = context.getResources().getDimensionPixelSize(
-            com.android.internal.R.dimen.min_xlarge_screen_width);
-        mUseFixedViewport = (metrics.density == 1.0f && landscapeWidth >= minTabletWidth);
-
         if (sLockForLocaleSettings == null) {
             sLockForLocaleSettings = new Object();
             sLocale = Locale.getDefault();
@@ -1652,11 +1643,11 @@
     }
 
     /**
-     * Returns whether to use fixed viewport.  Fixed viewport should operate only
-     * when wide viewport is on.
+     * Returns whether to use fixed viewport.  Use fixed viewport
+     * whenever wide viewport is on.
      */
     /* package */ boolean getUseFixedViewport() {
-        return getUseWideViewPort() && mUseFixedViewport;
+        return getUseWideViewPort();
     }
 
     /**
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index e8083eb..db5ff54 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1865,7 +1865,7 @@
             Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
             return;
         }
-        int width = calculateWindowWidth(w, textwrapWidth);
+        int width = calculateWindowWidth(w);
         int height = h;
         if (width != w) {
             float heightWidthRatio = data.mHeightWidthRatio;
@@ -1891,41 +1891,18 @@
     }
 
     // Calculate width to be used in webkit window.
-    private int calculateWindowWidth(int viewWidth, int textwrapWidth) {
+    private int calculateWindowWidth(int viewWidth) {
         int width = viewWidth;
         if (mSettings.getUseWideViewPort()) {
             if (mViewportWidth == -1) {
-                if (mSettings.getLayoutAlgorithm() ==
-                        WebSettings.LayoutAlgorithm.NORMAL || mSettings.getUseFixedViewport()) {
-                    width = WebView.DEFAULT_VIEWPORT_WIDTH;
-                } else {
-                    /*
-                     * if a page's minimum preferred width is wider than the
-                     * given "w", use it instead to get better layout result. If
-                     * we start a page with MAX_ZOOM_WIDTH, "w" will be always
-                     * wider. If we start a page with screen width, due to the
-                     * delay between {@link #didFirstLayout} and
-                     * {@link #viewSizeChanged},
-                     * {@link #nativeGetContentMinPrefWidth} will return a more
-                     * accurate value than initial 0 to result a better layout.
-                     * In the worse case, the native width will be adjusted when
-                     * next zoom or screen orientation change happens.
-                     */
-                    width = Math.min(WebView.sMaxViewportWidth, Math.max(viewWidth,
-                            Math.max(WebView.DEFAULT_VIEWPORT_WIDTH,
-                                    nativeGetContentMinPrefWidth())));
-                }
+                // Fixed viewport width.
+                width = WebView.DEFAULT_VIEWPORT_WIDTH;
             } else if (mViewportWidth > 0) {
-                if (mSettings.getUseFixedViewport()) {
-                    // Use website specified or desired fixed viewport width.
-                    width = mViewportWidth;
-                } else {
-                    width = Math.max(viewWidth, mViewportWidth);
-                }
-            } else if (mSettings.getUseFixedViewport()) {
-                width = mWebView.getViewWidth();
+                // Use website specified or desired fixed viewport width.
+                width = mViewportWidth;
             } else {
-                width = textwrapWidth;
+                // For mobile web site.
+                width = viewWidth;
             }
         }
         return width;
@@ -2025,7 +2002,8 @@
         if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw start");
         draw.mBaseLayer = nativeRecordContent(draw.mInvalRegion, draw.mContentSize);
         if (draw.mBaseLayer == 0) {
-            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort");
+            if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw abort, resending draw message");
+            mEventHub.sendMessage(Message.obtain(null, EventHub.WEBKIT_DRAW));
             return;
         }
         webkitDraw(draw);
@@ -2439,8 +2417,7 @@
                     // in zoom overview mode.
                     tentativeScale = mInitialViewState.mTextWrapScale;
                     int tentativeViewWidth = Math.round(webViewWidth / tentativeScale);
-                    int windowWidth = calculateWindowWidth(tentativeViewWidth,
-                            tentativeViewWidth);
+                    int windowWidth = calculateWindowWidth(tentativeViewWidth);
                     // In viewport setup time, since no content width is known, we assume
                     // the windowWidth will be the content width, to get a more likely
                     // zoom overview scale.
@@ -2449,8 +2426,7 @@
                         // If user choose non-overview mode.
                         data.mScale = Math.max(data.mScale, tentativeScale);
                     }
-                    if (mSettings.isNarrowColumnLayout() &&
-                            mSettings.getUseFixedViewport()) {
+                    if (mSettings.isNarrowColumnLayout()) {
                         // In case of automatic text reflow in fixed view port mode.
                         mInitialViewState.mTextWrapScale =
                                 ZoomManager.computeReadingLevelScale(data.mScale);
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index f2a1ec3..e330737 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1019,19 +1019,11 @@
         WebSettings settings = mWebView.getSettings();
         int newZoomOverviewWidth = mZoomOverviewWidth;
         if (settings.getUseWideViewPort()) {
-            if (!settings.getUseFixedViewport()) {
-                // limit mZoomOverviewWidth upper bound to
-                // sMaxViewportWidth so that if the page doesn't behave
-                // well, the WebView won't go insane. limit the lower
-                // bound to match the default scale for mobile sites.
-                newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth,
-                    Math.max((int) (viewWidth * mInvDefaultScale),
-                          Math.max(drawData.mMinPrefWidth, drawData.mViewSize.x)));
-            } else if (drawData.mContentSize.x > 0) {
+            if (drawData.mContentSize.x > 0) {
                 // The webkitDraw for layers will not populate contentSize, and it'll be
                 // ignored for zoom overview width update.
-                final int contentWidth = Math.max(drawData.mContentSize.x, drawData.mMinPrefWidth);
-                newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth, contentWidth);
+                newZoomOverviewWidth = Math.min(WebView.sMaxViewportWidth,
+                    drawData.mContentSize.x);
             }
         } else {
             // If not use wide viewport, use view width as the zoom overview width.
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index bac849e..6f30452 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -696,7 +696,8 @@
             mTotalLength += mDividerHeight;
         }
 
-        if (useLargestChild && heightMode == MeasureSpec.AT_MOST) {
+        if (useLargestChild &&
+                (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
             mTotalLength = 0;
 
             for (int i = 0; i < count; ++i) {
@@ -809,6 +810,31 @@
         } else {
             alternativeMaxWidth = Math.max(alternativeMaxWidth,
                                            weightedMaxWidth);
+
+
+            // We have no limit, so make all weighted views as tall as the largest child.
+            // Children will have already been measured once.
+            if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) {
+                for (int i = 0; i < count; i++) {
+                    final View child = getVirtualChildAt(i);
+
+                    if (child == null || child.getVisibility() == View.GONE) {
+                        continue;
+                    }
+
+                    final LinearLayout.LayoutParams lp =
+                            (LinearLayout.LayoutParams) child.getLayoutParams();
+
+                    float childExtra = lp.weight;
+                    if (childExtra > 0) {
+                        child.measure(
+                                MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
+                                        MeasureSpec.EXACTLY),
+                                MeasureSpec.makeMeasureSpec(largestChildHeight,
+                                        MeasureSpec.EXACTLY));
+                    }
+                }
+            }
         }
 
         if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
@@ -1044,7 +1070,8 @@
             maxHeight = Math.max(maxHeight, ascent + descent);
         }
 
-        if (useLargestChild && widthMode == MeasureSpec.AT_MOST) {
+        if (useLargestChild &&
+                (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED)) {
             mTotalLength = 0;
 
             for (int i = 0; i < count; ++i) {
@@ -1200,6 +1227,29 @@
             }
         } else {
             alternativeMaxHeight = Math.max(alternativeMaxHeight, weightedMaxHeight);
+
+            // We have no limit, so make all weighted views as wide as the largest child.
+            // Children will have already been measured once.
+            if (useLargestChild && widthMode == MeasureSpec.UNSPECIFIED) {
+                for (int i = 0; i < count; i++) {
+                    final View child = getVirtualChildAt(i);
+
+                    if (child == null || child.getVisibility() == View.GONE) {
+                        continue;
+                    }
+
+                    final LinearLayout.LayoutParams lp =
+                            (LinearLayout.LayoutParams) child.getLayoutParams();
+
+                    float childExtra = lp.weight;
+                    if (childExtra > 0) {
+                        child.measure(
+                                MeasureSpec.makeMeasureSpec(largestChildWidth, MeasureSpec.EXACTLY),
+                                MeasureSpec.makeMeasureSpec(child.getMeasuredHeight(),
+                                        MeasureSpec.EXACTLY));
+                    }
+                }
+            }
         }
 
         if (!allFillParent && heightMode != MeasureSpec.EXACTLY) {
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 71c91e1..c4ba7c8 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -116,7 +116,7 @@
 
     private static final int MIN_TIME_BETWEEN_INTERACTION_AND_AUTOADVANCE = 5000;
 
-    private static long MIN_TIME_BETWEEN_SCROLLS = 100;
+    private static final long MIN_TIME_BETWEEN_SCROLLS = 100;
 
     /**
      * These variables are all related to the current state of touch interaction
@@ -213,8 +213,7 @@
      * Animate the views between different relative indexes within the {@link AdapterViewAnimator}
      */
     void transformViewForTransition(int fromIndex, int toIndex, final View view, boolean animate) {
-        ObjectAnimator alphaOa = null;
-        ObjectAnimator oldAlphaOa = null;
+        ObjectAnimator alphaOa;
 
         if (!animate) {
             ((StackFrame) view).cancelSliderAnimator();
@@ -1276,13 +1275,11 @@
 
             boolean firstPass = true;
             parentRect.set(0, 0, 0, 0);
-            int depth = 0;
             while (p.getParent() != null && p.getParent() instanceof View
                     && !parentRect.contains(globalInvalidateRect)) {
                 if (!firstPass) {
                     globalInvalidateRect.offset(p.getLeft() - p.getScrollX(), p.getTop()
                             - p.getScrollY());
-                    depth++;
                 }
                 firstPass = false;
                 p = (View) p.getParent();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index d58c72b..3875765 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -16,11 +16,6 @@
 
 package android.widget;
 
-import com.android.internal.util.FastMath;
-import com.android.internal.widget.EditableInputConnection;
-
-import org.xmlpull.v1.XmlPullParserException;
-
 import android.R;
 import android.content.ClipData;
 import android.content.ClipData.Item;
@@ -129,6 +124,11 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.RemoteViews.RemoteView;
 
+import com.android.internal.util.FastMath;
+import com.android.internal.widget.EditableInputConnection;
+
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.lang.ref.WeakReference;
 import java.text.BreakIterator;
@@ -320,6 +320,7 @@
     private int mTextEditSuggestionItemLayout;
     private SuggestionsPopupWindow mSuggestionsPopupWindow;
     private SuggestionRangeSpan mSuggestionRangeSpan;
+    private boolean mSuggestionsEnabled = true;
 
     private int mCursorDrawableRes;
     private final Drawable[] mCursorDrawable = new Drawable[2];
@@ -329,7 +330,7 @@
     private Drawable mSelectHandleRight;
     private Drawable mSelectHandleCenter;
 
-    private int mLastDownPositionX, mLastDownPositionY;
+    private float mLastDownPositionX, mLastDownPositionY;
     private Callback mCustomSelectionActionModeCallback;
 
     private final int mSquaredTouchSlopDistance;
@@ -806,6 +807,10 @@
             case com.android.internal.R.styleable.TextView_textIsSelectable:
                 mTextIsSelectable = a.getBoolean(attr, false);
                 break;
+
+            case com.android.internal.R.styleable.TextView_suggestionsEnabled:
+                mSuggestionsEnabled = a.getBoolean(attr, true);
+                break;
             }
         }
         a.recycle();
@@ -2898,8 +2903,7 @@
         setText(mCharWrapper, mBufferType, false, oldlen);
     }
 
-    private static class CharWrapper
-            implements CharSequence, GetChars, GraphicsOperations {
+    private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations {
         private char[] mChars;
         private int mStart, mLength;
 
@@ -7323,8 +7327,8 @@
         }
 
         if (action == MotionEvent.ACTION_DOWN) {
-            mLastDownPositionX = (int) event.getX();
-            mLastDownPositionY = (int) event.getY();
+            mLastDownPositionX = event.getX();
+            mLastDownPositionY = event.getY();
 
             // Reset this state; it will be re-set if super.onTouchEvent
             // causes focus to move to the view.
@@ -7758,16 +7762,6 @@
                 hasPrimaryClip());
     }
 
-    private boolean isWordCharacter(int c, int type) {
-        return (c == '\'' || c == '"' ||
-                type == Character.UPPERCASE_LETTER ||
-                type == Character.LOWERCASE_LETTER ||
-                type == Character.TITLECASE_LETTER ||
-                type == Character.MODIFIER_LETTER ||
-                type == Character.OTHER_LETTER || // Should handle asian characters
-                type == Character.DECIMAL_DIGIT_NUMBER);
-    }
-
     private static long packRangeInLong(int start, int end) {
         return (((long) start) << 32) | end;
     }
@@ -8140,7 +8134,7 @@
         // Long press in empty space moves cursor and shows the Paste affordance if available.
         if (!isPositionOnText(mLastDownPositionX, mLastDownPositionY) &&
                 mInsertionControllerEnabled) {
-            final int offset = getOffset(mLastDownPositionX, mLastDownPositionY);
+            final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY);
             stopSelectionActionMode();
             Selection.setSelection((Spannable) mText, offset);
             getInsertionController().showWithPaste();
@@ -8208,7 +8202,7 @@
         private final ViewGroup[] mSuggestionViews = new ViewGroup[2];
         private final int[] mSuggestionViewLayouts = new int[] {
                 mTextEditSuggestionsBottomWindowLayout, mTextEditSuggestionsTopWindowLayout};
-        private WordIterator mWordIterator;
+        private WordIterator mSuggestionWordIterator;
         private TextAppearanceSpan[] mHighlightSpans = new TextAppearanceSpan[0];
 
         public SuggestionsPopupWindow() {
@@ -8344,26 +8338,27 @@
         }
 
         private long[] getWordLimits(CharSequence text) {
-            if (mWordIterator == null) mWordIterator = new WordIterator(); // TODO locale
-            mWordIterator.setCharSequence(text);
+            // TODO locale for mSuggestionWordIterator
+            if (mSuggestionWordIterator == null) mSuggestionWordIterator = new WordIterator();
+            mSuggestionWordIterator.setCharSequence(text);
 
             // First pass will simply count the number of words to be able to create an array
             // Not too expensive since previous break positions are cached by the BreakIterator
             int nbWords = 0;
-            int position = mWordIterator.following(0);
+            int position = mSuggestionWordIterator.following(0);
             while (position != BreakIterator.DONE) {
                 nbWords++;
-                position = mWordIterator.following(position);
+                position = mSuggestionWordIterator.following(position);
             }
 
             int index = 0;
             long[] result = new long[nbWords];
 
-            position = mWordIterator.following(0);
+            position = mSuggestionWordIterator.following(0);
             while (position != BreakIterator.DONE) {
-                int wordStart = mWordIterator.getBeginning(position);
+                int wordStart = mSuggestionWordIterator.getBeginning(position);
                 result[index++] = packRangeInLong(wordStart, position);
-                position = mWordIterator.following(position);
+                position = mSuggestionWordIterator.following(position);
             }
 
             return result;
@@ -8601,6 +8596,8 @@
     }
 
     void showSuggestions() {
+        if (!mSuggestionsEnabled || !isTextEditable()) return;
+
         if (mSuggestionsPopupWindow == null) {
             mSuggestionsPopupWindow = new SuggestionsPopupWindow();
         }
@@ -8615,6 +8612,31 @@
     }
 
     /**
+     * Some parts of the text can have alternate suggestion text attached. This is typically done by
+     * the IME by adding {@link SuggestionSpan}s to the text.
+     *
+     * When suggestions are enabled (default), this list of suggestions will be displayed when the
+     * user double taps on these parts of the text. No suggestions are displayed when this value is
+     * false. Use {@link #setSuggestionsEnabled(boolean)} to change this value.
+     *
+     * @return true if the suggestions popup window is enabled.
+     *
+     * @attr ref android.R.styleable#TextView_suggestionsEnabled
+     */
+    public boolean isSuggestionsEnabled() {
+        return mSuggestionsEnabled;
+    }
+
+    /**
+     * Enables or disables the suggestion popup. See {@link #isSuggestionsEnabled()}.
+     *
+     * @param enabled Whether or not suggestions are enabled.
+     */
+    public void setSuggestionsEnabled(boolean enabled) {
+        mSuggestionsEnabled = enabled;
+    }
+
+    /**
      * If provided, this ActionMode.Callback will be used to create the ActionMode when text
      * selection is initiated in this View.
      *
@@ -9115,7 +9137,7 @@
 
         public abstract void updateOffset(int offset);
 
-        public abstract void updatePosition(int x, int y);
+        public abstract void updatePosition(float x, float y);
 
         protected void positionAtCursorOffset(int offset) {
             addPositionToTouchUpFilter(offset);
@@ -9215,7 +9237,7 @@
                     final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
                     final float newPosY = rawY - mTouchToWindowOffsetY + mTouchOffsetY;
 
-                    updatePosition(Math.round(newPosX), Math.round(newPosY));
+                    updatePosition(newPosX, newPosY);
                     break;
                 }
 
@@ -9366,8 +9388,8 @@
         }
 
         @Override
-        public void updatePosition(int x, int y) {
-            updateOffset(getOffset(x, y));
+        public void updatePosition(float x, float y) {
+            updateOffset(getOffsetForPosition(x, y));
         }
 
         void showPastePopupWindow() {
@@ -9421,11 +9443,11 @@
         }
 
         @Override
-        public void updatePosition(int x, int y) {
+        public void updatePosition(float x, float y) {
             final int selectionStart = getSelectionStart();
             final int selectionEnd = getSelectionEnd();
 
-            int offset = getOffset(x, y);
+            int offset = getOffsetForPosition(x, y);
 
             // No need to redraw when the offset is unchanged
             if (offset == selectionStart) return;
@@ -9458,11 +9480,11 @@
         }
 
         @Override
-        public void updatePosition(int x, int y) {
+        public void updatePosition(float x, float y) {
             final int selectionStart = getSelectionStart();
             final int selectionEnd = getSelectionEnd();
 
-            int offset = getOffset(x, y);
+            int offset = getOffsetForPosition(x, y);
 
             // No need to redraw when the offset is unchanged
             if (offset == selectionEnd) return;
@@ -9560,7 +9582,7 @@
 
         // Double tap detection
         private long mPreviousTapUpTime = 0;
-        private int mPreviousTapPositionX, mPreviousTapPositionY;
+        private float mPreviousTapPositionX, mPreviousTapPositionY;
 
         SelectionModifierCursorController() {
             resetTouchOffsets();
@@ -9593,19 +9615,19 @@
             if (isTextEditable() || mTextIsSelectable) {
                 switch (event.getActionMasked()) {
                     case MotionEvent.ACTION_DOWN:
-                        final int x = (int) event.getX();
-                        final int y = (int) event.getY();
+                        final float x = event.getX();
+                        final float y = event.getY();
 
                         // Remember finger down position, to be able to start selection from there
-                        mMinTouchOffset = mMaxTouchOffset = getOffset(x, y);
+                        mMinTouchOffset = mMaxTouchOffset = getOffsetForPosition(x, y);
 
                         // Double tap detection
                         long duration = SystemClock.uptimeMillis() - mPreviousTapUpTime;
                         if (duration <= ViewConfiguration.getDoubleTapTimeout() &&
                                 isPositionOnText(x, y)) {
-                            final int deltaX = x - mPreviousTapPositionX;
-                            final int deltaY = y - mPreviousTapPositionY;
-                            final int distanceSquared = deltaX * deltaX + deltaY * deltaY;
+                            final float deltaX = x - mPreviousTapPositionX;
+                            final float deltaY = y - mPreviousTapPositionY;
+                            final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
                             if (distanceSquared < mSquaredTouchSlopDistance) {
                                 showSuggestions();
                                 mDiscardNextActionUp = true;
@@ -9641,9 +9663,7 @@
         private void updateMinAndMaxOffsets(MotionEvent event) {
             int pointerCount = event.getPointerCount();
             for (int index = 0; index < pointerCount; index++) {
-                final int x = (int) event.getX(index);
-                final int y = (int) event.getY(index);
-                int offset = getOffset(x, y);
+                int offset = getOffsetForPosition(event.getX(index), event.getY(index));
                 if (offset < mMinTouchOffset) mMinTouchOffset = offset;
                 if (offset > mMaxTouchOffset) mMaxTouchOffset = offset;
             }
@@ -9701,41 +9721,40 @@
     }
 
     /**
-     * Get the offset character closest to the specified absolute position.
+     * Get the character offset closest to the specified absolute position. A typical use case is to
+     * pass the result of {@link MotionEvent#getX()} and {@link MotionEvent#getY()} to this method.
      *
      * @param x The horizontal absolute position of a point on screen
      * @param y The vertical absolute position of a point on screen
      * @return the character offset for the character whose position is closest to the specified
      *  position. Returns -1 if there is no layout.
-     *
-     * @hide
      */
-    public int getOffset(int x, int y) {
+    public int getOffsetForPosition(float x, float y) {
         if (getLayout() == null) return -1;
         final int line = getLineAtCoordinate(y);
         final int offset = getOffsetAtCoordinate(line, x);
         return offset;
     }
 
-    private int convertToLocalHorizontalCoordinate(int x) {
+    private float convertToLocalHorizontalCoordinate(float x) {
         x -= getTotalPaddingLeft();
         // Clamp the position to inside of the view.
-        x = Math.max(0, x);
+        x = Math.max(0.0f, x);
         x = Math.min(getWidth() - getTotalPaddingRight() - 1, x);
         x += getScrollX();
         return x;
     }
 
-    private int getLineAtCoordinate(int y) {
+    private int getLineAtCoordinate(float y) {
         y -= getTotalPaddingTop();
         // Clamp the position to inside of the view.
-        y = Math.max(0, y);
+        y = Math.max(0.0f, y);
         y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y);
         y += getScrollY();
-        return getLayout().getLineForVertical(y);
+        return getLayout().getLineForVertical((int) y);
     }
 
-    private int getOffsetAtCoordinate(int line, int x) {
+    private int getOffsetAtCoordinate(int line, float x) {
         x = convertToLocalHorizontalCoordinate(x);
         return getLayout().getOffsetForHorizontal(line, x);
     }
@@ -9743,7 +9762,7 @@
     /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed
      * in the view. Returns false when the position is in the empty space of left/right of text.
      */
-    private boolean isPositionOnText(int x, int y) {
+    private boolean isPositionOnText(float x, float y) {
         if (getLayout() == null) return false;
 
         final int line = getLineAtCoordinate(y);
@@ -9765,7 +9784,7 @@
                 return true;
 
             case DragEvent.ACTION_DRAG_LOCATION:
-                final int offset = getOffset((int) event.getX(), (int) event.getY());
+                final int offset = getOffsetForPosition(event.getX(), event.getY());
                 Selection.setSelection((Spannable)mText, offset);
                 return true;
 
@@ -9789,7 +9808,7 @@
             content.append(item.coerceToText(TextView.this.mContext));
         }
 
-        final int offset = getOffset((int) event.getX(), (int) event.getY());
+        final int offset = getOffsetForPosition(event.getX(), event.getY());
 
         Object localState = event.getLocalState();
         DragLocalState dragLocalState = null;
diff --git a/core/java/com/android/internal/app/ActionBarImpl.java b/core/java/com/android/internal/app/ActionBarImpl.java
index 1e576ce..183cfbd 100644
--- a/core/java/com/android/internal/app/ActionBarImpl.java
+++ b/core/java/com/android/internal/app/ActionBarImpl.java
@@ -16,25 +16,27 @@
 
 package com.android.internal.app;
 
+import com.android.internal.R;
 import com.android.internal.view.menu.MenuBuilder;
 import com.android.internal.view.menu.MenuPopupHelper;
 import com.android.internal.view.menu.SubMenuBuilder;
-import com.android.internal.widget.AbsActionBarView;
 import com.android.internal.widget.ActionBarContainer;
 import com.android.internal.widget.ActionBarContextView;
 import com.android.internal.widget.ActionBarView;
+import com.android.internal.widget.ScrollingTabContainerView;
 
 import android.animation.Animator;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.animation.TimeInterpolator;
 import android.app.ActionBar;
 import android.app.Activity;
 import android.app.Dialog;
 import android.app.FragmentTransaction;
 import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.view.ActionMode;
@@ -43,10 +45,7 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.Window;
-import android.view.animation.DecelerateInterpolator;
-import android.widget.HorizontalScrollView;
 import android.widget.SpinnerAdapter;
 
 import java.lang.ref.WeakReference;
@@ -71,7 +70,7 @@
     private ActionBarContextView mContextView;
     private ActionBarContainer mSplitView;
     private View mContentView;
-    private ViewGroup mExternalTabView;
+    private ScrollingTabContainerView mTabScrollView;
 
     private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
 
@@ -90,16 +89,17 @@
     private static final int INVALID_POSITION = -1;
 
     private int mContextDisplayMode;
+    private boolean mHasEmbeddedTabs;
+    private int mContentHeight;
 
     final Handler mHandler = new Handler();
+    Runnable mTabSelector;
 
     private Animator mCurrentShowAnim;
     private Animator mCurrentModeAnim;
     private boolean mShowHideAnimationEnabled;
     boolean mWasHiddenBeforeMode;
 
-    private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator();
-
     final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
         @Override
         public void onAnimationEnd(Animator animation) {
@@ -150,21 +150,59 @@
                     "with a compatible window decor layout");
         }
 
+        mHasEmbeddedTabs = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.action_bar_embed_tabs);
         mActionView.setContextView(mContextView);
         mContextDisplayMode = mActionView.isSplitActionBar() ?
                 CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL;
 
-        if (!mActionView.hasEmbeddedTabs()) {
-            HorizontalScrollView tabScroller = new HorizontalScrollView(mContext);
-            ViewGroup tabContainer = mActionView.createTabContainer();
-            tabScroller.setHorizontalFadingEdgeEnabled(true);
-            tabScroller.addView(tabContainer);
+        TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar);
+        mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
+        a.recycle();
+    }
+
+    public void onConfigurationChanged(Configuration newConfig) {
+        mHasEmbeddedTabs = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.action_bar_embed_tabs);
+
+        // Switch tab layout configuration if needed
+        if (!mHasEmbeddedTabs) {
+            mActionView.setEmbeddedTabView(null);
+            mContainerView.setTabContainer(mTabScrollView);
+        } else {
+            mContainerView.setTabContainer(null);
+            if (mTabScrollView != null) {
+                mTabScrollView.setVisibility(View.VISIBLE);
+            }
+            mActionView.setEmbeddedTabView(mTabScrollView);
+        }
+
+        TypedArray a = mContext.obtainStyledAttributes(null, R.styleable.ActionBar);
+        mContentHeight = a.getLayoutDimension(R.styleable.ActionBar_height, 0);
+        a.recycle();
+
+        if (mTabScrollView != null) {
+            mTabScrollView.getLayoutParams().height = mContentHeight;
+            mTabScrollView.requestLayout();
+        }
+    }
+
+    private void ensureTabsExist() {
+        if (mTabScrollView != null) {
+            return;
+        }
+
+        ScrollingTabContainerView tabScroller = mActionView.createTabContainer();
+
+        if (mHasEmbeddedTabs) {
+            tabScroller.setVisibility(View.VISIBLE);
+            mActionView.setEmbeddedTabView(tabScroller);
+        } else {
             tabScroller.setVisibility(getNavigationMode() == NAVIGATION_MODE_TABS ?
                     View.VISIBLE : View.GONE);
-            mActionView.setExternalTabLayout(tabContainer);
             mContainerView.setTabContainer(tabScroller);
-            mExternalTabView = tabScroller;
         }
+        mTabScrollView = tabScroller;
     }
 
     /**
@@ -269,7 +307,7 @@
             selectTab(null);
         }
         mTabs.clear();
-        mActionView.removeAllTabs();
+        mTabScrollView.removeAllTabs();
         mSavedTabPosition = INVALID_POSITION;
     }
 
@@ -365,7 +403,8 @@
 
     @Override
     public void addTab(Tab tab, boolean setSelected) {
-        mActionView.addTab(tab, setSelected);
+        ensureTabsExist();
+        mTabScrollView.addTab(tab, setSelected);
         configureTab(tab, mTabs.size());
         if (setSelected) {
             selectTab(tab);
@@ -374,7 +413,8 @@
 
     @Override
     public void addTab(Tab tab, int position, boolean setSelected) {
-        mActionView.addTab(tab, position, setSelected);
+        ensureTabsExist();
+        mTabScrollView.addTab(tab, position, setSelected);
         configureTab(tab, position);
         if (setSelected) {
             selectTab(tab);
@@ -393,9 +433,14 @@
 
     @Override
     public void removeTabAt(int position) {
+        if (mTabScrollView == null) {
+            // No tabs around to remove
+            return;
+        }
+
         int selectedTabPosition = mSelectedTab != null
                 ? mSelectedTab.getPosition() : mSavedTabPosition;
-        mActionView.removeTabAt(position);
+        mTabScrollView.removeTabAt(position);
         TabImpl removedTab = mTabs.remove(position);
         if (removedTab != null) {
             removedTab.setPosition(-1);
@@ -424,9 +469,10 @@
         if (mSelectedTab == tab) {
             if (mSelectedTab != null) {
                 mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
+                mTabScrollView.animateToTab(tab.getPosition());
             }
         } else {
-            mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
+            mTabScrollView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
             if (mSelectedTab != null) {
                 mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
             }
@@ -705,7 +751,9 @@
         @Override
         public Tab setCustomView(View view) {
             mCustomView = view;
-            if (mPosition >= 0) mActionView.updateTab(mPosition);
+            if (mPosition >= 0) {
+                mTabScrollView.updateTab(mPosition);
+            }
             return this;
         }
 
@@ -736,7 +784,9 @@
         @Override
         public Tab setIcon(Drawable icon) {
             mIcon = icon;
-            if (mPosition >= 0) mActionView.updateTab(mPosition);
+            if (mPosition >= 0) {
+                mTabScrollView.updateTab(mPosition);
+            }
             return this;
         }
 
@@ -748,7 +798,9 @@
         @Override
         public Tab setText(CharSequence text) {
             mText = text;
-            if (mPosition >= 0) mActionView.updateTab(mPosition);
+            if (mPosition >= 0) {
+                mTabScrollView.updateTab(mPosition);
+            }
             return this;
         }
 
@@ -818,15 +870,16 @@
                 mSavedTabPosition = getSelectedNavigationIndex();
                 selectTab(null);
                 if (!mActionView.hasEmbeddedTabs()) {
-                    mExternalTabView.setVisibility(View.GONE);
+                    mTabScrollView.setVisibility(View.GONE);
                 }
                 break;
         }
         mActionView.setNavigationMode(mode);
         switch (mode) {
             case NAVIGATION_MODE_TABS:
+                ensureTabsExist();
                 if (!mActionView.hasEmbeddedTabs()) {
-                    mExternalTabView.setVisibility(View.VISIBLE);
+                    mTabScrollView.setVisibility(View.VISIBLE);
                 }
                 if (mSavedTabPosition != INVALID_POSITION) {
                     setSelectedNavigationItem(mSavedTabPosition);
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 0f086f6..5e9cd23 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -32,7 +32,6 @@
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.LogManager;
 import java.util.TimeZone;
 
@@ -45,6 +44,7 @@
  */
 public class RuntimeInit {
     private final static String TAG = "AndroidRuntime";
+    private final static boolean DEBUG = false;
 
     /** true if commonInit() has been called */
     private static boolean initialized;
@@ -89,14 +89,14 @@
     }
 
     private static final void commonInit() {
-        if (false) Slog.d(TAG, "Entered RuntimeInit!");
+        if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
 
         /* set default handler; this applies to all threads in the VM */
         Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
 
         int hasQwerty = getQwertyKeyboard();
 
-        if (false) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
+        if (DEBUG) Slog.d(TAG, ">>>>> qwerty keyboard = " + hasQwerty);
         if (hasQwerty == 1) {
             System.setProperty("qwerty", "1");
         }
@@ -183,11 +183,6 @@
      */
     private static void invokeStaticMain(String className, String[] argv)
             throws ZygoteInit.MethodAndArgsCaller {
-
-        // We want to be fairly aggressive about heap utilization, to avoid
-        // holding on to a lot of memory that isn't needed.
-        VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
-
         Class<?> cl;
 
         try {
@@ -225,6 +220,13 @@
     }
 
     public static final void main(String[] argv) {
+        if (argv.length == 2 && argv[1].equals("application")) {
+            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
+            redirectLogStreams();
+        } else {
+            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
+        }
+
         commonInit();
 
         /*
@@ -233,7 +235,7 @@
          */
         finishInit();
 
-        if (false) Slog.d(TAG, "Leaving RuntimeInit!");
+        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
     }
 
     public static final native void finishInit();
@@ -245,7 +247,6 @@
      *
      * Current recognized args:
      * <ul>
-     *   <li> --nice-name=<i>nice name to appear in ps</i>
      *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
      * </ul>
      *
@@ -253,45 +254,60 @@
      */
     public static final void zygoteInit(String[] argv)
             throws ZygoteInit.MethodAndArgsCaller {
-        // TODO: Doing this here works, but it seems kind of arbitrary. Find
-        // a better place. The goal is to set it up for applications, but not
-        // tools like am.
-        System.out.close();
-        System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
-        System.err.close();
-        System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
+        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
+
+        redirectLogStreams();
 
         commonInit();
         zygoteInitNative();
 
-        int curArg = 0;
-        for ( /* curArg */ ; curArg < argv.length; curArg++) {
-            String arg = argv[curArg];
+        applicationInit(argv);
+    }
 
-            if (arg.equals("--")) {
-                curArg++;
-                break;
-            } else if (!arg.startsWith("--")) {
-                break;
-            } else if (arg.startsWith("--nice-name=")) {
-                String niceName = arg.substring(arg.indexOf('=') + 1);
-                Process.setArgV0(niceName);
-            }
-        }
+    /**
+     * The main function called when an application is started through a
+     * wrapper process.
+     *
+     * When the wrapper starts, the runtime starts {@link RuntimeInit#main}
+     * which calls {@link WrapperInit#main} which then calls this method.
+     * So we don't need to call commonInit() here.
+     *
+     * @param argv arg strings
+     */
+    public static void wrapperInit(String[] argv)
+            throws ZygoteInit.MethodAndArgsCaller {
+        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper");
 
-        if (curArg == argv.length) {
-            Slog.e(TAG, "Missing classname argument to RuntimeInit!");
+        applicationInit(argv);
+    }
+
+    private static void applicationInit(String[] argv)
+            throws ZygoteInit.MethodAndArgsCaller {
+        // We want to be fairly aggressive about heap utilization, to avoid
+        // holding on to a lot of memory that isn't needed.
+        VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
+
+        final Arguments args;
+        try {
+            args = new Arguments(argv);
+        } catch (IllegalArgumentException ex) {
+            Slog.e(TAG, ex.getMessage());
             // let the process exit
             return;
         }
 
         // Remaining arguments are passed to the start class's static main
+        invokeStaticMain(args.startClass, args.startArgs);
+    }
 
-        String startClass = argv[curArg++];
-        String[] startArgs = new String[argv.length - curArg];
-
-        System.arraycopy(argv, curArg, startArgs, 0, startArgs.length);
-        invokeStaticMain(startClass, startArgs);
+    /**
+     * Redirect System.out and System.err to the Android log.
+     */
+    public static void redirectLogStreams() {
+        System.out.close();
+        System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
+        System.err.close();
+        System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));
     }
 
     public static final native void zygoteInitNative();
@@ -351,4 +367,55 @@
         // Register handlers for DDM messages.
         android.ddm.DdmRegister.registerHandlers();
     }
+
+    /**
+     * Handles argument parsing for args related to the runtime.
+     *
+     * Current recognized args:
+     * <ul>
+     *   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;
+     * </ul>
+     */
+    static class Arguments {
+        /** first non-option argument */
+        String startClass;
+
+        /** all following arguments */
+        String[] startArgs;
+
+        /**
+         * Constructs instance and parses args
+         * @param args runtime command-line args
+         * @throws IllegalArgumentException
+         */
+        Arguments(String args[]) throws IllegalArgumentException {
+            parseArgs(args);
+        }
+
+        /**
+         * Parses the commandline arguments intended for the Runtime.
+         */
+        private void parseArgs(String args[])
+                throws IllegalArgumentException {
+            int curArg = 0;
+            for (; curArg < args.length; curArg++) {
+                String arg = args[curArg];
+
+                if (arg.equals("--")) {
+                    curArg++;
+                    break;
+                } else if (!arg.startsWith("--")) {
+                    break;
+                }
+            }
+
+            if (curArg == args.length) {
+                throw new IllegalArgumentException("Missing classname argument to RuntimeInit!");
+            }
+
+            startClass = args[curArg++];
+            startArgs = new String[args.length - curArg];
+            System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
new file mode 100644
index 0000000..18d6caa
--- /dev/null
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.Process;
+import android.util.Slog;
+
+import java.io.DataOutputStream;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
+import dalvik.system.Zygote;
+
+/**
+ * Startup class for the wrapper process.
+ * @hide
+ */
+public class WrapperInit {
+    private final static String TAG = "AndroidRuntime";
+
+    /**
+     * Class not instantiable.
+     */
+    private WrapperInit() {
+    }
+
+    /**
+     * The main function called when starting a runtime application through a
+     * wrapper process instead of by forking Zygote.
+     *
+     * The first argument specifies the file descriptor for a pipe that should receive
+     * the pid of this process, or 0 if none.  The remaining arguments are passed to
+     * the runtime.
+     *
+     * @param args The command-line arguments.
+     */
+    public static void main(String[] args) {
+        try {
+            int fdNum = Integer.parseInt(args[0], 10);
+            if (fdNum != 0) {
+                try {
+                    FileDescriptor fd = ZygoteInit.createFileDescriptor(fdNum);
+                    DataOutputStream os = new DataOutputStream(new FileOutputStream(fd));
+                    os.writeInt(Process.myPid());
+                    os.close();
+                    IoUtils.closeQuietly(fd);
+                } catch (IOException ex) {
+                    Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex);
+                }
+            }
+
+            String[] runtimeArgs = new String[args.length - 1];
+            System.arraycopy(args, 1, runtimeArgs, 0, runtimeArgs.length);
+            RuntimeInit.wrapperInit(runtimeArgs);
+        } catch (ZygoteInit.MethodAndArgsCaller caller) {
+            caller.run();
+        }
+    }
+
+    /**
+     * Executes a runtime application with a wrapper command.
+     * This method never returns.
+     *
+     * @param invokeWith The wrapper command.
+     * @param niceName The nice name for the application, or null if none.
+     * @param pipeFd The pipe to which the application's pid should be written, or null if none.
+     * @param args Arguments for {@link RuntimeInit.main}.
+     */
+    public static void execApplication(String invokeWith, String niceName,
+            FileDescriptor pipeFd, String[] args) {
+        StringBuilder command = new StringBuilder(invokeWith);
+        command.append(" /system/bin/app_process /system/bin --application");
+        if (niceName != null) {
+            command.append(" '--nice-name=").append(niceName).append("'");
+        }
+        command.append(" com.android.internal.os.WrapperInit ");
+        command.append(pipeFd != null ? pipeFd.getInt$() : 0);
+        Zygote.appendQuotedShellArgs(command, args);
+        Zygote.execShell(command.toString());
+    }
+
+    /**
+     * Executes a standalone application with a wrapper command.
+     * This method never returns.
+     *
+     * @param invokeWith The wrapper command.
+     * @param classPath The class path.
+     * @param className The class name to invoke.
+     * @param args Arguments for the main() method of the specified class.
+     */
+    public static void execStandalone(String invokeWith, String classPath, String className,
+            String[] args) {
+        StringBuilder command = new StringBuilder(invokeWith);
+        command.append(" /system/bin/dalvikvm -classpath '").append(classPath);
+        command.append("' ").append(className);
+        Zygote.appendQuotedShellArgs(command, args);
+        Zygote.execShell(command.toString());
+    }
+}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index c473fd2..b872e22 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -26,14 +26,20 @@
 import dalvik.system.Zygote;
 
 import java.io.BufferedReader;
+import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
 import java.util.ArrayList;
 
+import libcore.io.ErrnoException;
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
+
 /**
  * A connection that can make spawn requests.
  */
@@ -193,15 +199,20 @@
                     new FileOutputStream(descriptors[2]));
         }
 
-        int pid;
+        int pid = -1;
+        FileDescriptor childPipeFd = null;
+        FileDescriptor serverPipeFd = null;
 
         try {
             parsedArgs = new Arguments(args);
 
             applyUidSecurityPolicy(parsedArgs, peer);
-            applyDebuggerSecurityPolicy(parsedArgs);
             applyRlimitSecurityPolicy(parsedArgs, peer);
             applyCapabilitiesSecurityPolicy(parsedArgs, peer);
+            applyInvokeWithSecurityPolicy(parsedArgs, peer);
+
+            applyDebuggerSystemProperty(parsedArgs);
+            applyInvokeWithSystemProperty(parsedArgs);
 
             int[][] rlimits = null;
 
@@ -209,25 +220,45 @@
                 rlimits = parsedArgs.rlimits.toArray(intArray2d);
             }
 
+            if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) {
+                FileDescriptor[] pipeFds = Libcore.os.pipe();
+                childPipeFd = pipeFds[1];
+                serverPipeFd = pipeFds[0];
+                ZygoteInit.setCloseOnExec(serverPipeFd, true);
+            }
+
             pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid,
                     parsedArgs.gids, parsedArgs.debugFlags, rlimits);
+        } catch (IOException ex) {
+            logAndPrintError(newStderr, "Exception creating pipe", ex);
+        } catch (ErrnoException ex) {
+            logAndPrintError(newStderr, "Exception creating pipe", ex);
         } catch (IllegalArgumentException ex) {
-            logAndPrintError (newStderr, "Invalid zygote arguments", ex);
-            pid = -1;
+            logAndPrintError(newStderr, "Invalid zygote arguments", ex);
         } catch (ZygoteSecurityException ex) {
             logAndPrintError(newStderr,
                     "Zygote security policy prevents request: ", ex);
-            pid = -1;
         }
 
-        if (pid == 0) {
-            // in child
-            handleChildProc(parsedArgs, descriptors, newStderr);
-            // should never happen
-            return true;
-        } else { /* pid != 0 */
-            // in parent...pid of < 0 means failure
-            return handleParentProc(pid, descriptors, parsedArgs);
+        try {
+            if (pid == 0) {
+                // in child
+                IoUtils.closeQuietly(serverPipeFd);
+                serverPipeFd = null;
+                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
+
+                // should never get here, the child is expected to either
+                // throw ZygoteInit.MethodAndArgsCaller or exec().
+                return true;
+            } else {
+                // in parent...pid of < 0 means failure
+                IoUtils.closeQuietly(childPipeFd);
+                childPipeFd = null;
+                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
+            }
+        } finally {
+            IoUtils.closeQuietly(childPipeFd);
+            IoUtils.closeQuietly(serverPipeFd);
         }
     }
 
@@ -244,8 +275,8 @@
     }
 
     /**
-     * Handles argument parsing for args related to the zygote spawner.<p>
-
+     * Handles argument parsing for args related to the zygote spawner.
+     *
      * Current recognized args:
      * <ul>
      *   <li> --setuid=<i>uid of child process, defaults to 0</i>
@@ -274,6 +305,7 @@
      * be handed off to com.android.internal.os.RuntimeInit, rather than
      * processed directly
      * Android runtime startup (eg, Binder initialization) is also eschewed.
+     *   <li> --nice-name=<i>nice name to appear in ps</i>
      *   <li> If <code>--runtime-init</code> is present:
      *      [--] &lt;args for RuntimeInit &gt;
      *   <li> If <code>--runtime-init</code> is absent:
@@ -307,6 +339,9 @@
         /** from --runtime-init */
         boolean runtimeInit;
 
+        /** from --nice-name */
+        String niceName;
+
         /** from --capabilities */
         boolean capabilitiesSpecified;
         long permittedCapabilities;
@@ -315,6 +350,9 @@
         /** from all --rlimit=r,c,m */
         ArrayList<int[]> rlimits;
 
+        /** from --invoke-with */
+        String invokeWith;
+
         /**
          * Any args after and including the first non-option arg
          * (or after a '--')
@@ -438,6 +476,23 @@
                     for (int i = params.length - 1; i >= 0 ; i--) {
                         gids[i] = Integer.parseInt(params[i]);
                     }
+                } else if (arg.equals("--invoke-with")) {
+                    if (invokeWith != null) {
+                        throw new IllegalArgumentException(
+                                "Duplicate arg specified");
+                    }
+                    try {
+                        invokeWith = args[++curArg];
+                    } catch (IndexOutOfBoundsException ex) {
+                        throw new IllegalArgumentException(
+                                "--invoke-with requires argument");
+                    }
+                } else if (arg.startsWith("--nice-name=")) {
+                    if (niceName != null) {
+                        throw new IllegalArgumentException(
+                                "Duplicate arg specified");
+                    }
+                    niceName = arg.substring(arg.indexOf('=') + 1);
                 } else {
                     break;
                 }
@@ -567,14 +622,15 @@
 
 
     /**
-     * Applies debugger security policy.
+     * Applies debugger system properties to the zygote arguments.
+     *
      * If "ro.debuggable" is "1", all apps are debuggable. Otherwise,
      * the debugger state is specified via the "--enable-debugger" flag
      * in the spawn request.
      *
      * @param args non-null; zygote spawner args
      */
-    private static void applyDebuggerSecurityPolicy(Arguments args) {
+    public static void applyDebuggerSystemProperty(Arguments args) {
         if ("1".equals(SystemProperties.get("ro.debuggable"))) {
             args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
         }
@@ -664,12 +720,56 @@
     }
 
     /**
+     * Applies zygote security policy.
+     * Based on the credentials of the process issuing a zygote command:
+     * <ol>
+     * <li> uid 0 (root) may specify --invoke-with to launch Zygote with a
+     * wrapper command.
+     * <li> Any other uid may not specify any invoke-with argument.
+     * </ul>
+     *
+     * @param args non-null; zygote spawner arguments
+     * @param peer non-null; peer credentials
+     * @throws ZygoteSecurityException
+     */
+    private static void applyInvokeWithSecurityPolicy(Arguments args, Credentials peer)
+            throws ZygoteSecurityException {
+        int peerUid = peer.getUid();
+
+        if (args.invokeWith != null && peerUid != 0) {
+            throw new ZygoteSecurityException("Peer is not permitted to specify "
+                    + "an explicit invoke-with wrapper command");
+        }
+    }
+
+    /**
+     * Applies invoke-with system properties to the zygote arguments.
+     *
+     * @param parsedArgs non-null; zygote args
+     */
+    public static void applyInvokeWithSystemProperty(Arguments args) {
+        if (args.invokeWith == null && args.niceName != null) {
+            if (args.niceName != null) {
+                String property = "wrap." + args.niceName;
+                if (property.length() > 31) {
+                    property = property.substring(0, 31);
+                }
+                args.invokeWith = SystemProperties.get(property);
+                if (args.invokeWith != null && args.invokeWith.length() == 0) {
+                    args.invokeWith = null;
+                }
+            }
+        }
+    }
+
+    /**
      * Handles post-fork setup of child proc, closing sockets as appropriate,
      * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
      * if successful or returning if failed.
      *
      * @param parsedArgs non-null; zygote args
      * @param descriptors null-ok; new file descriptors for stdio if available.
+     * @param pipeFd null-ok; pipe for communication back to Zygote.
      * @param newStderr null-ok; stream to use for stderr until stdio
      * is reopened.
      *
@@ -677,7 +777,7 @@
      * trampoline to code that invokes static main.
      */
     private void handleChildProc(Arguments parsedArgs,
-            FileDescriptor[] descriptors, PrintStream newStderr)
+            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
             throws ZygoteInit.MethodAndArgsCaller {
 
         /*
@@ -704,7 +804,7 @@
                         descriptors[1], descriptors[2]);
 
                 for (FileDescriptor fd: descriptors) {
-                    ZygoteInit.closeDescriptor(fd);
+                    IoUtils.closeQuietly(fd);
                 }
                 newStderr = System.err;
             } catch (IOException ex) {
@@ -712,37 +812,48 @@
             }
         }
 
+        if (parsedArgs.niceName != null) {
+            Process.setArgV0(parsedArgs.niceName);
+        }
+
         if (parsedArgs.runtimeInit) {
-            RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
-        } else {
-            ClassLoader cloader;
-
-            if (parsedArgs.classpath != null) {
-                cloader
-                    = new PathClassLoader(parsedArgs.classpath,
-                    ClassLoader.getSystemClassLoader());
+            if (parsedArgs.invokeWith != null) {
+                WrapperInit.execApplication(parsedArgs.invokeWith,
+                        parsedArgs.niceName, pipeFd, parsedArgs.remainingArgs);
             } else {
-                cloader = ClassLoader.getSystemClassLoader();
+                RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
             }
-
+        } else {
             String className;
             try {
                 className = parsedArgs.remainingArgs[0];
             } catch (ArrayIndexOutOfBoundsException ex) {
-                logAndPrintError (newStderr,
+                logAndPrintError(newStderr,
                         "Missing required class name argument", null);
                 return;
             }
-            String[] mainArgs
-                    = new String[parsedArgs.remainingArgs.length - 1];
 
+            String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
             System.arraycopy(parsedArgs.remainingArgs, 1,
                     mainArgs, 0, mainArgs.length);
 
-            try {
-                ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
-            } catch (RuntimeException ex) {
-                logAndPrintError (newStderr, "Error starting. ", ex);
+            if (parsedArgs.invokeWith != null) {
+                WrapperInit.execStandalone(parsedArgs.invokeWith,
+                        parsedArgs.classpath, className, mainArgs);
+            } else {
+                ClassLoader cloader;
+                if (parsedArgs.classpath != null) {
+                    cloader = new PathClassLoader(parsedArgs.classpath,
+                            ClassLoader.getSystemClassLoader());
+                } else {
+                    cloader = ClassLoader.getSystemClassLoader();
+                }
+
+                try {
+                    ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
+                } catch (RuntimeException ex) {
+                    logAndPrintError(newStderr, "Error starting.", ex);
+                }
             }
         }
     }
@@ -754,36 +865,54 @@
      * if &lt; 0;
      * @param descriptors null-ok; file descriptors for child's new stdio if
      * specified.
+     * @param pipeFd null-ok; pipe for communication with child.
      * @param parsedArgs non-null; zygote args
      * @return true for "exit command loop" and false for "continue command
      * loop"
      */
     private boolean handleParentProc(int pid,
-            FileDescriptor[] descriptors, Arguments parsedArgs) {
+            FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
 
-        if(pid > 0) {
-            // Try to move the new child into the peer's process group.
-            try {
-                ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
-            } catch (IOException ex) {
-                // This exception is expected in the case where
-                // the peer is not in our session
-                // TODO get rid of this log message in the case where
-                // getsid(0) != getsid(peer.getPid())
-                Log.i(TAG, "Zygote: setpgid failed. This is "
-                    + "normal if peer is not in our session");
+        if (pid > 0) {
+            setChildPgid(pid);
+        }
+
+        if (descriptors != null) {
+            for (FileDescriptor fd: descriptors) {
+                IoUtils.closeQuietly(fd);
             }
         }
 
-        try {
-            if (descriptors != null) {
-                for (FileDescriptor fd: descriptors) {
-                    ZygoteInit.closeDescriptor(fd);
+        if (pipeFd != null && pid > 0) {
+            DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
+            int innerPid = -1;
+            try {
+                innerPid = is.readInt();
+            } catch (IOException ex) {
+                Log.w(TAG, "Error reading pid from wrapped process, child may have died", ex);
+            } finally {
+                try {
+                    is.close();
+                } catch (IOException ex) {
                 }
             }
-        } catch (IOException ex) {
-            Log.e(TAG, "Error closing passed descriptors in "
-                    + "parent process", ex);
+
+            // Ensure that the pid reported by the wrapped process is either the
+            // child process that we forked, or a descendant of it.
+            if (innerPid > 0) {
+                int parentPid = innerPid;
+                while (parentPid > 0 && parentPid != pid) {
+                    parentPid = Process.getParentPid(parentPid);
+                }
+                if (parentPid > 0) {
+                    Log.i(TAG, "Wrapped process has pid " + innerPid);
+                    pid = innerPid;
+                } else {
+                    Log.w(TAG, "Wrapped process reported a pid that is not a child of "
+                            + "the process that we forked: childPid=" + pid
+                            + " innerPid=" + innerPid);
+                }
+            }
         }
 
         try {
@@ -808,6 +937,20 @@
         return false;
     }
 
+    private void setChildPgid(int pid) {
+        // Try to move the new child into the peer's process group.
+        try {
+            ZygoteInit.setpgid(pid, ZygoteInit.getpgid(peer.getPid()));
+        } catch (IOException ex) {
+            // This exception is expected in the case where
+            // the peer is not in our session
+            // TODO get rid of this log message in the case where
+            // getsid(0) != getsid(peer.getPid())
+            Log.i(TAG, "Zygote: setpgid failed. This is "
+                + "normal if peer is not in our session");
+        }
+    }
+
     /**
      * Logs an error message and prints it to the specified stream, if
      * provided
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index fbe66e5..157c0bf 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -23,6 +23,7 @@
 import android.net.LocalServerSocket;
 import android.os.Debug;
 import android.os.FileUtils;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.util.EventLog;
@@ -68,7 +69,7 @@
     private static final int PRELOAD_GC_THRESHOLD = 50000;
 
     public static final String USAGE_STRING =
-            " <\"true\"|\"false\" for startSystemServer>";
+            " <\"start-system-server\"|\"\" for startSystemServer>";
 
     private static LocalServerSocket sServerSocket;
 
@@ -441,11 +442,20 @@
         // set umask to 0077 so new files and directories will default to owner-only permissions.
         FileUtils.setUMask(FileUtils.S_IRWXG | FileUtils.S_IRWXO);
 
-        /*
-         * Pass the remaining arguments to SystemServer.
-         * "--nice-name=system_server com.android.server.SystemServer"
-         */
-        RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
+        if (parsedArgs.niceName != null) {
+            Process.setArgV0(parsedArgs.niceName);
+        }
+
+        if (parsedArgs.invokeWith != null) {
+            WrapperInit.execApplication(parsedArgs.invokeWith,
+                    parsedArgs.niceName, null, parsedArgs.remainingArgs);
+        } else {
+            /*
+             * Pass the remaining arguments to SystemServer.
+             */
+            RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
+        }
+
         /* should never reach here */
     }
 
@@ -470,20 +480,13 @@
 
         try {
             parsedArgs = new ZygoteConnection.Arguments(args);
-
-            /*
-             * Enable debugging of the system process if *either* the command line flags
-             * indicate it should be debuggable or the ro.debuggable system property
-             * is set to "1"
-             */
-            int debugFlags = parsedArgs.debugFlags;
-            if ("1".equals(SystemProperties.get("ro.debuggable")))
-                debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
+            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
+            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
 
             /* Request to fork the system server process */
             pid = Zygote.forkSystemServer(
                     parsedArgs.uid, parsedArgs.gid,
-                    parsedArgs.gids, debugFlags, null,
+                    parsedArgs.gids, parsedArgs.debugFlags, null,
                     parsedArgs.permittedCapabilities,
                     parsedArgs.effectiveCapabilities);
         } catch (IllegalArgumentException ex) {
@@ -522,9 +525,9 @@
                 throw new RuntimeException(argv[0] + USAGE_STRING);
             }
 
-            if (argv[1].equals("true")) {
+            if (argv[1].equals("start-system-server")) {
                 startSystemServer();
-            } else if (!argv[1].equals("false")) {
+            } else if (!argv[1].equals("")) {
                 throw new RuntimeException(argv[0] + USAGE_STRING);
             }
 
@@ -696,15 +699,6 @@
             FileDescriptor out, FileDescriptor err) throws IOException;
 
     /**
-     * Calls close() on a file descriptor
-     *
-     * @param fd descriptor to close
-     * @throws IOException
-     */
-    static native void closeDescriptor(FileDescriptor fd)
-            throws IOException;
-
-    /**
      * Toggles the close-on-exec flag for the specified file descriptor.
      *
      * @param fd non-null; file descriptor
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index f1887eb..ff04735 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -78,7 +78,7 @@
 
     private static final int DEFAULT_CUSTOM_GRAVITY = Gravity.LEFT | Gravity.CENTER_VERTICAL;
     
-    private final int mContentHeight;
+    private int mContentHeight;
 
     private int mNavigationMode;
     private int mDisplayOptions = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_HOME_AS_UP;
@@ -95,8 +95,7 @@
     private TextView mSubtitleView;
     private Spinner mSpinner;
     private LinearLayout mListNavLayout;
-    private HorizontalScrollView mTabScrollView;
-    private ViewGroup mTabLayout;
+    private ScrollingTabContainerView mTabScrollView;
     private View mCustomNavView;
     private ProgressBar mProgressView;
     private ProgressBar mIndeterminateProgressView;
@@ -122,6 +121,8 @@
     private SpinnerAdapter mSpinnerAdapter;
     private OnNavigationListener mCallback;
 
+    private Runnable mTabSelector;
+
     private final AdapterView.OnItemSelectedListener mNavItemSelectedListener =
             new AdapterView.OnItemSelectedListener() {
         public void onItemSelected(AdapterView parent, View view, int position, long id) {
@@ -199,8 +200,6 @@
         mProgressBarPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_progressBarPadding, 0);
         mItemPadding = a.getDimensionPixelOffset(R.styleable.ActionBar_itemPadding, 0);
 
-        mIncludeTabs = a.getBoolean(R.styleable.ActionBar_embeddedTabs, true);
-
         setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, DISPLAY_DEFAULT));
 
         final int customNavId = a.getResourceId(R.styleable.ActionBar_customNavigationLayout, 0);
@@ -229,6 +228,12 @@
     }
 
     @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        removeCallbacks(mTabSelector);
+    }
+
+    @Override
     public boolean shouldDelayChildPressedState() {
         return false;
     }
@@ -247,6 +252,11 @@
         addView(mIndeterminateProgressView);
     }
 
+    public void setContentHeight(int height) {
+        mContentHeight = height;
+        requestLayout();
+    }
+
     public void setSplitActionBar(boolean splitActionBar) {
         if (mSplitActionBar != splitActionBar) {
             if (mMenuView != null) {
@@ -271,8 +281,9 @@
         return mIncludeTabs;
     }
 
-    public void setExternalTabLayout(ViewGroup tabLayout) {
-        mTabLayout = tabLayout;
+    public void setEmbeddedTabView(ScrollingTabContainerView tabs) {
+        mTabScrollView = tabs;
+        mIncludeTabs = tabs != null;
     }
 
     public void setCallback(OnNavigationListener callback) {
@@ -489,7 +500,7 @@
                 }
                 break;
             case ActionBar.NAVIGATION_MODE_TABS:
-                if (mTabScrollView != null) {
+                if (mTabScrollView != null && mIncludeTabs) {
                     removeView(mTabScrollView);
                 }
             }
@@ -513,8 +524,7 @@
                 addView(mListNavLayout);
                 break;
             case ActionBar.NAVIGATION_MODE_TABS:
-                ensureTabsExist();
-                if (mTabScrollView != null) {
+                if (mTabScrollView != null && mIncludeTabs) {
                     addView(mTabScrollView);
                 }
                 break;
@@ -523,24 +533,17 @@
             requestLayout();
         }
     }
-    
-    private void ensureTabsExist() {
-        if (!mIncludeTabs) return;
 
-        if (mTabScrollView == null) {
-            mTabScrollView = new HorizontalScrollView(getContext());
-            mTabScrollView.setHorizontalFadingEdgeEnabled(true);
-            mTabLayout = createTabContainer();
-            mTabScrollView.addView(mTabLayout);
-        }
-    }
-
-    public ViewGroup createTabContainer() {
-        ViewGroup result = new LinearLayout(getContext(), null,
+    public ScrollingTabContainerView createTabContainer() {
+        final LinearLayout tabLayout = new LinearLayout(getContext(), null,
                 com.android.internal.R.attr.actionBarTabBarStyle);
-        result.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
-                mContentHeight));
-        return result;
+        tabLayout.setMeasureWithLargestChildEnabled(true);
+        tabLayout.setLayoutParams(new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT, mContentHeight));
+
+        final ScrollingTabContainerView scroller = new ScrollingTabContainerView(mContext);
+        scroller.setTabLayout(tabLayout);
+        return scroller;
     }
 
     public void setDropdownAdapter(SpinnerAdapter adapter) {
@@ -574,51 +577,6 @@
         return mDisplayOptions;
     }
 
-    private TabView createTabView(ActionBar.Tab tab) {
-        final TabView tabView = new TabView(getContext(), tab);
-        tabView.setFocusable(true);
-
-        if (mTabClickListener == null) {
-            mTabClickListener = new TabClickListener();
-        }
-        tabView.setOnClickListener(mTabClickListener);
-        return tabView;
-    }
-
-    public void addTab(ActionBar.Tab tab, boolean setSelected) {
-        ensureTabsExist();
-        View tabView = createTabView(tab);
-        mTabLayout.addView(tabView);
-        if (setSelected) {
-            tabView.setSelected(true);
-        }
-    }
-
-    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
-        ensureTabsExist();
-        final TabView tabView = createTabView(tab);
-        mTabLayout.addView(tabView, position);
-        if (setSelected) {
-            tabView.setSelected(true);
-        }
-    }
-
-    public void updateTab(int position) {
-        ((TabView) mTabLayout.getChildAt(position)).update();
-    }
-
-    public void removeTabAt(int position) {
-        if (mTabLayout != null) {
-            mTabLayout.removeViewAt(position);
-        }
-    }
-
-    public void removeAllTabs() {
-        if (mTabLayout != null) {
-            mTabLayout.removeAllViews();
-        }
-    }
-
     @Override
     protected LayoutParams generateDefaultLayoutParams() {
         // Used by custom nav views if they don't supply layout params. Everything else
@@ -667,15 +625,6 @@
         addView(mTitleLayout);
     }
 
-    public void setTabSelected(int position) {
-        ensureTabsExist();
-        final int tabCount = mTabLayout.getChildCount();
-        for (int i = 0; i < tabCount; i++) {
-            final View child = mTabLayout.getChildAt(i);
-            child.setSelected(i == position);
-        }
-    }
-
     public void setContextView(ActionBarContextView view) {
         mContextView = view;
     }
@@ -948,97 +897,6 @@
         }
     }
 
-    private static class TabView extends LinearLayout {
-        private ActionBar.Tab mTab;
-        private TextView mTextView;
-        private ImageView mIconView;
-        private View mCustomView;
-
-        public TabView(Context context, ActionBar.Tab tab) {
-            super(context, null, com.android.internal.R.attr.actionBarTabStyle);
-            mTab = tab;
-
-            update();
-
-            setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
-                    LayoutParams.MATCH_PARENT, 1));
-        }
-
-        public void update() {
-            final ActionBar.Tab tab = mTab;
-            final View custom = tab.getCustomView();
-            if (custom != null) {
-                addView(custom);
-                mCustomView = custom;
-                if (mTextView != null) mTextView.setVisibility(GONE);
-                if (mIconView != null) {
-                    mIconView.setVisibility(GONE);
-                    mIconView.setImageDrawable(null);
-                }
-            } else {
-                if (mCustomView != null) {
-                    removeView(mCustomView);
-                    mCustomView = null;
-                }
-
-                final Drawable icon = tab.getIcon();
-                final CharSequence text = tab.getText();
-
-                if (icon != null) {
-                    if (mIconView == null) {
-                        ImageView iconView = new ImageView(getContext());
-                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                                LayoutParams.WRAP_CONTENT);
-                        lp.gravity = Gravity.CENTER_VERTICAL;
-                        iconView.setLayoutParams(lp);
-                        addView(iconView, 0);
-                        mIconView = iconView;
-                    }
-                    mIconView.setImageDrawable(icon);
-                    mIconView.setVisibility(VISIBLE);
-                } else if (mIconView != null) {
-                    mIconView.setVisibility(GONE);
-                    mIconView.setImageDrawable(null);
-                }
-
-                if (text != null) {
-                    if (mTextView == null) {
-                        TextView textView = new TextView(getContext(), null,
-                                com.android.internal.R.attr.actionBarTabTextStyle);
-                        textView.setSingleLine();
-                        textView.setEllipsize(TruncateAt.END);
-                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
-                                LayoutParams.WRAP_CONTENT);
-                        lp.gravity = Gravity.CENTER_VERTICAL;
-                        textView.setLayoutParams(lp);
-                        addView(textView);
-                        mTextView = textView;
-                    }
-                    mTextView.setText(text);
-                    mTextView.setVisibility(VISIBLE);
-                } else {
-                    mTextView.setVisibility(GONE);
-                }
-            }
-        }
-
-        public ActionBar.Tab getTab() {
-            return mTab;
-        }
-    }
-
-    private class TabClickListener implements OnClickListener {
-        public void onClick(View view) {
-            TabView tabView = (TabView) view;
-            tabView.getTab().select();
-            final int tabCount = mTabLayout.getChildCount();
-            for (int i = 0; i < tabCount; i++) {
-                final View child = mTabLayout.getChildAt(i);
-                child.setSelected(child == view);
-            }
-        }
-    }
-
     private static class HomeView extends FrameLayout {
         private View mUpView;
         private View mIconView;
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index d789584..bf1c637 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -385,6 +385,7 @@
                 .append(" ToolMinor=").append(coords.toolMinor, 3)
                 .append(" Orientation=").append((float)(coords.orientation * 180 / Math.PI), 1)
                 .append("deg")
+                .append(" Distance=").append(coords.getAxisValue(MotionEvent.AXIS_DISTANCE), 1)
                 .append(" VScroll=").append(coords.getAxisValue(MotionEvent.AXIS_VSCROLL), 1)
                 .append(" HScroll=").append(coords.getAxisValue(MotionEvent.AXIS_HSCROLL), 1)
                 .append(" ToolType=").append(MotionEvent.toolTypeToString(toolType))
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
new file mode 100644
index 0000000..c7d37f2
--- /dev/null
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.widget;
+
+import android.app.ActionBar;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils.TruncateAt;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.HorizontalScrollView;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class ScrollingTabContainerView extends HorizontalScrollView {
+    Runnable mTabSelector;
+    private TabClickListener mTabClickListener;
+
+    private LinearLayout mTabLayout;
+
+    int mMaxTabWidth;
+
+    public ScrollingTabContainerView(Context context) {
+        super(context);
+        setHorizontalScrollBarEnabled(false);
+    }
+
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        setFillViewport(widthMode == MeasureSpec.EXACTLY);
+
+        final int childCount = getChildCount();
+        if (childCount > 1 &&
+                (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
+            if (childCount > 2) {
+                mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f);
+            } else {
+                mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
+            }
+        } else {
+            mMaxTabWidth = -1;
+        }
+
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+
+    public void setTabSelected(int position) {
+        if (mTabLayout == null) {
+            return;
+        }
+
+        final int tabCount = mTabLayout.getChildCount();
+        for (int i = 0; i < tabCount; i++) {
+            final View child = mTabLayout.getChildAt(i);
+            final boolean isSelected = i == position;
+            child.setSelected(isSelected);
+            if (isSelected) {
+                animateToTab(position);
+            }
+        }
+    }
+
+    public void animateToTab(int position) {
+        final View tabView = mTabLayout.getChildAt(position);
+        if (mTabSelector != null) {
+            removeCallbacks(mTabSelector);
+        }
+        mTabSelector = new Runnable() {
+            public void run() {
+                final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
+                smoothScrollTo(scrollPos, 0);
+                mTabSelector = null;
+            }
+        };
+        post(mTabSelector);
+    }
+
+    public void setTabLayout(LinearLayout tabLayout) {
+        if (mTabLayout != tabLayout) {
+            if (mTabLayout != null) {
+                ((ViewGroup) mTabLayout.getParent()).removeView(mTabLayout);
+            }
+            if (tabLayout != null) {
+                addView(tabLayout);
+            }
+            mTabLayout = tabLayout;
+        }
+    }
+
+    public LinearLayout getTabLayout() {
+        return mTabLayout;
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mTabSelector != null) {
+            removeCallbacks(mTabSelector);
+        }
+    }
+
+    private TabView createTabView(ActionBar.Tab tab) {
+        final TabView tabView = new TabView(getContext(), tab);
+        tabView.setFocusable(true);
+
+        if (mTabClickListener == null) {
+            mTabClickListener = new TabClickListener();
+        }
+        tabView.setOnClickListener(mTabClickListener);
+        return tabView;
+    }
+
+    public void addTab(ActionBar.Tab tab, boolean setSelected) {
+        View tabView = createTabView(tab);
+        mTabLayout.addView(tabView, new LinearLayout.LayoutParams(0,
+                LayoutParams.MATCH_PARENT, 1));
+        if (setSelected) {
+            tabView.setSelected(true);
+        }
+    }
+
+    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
+        final TabView tabView = createTabView(tab);
+        mTabLayout.addView(tabView, position, new LinearLayout.LayoutParams(
+                0, LayoutParams.MATCH_PARENT, 1));
+        if (setSelected) {
+            tabView.setSelected(true);
+        }
+    }
+
+    public void updateTab(int position) {
+        ((TabView) mTabLayout.getChildAt(position)).update();
+    }
+
+    public void removeTabAt(int position) {
+        if (mTabLayout != null) {
+            mTabLayout.removeViewAt(position);
+        }
+    }
+
+    public void removeAllTabs() {
+        if (mTabLayout != null) {
+            mTabLayout.removeAllViews();
+        }
+    }
+
+    private class TabView extends LinearLayout {
+        private ActionBar.Tab mTab;
+        private TextView mTextView;
+        private ImageView mIconView;
+        private View mCustomView;
+
+        public TabView(Context context, ActionBar.Tab tab) {
+            super(context, null, com.android.internal.R.attr.actionBarTabStyle);
+            mTab = tab;
+
+            update();
+        }
+
+        @Override
+        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+            // Re-measure if we went beyond our maximum size.
+            if (mMaxTabWidth > 0 && getMeasuredWidth() > mMaxTabWidth) {
+                super.onMeasure(MeasureSpec.makeMeasureSpec(mMaxTabWidth, MeasureSpec.EXACTLY),
+                        heightMeasureSpec);
+            }
+        }
+
+        public void update() {
+            final ActionBar.Tab tab = mTab;
+            final View custom = tab.getCustomView();
+            if (custom != null) {
+                addView(custom);
+                mCustomView = custom;
+                if (mTextView != null) mTextView.setVisibility(GONE);
+                if (mIconView != null) {
+                    mIconView.setVisibility(GONE);
+                    mIconView.setImageDrawable(null);
+                }
+            } else {
+                if (mCustomView != null) {
+                    removeView(mCustomView);
+                    mCustomView = null;
+                }
+
+                final Drawable icon = tab.getIcon();
+                final CharSequence text = tab.getText();
+
+                if (icon != null) {
+                    if (mIconView == null) {
+                        ImageView iconView = new ImageView(getContext());
+                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                                LayoutParams.WRAP_CONTENT);
+                        lp.gravity = Gravity.CENTER_VERTICAL;
+                        iconView.setLayoutParams(lp);
+                        addView(iconView, 0);
+                        mIconView = iconView;
+                    }
+                    mIconView.setImageDrawable(icon);
+                    mIconView.setVisibility(VISIBLE);
+                } else if (mIconView != null) {
+                    mIconView.setVisibility(GONE);
+                    mIconView.setImageDrawable(null);
+                }
+
+                if (text != null) {
+                    if (mTextView == null) {
+                        TextView textView = new TextView(getContext(), null,
+                                com.android.internal.R.attr.actionBarTabTextStyle);
+                        textView.setSingleLine();
+                        textView.setEllipsize(TruncateAt.END);
+                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                                LayoutParams.WRAP_CONTENT);
+                        lp.gravity = Gravity.CENTER_VERTICAL;
+                        textView.setLayoutParams(lp);
+                        addView(textView);
+                        mTextView = textView;
+                    }
+                    mTextView.setText(text);
+                    mTextView.setVisibility(VISIBLE);
+                } else {
+                    mTextView.setVisibility(GONE);
+                }
+            }
+        }
+
+        public ActionBar.Tab getTab() {
+            return mTab;
+        }
+    }
+
+    private class TabClickListener implements OnClickListener {
+        public void onClick(View view) {
+            TabView tabView = (TabView) view;
+            tabView.getTab().select();
+            final int tabCount = mTabLayout.getChildCount();
+            for (int i = 0; i < tabCount; i++) {
+                final View child = mTabLayout.getChildAt(i);
+                child.setSelected(child == view);
+            }
+        }
+    }
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index b787e9f..e610640 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -808,8 +808,11 @@
  * Start the Android runtime.  This involves starting the virtual machine
  * and calling the "static void main(String[] args)" method in the class
  * named by "className".
+ *
+ * Passes the main function two arguments, the class name and the specified
+ * options string.
  */
-void AndroidRuntime::start(const char* className, const bool startSystemServer)
+void AndroidRuntime::start(const char* className, const char* options)
 {
     LOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n",
             className != NULL ? className : "(unknown)");
@@ -820,7 +823,7 @@
      * 'startSystemServer == true' means runtime is obsolete and not run from
      * init.rc anymore, so we print out the boot start event here.
      */
-    if (startSystemServer) {
+    if (strcmp(options, "start-system-server") == 0) {
         /* track our progress through the boot sequence */
         const int LOG_BOOT_PROGRESS_START = 3000;
         LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,
@@ -857,13 +860,13 @@
 
     /*
      * We want to call main() with a String array with arguments in it.
-     * At present we only have one argument, the class name.  Create an
-     * array to hold it.
+     * At present we have two arguments, the class name and an option string.
+     * Create an array to hold them.
      */
     jclass stringClass;
     jobjectArray strArray;
     jstring classNameStr;
-    jstring startSystemServerStr;
+    jstring optionsStr;
 
     stringClass = env->FindClass("java/lang/String");
     assert(stringClass != NULL);
@@ -872,9 +875,8 @@
     classNameStr = env->NewStringUTF(className);
     assert(classNameStr != NULL);
     env->SetObjectArrayElement(strArray, 0, classNameStr);
-    startSystemServerStr = env->NewStringUTF(startSystemServer ?
-                                                 "true" : "false");
-    env->SetObjectArrayElement(strArray, 1, startSystemServerStr);
+    optionsStr = env->NewStringUTF(options);
+    env->SetObjectArrayElement(strArray, 1, optionsStr);
 
     /*
      * Start VM.  This thread becomes the main thread of the VM, and will
@@ -909,12 +911,6 @@
         LOGW("Warning: VM did not shut down cleanly\n");
 }
 
-void AndroidRuntime::start()
-{
-    start("com.android.internal.os.RuntimeInit",
-        false /* Don't start the system server */);
-}
-
 void AndroidRuntime::onExit(int code)
 {
     LOGV("AndroidRuntime onExit calling exit(%d)", code);
diff --git a/core/jni/com_android_internal_os_ZygoteInit.cpp b/core/jni/com_android_internal_os_ZygoteInit.cpp
index e627e4a..86fd9cb 100644
--- a/core/jni/com_android_internal_os_ZygoteInit.cpp
+++ b/core/jni/com_android_internal_os_ZygoteInit.cpp
@@ -131,28 +131,6 @@
     } while (err < 0 && errno == EINTR);
 }
 
-static void com_android_internal_os_ZygoteInit_closeDescriptor(JNIEnv* env,
-        jobject clazz, jobject descriptor)
-{
-    int fd;
-    int err;
-
-    fd = jniGetFDFromFileDescriptor(env, descriptor);
-
-    if  (env->ExceptionOccurred() != NULL) {
-        return;
-    }
-
-    do {
-        err = close(fd);
-    } while (err < 0 && errno == EINTR);
-
-    if (err < 0) {
-        jniThrowIOException(env, errno);
-        return;
-    }
-}
-
 static void com_android_internal_os_ZygoteInit_setCloseOnExec (JNIEnv *env,
     jobject clazz, jobject descriptor, jboolean flag)
 {
@@ -332,8 +310,6 @@
         "(Ljava/io/FileDescriptor;Ljava/io/FileDescriptor;"
         "Ljava/io/FileDescriptor;)V",
             (void *) com_android_internal_os_ZygoteInit_reopenStdio},
-    { "closeDescriptor", "(Ljava/io/FileDescriptor;)V",
-        (void *) com_android_internal_os_ZygoteInit_closeDescriptor},
     { "setCloseOnExec", "(Ljava/io/FileDescriptor;Z)V",
         (void *)  com_android_internal_os_ZygoteInit_setCloseOnExec},
     { "setCapabilities", "(JJ)V",
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 6c18089..c1e81c3 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -823,6 +823,10 @@
      Default value is false. EditText content is always selectable. -->
     <attr name="textIsSelectable" format="boolean" />
 
+    <!-- When true, IME suggestions will be displayed when the user double taps on editable text.
+     The default value is true. -->
+    <attr name="suggestionsEnabled" format="boolean" />
+
     <!-- Where to ellipsize text. -->
     <attr name="ellipsize">
         <enum name="none" value="0" />
@@ -2877,6 +2881,8 @@
 
         <!-- Indicates that the content of a non-editable text can be selected. -->
         <attr name="textIsSelectable" />
+        <!-- Suggestions will be displayed when the user double taps on editable text. -->
+        <attr name="suggestionsEnabled" />
     </declare-styleable>
     <!-- An <code>input-extras</code> is a container for extra data to supply to
          an input method.  Contains
@@ -4907,9 +4913,6 @@
         <!-- Specifies padding that should be applied to the left and right sides of
              system-provided items in the bar. -->
         <attr name="itemPadding" format="dimension" />
-        <!-- Specifies whether tabs should be embedded within the bar itself (true)
-             or displayed elsewhere (false). -->
-        <attr name="embeddedTabs" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="ActionMode">
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 8ad8f67..1957b2a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1670,5 +1670,6 @@
   <public type="attr" name="horizontalDirection" />
 
   <public type="attr" name="fullBackupAgent" />
+  <public type="attr" name="suggestionsEnabled" />
 
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ccb3518..816546b 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1852,7 +1852,7 @@
 
     <!-- Do not translate.  WebView User Agent string -->
     <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
-        AppleWebKit/534.16 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.16</string>
+        AppleWebKit/534.20 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.20</string>
     <!-- Do not translate.  WebView User Agent targeted content -->
     <string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
 
@@ -1998,6 +1998,9 @@
     <!-- Do not translate. Regex used by AutoFill. -->
     <string name="autofill_card_ignored_re">^card</string>
 
+    <!-- Do not translate. Regex used by AutoFill. -->
+    <string name="autofill_fax_re">fax<!-- fr-FR -->|télécopie|telecopie<!-- ja-JP -->|ファックス<!-- ru -->|факс<!-- zh-CN -->|传真<!-- zh-TW -->|傳真</string>
+
     <!-- Title of an application permission, listed so the user can choose whether
         they want to allow the application to do this. -->
     <string name="permlab_readHistoryBookmarks">read Browser\'s history and bookmarks</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 26b5d95..e95094f0 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1078,7 +1078,6 @@
         <item name="android:progressBarStyle">@android:style/Widget.ProgressBar.Horizontal</item>
         <item name="android:indeterminateProgressStyle">@android:style/Widget.ProgressBar.Small</item>
         <item name="android:homeLayout">@android:layout/action_bar_home</item>
-        <item name="android:embeddedTabs">@android:bool/action_bar_embed_tabs</item>
     </style>
 
     <style name="Widget.ActionMode">
@@ -1122,6 +1121,7 @@
     </style>
 
     <style name="Widget.ActionBarView_TabView">
+        <item name="android:gravity">center_horizontal</item>
         <item name="android:background">@drawable/minitab_lt</item>
         <item name="android:paddingLeft">4dip</item>
         <item name="android:paddingRight">4dip</item>
@@ -1795,6 +1795,9 @@
     </style>
 
     <style name="Widget.Holo.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar">
+        <item name="android:divider">?android:attr/dividerVertical</item>
+        <item name="android:showDividers">middle</item>
+        <item name="android:dividerPadding">8dip</item>
     </style>
 
     <style name="Widget.Holo.ActionBarView_TabText" parent="Widget.ActionBarView_TabText">
diff --git a/include/android_runtime/AndroidRuntime.h b/include/android_runtime/AndroidRuntime.h
index b02a057..32cd4f5 100644
--- a/include/android_runtime/AndroidRuntime.h
+++ b/include/android_runtime/AndroidRuntime.h
@@ -39,6 +39,13 @@
     AndroidRuntime();
     virtual ~AndroidRuntime();
 
+    enum StartMode {
+        Zygote,
+        SystemServer,
+        Application,
+        Tool,
+    };
+
     /**
      * Register a set of methods in the specified class.
      */
@@ -59,8 +66,7 @@
 
     int addVmArguments(int argc, const char* const argv[]);
 
-    void start(const char *classname, const bool startSystemServer);
-    void start();       // start in android.util.RuntimeInit
+    void start(const char *classname, const char* options);
 
     static AndroidRuntime* getRuntime();
 
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 9e4e132..2c7cf75 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -953,6 +953,7 @@
         UI_MODE_TYPE_NORMAL = ACONFIGURATION_UI_MODE_TYPE_NORMAL,
         UI_MODE_TYPE_DESK = ACONFIGURATION_UI_MODE_TYPE_DESK,
         UI_MODE_TYPE_CAR = ACONFIGURATION_UI_MODE_TYPE_CAR,
+        UI_MODE_TYPE_TELEVISION = ACONFIGURATION_UI_MODE_TYPE_TELEVISION,
 
         // uiMode bits for the night switch.
         MASK_UI_MODE_NIGHT = 0x30,
diff --git a/keystore/java/android/security/IKeyChainAliasResponse.aidl b/keystore/java/android/security/IKeyChainAliasResponse.aidl
new file mode 100644
index 0000000..e042001
--- /dev/null
+++ b/keystore/java/android/security/IKeyChainAliasResponse.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.security;
+
+/**
+ * Used by the {@code KeyChainActivity} to return alias for {@link KeyStore#chooseAlias}.
+ *
+ * @hide
+ */
+interface IKeyChainAliasResponse {
+
+    void alias(String alias);
+}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index be59f23..2763e46 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -15,8 +15,6 @@
  */
 package android.security;
 
-import android.os.Bundle;
-
 /**
  * Caller is required to ensure that {@link KeyStore#unlock
  * KeyStore.unlock} was successful.
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 08e05ef..ec820cf 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -17,9 +17,11 @@
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
+import android.accounts.AccountManagerCallback;
 import android.accounts.AccountManagerFuture;
 import android.accounts.AuthenticatorException;
 import android.accounts.OperationCanceledException;
+import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -56,64 +58,123 @@
     public static final String ACCOUNT_TYPE = "com.android.keychain";
 
     /**
-     * Returns an {@code Intent} for use with {@link
-     * android.app.Activity#startActivityForResult
-     * startActivityForResult}. The result will be returned via {@link
-     * android.app.Activity#onActivityResult onActivityResult} with
-     * {@link android.app.Activity#RESULT_OK RESULT_OK} and the alias
-     * in the returned {@code Intent}'s extra data with key {@link
-     * android.content.Intent#EXTRA_TEXT Intent.EXTRA_TEXT}.
+     * @hide Also used by KeyChainActivity implementation
      */
-    public static Intent chooseAlias() {
-        return new Intent("com.android.keychain.CHOOSER");
+    public static final String EXTRA_RESPONSE = "response";
+
+    /**
+     * Launches an {@code Activity} for the user to select the alias
+     * for a private key and certificate pair for authentication. The
+     * selected alias or null will be returned via the
+     * IKeyChainAliasResponse callback.
+     */
+    public static void choosePrivateKeyAlias(Activity activity, KeyChainAliasResponse response) {
+        if (activity == null) {
+            throw new NullPointerException("activity == null");
+        }
+        if (response == null) {
+            throw new NullPointerException("response == null");
+        }
+        Intent intent = new Intent("com.android.keychain.CHOOSER");
+        intent.putExtra(EXTRA_RESPONSE, new AliasResponse(activity, response));
+        activity.startActivity(intent);
+    }
+
+    private static class AliasResponse extends IKeyChainAliasResponse.Stub {
+        private final Activity activity;
+        private final KeyChainAliasResponse keyChainAliasResponse;
+        private AliasResponse(Activity activity, KeyChainAliasResponse keyChainAliasResponse) {
+            this.activity = activity;
+            this.keyChainAliasResponse = keyChainAliasResponse;
+        }
+        @Override public void alias(String alias) {
+            if (alias == null) {
+                keyChainAliasResponse.alias(null);
+                return;
+            }
+            AccountManager accountManager = AccountManager.get(activity);
+            accountManager.getAuthToken(getAccount(activity),
+                                        alias,
+                                        null,
+                                        activity,
+                                        new AliasAccountManagerCallback(keyChainAliasResponse,
+                                                                        alias),
+                                        null);
+        }
+    }
+
+    private static class AliasAccountManagerCallback implements AccountManagerCallback<Bundle> {
+        private final KeyChainAliasResponse keyChainAliasResponse;
+        private final String alias;
+        private AliasAccountManagerCallback(KeyChainAliasResponse keyChainAliasResponse,
+                                            String alias) {
+            this.keyChainAliasResponse = keyChainAliasResponse;
+            this.alias = alias;
+        }
+        @Override public void run(AccountManagerFuture<Bundle> future) {
+            Bundle bundle;
+            try {
+                bundle = future.getResult();
+            } catch (OperationCanceledException e) {
+                keyChainAliasResponse.alias(null);
+                return;
+            } catch (IOException e) {
+                keyChainAliasResponse.alias(null);
+                return;
+            } catch (AuthenticatorException e) {
+                keyChainAliasResponse.alias(null);
+                return;
+            }
+            String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
+            if (authToken != null) {
+                keyChainAliasResponse.alias(alias);
+            } else {
+                keyChainAliasResponse.alias(null);
+            }
+        }
     }
 
     /**
-     * Returns a new {@code KeyChainResult} instance.
+     * Returns the {@code PrivateKey} for the requested alias, or null
+     * if no there is no result.
      */
-    public static KeyChainResult get(Context context, String alias)
+    public static PrivateKey getPrivateKey(Context context, String alias)
             throws InterruptedException, RemoteException {
         if (alias == null) {
             throw new NullPointerException("alias == null");
         }
         KeyChainConnection keyChainConnection = bind(context);
         try {
-            // Account is created if necessary during binding of the IKeyChainService
-            AccountManager accountManager = AccountManager.get(context);
-            Account account = accountManager.getAccountsByType(ACCOUNT_TYPE)[0];
-            AccountManagerFuture<Bundle> future = accountManager.getAuthToken(account,
-                                                                              alias,
-                                                                              false,
-                                                                              null,
-                                                                              null);
-            Bundle bundle;
-            try {
-                bundle = future.getResult();
-            } catch (OperationCanceledException e) {
-                throw new AssertionError(e);
-            } catch (IOException e) {
-                throw new AssertionError(e);
-            } catch (AuthenticatorException e) {
-                throw new AssertionError(e);
-            }
-            Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
-            if (intent != null) {
-                Bundle result = new Bundle();
-                // we don't want this Eclair compatability flag,
-                // it will prevent onActivityResult from being called
-                intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
-                return new KeyChainResult(intent);
-            }
-
-            String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
+            String authToken = authToken(context, alias);
             if (authToken == null) {
-                throw new AssertionError("Invalid authtoken");
+                return null;
             }
             IKeyChainService keyChainService = keyChainConnection.getService();
             byte[] privateKeyBytes = keyChainService.getPrivateKey(alias, authToken);
+            return toPrivateKey(privateKeyBytes);
+        } finally {
+            keyChainConnection.close();
+        }
+    }
+
+    /**
+     * Returns the {@code X509Certificate} chain for the requested
+     * alias, or null if no there is no result.
+     */
+    public static X509Certificate[] getCertificateChain(Context context, String alias)
+            throws InterruptedException, RemoteException {
+        if (alias == null) {
+            throw new NullPointerException("alias == null");
+        }
+        KeyChainConnection keyChainConnection = bind(context);
+        try {
+            String authToken = authToken(context, alias);
+            if (authToken == null) {
+                return null;
+            }
+            IKeyChainService keyChainService = keyChainConnection.getService();
             byte[] certificateBytes = keyChainService.getCertificate(alias, authToken);
-            return new KeyChainResult(toPrivateKey(privateKeyBytes),
-                                      toCertificate(certificateBytes));
+            return new X509Certificate[] { toCertificate(certificateBytes) };
         } finally {
             keyChainConnection.close();
         }
@@ -146,6 +207,50 @@
         }
     }
 
+    private static String authToken(Context context, String alias) {
+        AccountManager accountManager = AccountManager.get(context);
+        AccountManagerFuture<Bundle> future = accountManager.getAuthToken(getAccount(context),
+                                                                          alias,
+                                                                          false,
+                                                                          null,
+                                                                          null);
+        Bundle bundle;
+        try {
+            bundle = future.getResult();
+        } catch (OperationCanceledException e) {
+            throw new AssertionError(e);
+        } catch (IOException e) {
+            // KeyChainAccountAuthenticator doesn't do I/O
+            throw new AssertionError(e);
+        } catch (AuthenticatorException e) {
+            throw new AssertionError(e);
+        }
+        Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
+        if (intent != null) {
+            return null;
+        }
+        String authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
+        if (authToken == null) {
+            throw new AssertionError("Invalid authtoken");
+        }
+        return authToken;
+    }
+
+    private static Account getAccount(Context context) {
+        AccountManager accountManager = AccountManager.get(context);
+        Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
+        if (accounts.length == 0) {
+            try {
+                // Account is created if necessary during binding of the IKeyChainService
+                bind(context).close();
+            } catch (InterruptedException e) {
+                throw new AssertionError(e);
+            }
+            accounts = accountManager.getAccountsByType(ACCOUNT_TYPE);
+        }
+        return accounts[0];
+    }
+
     /**
      * @hide for reuse by CertInstaller and Settings.
      * @see KeyChain#bind
diff --git a/keystore/java/android/security/KeyChainAliasResponse.java b/keystore/java/android/security/KeyChainAliasResponse.java
new file mode 100644
index 0000000..bcca123
--- /dev/null
+++ b/keystore/java/android/security/KeyChainAliasResponse.java
@@ -0,0 +1,35 @@
+/*
+ * 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.security;
+
+import android.content.Intent;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+
+/**
+ * The KeyChainAliasResponse is the callback for {@link
+ * KeyChain#chooseAlias}.
+ *
+ * @hide
+ */
+public interface KeyChainAliasResponse {
+
+    /**
+     * Called with the alias of the certificate chosen by the user, or
+     * null if no value was chosen.
+     */
+    public void alias(String alias);
+}
diff --git a/keystore/java/android/security/KeyChainResult.java b/keystore/java/android/security/KeyChainResult.java
deleted file mode 100644
index 85a2921..0000000
--- a/keystore/java/android/security/KeyChainResult.java
+++ /dev/null
@@ -1,72 +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.security;
-
-import android.content.Intent;
-import java.security.PrivateKey;
-import java.security.cert.X509Certificate;
-
-/**
- * The KeyChainResult is the complex result value from {@link
- * KeyChain#get}. The caller should first inspect {@link #getIntent}
- * to determine if the user needs to grant the application access to
- * the protected contents. If {@code getIntent} returns null, access
- * has been granted and the methods {@link #getPrivateKey} and {@link
- * #getCertificate} can be used to access the credentials.
- *
- * @hide
- */
-public final class KeyChainResult {
-
-    private final Intent intent;
-    private final PrivateKey privateKey;
-    private final X509Certificate certificate;
-
-    KeyChainResult(Intent intent) {
-        this(intent, null, null);
-    }
-
-    KeyChainResult(PrivateKey privateKey, X509Certificate certificate) {
-        this(null, privateKey, certificate);
-    }
-
-    private KeyChainResult(Intent intent, PrivateKey privateKey, X509Certificate certificate) {
-        this.intent = intent;
-        this.privateKey = privateKey;
-        this.certificate = certificate;
-    }
-
-    public Intent getIntent() {
-        return intent;
-    }
-
-    public PrivateKey getPrivateKey() {
-        checkIntent();
-        return privateKey;
-    }
-
-    public X509Certificate getCertificate() {
-        checkIntent();
-        return certificate;
-    }
-
-    private void checkIntent() {
-        if (intent != null) {
-            throw new IllegalStateException("non-null Intent, check getIntent()");
-        }
-    }
-
-}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e1daede..253010c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -828,29 +828,64 @@
      * or {@link #SCO_AUDIO_STATE_CONNECTED}
      *
      * @see #startBluetoothSco()
+     * @deprecated Use  {@link #ACTION_SCO_AUDIO_STATE_UPDATED} instead
      */
+    @Deprecated
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_SCO_AUDIO_STATE_CHANGED =
             "android.media.SCO_AUDIO_STATE_CHANGED";
+
+     /**
+     * Sticky broadcast intent action indicating that the bluetoooth SCO audio
+     * connection state has been updated.
+     * <p>This intent has two extras:
+     * <ul>
+     *   <li> {@link #EXTRA_SCO_AUDIO_STATE} - The new SCO audio state. </li>
+     *   <li> {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}- The previous SCO audio state. </li>
+     * </ul>
+     * <p> EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE can be any of:
+     * <ul>
+     *   <li> {@link #SCO_AUDIO_STATE_DISCONNECTED}, </li>
+     *   <li> {@link #SCO_AUDIO_STATE_CONNECTING} or </li>
+     *   <li> {@link #SCO_AUDIO_STATE_CONNECTED}, </li>
+     * </ul>
+     * @see #startBluetoothSco()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SCO_AUDIO_STATE_UPDATED =
+            "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
+
     /**
-     * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} containing the new
-     * bluetooth SCO connection state.
+     * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} or
+     * {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the new bluetooth SCO connection state.
      */
     public static final String EXTRA_SCO_AUDIO_STATE =
             "android.media.extra.SCO_AUDIO_STATE";
 
     /**
-     * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
-     * SCO audio channel is not established
+     * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the previous
+     * bluetooth SCO connection state.
+     */
+    public static final String EXTRA_SCO_AUDIO_PREVIOUS_STATE =
+            "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
+
+    /**
+     * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
+     * indicating that the SCO audio channel is not established
      */
     public static final int SCO_AUDIO_STATE_DISCONNECTED = 0;
     /**
-     * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
-     * SCO audio channel is established
+     * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} or {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}
+     * indicating that the SCO audio channel is established
      */
     public static final int SCO_AUDIO_STATE_CONNECTED = 1;
     /**
-     * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that
+     * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
+     * indicating that the SCO audio channel is being established
+     */
+    public static final int SCO_AUDIO_STATE_CONNECTING = 2;
+    /**
+     * Value for extra EXTRA_SCO_AUDIO_STATE indicating that
      * there was an error trying to obtain the state
      */
     public static final int SCO_AUDIO_STATE_ERROR = -1;
@@ -878,29 +913,37 @@
      * to/from a bluetooth SCO headset while the phone is not in call.
      * <p>As the SCO connection establishment can take several seconds,
      * applications should not rely on the connection to be available when the method
-     * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED}
+     * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED}
      * and wait for the state to be {@link #SCO_AUDIO_STATE_CONNECTED}.
-     * <p>As the connection is not guaranteed to succeed, applications must wait for this intent with
-     * a timeout.
-     * <p>When finished with the SCO connection or if the establishment times out,
-     * the application must call {@link #stopBluetoothSco()} to clear the request and turn
-     * down the bluetooth connection.
+     * <p>As the ACTION_SCO_AUDIO_STATE_UPDATED intent is sticky, the application can check the SCO
+     * audio state before calling startBluetoothSco() by reading the intent returned by the receiver
+     * registration. If the state is already CONNECTED, no state change will be received via the
+     * intent after calling startBluetoothSco(). It is however useful to call startBluetoothSco()
+     * so that the connection stays active in case the current initiator stops the connection.
+     * <p>Unless the connection is already active as described above, the state will always
+     * transition from DISCONNECTED to CONNECTING and then either to CONNECTED if the connection
+     * succeeds or back to DISCONNECTED if the connection fails (e.g no headset is connected).
+     * <p>When finished with the SCO connection or if the establishment fails, the application must
+     * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
      * <p>Even if a SCO connection is established, the following restrictions apply on audio
      * output streams so that they can be routed to SCO headset:
-     * - the stream type must be {@link #STREAM_VOICE_CALL}
-     * - the format must be mono
-     * - the sampling must be 16kHz or 8kHz
+     * <ul>
+     *   <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
+     *   <li> the format must be mono </li>
+     *   <li> the sampling must be 16kHz or 8kHz </li>
+     * </ul>
      * <p>The following restrictions apply on input streams:
-     * - the format must be mono
-     * - the sampling must be 8kHz
-     *
+     * <ul>
+     *   <li> the format must be mono </li>
+     *   <li> the sampling must be 8kHz </li>
+     * </ul>
      * <p>Note that the phone application always has the priority on the usage of the SCO
      * connection for telephony. If this method is called while the phone is in call
      * it will be ignored. Similarly, if a call is received or sent while an application
      * is using the SCO connection, the connection will be lost for the application and NOT
      * returned automatically when the call ends.
      * @see #stopBluetoothSco()
-     * @see #ACTION_SCO_AUDIO_STATE_CHANGED
+     * @see #ACTION_SCO_AUDIO_STATE_UPDATED
      */
     public void startBluetoothSco(){
         IAudioService service = getService();
@@ -917,7 +960,7 @@
      *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
      * <p>This method must be called by applications having requested the use of
      * bluetooth SCO audio with {@link #startBluetoothSco()}
-     * when finished with the SCO connection or if the establishment times out.
+     * when finished with the SCO connection or if connection fails.
      * @see #startBluetoothSco()
      */
     public void stopBluetoothSco(){
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 504cfde..4e77fcb 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -112,9 +112,12 @@
     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 MSG_BT_HEADSET_CNCT_FAILED = 12;
 
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
+    // Timeout for connection to bluetooth headset service
+    private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
+
 
     /** @see AudioSystemThread */
     private AudioSystemThread mAudioSystemThread;
@@ -273,11 +276,22 @@
     private int mScoAudioState;
     // SCO audio state is not active
     private static final int SCO_STATE_INACTIVE = 0;
+    // SCO audio activation request waiting for headset service to connect
+    private static final int SCO_STATE_ACTIVATE_REQ = 1;
     // SCO audio state is active or starting due to a local request to start a virtual call
-    private static final int SCO_STATE_ACTIVE_INTERNAL = 1;
+    private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
+    // SCO audio deactivation request waiting for headset service to connect
+    private static final int SCO_STATE_DEACTIVATE_REQ = 5;
+
     // SCO audio state is active due to an action in BT handsfree (either voice recognition or
     // in call audio)
     private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
+    // Deactivation request for all SCO connections (initiated by audio mode change)
+    // waiting for headset service to connect
+    private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
+
+    // Current connection state indicated by bluetooth headset
+    private int mScoConnectionState;
 
     // true if boot sequence has been completed
     private boolean mBootCompleted;
@@ -335,13 +349,6 @@
 
         AudioSystem.setErrorCallback(mAudioSystemCallback);
 
-        mBluetoothHeadsetDevice = null;
-        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-        if (adapter != null) {
-            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
-                                    BluetoothProfile.HEADSET);
-        }
-
         // Register for device connection intent broadcasts.
         IntentFilter intentFilter =
                 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
@@ -768,17 +775,7 @@
                             if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
                                 AudioService.this.mMode = mode;
                                 if (mode != AudioSystem.MODE_NORMAL) {
-                                    synchronized(mScoClients) {
-                                        checkScoAudioState();
-                                        if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
-                                            mBluetoothHeadset.stopVoiceRecognition(
-                                                    mBluetoothHeadsetDevice);
-                                            mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
-                                                    mBluetoothHeadsetDevice);
-                                        } else {
-                                            clearAllScoClients(mCb, true);
-                                        }
-                                    }
+                                    disconnectBluetoothSco(mCb);
                                 }
                             }
                         }
@@ -856,16 +853,7 @@
                     // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
                     // SCO connections not started by the application changing the mode
                     if (mode != AudioSystem.MODE_NORMAL) {
-                        synchronized(mScoClients) {
-                            checkScoAudioState();
-                            if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
-                                mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice);
-                                mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
-                                    mBluetoothHeadsetDevice);
-                            } else {
-                                clearAllScoClients(cb, true);
-                            }
-                        }
+                        disconnectBluetoothSco(cb);
                     }
                 }
             }
@@ -1213,7 +1201,8 @@
 
     /** @see AudioManager#startBluetoothSco() */
     public void startBluetoothSco(IBinder cb){
-        if (!checkAudioSettingsPermission("startBluetoothSco()")) {
+        if (!checkAudioSettingsPermission("startBluetoothSco()") ||
+                !mBootCompleted) {
             return;
         }
         ScoClient client = getScoClient(cb, true);
@@ -1222,7 +1211,8 @@
 
     /** @see AudioManager#stopBluetoothSco() */
     public void stopBluetoothSco(IBinder cb){
-        if (!checkAudioSettingsPermission("stopBluetoothSco()")) {
+        if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
+                !mBootCompleted) {
             return;
         }
         ScoClient client = getScoClient(cb, false);
@@ -1322,25 +1312,57 @@
         }
 
         private void requestScoState(int state) {
-            if (mBluetoothHeadset == null) {
-                return;
-            }
-
             checkScoAudioState();
-
-            if (totalCount() == 0 &&
-                mBluetoothHeadsetDevice != null) {
-                // Accept SCO audio activation only in NORMAL audio mode or if the mode is
-                // currently controlled by the same client.
-                if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL ||
-                        mSetModeDeathHandlers.get(0).getBinder() == mCb) &&
-                        state == BluetoothHeadset.STATE_AUDIO_CONNECTED &&
-                        mScoAudioState == SCO_STATE_INACTIVE) {
-                    mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
-                    mBluetoothHeadset.startScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice);
+            if (totalCount() == 0) {
+                if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+                    // Make sure that the state transitions to CONNECTING even if we cannot initiate
+                    // the connection.
+                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
+                    // Accept SCO audio activation only in NORMAL audio mode or if the mode is
+                    // currently controlled by the same client.
+                    if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL ||
+                            mSetModeDeathHandlers.get(0).getBinder() == mCb) &&
+                            mBluetoothHeadsetDevice != null &&
+                            (mScoAudioState == SCO_STATE_INACTIVE ||
+                             mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
+                        if (mScoAudioState == SCO_STATE_INACTIVE) {
+                            if (mBluetoothHeadset != null) {
+                                if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
+                                        mBluetoothHeadsetDevice)) {
+                                    mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                                } else {
+                                    broadcastScoConnectionState(
+                                            AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                                }
+                            } else if (getBluetoothHeadset()) {
+                                mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+                            }
+                        } else {
+                            mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+                        }
+                    } else {
+                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    }
                 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
-                        mScoAudioState == SCO_STATE_ACTIVE_INTERNAL){
-                    mBluetoothHeadset.stopScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice);
+                              mBluetoothHeadsetDevice != null &&
+                              (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
+                               mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
+                    if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
+                        if (mBluetoothHeadset != null) {
+                            if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+                                    mBluetoothHeadsetDevice)) {
+                                mScoAudioState = SCO_STATE_INACTIVE;
+                                broadcastScoConnectionState(
+                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                            }
+                        } else if (getBluetoothHeadset()) {
+                            mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
+                        }
+                    } else {
+                        mScoAudioState = SCO_STATE_INACTIVE;
+                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                    }
                 }
             }
         }
@@ -1348,7 +1370,7 @@
 
     private void checkScoAudioState() {
         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
-                mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+                mScoAudioState == SCO_STATE_INACTIVE &&
                 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
                 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
@@ -1391,10 +1413,72 @@
         }
     }
 
+    private boolean getBluetoothHeadset() {
+        boolean result = false;
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        if (adapter != null) {
+            result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+                                    BluetoothProfile.HEADSET);
+        }
+        // If we could not get a bluetooth headset proxy, send a failure message
+        // without delay to reset the SCO audio state and clear SCO clients.
+        // If we could get a proxy, send a delayed failure message that will reset our state
+        // in case we don't receive onServiceConnected().
+        sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+                SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
+        return result;
+    }
+
+    private void disconnectBluetoothSco(IBinder exceptBinder) {
+        synchronized(mScoClients) {
+            checkScoAudioState();
+            if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
+                    mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
+                if (mBluetoothHeadsetDevice != null) {
+                    if (mBluetoothHeadset != null) {
+                        if (!mBluetoothHeadset.stopVoiceRecognition(
+                                mBluetoothHeadsetDevice) ||
+                                !mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+                                        mBluetoothHeadsetDevice)) {
+                            sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+                                    SENDMSG_REPLACE, 0, 0, null, 0);
+                        }
+                    } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
+                            getBluetoothHeadset()) {
+                        mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
+                    }
+                }
+            } else {
+                clearAllScoClients(exceptBinder, true);
+            }
+        }
+    }
+
+    private void resetBluetoothSco() {
+        synchronized(mScoClients) {
+            clearAllScoClients(null, false);
+            mScoAudioState = SCO_STATE_INACTIVE;
+            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+        }
+    }
+
+    private void broadcastScoConnectionState(int state) {
+        if (state != mScoConnectionState) {
+            Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
+            newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
+            newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
+                    mScoConnectionState);
+            mContext.sendStickyBroadcast(newIntent);
+            mScoConnectionState = state;
+        }
+    }
+
     private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
         new BluetoothProfile.ServiceListener() {
         public void onServiceConnected(int profile, BluetoothProfile proxy) {
             synchronized (mScoClients) {
+                // Discard timeout message
+                mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
                 mBluetoothHeadset = (BluetoothHeadset) proxy;
                 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
                 if (deviceList.size() > 0) {
@@ -1402,19 +1486,41 @@
                 } else {
                     mBluetoothHeadsetDevice = null;
                 }
+                // Refresh SCO audio state
+                checkScoAudioState();
+                // Continue pending action if any
+                if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
+                        mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
+                        mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
+                    boolean status = false;
+                    if (mBluetoothHeadsetDevice != null) {
+                        switch (mScoAudioState) {
+                        case SCO_STATE_ACTIVATE_REQ:
+                            mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                            status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
+                                    mBluetoothHeadsetDevice);
+                            break;
+                        case SCO_STATE_DEACTIVATE_REQ:
+                            status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+                                    mBluetoothHeadsetDevice);
+                            break;
+                        case SCO_STATE_DEACTIVATE_EXT_REQ:
+                            status = mBluetoothHeadset.stopVoiceRecognition(
+                                    mBluetoothHeadsetDevice) &&
+                                    mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+                                    mBluetoothHeadsetDevice);
+                        }
+                    }
+                    if (!status) {
+                        sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+                                SENDMSG_REPLACE, 0, 0, null, 0);
+                    }
+                }
             }
         }
         public void onServiceDisconnected(int profile) {
             synchronized (mScoClients) {
-                if (mBluetoothHeadset != null) {
-                    List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
-                    if (devices.size() == 0) {
-                        mBluetoothHeadsetDevice = null;
-                        clearAllScoClients(null, false);
-                        mScoAudioState = SCO_STATE_INACTIVE;
-                    }
-                    mBluetoothHeadset = null;
-                }
+                mBluetoothHeadset = null;
             }
         }
     };
@@ -2041,6 +2147,10 @@
                 case MSG_PERSIST_MEDIABUTTONRECEIVER:
                     persistMediaButtonReceiver( (ComponentName) msg.obj );
                     break;
+
+                case MSG_BT_HEADSET_CNCT_FAILED:
+                    resetBluetoothSco();
+                    break;
             }
         }
     }
@@ -2241,8 +2351,7 @@
                                                              address);
                         mConnectedDevices.remove(device);
                         mBluetoothHeadsetDevice = null;
-                        clearAllScoClients(null, false);
-                        mScoAudioState = SCO_STATE_INACTIVE;
+                        resetBluetoothSco();
                     } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                         AudioSystem.setDeviceConnectionState(device,
                                                              AudioSystem.DEVICE_STATE_AVAILABLE,
@@ -2332,26 +2441,34 @@
                 }
             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
                 boolean broadcast = false;
-                int audioState = AudioManager.SCO_AUDIO_STATE_ERROR;
+                int state = AudioManager.SCO_AUDIO_STATE_ERROR;
                 synchronized (mScoClients) {
                     int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
-                    if (!mScoClients.isEmpty() && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
+                    // broadcast intent if the connection was initated by AudioService
+                    if (!mScoClients.isEmpty() &&
+                            (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
+                             mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
+                             mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
                         broadcast = true;
                     }
                     switch (btState) {
                     case BluetoothHeadset.STATE_AUDIO_CONNECTED:
-                        audioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
-                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
+                        state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
+                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
+                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
                             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                         }
                         break;
                     case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
-                        audioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+                        state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
                         mScoAudioState = SCO_STATE_INACTIVE;
                         clearAllScoClients(null, false);
                         break;
                     case BluetoothHeadset.STATE_AUDIO_CONNECTING:
-                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
+                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
+                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
                             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
                         }
                     default:
@@ -2361,14 +2478,23 @@
                     }
                 }
                 if (broadcast) {
+                    broadcastScoConnectionState(state);
+                    //FIXME: this is to maintain compatibility with deprecated intent
+                    // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
                     Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
-                    newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, audioState);
+                    newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
                     mContext.sendStickyBroadcast(newIntent);
                 }
             } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
                 mBootCompleted = true;
                 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
                         0, 0, null, 0);
+
+                mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
+                resetBluetoothSco();
+                getBluetoothHeadset();
+                //FIXME: this is to maintain compatibility with deprecated intent
+                // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
                 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
                 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
                         AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index fb7a871..70053ea 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -400,6 +400,7 @@
 }
 
 void AwesomePlayer::reset() {
+    LOGI("reset");
     Mutex::Autolock autoLock(mLock);
     reset_l();
 }
@@ -413,8 +414,10 @@
                     Playback::STOP, 0);
             mDecryptHandle = NULL;
             mDrmManagerClient = NULL;
+            LOGI("DRM manager client stopped");
     }
 
+
     if (mFlags & PLAYING) {
         uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder;
         if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
@@ -447,6 +450,7 @@
         mPreparedCondition.wait(mLock);
     }
 
+    LOGI("cancel player events");
     cancelPlayerEvents();
 
     mWVMExtractor.clear();
@@ -1081,6 +1085,7 @@
         usleep(1000);
     }
     IPCThreadState::self()->flushCommands();
+    LOGI("video decoder shutdown completed");
 }
 
 void AwesomePlayer::setNativeWindow_l(const sp<ANativeWindow> &native) {
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 0f0ffd4..ba495cc 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -3443,7 +3443,7 @@
 }
 
 status_t OMXCodec::stop() {
-    CODEC_LOGV("stop mState=%d", mState);
+    CODEC_LOGI("stop mState=%d", mState);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -3505,6 +3505,7 @@
         mLeftOverBuffer = NULL;
     }
 
+    CODEC_LOGI("stopping video source");
     mSource->stop();
 
     CODEC_LOGI("stopped in state %d", mState);
diff --git a/media/libstagefright/WVMExtractor.cpp b/media/libstagefright/WVMExtractor.cpp
index 83a1eaa..26eda0c 100644
--- a/media/libstagefright/WVMExtractor.cpp
+++ b/media/libstagefright/WVMExtractor.cpp
@@ -45,8 +45,7 @@
 static Mutex gWVMutex;
 
 WVMExtractor::WVMExtractor(const sp<DataSource> &source)
-    : mDataSource(source),
-      mUseAdaptiveStreaming(false) {
+    : mDataSource(source) {
     {
         Mutex::Autolock autoLock(gWVMutex);
         if (gVendorLibHandle == NULL) {
@@ -59,13 +58,12 @@
         }
     }
 
-    typedef MediaExtractor *(*GetInstanceFunc)(sp<DataSource>);
+    typedef WVMLoadableExtractor *(*GetInstanceFunc)(sp<DataSource>);
     GetInstanceFunc getInstanceFunc =
         (GetInstanceFunc) dlsym(gVendorLibHandle,
                 "_ZN7android11GetInstanceENS_2spINS_10DataSourceEEE");
 
     if (getInstanceFunc) {
-        LOGD("Calling GetInstanceFunc");
         mImpl = (*getInstanceFunc)(source);
         CHECK(mImpl != NULL);
     } else {
@@ -102,19 +100,17 @@
 }
 
 int64_t WVMExtractor::getCachedDurationUs(status_t *finalStatus) {
-    // TODO: Fill this with life.
+    if (mImpl == NULL) {
+        return 0;
+    }
 
-    *finalStatus = OK;
-
-    return 0;
+    return mImpl->getCachedDurationUs(finalStatus);
 }
 
 void WVMExtractor::setAdaptiveStreamingMode(bool adaptive) {
-    mUseAdaptiveStreaming = adaptive;
-}
-
-bool WVMExtractor::getAdaptiveStreamingMode() const {
-    return mUseAdaptiveStreaming;
+    if (mImpl != NULL) {
+        mImpl->setAdaptiveStreamingMode(adaptive);
+    }
 }
 
 } //namespace android
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index 13e1662..cffbfb5 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -360,10 +360,14 @@
             mFramesConfigured = true;
         }
 
-        uint32_t timestamp = 0xFFFFFFFF;
+        uint32_t useExtTimestamp = (inHeader->nOffset == 0);
+
+        // decoder deals in ms, OMX in us.
+        uint32_t timestamp =
+            useExtTimestamp ? (inHeader->nTimeStamp + 500) / 1000 : 0xFFFFFFFF;
+
         int32_t bufferSize = inHeader->nFilledLen;
 
-        uint32_t useExtTimestamp = 0;
         if (PVDecodeVideoFrame(
                     mHandle, &bitstream, &timestamp, &bufferSize,
                     &useExtTimestamp,
@@ -379,13 +383,20 @@
             return;
         }
 
-        outHeader->nTimeStamp = inHeader->nTimeStamp;
+        // decoder deals in ms, OMX in us.
+        outHeader->nTimeStamp = timestamp * 1000;
 
-        inInfo->mOwnedByUs = false;
-        inQueue.erase(inQueue.begin());
-        inInfo = NULL;
-        notifyEmptyBufferDone(inHeader);
-        inHeader = NULL;
+        CHECK_LE(bufferSize, inHeader->nFilledLen);
+        inHeader->nOffset += inHeader->nFilledLen - bufferSize;
+        inHeader->nFilledLen = bufferSize;
+
+        if (inHeader->nFilledLen == 0) {
+            inInfo->mOwnedByUs = false;
+            inQueue.erase(inQueue.begin());
+            inInfo = NULL;
+            notifyEmptyBufferDone(inHeader);
+            inHeader = NULL;
+        }
 
         ++mInputBufferCount;
 
diff --git a/media/libstagefright/include/SimpleSoftOMXComponent.h b/media/libstagefright/include/SimpleSoftOMXComponent.h
index 2a29a7d..50cd275 100644
--- a/media/libstagefright/include/SimpleSoftOMXComponent.h
+++ b/media/libstagefright/include/SimpleSoftOMXComponent.h
@@ -36,7 +36,7 @@
             OMX_PTR appData,
             OMX_COMPONENTTYPE **component);
 
-    virtual ~SimpleSoftOMXComponent();
+    virtual void prepareForDestruction();
 
     void onMessageReceived(const sp<AMessage> &msg);
 
diff --git a/media/libstagefright/include/SoftOMXComponent.h b/media/libstagefright/include/SoftOMXComponent.h
index 053bc22..a808611 100644
--- a/media/libstagefright/include/SoftOMXComponent.h
+++ b/media/libstagefright/include/SoftOMXComponent.h
@@ -38,6 +38,8 @@
     void setLibHandle(void *libHandle);
     void *libHandle() const;
 
+    virtual void prepareForDestruction() {}
+
 protected:
     virtual ~SoftOMXComponent();
 
diff --git a/media/libstagefright/include/WVMExtractor.h b/media/libstagefright/include/WVMExtractor.h
index 62e5aa5..deecd25 100644
--- a/media/libstagefright/include/WVMExtractor.h
+++ b/media/libstagefright/include/WVMExtractor.h
@@ -25,6 +25,15 @@
 
 class DataSource;
 
+class WVMLoadableExtractor : public MediaExtractor {
+public:
+    WVMLoadableExtractor() {}
+    virtual ~WVMLoadableExtractor() {}
+
+    virtual int64_t getCachedDurationUs(status_t *finalStatus) = 0;
+    virtual void setAdaptiveStreamingMode(bool adaptive) = 0;
+};
+
 class WVMExtractor : public MediaExtractor {
 public:
     WVMExtractor(const sp<DataSource> &source);
@@ -49,20 +58,15 @@
     // is used.
     void setAdaptiveStreamingMode(bool adaptive);
 
-    // Retrieve the adaptive streaming mode used by the WV component.
-    bool getAdaptiveStreamingMode() const;
-
 protected:
     virtual ~WVMExtractor();
 
 private:
     sp<DataSource> mDataSource;
-    sp<MediaExtractor> mImpl;
-    bool mUseAdaptiveStreaming;
+    sp<WVMLoadableExtractor> mImpl;
 
     WVMExtractor(const WVMExtractor &);
     WVMExtractor &operator=(const WVMExtractor &);
-
 };
 
 }  // namespace android
diff --git a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
index 179b2a0..f7330f3 100644
--- a/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
+++ b/media/libstagefright/omx/SimpleSoftOMXComponent.cpp
@@ -45,7 +45,11 @@
             PRIORITY_AUDIO);
 }
 
-SimpleSoftOMXComponent::~SimpleSoftOMXComponent() {
+void SimpleSoftOMXComponent::prepareForDestruction() {
+    // The looper's queue may still contain messages referencing this
+    // object. Make sure those are flushed before returning so that
+    // a subsequent dlunload() does not pull out the rug from under us.
+
     mLooper->unregisterHandler(mHandler->id());
     mLooper->stop();
 }
diff --git a/media/libstagefright/omx/SoftOMXPlugin.cpp b/media/libstagefright/omx/SoftOMXPlugin.cpp
index 6bd6624..04ca39e 100644
--- a/media/libstagefright/omx/SoftOMXPlugin.cpp
+++ b/media/libstagefright/omx/SoftOMXPlugin.cpp
@@ -21,6 +21,7 @@
 #include "SoftOMXPlugin.h"
 #include "include/SoftOMXComponent.h"
 
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AString.h>
 
 #include <dlfcn.h>
@@ -126,8 +127,11 @@
         (SoftOMXComponent *)
             ((OMX_COMPONENTTYPE *)component)->pComponentPrivate;
 
+    me->prepareForDestruction();
+
     void *libHandle = me->libHandle();
 
+    CHECK_EQ(me->getStrongCount(), 1);
     me->decStrong(this);
     me = NULL;
 
diff --git a/native/include/android/configuration.h b/native/include/android/configuration.h
index 91533c8..39fef21 100644
--- a/native/include/android/configuration.h
+++ b/native/include/android/configuration.h
@@ -77,6 +77,7 @@
     ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01,
     ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02,
     ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03,
+    ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04,
 
     ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00,
     ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1,
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index ff4b11a..af30887 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -101,7 +101,7 @@
         const InputDeviceIdentifier& identifier) :
         next(NULL),
         fd(fd), id(id), path(path), identifier(identifier),
-        classes(0), keyBitmask(NULL), relBitmask(NULL),
+        classes(0), keyBitmask(NULL), relBitmask(NULL), propBitmask(NULL),
         configuration(NULL), virtualKeyMap(NULL) {
 }
 
@@ -109,6 +109,7 @@
     close();
     delete[] keyBitmask;
     delete[] relBitmask;
+    delete[] propBitmask;
     delete configuration;
     delete virtualKeyMap;
 }
@@ -205,6 +206,18 @@
     return false;
 }
 
+bool EventHub::hasInputProperty(int32_t deviceId, int property) const {
+    if (property >= 0 && property <= INPUT_PROP_MAX) {
+        AutoMutex _l(mLock);
+
+        Device* device = getDeviceLocked(deviceId);
+        if (device && device->propBitmask) {
+            return test_bit(property, device->propBitmask);
+        }
+    }
+    return false;
+}
+
 int32_t EventHub::getScanCodeState(int32_t deviceId, int32_t scanCode) const {
     if (scanCode >= 0 && scanCode <= KEY_MAX) {
         AutoMutex _l(mLock);
@@ -834,23 +847,23 @@
     memset(sw_bitmask, 0, sizeof(sw_bitmask));
     ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask);
 
+    uint8_t prop_bitmask[sizeof_bit_array(INPUT_PROP_MAX + 1)];
+    memset(prop_bitmask, 0, sizeof(prop_bitmask));
+    ioctl(fd, EVIOCGPROP(sizeof(prop_bitmask)), prop_bitmask);
+
     device->keyBitmask = new uint8_t[sizeof(key_bitmask)];
-    if (device->keyBitmask != NULL) {
-        memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
-    } else {
+    device->relBitmask = new uint8_t[sizeof(rel_bitmask)];
+    device->propBitmask = new uint8_t[sizeof(prop_bitmask)];
+
+    if (!device->keyBitmask || !device->relBitmask || !device->propBitmask) {
         delete device;
-        LOGE("out of memory allocating key bitmask");
+        LOGE("out of memory allocating bitmasks");
         return -1;
     }
 
-    device->relBitmask = new uint8_t[sizeof(rel_bitmask)];
-    if (device->relBitmask != NULL) {
-        memcpy(device->relBitmask, rel_bitmask, sizeof(rel_bitmask));
-    } else {
-        delete device;
-        LOGE("out of memory allocating rel bitmask");
-        return -1;
-    }
+    memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask));
+    memcpy(device->relBitmask, rel_bitmask, sizeof(rel_bitmask));
+    memcpy(device->propBitmask, prop_bitmask, sizeof(prop_bitmask));
 
     // See if this is a keyboard.  Ignore everything in the button range except for
     // joystick and gamepad buttons which are handled like keyboards for the most part.
diff --git a/services/input/EventHub.h b/services/input/EventHub.h
index 4d26a95..ca33619 100644
--- a/services/input/EventHub.h
+++ b/services/input/EventHub.h
@@ -34,25 +34,38 @@
 
 #include <linux/input.h>
 
-/* These constants are not defined in linux/input.h but they are part of the multitouch
- * input protocol. */
+/* These constants are not defined in linux/input.h in the version of the kernel
+ * headers currently provided with Bionic. */
 
-#define ABS_MT_TOUCH_MAJOR 0x30  /* Major axis of touching ellipse */
-#define ABS_MT_TOUCH_MINOR 0x31  /* Minor axis (omit if circular) */
-#define ABS_MT_WIDTH_MAJOR 0x32  /* Major axis of approaching ellipse */
-#define ABS_MT_WIDTH_MINOR 0x33  /* Minor axis (omit if circular) */
-#define ABS_MT_ORIENTATION 0x34  /* Ellipse orientation */
-#define ABS_MT_POSITION_X 0x35   /* Center X ellipse position */
-#define ABS_MT_POSITION_Y 0x36   /* Center Y ellipse position */
-#define ABS_MT_TOOL_TYPE 0x37    /* Type of touching device (finger, pen, ...) */
-#define ABS_MT_BLOB_ID 0x38      /* Group a set of packets as a blob */
-#define ABS_MT_TRACKING_ID 0x39  /* Unique ID of initiated contact */
-#define ABS_MT_PRESSURE 0x3a     /* Pressure on contact area */
+#define EVIOCGPROP(len) _IOC(_IOC_READ, 'E', 0x09, len)
 
-#define MT_TOOL_FINGER 0 /* Identifies a finger */
-#define MT_TOOL_PEN 1    /* Identifies a pen */
+#define INPUT_PROP_POINTER 0x00
+#define INPUT_PROP_DIRECT 0x01
+#define INPUT_PROP_BUTTONPAD 0x02
+#define INPUT_PROP_SEMI_MT 0x03
+#define INPUT_PROP_MAX 0x1f
+#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
+
+#define ABS_MT_SLOT 0x2f
+#define ABS_MT_TOUCH_MAJOR 0x30
+#define ABS_MT_TOUCH_MINOR 0x31
+#define ABS_MT_WIDTH_MAJOR 0x32
+#define ABS_MT_WIDTH_MINOR 0x33
+#define ABS_MT_ORIENTATION 0x34
+#define ABS_MT_POSITION_X 0x35
+#define ABS_MT_POSITION_Y 0x36
+#define ABS_MT_TOOL_TYPE 0x37
+#define ABS_MT_BLOB_ID 0x38
+#define ABS_MT_TRACKING_ID 0x39
+#define ABS_MT_PRESSURE 0x3a
+#define ABS_MT_DISTANCE 0x3b
+
+#define MT_TOOL_FINGER 0
+#define MT_TOOL_PEN 1
 
 #define SYN_MT_REPORT 2
+#define SYN_DROPPED 3
+
 
 /* Convenience constants. */
 
@@ -172,6 +185,8 @@
 
     virtual bool hasRelativeAxis(int32_t deviceId, int axis) const = 0;
 
+    virtual bool hasInputProperty(int32_t deviceId, int property) const = 0;
+
     virtual status_t mapKey(int32_t deviceId, int scancode,
             int32_t* outKeycode, uint32_t* outFlags) const = 0;
 
@@ -236,6 +251,8 @@
 
     virtual bool hasRelativeAxis(int32_t deviceId, int axis) const;
 
+    virtual bool hasInputProperty(int32_t deviceId, int property) const;
+
     virtual status_t mapKey(int32_t deviceId, int scancode,
             int32_t* outKeycode, uint32_t* outFlags) const;
 
@@ -286,6 +303,7 @@
         uint32_t classes;
         uint8_t* keyBitmask;
         uint8_t* relBitmask;
+        uint8_t* propBitmask;
         String8 configurationFile;
         PropertyMap* configuration;
         VirtualKeyMap* virtualKeyMap;
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 6003207..25a2c78 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -58,6 +58,9 @@
 
 // --- Constants ---
 
+// Maximum number of slots supported when using the slot-based Multitouch Protocol B.
+static const size_t MAX_SLOTS = 32;
+
 // Quiet time between certain gesture transitions.
 // Time to allow for all fingers or buttons to settle into a stable state before
 // starting a new gesture.
@@ -809,7 +812,8 @@
 // --- InputDevice ---
 
 InputDevice::InputDevice(InputReaderContext* context, int32_t id, const String8& name) :
-        mContext(context), mId(id), mName(name), mSources(0), mIsExternal(false) {
+        mContext(context), mId(id), mName(name), mSources(0),
+        mIsExternal(false), mDropUntilNextSync(false) {
 }
 
 InputDevice::~InputDevice() {
@@ -898,9 +902,26 @@
                 rawEvent->value, rawEvent->flags);
 #endif
 
-        for (size_t i = 0; i < numMappers; i++) {
-            InputMapper* mapper = mMappers[i];
-            mapper->process(rawEvent);
+        if (mDropUntilNextSync) {
+            if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_REPORT) {
+                mDropUntilNextSync = false;
+#if DEBUG_RAW_EVENTS
+                LOGD("Recovered from input event buffer overrun.");
+#endif
+            } else {
+#if DEBUG_RAW_EVENTS
+                LOGD("Dropped input event while waiting for next input sync.");
+#endif
+            }
+        } else if (rawEvent->type == EV_SYN && rawEvent->scanCode == SYN_DROPPED) {
+            LOGI("Detected input event buffer overrun for device %s.", mName.string());
+            mDropUntilNextSync = true;
+            reset();
+        } else {
+            for (size_t i = 0; i < numMappers; i++) {
+                InputMapper* mapper = mMappers[i];
+                mapper->process(rawEvent);
+            }
         }
     }
 }
@@ -1812,6 +1833,10 @@
             info->addMotionRange(mLocked.orientedRanges.orientation);
         }
 
+        if (mLocked.orientedRanges.haveDistance) {
+            info->addMotionRange(mLocked.orientedRanges.distance);
+        }
+
         if (mPointerController != NULL) {
             float minX, minY, maxX, maxY;
             if (mPointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
@@ -1849,6 +1874,7 @@
         dump.appendFormat(INDENT4 "PressureScale: %0.3f\n", mLocked.pressureScale);
         dump.appendFormat(INDENT4 "SizeScale: %0.3f\n", mLocked.sizeScale);
         dump.appendFormat(INDENT4 "OrientationScale: %0.3f\n", mLocked.orientationScale);
+        dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mLocked.distanceScale);
 
         dump.appendFormat(INDENT3 "Last Touch:\n");
         dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount);
@@ -1889,6 +1915,7 @@
     mLocked.orientedRanges.haveTouchSize = false;
     mLocked.orientedRanges.haveToolSize = false;
     mLocked.orientedRanges.haveOrientation = false;
+    mLocked.orientedRanges.haveDistance = false;
 
     mPointerGesture.reset();
 }
@@ -1947,9 +1974,14 @@
         // The device is a cursor device with a touch pad attached.
         // By default don't use the touch pad to move the pointer.
         mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_PAD;
+    } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_POINTER)) {
+        // The device is a pointing device like a track pad.
+        mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
+    } else if (getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_DIRECT)) {
+        // The device is a touch screen.
+        mParameters.deviceType = Parameters::DEVICE_TYPE_TOUCH_SCREEN;
     } else {
-        // The device is just a touch pad.
-        // By default use the touch pad to move the pointer and to perform related gestures.
+        // The device is a touch pad of unknown purpose.
         mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
     }
 
@@ -2016,6 +2048,9 @@
     mRawAxes.toolMajor.clear();
     mRawAxes.toolMinor.clear();
     mRawAxes.orientation.clear();
+    mRawAxes.distance.clear();
+    mRawAxes.trackingId.clear();
+    mRawAxes.slot.clear();
 }
 
 void TouchInputMapper::dumpRawAxes(String8& dump) {
@@ -2028,6 +2063,9 @@
     dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMajor, "ToolMajor");
     dumpRawAbsoluteAxisInfo(dump, mRawAxes.toolMinor, "ToolMinor");
     dumpRawAbsoluteAxisInfo(dump, mRawAxes.orientation, "Orientation");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.distance, "Distance");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.trackingId, "TrackingId");
+    dumpRawAbsoluteAxisInfo(dump, mRawAxes.slot, "Slot");
 }
 
 bool TouchInputMapper::configureSurfaceLocked() {
@@ -2234,6 +2272,8 @@
                 }
             }
 
+            mLocked.orientedRanges.haveOrientation = true;
+
             mLocked.orientedRanges.orientation.axis = AMOTION_EVENT_AXIS_ORIENTATION;
             mLocked.orientedRanges.orientation.source = mTouchSource;
             mLocked.orientedRanges.orientation.min = - M_PI_2;
@@ -2241,6 +2281,31 @@
             mLocked.orientedRanges.orientation.flat = 0;
             mLocked.orientedRanges.orientation.fuzz = 0;
         }
+
+        // Distance
+        mLocked.distanceScale = 0;
+        if (mCalibration.distanceCalibration != Calibration::DISTANCE_CALIBRATION_NONE) {
+            if (mCalibration.distanceCalibration
+                    == Calibration::DISTANCE_CALIBRATION_SCALED) {
+                if (mCalibration.haveDistanceScale) {
+                    mLocked.distanceScale = mCalibration.distanceScale;
+                } else {
+                    mLocked.distanceScale = 1.0f;
+                }
+            }
+
+            mLocked.orientedRanges.haveDistance = true;
+
+            mLocked.orientedRanges.distance.axis = AMOTION_EVENT_AXIS_DISTANCE;
+            mLocked.orientedRanges.distance.source = mTouchSource;
+            mLocked.orientedRanges.distance.min =
+                    mRawAxes.distance.minValue * mLocked.distanceScale;
+            mLocked.orientedRanges.distance.max =
+                    mRawAxes.distance.minValue * mLocked.distanceScale;
+            mLocked.orientedRanges.distance.flat = 0;
+            mLocked.orientedRanges.distance.fuzz =
+                    mRawAxes.distance.fuzz * mLocked.distanceScale;
+        }
     }
 
     if (orientationChanged || sizeChanged) {
@@ -2518,6 +2583,23 @@
                     orientationCalibrationString.string());
         }
     }
+
+    // Distance
+    out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_DEFAULT;
+    String8 distanceCalibrationString;
+    if (in.tryGetProperty(String8("touch.distance.calibration"), distanceCalibrationString)) {
+        if (distanceCalibrationString == "none") {
+            out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+        } else if (distanceCalibrationString == "scaled") {
+            out.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+        } else if (distanceCalibrationString != "default") {
+            LOGW("Invalid value for touch.distance.calibration: '%s'",
+                    distanceCalibrationString.string());
+        }
+    }
+
+    out.haveDistanceScale = in.tryGetProperty(String8("touch.distance.scale"),
+            out.distanceScale);
 }
 
 void TouchInputMapper::resolveCalibration() {
@@ -2618,6 +2700,20 @@
     default:
         break;
     }
+
+    // Distance
+    switch (mCalibration.distanceCalibration) {
+    case Calibration::DISTANCE_CALIBRATION_DEFAULT:
+        if (mRawAxes.distance.valid) {
+            mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_SCALED;
+        } else {
+            mCalibration.distanceCalibration = Calibration::DISTANCE_CALIBRATION_NONE;
+        }
+        break;
+
+    default:
+        break;
+    }
 }
 
 void TouchInputMapper::dumpCalibration(String8& dump) {
@@ -2740,6 +2836,23 @@
     default:
         LOG_ASSERT(false);
     }
+
+    // Distance
+    switch (mCalibration.distanceCalibration) {
+    case Calibration::DISTANCE_CALIBRATION_NONE:
+        dump.append(INDENT4 "touch.distance.calibration: none\n");
+        break;
+    case Calibration::DISTANCE_CALIBRATION_SCALED:
+        dump.append(INDENT4 "touch.distance.calibration: scaled\n");
+        break;
+    default:
+        LOG_ASSERT(false);
+    }
+
+    if (mCalibration.haveDistanceScale) {
+        dump.appendFormat(INDENT4 "touch.distance.scale: %0.3f\n",
+                mCalibration.distanceScale);
+    }
 }
 
 void TouchInputMapper::reset() {
@@ -3247,6 +3360,16 @@
             orientation = 0;
         }
 
+        // Distance
+        float distance;
+        switch (mCalibration.distanceCalibration) {
+        case Calibration::DISTANCE_CALIBRATION_SCALED:
+            distance = in.distance * mLocked.distanceScale;
+            break;
+        default:
+            distance = 0;
+        }
+
         // X and Y
         // Adjust coords for surface orientation.
         float x, y;
@@ -3289,6 +3412,9 @@
         out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR, toolMajor);
         out.setAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR, toolMinor);
         out.setAxisValue(AMOTION_EVENT_AXIS_ORIENTATION, orientation);
+        if (distance != 0) {
+            out.setAxisValue(AMOTION_EVENT_AXIS_DISTANCE, distance);
+        }
 
         // Write output properties.
         PointerProperties& properties = mCurrentTouchProperties[i];
@@ -5020,13 +5146,13 @@
 
 SingleTouchInputMapper::SingleTouchInputMapper(InputDevice* device) :
         TouchInputMapper(device) {
-    initialize();
+    clearState();
 }
 
 SingleTouchInputMapper::~SingleTouchInputMapper() {
 }
 
-void SingleTouchInputMapper::initialize() {
+void SingleTouchInputMapper::clearState() {
     mAccumulator.clear();
 
     mDown = false;
@@ -5040,7 +5166,7 @@
 void SingleTouchInputMapper::reset() {
     TouchInputMapper::reset();
 
-    initialize();
+    clearState();
  }
 
 void SingleTouchInputMapper::process(const RawEvent* rawEvent) {
@@ -5144,6 +5270,7 @@
         mCurrentTouch.pointers[0].toolMajor = mToolWidth;
         mCurrentTouch.pointers[0].toolMinor = mToolWidth;
         mCurrentTouch.pointers[0].orientation = 0;
+        mCurrentTouch.pointers[0].distance = 0;
         mCurrentTouch.pointers[0].isStylus = false; // TODO: Set stylus
         mCurrentTouch.idToIndex[0] = 0;
         mCurrentTouch.idBits.markBit(0);
@@ -5168,22 +5295,22 @@
 // --- MultiTouchInputMapper ---
 
 MultiTouchInputMapper::MultiTouchInputMapper(InputDevice* device) :
-        TouchInputMapper(device) {
-    initialize();
+        TouchInputMapper(device), mSlotCount(0), mUsingSlotsProtocol(false) {
+    clearState();
 }
 
 MultiTouchInputMapper::~MultiTouchInputMapper() {
 }
 
-void MultiTouchInputMapper::initialize() {
-    mAccumulator.clear();
+void MultiTouchInputMapper::clearState() {
+    mAccumulator.clear(mSlotCount);
     mButtonState = 0;
 }
 
 void MultiTouchInputMapper::reset() {
     TouchInputMapper::reset();
 
-    initialize();
+    clearState();
 }
 
 void MultiTouchInputMapper::process(const RawEvent* rawEvent) {
@@ -5203,45 +5330,69 @@
     }
 
     case EV_ABS: {
-        uint32_t pointerIndex = mAccumulator.pointerCount;
-        Accumulator::Pointer* pointer = & mAccumulator.pointers[pointerIndex];
+        bool newSlot = false;
+        if (mUsingSlotsProtocol && rawEvent->scanCode == ABS_MT_SLOT) {
+            mAccumulator.currentSlot = rawEvent->value;
+            newSlot = true;
+        }
+
+        if (mAccumulator.currentSlot < 0 || size_t(mAccumulator.currentSlot) >= mSlotCount) {
+            if (newSlot) {
+#if DEBUG_POINTERS
+                LOGW("MultiTouch device %s emitted invalid slot index %d but it "
+                        "should be between 0 and %d; ignoring this slot.",
+                        getDeviceName().string(), mAccumulator.currentSlot, mSlotCount);
+#endif
+            }
+            break;
+        }
+
+        Accumulator::Slot* slot = &mAccumulator.slots[mAccumulator.currentSlot];
 
         switch (rawEvent->scanCode) {
         case ABS_MT_POSITION_X:
-            pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_X;
-            pointer->absMTPositionX = rawEvent->value;
+            slot->fields |= Accumulator::FIELD_ABS_MT_POSITION_X;
+            slot->absMTPositionX = rawEvent->value;
             break;
         case ABS_MT_POSITION_Y:
-            pointer->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y;
-            pointer->absMTPositionY = rawEvent->value;
+            slot->fields |= Accumulator::FIELD_ABS_MT_POSITION_Y;
+            slot->absMTPositionY = rawEvent->value;
             break;
         case ABS_MT_TOUCH_MAJOR:
-            pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR;
-            pointer->absMTTouchMajor = rawEvent->value;
+            slot->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MAJOR;
+            slot->absMTTouchMajor = rawEvent->value;
             break;
         case ABS_MT_TOUCH_MINOR:
-            pointer->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR;
-            pointer->absMTTouchMinor = rawEvent->value;
+            slot->fields |= Accumulator::FIELD_ABS_MT_TOUCH_MINOR;
+            slot->absMTTouchMinor = rawEvent->value;
             break;
         case ABS_MT_WIDTH_MAJOR:
-            pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
-            pointer->absMTWidthMajor = rawEvent->value;
+            slot->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
+            slot->absMTWidthMajor = rawEvent->value;
             break;
         case ABS_MT_WIDTH_MINOR:
-            pointer->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR;
-            pointer->absMTWidthMinor = rawEvent->value;
+            slot->fields |= Accumulator::FIELD_ABS_MT_WIDTH_MINOR;
+            slot->absMTWidthMinor = rawEvent->value;
             break;
         case ABS_MT_ORIENTATION:
-            pointer->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION;
-            pointer->absMTOrientation = rawEvent->value;
+            slot->fields |= Accumulator::FIELD_ABS_MT_ORIENTATION;
+            slot->absMTOrientation = rawEvent->value;
             break;
         case ABS_MT_TRACKING_ID:
-            pointer->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID;
-            pointer->absMTTrackingId = rawEvent->value;
+            if (mUsingSlotsProtocol && rawEvent->value < 0) {
+                slot->clear();
+            } else {
+                slot->fields |= Accumulator::FIELD_ABS_MT_TRACKING_ID;
+                slot->absMTTrackingId = rawEvent->value;
+            }
             break;
         case ABS_MT_PRESSURE:
-            pointer->fields |= Accumulator::FIELD_ABS_MT_PRESSURE;
-            pointer->absMTPressure = rawEvent->value;
+            slot->fields |= Accumulator::FIELD_ABS_MT_PRESSURE;
+            slot->absMTPressure = rawEvent->value;
+            break;
+        case ABS_MT_TOOL_TYPE:
+            slot->fields |= Accumulator::FIELD_ABS_MT_TOOL_TYPE;
+            slot->absMTToolType = rawEvent->value;
             break;
         }
         break;
@@ -5251,19 +5402,7 @@
         switch (rawEvent->scanCode) {
         case SYN_MT_REPORT: {
             // MultiTouch Sync: The driver has returned all data for *one* of the pointers.
-            uint32_t pointerIndex = mAccumulator.pointerCount;
-
-            if (mAccumulator.pointers[pointerIndex].fields) {
-                if (pointerIndex == MAX_POINTERS) {
-                    LOGW("MultiTouch device driver returned more than maximum of %d pointers.",
-                            MAX_POINTERS);
-                } else {
-                    pointerIndex += 1;
-                    mAccumulator.pointerCount = pointerIndex;
-                }
-            }
-
-            mAccumulator.pointers[pointerIndex].clear();
+            mAccumulator.currentSlot += 1;
             break;
         }
 
@@ -5279,99 +5418,120 @@
     static const uint32_t REQUIRED_FIELDS =
             Accumulator::FIELD_ABS_MT_POSITION_X | Accumulator::FIELD_ABS_MT_POSITION_Y;
 
-    uint32_t inCount = mAccumulator.pointerCount;
-    uint32_t outCount = 0;
+    size_t inCount = mSlotCount;
+    size_t outCount = 0;
     bool havePointerIds = true;
 
     mCurrentTouch.clear();
 
-    for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
-        const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex];
-        uint32_t fields = inPointer.fields;
+    for (size_t inIndex = 0; inIndex < inCount; inIndex++) {
+        const Accumulator::Slot& inSlot = mAccumulator.slots[inIndex];
+        uint32_t fields = inSlot.fields;
 
         if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
             // Some drivers send empty MT sync packets without X / Y to indicate a pointer up.
+            // This may also indicate an unused slot.
             // Drop this finger.
             continue;
         }
 
+        if (outCount >= MAX_POINTERS) {
+#if DEBUG_POINTERS
+            LOGD("MultiTouch device %s emitted more than maximum of %d pointers; "
+                    "ignoring the rest.",
+                    getDeviceName().string(), MAX_POINTERS);
+#endif
+            break; // too many fingers!
+        }
+
         PointerData& outPointer = mCurrentTouch.pointers[outCount];
-        outPointer.x = inPointer.absMTPositionX;
-        outPointer.y = inPointer.absMTPositionY;
+        outPointer.x = inSlot.absMTPositionX;
+        outPointer.y = inSlot.absMTPositionY;
 
         if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) {
-            if (inPointer.absMTPressure <= 0) {
-                // Some devices send sync packets with X / Y but with a 0 pressure to indicate
-                // a pointer going up.  Drop this finger.
-                continue;
-            }
-            outPointer.pressure = inPointer.absMTPressure;
+            outPointer.pressure = inSlot.absMTPressure;
         } else {
             // Default pressure to 0 if absent.
             outPointer.pressure = 0;
         }
 
         if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) {
-            if (inPointer.absMTTouchMajor <= 0) {
+            if (inSlot.absMTTouchMajor <= 0) {
                 // Some devices send sync packets with X / Y but with a 0 touch major to indicate
                 // a pointer going up.  Drop this finger.
                 continue;
             }
-            outPointer.touchMajor = inPointer.absMTTouchMajor;
+            outPointer.touchMajor = inSlot.absMTTouchMajor;
         } else {
             // Default touch area to 0 if absent.
             outPointer.touchMajor = 0;
         }
 
         if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) {
-            outPointer.touchMinor = inPointer.absMTTouchMinor;
+            outPointer.touchMinor = inSlot.absMTTouchMinor;
         } else {
             // Assume touch area is circular.
             outPointer.touchMinor = outPointer.touchMajor;
         }
 
         if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) {
-            outPointer.toolMajor = inPointer.absMTWidthMajor;
+            outPointer.toolMajor = inSlot.absMTWidthMajor;
         } else {
             // Default tool area to 0 if absent.
             outPointer.toolMajor = 0;
         }
 
         if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) {
-            outPointer.toolMinor = inPointer.absMTWidthMinor;
+            outPointer.toolMinor = inSlot.absMTWidthMinor;
         } else {
             // Assume tool area is circular.
             outPointer.toolMinor = outPointer.toolMajor;
         }
 
         if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) {
-            outPointer.orientation = inPointer.absMTOrientation;
+            outPointer.orientation = inSlot.absMTOrientation;
         } else {
             // Default orientation to vertical if absent.
             outPointer.orientation = 0;
         }
 
-        outPointer.isStylus = false; // TODO: Handle stylus
+        if (fields & Accumulator::FIELD_ABS_MT_DISTANCE) {
+            outPointer.distance = inSlot.absMTDistance;
+        } else {
+            // Default distance is 0 (direct contact).
+            outPointer.distance = 0;
+        }
+
+        if (fields & Accumulator::FIELD_ABS_MT_TOOL_TYPE) {
+            outPointer.isStylus = (inSlot.absMTToolType == MT_TOOL_PEN);
+        } else {
+            // Assume this is not a stylus.
+            outPointer.isStylus = false;
+        }
 
         // Assign pointer id using tracking id if available.
         if (havePointerIds) {
-            if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
-                uint32_t id = uint32_t(inPointer.absMTTrackingId);
+            int32_t id;
+            if (mUsingSlotsProtocol) {
+                id = inIndex;
+            } else if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
+                id = inSlot.absMTTrackingId;
+            } else {
+                id = -1;
+            }
 
-                if (id > MAX_POINTER_ID) {
+            if (id >= 0 && id <= MAX_POINTER_ID) {
+                outPointer.id = id;
+                mCurrentTouch.idToIndex[id] = outCount;
+                mCurrentTouch.idBits.markBit(id);
+            } else {
+                if (id >= 0) {
 #if DEBUG_POINTERS
-                    LOGD("Pointers: Ignoring driver provided pointer id %d because "
-                            "it is larger than max supported id %d",
+                    LOGD("Pointers: Ignoring driver provided slot index or tracking id %d because "
+                            "it is larger than the maximum supported pointer id %d",
                             id, MAX_POINTER_ID);
 #endif
-                    havePointerIds = false;
                 }
-                else {
-                    outPointer.id = id;
-                    mCurrentTouch.idToIndex[id] = outCount;
-                    mCurrentTouch.idBits.markBit(id);
-                }
-            } else {
                 havePointerIds = false;
             }
         }
@@ -5386,20 +5546,40 @@
 
     syncTouch(when, havePointerIds);
 
-    mAccumulator.clear();
+    mAccumulator.clear(mUsingSlotsProtocol ? 0 : mSlotCount);
 }
 
 void MultiTouchInputMapper::configureRawAxes() {
     TouchInputMapper::configureRawAxes();
 
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, & mRawAxes.x);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, & mRawAxes.y);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, & mRawAxes.touchMajor);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, & mRawAxes.touchMinor);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mRawAxes.toolMajor);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mRawAxes.toolMinor);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mRawAxes.orientation);
-    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mRawAxes.pressure);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_X, &mRawAxes.x);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_POSITION_Y, &mRawAxes.y);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MAJOR, &mRawAxes.touchMajor);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TOUCH_MINOR, &mRawAxes.touchMinor);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, &mRawAxes.toolMajor);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, &mRawAxes.toolMinor);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, &mRawAxes.orientation);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, &mRawAxes.pressure);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_DISTANCE, &mRawAxes.distance);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_TRACKING_ID, &mRawAxes.trackingId);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_SLOT, &mRawAxes.slot);
+
+    if (mRawAxes.trackingId.valid
+            && mRawAxes.slot.valid && mRawAxes.slot.minValue == 0 && mRawAxes.slot.maxValue > 0) {
+        mSlotCount = mRawAxes.slot.maxValue + 1;
+        if (mSlotCount > MAX_SLOTS) {
+            LOGW("MultiTouch Device %s reported %d slots but the framework "
+                    "only supports a maximum of %d slots at this time.",
+                    getDeviceName().string(), mSlotCount, MAX_SLOTS);
+            mSlotCount = MAX_SLOTS;
+        }
+        mUsingSlotsProtocol = true;
+    } else {
+        mSlotCount = MAX_POINTERS;
+        mUsingSlotsProtocol = false;
+    }
+
+    mAccumulator.allocateSlots(mSlotCount);
 }
 
 
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 62ac4b2..85338b6 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -331,6 +331,7 @@
     String8 mName;
     uint32_t mSources;
     bool mIsExternal;
+    bool mDropUntilNextSync;
 
     typedef int32_t (InputMapper::*GetStateFunc)(uint32_t sourceMask, int32_t code);
     int32_t getState(uint32_t sourceMask, int32_t code, GetStateFunc getStateFunc);
@@ -602,6 +603,7 @@
         int32_t toolMajor;
         int32_t toolMinor;
         int32_t orientation;
+        int32_t distance;
         bool isStylus;
 
         inline bool operator== (const PointerData& other) const {
@@ -613,7 +615,8 @@
                     && touchMinor == other.touchMinor
                     && toolMajor == other.toolMajor
                     && toolMinor == other.toolMinor
-                    && orientation == other.orientation;
+                    && orientation == other.orientation
+                    && distance == other.distance;
         }
         inline bool operator!= (const PointerData& other) const {
             return !(*this == other);
@@ -759,6 +762,17 @@
         };
 
         OrientationCalibration orientationCalibration;
+
+        // Distance
+        enum DistanceCalibration {
+            DISTANCE_CALIBRATION_DEFAULT,
+            DISTANCE_CALIBRATION_NONE,
+            DISTANCE_CALIBRATION_SCALED,
+        };
+
+        DistanceCalibration distanceCalibration;
+        bool haveDistanceScale;
+        float distanceScale;
     } mCalibration;
 
     // Raw axis information from the driver.
@@ -771,6 +785,9 @@
         RawAbsoluteAxisInfo toolMajor;
         RawAbsoluteAxisInfo toolMinor;
         RawAbsoluteAxisInfo orientation;
+        RawAbsoluteAxisInfo distance;
+        RawAbsoluteAxisInfo trackingId;
+        RawAbsoluteAxisInfo slot;
     } mRawAxes;
 
     // Current and previous touch sample data.
@@ -819,6 +836,8 @@
 
         float orientationScale;
 
+        float distanceScale;
+
         // Oriented motion ranges for input device info.
         struct OrientedRanges {
             InputDeviceInfo::MotionRange x;
@@ -840,6 +859,9 @@
 
             bool haveOrientation;
             InputDeviceInfo::MotionRange orientation;
+
+            bool haveDistance;
+            InputDeviceInfo::MotionRange distance;
         } orientedRanges;
 
         // Oriented dimensions and precision.
@@ -1146,7 +1168,7 @@
     int32_t mToolWidth;
     int32_t mButtonState;
 
-    void initialize();
+    void clearState();
 
     void sync(nsecs_t when);
 };
@@ -1166,20 +1188,21 @@
 private:
     struct Accumulator {
         enum {
-            FIELD_ABS_MT_POSITION_X = 1,
-            FIELD_ABS_MT_POSITION_Y = 2,
-            FIELD_ABS_MT_TOUCH_MAJOR = 4,
-            FIELD_ABS_MT_TOUCH_MINOR = 8,
-            FIELD_ABS_MT_WIDTH_MAJOR = 16,
-            FIELD_ABS_MT_WIDTH_MINOR = 32,
-            FIELD_ABS_MT_ORIENTATION = 64,
-            FIELD_ABS_MT_TRACKING_ID = 128,
-            FIELD_ABS_MT_PRESSURE = 256,
+            FIELD_ABS_MT_POSITION_X = 1 << 0,
+            FIELD_ABS_MT_POSITION_Y = 1 << 1,
+            FIELD_ABS_MT_TOUCH_MAJOR = 1 << 2,
+            FIELD_ABS_MT_TOUCH_MINOR = 1 << 3,
+            FIELD_ABS_MT_WIDTH_MAJOR = 1 << 4,
+            FIELD_ABS_MT_WIDTH_MINOR = 1 << 5,
+            FIELD_ABS_MT_ORIENTATION = 1 << 6,
+            FIELD_ABS_MT_TRACKING_ID = 1 << 7,
+            FIELD_ABS_MT_PRESSURE = 1 << 8,
+            FIELD_ABS_MT_TOOL_TYPE = 1 << 9,
+            FIELD_ABS_MT_DISTANCE = 1 << 10,
         };
 
-        uint32_t pointerCount;
-        struct Pointer {
-            uint32_t fields;
+        struct Slot {
+            uint32_t fields; // 0 if slot is unused
 
             int32_t absMTPositionX;
             int32_t absMTPositionY;
@@ -1190,27 +1213,56 @@
             int32_t absMTOrientation;
             int32_t absMTTrackingId;
             int32_t absMTPressure;
+            int32_t absMTToolType;
+            int32_t absMTDistance;
+
+            inline Slot() {
+                clear();
+            }
 
             inline void clear() {
                 fields = 0;
             }
-        } pointers[MAX_POINTERS + 1]; // + 1 to remove the need for extra range checks
+        };
+
+        // Current slot index.
+        int32_t currentSlot;
+
+        // Array of slots.
+        Slot* slots;
 
         // Bitfield of buttons that went down or up.
         uint32_t buttonDown;
         uint32_t buttonUp;
 
-        inline void clear() {
-            pointerCount = 0;
-            pointers[0].clear();
+        Accumulator() : slots(NULL) {
+            clear(false);
+        }
+
+        ~Accumulator() {
+            delete[] slots;
+        }
+
+        void allocateSlots(size_t slotCount) {
+            slots = new Slot[slotCount];
+        }
+
+        void clear(size_t slotCount) {
+            for (size_t i = 0; i < slotCount; i++) {
+                slots[i].clear();
+            }
+            currentSlot = 0;
             buttonDown = 0;
             buttonUp = 0;
         }
     } mAccumulator;
 
+    size_t mSlotCount;
+    bool mUsingSlotsProtocol;
+
     int32_t mButtonState;
 
-    void initialize();
+    void clearState();
 
     void sync(nsecs_t when);
 };
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index f5d7ae8..1ab2a3e 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -613,6 +613,10 @@
         return false;
     }
 
+    virtual bool hasInputProperty(int32_t deviceId, int property) const {
+        return false;
+    }
+
     virtual status_t mapKey(int32_t deviceId, int scancode,
             int32_t* outKeycode, uint32_t* outFlags) const {
         Device* device = getDevice(deviceId);
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index ae04b7f..f78dca9 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -163,6 +163,7 @@
     private final ArrayList<StorageVolume>        mVolumes = new ArrayList<StorageVolume>();
     private StorageVolume                         mPrimaryVolume;
     private final HashMap<String, String>         mVolumeStates = new HashMap<String, String>();
+    private final HashMap<String, StorageVolume>  mVolumeMap = new HashMap<String, StorageVolume>();
     private String                                mExternalStoragePath;
     private PackageManagerService                 mPms;
     private boolean                               mUmsEnabling;
@@ -672,8 +673,6 @@
      * Callback from NativeDaemonConnector
      */
     public boolean onEvent(int code, String raw, String[] cooked) {
-        Intent in = null;
-
         if (DEBUG_EVENTS) {
             StringBuilder builder = new StringBuilder();
             builder.append("onEvent::");
@@ -708,6 +707,7 @@
             // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
             // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
             // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
+            String action = null;
             final String label = cooked[2];
             final String path = cooked[3];
             int major = -1;
@@ -746,32 +746,31 @@
                 /* Send the media unmounted event first */
                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
                 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
-                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
-                mContext.sendBroadcast(in);
+                sendStorageIntent(Environment.MEDIA_UNMOUNTED, path);
 
                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
                 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
-                in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
+                action = Intent.ACTION_MEDIA_REMOVED;
             } else if (code == VoldResponseCode.VolumeBadRemoval) {
                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
                 /* Send the media unmounted event first */
                 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
-                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
-                mContext.sendBroadcast(in);
+                action = Intent.ACTION_MEDIA_UNMOUNTED;
 
                 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
                 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
-                in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
+                action = Intent.ACTION_MEDIA_BAD_REMOVAL;
             } else {
                 Slog.e(TAG, String.format("Unknown code {%d}", code));
             }
+
+            if (action != null) {
+                sendStorageIntent(action, path);
+            }
         } else {
             return false;
         }
 
-        if (in != null) {
-            mContext.sendBroadcast(in);
-        }
         return true;
     }
 
@@ -779,12 +778,11 @@
         String vs = getVolumeState(path);
         if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
 
-        Intent in = null;
+        String action = null;
 
         if (oldState == VolumeState.Shared && newState != oldState) {
             if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
-            mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
-                                                Uri.parse("file://" + path)));
+            sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED,  path);
         }
 
         if (newState == VolumeState.Init) {
@@ -801,31 +799,29 @@
                                     Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
                 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
                 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
-                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
+                action = Intent.ACTION_MEDIA_UNMOUNTED;
             }
         } else if (newState == VolumeState.Pending) {
         } else if (newState == VolumeState.Checking) {
             if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
             updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
-            in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
+            action = Intent.ACTION_MEDIA_CHECKING;
         } else if (newState == VolumeState.Mounted) {
             if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
             updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
-            in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
-            in.putExtra("read-only", false);
+            action = Intent.ACTION_MEDIA_MOUNTED;
         } else if (newState == VolumeState.Unmounting) {
-            in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
+            action = Intent.ACTION_MEDIA_EJECT;
         } else if (newState == VolumeState.Formatting) {
         } else if (newState == VolumeState.Shared) {
             if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
             /* Send the media unmounted event first */
             updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
-            in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
-            mContext.sendBroadcast(in);
+            sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, path);
 
             if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
             updatePublicVolumeState(path, Environment.MEDIA_SHARED);
-            in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
+            action = Intent.ACTION_MEDIA_SHARED;
             if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
         } else if (newState == VolumeState.SharedMnt) {
             Slog.e(TAG, "Live shared mounts not supported yet!");
@@ -834,8 +830,8 @@
             Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
         }
 
-        if (in != null) {
-            mContext.sendBroadcast(in);
+        if (action != null) {
+            sendStorageIntent(action, path);
         }
     }
 
@@ -885,7 +881,7 @@
             /*
              * Mount failed for some reason
              */
-            Intent in = null;
+            String action = null;
             int code = e.getCode();
             if (code == VoldResponseCode.OpFailedNoMedia) {
                 /*
@@ -898,7 +894,7 @@
                  * Media is blank or does not contain a supported filesystem
                  */
                 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
-                in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
+                action = Intent.ACTION_MEDIA_NOFS;
                 rc = StorageResultCode.OperationFailedMediaBlank;
             } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
                 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
@@ -906,7 +902,7 @@
                  * Volume consistency check failed
                  */
                 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
-                in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
+                action = Intent.ACTION_MEDIA_UNMOUNTABLE;
                 rc = StorageResultCode.OperationFailedMediaCorrupt;
             } else {
                 rc = StorageResultCode.OperationFailedInternalError;
@@ -915,8 +911,8 @@
             /*
              * Send broadcast intent (if required for the failure)
              */
-            if (in != null) {
-                mContext.sendBroadcast(in);
+            if (action != null) {
+                sendStorageIntent(action, path);
             }
         }
 
@@ -1073,6 +1069,14 @@
         }
     }
 
+    private void sendStorageIntent(String action, String path) {
+        Intent intent = new Intent(action, Uri.parse("file://" + path));
+        // add StorageVolume extra
+        intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap.get(path));
+        Slog.d(TAG, "sendStorageIntent " + intent);
+        mContext.sendBroadcast(intent);
+    }
+
     private void sendUmsIntent(boolean c) {
         mContext.sendBroadcast(
                 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
@@ -1124,7 +1128,8 @@
                     if (path == null || description == null) {
                         Slog.e(TAG, "path or description is null in readStorageList");
                     } else {
-                        StorageVolume volume = new StorageVolume(path.toString(),
+                        String pathString = path.toString();
+                        StorageVolume volume = new StorageVolume(pathString,
                                 description.toString(), removable, emulated, mtpReserve);
                         if (primary) {
                             if (mPrimaryVolume == null) {
@@ -1139,6 +1144,7 @@
                         } else {
                             mVolumes.add(volume);
                         }
+                        mVolumeMap.put(pathString, volume);
                     }
                     a.recycle();
                 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 568183b..cf5592c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -853,14 +853,6 @@
      * Current sequence id for process LRU updating.
      */
     int mLruSeq = 0;
-    
-    /**
-     * Set to true if the ANDROID_SIMPLE_PROCESS_MANAGEMENT envvar
-     * is set, indicating the user wants processes started in such a way
-     * that they can use ANDROID_PROCESS_WRAPPER and know what will be
-     * running in each process (thus no pre-initialized process, etc).
-     */
-    boolean mSimpleProcessManagement = false;
 
     /**
      * System monitoring: number of processes that died since the last
@@ -1455,15 +1447,6 @@
     }
 
     private ActivityManagerService() {
-        String v = System.getenv("ANDROID_SIMPLE_PROCESS_MANAGEMENT");
-        if (v != null && Integer.getInteger(v) != 0) {
-            mSimpleProcessManagement = true;
-        }
-        v = System.getenv("ANDROID_DEBUG_APP");
-        if (v != null) {
-            mSimpleProcessManagement = true;
-        }
-
         Slog.i(TAG, "Memory class: " + ActivityManager.staticGetMemoryClass());
         
         File dataDir = Environment.getDataDirectory();
@@ -1935,8 +1918,7 @@
                 debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
             }
             int pid = Process.start("android.app.ActivityThread",
-                    mSimpleProcessManagement ? app.processName : null, uid, uid,
-                    gids, debugFlags, null);
+                    app.processName, uid, uid, gids, debugFlags, null);
             BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
             synchronized (bs) {
                 if (bs.isOnBattery()) {
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index b1ab05b..67e73f5f 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -277,8 +277,8 @@
     private final SparseIntArray mClientUids = new SparseIntArray();
 
     // how often to request NTP time, in milliseconds
-    // current setting 4 hours
-    private static final long NTP_INTERVAL = 4*60*60*1000;
+    // current setting 24 hours
+    private static final long NTP_INTERVAL = 24*60*60*1000;
     // how long to wait if we have a network error in NTP or XTRA downloading
     // current setting - 5 minutes
     private static final long RETRY_INTERVAL = 5*60*1000;
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index dc07ef2..3636baa 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -205,6 +205,7 @@
     protected long lastFailTime;
     protected FailCause lastFailCause;
     protected static final String NULL_IP = "0.0.0.0";
+    private int mRefCount;
     Object userData;
 
     //***** Abstract methods
@@ -413,49 +414,6 @@
         return mRetryMgr.configure(configStr);
     }
 
-    private AtomicInteger mRefCount = new AtomicInteger(0);
-
-    /**
-     * Set refCount.
-     *
-     * @param val is new refCount
-     */
-    public void setRefCount(int val) {
-        mRefCount.set(val);
-    }
-
-    /**
-     * Get refCount
-     *
-     * @return refCount
-     */
-    public int getRefCount() {
-        return mRefCount.get();
-    }
-
-    /**
-     * @return decrement and return refCount
-     *
-     * TODO: Consider using the refCount for defining the
-     * life time of a connection. When this goes zero the
-     * DataConnection could tear itself down.
-     */
-    public int decAndGetRefCount() {
-        int v = mRefCount.decrementAndGet();
-        if (v < 0) {
-            log("BUG: decAndGetRefCount caused refCount to be < 0");
-            mRefCount.set(0);
-        }
-        return v;
-     }
-
-    /**
-     * @return increment and return refCount
-     */
-    public int incAndGetRefCount() {
-        return mRefCount.incrementAndGet();
-    }
-
     /*
      * **************************************************************************
      * End members owned by DataConnectionTracker
@@ -471,6 +429,7 @@
         createTime = -1;
         lastFailTime = -1;
         lastFailCause = FailCause.NONE;
+        mRefCount = 0;
 
         mLinkProperties = new LinkProperties();
         mApn = null;
@@ -674,6 +633,11 @@
                     mAc.replyToMessage(msg, DataConnectionAc.RSP_RESET);
                     transitionTo(mInactiveState);
                     break;
+                case DataConnectionAc.REQ_GET_REFCOUNT: {
+                    log("REQ_GET_REFCOUNT  refCount=" + mRefCount);
+                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
+                    break;
+                }
 
                 case EVENT_CONNECT:
                     if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
@@ -774,9 +738,13 @@
                     break;
 
                 case EVENT_CONNECT:
-                    if (DBG) log("DcInactiveState msg.what=EVENT_CONNECT");
                     ConnectionParams cp = (ConnectionParams) msg.obj;
                     cp.tag = mTag;
+                    if (DBG) {
+                        log("DcInactiveState msg.what=EVENT_CONNECT." + "RefCount = "
+                                + mRefCount);
+                    }
+                    mRefCount = 1;
                     onConnect(cp);
                     transitionTo(mActivatingState);
                     retVal = HANDLED;
@@ -804,7 +772,15 @@
 
             switch (msg.what) {
                 case EVENT_DISCONNECT:
-                    if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT");
+                    if (DBG) log("DcActivatingState deferring msg.what=EVENT_DISCONNECT"
+                            + mRefCount);
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+
+                case EVENT_CONNECT:
+                    if (DBG) log("DcActivatingState deferring msg.what=EVENT_CONNECT refCount = "
+                            + mRefCount);
                     deferMessage(msg);
                     retVal = HANDLED;
                     break;
@@ -928,12 +904,28 @@
             boolean retVal;
 
             switch (msg.what) {
+                case EVENT_CONNECT:
+                    mRefCount++;
+                    if (DBG) log("DcActiveState msg.what=EVENT_CONNECT RefCount=" + mRefCount);
+                    if (msg.obj != null) {
+                        notifyConnectCompleted((ConnectionParams) msg.obj, FailCause.NONE);
+                    }
+                    retVal = HANDLED;
+                    break;
                 case EVENT_DISCONNECT:
-                    if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT");
-                    DisconnectParams dp = (DisconnectParams) msg.obj;
-                    dp.tag = mTag;
-                    tearDownData(dp);
-                    transitionTo(mDisconnectingState);
+                    mRefCount--;
+                    if (DBG) log("DcActiveState msg.what=EVENT_DISCONNECT RefCount=" + mRefCount);
+                    if (mRefCount == 0)
+                    {
+                        DisconnectParams dp = (DisconnectParams) msg.obj;
+                        dp.tag = mTag;
+                        tearDownData(dp);
+                        transitionTo(mDisconnectingState);
+                    } else {
+                        if (msg.obj != null) {
+                            notifyDisconnectCompleted((DisconnectParams) msg.obj);
+                        }
+                    }
                     retVal = HANDLED;
                     break;
 
@@ -956,6 +948,13 @@
             boolean retVal;
 
             switch (msg.what) {
+                case EVENT_CONNECT:
+                    if (DBG) log("DcDisconnectingState msg.what=EVENT_CONNECT. Defer. RefCount = "
+                            + mRefCount);
+                    deferMessage(msg);
+                    retVal = HANDLED;
+                    break;
+
                 case EVENT_DEACTIVATE_DONE:
                     if (DBG) log("DcDisconnectingState msg.what=EVENT_DEACTIVATE_DONE");
                     AsyncResult ar = (AsyncResult) msg.obj;
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionAc.java b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
index 939bab2..a0d9b0f 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
@@ -59,6 +59,9 @@
     public static final int REQ_RESET = BASE + 14;
     public static final int RSP_RESET = BASE + 15;
 
+    public static final int REQ_GET_REFCOUNT = BASE + 16;
+    public static final int RSP_GET_REFCOUNT = BASE + 17;
+
     /**
      * enum used to notify action taken or necessary to be
      * taken after the link property is changed.
@@ -152,6 +155,40 @@
     }
 
     /**
+     * Request the Reference Count.
+     * Response {@link #rspRefCount}
+     */
+    public void reqRefCount() {
+        sendMessage(REQ_GET_REFCOUNT);
+        if (DBG) log("reqRefCount");
+    }
+
+    /**
+     * Evaluate a RSP_GET_REFCOUNT message and return the refCount.
+     *
+     * @param response Message
+     * @return ref count or -1 if an error
+     */
+    public int rspRefCount(Message response) {
+        int retVal = response.arg1;
+        if (DBG) log("rspRefCount=" + retVal);
+        return retVal;
+    }
+
+    /**
+     * @return connection id or -1 if an error
+     */
+    public int getRefCountSync() {
+        Message response = sendMessageSynchronously(REQ_GET_REFCOUNT);
+        if ((response != null) && (response.what == RSP_GET_REFCOUNT)) {
+            return rspRefCount(response);
+        } else {
+            log("rspRefCount error response=" + response);
+            return -1;
+        }
+    }
+
+    /**
      * Request the connections ApnSetting.
      * Response {@link #rspApnSetting}
      */
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index b7ac879..5ddfcd1 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -987,7 +987,7 @@
                     resetAllRetryCounts();
                     onTrySetupData(Phone.REASON_DATA_ENABLED);
                 } else {
-                    onCleanUpConnection(true, APN_DEFAULT_ID, Phone.REASON_DATA_DISABLED);
+                    onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
                 }
             }
         }
diff --git a/telephony/java/com/android/internal/telephony/cat/CatService.java b/telephony/java/com/android/internal/telephony/cat/CatService.java
index 33cc97e..fb53686 100644
--- a/telephony/java/com/android/internal/telephony/cat/CatService.java
+++ b/telephony/java/com/android/internal/telephony/cat/CatService.java
@@ -32,6 +32,7 @@
 
 
 import java.io.ByteArrayOutputStream;
+import java.util.Locale;
 
 /**
  * Enumeration for representing the tag value of COMPREHENSION-TLV objects. If
@@ -273,8 +274,20 @@
                 sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
                 break;
             case PROVIDE_LOCAL_INFORMATION:
-                sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
-                return;
+                ResponseData resp;
+                switch (cmdParams.cmdDet.commandQualifier) {
+                    case CommandParamsFactory.DTTZ_SETTING:
+                        resp = new DTTZResponseData(null);
+                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
+                        break;
+                    case CommandParamsFactory.LANGUAGE_SETTING:
+                        resp = new LanguageResponseData(Locale.getDefault().getLanguage());
+                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp);
+                        break;
+                    default:
+                        sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null);
+                        return;
+                }
             case LAUNCH_BROWSER:
             case SELECT_ITEM:
             case GET_INPUT:
diff --git a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
index 12204a0..686fe46 100644
--- a/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
+++ b/telephony/java/com/android/internal/telephony/cat/CommandParamsFactory.java
@@ -53,6 +53,7 @@
     static final int REFRESH_UICC_RESET                     = 0x04;
 
     // Command Qualifier values for PLI command
+    static final int DTTZ_SETTING                           = 0x03;
     static final int LANGUAGE_SETTING                       = 0x04;
 
     static synchronized CommandParamsFactory getInstance(RilMessageDecoder caller,
@@ -883,6 +884,10 @@
             throws ResultException {
         CatLog.d(this, "process ProvideLocalInfo");
         switch (cmdDet.commandQualifier) {
+            case DTTZ_SETTING:
+                CatLog.d(this, "PLI [DTTZ_SETTING]");
+                mCmdParams = new CommandParams(cmdDet);
+                break;
             case LANGUAGE_SETTING:
                 CatLog.d(this, "PLI [LANGUAGE_SETTING]");
                 mCmdParams = new CommandParams(cmdDet);
diff --git a/telephony/java/com/android/internal/telephony/cat/ResponseData.java b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
index 95f0399..55a2b63 100644
--- a/telephony/java/com/android/internal/telephony/cat/ResponseData.java
+++ b/telephony/java/com/android/internal/telephony/cat/ResponseData.java
@@ -18,6 +18,8 @@
 
 import com.android.internal.telephony.EncodeException;
 import com.android.internal.telephony.GsmAlphabet;
+import java.util.Calendar;
+import com.android.internal.telephony.cat.AppInterface.CommandType;
 
 import java.io.ByteArrayOutputStream;
 import java.io.UnsupportedEncodingException;
@@ -147,4 +149,109 @@
     }
 }
 
+// For "PROVIDE LOCAL INFORMATION" command.
+// See TS 31.111 section 6.4.15/ETSI TS 102 223
+// TS 31.124 section 27.22.4.15 for test spec
+class LanguageResponseData extends ResponseData {
+    private String lang;
+
+    public LanguageResponseData(String lang) {
+        super();
+        this.lang = lang;
+    }
+
+    @Override
+    public void format(ByteArrayOutputStream buf) {
+        if (buf == null) {
+            return;
+        }
+
+        // Text string object
+        int tag = 0x80 | ComprehensionTlvTag.LANGUAGE.value();
+        buf.write(tag); // tag
+
+        byte[] data;
+
+        if (lang != null && lang.length() > 0) {
+            data = GsmAlphabet.stringToGsm8BitPacked(lang);
+        }
+        else {
+            data = new byte[0];
+        }
+
+        buf.write(data.length);
+
+        for (byte b : data) {
+            buf.write(b);
+        }
+    }
+}
+
+// For "PROVIDE LOCAL INFORMATION" command.
+// See TS 31.111 section 6.4.15/ETSI TS 102 223
+// TS 31.124 section 27.22.4.15 for test spec
+class DTTZResponseData extends ResponseData {
+    private Calendar calendar;
+
+    public DTTZResponseData(Calendar cal) {
+        super();
+        calendar = cal;
+    }
+
+    @Override
+    public void format(ByteArrayOutputStream buf) {
+        if (buf == null) {
+            return;
+        }
+
+        // DTTZ object
+        int tag = 0x80 | CommandType.PROVIDE_LOCAL_INFORMATION.value();
+        buf.write(tag); // tag
+
+        byte[] data = new byte[8];
+        byte btmp; // temp variable
+
+        data[0] = 0x07; // Write length of DTTZ data
+
+        if (calendar == null) {
+            calendar = Calendar.getInstance();
+        }
+        // Fill year byte
+        btmp = (byte) (calendar.get(java.util.Calendar.YEAR) % 100);
+        data[1] = (byte) (btmp / 10);
+        data[1] += (byte) ((btmp % 10) << 4);
+
+        // Fill month byte
+        btmp = (byte) (calendar.get(java.util.Calendar.MONTH) + 1);
+        data[2] = (byte) (btmp / 10);
+        data[2] += (byte) ((btmp % 10) << 4);
+
+        // Fill day byte
+        btmp = (byte) (calendar.get(java.util.Calendar.DATE));
+        data[3] = (byte) (btmp / 10);
+        data[3] += (byte) ((btmp % 10) << 4);
+
+        // Fill hour byte
+        btmp = (byte) (calendar.get(java.util.Calendar.HOUR_OF_DAY));
+        data[4] = (byte) (btmp / 10);
+        data[4] += (byte) ((btmp % 10) << 4);
+
+        // Fill minute byte
+        btmp = (byte) (calendar.get(java.util.Calendar.MINUTE));
+        data[5] = (byte) (btmp / 10);
+        data[5] += (byte) ((btmp % 10) << 4);
+
+        // Fill second byte
+        btmp = (byte) (calendar.get(java.util.Calendar.SECOND));
+        data[6] = (byte) (btmp / 10);
+        data[6] += (byte) ((btmp % 10) << 4);
+
+        // No time zone info
+        data[7] = (byte) 0xFF;
+
+        for (byte b : data) {
+            buf.write(b);
+        }
+    }
+}
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 375d0d1..e3e3d78 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -521,19 +521,7 @@
              * TODO: Make this configurable?
              */
             int nextReconnectDelay = mDataConnections.get(0).getRetryTimer();
-            log("Data Connection activate failed. Scheduling next attempt for "
-                    + (nextReconnectDelay / 1000) + "s");
-
-            AlarmManager am =
-                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
-            Intent intent = new Intent(INTENT_RECONNECT_ALARM);
-            intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
-            mReconnectIntent = PendingIntent.getBroadcast(
-                    mPhone.getContext(), 0, intent, 0);
-            am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                    SystemClock.elapsedRealtime() + nextReconnectDelay,
-                    mReconnectIntent);
-
+            startAlarmForReconnect(nextReconnectDelay, reason);
             mDataConnections.get(0).increaseRetryCount();
 
             if (!shouldPostNotification(lastFailCauseCode)) {
@@ -545,6 +533,22 @@
         }
     }
 
+    private void startAlarmForReconnect(int delay, String reason) {
+
+        log("Data Connection activate failed. Scheduling next attempt for "
+                + (delay / 1000) + "s");
+
+        AlarmManager am =
+            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+        Intent intent = new Intent(INTENT_RECONNECT_ALARM);
+        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, reason);
+        mReconnectIntent = PendingIntent.getBroadcast(
+                mPhone.getContext(), 0, intent, 0);
+        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                SystemClock.elapsedRealtime() + delay, mReconnectIntent);
+
+    }
+
     private void notifyNoData(FailCause lastFailCauseCode) {
         setState(State.FAILED);
         notifyDataAvailability(null);
@@ -702,7 +706,7 @@
         mActiveApn = null;
         if (retryAfterDisconnected(reason)) {
           // Wait a bit before trying, so we're not tying up RIL command channel.
-          sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, reason), APN_DELAY_MILLIS);
+          startAlarmForReconnect(APN_DELAY_MILLIS, reason);
       }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index a48202f..e1a6fef 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -612,28 +612,26 @@
      *
      * @param apnContext
      * @param tearDown
-     * @return refCount
+     * @return none
      */
-    private int releaseApnContext(ApnContext apnContext, boolean tearDown) {
+    private void releaseApnContext(ApnContext apnContext, boolean tearDown) {
         if (apnContext == null) {
             if (DBG) loge("releaseApnContext: apnContext null should not happen, ignore");
-            return -1;
+            return;
         }
         DataConnection dc = apnContext.getDataConnection();
         if (dc == null) {
             if (DBG) loge("releaseApnContext: apnContext dc == null should not happen, ignore");
-            return -1;
+            return;
         }
-        int refCount = dc.decAndGetRefCount();
-        if (DBG) log("releaseApnContext: dec refCount=" + refCount + " tearDown=" + tearDown);
-        if (tearDown && (refCount == 0)) {
+        if (tearDown) {
             if (DBG) log("releaseApnContext: tearing down");
             Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
             apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
         }
         apnContext.setDataConnection(null);
         apnContext.setDataConnectionAc(null);
-        return refCount;
+        return;
     }
 
     private void setupDataOnReadyApns(String reason) {
@@ -809,12 +807,7 @@
                 apnContext.setState(State.DISCONNECTING);
                 releaseApnContext(apnContext, tearDown);
             } else {
-                // STOPSHIP: Reference counting logic in GDCT still have issue.
-                //           Need to be cleaned up in later patch
                 dcac.resetSync();
-                if (apnContext.getDataConnection() != null) {
-                    apnContext.getDataConnection().setRefCount(0);
-                }
                 apnContext.setState(State.IDLE);
                 mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
                 apnContext.setDataConnection(null);
@@ -946,7 +939,12 @@
         dc = (GsmDataConnection)apnContext.getDataConnection();
 
         if (dc == null) {
-            dc = findReadyDataConnection(apn);
+
+            dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext);
+
+            if (dc == null) {
+                dc = findReadyDataConnection(apn);
+            }
 
             if (dc == null) {
                 if (DBG) log("setupData: No ready GsmDataConnection found!");
@@ -964,16 +962,16 @@
                 return false;
             }
 
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
             dc.setProfileId( profileId );
             dc.setActiveApnType(apnContext.getApnType());
-            int refCount = dc.incAndGetRefCount();
+            int refCount = dcac.getRefCountSync();
             if (DBG) log("setupData: init dc and apnContext refCount=" + refCount);
 
             // configure retry count if no other Apn is using the same connection.
-            if (refCount == 1) {
+            if (refCount == 0) {
                 configureRetry(dc, apnContext.getApnType());
             }
-            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
             apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId()));
             apnContext.setApnSetting(apn);
             apnContext.setDataConnection(dc);
@@ -1406,23 +1404,7 @@
             }
 
             int nextReconnectDelay = apnContext.getDataConnection().getRetryTimer();
-            if (DBG) {
-                log("reconnectAfterFail: activate failed. Scheduling next attempt for "
-                    + (nextReconnectDelay / 1000) + "s");
-            }
-
-            AlarmManager am =
-                (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
-            Intent intent = new Intent(INTENT_RECONNECT_ALARM);
-            intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
-            // Should put an extra of apn type?
-            intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnContext.getApnType());
-            apnContext.setReconnectIntent(PendingIntent.getBroadcast (
-                    mPhone.getContext(), 0, intent, 0));
-            am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                    SystemClock.elapsedRealtime() + nextReconnectDelay,
-                    apnContext.getReconnectIntent());
-
+            startAlarmForReconnect(nextReconnectDelay, apnContext);
             apnContext.getDataConnection().increaseRetryCount();
 
             if (!shouldPostNotification(lastFailCauseCode)) {
@@ -1436,6 +1418,25 @@
         }
     }
 
+    private void startAlarmForReconnect(int delay, ApnContext apnContext) {
+
+        if (DBG) {
+            log("Schedule alarm for reconnect: activate failed. Scheduling next attempt for "
+                + (delay / 1000) + "s");
+        }
+
+        AlarmManager am =
+            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
+        Intent intent = new Intent(INTENT_RECONNECT_ALARM);
+        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
+        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnContext.getApnType());
+        apnContext.setReconnectIntent(PendingIntent.getBroadcast (
+                mPhone.getContext(), 0, intent, 0));
+        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                SystemClock.elapsedRealtime() + delay, apnContext.getReconnectIntent());
+
+    }
+
     private void notifyNoData(GsmDataConnection.FailCause lastFailCauseCode,
                               ApnContext apnContext) {
         if (DBG) log( "notifyNoData: type=" + apnContext.getApnType());
@@ -1490,23 +1491,10 @@
                 } else {
                     apnContext.setReason(Phone.REASON_DATA_ENABLED);
                 }
-                DataConnection conn = checkForConnectionForApnContext(apnContext);
-                if (conn == null) {
-                    if (apnContext.getState() == State.FAILED) {
-                        apnContext.setState(State.IDLE);
-                    }
-                    trySetup = true;
-                } else {
-                    int refCount = conn.incAndGetRefCount();
-                    apnContext.setDataConnection(conn);
-                    apnContext.setDataConnectionAc(
-                            mDataConnectionAsyncChannels.get(conn.getDataConnectionId()));
-                    if (DBG) {
-                        log("applyNewState: Found existing connection for " +
-                                apnContext.getApnType() + " inc refCount=" + refCount +
-                                " conn=" + conn);
-                    }
+                if (apnContext.getState() == State.FAILED) {
+                    apnContext.setState(State.IDLE);
                 }
+                trySetup = true;
             }
         }
         apnContext.setEnabled(enabled);
@@ -1641,7 +1629,7 @@
 
             if (DBG) {
                 log(String.format("onDataSetupComplete: success apn=%s",
-                    apnContext.getWaitingApns().get(0).apn) + " refCount=" + dc.getRefCount());
+                    apnContext.getWaitingApns().get(0).apn));
             }
             ApnSetting apn = apnContext.getApnSetting();
             if (apn.proxy != null && apn.proxy.length() != 0) {
@@ -1709,10 +1697,9 @@
                     apnContext.setState(State.FAILED);
                     mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
 
-                    int refCount = releaseApnContext(apnContext, false);
+                    releaseApnContext(apnContext, false);
                     if (DBG) {
-                        log("onDataSetupComplete: permanent error apn=%s" + apnString +
-                                                                            " refCount=" + refCount);
+                        log("onDataSetupComplete: permanent error apn=%s" + apnString );
                     }
                 } else {
                     if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
@@ -1723,8 +1710,7 @@
                 apnContext.setState(State.SCANNING);
                 // Wait a bit before trying the next APN, so that
                 // we're not tying up the RIL command channel
-                sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext),
-                        APN_DELAY_MILLIS);
+                startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
             }
         }
     }
@@ -1764,7 +1750,7 @@
             // Wait a bit before trying the next APN, so that
             // we're not tying up the RIL command channel.
             // This also helps in any external dependency to turn off the context.
-            sendMessageDelayed(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext),APN_DELAY_MILLIS);
+            startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
         }
     }
 
@@ -1807,7 +1793,10 @@
     protected void onCleanUpConnection(boolean tearDown, int apnId, String reason) {
         if (DBG) log("onCleanUpConnection");
         ApnContext apnContext = mApnContexts.get(apnIdToType(apnId));
-        cleanUpConnection(tearDown, apnContext);
+        if (apnContext != null) {
+            apnContext.setReason(reason);
+            cleanUpConnection(tearDown, apnContext);
+        }
     }
 
     protected boolean isConnected() {
diff --git a/tests/DumpRenderTree2/assets/run_apache2.py b/tests/DumpRenderTree2/assets/run_apache2.py
index b4a8685..3806599 100755
--- a/tests/DumpRenderTree2/assets/run_apache2.py
+++ b/tests/DumpRenderTree2/assets/run_apache2.py
@@ -79,8 +79,8 @@
   # complete set of tests and the required scripts.
   directives += " -c \"DocumentRoot " + os.path.join(layout_tests_path, "http", "tests/") + "\""
   directives += " -c \"Alias /LayoutTests " + layout_tests_path + "\""
-  directives += " -c \"Alias /WebKitTools/DumpRenderTree/android " + \
-    os.path.join(webkit_path, "WebKitTools", "DumpRenderTree", "android") + "\""
+  directives += " -c \"Alias /Tools/DumpRenderTree/android " + \
+    os.path.join(webkit_path, "Tools", "DumpRenderTree", "android") + "\""
   directives += " -c \"Alias /ThirdPartyProject.prop " + \
     os.path.join(webkit_path, "ThirdPartyProject.prop") + "\""
 
diff --git a/tests/DumpRenderTree2/assets/run_layout_tests.py b/tests/DumpRenderTree2/assets/run_layout_tests.py
index 3b8c09a..161416a 100755
--- a/tests/DumpRenderTree2/assets/run_layout_tests.py
+++ b/tests/DumpRenderTree2/assets/run_layout_tests.py
@@ -44,9 +44,15 @@
   logging.info("Running the tests...")
   logging.debug("Command = %s" % cmd)
   (stdoutdata, stderrdata) = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+  if stderrdata != "":
+    logging.info("Failed to start tests:\n%s", stderrdata)
+    return
   if re.search("^INSTRUMENTATION_STATUS_CODE: -1", stdoutdata, re.MULTILINE) != None:
     logging.info("Failed to run the tests. Is DumpRenderTree2 installed on the device?")
     return
+  if re.search("^OK \([0-9]+ tests?\)", stdoutdata, re.MULTILINE) == None:
+    logging.info("DumpRenderTree2 failed to run correctly:\n%s", stdoutdata)
+    return
 
   logging.info("Downloading the summaries...")
 
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
index d1aba437..54cbfda 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/FsUtils.java
@@ -63,7 +63,7 @@
     public static final String LOG_TAG = "FsUtils";
 
     private static final String SCRIPT_URL = ForwarderManager.getHostSchemePort(false) +
-            "WebKitTools/DumpRenderTree/android/get_layout_tests_dir_contents.php";
+            "Tools/DumpRenderTree/android/get_layout_tests_dir_contents.php";
 
     private static final int HTTP_TIMEOUT_MS = 5000;
 
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
index d9f5dd4..f59da37 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/LayoutTestsExecutor.java
@@ -398,6 +398,11 @@
     }
 
     private void startTests() {
+        // This is called when the tests are started and after each crash.
+        // We only send the reset message in the former case.
+        if (mCurrentTestIndex <= 0) {
+            sendResetMessage();
+        }
         if (mCurrentTestIndex == 0) {
             sendFirstTestMessage();
         }
@@ -405,6 +410,15 @@
         runNextTest();
     }
 
+    private void sendResetMessage() {
+        try {
+            Message serviceMsg = Message.obtain(null, ManagerService.MSG_RESET);
+            mManagerServiceMessenger.send(serviceMsg);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Error sending message to manager service:", e);
+        }
+    }
+
     private void sendFirstTestMessage() {
         try {
             Message serviceMsg = Message.obtain(null, ManagerService.MSG_FIRST_TEST);
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
index e4df62d..4783cc7 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/ManagerService.java
@@ -66,6 +66,7 @@
     static final int MSG_ALL_TESTS_FINISHED = 1;
     static final int MSG_FIRST_TEST = 2;
     static final int MSG_CURRENT_TEST_CRASHED = 3;
+    static final int MSG_RESET = 4;
 
     /**
      * This handler is purely for IPC. It is used to create mMessenger
@@ -75,8 +76,11 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case MSG_FIRST_TEST:
+                case MSG_RESET:
                     mSummarizer.reset();
+                    break;
+
+                case MSG_FIRST_TEST:
                     Bundle bundle = msg.getData();
                     ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index"));
                     break;
diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
index 65c6964..bae8e6b 100644
--- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
+++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/Summarizer.java
@@ -568,7 +568,7 @@
         URL url = null;
         try {
             url = new URL("http", "localhost", ForwarderManager.HTTP_PORT,
-                    "/WebKitTools/DumpRenderTree/android/view_source.php?src=" +
+                    "/Tools/DumpRenderTree/android/view_source.php?src=" +
                     relativePath);
         } catch (MalformedURLException e) {
             assert false : "relativePath=" + relativePath;
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp
index 75535f8..4894196 100644
--- a/tools/aapt/AaptAssets.cpp
+++ b/tools/aapt/AaptAssets.cpp
@@ -913,6 +913,11 @@
               (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
               | ResTable_config::UI_MODE_TYPE_CAR;
         return true;
+    } else if (strcmp(name, "television") == 0) {
+      if (out) out->uiMode =
+              (out->uiMode&~ResTable_config::MASK_UI_MODE_TYPE)
+              | ResTable_config::UI_MODE_TYPE_TELEVISION;
+        return true;
     }
 
     return false;
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 134f29f..bd6da64 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -491,7 +491,7 @@
         mNetworkInfo.setIsAvailable(false);
         mLinkProperties.clear();
         mLastBssid = null;
-        mLastNetworkId = -1;
+        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
         mLastSignalLevel = -1;
 
         mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
@@ -837,11 +837,12 @@
     }
 
     public void connectNetwork(WifiConfiguration wifiConfig) {
-        /* arg1 is used to indicate netId, force a netId value of -1 when
-         * we are passing a configuration since the default value of
-         * 0 is a valid netId
+        /* arg1 is used to indicate netId, force a netId value of
+         * WifiConfiguration.INVALID_NETWORK_ID when we are passing
+         * a configuration since the default value of 0 is a valid netId
          */
-        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, -1, 0, wifiConfig));
+        sendMessage(obtainMessage(CMD_CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
+                0, wifiConfig));
     }
 
     public void saveNetwork(WifiConfiguration wifiConfig) {
@@ -1445,7 +1446,7 @@
         mWifiInfo.setInetAddress(null);
         mWifiInfo.setBSSID(null);
         mWifiInfo.setSSID(null);
-        mWifiInfo.setNetworkId(-1);
+        mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
         mWifiInfo.setRssi(MIN_RSSI);
         mWifiInfo.setLinkSpeed(-1);
 
@@ -1457,7 +1458,7 @@
         mLinkProperties.clear();
 
         mLastBssid= null;
-        mLastNetworkId = -1;
+        mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
 
     }
 
@@ -2050,7 +2051,7 @@
                     mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
                     /* Initialize data structures */
                     mLastBssid = null;
-                    mLastNetworkId = -1;
+                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
                     mLastSignalLevel = -1;
 
                     mWifiInfo.setMacAddress(WifiNative.getMacAddressCommand());
@@ -2545,7 +2546,10 @@
                     // Network id is only valid when we start connecting
                     if (SupplicantState.isConnecting(state)) {
                         mWifiInfo.setNetworkId(stateChangeResult.networkId);
+                    } else {
+                        mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
                     }
+
                     if (state == SupplicantState.ASSOCIATING) {
                         /* BSSID is valid only in ASSOCIATING state */
                         mWifiInfo.setBSSID(stateChangeResult.BSSID);