Merge "Convert line breaks to Unix style"
diff --git a/Android.mk b/Android.mk
index b8a48a4..e3406ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -71,6 +71,7 @@
 	core/java/android/app/IBackupAgent.aidl \
 	core/java/android/app/IInstrumentationWatcher.aidl \
 	core/java/android/app/INotificationManager.aidl \
+	core/java/android/app/IProcessObserver.aidl \
 	core/java/android/app/ISearchManager.aidl \
 	core/java/android/app/ISearchManagerCallback.aidl \
 	core/java/android/app/IServiceConnection.aidl \
@@ -113,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 \
@@ -160,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 \
@@ -361,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
@@ -584,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 7d87425..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
@@ -10058,6 +10064,7 @@
     method public void setOnSeekCompleteListener(android.media.MediaPlayer.OnSeekCompleteListener);
     method public void setOnVideoSizeChangedListener(android.media.MediaPlayer.OnVideoSizeChangedListener);
     method public void setScreenOnWhilePlaying(boolean);
+    method public void setTexture(android.graphics.SurfaceTexture);
     method public void setVolume(float, float);
     method public void setWakeMode(android.content.Context, int);
     method public void start() throws java.lang.IllegalStateException;
@@ -11640,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();
@@ -11651,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();
   }
@@ -13923,12 +13936,14 @@
 
   public class ParcelFileDescriptor implements android.os.Parcelable {
     ctor public ParcelFileDescriptor(android.os.ParcelFileDescriptor);
+    method public static android.os.ParcelFileDescriptor adoptFd(int);
     method public void close() throws java.io.IOException;
     method public static android.os.ParcelFileDescriptor[] createPipe() throws java.io.IOException;
     method public int describeContents();
     method public int detachFd();
     method public static android.os.ParcelFileDescriptor dup(java.io.FileDescriptor) throws java.io.IOException;
     method public static android.os.ParcelFileDescriptor fromDatagramSocket(java.net.DatagramSocket);
+    method public static android.os.ParcelFileDescriptor fromFd(int) throws java.io.IOException;
     method public static android.os.ParcelFileDescriptor fromSocket(java.net.Socket);
     method public int getFd();
     method public java.io.FileDescriptor getFileDescriptor();
@@ -21430,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);
@@ -21847,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);
@@ -21860,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);
@@ -25204,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();
@@ -25224,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();
@@ -25295,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/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f2c97964..2a0d798 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1467,6 +1467,22 @@
             return true;
         }
 
+        case REGISTER_PROCESS_OBSERVER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IProcessObserver observer = IProcessObserver.Stub.asInterface(
+                    data.readStrongBinder());
+            registerProcessObserver(observer);
+            return true;
+        }
+
+        case UNREGISTER_PROCESS_OBSERVER_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IProcessObserver observer = IProcessObserver.Stub.asInterface(
+                    data.readStrongBinder());
+            unregisterProcessObserver(observer);
+            return true;
+        }
+
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3300,5 +3316,27 @@
         return result;
     }
 
+    public void registerProcessObserver(IProcessObserver observer) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+        mRemote.transact(REGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(observer != null ? observer.asBinder() : null);
+        mRemote.transact(UNREGISTER_PROCESS_OBSERVER_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 54c3422..1f53c0e 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -355,6 +355,9 @@
 
     public boolean removeTask(int taskId, int flags) throws RemoteException;
 
+    public void registerProcessObserver(IProcessObserver observer) throws RemoteException;
+    public void unregisterProcessObserver(IProcessObserver observer) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -577,4 +580,6 @@
     int SWITCH_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+127;
     int REMOVE_SUB_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+128;
     int REMOVE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+129;
+    int REGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+130;
+    int UNREGISTER_PROCESS_OBSERVER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+131;
 }
diff --git a/core/java/android/app/IProcessObserver.aidl b/core/java/android/app/IProcessObserver.aidl
new file mode 100644
index 0000000..2094294
--- /dev/null
+++ b/core/java/android/app/IProcessObserver.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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.app;
+
+/** {@hide} */
+oneway interface IProcessObserver {
+
+    void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities);
+    void onProcessDied(int pid, int uid);
+
+}
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/Context.java b/core/java/android/content/Context.java
index 4c7d87f..a660bd7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1544,6 +1544,9 @@
      */
     public static final String NETWORKMANAGEMENT_SERVICE = "network_management";
 
+    /** {@hide} */
+    public static final String NETWORK_POLICY_SERVICE = "netpolicy";
+
     /**
      * Use with {@link #getSystemService} to retrieve a {@link
      * android.net.wifi.WifiManager} for handling management of
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/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index fa6eae5..d9351ee 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -23,9 +23,6 @@
  */
 interface INetworkPolicyManager {
 
-    void onForegroundActivitiesChanged(int uid, int pid, boolean foregroundActivities);
-    void onProcessDied(int uid, int pid);
-
     void setUidPolicy(int uid, int policy);
     int getUidPolicy(int uid);
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 2312bd9..1913aa7 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.content.Context;
 import android.os.RemoteException;
 
 /**
@@ -43,6 +44,10 @@
         mService = service;
     }
 
+    public static NetworkPolicyManager getSystemService(Context context) {
+        return (NetworkPolicyManager) context.getSystemService(Context.NETWORK_POLICY_SERVICE);
+    }
+
     /**
      * Set policy flags for specific UID.
      *
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 4430e00..0f207bc 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -36,6 +36,9 @@
     /** {@link #uid} value when entry is summarized over all UIDs. */
     public static final int UID_ALL = 0;
 
+    // NOTE: data should only be accounted for once in this structure; if data
+    // is broken out, the summarized version should not be included.
+
     /**
      * {@link SystemClock#elapsedRealtime()} timestamp when this data was
      * generated.
@@ -81,12 +84,13 @@
             mTx = new long[size];
         }
 
-        public void addEntry(String iface, int uid, long rx, long tx) {
+        public Builder addEntry(String iface, int uid, long rx, long tx) {
             mIface[mIndex] = iface;
             mUid[mIndex] = uid;
             mRx[mIndex] = rx;
             mTx[mIndex] = tx;
             mIndex++;
+            return this;
         }
 
         public NetworkStats build() {
@@ -97,11 +101,17 @@
         }
     }
 
+    public int length() {
+        // length is identical for all fields
+        return iface.length;
+    }
+
     /**
      * Find first stats index that matches the requested parameters.
      */
     public int findIndex(String iface, int uid) {
-        for (int i = 0; i < this.iface.length; i++) {
+        final int length = length();
+        for (int i = 0; i < length; i++) {
             if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
                 return i;
             }
@@ -109,13 +119,38 @@
         return -1;
     }
 
-    private static boolean equal(Object a, Object b) {
-        return a == b || (a != null && a.equals(b));
+    /**
+     * Subtract the given {@link NetworkStats}, effectively leaving the delta
+     * between two snapshots in time. Assumes that statistics rows collect over
+     * time, and that none of them have disappeared.
+     */
+    public NetworkStats subtract(NetworkStats value) {
+        // result will have our rows, but no meaningful timestamp
+        final int length = length();
+        final NetworkStats.Builder result = new NetworkStats.Builder(-1, length);
+
+        for (int i = 0; i < length; i++) {
+            final String iface = this.iface[i];
+            final int uid = this.uid[i];
+
+            // find remote row that matches, and subtract
+            final int j = value.findIndex(iface, uid);
+            if (j == -1) {
+                // newly appearing row, return entire value
+                result.addEntry(iface, uid, this.rx[i], this.tx[i]);
+            } else {
+                // existing row, subtract remote value
+                final long rx = this.rx[i] - value.rx[j];
+                final long tx = this.tx[i] - value.tx[j];
+                result.addEntry(iface, uid, rx, tx);
+            }
+        }
+
+        return result.build();
     }
 
-    /** {@inheritDoc} */
-    public int describeContents() {
-        return 0;
+    private static boolean equal(Object a, Object b) {
+        return a == b || (a != null && a.equals(b));
     }
 
     public void dump(String prefix, PrintWriter pw) {
@@ -138,6 +173,11 @@
     }
 
     /** {@inheritDoc} */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeLong(elapsedRealtime);
         dest.writeStringArray(iface);
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 7ee7a81..c0ff734 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,6 +16,12 @@
 
 package android.net;
 
+import android.content.Context;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
 import dalvik.system.BlockGuard;
 
 import java.net.Socket;
@@ -36,6 +42,17 @@
     public final static int UNSUPPORTED = -1;
 
     /**
+     * Snapshot of {@link NetworkStats} when the currently active profiling
+     * session started, or {@code null} if no session active.
+     *
+     * @see #startDataProfiling(Context)
+     * @see #stopDataProfiling(Context)
+     */
+    private static NetworkStats sActiveProfilingStart;
+
+    private static Object sProfilingLock = new Object();
+
+    /**
      * Set active tag to use when accounting {@link Socket} traffic originating
      * from the current thread. Only one active tag per thread is supported.
      * <p>
@@ -93,6 +110,44 @@
     }
 
     /**
+     * Start profiling data usage for current UID. Only one profiling session
+     * can be active at a time.
+     *
+     * @hide
+     */
+    public static void startDataProfiling(Context context) {
+        synchronized (sProfilingLock) {
+            if (sActiveProfilingStart != null) {
+                throw new IllegalStateException("already profiling data");
+            }
+
+            // take snapshot in time; we calculate delta later
+            sActiveProfilingStart = getNetworkStatsForUid(context);
+        }
+    }
+
+    /**
+     * Stop profiling data usage for current UID.
+     *
+     * @return Detailed {@link NetworkStats} of data that occurred since last
+     *         {@link #startDataProfiling(Context)} call.
+     * @hide
+     */
+    public static NetworkStats stopDataProfiling(Context context) {
+        synchronized (sProfilingLock) {
+            if (sActiveProfilingStart == null) {
+                throw new IllegalStateException("not profiling data");
+            }
+
+            // subtract starting values and return delta
+            final NetworkStats profilingStop = getNetworkStatsForUid(context);
+            final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart);
+            sActiveProfilingStart = null;
+            return profilingDelta;
+        }
+    }
+
+    /**
      * Get the total number of packets transmitted through the mobile interface.
      *
      * @return number of packets.  If the statistics are not supported by this device,
@@ -350,4 +405,21 @@
      * {@link #UNSUPPORTED} will be returned.
      */
     public static native long getUidUdpRxPackets(int uid);
+
+    /**
+     * Return detailed {@link NetworkStats} for the current UID. Requires no
+     * special permission.
+     */
+    private static NetworkStats getNetworkStatsForUid(Context context) {
+        final IBinder binder = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        final INetworkManagementService service = INetworkManagementService.Stub.asInterface(
+                binder);
+
+        final int uid = android.os.Process.myUid();
+        try {
+            return service.getNetworkStatsUidDetail(uid);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
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/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index ecc111b..f17a6f2 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -214,6 +214,12 @@
     NetworkStats getNetworkStatsDetail();
 
     /**
+     * Return detailed network statistics for the requested UID,
+     * including interface and tag details.
+     */
+    NetworkStats getNetworkStatsUidDetail(int uid);
+
+    /**
      * Configures bandwidth throttling on an interface.
      */
     void setInterfaceThrottle(String iface, int rxKbps, int txKbps);
diff --git a/core/java/android/os/ParcelFileDescriptor.java b/core/java/android/os/ParcelFileDescriptor.java
index 727fcca..3ea3f56 100644
--- a/core/java/android/os/ParcelFileDescriptor.java
+++ b/core/java/android/os/ParcelFileDescriptor.java
@@ -129,7 +129,46 @@
     }
 
     /**
-     * Create a new ParcelFileDescriptor from the specified Socket.
+     * Create a new ParcelFileDescriptor from a raw native fd.  The new
+     * ParcelFileDescriptor holds a dup of the original fd passed in here,
+     * so you must still close that fd as well as the new ParcelFileDescriptor.
+     *
+     * @param fd The native fd that the ParcelFileDescriptor should dup.
+     *
+     * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
+     * for a dup of the given fd.
+     */
+    public static ParcelFileDescriptor fromFd(int fd) throws IOException {
+        FileDescriptor fdesc = getFileDescriptorFromFd(fd);
+        return new ParcelFileDescriptor(fdesc);
+    }
+
+    // Extracts the file descriptor from the specified socket and returns it untouched
+    private static native FileDescriptor getFileDescriptorFromFd(int fd) throws IOException;
+
+    /**
+     * Take ownership of a raw native fd in to a new ParcelFileDescriptor.
+     * The returned ParcelFileDescriptor now owns the given fd, and will be
+     * responsible for closing it.  You must not close the fd yourself.
+     *
+     * @param fd The native fd that the ParcelFileDescriptor should adopt.
+     *
+     * @return Returns a new ParcelFileDescriptor holding a FileDescriptor
+     * for the given fd.
+     */
+    public static ParcelFileDescriptor adoptFd(int fd) {
+        FileDescriptor fdesc = getFileDescriptorFromFdNoDup(fd);
+        return new ParcelFileDescriptor(fdesc);
+    }
+
+    // Extracts the file descriptor from the specified socket and returns it untouched
+    private static native FileDescriptor getFileDescriptorFromFdNoDup(int fd);
+
+    /**
+     * Create a new ParcelFileDescriptor from the specified Socket.  The new
+     * ParcelFileDescriptor holds a dup of the original FileDescriptor in
+     * the Socket, so you must still close the Socket as well as the new
+     * ParcelFileDescriptor.
      *
      * @param socket The Socket whose FileDescriptor is used to create
      *               a new ParcelFileDescriptor.
@@ -163,17 +202,14 @@
      */
     public static ParcelFileDescriptor[] createPipe() throws IOException {
         FileDescriptor[] fds = new FileDescriptor[2];
-        int res = createPipeNative(fds);
-        if (res == 0) {
-            ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2];
-            pfds[0] = new ParcelFileDescriptor(fds[0]);
-            pfds[1] = new ParcelFileDescriptor(fds[1]);
-            return pfds;
-        }
-        throw new IOException("Unable to create pipe: errno=" + -res);
+        createPipeNative(fds);
+        ParcelFileDescriptor[] pfds = new ParcelFileDescriptor[2];
+        pfds[0] = new ParcelFileDescriptor(fds[0]);
+        pfds[1] = new ParcelFileDescriptor(fds[1]);
+        return pfds;
     }
 
-    private static native int createPipeNative(FileDescriptor[] outFds);
+    private static native void createPipeNative(FileDescriptor[] outFds) throws IOException;
 
     /**
      * @hide Please use createPipe() or ContentProvider.openPipeHelper().
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/StrictMode.java b/core/java/android/os/StrictMode.java
index 1375a29..01c640a 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -1481,6 +1481,13 @@
         onVmPolicyViolation(message, originStack);
     }
 
+    /**
+     * @hide
+     */
+    public static void onWebViewMethodCalledOnWrongThread(Throwable originStack) {
+        onVmPolicyViolation(null, originStack);
+    }
+
     // Map from VM violation fingerprint to uptime millis.
     private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();
 
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/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
old mode 100644
new mode 100755
index 2c79385..60bee9a
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -1104,7 +1104,7 @@
     }
 
     /*package*/ synchronized boolean setBondState(String address, int state, int reason) {
-        mBondState.setBondState(address.toUpperCase(), state);
+        mBondState.setBondState(address.toUpperCase(), state, reason);
         return true;
     }
 
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/WebView.java b/core/java/android/webkit/WebView.java
index f774803..61a69ca 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -54,7 +54,9 @@
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
+import android.os.StrictMode;
 import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
 import android.text.Selection;
@@ -8933,15 +8935,14 @@
     }
 
     private static void checkThread() {
-        if (!"main".equals(Thread.currentThread().getName())) {
-            try {
-                throw new RuntimeException("A WebView method was called on thread '" +
-                        Thread.currentThread().getName() + "'. " +
-                        "All WebView methods must be called on the UI thread. " +
-                        "Future versions of WebView may not support use on other threads.");
-            } catch (RuntimeException e) {
-                Log.e(LOGTAG, Log.getStackTraceString(e));
-            }
+        if (Looper.myLooper() != Looper.getMainLooper()) {
+            RuntimeException exception = new RuntimeException(
+                    "A WebView method was called on thread '" +
+                    Thread.currentThread().getName() + "'. " +
+                    "All WebView methods must be called on the UI thread. " +
+                    "Future versions of WebView may not support use on other threads.");
+            Log.e(LOGTAG, Log.getStackTraceString(exception));
+            StrictMode.onWebViewMethodCalledOnWrongThread(exception);
         }
     }
 
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 86fefaf..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) {
@@ -1331,7 +1381,7 @@
     void layoutVertical() {
         final int paddingLeft = mPaddingLeft;
 
-        int childTop = mPaddingTop;
+        int childTop;
         int childLeft;
         
         // Where right end of child should go
@@ -1346,19 +1396,21 @@
         final int majorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
         final int minorGravity = mGravity & Gravity.HORIZONTAL_GRAVITY_MASK;
 
-        if (majorGravity != Gravity.TOP) {
-           switch (majorGravity) {
-               case Gravity.BOTTOM:
-                   // mTotalLength contains the padding already, we add the top
-                   // padding to compensate
-                   childTop = mBottom - mTop + mPaddingTop - mTotalLength;
-                   break;
+        switch (majorGravity) {
+           case Gravity.BOTTOM:
+               // mTotalLength contains the padding already
+               childTop = mPaddingTop + mBottom - mTop - mTotalLength;
+               break;
 
-               case Gravity.CENTER_VERTICAL:
-                   childTop += ((mBottom - mTop)  - mTotalLength) / 2;
-                   break;
-           }
-           
+               // mTotalLength contains the padding already
+           case Gravity.CENTER_VERTICAL:
+               childTop = mPaddingTop + (mBottom - mTop - mTotalLength) / 2;
+               break;
+
+           case Gravity.TOP:
+           default:
+               childTop = mPaddingTop;
+               break;
         }
 
         for (int i = 0; i < count; i++) {
@@ -1376,12 +1428,8 @@
                 if (gravity < 0) {
                     gravity = minorGravity;
                 }
-                
-                switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
-                    case Gravity.LEFT:
-                        childLeft = paddingLeft + lp.leftMargin;
-                        break;
 
+                switch (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
                     case Gravity.CENTER_HORIZONTAL:
                         childLeft = paddingLeft + ((childSpace - childWidth) / 2)
                                 + lp.leftMargin - lp.rightMargin;
@@ -1390,11 +1438,13 @@
                     case Gravity.RIGHT:
                         childLeft = childRight - childWidth - lp.rightMargin;
                         break;
+
+                    case Gravity.LEFT:
                     default:
-                        childLeft = paddingLeft;
+                        childLeft = paddingLeft + lp.leftMargin;
                         break;
                 }
-                
+
                 if (hasDividerBeforeChildAt(i)) {
                     childTop += mDividerHeight;
                 }
@@ -1418,10 +1468,11 @@
      * @see #onLayout(boolean, int, int, int, int)
      */
     void layoutHorizontal() {
+        final boolean isLayoutRtl = isLayoutRtl();
         final int paddingTop = mPaddingTop;
 
         int childTop;
-        int childLeft = mPaddingLeft;
+        int childLeft;
         
         // Where bottom of child should go
         final int height = mBottom - mTop;
@@ -1440,25 +1491,37 @@
         final int[] maxAscent = mMaxAscent;
         final int[] maxDescent = mMaxDescent;
 
-        if (majorGravity != Gravity.LEFT) {
-            switch (majorGravity) {
-                case Gravity.RIGHT:
-                    // mTotalLength contains the padding already, we add the left
-                    // padding to compensate
-                    childLeft = mRight - mLeft + mPaddingLeft - mTotalLength;
-                    break;
+        switch (majorGravity) {
+            case Gravity.RIGHT:
+                // mTotalLength contains the padding already
+                childLeft = mPaddingLeft + mRight - mLeft - mTotalLength;
+                break;
 
-                case Gravity.CENTER_HORIZONTAL:
-                    childLeft += ((mRight - mLeft) - mTotalLength) / 2;
-                    break;
-            }
+            case Gravity.CENTER_HORIZONTAL:
+                // mTotalLength contains the padding already
+                childLeft = mPaddingLeft + (mRight - mLeft - mTotalLength) / 2;
+                break;
+
+            case Gravity.LEFT:
+            default:
+                childLeft = mPaddingLeft;
+                break;
+        }
+
+        int start = 0;
+        int dir = 1;
+        //In case of RTL, start drawing from the last child.
+        if (isLayoutRtl) {
+            start = count - 1;
+            dir = -1;
         }
 
         for (int i = 0; i < count; i++) {
-            final View child = getVirtualChildAt(i);
+            int childIndex = start + dir * i;
+            final View child = getVirtualChildAt(childIndex);
 
             if (child == null) {
-                childLeft += measureNullChild(i);
+                childLeft += measureNullChild(childIndex);
             } else if (child.getVisibility() != GONE) {
                 final int childWidth = child.getMeasuredWidth();
                 final int childHeight = child.getMeasuredHeight();
@@ -1512,7 +1575,7 @@
                         break;
                 }
 
-                if (hasDividerBeforeChildAt(i)) {
+                if (hasDividerBeforeChildAt(childIndex)) {
                     childLeft += mDividerWidth;
                 }
 
@@ -1522,7 +1585,7 @@
                 childLeft += childWidth + lp.rightMargin +
                         getNextLocationOffset(child);
 
-                i += getChildrenSkipCount(child, i);
+                i += getChildrenSkipCount(child, childIndex);
             }
         }
     }
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 1e1a043..563fc26 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1171,8 +1171,7 @@
         int bottomEdge = displayFrame.bottom;
         if (ignoreBottomDecorations) {
             Resources res = anchor.getContext().getResources();
-            bottomEdge = res.getDisplayMetrics().heightPixels -
-                    (int) res.getDimension(com.android.internal.R.dimen.screen_margin_bottom);
+            bottomEdge = res.getDisplayMetrics().heightPixels;
         }
         final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
         final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
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/view/menu/ActionMenuView.java b/core/java/com/android/internal/view/menu/ActionMenuView.java
index 290bf08..7b4f216 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuView.java
@@ -89,7 +89,6 @@
         final int childCount = getChildCount();
         final int midVertical = (top + bottom) / 2;
         final int dividerWidth = getDividerWidth();
-        boolean hasOverflow = false;
         int overflowWidth = 0;
         int nonOverflowWidth = 0;
         int nonOverflowCount = 0;
@@ -102,7 +101,6 @@
 
             LayoutParams p = (LayoutParams) v.getLayoutParams();
             if (p.isOverflowButton) {
-                hasOverflow = true;
                 overflowWidth = v.getMeasuredWidth();
                 if (hasDividerBeforeChildAt(i)) {
                     overflowWidth += dividerWidth;
@@ -125,15 +123,12 @@
             }
         }
 
-        // Try to center non-overflow items with uniformly spaced padding, including on the edges.
-        // Overflow will always pin to the right edge. If there isn't enough room for that,
-        // center in the remaining space.
+        // Fill action items from the left. Overflow will always pin to the right edge.
         if (nonOverflowWidth <= widthRemaining - overflowWidth) {
             widthRemaining -= overflowWidth;
         }
 
-        final int spacing = (widthRemaining - nonOverflowWidth) / (nonOverflowCount + 1);
-        int startLeft = getPaddingLeft() + overflowWidth + spacing;
+        int startLeft = getPaddingLeft();
         for (int i = 0; i < childCount; i++) {
             final View v = getChildAt(i);
             final LayoutParams lp = (LayoutParams) v.getLayoutParams();
@@ -146,7 +141,7 @@
             int height = v.getMeasuredHeight();
             int t = midVertical - (height / 2);
             v.layout(startLeft, t, startLeft + width, t + height);
-            startLeft += width + lp.rightMargin + spacing;
+            startLeft += width + lp.rightMargin;
         }
     }
 
diff --git a/core/java/com/android/internal/view/menu/SubMenuBuilder.java b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
index ad773ee..834041f 100644
--- a/core/java/com/android/internal/view/menu/SubMenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/SubMenuBuilder.java
@@ -76,6 +76,12 @@
         return mParentMenu;
     }
 
+    @Override
+    boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
+        return super.dispatchMenuItemSelected(menu, item) ||
+                mParentMenu.dispatchMenuItemSelected(menu, item);
+    }
+
     public SubMenu setIcon(Drawable icon) {
         mItem.setIcon(icon);
         return this;
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/android_os_ParcelFileDescriptor.cpp b/core/jni/android_os_ParcelFileDescriptor.cpp
index 4ec131c..99a2d04 100644
--- a/core/jni/android_os_ParcelFileDescriptor.cpp
+++ b/core/jni/android_os_ParcelFileDescriptor.cpp
@@ -34,20 +34,37 @@
     jfieldID mFileDescriptor;
 } gParcelFileDescriptorOffsets;
 
-static int android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env,
+static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFd(JNIEnv* env,
+    jobject clazz, jint origfd)
+{
+    int fd = dup(origfd);
+    if (fd < 0) {
+        jniThrowException(env, "java/io/IOException", strerror(errno));
+        return NULL;
+    }
+    return jniCreateFileDescriptor(env, fd);
+}
+
+static jobject android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup(JNIEnv* env,
+    jobject clazz, jint fd)
+{
+    return jniCreateFileDescriptor(env, fd);
+}
+
+static void android_os_ParcelFileDescriptor_createPipeNative(JNIEnv* env,
     jobject clazz, jobjectArray outFds)
 {
     int fds[2];
     if (pipe(fds) < 0) {
-        return -errno;
+        int therr = errno;
+        jniThrowException(env, "java/io/IOException", strerror(therr));
+        return;
     }
 
     for (int i=0; i<2; i++) {
         jobject fdObj = jniCreateFileDescriptor(env, fds[i]);
         env->SetObjectArrayElement(outFds, i, fdObj);
     }
-
-    return 0;
 }
 
 static jint getFd(JNIEnv* env, jobject clazz)
@@ -102,7 +119,11 @@
 }
 
 static const JNINativeMethod gParcelFileDescriptorMethods[] = {
-    {"createPipeNative", "([Ljava/io/FileDescriptor;)I",
+    {"getFileDescriptorFromFd", "(I)Ljava/io/FileDescriptor;",
+        (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFd},
+    {"getFileDescriptorFromFdNoDup", "(I)Ljava/io/FileDescriptor;",
+        (void*)android_os_ParcelFileDescriptor_getFileDescriptorFromFdNoDup},
+    {"createPipeNative", "([Ljava/io/FileDescriptor;)V",
         (void*)android_os_ParcelFileDescriptor_createPipeNative},
     {"getStatSize", "()J",
         (void*)android_os_ParcelFileDescriptor_getStatSize},
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index dced1a5..59b97c2 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -351,7 +351,7 @@
 {
     DBusMessage *msg, *reply;
     DBusError err;
-    bool oob = TRUE;
+    dbus_bool_t oob = TRUE;
 
     if (!dbus_connection_register_object_path(nat->conn, agent_path,
             &agent_vtable, nat)) {
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/drawable-hdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png
deleted file mode 100644
index 61db22c..0000000
--- a/core/res/res/drawable-hdpi/keyboard_textfield_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png
deleted file mode 100644
index d6478fb..0000000
--- a/core/res/res/drawable-ldpi/keyboard_textfield_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_default.png b/core/res/res/drawable-mdpi/btn_code_lock_default.png
old mode 100755
new mode 100644
index f524317..45cc20d
--- a/core/res/res/drawable-mdpi/btn_code_lock_default.png
+++ b/core/res/res/drawable-mdpi/btn_code_lock_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-mdpi/btn_code_lock_touched.png
old mode 100755
new mode 100644
index 5cd436c..45cc20d
--- a/core/res/res/drawable-mdpi/btn_code_lock_touched.png
+++ b/core/res/res/drawable-mdpi/btn_code_lock_touched.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
index 20f3d50..0fbdbfa 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
index d09ce53..ae97453 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
index a9e008c..4127d1e 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
index 1ed3065..525ab8a 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
index 5710ebf..eb05820 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
index dd7d89e..416b2c7 100644
--- a/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
+++ b/core/res/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png
index d29c6c3..97ac023 100644
--- a/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_alarm.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_charging.png b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png
old mode 100755
new mode 100644
index 20d6320..4210db2
--- a/core/res/res/drawable-mdpi/ic_lock_idle_charging.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_charging.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_lock.png b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png
old mode 100755
new mode 100644
index 0206aee..1060f5a
--- a/core/res/res/drawable-mdpi/ic_lock_idle_lock.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_lock.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png
old mode 100755
new mode 100644
index bb96782..72e4afa
--- a/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png
+++ b/core/res/res/drawable-mdpi/ic_lock_idle_low_battery.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
index 7ddeba5..0bc86c3 100644
--- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_green_up.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
index 7201e58..2ab4547 100644
--- a/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_drag_direction_red_up.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
old mode 100755
new mode 100644
index 8546c5f..fe72d00
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_default.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
old mode 100755
new mode 100644
index a98a29a..be666c6
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_green.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
old mode 100755
new mode 100644
index 6d579cb..9627197
--- a/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
+++ b/core/res/res/drawable-mdpi/indicator_code_lock_point_area_red.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png b/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png
deleted file mode 100644
index 6e703af..0000000
--- a/core/res/res/drawable-mdpi/keyboard_textfield_selected.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_delete_holo.png b/core/res/res/drawable-mdpi/sym_keyboard_delete_holo.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/sym_keyboard_delete_holo.png
rename to core/res/res/drawable-mdpi/sym_keyboard_delete_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-mdpi/sym_keyboard_shift.png
index 0566e5a..91d6e32 100644
--- a/core/res/res/drawable-mdpi/sym_keyboard_shift.png
+++ b/core/res/res/drawable-mdpi/sym_keyboard_shift.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png
old mode 100755
new mode 100644
index ccaf05d..2bd0536
--- a/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png
+++ b/core/res/res/drawable-mdpi/sym_keyboard_shift_locked.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_activated_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_activated_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_default_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_default_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_default_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_disabled_focused_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_focused_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_disabled_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_disabled_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_disabled_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/textfield_bg_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/textfield_bg_focused_holo_dark.9.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/textfield_bg_focused_holo_dark.9.png
rename to core/res/res/drawable-mdpi/textfield_bg_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_default.png b/core/res/res/drawable-mdpi/unlock_default.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/unlock_default.png
rename to core/res/res/drawable-mdpi/unlock_default.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_halo.png b/core/res/res/drawable-mdpi/unlock_halo.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/unlock_halo.png
rename to core/res/res/drawable-mdpi/unlock_halo.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_ring.png b/core/res/res/drawable-mdpi/unlock_ring.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/unlock_ring.png
rename to core/res/res/drawable-mdpi/unlock_ring.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/unlock_wave.png b/core/res/res/drawable-mdpi/unlock_wave.png
similarity index 100%
rename from core/res/res/drawable-xlarge-mdpi/unlock_wave.png
rename to core/res/res/drawable-mdpi/unlock_wave.png
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png
deleted file mode 100644
index 45cc20d..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png b/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png
deleted file mode 100644
index 45cc20d..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_code_lock_touched.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png
deleted file mode 100644
index 0fbdbfa..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
deleted file mode 100644
index ae97453..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_off.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
deleted file mode 100644
index 4127d1e..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_normal_on.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
deleted file mode 100644
index 525ab8a..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
deleted file mode 100644
index eb05820..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_off.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png b/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
deleted file mode 100644
index 416b2c7..0000000
--- a/core/res/res/drawable-xlarge-mdpi/btn_keyboard_key_fulltrans_pressed_on.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png
deleted file mode 100644
index 97ac023..0000000
--- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_alarm.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png
deleted file mode 100644
index 4210db2..0000000
--- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_charging.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png
deleted file mode 100644
index 1060f5a..0000000
--- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_lock.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png b/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png
deleted file mode 100644
index 72e4afa..0000000
--- a/core/res/res/drawable-xlarge-mdpi/ic_lock_idle_low_battery.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png
deleted file mode 100644
index 0bc86c3..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_green_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png
deleted file mode 100644
index 2ab4547..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_drag_direction_red_up.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png
deleted file mode 100644
index fe72d00..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_default.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png
deleted file mode 100644
index be666c6..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_green.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png b/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png
deleted file mode 100644
index 9627197..0000000
--- a/core/res/res/drawable-xlarge-mdpi/indicator_code_lock_point_area_red.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png b/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png
deleted file mode 100644
index 91d6e32..0000000
--- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png b/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png
deleted file mode 100644
index 2bd0536..0000000
--- a/core/res/res/drawable-xlarge-mdpi/sym_keyboard_shift_locked.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable/extract_edit_text.xml b/core/res/res/drawable/extract_edit_text.xml
deleted file mode 100644
index c7f66f6..0000000
--- a/core/res/res/drawable/extract_edit_text.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_focused="true" android:drawable="@drawable/keyboard_textfield_selected" />
-    <item android:drawable="@drawable/textfield_disabled" />
-</selector>
-
diff --git a/core/res/res/layout-xlarge/keyguard.xml b/core/res/res/layout-sw600dp/keyguard.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard.xml
rename to core/res/res/layout-sw600dp/keyguard.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_glogin_unlock.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_glogin_unlock.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_lock.xml b/core/res/res/layout-sw600dp/keyguard_screen_lock.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_lock.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_lock.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_password_landscape.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_password_landscape.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_password_portrait.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_password_portrait.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_sim_pin_landscape.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_sim_pin_landscape.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_sim_pin_portrait.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_sim_pin_portrait.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_status_land.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_status_land.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_status_port.xml b/core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_status_port.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_status_port.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_tab_unlock.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
similarity index 96%
rename from core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
index 8acb656..e3d7a3f 100644
--- a/core/res/res/layout-xlarge/keyguard_screen_unlock_landscape.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_unlock_landscape.xml
@@ -47,13 +47,12 @@
     <RelativeLayout
         android:layout_weight="1"
         android:layout_width="0dip"
-        android:layout_height="match_parent">
+        android:layout_height="match_parent"
+        android:gravity="center_vertical|center_horizontal">
 
         <com.android.internal.widget.LockPatternView android:id="@+id/lockPattern"
             android:layout_width="354dip"
             android:layout_height="354dip"
-            android:layout_marginLeft="143dip"
-            android:layout_marginTop="201dip"
             android:layout_gravity="center_vertical"
         />
 
diff --git a/core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml b/core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
similarity index 100%
rename from core/res/res/layout-xlarge/keyguard_screen_unlock_portrait.xml
rename to core/res/res/layout-sw600dp/keyguard_screen_unlock_portrait.xml
diff --git a/core/res/res/layout/input_method_extract_view.xml b/core/res/res/layout/input_method_extract_view.xml
index 689ba7b..7d59d02 100644
--- a/core/res/res/layout/input_method_extract_view.xml
+++ b/core/res/res/layout/input_method_extract_view.xml
@@ -31,7 +31,6 @@
             android:gravity="top"
             android:minLines="1"
             android:inputType="text"
-            android:background="@android:drawable/extract_edit_text"
         >
     </android.inputmethodservice.ExtractEditText>
 
diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-sw600dp/config.xml
similarity index 99%
rename from core/res/res/values-xlarge/config.xml
rename to core/res/res/values-sw600dp/config.xml
index 4c8bbe6..49ace34 100644
--- a/core/res/res/values-xlarge/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -27,6 +27,7 @@
 
     <!-- Show sliding tab before lockscreen -->
     <bool name="config_enableSlidingTabFirst">false</bool>
+
     <!-- Enable lockscreen rotation -->
     <bool name="config_enableLockScreenRotation">true</bool>
 
diff --git a/core/res/res/values-xlarge/dimens.xml b/core/res/res/values-xlarge/dimens.xml
index e058442..b906e1a 100644
--- a/core/res/res/values-xlarge/dimens.xml
+++ b/core/res/res/values-xlarge/dimens.xml
@@ -25,10 +25,6 @@
     <!-- Size of the giant number (unread count) in the notifications -->
     <dimen name="status_bar_content_number_size">48sp</dimen>
 
-    <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. -->
-    <!-- Margin for permanent screen decorations at the bottom. -->
-    <dimen name="screen_margin_bottom">48dip</dimen>
-
     <!-- Default height of a key in the password keyboard for alpha -->
     <dimen name="password_keyboard_key_height_alpha">75dip</dimen>
     <!-- Default height of a key in the password keyboard for numeric -->
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/colors.xml b/core/res/res/values/colors.xml
index 39d2329..6529fe1 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -77,6 +77,7 @@
     <drawable name="dialog_holo_light_frame">@drawable/dialog_full_holo_light</drawable>
     
     <drawable name="input_method_fullscreen_background">#fff9f9f9</drawable>
+    <drawable name="input_method_fullscreen_background_holo">@drawable/screen_background_holo_dark</drawable>
 
     <!-- For date picker widget -->
     <drawable name="selected_day_background">#ff0092f4</drawable>
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 158d524..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>
@@ -2449,6 +2452,20 @@
     <!-- See SMS_DIALOG.  This is a button choice to disallow sending the SMSes.. -->
     <string name="sms_control_no">Cancel</string>
 
+    <!-- SIM swap and device reboot Dialog --> <skip />
+    <!-- See SIM_REMOVED_DIALOG.  This is the title of that dialog. -->
+    <string name="sim_removed_title">SIM card removed</string>
+    <!-- See SIM_REMOVED_DIALOG.  This is the message of that dialog. -->
+    <string name="sim_removed_message">The mobile network will be unavailable until you replace the SIM card.</string>
+    <!-- See SIM_REMOVED_DIALOG.  This is the button of that dialog. -->
+    <string name="sim_done_button">Done</string>
+    <!-- See SIM_ADDED_DIALOG.  This is the title of that dialog. -->
+    <string name="sim_added_title">SIM card added</string>
+    <!-- See SIM_ADDED_DIALOG.  This is the message of that dialog. -->
+    <string name="sim_added_message">You must restart your device to access the mobile network.</string>
+    <!-- See SIM_ADDED_DIALOG.  This is the button of that dialog. -->
+    <string name="sim_restart_button">Restart</string>
+
     <!-- Date/Time picker dialogs strings -->
 
     <!-- The title of the time picker dialog. [CHAR LIMIT=NONE] -->
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/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index aa9ddff..0a614b2 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -681,7 +681,7 @@
          and a few custom attributes. -->
     <style name="Theme.Holo.InputMethod" parent="Theme.Holo.Panel">
         <item name="android:windowAnimationStyle">@android:style/Animation.InputMethod</item>
-        <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background</item>
+        <item name="android:imeFullscreenBackground">@android:drawable/input_method_fullscreen_background_holo</item>
         <item name="android:imeExtractEnterAnimation">@android:anim/input_method_extract_enter</item>
         <item name="android:imeExtractExitAnimation">@android:anim/input_method_extract_exit</item>
     </style>
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
new file mode 100644
index 0000000..45719c2
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.net;
+
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+@SmallTest
+public class NetworkStatsTest extends TestCase {
+
+    private static final String TEST_IFACE = "test0";
+
+    public void testFindIndex() throws Exception {
+        final NetworkStats stats = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+                .addEntry(TEST_IFACE, 100, 1024, 0)
+                .addEntry(TEST_IFACE, 101, 0, 1024)
+                .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+
+        assertEquals(2, stats.findIndex(TEST_IFACE, 102));
+        assertEquals(2, stats.findIndex(TEST_IFACE, 102));
+        assertEquals(0, stats.findIndex(TEST_IFACE, 100));
+        assertEquals(-1, stats.findIndex(TEST_IFACE, 6));
+    }
+
+    public void testSubtractIdenticalData() throws Exception {
+        final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+                .addEntry(TEST_IFACE, 100, 1024, 0)
+                .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+        final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+                .addEntry(TEST_IFACE, 100, 1024, 0)
+                .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+        final NetworkStats result = after.subtract(before);
+
+        assertEquals(0, result.rx[0]);
+        assertEquals(0, result.tx[0]);
+        assertEquals(0, result.rx[1]);
+        assertEquals(0, result.tx[1]);
+    }
+
+    public void testSubtractIdenticalRows() throws Exception {
+        final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+                .addEntry(TEST_IFACE, 100, 1024, 0)
+                .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+        final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+                .addEntry(TEST_IFACE, 100, 1025, 2)
+                .addEntry(TEST_IFACE, 101, 3, 1028).build();
+
+        final NetworkStats result = after.subtract(before);
+
+        // expect delta between measurements
+        assertEquals(1, result.rx[0]);
+        assertEquals(2, result.tx[0]);
+        assertEquals(3, result.rx[1]);
+        assertEquals(4, result.tx[1]);
+    }
+
+    public void testSubtractNewRows() throws Exception {
+        final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+                .addEntry(TEST_IFACE, 100, 1024, 0)
+                .addEntry(TEST_IFACE, 101, 0, 1024).build();
+
+        final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+                .addEntry(TEST_IFACE, 100, 1024, 0)
+                .addEntry(TEST_IFACE, 101, 0, 1024)
+                .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+
+        final NetworkStats result = after.subtract(before);
+
+        // its okay to have new rows
+        assertEquals(0, result.rx[0]);
+        assertEquals(0, result.tx[0]);
+        assertEquals(0, result.rx[1]);
+        assertEquals(0, result.tx[1]);
+        assertEquals(1024, result.rx[2]);
+        assertEquals(1024, result.tx[2]);
+    }
+
+}
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/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp
index 9f55a71..dd0052a 100644
--- a/libs/utils/RefBase.cpp
+++ b/libs/utils/RefBase.cpp
@@ -419,7 +419,8 @@
     
     if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
         if (impl->mStrong == INITIAL_STRONG_VALUE)
-            delete impl->mBase;
+            if (impl->mBase)
+                impl->mBase->destroy();
         else {
             // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
             delete impl;
@@ -427,7 +428,8 @@
     } else {
         impl->mBase->onLastWeakRef(id);
         if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
-            delete impl->mBase;
+            if (impl->mBase)
+                impl->mBase->destroy();
         }
     }
 }
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/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 84f588e..0e161a8 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -628,9 +628,11 @@
      * and cannot be directly compared between different media sources or different
      * instances of the same media source, or across multiple runs of the same
      * program.
-     * @hide
      */
     public void setTexture(SurfaceTexture st) {
+        if (mScreenOnWhilePlaying && st != null && mSurfaceTexture == null) {
+            Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for SurfaceTexture");
+        }
         mSurfaceHolder = null;
         mSurface = null;
         mSurfaceTexture = st;
@@ -960,6 +962,9 @@
      */
     public void setScreenOnWhilePlaying(boolean screenOn) {
         if (mScreenOnWhilePlaying != screenOn) {
+            if (screenOn && mSurfaceTexture != null) {
+                Log.w(TAG, "setScreenOnWhilePlaying(true) is ineffective for SurfaceTexture");
+            }
             mScreenOnWhilePlaying = screenOn;
             updateSurfaceScreenOn();
         }
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/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp
index 3e4e493..805bd48 100644
--- a/media/libstagefright/chromium_http/support.cpp
+++ b/media/libstagefright/chromium_http/support.cpp
@@ -23,7 +23,7 @@
 #include "support.h"
 
 #include "android/net/android_network_library_impl.h"
-#include "base/thread.h"
+#include "base/threading/thread.h"
 #include "net/base/cert_verifier.h"
 #include "net/base/host_resolver.h"
 #include "net/base/ssl_config_service.h"
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/opengl/libs/Android.mk b/opengl/libs/Android.mk
index afefee6..ff45edc 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -14,7 +14,6 @@
 	EGL/eglApi.cpp 	       \
 	EGL/trace.cpp              \
 	EGL/getProcAddress.cpp.arm \
-	EGL/hooks.cpp 	       \
 	EGL/Loader.cpp 	       \
 #
 
diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp
index da26229..e94e50e 100644
--- a/opengl/libs/EGL/Loader.cpp
+++ b/opengl/libs/EGL/Loader.cpp
@@ -26,11 +26,10 @@
 
 #include <EGL/egl.h>
 
-#include "hooks.h"
-#include "egl_impl.h"
-
-#include "Loader.h"
+#include "egldefs.h"
 #include "glesv2dbg.h"
+#include "hooks.h"
+#include "Loader.h"
 
 // ----------------------------------------------------------------------------
 namespace android {
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index 31fe306..b11db32 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -33,6 +33,7 @@
 
 #include <utils/String8.h>
 
+#include "egldefs.h"
 #include "egl_impl.h"
 #include "egl_tls.h"
 #include "glesv2dbg.h"
@@ -278,6 +279,71 @@
     return res;
 }
 
+void gl_unimplemented() {
+    LOGE("called unimplemented OpenGL ES API");
+}
+
+// ----------------------------------------------------------------------------
+
+#if USE_FAST_TLS_KEY
+
+// We have a dedicated TLS slot in bionic
+static inline gl_hooks_t const * volatile * get_tls_hooks() {
+    volatile void *tls_base = __get_tls();
+    gl_hooks_t const * volatile * tls_hooks =
+            reinterpret_cast<gl_hooks_t const * volatile *>(tls_base);
+    return tls_hooks;
+}
+
+void setGlThreadSpecific(gl_hooks_t const *value) {
+    gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
+    tls_hooks[TLS_SLOT_OPENGL_API] = value;
+}
+
+gl_hooks_t const* getGlThreadSpecific() {
+    gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
+    gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API];
+    if (hooks) return hooks;
+    return &gHooksNoContext;
+}
+
+#else
+
+void setGlThreadSpecific(gl_hooks_t const *value) {
+    pthread_setspecific(gGLWrapperKey, value);
+}
+
+gl_hooks_t const* getGlThreadSpecific() {
+    gl_hooks_t const* hooks =  static_cast<gl_hooks_t*>(pthread_getspecific(gGLWrapperKey));
+    if (hooks) return hooks;
+    return &gHooksNoContext;
+}
+
+#endif
+
+// ----------------------------------------------------------------------------
+// GL / EGL hooks
+// ----------------------------------------------------------------------------
+
+#undef GL_ENTRY
+#undef EGL_ENTRY
+#define GL_ENTRY(_r, _api, ...) #_api,
+#define EGL_ENTRY(_r, _api, ...) #_api,
+
+char const * const gl_names[] = {
+    #include "entries.in"
+    NULL
+};
+
+char const * const egl_names[] = {
+    #include "egl_entries.in"
+    NULL
+};
+
+#undef GL_ENTRY
+#undef EGL_ENTRY
+
+
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h
index 8c482c3..113595f 100644
--- a/opengl/libs/EGL/egl_display.h
+++ b/opengl/libs/EGL/egl_display.h
@@ -30,6 +30,7 @@
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
+#include "egldefs.h"
 #include "hooks.h"
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/EGL/egl_tls.h b/opengl/libs/EGL/egl_tls.h
index 8b31468..a7989ef 100644
--- a/opengl/libs/EGL/egl_tls.h
+++ b/opengl/libs/EGL/egl_tls.h
@@ -21,7 +21,12 @@
 
 #include <EGL/egl.h>
 
+#include "egldefs.h"
+#include "hooks.h"
+
+// ----------------------------------------------------------------------------
 namespace android {
+// ----------------------------------------------------------------------------
 
 class DbgContext;
 
@@ -58,6 +63,16 @@
 
 #define setError(_e, _r) egl_tls_t::setErrorEtc(__FUNCTION__, __LINE__, _e, _r)
 
+// ----------------------------------------------------------------------------
+
+#if EGL_TRACE
+
+extern gl_hooks_t const* getGLTraceThreadSpecific();
+
+#endif
+
+// ----------------------------------------------------------------------------
 }; // namespace android
+// ----------------------------------------------------------------------------
 
 #endif // ANDROID_EGL_TLS_H
diff --git a/opengl/libs/EGL/egldefs.h b/opengl/libs/EGL/egldefs.h
new file mode 100644
index 0000000..107acd9
--- /dev/null
+++ b/opengl/libs/EGL/egldefs.h
@@ -0,0 +1,71 @@
+/*
+ ** Copyright 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.
+ */
+
+#ifndef ANDROID_EGLDEFS_H
+#define ANDROID_EGLDEFS_H
+
+#include "hooks.h"
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+#define VERSION_MAJOR 1
+#define VERSION_MINOR 4
+
+//  EGLDisplay are global, not attached to a given thread
+const unsigned int NUM_DISPLAYS = 1;
+
+enum {
+    IMPL_HARDWARE = 0,
+    IMPL_SOFTWARE,
+    IMPL_NUM_IMPLEMENTATIONS
+};
+
+enum {
+    GLESv1_INDEX = 0,
+    GLESv2_INDEX = 1,
+};
+
+// ----------------------------------------------------------------------------
+
+struct egl_connection_t
+{
+    inline egl_connection_t() : dso(0) { }
+    void *              dso;
+    gl_hooks_t *        hooks[2];
+    EGLint              major;
+    EGLint              minor;
+    egl_t               egl;
+};
+
+// ----------------------------------------------------------------------------
+
+extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS];
+extern gl_hooks_t gHooksNoContext;
+extern pthread_key_t gGLWrapperKey;
+extern "C" void gl_unimplemented();
+
+extern char const * const gl_names[];
+extern char const * const egl_names[];
+
+extern egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
+
+#endif /* ANDROID_EGLDEFS_H */
diff --git a/opengl/libs/EGL/getProcAddress.cpp b/opengl/libs/EGL/getProcAddress.cpp
index dcf8735c..f89c865 100644
--- a/opengl/libs/EGL/getProcAddress.cpp
+++ b/opengl/libs/EGL/getProcAddress.cpp
@@ -20,6 +20,7 @@
 
 #include <cutils/log.h>
 
+#include "egldefs.h"
 #include "hooks.h"
 
 // ----------------------------------------------------------------------------
@@ -34,7 +35,7 @@
 #undef GL_EXTENSION_LIST
 #undef GET_TLS
 
-#if defined(__arm__)
+#if USE_FAST_TLS_KEY
 
     #ifdef HAVE_ARM_TLS_REGISTER
         #define GET_TLS(reg) \
@@ -77,7 +78,7 @@
 
     #define GL_EXTENSION(_n)
 
-    #warning "eglGetProcAddress() partially supported on this architecture"
+    #warning "eglGetProcAddress() partially supported"
 
 #endif
 
diff --git a/opengl/libs/EGL/hooks.cpp b/opengl/libs/EGL/hooks.cpp
deleted file mode 100644
index 72ad6b3..0000000
--- a/opengl/libs/EGL/hooks.cpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/* 
- ** Copyright 2009, The Android Open Source Project
- **
- ** Licensed under the Apache License, Version 2.0 (the "License"); 
- ** you may not use this file except in compliance with the License. 
- ** You may obtain a copy of the License at 
- **
- **     http://www.apache.org/licenses/LICENSE-2.0 
- **
- ** Unless required by applicable law or agreed to in writing, software 
- ** distributed under the License is distributed on an "AS IS" BASIS, 
- ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
- ** See the License for the specific language governing permissions and 
- ** limitations under the License.
- */
-
-#include <ctype.h>
-#include <stdlib.h>
-#include <errno.h>
-
-#include <cutils/log.h>
-
-#include "hooks.h"
-
-// ----------------------------------------------------------------------------
-namespace android {
-// ----------------------------------------------------------------------------
-
-void gl_unimplemented() {
-    LOGE("called unimplemented OpenGL ES API");
-}
-
-
-// ----------------------------------------------------------------------------
-// GL / EGL hooks
-// ----------------------------------------------------------------------------
-
-#undef GL_ENTRY
-#undef EGL_ENTRY
-#define GL_ENTRY(_r, _api, ...) #_api,
-#define EGL_ENTRY(_r, _api, ...) #_api,
-
-char const * const gl_names[] = {
-    #include "entries.in"
-    NULL
-};
-
-char const * const egl_names[] = {
-    #include "egl_entries.in"
-    NULL
-};
-
-#undef GL_ENTRY
-#undef EGL_ENTRY
-
-
-// ----------------------------------------------------------------------------
-}; // namespace android
-// ----------------------------------------------------------------------------
-
diff --git a/opengl/libs/EGL/trace.cpp b/opengl/libs/EGL/trace.cpp
index f3e101b..0e934e2 100644
--- a/opengl/libs/EGL/trace.cpp
+++ b/opengl/libs/EGL/trace.cpp
@@ -26,6 +26,7 @@
 
 #include <cutils/log.h>
 
+#include "egl_tls.h"
 #include "hooks.h"
 
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/egl_impl.h b/opengl/libs/egl_impl.h
index d24b047..a809316 100644
--- a/opengl/libs/egl_impl.h
+++ b/opengl/libs/egl_impl.h
@@ -25,27 +25,12 @@
 
 #include "hooks.h"
 
-#define VERSION_MAJOR 1
-#define VERSION_MINOR 4
-
 // ----------------------------------------------------------------------------
 namespace android {
 // ----------------------------------------------------------------------------
 
-struct egl_connection_t
-{
-    inline egl_connection_t() : dso(0) { }
-    void *              dso;
-    gl_hooks_t *        hooks[2];
-    EGLint              major;
-    EGLint              minor;
-    egl_t               egl;
-};
-
 EGLAPI EGLImageKHR egl_get_image_for_current_context(EGLImageKHR image);
 
-extern egl_connection_t gEGLImpl[IMPL_NUM_IMPLEMENTATIONS];
-
 // ----------------------------------------------------------------------------
 }; // namespace android
 // ----------------------------------------------------------------------------
diff --git a/opengl/libs/hooks.h b/opengl/libs/hooks.h
index 812e26d..7ac88cd 100644
--- a/opengl/libs/hooks.h
+++ b/opengl/libs/hooks.h
@@ -54,22 +54,6 @@
 namespace android {
 // ----------------------------------------------------------------------------
 
-//  EGLDisplay are global, not attached to a given thread
-const unsigned int NUM_DISPLAYS = 1;
-
-enum {
-    IMPL_HARDWARE = 0,
-    IMPL_SOFTWARE,
-    IMPL_NUM_IMPLEMENTATIONS
-};
-
-enum {
-    GLESv1_INDEX = 0,
-    GLESv2_INDEX = 1,
-};
-
-// ----------------------------------------------------------------------------
-
 // GL / EGL hooks
 
 #undef GL_ENTRY
@@ -92,60 +76,8 @@
 #undef GL_ENTRY
 #undef EGL_ENTRY
 
-
-// ----------------------------------------------------------------------------
-
-extern gl_hooks_t gHooks[2][IMPL_NUM_IMPLEMENTATIONS];
-extern gl_hooks_t gHooksNoContext;
-extern pthread_key_t gGLWrapperKey;
-extern "C" void gl_unimplemented();
-
-extern char const * const gl_names[];
-extern char const * const egl_names[];
-
-// ----------------------------------------------------------------------------
-
-#if USE_FAST_TLS_KEY
-
-// We have a dedicated TLS slot in bionic
-static inline gl_hooks_t const * volatile * get_tls_hooks() {
-    volatile void *tls_base = __get_tls();
-    gl_hooks_t const * volatile * tls_hooks = 
-            reinterpret_cast<gl_hooks_t const * volatile *>(tls_base);
-    return tls_hooks;
-}
-
-static inline void setGlThreadSpecific(gl_hooks_t const *value) {
-    gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
-    tls_hooks[TLS_SLOT_OPENGL_API] = value;
-}
-
-static gl_hooks_t const* getGlThreadSpecific() {
-    gl_hooks_t const * volatile * tls_hooks = get_tls_hooks();
-    gl_hooks_t const* hooks = tls_hooks[TLS_SLOT_OPENGL_API];
-    if (hooks) return hooks;
-    return &gHooksNoContext;
-}
-
-#else
-
-static inline void setGlThreadSpecific(gl_hooks_t const *value) {
-    pthread_setspecific(gGLWrapperKey, value);
-}
-
-static gl_hooks_t const* getGlThreadSpecific() {
-    gl_hooks_t const* hooks =  static_cast<gl_hooks_t*>(pthread_getspecific(gGLWrapperKey));
-    if (hooks) return hooks;
-    return &gHooksNoContext;
-}
-
-#endif
-
-#if EGL_TRACE
-
-extern gl_hooks_t const* getGLTraceThreadSpecific();
-
-#endif
+EGLAPI void setGlThreadSpecific(gl_hooks_t const *value);
+EGLAPI gl_hooks_t const* getGlThreadSpecific();
 
 // ----------------------------------------------------------------------------
 }; // namespace android
diff --git a/packages/SystemUI/res/layout-large/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_input_methods_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_input_methods_item.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_input_methods_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_input_methods_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_panel.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_area.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_area.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_panel_title.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_panel_title.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_peek.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_peek.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_peek.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_peek.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_notification_row.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_notification_row.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_pocket_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_pocket_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_pocket_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_pocket_panel.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_recent_item.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_recent_item.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_recent_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_recent_panel_footer.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel_footer.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_recent_panel_footer.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_recent_panel_footer.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_settings_view.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_settings_view.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_settings_view.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_settings_view.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_ticker_compat.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_compat.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_ticker_compat.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_ticker_compat.xml
diff --git a/packages/SystemUI/res/layout-large/status_bar_ticker_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml
similarity index 100%
rename from packages/SystemUI/res/layout-large/status_bar_ticker_panel.xml
rename to packages/SystemUI/res/layout-sw600dp/status_bar_ticker_panel.xml
diff --git a/packages/SystemUI/res/values-xlarge/styles.xml b/packages/SystemUI/res/values-sw600dp/styles.xml
similarity index 100%
rename from packages/SystemUI/res/values-xlarge/styles.xml
rename to packages/SystemUI/res/values-sw600dp/styles.xml
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a6ba1a0..8e86eda 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -87,40 +87,20 @@
 static const nsecs_t kWarningThrottle = seconds(5);
 
 
-#define AUDIOFLINGER_SECURITY_ENABLED 1
-
 // ----------------------------------------------------------------------------
 
 static bool recordingAllowed() {
-#ifndef HAVE_ANDROID_OS
-    return true;
-#endif
-#if AUDIOFLINGER_SECURITY_ENABLED
     if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
     bool ok = checkCallingPermission(String16("android.permission.RECORD_AUDIO"));
     if (!ok) LOGE("Request requires android.permission.RECORD_AUDIO");
     return ok;
-#else
-    if (!checkCallingPermission(String16("android.permission.RECORD_AUDIO")))
-        LOGW("WARNING: Need to add android.permission.RECORD_AUDIO to manifest");
-    return true;
-#endif
 }
 
 static bool settingsAllowed() {
-#ifndef HAVE_ANDROID_OS
-    return true;
-#endif
-#if AUDIOFLINGER_SECURITY_ENABLED
     if (getpid() == IPCThreadState::self()->getCallingPid()) return true;
     bool ok = checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS"));
     if (!ok) LOGE("Request requires android.permission.MODIFY_AUDIO_SETTINGS");
     return ok;
-#else
-    if (!checkCallingPermission(String16("android.permission.MODIFY_AUDIO_SETTINGS")))
-        LOGW("WARNING: Need to add android.permission.MODIFY_AUDIO_SETTINGS to manifest");
-    return true;
-#endif
 }
 
 // To collect the amplifier usage
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/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index ca2adb4..d2fd04b 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -543,11 +543,7 @@
      * active
      */
     public NetworkInfo getActiveNetworkInfo() {
-        enforceAccessPermission();
-        if (mActiveDefaultNetwork != -1) {
-            return mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
-        }
-        return null;
+        return getNetworkInfo(mActiveDefaultNetwork);
     }
 
     public NetworkInfo getNetworkInfo(int networkType) {
@@ -579,18 +575,7 @@
      * none is active
      */
     public LinkProperties getActiveLinkProperties() {
-        enforceAccessPermission();
-        for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) {
-            if (mNetConfigs[type] == null || !mNetConfigs[type].isDefault()) {
-                continue;
-            }
-            NetworkStateTracker t = mNetTrackers[type];
-            NetworkInfo info = t.getNetworkInfo();
-            if (info.isConnected()) {
-                return t.getLinkProperties();
-            }
-        }
-        return null;
+        return getLinkProperties(mActiveDefaultNetwork);
     }
 
     public LinkProperties getLinkProperties(int networkType) {
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/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 7c613c1..8f179f5 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -16,51 +16,34 @@
 
 package com.android.server;
 
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.res.Resources;
 import android.content.pm.PackageManager;
-import android.net.NetworkStats;
-import android.net.Uri;
-import android.net.InterfaceConfiguration;
 import android.net.INetworkManagementEventObserver;
+import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
+import android.net.NetworkStats;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.os.Binder;
 import android.os.INetworkManagementService;
-import android.os.Handler;
-import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
-import java.util.StringTokenizer;
-import android.provider.Settings;
-import android.content.ContentResolver;
-import android.database.ContentObserver;
 
 import java.io.BufferedReader;
 import java.io.DataInputStream;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.RandomAccessFile;
-import java.io.Reader;
-import java.lang.IllegalStateException;
-import java.net.InetAddress;
 import java.net.Inet4Address;
-import java.net.UnknownHostException;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
 import java.util.concurrent.CountDownLatch;
 
 import libcore.io.IoUtils;
@@ -69,14 +52,16 @@
  * @hide
  */
 class NetworkManagementService extends INetworkManagementService.Stub {
-
-    private static final String TAG = "NetworkManagmentService";
+    private static final String TAG = "NetworkManagementService";
     private static final boolean DBG = false;
     private static final String NETD_TAG = "NetdConnector";
 
     private static final int ADD = 1;
     private static final int REMOVE = 2;
 
+    /** Base path to UID-granularity network statistics. */
+    private static final File PATH_PROC_UID_STAT = new File("/proc/uid_stat");
+
     class NetdResponseCode {
         public static final int InterfaceListResult       = 110;
         public static final int TetherInterfaceListResult = 111;
@@ -891,7 +876,7 @@
         return -1;
     }
 
-    /** {@inheritDoc} */
+    @Override
     public NetworkStats getNetworkStatsSummary() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
@@ -909,31 +894,46 @@
         return stats.build();
     }
 
-    /** {@inheritDoc} */
+    @Override
     public NetworkStats getNetworkStatsDetail() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
 
-        final File procPath = new File("/proc/uid_stat");
-        final String[] knownUids = procPath.list();
+        final String[] knownUids = PATH_PROC_UID_STAT.list();
         final NetworkStats.Builder stats = new NetworkStats.Builder(
                 SystemClock.elapsedRealtime(), knownUids.length);
 
-        // TODO: kernel module will provide interface-level stats in future
-        // TODO: migrate these stats to come across netd in bulk, instead of all
-        // these individual file reads.
         for (String uid : knownUids) {
-            final File uidPath = new File(procPath, uid);
-            final int rx = readSingleIntFromFile(new File(uidPath, "tcp_rcv"));
-            final int tx = readSingleIntFromFile(new File(uidPath, "tcp_snd"));
-
             final int uidInt = Integer.parseInt(uid);
-            stats.addEntry(NetworkStats.IFACE_ALL, uidInt, rx, tx);
+            collectNetworkStatsDetail(stats, uidInt);
         }
 
         return stats.build();
     }
 
+    @Override
+    public NetworkStats getNetworkStatsUidDetail(int uid) {
+        if (Binder.getCallingUid() != uid) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+        }
+
+        final NetworkStats.Builder stats = new NetworkStats.Builder(
+                SystemClock.elapsedRealtime(), 1);
+        collectNetworkStatsDetail(stats, uid);
+        return stats.build();
+    }
+
+    private void collectNetworkStatsDetail(NetworkStats.Builder stats, int uid) {
+        // TODO: kernel module will provide interface-level stats in future
+        // TODO: migrate these stats to come across netd in bulk, instead of all
+        // these individual file reads.
+        final File uidPath = new File(PATH_PROC_UID_STAT, Integer.toString(uid));
+        final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
+        final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
+        stats.addEntry(NetworkStats.IFACE_ALL, uid, rx, tx);
+    }
+
     public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
@@ -994,22 +994,17 @@
     }
 
     /**
-     * Utility method to read a single plain-text {@link Integer} from the given
+     * Utility method to read a single plain-text {@link Long} from the given
      * {@link File}, usually from a {@code /proc/} filesystem.
      */
-    private static int readSingleIntFromFile(File file) {
-        RandomAccessFile f = null;
+    private static long readSingleLongFromFile(File file) {
         try {
-            f = new RandomAccessFile(file, "r");
-            byte[] buffer = new byte[(int) f.length()];
-            f.readFully(buffer);
-            return Integer.parseInt(new String(buffer).trim());
+            final byte[] buffer = IoUtils.readFileAsByteArray(file.toString());
+            return Long.parseLong(new String(buffer).trim());
         } catch (NumberFormatException e) {
             return -1;
         } catch (IOException e) {
             return -1;
-        } finally {
-            IoUtils.closeQuietly(f);
         }
     }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cd8915d..5355d44 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -18,6 +18,7 @@
 
 import com.android.server.accessibility.AccessibilityManagerService;
 import com.android.server.am.ActivityManagerService;
+import com.android.server.net.NetworkPolicyManagerService;
 import com.android.server.pm.PackageManagerService;
 import com.android.server.usb.UsbService;
 import com.android.server.wm.WindowManagerService;
@@ -119,6 +120,7 @@
         LightsService lights = null;
         PowerManagerService power = null;
         BatteryService battery = null;
+        NetworkPolicyManagerService networkPolicy = null;
         ConnectivityService connectivity = null;
         IPackageManager pm = null;
         Context context = null;
@@ -282,6 +284,15 @@
             }
 
             try {
+                Slog.i(TAG, "NetworkPolicy Service");
+                networkPolicy = new NetworkPolicyManagerService(
+                        context, ActivityManagerService.self(), power);
+                ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
+            } catch (Throwable e) {
+                Slog.e(TAG, "Failure starting Connectivity Service", e);
+            }
+
+            try {
                 Slog.i(TAG, "NetworkManagement Service");
                 ServiceManager.addService(
                         Context.NETWORKMANAGEMENT_SERVICE,
@@ -528,6 +539,7 @@
         // These are needed to propagate to the runnable below.
         final Context contextF = context;
         final BatteryService batteryF = battery;
+        final NetworkPolicyManagerService networkPolicyF = networkPolicy;
         final ConnectivityService connectivityF = connectivity;
         final DockObserver dockF = dock;
         final UsbService usbF = usb;
@@ -553,6 +565,7 @@
 
                 startSystemUi(contextF);
                 if (batteryF != null) batteryF.systemReady();
+                if (networkPolicyF != null) networkPolicyF.systemReady();
                 if (connectivityF != null) connectivityF.systemReady();
                 if (dockF != null) dockF.systemReady();
                 if (usbF != null) usbF.systemReady();
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 5aae539..cf5592c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -43,6 +43,7 @@
 import android.app.IApplicationThread;
 import android.app.IInstrumentationWatcher;
 import android.app.INotificationManager;
+import android.app.IProcessObserver;
 import android.app.IServiceConnection;
 import android.app.IThumbnailReceiver;
 import android.app.IThumbnailRetriever;
@@ -752,8 +753,6 @@
      */
     final UsageStatsService mUsageStatsService;
 
-    final NetworkPolicyManagerService mNetworkPolicyService;
-
     /**
      * Current configuration information.  HistoryRecord objects are given
      * a reference to this object to indicate which configuration they are
@@ -854,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
@@ -885,7 +876,10 @@
 
     final RemoteCallbackList<IActivityWatcher> mWatchers
             = new RemoteCallbackList<IActivityWatcher>();
-    
+
+    final RemoteCallbackList<IProcessObserver> mProcessObservers
+            = new RemoteCallbackList<IProcessObserver>();
+
     /**
      * Callback of last caller to {@link #requestPss}.
      */
@@ -1277,16 +1271,15 @@
                 }
             } break;
             case DISPATCH_FOREGROUND_ACTIVITIES_CHANGED: {
-                // Flag might have changed during dispatch, but it's always
-                // consistent since we dispatch for every change.
                 final ProcessRecord app = (ProcessRecord) msg.obj;
-                mNetworkPolicyService.onForegroundActivitiesChanged(
-                        app.info.uid, app.pid, app.foregroundActivities);
+                final boolean foregroundActivities = msg.arg1 != 0;
+                dispatchForegroundActivitiesChanged(
+                        app.pid, app.info.uid, foregroundActivities);
                 break;
             }
             case DISPATCH_PROCESS_DIED: {
                 final ProcessRecord app = (ProcessRecord) msg.obj;
-                mNetworkPolicyService.onProcessDied(app.info.uid, app.pid);
+                dispatchProcessDied(app.pid, app.info.uid);
                 break;
             }
             }
@@ -1358,7 +1351,6 @@
         
         m.mBatteryStatsService.publish(context);
         m.mUsageStatsService.publish(context);
-        m.mNetworkPolicyService.publish(context);
         
         synchronized (thr) {
             thr.mReady = true;
@@ -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();
@@ -1480,8 +1463,6 @@
         mUsageStatsService = new UsageStatsService(new File(
                 systemDir, "usagestats").toString());
 
-        mNetworkPolicyService = new NetworkPolicyManagerService();
-
         GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
             ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
 
@@ -1937,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()) {
@@ -2152,6 +2132,36 @@
         mWatchers.finishBroadcast();
     }
 
+    private void dispatchForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+        int i = mProcessObservers.beginBroadcast();
+        while (i > 0) {
+            i--;
+            final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+            if (observer != null) {
+                try {
+                    observer.onForegroundActivitiesChanged(pid, uid, foregroundActivities);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+        mProcessObservers.finishBroadcast();
+    }
+
+    private void dispatchProcessDied(int pid, int uid) {
+        int i = mProcessObservers.beginBroadcast();
+        while (i > 0) {
+            i--;
+            final IProcessObserver observer = mProcessObservers.getBroadcastItem(i);
+            if (observer != null) {
+                try {
+                    observer.onProcessDied(pid, uid);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+        mProcessObservers.finishBroadcast();
+    }
+
     final void doPendingActivityLaunchesLocked(boolean doResume) {
         final int N = mPendingActivityLaunches.size();
         if (N <= 0) {
@@ -6084,7 +6094,6 @@
         
         mUsageStatsService.shutdown();
         mBatteryStatsService.shutdown();
-        mNetworkPolicyService.shutdown();
         
         return timedout;
     }
@@ -6241,6 +6250,14 @@
         }
     }
 
+    public void registerProcessObserver(IProcessObserver observer) {
+        mProcessObservers.register(observer);
+    }
+
+    public void unregisterProcessObserver(IProcessObserver observer) {
+        mProcessObservers.unregister(observer);
+    }
+
     public void setImmersive(IBinder token, boolean immersive) {
         synchronized(this) {
             int index = (token != null) ? mMainStack.indexOfTokenLocked(token) : -1;
@@ -12755,7 +12772,8 @@
         app.curSchedGroup = schedGroup;
 
         if (hadForegroundActivities != app.foregroundActivities) {
-            mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED, app).sendToTarget();
+            mHandler.obtainMessage(DISPATCH_FOREGROUND_ACTIVITIES_CHANGED,
+                    app.foregroundActivities ? 1 : 0, 0, app).sendToTarget();
         }
 
         return adj;
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/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index a7a4f07..d083d01 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -19,13 +19,20 @@
 import static android.Manifest.permission.MANAGE_APP_TOKENS;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
-import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID;
 
+import android.app.IActivityManager;
+import android.app.IProcessObserver;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.INetworkPolicyManager;
-import android.os.ServiceManager;
+import android.os.IPowerManager;
+import android.os.RemoteException;
 import android.util.Log;
+import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
@@ -38,92 +45,139 @@
     private static final String TAG = "NetworkPolicy";
     private static final boolean LOGD = true;
 
-    private static final String SERVICE_NAME = "netpolicy";
-
     private Context mContext;
+    private IActivityManager mActivityManager;
+    private IPowerManager mPowerManager;
+
+    private Object mRulesLock = new Object();
+
+    private boolean mScreenOn = false;
 
     /** Current network policy for each UID. */
-    private SparseIntArray mUidPolicy;
+    private SparseIntArray mUidPolicy = new SparseIntArray();
 
     /** Foreground at both UID and PID granularity. */
-    private SparseBooleanArray mUidForeground;
-    private SparseArray<SparseBooleanArray> mUidPidForeground;
+    private SparseBooleanArray mUidForeground = new SparseBooleanArray();
+    private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
+            SparseBooleanArray>();
 
     // TODO: periodically poll network stats and write to disk
     // TODO: save/restore policy information from disk
 
-    // TODO: watch screen on/off broadcasts to track foreground
+    public NetworkPolicyManagerService(
+            Context context, IActivityManager activityManager, IPowerManager powerManager) {
+        mContext = checkNotNull(context, "missing context");
+        mActivityManager = checkNotNull(activityManager, "missing activityManager");
+        mPowerManager = checkNotNull(powerManager, "missing powerManager");
+    }
 
-    public void publish(Context context) {
-        mContext = context;
-        ServiceManager.addService(SERVICE_NAME, asBinder());
-
-        mUidPolicy = new SparseIntArray();
-        mUidForeground = new SparseBooleanArray();
-        mUidPidForeground = new SparseArray<SparseBooleanArray>();
-
-        // TODO: register for NetworkManagementService callbacks
+    public void systemReady() {
         // TODO: read current policy+stats from disk and generate NMS rules
-    }
 
-    public void shutdown() {
-        // TODO: persist any pending stats during clean shutdown
+        updateScreenOn();
 
-        mUidPolicy = null;
-        mUidForeground = null;
-        mUidPidForeground = null;
-    }
-
-    @Override
-    public void onForegroundActivitiesChanged(int uid, int pid, boolean foreground) {
-        // only someone like AMS should only be calling us
-        mContext.enforceCallingOrSelfPermission(
-                MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
-
-        // because a uid can have multiple pids running inside, we need to
-        // remember all pid states and summarize foreground at uid level.
-
-        // record foreground for this specific pid
-        SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
-        if (pidForeground == null) {
-            pidForeground = new SparseBooleanArray(2);
-            mUidPidForeground.put(uid, pidForeground);
+        try {
+            mActivityManager.registerProcessObserver(mProcessObserver);
+        } catch (RemoteException e) {
+            // ouch, no foregroundActivities updates means some processes may
+            // never get network access.
+            Slog.e(TAG, "unable to register IProcessObserver", e);
         }
-        pidForeground.put(pid, foreground);
-        computeUidForeground(uid);
+
+        // TODO: traverse existing processes to know foreground state, or have
+        // activitymanager dispatch current state when new observer attached.
+
+        final IntentFilter screenFilter = new IntentFilter();
+        screenFilter.addAction(Intent.ACTION_SCREEN_ON);
+        screenFilter.addAction(Intent.ACTION_SCREEN_OFF);
+        mContext.registerReceiver(mScreenReceiver, screenFilter);
+
+        final IntentFilter shutdownFilter = new IntentFilter();
+        shutdownFilter.addAction(Intent.ACTION_SHUTDOWN);
+        mContext.registerReceiver(mShutdownReceiver, shutdownFilter);
+
     }
 
-    @Override
-    public void onProcessDied(int uid, int pid) {
-        // only someone like AMS should only be calling us
-        mContext.enforceCallingOrSelfPermission(
-                MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
+    private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
+        @Override
+        public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
+            // only someone like AMS should only be calling us
+            mContext.enforceCallingOrSelfPermission(
+                    MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
 
-        // clear records and recompute, when they exist
-        final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
-        if (pidForeground != null) {
-            pidForeground.delete(pid);
-            computeUidForeground(uid);
+            synchronized (mRulesLock) {
+                // because a uid can have multiple pids running inside, we need to
+                // remember all pid states and summarize foreground at uid level.
+
+                // record foreground for this specific pid
+                SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+                if (pidForeground == null) {
+                    pidForeground = new SparseBooleanArray(2);
+                    mUidPidForeground.put(uid, pidForeground);
+                }
+                pidForeground.put(pid, foregroundActivities);
+                computeUidForegroundL(uid);
+            }
         }
-    }
+
+        @Override
+        public void onProcessDied(int pid, int uid) {
+            // only someone like AMS should only be calling us
+            mContext.enforceCallingOrSelfPermission(
+                    MANAGE_APP_TOKENS, "requires MANAGE_APP_TOKENS permission");
+
+            synchronized (mRulesLock) {
+                // clear records and recompute, when they exist
+                final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
+                if (pidForeground != null) {
+                    pidForeground.delete(pid);
+                    computeUidForegroundL(uid);
+                }
+            }
+        }
+    };
+
+    private BroadcastReceiver mScreenReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mRulesLock) {
+                // screen-related broadcasts are protected by system, no need
+                // for permissions check.
+                updateScreenOn();
+            }
+        }
+    };
+
+    private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // TODO: persist any pending stats during clean shutdown
+            Log.d(TAG, "persisting stats");
+        }
+    };
 
     @Override
     public void setUidPolicy(int uid, int policy) {
         mContext.enforceCallingOrSelfPermission(
                 UPDATE_DEVICE_STATS, "requires UPDATE_DEVICE_STATS permission");
-        mUidPolicy.put(uid, policy);
+
+        synchronized (mRulesLock) {
+            mUidPolicy.put(uid, policy);
+        }
     }
 
     @Override
     public int getUidPolicy(int uid) {
-        return mUidPolicy.get(uid, POLICY_NONE);
+        synchronized (mRulesLock) {
+            return mUidPolicy.get(uid, POLICY_NONE);
+        }
     }
 
     /**
      * Foreground for PID changed; recompute foreground at UID level. If
-     * changed, will trigger {@link #updateRulesForUid(int)}.
+     * changed, will trigger {@link #updateRulesForUidL(int)}.
      */
-    private void computeUidForeground(int uid) {
+    private void computeUidForegroundL(int uid) {
         final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
 
         // current pid is dropping foreground; examine other pids
@@ -140,12 +194,37 @@
         if (oldUidForeground != uidForeground) {
             // foreground changed, push updated rules
             mUidForeground.put(uid, uidForeground);
-            updateRulesForUid(uid);
+            updateRulesForUidL(uid);
         }
     }
 
-    private void updateRulesForUid(int uid) {
-        final boolean uidForeground = mUidForeground.get(uid, false);
+    private void updateScreenOn() {
+        synchronized (mRulesLock) {
+            try {
+                mScreenOn = mPowerManager.isScreenOn();
+            } catch (RemoteException e) {
+            }
+            updateRulesForScreenL();
+        }
+    }
+
+    /**
+     * Update rules that might be changed by {@link #mScreenOn} value.
+     */
+    private void updateRulesForScreenL() {
+        // only update rules for anyone with foreground activities
+        final int size = mUidForeground.size();
+        for (int i = 0; i < size; i++) {
+            if (mUidForeground.valueAt(i)) {
+                final int uid = mUidForeground.keyAt(i);
+                updateRulesForUidL(uid);
+            }
+        }
+    }
+
+    private void updateRulesForUidL(int uid) {
+        // only really in foreground when screen on
+        final boolean uidForeground = mUidForeground.get(uid, false) && mScreenOn;
         final int uidPolicy = getUidPolicy(uid);
 
         if (LOGD) {
@@ -162,4 +241,10 @@
         }
     }
 
+    private static <T> T checkNotNull(T value, String message) {
+        if (value == null) {
+            throw new NullPointerException(message);
+        }
+        return value;
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
index f20d5e5..ca33d32 100644
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -42,6 +42,7 @@
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
 import android.test.suitebuilder.annotation.Suppress;
 import android.text.format.DateUtils;
 import android.util.Log;
@@ -54,6 +55,7 @@
 /**
  * Tests for {@link ThrottleService}.
  */
+@LargeTest
 public class ThrottleServiceTest extends AndroidTestCase {
     private static final String TAG = "ThrottleServiceTest";
 
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index 9fc4667..0c4581b 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -25,6 +25,11 @@
 import android.os.SystemProperties;
 import android.util.Log;
 
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 /**
  * {@hide}
  */
@@ -794,6 +799,34 @@
     }
 
     /**
+     * The contents of the /proc/cmdline file
+     */
+    private static String getProcCmdLine()
+    {
+        String cmdline = "";
+        FileInputStream is = null;
+        try {
+            is = new FileInputStream("/proc/cmdline");
+            byte [] buffer = new byte[2048];
+            int count = is.read(buffer);
+            if (count > 0) {
+                cmdline = new String(buffer, 0, count);
+            }
+        } catch (IOException e) {
+            Log.d(LOG_TAG, "No /proc/cmdline exception=" + e);
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        Log.d(LOG_TAG, "/proc/cmdline=" + cmdline);
+        return cmdline;
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -801,6 +834,17 @@
         return getLteOnCdmaModeStatic();
     }
 
+    /** Kernel command line */
+    private static final String sKernelCmdLine = getProcCmdLine();
+
+    /** Pattern for selecting the product type from the kernel command line */
+    private static final Pattern sProductTypePattern =
+        Pattern.compile("\\sproduct_type\\s*=\\s*(\\w+)");
+
+    /** The ProductType used for LTE on CDMA devices */
+    private static final String sLteOnCdmaProductType =
+        SystemProperties.get(TelephonyProperties.PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE, "");
+
     /**
      * Return if the current radio is LTE on CDMA. This
      * is a tri-state return value as for a period of time
@@ -810,9 +854,24 @@
      * or {@link Phone#LTE_ON_CDMA_TRUE}
      */
     public static int getLteOnCdmaModeStatic() {
-        int retVal = SystemProperties.getInt(TelephonyProperties.PROPERTY_NETWORK_LTE_ON_CDMA,
-                Phone.LTE_ON_CDMA_FALSE);
-        Log.d(LOG_TAG, "getLteOnCdmaMode=" + retVal);
+        int retVal;
+        String productType;
+
+        Matcher matcher = sProductTypePattern.matcher(sKernelCmdLine);
+        if (matcher.find()) {
+            productType = matcher.group(1);
+            if (sLteOnCdmaProductType.equals(productType)) {
+                retVal = Phone.LTE_ON_CDMA_TRUE;
+            } else {
+                retVal = Phone.LTE_ON_CDMA_FALSE;
+            }
+        } else {
+            retVal = Phone.LTE_ON_CDMA_FALSE;
+            productType = "";
+        }
+
+        Log.d(LOG_TAG, "getLteOnCdmaMode=" + retVal + " product_type='" + productType +
+                "' lteOnCdmaProductType='" + sLteOnCdmaProductType + "'");
         return retVal;
     }
 }
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index 2e781b2..3636baa 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -22,8 +22,10 @@
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
 
+import android.net.LinkAddress;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
+import android.net.NetworkUtils;
 import android.net.ProxyProperties;
 import android.os.AsyncResult;
 import android.os.Bundle;
@@ -33,6 +35,7 @@
 import android.os.SystemProperties;
 import android.text.TextUtils;
 
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -202,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
@@ -249,6 +253,8 @@
             Message m = dp.onCompletedMsg;
             if (TextUtils.equals(dp.reason, Phone.REASON_RADIO_TURNED_OFF)) {
                 discReason = RILConstants.DEACTIVATE_REASON_RADIO_OFF;
+            } else if (TextUtils.equals(dp.reason, Phone.REASON_PDP_RESET)) {
+                discReason = RILConstants.DEACTIVATE_REASON_PDP_RESET;
             }
         }
         if (phone.mCM.getRadioState().isOn()) {
@@ -408,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
@@ -466,6 +429,7 @@
         createTime = -1;
         lastFailTime = -1;
         lastFailCause = FailCause.NONE;
+        mRefCount = 0;
 
         mLinkProperties = new LinkProperties();
         mApn = null;
@@ -531,8 +495,10 @@
         return response.setLinkProperties(lp, okToUseSystemPropertyDns);
     }
 
-    private boolean updateLinkProperty(DataCallState newState) {
-        boolean changed = false;
+    private DataConnectionAc.LinkPropertyChangeAction updateLinkProperty(
+                                                      DataCallState newState) {
+        DataConnectionAc.LinkPropertyChangeAction changed =
+                        DataConnectionAc.LinkPropertyChangeAction.NONE;
 
         if (newState == null) return changed;
 
@@ -551,9 +517,23 @@
         if (DBG) log("old LP=" + mLinkProperties);
         if (DBG) log("new LP=" + newLp);
 
+        // Check consistency of link address. Currently we expect
+        // only one "global" address is assigned per each IP type.
+        Collection<LinkAddress> oLinks = mLinkProperties.getLinkAddresses();
+        Collection<LinkAddress> nLinks = newLp.getLinkAddresses();
+        for (LinkAddress oldLink : oLinks) {
+            for (LinkAddress newLink : nLinks) {
+                if ((NetworkUtils.addressTypeMatches(oldLink.getAddress(),
+                                        newLink.getAddress())) &&
+                    (oldLink.equals(newLink) == false)) {
+                    return DataConnectionAc.LinkPropertyChangeAction.RESET;
+                }
+            }
+        }
+
         if (mLinkProperties == null || !mLinkProperties.equals(newLp)) {
             mLinkProperties = newLp;
-            changed = true;
+            changed = DataConnectionAc.LinkPropertyChangeAction.CHANGED;
         }
 
         return changed;
@@ -631,15 +611,14 @@
                 }
                 case DataConnectionAc.REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE: {
                     DataCallState newState = (DataCallState) msg.obj;
-                    int updated = updateLinkProperty(newState) ? 1 : 0;
+                    DataConnectionAc.LinkPropertyChangeAction action = updateLinkProperty(newState);
                     if (DBG) {
-                        log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE updated="
-                                + (updated == 1)
-                                + " newState=" + newState);
+                        log("REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE action="
+                            + action + " newState=" + newState);
                     }
                     mAc.replyToMessage(msg,
                                    DataConnectionAc.RSP_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE,
-                                   updated);
+                                   action.ordinal());
                     break;
                 }
                 case DataConnectionAc.REQ_GET_LINK_CAPABILITIES: {
@@ -654,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");
@@ -754,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;
@@ -784,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;
@@ -908,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;
 
@@ -936,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 2ab6184..a0d9b0f 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionAc.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionAc.java
@@ -59,6 +59,29 @@
     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.
+     */
+    public enum LinkPropertyChangeAction {
+        NONE, CHANGED, RESET;
+
+        public static LinkPropertyChangeAction fromInt(int value) {
+            if (value == NONE.ordinal()) {
+                return NONE;
+            } else if (value == CHANGED.ordinal()) {
+                return CHANGED;
+            } else if (value == RESET.ordinal()) {
+                return RESET;
+            } else {
+                throw new RuntimeException("LinkPropertyChangeAction.fromInt: bad value=" + value);
+            }
+        }
+    }
+
     public DataConnectionAc(DataConnection dc, String logTag) {
         dataConnection = dc;
         mLogTag = logTag;
@@ -132,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}
      */
@@ -234,8 +291,8 @@
         if (DBG) log("reqUpdateLinkPropertiesDataCallState");
     }
 
-    public boolean rspUpdateLinkPropertiesDataCallState(Message response) {
-        boolean retVal = response.arg1 == 1;
+    public LinkPropertyChangeAction rspUpdateLinkPropertiesDataCallState(Message response) {
+        LinkPropertyChangeAction retVal = LinkPropertyChangeAction.fromInt(response.arg1);
         if (DBG) log("rspUpdateLinkPropertiesState=" + retVal);
         return retVal;
     }
@@ -245,7 +302,7 @@
      *
      * @return true if link property has been updated. false otherwise.
      */
-    public boolean updateLinkPropertiesDataCallStateSync(DataCallState newState) {
+    public LinkPropertyChangeAction updateLinkPropertiesDataCallStateSync(DataCallState newState) {
         Message response =
             sendMessageSynchronously(REQ_UPDATE_LINK_PROPERTIES_DATA_CALL_STATE, newState);
         if ((response != null) &&
@@ -253,7 +310,7 @@
             return rspUpdateLinkPropertiesDataCallState(response);
         } else {
             log("getLinkProperties error response=" + response);
-            return false;
+            return LinkPropertyChangeAction.NONE;
         }
     }
 
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/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java
index 1b49d2d..a516b49 100644
--- a/telephony/java/com/android/internal/telephony/IccCard.java
+++ b/telephony/java/com/android/internal/telephony/IccCard.java
@@ -18,13 +18,20 @@
 
 import static android.Manifest.permission.READ_PHONE_STATE;
 import android.app.ActivityManagerNative;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Power;
+import android.os.PowerManager;
 import android.os.Registrant;
 import android.os.RegistrantList;
 import android.util.Log;
+import android.view.WindowManager;
 
 import com.android.internal.telephony.PhoneBase;
 import com.android.internal.telephony.CommandsInterface.RadioState;
@@ -32,6 +39,8 @@
 
 import android.os.SystemProperties;
 
+import com.android.internal.R;
+
 /**
  * {@hide}
  */
@@ -88,6 +97,8 @@
     private static final int EVENT_QUERY_FACILITY_FDN_DONE = 10;
     private static final int EVENT_CHANGE_FACILITY_FDN_DONE = 11;
     private static final int EVENT_ICC_STATUS_CHANGED = 12;
+    private static final int EVENT_CARD_REMOVED = 13;
+    private static final int EVENT_CARD_ADDED = 14;
 
     /*
       UNKNOWN is a transient state, for example, after uesr inputs ICC pin under
@@ -106,6 +117,11 @@
         public boolean isPinLocked() {
             return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED));
         }
+
+        public boolean iccCardExist() {
+            return ((this == PIN_REQUIRED) || (this == PUK_REQUIRED)
+                    || (this == NETWORK_LOCKED) || (this == READY));
+        }
     }
 
     public State getState() {
@@ -400,6 +416,8 @@
         boolean transitionedIntoPinLocked;
         boolean transitionedIntoAbsent;
         boolean transitionedIntoNetworkLocked;
+        boolean isIccCardRemoved;
+        boolean isIccCardAdded;
 
         State oldState, newState;
 
@@ -416,24 +434,36 @@
         transitionedIntoAbsent = (oldState != State.ABSENT && newState == State.ABSENT);
         transitionedIntoNetworkLocked = (oldState != State.NETWORK_LOCKED
                 && newState == State.NETWORK_LOCKED);
+        isIccCardRemoved = (oldState != null &&
+                        oldState.iccCardExist() && newState == State.ABSENT);
+        isIccCardAdded = (oldState == State.ABSENT &&
+                        newState != null && newState.iccCardExist());
 
         if (transitionedIntoPinLocked) {
-            if(mDbg) log("Notify SIM pin or puk locked.");
+            if (mDbg) log("Notify SIM pin or puk locked.");
             mPinLockedRegistrants.notifyRegistrants();
             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
                     (newState == State.PIN_REQUIRED) ?
                        INTENT_VALUE_LOCKED_ON_PIN : INTENT_VALUE_LOCKED_ON_PUK);
         } else if (transitionedIntoAbsent) {
-            if(mDbg) log("Notify SIM missing.");
+            if (mDbg) log("Notify SIM missing.");
             mAbsentRegistrants.notifyRegistrants();
             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_ABSENT, null);
         } else if (transitionedIntoNetworkLocked) {
-            if(mDbg) log("Notify SIM network locked.");
+            if (mDbg) log("Notify SIM network locked.");
             mNetworkLockedRegistrants.notifyRegistrants();
             broadcastIccStateChangedIntent(INTENT_VALUE_ICC_LOCKED,
                   INTENT_VALUE_LOCKED_NETWORK);
         }
 
+        if (isIccCardRemoved) {
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_REMOVED, null));
+        } else if (isIccCardAdded) {
+            mHandler.sendMessage(mHandler.obtainMessage(EVENT_CARD_ADDED, null));
+        }
+
+
+
         /*
          * TODO: We need to try to remove this, maybe if the RIL sends up a RIL_UNSOL_SIM_REFRESH?
          */
@@ -443,6 +473,48 @@
                 ((SIMRecords)mPhone.mIccRecords).onSimReady();
             }
         }
+
+    }
+
+    private void onIccSwap(boolean isAdded) {
+        // TODO: Here we assume the device can't handle SIM hot-swap
+        //      and has to reboot. We may want to add a property,
+        //      e.g. REBOOT_ON_SIM_SWAP, to indicate if modem support
+        //      hot-swap.
+        DialogInterface.OnClickListener listener = null;
+
+
+        // TODO: SimRecords is not reset while SIM ABSENT (only reset while
+        //       Radio_off_or_not_available). Have to reset in both both
+        //       added or removed situation.
+        listener = new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                if (which == DialogInterface.BUTTON_POSITIVE) {
+                    if (mDbg) log("Reboot due to SIM swap");
+                    PowerManager pm = (PowerManager) mPhone.getContext()
+                    .getSystemService(Context.POWER_SERVICE);
+                    pm.reboot("SIM is added.");
+                }
+            }
+
+        };
+
+        Resources r = Resources.getSystem();
+
+        String title = (isAdded) ? r.getString(R.string.sim_added_title) :
+            r.getString(R.string.sim_removed_title);
+        String message = (isAdded) ? r.getString(R.string.sim_added_message) :
+            r.getString(R.string.sim_removed_message);
+        String buttonTxt = r.getString(R.string.sim_restart_button);
+
+        AlertDialog dialog = new AlertDialog.Builder(mPhone.getContext())
+            .setTitle(title)
+            .setMessage(message)
+            .setPositiveButton(buttonTxt, listener)
+            .create();
+        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+        dialog.show();
     }
 
     /**
@@ -609,6 +681,12 @@
                     Log.d(mLogTag, "Received Event EVENT_ICC_STATUS_CHANGED");
                     mPhone.mCM.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
                     break;
+                case EVENT_CARD_REMOVED:
+                    onIccSwap(false);
+                    break;
+                case EVENT_CARD_ADDED:
+                    onIccSwap(true);
+                    break;
                 default:
                     Log.e(mLogTag, "[IccCard] Unknown Event " + msg.what);
             }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index f5d6c51..93fc9ce 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -113,6 +113,7 @@
     /* Deactivate data call reasons */
     int DEACTIVATE_REASON_NONE = 0;
     int DEACTIVATE_REASON_RADIO_OFF = 1;
+    int DEACTIVATE_REASON_PDP_RESET = 2;
 
 /*
 cat include/telephony/ril.h | \
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 4927006..4309309 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -72,10 +72,12 @@
      */
     static final String PROPERTY_OPERATOR_ISO_COUNTRY = "gsm.operator.iso-country";
 
-    /** 'true' if device supports both LTE and CDMA mode of operation.
-     *  Availability: Set only on devices supporting LTE and CDMA.
+    /**
+     * The contents of this property is the value of the kernel command line
+     * product_type variable that corresponds to a product that supports LTE on CDMA.
+     * {@see BaseCommands#getLteOnCdmaMode()}
      */
-    static final String PROPERTY_NETWORK_LTE_ON_CDMA = "telephony.lte_on_cdma";
+    static final String PROPERTY_LTE_ON_CDMA_PRODUCT_TYPE = "telephony.lteOnCdmaProductType";
 
     static final String CURRENT_ACTIVE_PHONE = "gsm.current.phone-type";
 
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 6b4054a..e1a6fef 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -127,10 +127,7 @@
         if (apnContext != null) {
             apnContext.setReason(reason);
             if (apnContext.getState() == State.FAILED) {
-                Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION);
-                msg.arg1 = 0; // tearDown is false
-                msg.obj = (ApnContext)apnContext;
-                sendMessage(msg);
+                apnContext.setState(State.IDLE);
             }
             sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
         }
@@ -615,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) {
@@ -644,7 +639,7 @@
         for (ApnContext apnContext : mApnContexts.values()) {
             if (apnContext.isReady()) {
                 if (apnContext.getState() == State.FAILED) {
-                    cleanUpConnection(false, apnContext);
+                    cleanApnContextBeforeRestart(apnContext);
                     if (apnContext.getDataConnection() != null) {
                         apnContext.getDataConnection().resetRetryCount();
                     }
@@ -800,6 +795,34 @@
         if (DBG) {
             log("cleanUpConnection: tearDown=" + tearDown + " reason=" + apnContext.getReason());
         }
+        if (tearDown && cleanApnContextBeforeRestart(apnContext)) {
+            // if the request is tearDown and ApnContext does not hold an active connection,
+            // we're ok to return here.
+            return;
+        }
+
+        DataConnectionAc dcac = apnContext.getDataConnectionAc();
+        if (dcac != null) {
+            if (tearDown) {
+                apnContext.setState(State.DISCONNECTING);
+                releaseApnContext(apnContext, tearDown);
+            } else {
+                dcac.resetSync();
+                apnContext.setState(State.IDLE);
+                mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
+                apnContext.setDataConnection(null);
+                apnContext.setDataConnectionAc(null);
+            }
+        }
+    }
+
+    /**
+     * @param APNContext to clean
+     * @return true if ApnContext is not connected anymore.
+     *         false if ApnContext still holds a connection.
+     */
+    private boolean cleanApnContextBeforeRestart(ApnContext apnContext) {
+        if (apnContext == null) return true;
 
         // Clear the reconnect alarm, if set.
         if (apnContext.getReconnectIntent() != null) {
@@ -811,32 +834,16 @@
 
         if (apnContext.getState() == State.IDLE || apnContext.getState() == State.DISCONNECTING) {
             if (DBG) log("cleanUpConnection: state= " + apnContext.getState());
-            return;
+            return true;
         }
 
         if (apnContext.getState() == State.FAILED) {
-            if (DBG) log("cleanUpConnection: state is in FAILED");
             apnContext.setState(State.IDLE);
-            return;
+            return true;
         }
-
-        DataConnection conn = apnContext.getDataConnection();
-        if (conn != null) {
-            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(conn.getDataConnectionId());
-            apnContext.setState(State.DISCONNECTING);
-            if (tearDown) {
-                releaseApnContext(apnContext, tearDown);
-            } else {
-                if (dcac != null) {
-                    dcac.resetSync();
-                }
-                apnContext.setState(State.IDLE);
-                mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
-            }
-        }
+        return false;
     }
 
-
     /**
      * @param types comma delimited list of APN types
      * @return array of APN types
@@ -927,37 +934,48 @@
             return false;
         }
 
-        dc = findReadyDataConnection(apn);
+        // First, check to see if ApnContext already has DC.
+        // This could happen if the retries are currently  engaged.
+        dc = (GsmDataConnection)apnContext.getDataConnection();
 
         if (dc == null) {
-            if (DBG) log("setupData: No ready GsmDataConnection found!");
-            // TODO: When allocating you are mapping type to id. If more than 1 free,
-            // then could findFreeDataConnection get the wrong one??
-            dc = findFreeDataConnection();
-        }
 
-        if (dc == null) {
-            dc = createDataConnection();
-        }
+            dc = (GsmDataConnection) checkForConnectionForApnContext(apnContext);
 
-        if (dc == null) {
-            if (DBG) log("setupData: No free GsmDataConnection found!");
-            return false;
-        }
+            if (dc == null) {
+                dc = findReadyDataConnection(apn);
+            }
 
-        dc.setProfileId( profileId );
-        dc.setActiveApnType(apnContext.getApnType());
-        int refCount = dc.incAndGetRefCount();
-        if (DBG) log("setupData: init dc and apnContext refCount=" + refCount);
+            if (dc == null) {
+                if (DBG) log("setupData: No ready GsmDataConnection found!");
+                // TODO: When allocating you are mapping type to id. If more than 1 free,
+                // then could findFreeDataConnection get the wrong one??
+                dc = findFreeDataConnection();
+            }
 
-        // configure retry count if no other Apn is using the same connection.
-        if (refCount == 1) {
-            configureRetry(dc, apnContext.getApnType());
+            if (dc == null) {
+                dc = createDataConnection();
+            }
+
+            if (dc == null) {
+                if (DBG) log("setupData: No free GsmDataConnection found!");
+                return false;
+            }
+
+            DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
+            dc.setProfileId( profileId );
+            dc.setActiveApnType(apnContext.getApnType());
+            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 == 0) {
+                configureRetry(dc, apnContext.getApnType());
+            }
+            apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId()));
+            apnContext.setApnSetting(apn);
+            apnContext.setDataConnection(dc);
         }
-        DataConnectionAc dcac = mDataConnectionAsyncChannels.get(dc.getDataConnectionId());
-        apnContext.setDataConnectionAc(mDataConnectionAsyncChannels.get(dc.getDataConnectionId()));
-        apnContext.setApnSetting(apn);
-        apnContext.setDataConnection(dc);
 
         Message msg = obtainMessage();
         msg.what = EVENT_DATA_SETUP_COMPLETE;
@@ -1053,25 +1071,37 @@
                     if (DBG) log("onDataStateChanged(ar): Found ConnId=" + connectionId
                             + " newState=" + newState.toString());
                     if (newState.active != 0) {
-                        boolean changed
-                            = dcac.updateLinkPropertiesDataCallStateSync(newState);
-                        if (changed) {
+                        boolean resetConnection;
+                        switch (dcac.updateLinkPropertiesDataCallStateSync(newState)) {
+                        case NONE:
+                            if (DBG) log("onDataStateChanged(ar): Found but no change, skip");
+                            resetConnection = false;
+                            break;
+                        case CHANGED:
                             if (DBG) log("onDataStateChanged(ar): Found and changed, notify");
                             mPhone.notifyDataConnection(Phone.REASON_LINK_PROPERTIES_CHANGED,
-                                    apnContext.getApnType());
-                            // Temporary hack, if false we'll reset connections and at this
-                            // time a transition from CDMA -> Global fails. The DEACTIVATE
-                            // fails with a GENERIC_FAILURE and the VZWINTERNET connection is
-                            // never setup. @see bug/
+                                                        apnContext.getApnType());
+                            // Temporary hack, at this time a transition from CDMA -> Global
+                            // fails so we'll hope for the best and not reset the connection.
+                            // @see bug/4455071
                             if (SystemProperties.getBoolean("telephony.ignore-state-changes",
-                                    true)) {
+                                                            true)) {
                                 log("onDataStateChanged(ar): STOPSHIP don't reset, continue");
-                                continue;
+                                resetConnection = false;
+                            } else {
+                                // Things changed so reset connection, when hack is removed
+                                // this is the normal path.
+                                log("onDataStateChanged(ar): changed so resetting connection");
+                                resetConnection = true;
                             }
-                        } else {
-                            if (DBG) log("onDataStateChanged(ar): Found but no change, skip");
-                            continue;
+                            break;
+                        case RESET:
+                        default:
+                            if (DBG) log("onDataStateChanged(ar): an error, reset connection");
+                            resetConnection = true;
+                            break;
                         }
+                        if (resetConnection == false) continue;
                     }
                 }
 
@@ -1214,10 +1244,30 @@
             preTxPkts = mTxPkts;
             preRxPkts = mRxPkts;
 
-            mTxPkts = TrafficStats.getMobileTxPackets();
-            mRxPkts = TrafficStats.getMobileRxPackets();
+            long txSum = 0, rxSum = 0;
+            for (ApnContext apnContext : mApnContexts.values()) {
+                if (apnContext.getState() == State.CONNECTED) {
+                    DataConnectionAc dcac = apnContext.getDataConnectionAc();
+                    if (dcac == null) continue;
 
-            //log("rx " + String.valueOf(rxPkts) + " tx " + String.valueOf(txPkts));
+                    LinkProperties linkProp = dcac.getLinkPropertiesSync();
+                    if (linkProp == null) continue;
+
+                    String iface = linkProp.getInterfaceName();
+
+                    if (iface != null) {
+                        long stats = TrafficStats.getTxPackets(iface);
+                        if (stats > 0) txSum += stats;
+                        stats = TrafficStats.getRxPackets(iface);
+                        if (stats > 0) rxSum += stats;
+                    }
+                }
+            }
+
+            mTxPkts = txSum;
+            mRxPkts = rxSum;
+
+            // log("tx " + mTxPkts + " rx " + mRxPkts);
 
             if (mNetStatPollEnabled && (preTxPkts > 0 || preRxPkts > 0)) {
                 sent = mTxPkts - preTxPkts;
@@ -1333,7 +1383,8 @@
             loge("reconnectAfterFail: apnContext == null, impossible");
             return;
         }
-        if (apnContext.getState() == State.FAILED) {
+        if ((apnContext.getState() == State.FAILED) &&
+            (apnContext.getDataConnection() != null)) {
             if (!apnContext.getDataConnection().isRetryNeeded()) {
                 if (!apnContext.getApnType().equals(Phone.APN_TYPE_DEFAULT)) {
                     mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
@@ -1353,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)) {
@@ -1383,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());
@@ -1437,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);
@@ -1588,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) {
@@ -1655,6 +1696,11 @@
                     }
                     apnContext.setState(State.FAILED);
                     mPhone.notifyDataConnection(Phone.REASON_APN_FAILED, apnContext.getApnType());
+
+                    releaseApnContext(apnContext, false);
+                    if (DBG) {
+                        log("onDataSetupComplete: permanent error apn=%s" + apnString );
+                    }
                 } else {
                     if (DBG) log("onDataSetupComplete: Not all permanent failures, retry");
                     startDelayedRetry(cause, apnContext);
@@ -1664,13 +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);
-            }
-
-            int refCount = releaseApnContext(apnContext, false);
-            if (DBG) {
-                log("onDataSetupComplete: error apn=%s" + apnString + " refCount=" + refCount);
+                startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
             }
         }
     }
@@ -1710,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);
         }
     }
 
@@ -1753,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/telephony/java/com/android/internal/telephony/gsm/SimCard.java b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
index b7b0af3..643f709 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimCard.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimCard.java
@@ -50,6 +50,7 @@
 
         if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
             mPhone.mCM.registerForNVReady(mHandler, EVENT_ICC_READY, null);
+            mPhone.mCM.registerForIccStatusChanged(mHandler, EVENT_ICC_LOCKED_OR_ABSENT, null);
         }
     }
 
@@ -60,6 +61,11 @@
         mPhone.mCM.unregisterForSIMLockedOrAbsent(mHandler);
         mPhone.mCM.unregisterForOffOrNotAvailable(mHandler);
         mPhone.mCM.unregisterForSIMReady(mHandler);
+
+        if(mPhone.getLteOnCdmaMode() == Phone.LTE_ON_CDMA_TRUE) {
+            mPhone.mCM.unregisterForNVReady(mHandler);
+            mPhone.mCM.unregisterForIccStatusChanged(mHandler);
+        }
     }
 
     @Override
diff --git a/tests/BiDiTests/AndroidManifest.xml b/tests/BiDiTests/AndroidManifest.xml
index 727f980..8a77519 100644
--- a/tests/BiDiTests/AndroidManifest.xml
+++ b/tests/BiDiTests/AndroidManifest.xml
@@ -14,24 +14,49 @@
      limitations under the License.
 -->
 
-<!-- Declare the contents of this Android application.  The namespace
-     attribute brings in the Android platform namespace, and the package
-     supplies a unique name for the application.  When writing your
-     own application, the package name must be changed from "com.example.*"
-     to come from a domain that you own or have control over. -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.bidi"
     android:versionCode="1"
     android:versionName="1.0">
 
     <application android:label="BiDiTests">
-        <activity android:name="BiDiTestActivity"
-                android:windowSoftInputMode="stateAlwaysHidden">
+
+        <activity android:name=".BiDiTestActivity"
+            android:windowSoftInputMode="stateAlwaysHidden">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
 
+        <activity android:name=".BiDiTestBasicActivity"
+                android:windowSoftInputMode="stateAlwaysHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".BiDiTestCanvasActivity"
+                  android:windowSoftInputMode="stateAlwaysHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".BiDiTestLinearLayoutLtrActivity"
+                  android:windowSoftInputMode="stateAlwaysHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".BiDiTestLinearLayoutRtlActivity"
+                  android:windowSoftInputMode="stateAlwaysHidden">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
     </application>
+
 </manifest>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/biditest_main.xml b/tests/BiDiTests/res/layout/basic.xml
similarity index 79%
rename from tests/BiDiTests/res/layout/biditest_main.xml
rename to tests/BiDiTests/res/layout/basic.xml
index 044a355..c4807ff 100644
--- a/tests/BiDiTests/res/layout/biditest_main.xml
+++ b/tests/BiDiTests/res/layout/basic.xml
@@ -20,7 +20,7 @@
     android:layout_height="match_parent">
 
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:orientation="horizontal"
+        android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content">
 
@@ -39,7 +39,7 @@
                   android:text="@string/textview_text"
         />
 
-        <EditText android:id="@+id/textview"
+        <EditText android:id="@+id/edittext"
                   android:layout_height="wrap_content"
                   android:layout_width="match_parent"
                   android:textSize="32dip"
@@ -47,16 +47,4 @@
 
     </LinearLayout>
 
-    <SeekBar android:id="@+id/seekbar"
-               android:layout_height="wrap_content"
-               android:layout_width="match_parent"
-               />
-
-    <view class="com.android.bidi.BiDiTestView"
-        android:id="@+id/main"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:background="#FF0000"
-    />
-
 </LinearLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/canvas.xml b/tests/BiDiTests/res/layout/canvas.xml
new file mode 100644
index 0000000..371cc23
--- /dev/null
+++ b/tests/BiDiTests/res/layout/canvas.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <SeekBar android:id="@+id/seekbar"
+               android:layout_height="wrap_content"
+               android:layout_width="match_parent"
+               />
+
+    <view class="com.android.bidi.BiDiTestView"
+        android:id="@+id/testview"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="#FF0000"
+    />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/linear_layout_ltr.xml b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
new file mode 100644
index 0000000..a95fb0e
--- /dev/null
+++ b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/layouttest"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:horizontalDirection="ltr">
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="inherit">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="ltr">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="rtl">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="inherit">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="ltr">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="rtl">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/linear_layout_rtl.xml b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
new file mode 100644
index 0000000..0d60950
--- /dev/null
+++ b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/layouttest"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:horizontalDirection="rtl">
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="inherit">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="ltr">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="rtl">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="inherit">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="ltr">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:horizontalDirection="rtl">
+
+       <Button android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:text="@string/button1_text"
+               android:textSize="32dip"
+        />
+
+        <TextView android:id="@+id/textview"
+                  android:layout_height="wrap_content"
+                  android:layout_width="wrap_content"
+                  android:textSize="32dip"
+                  android:text="@string/textview_text"
+        />
+
+        <Button android:layout_height="wrap_content"
+                android:layout_width="wrap_content"
+                android:text="@string/button2_text"
+                android:textSize="32dip"
+         />
+
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/main.xml b/tests/BiDiTests/res/layout/main.xml
new file mode 100644
index 0000000..e39d1d6
--- /dev/null
+++ b/tests/BiDiTests/res/layout/main.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/tabhost"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:padding="5dp">
+
+        <TabWidget
+            android:id="@android:id/tabs"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content" />
+
+        <FrameLayout
+            android:id="@android:id/tabcontent"
+            android:layout_width="fill_parent"
+            android:layout_height="fill_parent"
+            android:padding="5dp" />
+
+    </LinearLayout>
+
+</TabHost>
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index c272df1..3795d4b 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -14,6 +14,9 @@
 -->
 <resources>
     <string name="button_text">Button</string>
+    <string name="button1_text">Button1</string>
+    <string name="button2_text">Button2</string>
+    <string name="button_requestlayout_text">Request Layout</string>
     <string name="textview_text">This is a text for a TextView</string>
     <string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
     <string name="normal_text">Normal String</string>
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index 6c71574..0d9b4f7 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -16,56 +16,43 @@
 
 package com.android.bidi;
 
-import android.app.Activity;
+import android.app.TabActivity;
+import android.content.Intent;
 import android.os.Bundle;
-import android.util.Log;
-import android.view.View;
-import android.widget.SeekBar;
+import android.widget.TabHost;
 
-import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE;
-import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE;
-
-public class BiDiTestActivity extends Activity {
-
-    static final String TAG = "BiDiTestActivity";
-
-    static final int INIT_TEXT_SIZE = (FONT_MAX_SIZE - FONT_MIN_SIZE) / 2;
-
-    private BiDiTestView textView;
-    private SeekBar textSizeSeekBar;
+public class BiDiTestActivity extends TabActivity {
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        setContentView(R.layout.biditest_main);
+        setContentView(R.layout.main);
 
-        textView = (BiDiTestView) findViewById(R.id.main);
-        textView.setCurrentTextSize(INIT_TEXT_SIZE);
+        TabHost tabHost = getTabHost();
+        TabHost.TabSpec spec;
+        Intent intent;
 
-        textSizeSeekBar = (SeekBar) findViewById(R.id.seekbar);
-        textSizeSeekBar.setProgress(INIT_TEXT_SIZE);
-        textSizeSeekBar.setMax(FONT_MAX_SIZE - FONT_MIN_SIZE);
+        // Create an Intent to launch an Activity for the tab (to be reused)
+        intent = new Intent().setClass(this, BiDiTestBasicActivity.class);
 
-        textSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
-            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-                textView.setCurrentTextSize(FONT_MIN_SIZE + progress);
-            }
+        // Initialize a TabSpec for each tab and add it to the TabHost
+        spec = tabHost.newTabSpec("basic").setIndicator("Basic").setContent(intent);
+        tabHost.addTab(spec);
 
-            public void onStartTrackingTouch(SeekBar seekBar) {
-            }
+        // Do the same for the other tabs
+        intent = new Intent().setClass(this, BiDiTestCanvasActivity.class);
+        spec = tabHost.newTabSpec("canvas").setIndicator("Canvas").setContent(intent);
+        tabHost.addTab(spec);
 
-            public void onStopTrackingTouch(SeekBar seekBar) {
-            }
-        });
-    }
+        intent = new Intent().setClass(this, BiDiTestLinearLayoutLtrActivity.class);
+        spec = tabHost.newTabSpec("layout-ltr").setIndicator("LinearLayout LTR").setContent(intent);
+        tabHost.addTab(spec);
 
-    @Override
-    protected void onResume() {
-        super.onResume();
-    }
+        intent = new Intent().setClass(this, BiDiTestLinearLayoutRtlActivity.class);
+        spec = tabHost.newTabSpec("layout-rtl").setIndicator("LinearLayout RTL").setContent(intent);
+        tabHost.addTab(spec);
 
-    public void onButtonClick(View v) {
-        Log.v(TAG, "onButtonClick");
+        tabHost.setCurrentTab(0);
     }
 }
\ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestBasicActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestBasicActivity.java
new file mode 100644
index 0000000..2a8de04
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestBasicActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.bidi;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class BiDiTestBasicActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.basic);
+    }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvasActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvasActivity.java
new file mode 100644
index 0000000..3ab75d5
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestCanvasActivity.java
@@ -0,0 +1,58 @@
+/*
+ * 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.bidi;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.SeekBar;
+
+import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE;
+import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE;
+
+public class BiDiTestCanvasActivity extends Activity {
+
+    static final int INIT_TEXT_SIZE = (FONT_MAX_SIZE - FONT_MIN_SIZE) / 2;
+
+    private BiDiTestView testView;
+    private SeekBar textSizeSeekBar;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.canvas);
+
+        testView = (BiDiTestView) findViewById(R.id.testview);
+        testView.setCurrentTextSize(INIT_TEXT_SIZE);
+
+        textSizeSeekBar = (SeekBar) findViewById(R.id.seekbar);
+        textSizeSeekBar.setProgress(INIT_TEXT_SIZE);
+        textSizeSeekBar.setMax(FONT_MAX_SIZE - FONT_MIN_SIZE);
+
+        textSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                testView.setCurrentTextSize(FONT_MIN_SIZE + progress);
+            }
+
+            public void onStartTrackingTouch(SeekBar seekBar) {
+            }
+
+            public void onStopTrackingTouch(SeekBar seekBar) {
+            }
+        });
+    }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutLtrActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutLtrActivity.java
new file mode 100644
index 0000000..6d8f11d
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutLtrActivity.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 com.android.bidi;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
+public class BiDiTestLinearLayoutLtrActivity extends Activity {
+
+    private LinearLayout layout;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.linear_layout_ltr);
+
+        layout = (LinearLayout) findViewById(R.id.layouttest);
+    }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutRtlActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutRtlActivity.java
new file mode 100644
index 0000000..0130793
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestLinearLayoutRtlActivity.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 com.android.bidi;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.LinearLayout;
+
+public class BiDiTestLinearLayoutRtlActivity extends Activity {
+
+    private LinearLayout layout;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.linear_layout_rtl);
+
+        layout = (LinearLayout) findViewById(R.id.layouttest);
+    }
+}
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);