Merge "Fix issue #3400119: API to specify a black background behind a window transition"
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index f750122..a408ea0 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -233,17 +233,11 @@
                 if (host.equalsIgnoreCase("localhost")) {
                     return true;
                 }
-                // Check we have a numeric address so we don't cause a DNS lookup in getByName.
-                if (InetAddress.isNumeric(host)) {
-                    if (InetAddress.getByName(host).isLoopbackAddress()) {
-                        return true;
-                    }
+                if (NetworkUtils.numericToInetAddress(host).isLoopbackAddress()) {
+                    return true;
                 }
             }
-        } catch (UnknownHostException ignored) {
-            // Can't happen for a numeric address (InetAddress.getByName).
         } catch (IllegalArgumentException iex) {
-            // Ignore (URI.create)
         }
         return false;
     }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index a402c91..7dc36f9 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -392,4 +392,5 @@
     
     final private WeakReference mSelf;
     private int mObject;
+    private int mOrgue;
 }
diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java
index b96defe..4e22ba0 100644
--- a/core/java/android/preference/PreferenceFragment.java
+++ b/core/java/android/preference/PreferenceFragment.java
@@ -187,6 +187,7 @@
     public void onDestroyView() {
         mList = null;
         mHandler.removeCallbacks(mRequestFocus);
+        mHandler.removeMessages(MSG_BIND_PREFERENCES);
         super.onDestroyView();
     }
 
diff --git a/core/java/android/server/BluetoothPanProfileHandler.java b/core/java/android/server/BluetoothPanProfileHandler.java
index 3f24811..8925856 100644
--- a/core/java/android/server/BluetoothPanProfileHandler.java
+++ b/core/java/android/server/BluetoothPanProfileHandler.java
@@ -28,6 +28,7 @@
 import android.net.ConnectivityManager;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
+import android.net.NetworkUtils;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.ServiceManager;
@@ -379,9 +380,9 @@
             if (ifcg != null) {
                 InetAddress addr = null;
                 if (ifcg.addr == null || (addr = ifcg.addr.getAddress()) == null ||
-                        addr.equals(InetAddress.getByName("0.0.0.0")) ||
-                        addr.equals(InetAddress.getByName("::0"))) {
-                    addr = InetAddress.getByName(address);
+                        addr.equals(NetworkUtils.numericToInetAddress("0.0.0.0")) ||
+                        addr.equals(NetworkUtils.numericToInetAddress("::0"))) {
+                    addr = NetworkUtils.numericToInetAddress(address);
                 }
                 ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
                 ifcg.addr = new LinkAddress(addr, BLUETOOTH_PREFIX_LENGTH);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 01bc2df..2170d72 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1678,7 +1678,7 @@
     public static final int STATUS_BAR_VISIBLE = 0;
 
     /**
-     * View has requested the status bar to be visible (the default).
+     * View has requested the status bar to be hidden.
      *
      * @see #setSystemUiVisibility(int)
      */
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 43f8790..af20ddb 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -3666,9 +3666,11 @@
      * @param interfaceName The name of the interface to remove.
      */
     public void removeJavascriptInterface(String interfaceName) {
-        WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
-        arg.mInterfaceName = interfaceName;
-        mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
+        if (mWebViewCore != null) {
+            WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
+            arg.mInterfaceName = interfaceName;
+            mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
+        }
     }
 
     /**
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index ee0d122..28f64a9 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -632,7 +632,7 @@
     }
 
     /* package */ float getZoomOverviewScale() {
-        return computeScaleWithLimits(mWebView.getViewWidth() * mInvZoomOverviewWidth);
+        return mWebView.getViewWidth() * mInvZoomOverviewWidth;
     }
 
     public boolean isInZoomOverview() {
@@ -725,7 +725,10 @@
         }
 
         public boolean onScale(ScaleGestureDetector detector) {
-            float scale = computeScaleWithLimits(detector.getScaleFactor() * mActualScale);
+            // Prevent scaling beyond overview scale.
+            float scale = Math.max(
+                    computeScaleWithLimits(detector.getScaleFactor() * mActualScale),
+                    getZoomOverviewScale());
             if (mPinchToZoomAnimating || willScaleTriggerZoom(scale)) {
                 mPinchToZoomAnimating = true;
                 // limit the scale change per step
@@ -781,6 +784,13 @@
 
         // update mMinZoomScale if the minimum zoom scale is not fixed
         if (!mMinZoomScaleFixed) {
+            // when change from narrow screen to wide screen, the new viewWidth
+            // can be wider than the old content width. We limit the minimum
+            // scale to 1.0f. The proper minimum scale will be calculated when
+            // the new picture shows up.
+            mMinZoomScale = Math.min(1.0f, (float) mWebView.getViewWidth()
+                    / (mWebView.drawHistory() ? mWebView.getHistoryPictureWidth()
+                            : mZoomOverviewWidth));
             // limit the minZoomScale to the initialScale if it is set
             if (mInitialScale > 0 && mInitialScale < mMinZoomScale) {
                 mMinZoomScale = mInitialScale;
@@ -817,7 +827,7 @@
                 // Keep overview mode unchanged when rotating.
                 final float zoomOverviewScale = getZoomOverviewScale();
                 final float newScale = (mInZoomOverviewBeforeSizeChange) ?
-                    zoomOverviewScale : mActualScale;
+                    zoomOverviewScale : Math.max(mActualScale, zoomOverviewScale); 
                 setZoomScale(newScale, mUpdateTextWrap, true);
                 // update the zoom buttons as the scale can be changed
                 updateZoomPicker();
@@ -873,15 +883,22 @@
             }
         }
 
+        if (!mMinZoomScaleFixed) {
+            mMinZoomScale = newZoomOverviewScale;
+            mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale);
+        }
         // fit the content width to the current view for the first new picture
         // after first layout.
         boolean scaleHasDiff = exceedsMinScaleIncrement(newZoomOverviewScale, mActualScale);
+        // Make sure the actual scale is no less than zoom overview scale.
+        boolean scaleLessThanOverview =
+                (newZoomOverviewScale - mActualScale) >= MINIMUM_SCALE_INCREMENT;
         // Make sure mobile sites are correctly handled since mobile site will
         // change content width after rotating.
         boolean mobileSiteInOverview = mInZoomOverview &&
                 !exceedsMinScaleIncrement(newZoomOverviewScale, 1.0f);
         if (!mWebView.drawHistory() &&
-                (mInitialZoomOverview || mobileSiteInOverview) &&
+                (mInitialZoomOverview || scaleLessThanOverview || mobileSiteInOverview) &&
                 scaleHasDiff && zoomOverviewWidthChanged) {
             mInitialZoomOverview = false;
             setZoomScale(newZoomOverviewScale, !willScaleTriggerZoom(mTextWrapScale) &&
@@ -943,6 +960,10 @@
         final Point viewSize = drawData.mViewSize;
         updateZoomRange(viewState, viewSize.x, drawData.mMinPrefWidth);
         setupZoomOverviewWidth(drawData, mWebView.getViewWidth());
+        if (!mMinZoomScaleFixed) {
+            mMinZoomScale = getZoomOverviewScale();
+            mMaxZoomScale = Math.max(mMaxZoomScale, mMinZoomScale);
+        }
 
         if (!mWebView.drawHistory()) {
             float scale;
@@ -955,11 +976,10 @@
                 mTextWrapScale = viewState.mTextWrapScale;
                 scale = viewState.mViewScale;
             } else {
-                scale = mDefaultScale;
-                mTextWrapScale = mDefaultScale;
-                if (settings.getUseWideViewPort()
-                        && settings.getLoadWithOverviewMode()) {
-                    scale = Math.max(overviewScale, scale);
+                scale = overviewScale;
+                if (!settings.getUseWideViewPort()
+                    || !settings.getLoadWithOverviewMode()) {
+                    scale = Math.max(mDefaultScale, scale);
                 }
                 if (settings.isNarrowColumnLayout() &&
                     settings.getUseFixedViewport()) {
@@ -970,7 +990,7 @@
             }
             boolean reflowText = false;
             if (!viewState.mIsRestored) {
-                if (settings.getUseFixedViewport() && settings.getLoadWithOverviewMode()) {
+                if (settings.getUseFixedViewport()) {
                     // Override the scale only in case of fixed viewport.
                     scale = Math.max(scale, overviewScale);
                     mTextWrapScale = Math.max(mTextWrapScale, overviewScale);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 2925632..eca39fc 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -624,6 +624,9 @@
      */
     private boolean mForceTranscriptScroll;
 
+    private int mGlowPaddingLeft;
+    private int mGlowPaddingRight;
+
     /**
      * Interface definition for a callback to be invoked when the list or grid
      * has been scrolled.
@@ -3314,9 +3317,11 @@
             final int scrollY = mScrollY;
             if (!mEdgeGlowTop.isFinished()) {
                 final int restoreCount = canvas.save();
-                final int width = getWidth() - mListPadding.left - mListPadding.right;
+                final int leftPadding = mListPadding.left + mGlowPaddingLeft;
+                final int rightPadding = mListPadding.right + mGlowPaddingRight;
+                final int width = getWidth() - leftPadding - rightPadding;
 
-                canvas.translate(mListPadding.left,
+                canvas.translate(leftPadding,
                         Math.min(0, scrollY + mFirstPositionDistanceGuess));
                 mEdgeGlowTop.setSize(width, getHeight());
                 if (mEdgeGlowTop.draw(canvas)) {
@@ -3326,10 +3331,12 @@
             }
             if (!mEdgeGlowBottom.isFinished()) {
                 final int restoreCount = canvas.save();
-                final int width = getWidth() - mListPadding.left - mListPadding.right;
+                final int leftPadding = mListPadding.left + mGlowPaddingLeft;
+                final int rightPadding = mListPadding.right + mGlowPaddingRight;
+                final int width = getWidth() - leftPadding - rightPadding;
                 final int height = getHeight();
 
-                canvas.translate(-width + mListPadding.left,
+                canvas.translate(-width + leftPadding,
                         Math.max(height, scrollY + mLastPositionDistanceGuess));
                 canvas.rotate(180, width, 0);
                 mEdgeGlowBottom.setSize(width, height);
@@ -3353,6 +3360,14 @@
         }
     }
 
+    /**
+     * @hide
+     */
+    public void setOverScrollEffectPadding(int leftPadding, int rightPadding) {
+        mGlowPaddingLeft = leftPadding;
+        mGlowPaddingRight = rightPadding;
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         int action = ev.getAction();
diff --git a/core/java/com/android/internal/net/DomainNameValidator.java b/core/java/com/android/internal/net/DomainNameValidator.java
index dbd5019..36973f1 100644
--- a/core/java/com/android/internal/net/DomainNameValidator.java
+++ b/core/java/com/android/internal/net/DomainNameValidator.java
@@ -15,12 +15,11 @@
  */
 package com.android.internal.net;
 
-
+import android.net.NetworkUtils;
 import android.util.Config;
 import android.util.Log;
 
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
 import java.util.Collection;
@@ -38,13 +37,6 @@
     private static final boolean DEBUG = false;
     private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV;
 
-    private static Pattern QUICK_IP_PATTERN;
-    static {
-        try {
-            QUICK_IP_PATTERN = Pattern.compile("^[a-f0-9\\.:]+$");
-        } catch (PatternSyntaxException e) {}
-    }
-
     private static final int ALT_DNS_NAME = 2;
     private static final int ALT_IPA_NAME = 7;
 
@@ -75,19 +67,11 @@
         if (rval) {
             try {
                 // do a quick-dirty IP match first to avoid DNS lookup
-                rval = QUICK_IP_PATTERN.matcher(domain).matches();
-                if (rval) {
-                    rval = domain.equals(
-                        InetAddress.getByName(domain).getHostAddress());
-                }
-            } catch (UnknownHostException e) {
-                String errorMessage = e.getMessage();
-                if (errorMessage == null) {
-                  errorMessage = "unknown host exception";
-                }
-
+                rval = domain.equals(
+                        NetworkUtils.numericToInetAddress(domain).getHostAddress());
+            } catch (IllegalArgumentException e) {
                 if (LOG_ENABLED) {
-                    Log.v(TAG, "DomainNameValidator.isIpAddress(): " + errorMessage);
+                    Log.v(TAG, "DomainNameValidator.isIpAddress(): " + e);
                 }
 
                 rval = false;
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 15362eb..5deed1e 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -105,6 +105,7 @@
     // Object state.
     jfieldID mObject;
     jfieldID mSelf;
+    jfieldID mOrgue;
 
 } gBinderProxyOffsets;
 
@@ -374,12 +375,12 @@
 class JavaDeathRecipient : public IBinder::DeathRecipient
 {
 public:
-    JavaDeathRecipient(JNIEnv* env, jobject object, sp<DeathRecipientList>& list)
+    JavaDeathRecipient(JNIEnv* env, jobject object, const sp<DeathRecipientList>& list)
         : mVM(jnienv_to_javavm(env)), mObject(env->NewGlobalRef(object)), mList(list)
     {
         // These objects manage their own lifetimes so are responsible for final bookkeeping.
         // The list holds a strong reference to this object.
-        mList->add(this);
+        list->add(this);
 
         android_atomic_inc(&gNumDeathRefs);
         incRefsCreated(env);
@@ -404,7 +405,10 @@
 
     void clearReference()
     {
-        mList->remove(this);
+        sp<DeathRecipientList> list = mList.promote();
+        if (list != NULL) {
+            list->remove(this);
+        }
     }
 
     bool matches(jobject obj) {
@@ -424,7 +428,7 @@
 private:
     JavaVM* const   mVM;
     jobject const   mObject;
-    sp<DeathRecipientList> mList;
+    wp<DeathRecipientList> mList;
 };
 
 // ----------------------------------------------------------------------------
@@ -436,7 +440,7 @@
     // to the list are holding references on the list object.  Only when they are torn
     // down can the list header be destroyed.
     if (mList.size() > 0) {
-        LOGE("Retiring binder %p with extant death recipients\n", this);
+        LOGE("Retiring DRL %p with extant death recipients\n", this);
     }
 }
 
@@ -470,9 +474,6 @@
     return NULL;
 }
 
-static KeyedVector<IBinder*, sp<DeathRecipientList> > gDeathRecipientsByIBinder;
-static Mutex gDeathRecipientMapLock;
-
 // ----------------------------------------------------------------------------
 
 namespace android {
@@ -517,7 +518,7 @@
 
     object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
     if (object != NULL) {
-        LOGV("objectForBinder %p: created new %p!\n", val.get(), object);
+        LOGV("objectForBinder %p: created new proxy %p !\n", val.get(), object);
         // The proxy holds a reference to the native object.
         env->SetIntField(object, gBinderProxyOffsets.mObject, (int)val.get());
         val->incStrong(object);
@@ -529,6 +530,11 @@
         val->attachObject(&gBinderProxyOffsets, refObject,
                 jnienv_to_javavm(env), proxy_cleanup);
 
+        // Also remember the death recipients registered on this proxy
+        sp<DeathRecipientList> drl = new DeathRecipientList;
+        drl->incStrong((void*)javaObjectForIBinder);
+        env->SetIntField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jint>(drl.get()));
+
         // Note that a new object reference has been created.
         android_atomic_inc(&gNumProxyRefs);
         incRefsCreated(env);
@@ -1027,24 +1033,9 @@
     LOGV("linkToDeath: binder=%p recipient=%p\n", target, recipient);
 
     if (!target->localBinder()) {
-        sp<JavaDeathRecipient> jdr;
-
-        {
-            sp<DeathRecipientList> list;
-            AutoMutex _maplocker(gDeathRecipientMapLock);
-
-            ssize_t listIndex = gDeathRecipientsByIBinder.indexOfKey(target);
-            if (listIndex < 0) {
-                // Set up the death notice bookkeeping for this binder lazily
-                list = new DeathRecipientList;
-                gDeathRecipientsByIBinder.add(target, list);
-            } else {
-                list = gDeathRecipientsByIBinder.valueAt(listIndex);
-            }
-
-            jdr = new JavaDeathRecipient(env, recipient, list);
-        }
-
+        DeathRecipientList* list = (DeathRecipientList*)
+                env->GetIntField(obj, gBinderProxyOffsets.mOrgue);
+        sp<JavaDeathRecipient> jdr = new JavaDeathRecipient(env, recipient, list);
         status_t err = target->linkToDeath(jdr, NULL, flags);
         if (err != NO_ERROR) {
             // Failure adding the death recipient, so clear its reference
@@ -1075,20 +1066,11 @@
 
     if (!target->localBinder()) {
         status_t err = NAME_NOT_FOUND;
-        sp<JavaDeathRecipient> origJDR;
-        {
-            AutoMutex _maplocker(gDeathRecipientMapLock);
-            ssize_t listIndex = gDeathRecipientsByIBinder.indexOfKey(target);
-            if (listIndex >= 0) {
-                sp<DeathRecipientList> list = gDeathRecipientsByIBinder.valueAt(listIndex);
-                origJDR = list->find(recipient);
-            } else {
-                // If there is no DeathRecipientList for this binder, it means the binder
-                // is dead and in the process of being cleaned up.
-                err = DEAD_OBJECT;
-            }
-        }
-        // If we found the matching recipient, proceed to unlink using that
+
+        // If we find the matching recipient, proceed to unlink using that
+        DeathRecipientList* list = (DeathRecipientList*)
+                env->GetIntField(obj, gBinderProxyOffsets.mOrgue);
+        sp<JavaDeathRecipient> origJDR = list->find(recipient);
         if (origJDR != NULL) {
             wp<IBinder::DeathRecipient> dr;
             err = target->unlinkToDeath(origJDR, NULL, flags, &dr);
@@ -1115,20 +1097,17 @@
 static void android_os_BinderProxy_destroy(JNIEnv* env, jobject obj)
 {
     IBinder* b = (IBinder*)
-        env->GetIntField(obj, gBinderProxyOffsets.mObject);
-    LOGV("Destroying BinderProxy %p: binder=%p\n", obj, b);
-    env->SetIntField(obj, gBinderProxyOffsets.mObject, 0);
-    b->decStrong(obj);
-    IPCThreadState::self()->flushCommands();
+            env->GetIntField(obj, gBinderProxyOffsets.mObject);
+    DeathRecipientList* drl = (DeathRecipientList*)
+            env->GetIntField(obj, gBinderProxyOffsets.mOrgue);
 
-    // tear down the death recipient bookkeeping
-    {
-        AutoMutex _maplocker(gDeathRecipientMapLock);
-        ssize_t listIndex = gDeathRecipientsByIBinder.indexOfKey(b);
-        if (listIndex >= 0) {
-            gDeathRecipientsByIBinder.removeItemsAt((size_t)listIndex);
-        }
-    }
+    LOGV("Destroying BinderProxy %p: binder=%p drl=%p\n", obj, b, drl);
+    env->SetIntField(obj, gBinderProxyOffsets.mObject, 0);
+    env->SetIntField(obj, gBinderProxyOffsets.mOrgue, 0);
+    drl->decStrong((void*)javaObjectForIBinder);
+    b->decStrong(obj);
+
+    IPCThreadState::self()->flushCommands();
 }
 
 // ----------------------------------------------------------------------------
@@ -1178,6 +1157,9 @@
     gBinderProxyOffsets.mSelf
         = env->GetFieldID(clazz, "mSelf", "Ljava/lang/ref/WeakReference;");
     assert(gBinderProxyOffsets.mSelf);
+    gBinderProxyOffsets.mOrgue
+        = env->GetFieldID(clazz, "mOrgue", "I");
+    assert(gBinderProxyOffsets.mOrgue);
 
     return AndroidRuntime::registerNativeMethods(
         env, kBinderProxyPathName,
diff --git a/core/res/res/drawable-hdpi/ic_paste_bubble_holo.png b/core/res/res/drawable-hdpi/ic_paste_bubble_holo.png
new file mode 100644
index 0000000..15bd8b2
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_paste_bubble_holo.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png b/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png
index 2dabb5f..21e2fb9 100644
--- a/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png
+++ b/core/res/res/drawable-hdpi/list_section_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png b/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png
index 8069452..8e9c02c 100644
--- a/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png
+++ b/core/res/res/drawable-hdpi/list_section_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_edit_paste_window.9.png b/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
index 7cd000b..6654e4d 100644
--- a/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
+++ b/core/res/res/drawable-hdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_edit_side_paste_window.9.png b/core/res/res/drawable-hdpi/text_edit_side_paste_window.9.png
index 7cd000b..c564495 100644
--- a/core/res/res/drawable-hdpi/text_edit_side_paste_window.9.png
+++ b/core/res/res/drawable-hdpi/text_edit_side_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_left.png b/core/res/res/drawable-hdpi/text_select_handle_left.png
old mode 100755
new mode 100644
index 3743d91..e42a62e
--- a/core/res/res/drawable-hdpi/text_select_handle_left.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_left.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_middle.png b/core/res/res/drawable-hdpi/text_select_handle_middle.png
old mode 100755
new mode 100644
index 5a83c6c..00d47f2
--- a/core/res/res/drawable-hdpi/text_select_handle_middle.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_middle.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/text_select_handle_right.png b/core/res/res/drawable-hdpi/text_select_handle_right.png
old mode 100755
new mode 100644
index 12a3dff..7426543
--- a/core/res/res/drawable-hdpi/text_select_handle_right.png
+++ b/core/res/res/drawable-hdpi/text_select_handle_right.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_paste_bubble_holo.png b/core/res/res/drawable-mdpi/ic_paste_bubble_holo.png
new file mode 100644
index 0000000..e483e84
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_paste_bubble_holo.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png b/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png
index f873edb..b888135 100644
--- a/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png
+++ b/core/res/res/drawable-mdpi/list_section_divider_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png b/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png
index ba11cfb..1cc1f7f 100644
--- a/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png
+++ b/core/res/res/drawable-mdpi/list_section_divider_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_paste_window.9.png b/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
index 6b98c13..41886eb 100644
--- a/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
+++ b/core/res/res/drawable-mdpi/text_edit_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_edit_side_paste_window.9.png b/core/res/res/drawable-mdpi/text_edit_side_paste_window.9.png
index d87c35b..d8ae54c 100644
--- a/core/res/res/drawable-mdpi/text_edit_side_paste_window.9.png
+++ b/core/res/res/drawable-mdpi/text_edit_side_paste_window.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_left.png b/core/res/res/drawable-mdpi/text_select_handle_left.png
index 4ee2f42..959887f 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_left.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_left.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_middle.png b/core/res/res/drawable-mdpi/text_select_handle_middle.png
index 3d16052..42d4e1a 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_middle.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_middle.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/text_select_handle_right.png b/core/res/res/drawable-mdpi/text_select_handle_right.png
index 3d38928..61f9c12 100644
--- a/core/res/res/drawable-mdpi/text_select_handle_right.png
+++ b/core/res/res/drawable-mdpi/text_select_handle_right.png
Binary files differ
diff --git a/core/res/res/layout/text_edit_paste_window.xml b/core/res/res/layout/text_edit_paste_window.xml
index 575b98e..01e5530 100644
--- a/core/res/res/layout/text_edit_paste_window.xml
+++ b/core/res/res/layout/text_edit_paste_window.xml
@@ -24,8 +24,8 @@
         android:paddingLeft="16dip"
         android:paddingRight="16dip"
         android:paddingTop="8dip"
-        android:paddingBottom="8dip"
-        android:drawableLeft="@android:drawable/ic_menu_paste_light"
+        android:paddingBottom="12dip"
+        android:drawableLeft="@android:drawable/ic_paste_bubble_holo"
         android:drawablePadding="8dip"
         android:gravity="center"
         android:textAppearance="?android:attr/textAppearanceMediumInverse"
diff --git a/core/res/res/layout/text_edit_side_paste_window.xml b/core/res/res/layout/text_edit_side_paste_window.xml
index 689a039..6651786 100644
--- a/core/res/res/layout/text_edit_side_paste_window.xml
+++ b/core/res/res/layout/text_edit_side_paste_window.xml
@@ -25,7 +25,7 @@
         android:paddingRight="16dip"
         android:paddingTop="8dip"
         android:paddingBottom="8dip"
-        android:drawableLeft="@android:drawable/ic_menu_paste_light"
+        android:drawableLeft="@android:drawable/ic_paste_bubble_holo"
         android:drawablePadding="8dip"
         android:gravity="center"
         android:textAppearance="?android:attr/textAppearanceMediumInverse"
diff --git a/data/etc/android.hardware.usb.accessory.xml b/data/etc/android.hardware.usb.accessory.xml
index 29df966..80a0904 100644
--- a/data/etc/android.hardware.usb.accessory.xml
+++ b/data/etc/android.hardware.usb.accessory.xml
@@ -17,4 +17,6 @@
 <!-- This is the standard feature indicating that the device supports USB accessories. -->
 <permissions>
     <feature name="android.hardware.usb.accessory" />
+    <library name="com.android.future.usb.accessory"
+            file="/system/framework/com.android.future.usb.accessory.jar" />
 </permissions>
diff --git a/data/etc/com.google.android.usb.xml b/data/etc/com.google.android.usb.xml
deleted file mode 100644
index fae3d23..0000000
--- a/data/etc/com.google.android.usb.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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.
--->
-
-<permissions>
-    <library name="com.google.android.usb"
-            file="/system/framework/com.google.android.usb.jar" />
-</permissions>
diff --git a/drm/common/DrmSupportInfo.cpp b/drm/common/DrmSupportInfo.cpp
index ffc8953..3e02093 100644
--- a/drm/common/DrmSupportInfo.cpp
+++ b/drm/common/DrmSupportInfo.cpp
@@ -56,7 +56,7 @@
     for (unsigned int i = 0; i < mFileSuffixVector.size(); i++) {
         const String8 item = mFileSuffixVector.itemAt(i);
 
-        if (String8("") != fileType && item.find(fileType) != -1) {
+        if (item.find(fileType) != -1) {
             return true;
         }
     }
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index ef7d274..ec400b7 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -184,7 +184,10 @@
             IDrmEngine& rDrmEngine = mPlugInManager.getPlugIn(plugInId);
             result = rDrmEngine.canHandle(uniqueId, path);
         } else {
-            result = canHandle(uniqueId, path);
+            String8 extension = path.getPathExtension();
+            if (String8("") != extension) {
+                result = canHandle(uniqueId, path);
+            }
         }
     }
     return result;
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 3f9f961..b8e9384 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -17,83 +17,93 @@
 package android.graphics;
 
 public class ImageFormat {
-	/*
-	 * these constants are chosen to be binary compatible with their previous
-	 * location in PixelFormat.java
-	 */
+    /*
+     * these constants are chosen to be binary compatible with their previous
+     * location in PixelFormat.java
+     */
 
-	public static final int UNKNOWN = 0;
+    public static final int UNKNOWN = 0;
 
-	/**
-	 * RGB format used for pictures encoded as RGB_565 see
-	 * {@link android.hardware.Camera.Parameters#setPictureFormat(int)}.
-	 */
-	public static final int RGB_565 = 4;
+    /**
+     * RGB format used for pictures encoded as RGB_565 see
+     * {@link android.hardware.Camera.Parameters#setPictureFormat(int)}.
+     */
+    public static final int RGB_565 = 4;
 
-	/**
-	 * Planar 4:2:0 YCrCb format. This format assumes an horizontal stride of 16
-	 * pixels for all planes and an implicit vertical stride of the image
-	 * height's next multiple of two.
-	 *   y_size = stride * ALIGN(height, 2)
-	 *   c_size = ALIGN(stride/2, 16) * height
-	 *   size = y_size + c_size * 2
-	 *   cr_offset = y_size
-	 *   cb_offset = y_size + c_size
-	 * 
-	 * Whether this format is supported by the camera hardware can be determined
-	 * by
-	 * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
-	 */
-	public static final int YV12 = 0x32315659;
+    /**
+     * Android YUV format:
+     *
+     * This format is exposed to software decoders and applications.
+     *
+     * YV12 is a 4:2:0 YCrCb planar format comprised of a WxH Y plane followed
+     * by (W/2) x (H/2) Cr and Cb planes.
+     *
+     * This format assumes
+     * - an even width
+     * - an even height
+     * - a horizontal stride multiple of 16 pixels
+     * - a vertical stride equal to the height
+     *
+     *   y_size = stride * height
+     *   c_size = ALIGN(stride/2, 16) * height/2
+     *   size = y_size + c_size * 2
+     *   cr_offset = y_size
+     *   cb_offset = y_size + c_size
+     *
+     * Whether this format is supported by the camera hardware can be determined
+     * by
+     * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
+     */
+    public static final int YV12 = 0x32315659;
 
-	/**
-	 * YCbCr format, used for video. Whether this format is supported by the
-	 * camera hardware can be determined by
-	 * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
-	 */
-	public static final int NV16 = 0x10;
+    /**
+     * YCbCr format, used for video. Whether this format is supported by the
+     * camera hardware can be determined by
+     * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
+     */
+    public static final int NV16 = 0x10;
 
-	/**
-	 * YCrCb format used for images, which uses the NV21 encoding format. This
-	 * is the default format for camera preview images, when not otherwise set
-	 * with {@link android.hardware.Camera.Parameters#setPreviewFormat(int)}.
-	 */
-	public static final int NV21 = 0x11;
+    /**
+     * YCrCb format used for images, which uses the NV21 encoding format. This
+     * is the default format for camera preview images, when not otherwise set
+     * with {@link android.hardware.Camera.Parameters#setPreviewFormat(int)}.
+     */
+    public static final int NV21 = 0x11;
 
-	/**
-	 * YCbCr format used for images, which uses YUYV (YUY2) encoding format.
-	 * This is an alternative format for camera preview images. Whether this
-	 * format is supported by the camera hardware can be determined by
-	 * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
-	 */
-	public static final int YUY2 = 0x14;
+    /**
+     * YCbCr format used for images, which uses YUYV (YUY2) encoding format.
+     * This is an alternative format for camera preview images. Whether this
+     * format is supported by the camera hardware can be determined by
+     * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}.
+     */
+    public static final int YUY2 = 0x14;
 
-	/**
-	 * Encoded formats. These are not necessarily supported by the hardware.
-	 */
-	public static final int JPEG = 0x100;
+    /**
+     * Encoded formats. These are not necessarily supported by the hardware.
+     */
+    public static final int JPEG = 0x100;
 
-	/**
-	 * Use this function to retrieve the number of bits per pixel of an
-	 * ImageFormat.
-	 * 
-	 * @param format
-	 * @return the number of bits per pixel of the given format or -1 if the
-	 *         format doesn't exist or is not supported.
-	 */
-	public static int getBitsPerPixel(int format) {
-		switch (format) {
-		case RGB_565:
-			return 16;
-		case NV16:
-			return 16;
-		case YUY2:
-			return 16;
-		case YV12:
-			return 12;
-		case NV21:
-			return 12;
-		}
-		return -1;
-	}
+    /**
+     * Use this function to retrieve the number of bits per pixel of an
+     * ImageFormat.
+     * 
+     * @param format
+     * @return the number of bits per pixel of the given format or -1 if the
+     *         format doesn't exist or is not supported.
+     */
+    public static int getBitsPerPixel(int format) {
+        switch (format) {
+            case RGB_565:
+                return 16;
+            case NV16:
+                return 16;
+            case YUY2:
+                return 16;
+            case YV12:
+                return 12;
+            case NV21:
+                return 12;
+        }
+        return -1;
+    }
 }
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 9979e2a..b39d2e4 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -142,7 +142,8 @@
         }
 
         int count = x * y * z * faces;
-        if (hasLod && (x > 1) && (y > 1) && (z > 1)) {
+
+        while (hasLod && ((x > 1) || (y > 1) || (z > 1))) {
             if(x > 1) {
                 x >>= 1;
             }
diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h
index 4b698e6..d310cee 100644
--- a/include/media/IStreamSource.h
+++ b/include/media/IStreamSource.h
@@ -45,6 +45,12 @@
 
     virtual void queueBuffer(size_t index, size_t size) = 0;
 
+    // When signalling a discontinuity you can optionally
+    // specify an int64_t PTS timestamp in "msg".
+    // If present, rendering of data following the discontinuity
+    // will be suppressed until media time reaches this timestamp.
+    static const char *const kKeyResumeAtPTS;
+
     virtual void issueCommand(
             Command cmd, bool synchronous, const sp<AMessage> &msg = NULL) = 0;
 };
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 86ce098..082f11c 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -52,6 +52,14 @@
 };
 
 /*
+ * SystemUiVisibility constants from View.
+ */
+enum {
+    ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE = 0,
+    ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN = 0x00000001,
+};
+
+/*
  * Maximum number of pointers supported per motion event.
  * Smallest number of pointers is 1.
  * (We want at least 10 but some touch controllers obstensibly configured for 10 pointers
diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h
index 54bc968..609f319 100644
--- a/include/ui/Keyboard.h
+++ b/include/ui/Keyboard.h
@@ -127,6 +127,11 @@
  */
 extern int32_t updateMetaState(int32_t keyCode, bool down, int32_t oldMetaState);
 
+/**
+ * Returns true if a key is a meta key like ALT or CAPS_LOCK.
+ */
+extern bool isMetaKey(int32_t keyCode);
+
 } // namespace android
 
 #endif // _UI_KEYBOARD_H
diff --git a/include/utils/Looper.h b/include/utils/Looper.h
index eefff31..3c2905d 100644
--- a/include/utils/Looper.h
+++ b/include/utils/Looper.h
@@ -45,6 +45,51 @@
 namespace android {
 
 /**
+ * A message that can be posted to a Looper.
+ */
+struct Message {
+    Message() : what(0) { }
+    Message(int what) : what(what) { }
+
+    /* The message type. (interpretation is left up to the handler) */
+    int what;
+};
+
+
+/**
+ * Interface for a Looper message handler.
+ *
+ * The Looper holds a strong reference to the message handler whenever it has
+ * a message to deliver to it.  Make sure to call Looper::removeMessages
+ * to remove any pending messages destined for the handler so that the handler
+ * can be destroyed.
+ */
+class MessageHandler : public virtual RefBase {
+protected:
+    virtual ~MessageHandler() { }
+
+public:
+    /**
+     * Handles a message.
+     */
+    virtual void handleMessage(const Message& message) = 0;
+};
+
+
+/**
+ * A simple proxy that holds a weak reference to a message handler.
+ */
+class WeakMessageHandler : public MessageHandler {
+public:
+    WeakMessageHandler(const wp<MessageHandler>& handler);
+    virtual void handleMessage(const Message& message);
+
+private:
+    wp<MessageHandler> mHandler;
+};
+
+
+/**
  * A polling loop that supports monitoring file descriptor events, optionally
  * using callbacks.  The implementation uses epoll() internally.
  *
@@ -166,6 +211,52 @@
     int removeFd(int fd);
 
     /**
+     * Enqueues a message to be processed by the specified handler.
+     *
+     * The handler must not be null.
+     * This method can be called on any thread.
+     */
+    void sendMessage(const sp<MessageHandler>& handler, const Message& message);
+
+    /**
+     * Enqueues a message to be processed by the specified handler after all pending messages
+     * after the specified delay.
+     *
+     * The time delay is specified in uptime nanoseconds.
+     * The handler must not be null.
+     * This method can be called on any thread.
+     */
+    void sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
+            const Message& message);
+
+    /**
+     * Enqueues a message to be processed by the specified handler after all pending messages
+     * at the specified time.
+     *
+     * The time is specified in uptime nanoseconds.
+     * The handler must not be null.
+     * This method can be called on any thread.
+     */
+    void sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
+            const Message& message);
+
+    /**
+     * Removes all messages for the specified handler from the queue.
+     *
+     * The handler must not be null.
+     * This method can be called on any thread.
+     */
+    void removeMessages(const sp<MessageHandler>& handler);
+
+    /**
+     * Removes all messages of a particular type for the specified handler from the queue.
+     *
+     * The handler must not be null.
+     * This method can be called on any thread.
+     */
+    void removeMessages(const sp<MessageHandler>& handler, int what);
+
+    /**
      * Prepares a looper associated with the calling thread, and returns it.
      * If the thread already has a looper, it is returned.  Otherwise, a new
      * one is created, associated with the thread, and returned.
@@ -201,12 +292,27 @@
         Request request;
     };
 
+    struct MessageEnvelope {
+        MessageEnvelope() : uptime(0) { }
+
+        MessageEnvelope(nsecs_t uptime, const sp<MessageHandler> handler,
+                const Message& message) : uptime(uptime), handler(handler), message(message) {
+        }
+
+        nsecs_t uptime;
+        sp<MessageHandler> handler;
+        Message message;
+    };
+
     const bool mAllowNonCallbacks; // immutable
 
     int mWakeReadPipeFd;  // immutable
     int mWakeWritePipeFd; // immutable
     Mutex mLock;
 
+    Vector<MessageEnvelope> mMessageEnvelopes; // guarded by mLock
+    bool mSendingMessage; // guarded by mLock
+
 #ifdef LOOPER_USES_EPOLL
     int mEpollFd; // immutable
 
@@ -256,6 +362,7 @@
     // it runs on a single thread.
     Vector<Response> mResponses;
     size_t mResponseIndex;
+    nsecs_t mNextMessageUptime; // set to LLONG_MAX when none
 
     int pollInner(int timeoutMillis);
     void awoken();
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 8bae684..aa9b40e 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -40,9 +40,9 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
-        int flags, uint32_t italicStyle) :
+        int flags, uint32_t italicStyle, uint32_t scaleX) :
         mState(state), mFontId(fontId), mFontSize(fontSize),
-        mFlags(flags), mItalicStyle(italicStyle) {
+        mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX) {
 }
 
 
@@ -279,18 +279,19 @@
 }
 
 Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
-        int flags, uint32_t italicStyle) {
+        int flags, uint32_t italicStyle, uint32_t scaleX) {
     Vector<Font*> &activeFonts = state->mActiveFonts;
 
     for (uint32_t i = 0; i < activeFonts.size(); i++) {
         Font* font = activeFonts[i];
         if (font->mFontId == fontId && font->mFontSize == fontSize &&
-                font->mFlags == flags && font->mItalicStyle == italicStyle) {
+                font->mFlags == flags && font->mItalicStyle == italicStyle &&
+                font->mScaleX == scaleX) {
             return font;
         }
     }
 
-    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle);
+    Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle, scaleX);
     activeFonts.push(newFont);
     return newFont;
 }
@@ -657,7 +658,9 @@
 
     const float skewX = paint->getTextSkewX();
     uint32_t italicStyle = *(uint32_t*) &skewX;
-    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle);
+    const float scaleXFloat = paint->getTextScaleX();
+    uint32_t scaleX = *(uint32_t*) &scaleXFloat;
+    mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle, scaleX);
 
     const float maxPrecacheFontSize = 40.0f;
     bool isNewFont = currentNumFonts != mActiveFonts.size();
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 46f332e..3a7aa96 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -58,7 +58,7 @@
      * Creates a new font associated with the specified font state.
      */
     static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
-            int flags, uint32_t italicStyle);
+            int flags, uint32_t italicStyle, uint32_t scaleX);
 
 protected:
     friend class FontRenderer;
@@ -104,7 +104,8 @@
         SkFixed mRsbDelta;
     };
 
-    Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle);
+    Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
+            uint32_t scaleX);
 
     DefaultKeyedVector<int32_t, CachedGlyphInfo*> mCachedGlyphs;
 
@@ -124,6 +125,7 @@
     float mFontSize;
     int mFlags;
     uint32_t mItalicStyle;
+    uint32_t mScaleX;
 };
 
 class FontRenderer {
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 24f9739..b1eb164 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -214,6 +214,11 @@
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
             layer->texture, 0);
 
+    glDisable(GL_SCISSOR_TEST);
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+    glEnable(GL_SCISSOR_TEST);
+
     glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
 
     return layer;
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 361815a..dfca7eb 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -144,6 +144,8 @@
 
     mSnapshot = new Snapshot(mFirstSnapshot,
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    mSnapshot->fbo = getTargetFbo();
+
     mSaveCount = 1;
 
     glViewport(0, 0, mWidth, mHeight);
diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp
index 8b6300a..600a951 100644
--- a/libs/ui/Keyboard.cpp
+++ b/libs/ui/Keyboard.cpp
@@ -322,5 +322,26 @@
     }
 }
 
+bool isMetaKey(int32_t keyCode) {
+    switch (keyCode) {
+    case AKEYCODE_ALT_LEFT:
+    case AKEYCODE_ALT_RIGHT:
+    case AKEYCODE_SHIFT_LEFT:
+    case AKEYCODE_SHIFT_RIGHT:
+    case AKEYCODE_SYM:
+    case AKEYCODE_FUNCTION:
+    case AKEYCODE_CTRL_LEFT:
+    case AKEYCODE_CTRL_RIGHT:
+    case AKEYCODE_META_LEFT:
+    case AKEYCODE_META_RIGHT:
+    case AKEYCODE_CAPS_LOCK:
+    case AKEYCODE_NUM_LOCK:
+    case AKEYCODE_SCROLL_LOCK:
+        return true;
+    default:
+        return false;
+    }
+}
+
 
 } // namespace android
diff --git a/libs/usb/Android.mk b/libs/usb/Android.mk
index d0ef6f0..b4e1fbf 100644
--- a/libs/usb/Android.mk
+++ b/libs/usb/Android.mk
@@ -22,6 +22,6 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_MODULE:= com.google.android.usb
+LOCAL_MODULE:= com.android.future.usb.accessory
 
 include $(BUILD_JAVA_LIBRARY)
diff --git a/libs/usb/src/com/google/android/usb/UsbAccessory.java b/libs/usb/src/com/android/future/usb/UsbAccessory.java
similarity index 98%
rename from libs/usb/src/com/google/android/usb/UsbAccessory.java
rename to libs/usb/src/com/android/future/usb/UsbAccessory.java
index 931f42e..cdd2b73 100644
--- a/libs/usb/src/com/google/android/usb/UsbAccessory.java
+++ b/libs/usb/src/com/android/future/usb/UsbAccessory.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.android.usb;
+package com.android.future.usb;
 
 /**
  * A class representing a USB accessory.
diff --git a/libs/usb/src/com/google/android/usb/UsbManager.java b/libs/usb/src/com/android/future/usb/UsbManager.java
similarity index 98%
rename from libs/usb/src/com/google/android/usb/UsbManager.java
rename to libs/usb/src/com/android/future/usb/UsbManager.java
index d7afb95..f74b291 100644
--- a/libs/usb/src/com/google/android/usb/UsbManager.java
+++ b/libs/usb/src/com/android/future/usb/UsbManager.java
@@ -15,7 +15,7 @@
  */
 
 
-package com.google.android.usb;
+package com.android.future.usb;
 
 import android.content.Context;
 import android.content.Intent;
diff --git a/libs/usb/tests/AccessoryChat/Android.mk b/libs/usb/tests/AccessoryChat/Android.mk
index b854569..98b6090 100644
--- a/libs/usb/tests/AccessoryChat/Android.mk
+++ b/libs/usb/tests/AccessoryChat/Android.mk
@@ -7,7 +7,7 @@
 
 LOCAL_PACKAGE_NAME := AccessoryChatGB
 
-LOCAL_JAVA_LIBRARIES := com.google.android.usb
+LOCAL_JAVA_LIBRARIES := com.android.future.usb.accessory
 
 # Force an old SDK version to make sure we aren't using newer UsbManager APIs
 LOCAL_SDK_VERSION := 8
diff --git a/libs/usb/tests/AccessoryChat/AndroidManifest.xml b/libs/usb/tests/AccessoryChat/AndroidManifest.xml
index 5922421..97e2ade 100644
--- a/libs/usb/tests/AccessoryChat/AndroidManifest.xml
+++ b/libs/usb/tests/AccessoryChat/AndroidManifest.xml
@@ -1,8 +1,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.google.android.accessorychat">
+        package="com.android.accessorychat">
 
     <application>
-        <uses-library android:name="com.google.android.usb" />
+        <uses-library android:name="com.android.future.usb.accessory" />
 
         <activity android:name="AccessoryChat" android:label="Accessory Chat GB">
             <intent-filter>
@@ -19,5 +19,5 @@
                 android:resource="@xml/accessory_filter" />
         </activity>
     </application>
-    <uses-sdk android:minSdkVersion="8" />
+    <uses-sdk android:minSdkVersion="10" />
 </manifest>
diff --git a/libs/usb/tests/AccessoryChat/src/com/google/android/accessorychat/AccessoryChat.java b/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
similarity index 97%
rename from libs/usb/tests/AccessoryChat/src/com/google/android/accessorychat/AccessoryChat.java
rename to libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
index 0a5701c..5cf02c7 100644
--- a/libs/usb/tests/AccessoryChat/src/com/google/android/accessorychat/AccessoryChat.java
+++ b/libs/usb/tests/AccessoryChat/src/com/android/accessorychat/AccessoryChat.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.android.accessorychat;
+package com.android.accessorychat;
 
 import android.app.Activity;
 import android.content.BroadcastReceiver;
@@ -31,8 +31,8 @@
 import android.widget.EditText;
 import android.widget.TextView;
 
-import com.google.android.usb.UsbAccessory;
-import com.google.android.usb.UsbManager;
+import com.android.future.usb.UsbAccessory;
+import com.android.future.usb.UsbManager;
 
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
diff --git a/libs/utils/Looper.cpp b/libs/utils/Looper.cpp
index a5363d6..18f858b 100644
--- a/libs/utils/Looper.cpp
+++ b/libs/utils/Looper.cpp
@@ -19,10 +19,27 @@
 
 #include <unistd.h>
 #include <fcntl.h>
+#include <limits.h>
 
 
 namespace android {
 
+// --- WeakMessageHandler ---
+
+WeakMessageHandler::WeakMessageHandler(const wp<MessageHandler>& handler) :
+        mHandler(handler) {
+}
+
+void WeakMessageHandler::handleMessage(const Message& message) {
+    sp<MessageHandler> handler = mHandler.promote();
+    if (handler != NULL) {
+        handler->handleMessage(message);
+    }
+}
+
+
+// --- Looper ---
+
 #ifdef LOOPER_USES_EPOLL
 // Hint for number of file descriptors to be associated with the epoll instance.
 static const int EPOLL_SIZE_HINT = 8;
@@ -35,8 +52,8 @@
 static pthread_key_t gTLSKey = 0;
 
 Looper::Looper(bool allowNonCallbacks) :
-        mAllowNonCallbacks(allowNonCallbacks),
-        mResponseIndex(0) {
+        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
+        mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
     int wakeFds[2];
     int result = pipe(wakeFds);
     LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
@@ -161,17 +178,21 @@
     for (;;) {
         while (mResponseIndex < mResponses.size()) {
             const Response& response = mResponses.itemAt(mResponseIndex++);
-            if (! response.request.callback) {
+            ALooper_callbackFunc callback = response.request.callback;
+            if (!callback) {
+                int ident = response.request.ident;
+                int fd = response.request.fd;
+                int events = response.events;
+                void* data = response.request.data;
 #if DEBUG_POLL_AND_WAKE
                 LOGD("%p ~ pollOnce - returning signalled identifier %d: "
-                        "fd=%d, events=0x%x, data=%p", this,
-                        response.request.ident, response.request.fd,
-                        response.events, response.request.data);
+                        "fd=%d, events=0x%x, data=%p",
+                        this, ident, fd, events, data);
 #endif
-                if (outFd != NULL) *outFd = response.request.fd;
-                if (outEvents != NULL) *outEvents = response.events;
-                if (outData != NULL) *outData = response.request.data;
-                return response.request.ident;
+                if (outFd != NULL) *outFd = fd;
+                if (outEvents != NULL) *outEvents = events;
+                if (outData != NULL) *outData = data;
+                return ident;
             }
         }
 
@@ -194,6 +215,25 @@
     LOGD("%p ~ pollOnce - waiting: timeoutMillis=%d", this, timeoutMillis);
 #endif
 
+    // Adjust the timeout based on when the next message is due.
+    if (timeoutMillis != 0 && mNextMessageUptime != LLONG_MAX) {
+        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+        if (mNextMessageUptime <= now) {
+            timeoutMillis = 0;
+        } else {
+            uint64_t delay = (mNextMessageUptime - now + 999999LL) / 1000000LL;
+            if (delay < INT_MAX
+                    && (timeoutMillis < 0 || int(delay) < timeoutMillis)) {
+                timeoutMillis = int(delay);
+            }
+        }
+#if DEBUG_POLL_AND_WAKE
+        LOGD("%p ~ pollOnce - next message in %lldns, adjusted timeout: timeoutMillis=%d",
+                this, mNextMessageUptime - now, timeoutMillis);
+#endif
+    }
+
+    // Poll.
     int result = ALOOPER_POLL_WAKE;
     mResponses.clear();
     mResponseIndex = 0;
@@ -205,7 +245,6 @@
 #ifdef LOOPER_USES_EPOLL
     struct epoll_event eventItems[EPOLL_MAX_EVENTS];
     int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
-    bool acquiredLock = false;
 #else
     // Wait for wakeAndLock() waiters to run then set mPolling to true.
     mLock.lock();
@@ -219,16 +258,20 @@
     int eventCount = poll(mRequestedFds.editArray(), requestedCount, timeoutMillis);
 #endif
 
+    // Acquire lock.
+    mLock.lock();
+
+    // Check for poll error.
     if (eventCount < 0) {
         if (errno == EINTR) {
             goto Done;
         }
-
         LOGW("Poll failed with an unexpected error, errno=%d", errno);
         result = ALOOPER_POLL_ERROR;
         goto Done;
     }
 
+    // Check for poll timeout.
     if (eventCount == 0) {
 #if DEBUG_POLL_AND_WAKE
         LOGD("%p ~ pollOnce - timeout", this);
@@ -237,6 +280,7 @@
         goto Done;
     }
 
+    // Handle all events.
 #if DEBUG_POLL_AND_WAKE
     LOGD("%p ~ pollOnce - handling events from %d fds", this, eventCount);
 #endif
@@ -252,11 +296,6 @@
                 LOGW("Ignoring unexpected epoll events 0x%x on wake read pipe.", epollEvents);
             }
         } else {
-            if (! acquiredLock) {
-                mLock.lock();
-                acquiredLock = true;
-            }
-
             ssize_t requestIndex = mRequests.indexOfKey(fd);
             if (requestIndex >= 0) {
                 int events = 0;
@@ -271,9 +310,6 @@
             }
         }
     }
-    if (acquiredLock) {
-        mLock.unlock();
-    }
 Done: ;
 #else
     for (size_t i = 0; i < requestedCount; i++) {
@@ -301,15 +337,12 @@
             }
         }
     }
-
 Done:
     // Set mPolling to false and wake up the wakeAndLock() waiters.
-    mLock.lock();
     mPolling = false;
     if (mWaiters != 0) {
         mAwake.broadcast();
     }
-    mLock.unlock();
 #endif
 
 #ifdef LOOPER_STATISTICS
@@ -335,19 +368,59 @@
     }
 #endif
 
+    // Invoke pending message callbacks.
+    mNextMessageUptime = LLONG_MAX;
+    while (mMessageEnvelopes.size() != 0) {
+        nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+        const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(0);
+        if (messageEnvelope.uptime <= now) {
+            // Remove the envelope from the list.
+            // We keep a strong reference to the handler until the call to handleMessage
+            // finishes.  Then we drop it so that the handler can be deleted *before*
+            // we reacquire our lock.
+            { // obtain handler
+                sp<MessageHandler> handler = messageEnvelope.handler;
+                Message message = messageEnvelope.message;
+                mMessageEnvelopes.removeAt(0);
+                mSendingMessage = true;
+                mLock.unlock();
+
+#if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
+                LOGD("%p ~ pollOnce - sending message: handler=%p, what=%d",
+                        this, handler.get(), message.what);
+#endif
+                handler->handleMessage(message);
+            } // release handler
+
+            mLock.lock();
+            mSendingMessage = false;
+            result = ALOOPER_POLL_CALLBACK;
+        } else {
+            // The last message left at the head of the queue determines the next wakeup time.
+            mNextMessageUptime = messageEnvelope.uptime;
+            break;
+        }
+    }
+
+    // Release lock.
+    mLock.unlock();
+
+    // Invoke all response callbacks.
     for (size_t i = 0; i < mResponses.size(); i++) {
         const Response& response = mResponses.itemAt(i);
-        if (response.request.callback) {
+        ALooper_callbackFunc callback = response.request.callback;
+        if (callback) {
+            int fd = response.request.fd;
+            int events = response.events;
+            void* data = response.request.data;
 #if DEBUG_POLL_AND_WAKE || DEBUG_CALLBACKS
-            LOGD("%p ~ pollOnce - invoking callback: fd=%d, events=0x%x, data=%p", this,
-                    response.request.fd, response.events, response.request.data);
+            LOGD("%p ~ pollOnce - invoking fd event callback %p: fd=%d, events=0x%x, data=%p",
+                    this, callback, fd, events, data);
 #endif
-            int callbackResult = response.request.callback(
-                    response.request.fd, response.events, response.request.data);
+            int callbackResult = callback(fd, events, data);
             if (callbackResult == 0) {
-                removeFd(response.request.fd);
+                removeFd(fd);
             }
-
             result = ALOOPER_POLL_CALLBACK;
         }
     }
@@ -593,4 +666,83 @@
 }
 #endif
 
+void Looper::sendMessage(const sp<MessageHandler>& handler, const Message& message) {
+    sendMessageAtTime(LLONG_MIN, handler, message);
+}
+
+void Looper::sendMessageDelayed(nsecs_t uptimeDelay, const sp<MessageHandler>& handler,
+        const Message& message) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    sendMessageAtTime(now + uptimeDelay, handler, message);
+}
+
+void Looper::sendMessageAtTime(nsecs_t uptime, const sp<MessageHandler>& handler,
+        const Message& message) {
+#if DEBUG_CALLBACKS
+    LOGD("%p ~ sendMessageAtTime - uptime=%lld, handler=%p, what=%d",
+            this, uptime, handler.get(), message.what);
+#endif
+
+    size_t i = 0;
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        size_t messageCount = mMessageEnvelopes.size();
+        while (i < messageCount && uptime >= mMessageEnvelopes.itemAt(i).uptime) {
+            i += 1;
+        }
+
+        MessageEnvelope messageEnvelope(uptime, handler, message);
+        mMessageEnvelopes.insertAt(messageEnvelope, i, 1);
+
+        // Optimization: If the Looper is currently sending a message, then we can skip
+        // the call to wake() because the next thing the Looper will do after processing
+        // messages is to decide when the next wakeup time should be.  In fact, it does
+        // not even matter whether this code is running on the Looper thread.
+        if (mSendingMessage) {
+            return;
+        }
+    } // release lock
+
+    // Wake the poll loop only when we enqueue a new message at the head.
+    if (i == 0) {
+        wake();
+    }
+}
+
+void Looper::removeMessages(const sp<MessageHandler>& handler) {
+#if DEBUG_CALLBACKS
+    LOGD("%p ~ removeMessages - handler=%p", this, handler.get());
+#endif
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
+            const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
+            if (messageEnvelope.handler == handler) {
+                mMessageEnvelopes.removeAt(i);
+            }
+        }
+    } // release lock
+}
+
+void Looper::removeMessages(const sp<MessageHandler>& handler, int what) {
+#if DEBUG_CALLBACKS
+    LOGD("%p ~ removeMessages - handler=%p, what=%d", this, handler.get(), what);
+#endif
+
+    { // acquire lock
+        AutoMutex _l(mLock);
+
+        for (size_t i = mMessageEnvelopes.size(); i != 0; ) {
+            const MessageEnvelope& messageEnvelope = mMessageEnvelopes.itemAt(--i);
+            if (messageEnvelope.handler == handler
+                    && messageEnvelope.message.what == what) {
+                mMessageEnvelopes.removeAt(i);
+            }
+        }
+    } // release lock
+}
+
 } // namespace android
diff --git a/libs/utils/tests/Looper_test.cpp b/libs/utils/tests/Looper_test.cpp
index cea1313..8bf2ba2 100644
--- a/libs/utils/tests/Looper_test.cpp
+++ b/libs/utils/tests/Looper_test.cpp
@@ -16,6 +16,13 @@
 
 namespace android {
 
+enum {
+    MSG_TEST1 = 1,
+    MSG_TEST2 = 2,
+    MSG_TEST3 = 3,
+    MSG_TEST4 = 4,
+};
+
 class DelayedWake : public DelayedTask {
     sp<Looper> mLooper;
 
@@ -82,6 +89,15 @@
     }
 };
 
+class StubMessageHandler : public MessageHandler {
+public:
+    Vector<Message> messages;
+
+    virtual void handleMessage(const Message& message) {
+        messages.push(message);
+    }
+};
+
 class LooperTest : public testing::Test {
 protected:
     sp<Looper> mLooper;
@@ -421,5 +437,257 @@
             << "replacement handler callback should be invoked";
 }
 
+TEST_F(LooperTest, SendMessage_WhenOneMessageIsEnqueue_ShouldInvokeHandlerDuringNextPoll) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessage(handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessage_WhenMultipleMessagesAreEnqueued_ShouldInvokeHandlersInOrderDuringNextPoll) {
+    sp<StubMessageHandler> handler1 = new StubMessageHandler();
+    sp<StubMessageHandler> handler2 = new StubMessageHandler();
+    mLooper->sendMessage(handler1, Message(MSG_TEST1));
+    mLooper->sendMessage(handler2, Message(MSG_TEST2));
+    mLooper->sendMessage(handler1, Message(MSG_TEST3));
+    mLooper->sendMessage(handler1, Message(MSG_TEST4));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(3), handler1->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler1->messages[0].what)
+            << "handled message";
+    EXPECT_EQ(MSG_TEST3, handler1->messages[1].what)
+            << "handled message";
+    EXPECT_EQ(MSG_TEST4, handler1->messages[2].what)
+            << "handled message";
+    EXPECT_EQ(size_t(1), handler2->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST2, handler2->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageDelayed(ms2ns(100), handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "first poll should end quickly because next message timeout was computed";
+    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+            << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup";
+    EXPECT_EQ(size_t(0), handler->messages.size())
+            << "no message handled yet";
+
+    result = mLooper->pollOnce(1000);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "second poll should end around the time of the delayed message dispatch";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+
+    result = mLooper->pollOnce(100);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "third poll should timeout";
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageDelayed(ms2ns(-1000), handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageDelayed_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageDelayed(0, handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToTheFuture_ShouldInvokeHandlerAfterDelayTime) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageAtTime(now + ms2ns(100), handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(1000);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "first poll should end quickly because next message timeout was computed";
+    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+            << "pollOnce result should be ALOOPER_POLL_WAKE due to wakeup";
+    EXPECT_EQ(size_t(0), handler->messages.size())
+            << "no message handled yet";
+
+    result = mLooper->pollOnce(1000);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+    EXPECT_NEAR(100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "second poll should end around the time of the delayed message dispatch";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+
+    result = mLooper->pollOnce(100);
+    elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(100 + 100, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "third poll should timeout";
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there were no messages left";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePast_ShouldInvokeHandlerDuringNextPoll) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageAtTime(now - ms2ns(1000), handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, SendMessageAtTime_WhenSentToThePresent_ShouldInvokeHandlerDuringNextPoll) {
+    nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessageAtTime(now, handler, Message(MSG_TEST1));
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(100);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was already sent";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because message was sent";
+    EXPECT_EQ(size_t(1), handler->messages.size())
+            << "handled message";
+    EXPECT_EQ(MSG_TEST1, handler->messages[0].what)
+            << "handled message";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingAllMessagesForHandler_ShouldRemoveThoseMessage) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessage(handler, Message(MSG_TEST1));
+    mLooper->sendMessage(handler, Message(MSG_TEST2));
+    mLooper->sendMessage(handler, Message(MSG_TEST3));
+    mLooper->removeMessages(handler);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was sent so looper was awoken";
+    EXPECT_EQ(ALOOPER_POLL_WAKE, result)
+            << "pollOnce result should be ALOOPER_POLL_WAKE because looper was awoken";
+    EXPECT_EQ(size_t(0), handler->messages.size())
+            << "no messages to handle";
+
+    result = mLooper->pollOnce(0);
+
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do";
+    EXPECT_EQ(size_t(0), handler->messages.size())
+            << "no messages to handle";
+}
+
+TEST_F(LooperTest, RemoveMessage_WhenRemovingSomeMessagesForHandler_ShouldRemoveThoseMessage) {
+    sp<StubMessageHandler> handler = new StubMessageHandler();
+    mLooper->sendMessage(handler, Message(MSG_TEST1));
+    mLooper->sendMessage(handler, Message(MSG_TEST2));
+    mLooper->sendMessage(handler, Message(MSG_TEST3));
+    mLooper->sendMessage(handler, Message(MSG_TEST4));
+    mLooper->removeMessages(handler, MSG_TEST3);
+    mLooper->removeMessages(handler, MSG_TEST1);
+
+    StopWatch stopWatch("pollOnce");
+    int result = mLooper->pollOnce(0);
+    int32_t elapsedMillis = ns2ms(stopWatch.elapsedTime());
+
+    EXPECT_NEAR(0, elapsedMillis, TIMING_TOLERANCE_MS)
+            << "elapsed time should approx. zero because message was sent so looper was awoken";
+    EXPECT_EQ(ALOOPER_POLL_CALLBACK, result)
+            << "pollOnce result should be ALOOPER_POLL_CALLBACK because two messages were sent";
+    EXPECT_EQ(size_t(2), handler->messages.size())
+            << "no messages to handle";
+    EXPECT_EQ(MSG_TEST2, handler->messages[0].what)
+            << "handled message";
+    EXPECT_EQ(MSG_TEST4, handler->messages[1].what)
+            << "handled message";
+
+    result = mLooper->pollOnce(0);
+
+    EXPECT_EQ(ALOOPER_POLL_TIMEOUT, result)
+            << "pollOnce result should be ALOOPER_POLL_TIMEOUT because there was nothing to do";
+    EXPECT_EQ(size_t(2), handler->messages.size())
+            << "no more messages to handle";
+}
 
 } // namespace android
diff --git a/media/java/android/mtp/MtpClient.java b/media/java/android/mtp/MtpClient.java
index 6c8b228..c4ee19e 100644
--- a/media/java/android/mtp/MtpClient.java
+++ b/media/java/android/mtp/MtpClient.java
@@ -163,15 +163,6 @@
         mContext.unregisterReceiver(mUsbReceiver);
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-
     /**
      * Registers a {@link android.mtp.MtpClient.Listener} interface to receive
      * notifications when MTP or PTP devices are added or removed.
diff --git a/media/jni/mediaeditor/VideoBrowserMain.c b/media/jni/mediaeditor/VideoBrowserMain.c
index bb13fba..caf4497 100755
--- a/media/jni/mediaeditor/VideoBrowserMain.c
+++ b/media/jni/mediaeditor/VideoBrowserMain.c
@@ -206,8 +206,8 @@
                 pContext->m_pReaderCtx, &mediaFamily, &pStreamHandler);
 
         /*in case we found a bifs stream or something else...*/
-        if ((err == M4ERR_READER_UNKNOWN_STREAM_TYPE) ||
-            (err == M4WAR_TOO_MUCH_STREAMS))
+        if ((err == (M4OSA_UInt32)M4ERR_READER_UNKNOWN_STREAM_TYPE) ||
+            (err == (M4OSA_UInt32)M4WAR_TOO_MUCH_STREAMS))
         {
             err = M4NO_ERROR;
             continue;
@@ -251,7 +251,7 @@
                             &decoderType, &pContext->m_pDecoder);
 #else
                         err = VideoEditorVideoDecoder_getInterface_MPEG4(
-                            &decoderType, &pContext->m_pDecoder);
+                            &decoderType, (void **)&pContext->m_pDecoder);
 #endif
                     CHECK_ERR(videoBrowserCreate, err) ;
 
@@ -277,7 +277,7 @@
                             &decoderType, &pContext->m_pDecoder);
 #else
                         err = VideoEditorVideoDecoder_getInterface_H264(
-                            &decoderType, &pContext->m_pDecoder);
+                            &decoderType, (void **)&pContext->m_pDecoder);
 #endif
                    CHECK_ERR(videoBrowserCreate, err) ;
 
diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp
index 8a35041..ea73e11 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.cpp
+++ b/media/jni/mediaeditor/VideoEditorClasses.cpp
@@ -896,8 +896,9 @@
                 jobject                             object,
                 M4xVSS_AlphaMagicSettings**         ppSettings)
 {
-    VideoEditJava_AlphaMagicFieldIds   fieldIds  = {NULL, NULL, NULL, NULL, NULL};
+    VideoEditJava_AlphaMagicFieldIds fieldIds;
     M4xVSS_AlphaMagicSettings* pSettings = M4OSA_NULL;
+    memset(&fieldIds, 0, sizeof(VideoEditJava_AlphaMagicFieldIds));
 
     // Check if the previous action succeeded.
     if (*pResult)
@@ -1036,11 +1037,10 @@
                 jobject                             object,
                 M4xVSS_BGMSettings**                ppSettings)
 {
-    VideoEditJava_BackgroundMusicFieldIds fieldIds  = {NULL, NULL, NULL, NULL,
-                                                       NULL, NULL,NULL,NULL,NULL,NULL};
+    VideoEditJava_BackgroundMusicFieldIds fieldIds;
     M4xVSS_BGMSettings*           pSettings = M4OSA_NULL;
     bool                          converted = true;
-
+    memset(&fieldIds, 0, sizeof(VideoEditJava_BackgroundMusicFieldIds));
     // Check if the previous action succeeded.
     if (*pResult)
     {
@@ -1324,12 +1324,12 @@
                 jobject                             object,
                 M4VSS3GPP_ClipSettings**            ppSettings)
 {
-    VideoEditJava_ClipSettingsFieldIds fieldIds  = {NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                             NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+    VideoEditJava_ClipSettingsFieldIds fieldIds;
     M4VSS3GPP_ClipSettings*    pSettings = M4OSA_NULL;
     M4OSA_ERR                  result    = M4NO_ERROR;
     bool                       converted = true;
-
+    memset(&fieldIds, 0, sizeof(VideoEditJava_ClipSettingsFieldIds));
     // Check if the previous action succeeded.
     if (*pResult)
     {
@@ -1500,10 +1500,10 @@
                 M4VSS3GPP_ClipSettings*             pSettings,
                 jobject*                            pObject)
 {
-    VideoEditJava_ClipSettingsFieldIds fieldIds = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                           NULL, NULL, NULL, NULL, NULL, NULL};
+    VideoEditJava_ClipSettingsFieldIds fieldIds;
     jclass                     clazz    = NULL;
     jobject                    object   = NULL;
+    memset(&fieldIds, 0, sizeof(VideoEditJava_ClipSettingsFieldIds));
 
     // Check if the previous action succeeded.
     if (*pResult)
@@ -1600,11 +1600,10 @@
                 VideoEditPropClass_Properties*      pProperties,
                 jobject*                            pObject)
 {
-    VideoEditJava_PropertiesFieldIds fieldIds = {NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+    VideoEditJava_PropertiesFieldIds fieldIds;
     jclass                   clazz    = NULL;
     jobject                  object   = NULL;
-
+    memset(&fieldIds, 0, sizeof(VideoEditJava_PropertiesFieldIds));
     // Check if the previous action succeeded.
     if (*pResult)
     {
@@ -1783,9 +1782,7 @@
                 M4VSS3GPP_EditSettings**            ppSettings,
                 bool                                flag)
 {
-    VideoEditJava_EditSettingsFieldIds fieldIds            ={NULL, NULL, NULL, NULL, NULL, NULL,
-                                                            NULL, NULL, NULL, NULL, NULL, NULL,
-                                                            NULL, NULL,NULL};
+    VideoEditJava_EditSettingsFieldIds fieldIds;
     jobjectArray               clipSettingsArray           = NULL;
     jsize                      clipSettingsArraySize       = 0;
     jobject                    clipSettings                = NULL;
@@ -1799,7 +1796,7 @@
     int                        audioChannels               = 0;
     M4VSS3GPP_EditSettings*    pSettings                   = M4OSA_NULL;
     bool                       converted                   = true;
-
+    memset(&fieldIds, 0, sizeof(VideoEditJava_EditSettingsFieldIds));
     // Check if the previous action succeeded.
     if (*pResult)
     {
@@ -2304,10 +2301,10 @@
                 jobject                             object,
                 M4VSS3GPP_EffectSettings*           pSettings)
 {
-    VideoEditJava_EffectSettingsFieldIds fieldIds  = {NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
-                                  NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+    VideoEditJava_EffectSettingsFieldIds fieldIds;
     bool                         converted = true;
+    memset(&fieldIds, 0, sizeof(VideoEditJava_EffectSettingsFieldIds));
 
     // Check if the previous action succeeded.
     if (*pResult)
@@ -2736,11 +2733,13 @@
                 jobject                             object,
                 M4VSS3GPP_TransitionSettings**      ppSettings)
 {
-    VideoEditJava_TransitionSettingsFieldIds fieldIds      = {NULL, NULL, NULL, NULL, NULL, NULL};
+
+    VideoEditJava_TransitionSettingsFieldIds fieldIds;
     jobject                          alphaSettings = NULL;
     jobject                          slideSettings = NULL;
     M4VSS3GPP_TransitionSettings*    pSettings     = M4OSA_NULL;
     bool                             converted     = true;
+    memset(&fieldIds, 0, sizeof(VideoEditJava_TransitionSettingsFieldIds));
 
     // Check if the previous action succeeded.
     if (*pResult)
@@ -3025,10 +3024,11 @@
                 M4_VersionInfo*                     pVersionInfo,
                 jobject*                            pObject)
 {
-    VideoEditJava_VersionFieldIds fieldIds = {NULL, NULL, NULL};
+
+    VideoEditJava_VersionFieldIds fieldIds;
     jclass                clazz    = NULL;
     jobject               object   = NULL;
-
+    memset(&fieldIds, 0, sizeof(VideoEditJava_VersionFieldIds));
     // Check if the previous action succeeded.
     if (*pResult)
     {
diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp
index 5069002..c14ee82 100644
--- a/media/libmedia/IStreamSource.cpp
+++ b/media/libmedia/IStreamSource.cpp
@@ -26,6 +26,9 @@
 
 namespace android {
 
+// static
+const char *const IStreamListener::kKeyResumeAtPTS = "resume-at-PTS";
+
 enum {
     // IStreamSource
     SET_LISTENER = IBinder::FIRST_CALL_TRANSACTION,
diff --git a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
index b3314be..d07ea1b 100644
--- a/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/HTTPLiveSource.cpp
@@ -96,10 +96,12 @@
         } else {
             if (buffer[0] == 0x00) {
                 // XXX legacy
+                sp<AMessage> extra;
                 mTSParser->signalDiscontinuity(
                         buffer[1] == 0x00
                             ? ATSParser::DISCONTINUITY_SEEK
-                            : ATSParser::DISCONTINUITY_FORMATCHANGE);
+                            : ATSParser::DISCONTINUITY_FORMATCHANGE,
+                        extra);
             } else {
                 mTSParser->feedTSPacket(buffer, sizeof(buffer));
             }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 474c056..d439f6e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -191,6 +191,8 @@
 
             mAudioEOS = false;
             mVideoEOS = false;
+            mSkipRenderingAudioUntilMediaTimeUs = -1;
+            mSkipRenderingVideoUntilMediaTimeUs = -1;
 
             mSource->start();
 
@@ -592,6 +594,31 @@
             LOGV("%s discontinuity (formatChange=%d)",
                  audio ? "audio" : "video", formatChange);
 
+            if (audio) {
+                mSkipRenderingAudioUntilMediaTimeUs = -1;
+            } else {
+                mSkipRenderingVideoUntilMediaTimeUs = -1;
+            }
+
+            sp<AMessage> extra;
+            if (accessUnit->meta()->findMessage("extra", &extra)
+                    && extra != NULL) {
+                int64_t resumeAtMediaTimeUs;
+                if (extra->findInt64(
+                            "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) {
+                    LOGI("suppressing rendering of %s until %lld us",
+                            audio ? "audio" : "video", resumeAtMediaTimeUs);
+
+                    if (audio) {
+                        mSkipRenderingAudioUntilMediaTimeUs =
+                            resumeAtMediaTimeUs;
+                    } else {
+                        mSkipRenderingVideoUntilMediaTimeUs =
+                            resumeAtMediaTimeUs;
+                    }
+                }
+            }
+
             flushDecoder(audio, formatChange);
         }
 
@@ -627,6 +654,27 @@
 
     sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
 
+    int64_t &skipUntilMediaTimeUs =
+        audio
+            ? mSkipRenderingAudioUntilMediaTimeUs
+            : mSkipRenderingVideoUntilMediaTimeUs;
+
+    if (skipUntilMediaTimeUs >= 0) {
+        int64_t mediaTimeUs;
+        CHECK(buffer->meta()->findInt64("timeUs", &mediaTimeUs));
+
+        if (mediaTimeUs < skipUntilMediaTimeUs) {
+            LOGV("dropping %s buffer at time %lld as requested.",
+                 audio ? "audio" : "video",
+                 mediaTimeUs);
+
+            reply->post();
+            return;
+        }
+
+        skipUntilMediaTimeUs = -1;
+    }
+
     mRenderer->queueBuffer(audio, buffer, reply);
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index e7c6a42..fb5b001 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -112,6 +112,9 @@
     bool mResetInProgress;
     bool mResetPostponed;
 
+    int64_t mSkipRenderingAudioUntilMediaTimeUs;
+    int64_t mSkipRenderingVideoUntilMediaTimeUs;
+
     status_t instantiateDecoder(bool audio, sp<Decoder> *decoder);
 
     status_t feedDecoderInputData(bool audio, const sp<AMessage> &msg);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
index a23beb7..885ebe4 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.cpp
@@ -92,9 +92,12 @@
     }
 }
 
-ssize_t NuPlayer::NuPlayerStreamListener::read(void *data, size_t size) {
+ssize_t NuPlayer::NuPlayerStreamListener::read(
+        void *data, size_t size, sp<AMessage> *extra) {
     CHECK_GT(size, 0u);
 
+    extra->clear();
+
     Mutex::Autolock autoLock(mLock);
 
     if (mEOS) {
@@ -122,6 +125,8 @@
 
             case DISCONTINUITY:
             {
+                *extra = entry->mExtra;
+
                 mQueue.erase(mQueue.begin());
                 entry = NULL;
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
index f88e945..df0935d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerStreamListener.h
@@ -37,7 +37,7 @@
             Command cmd, bool synchronous, const sp<AMessage> &extra);
 
     void start();
-    ssize_t read(void *data, size_t size);
+    ssize_t read(void *data, size_t size, sp<AMessage> *extra);
 
 private:
     enum {
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index b85ac9f..2016282 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -54,7 +54,8 @@
 
     for (int32_t i = 0; i < 10; ++i) {
         char buffer[188];
-        ssize_t n = mStreamListener->read(buffer, sizeof(buffer));
+        sp<AMessage> extra;
+        ssize_t n = mStreamListener->read(buffer, sizeof(buffer), &extra);
 
         if (n == 0) {
             LOGI("input data EOS reached.");
@@ -62,7 +63,8 @@
             mEOS = true;
             break;
         } else if (n == INFO_DISCONTINUITY) {
-            mTSParser->signalDiscontinuity(ATSParser::DISCONTINUITY_SEEK);
+            mTSParser->signalDiscontinuity(
+                    ATSParser::DISCONTINUITY_SEEK, extra);
         } else if (n < 0) {
             CHECK_EQ(n, -EWOULDBLOCK);
             break;
@@ -72,7 +74,8 @@
                 mTSParser->signalDiscontinuity(
                         buffer[1] == 0x00
                             ? ATSParser::DISCONTINUITY_SEEK
-                            : ATSParser::DISCONTINUITY_FORMATCHANGE);
+                            : ATSParser::DISCONTINUITY_FORMATCHANGE,
+                        extra);
             } else {
                 mTSParser->feedTSPacket(buffer, sizeof(buffer));
             }
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index e43cdaa..d590ab9 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -429,16 +429,6 @@
         return err;
     }
 
-    // Increase the buffer count by one to allow for the ANativeWindow to hold
-    // on to one of the buffers.
-    def.nBufferCountActual++;
-    err = mOMX->setParameter(
-            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-
-    if (err != OK) {
-        return err;
-    }
-
     // Set up the native window.
     OMX_U32 usage = 0;
     err = mOMX->getGraphicBufferUsage(mNode, kPortIndexOutput, &usage);
@@ -457,6 +447,33 @@
         return err;
     }
 
+    int minUndequeuedBufs = 0;
+    err = mNativeWindow->query(
+            mNativeWindow.get(), NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+            &minUndequeuedBufs);
+
+    if (err != 0) {
+        LOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
+                strerror(-err), -err);
+        return err;
+    }
+
+    // XXX: Is this the right logic to use?  It's not clear to me what the OMX
+    // buffer counts refer to - how do they account for the renderer holding on
+    // to buffers?
+    if (def.nBufferCountActual < def.nBufferCountMin + minUndequeuedBufs) {
+        OMX_U32 newBufferCount = def.nBufferCountMin + minUndequeuedBufs;
+        def.nBufferCountActual = newBufferCount;
+        err = mOMX->setParameter(
+                mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+        if (err != OK) {
+            LOGE("[%s] setting nBufferCountActual to %lu failed: %d",
+                    mComponentName.c_str(), newBufferCount, err);
+            return err;
+        }
+    }
+
     err = native_window_set_buffer_count(
             mNativeWindow.get(), def.nBufferCountActual);
 
@@ -512,11 +529,7 @@
         cancelEnd = mBuffers[kPortIndexOutput].size();
     } else {
         // Return the last two buffers to the native window.
-        // XXX TODO: The number of buffers the native window owns should
-        // probably be queried from it when we put the native window in
-        // fixed buffer pool mode (which needs to be implemented).
-        // Currently it's hard-coded to 2.
-        cancelStart = def.nBufferCountActual - 2;
+        cancelStart = def.nBufferCountActual - minUndequeuedBufs;
         cancelEnd = def.nBufferCountActual;
     }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 5f40893..4a94e0d 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1738,15 +1738,6 @@
         return err;
     }
 
-    // Increase the buffer count by one to allow for the ANativeWindow to hold
-    // on to one of the buffers.
-    def.nBufferCountActual++;
-    err = mOMX->setParameter(
-            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
-    if (err != OK) {
-        return err;
-    }
-
     err = applyRotation();
     if (err != OK) {
         return err;
@@ -1768,6 +1759,30 @@
         return err;
     }
 
+    int minUndequeuedBufs = 0;
+    err = mNativeWindow->query(mNativeWindow.get(),
+            NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &minUndequeuedBufs);
+    if (err != 0) {
+        LOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d)",
+                strerror(-err), -err);
+        return err;
+    }
+
+    // XXX: Is this the right logic to use?  It's not clear to me what the OMX
+    // buffer counts refer to - how do they account for the renderer holding on
+    // to buffers?
+    if (def.nBufferCountActual < def.nBufferCountMin + minUndequeuedBufs) {
+        OMX_U32 newBufferCount = def.nBufferCountMin + minUndequeuedBufs;
+        def.nBufferCountActual = newBufferCount;
+        err = mOMX->setParameter(
+                mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+        if (err != OK) {
+            CODEC_LOGE("setting nBufferCountActual to %lu failed: %d",
+                    newBufferCount, err);
+            return err;
+        }
+    }
+
     err = native_window_set_buffer_count(
             mNativeWindow.get(), def.nBufferCountActual);
     if (err != 0) {
@@ -1822,10 +1837,7 @@
         cancelEnd = mPortBuffers[kPortIndexOutput].size();
     } else {
         // Return the last two buffers to the native window.
-        // XXX TODO: The number of buffers the native window owns should probably be
-        // queried from it when we put the native window in fixed buffer pool mode
-        // (which needs to be implemented).  Currently it's hard-coded to 2.
-        cancelStart = def.nBufferCountActual - 2;
+        cancelStart = def.nBufferCountActual - minUndequeuedBufs;
         cancelEnd = def.nBufferCountActual;
     }
 
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 0e51caf..6538a05 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -73,6 +73,7 @@
     // Returns an approximate bitrate in bits per second.
     uint64_t approxBitrate();
 
+    status_t seekToTime(int64_t timeUs);
     status_t seekToOffset(off64_t offset);
     status_t readNextPacket(MediaBuffer **buffer);
 
@@ -90,6 +91,11 @@
         uint8_t mLace[255];
     };
 
+    struct TOCEntry {
+        off64_t mPageOffset;
+        int64_t mTimeUs;
+    };
+
     sp<DataSource> mSource;
     off64_t mOffset;
     Page mCurrentPage;
@@ -107,6 +113,8 @@
     sp<MetaData> mMeta;
     sp<MetaData> mFileMeta;
 
+    Vector<TOCEntry> mTableOfContents;
+
     ssize_t readPage(off64_t offset, Page *page);
     status_t findNextPage(off64_t startOffset, off64_t *pageOffset);
 
@@ -115,7 +123,9 @@
 
     void parseFileMetaData();
 
-    uint64_t findPrevGranulePosition(off64_t pageOffset);
+    status_t findPrevGranulePosition(off64_t pageOffset, uint64_t *granulePos);
+
+    void buildTableOfContents();
 
     MyVorbisExtractor(const MyVorbisExtractor &);
     MyVorbisExtractor &operator=(const MyVorbisExtractor &);
@@ -164,10 +174,7 @@
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
-        off64_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
-        LOGV("seeking to offset %ld", pos);
-
-        if (mExtractor->mImpl->seekToOffset(pos) != OK) {
+        if (mExtractor->mImpl->seekToTime(seekTimeUs) != OK) {
             return ERROR_END_OF_STREAM;
         }
     }
@@ -237,7 +244,7 @@
 
         if (!memcmp(signature, "OggS", 4)) {
             if (*pageOffset > startOffset) {
-                LOGV("skipped %ld bytes of junk to reach next frame",
+                LOGV("skipped %lld bytes of junk to reach next frame",
                      *pageOffset - startOffset);
             }
 
@@ -252,7 +259,10 @@
 // it (if any) and return its granule position.
 // To do this we back up from the "current" page's offset until we find any
 // page preceding it and then scan forward to just before the current page.
-uint64_t MyVorbisExtractor::findPrevGranulePosition(off64_t pageOffset) {
+status_t MyVorbisExtractor::findPrevGranulePosition(
+        off64_t pageOffset, uint64_t *granulePos) {
+    *granulePos = 0;
+
     off64_t prevPageOffset = 0;
     off64_t prevGuess = pageOffset;
     for (;;) {
@@ -262,9 +272,12 @@
             prevGuess = 0;
         }
 
-        LOGV("backing up %ld bytes", pageOffset - prevGuess);
+        LOGV("backing up %lld bytes", pageOffset - prevGuess);
 
-        CHECK_EQ(findNextPage(prevGuess, &prevPageOffset), (status_t)OK);
+        status_t err = findNextPage(prevGuess, &prevPageOffset);
+        if (err != OK) {
+            return err;
+        }
 
         if (prevPageOffset < pageOffset || prevGuess == 0) {
             break;
@@ -273,27 +286,64 @@
 
     if (prevPageOffset == pageOffset) {
         // We did not find a page preceding this one.
-        return 0;
+        return UNKNOWN_ERROR;
     }
 
-    LOGV("prevPageOffset at %ld, pageOffset at %ld", prevPageOffset, pageOffset);
+    LOGV("prevPageOffset at %lld, pageOffset at %lld",
+         prevPageOffset, pageOffset);
 
     for (;;) {
         Page prevPage;
         ssize_t n = readPage(prevPageOffset, &prevPage);
 
         if (n <= 0) {
-            return 0;
+            return (status_t)n;
         }
 
         prevPageOffset += n;
 
         if (prevPageOffset == pageOffset) {
-            return prevPage.mGranulePosition;
+            *granulePos = prevPage.mGranulePosition;
+            return OK;
         }
     }
 }
 
+status_t MyVorbisExtractor::seekToTime(int64_t timeUs) {
+    if (mTableOfContents.isEmpty()) {
+        // Perform approximate seeking based on avg. bitrate.
+
+        off64_t pos = timeUs * approxBitrate() / 8000000ll;
+
+        LOGV("seeking to offset %lld", pos);
+        return seekToOffset(pos);
+    }
+
+    size_t left = 0;
+    size_t right = mTableOfContents.size();
+    while (left < right) {
+        size_t center = left / 2 + right / 2 + (left & right & 1);
+
+        const TOCEntry &entry = mTableOfContents.itemAt(center);
+
+        if (timeUs < entry.mTimeUs) {
+            right = center;
+        } else if (timeUs > entry.mTimeUs) {
+            left = center + 1;
+        } else {
+            left = right = center;
+            break;
+        }
+    }
+
+    const TOCEntry &entry = mTableOfContents.itemAt(left);
+
+    LOGV("seeking to entry %d / %d at offset %lld",
+         left, mTableOfContents.size(), entry.mPageOffset);
+
+    return seekToOffset(entry.mPageOffset);
+}
+
 status_t MyVorbisExtractor::seekToOffset(off64_t offset) {
     if (mFirstDataOffset >= 0 && offset < mFirstDataOffset) {
         // Once we know where the actual audio data starts (past the headers)
@@ -311,7 +361,7 @@
     // We found the page we wanted to seek to, but we'll also need
     // the page preceding it to determine how many valid samples are on
     // this page.
-    mPrevGranulePosition = findPrevGranulePosition(pageOffset);
+    findPrevGranulePosition(pageOffset, &mPrevGranulePosition);
 
     mOffset = pageOffset;
 
@@ -330,7 +380,8 @@
     uint8_t header[27];
     if (mSource->readAt(offset, header, sizeof(header))
             < (ssize_t)sizeof(header)) {
-        LOGV("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
+        LOGV("failed to read %d bytes at offset 0x%016llx",
+             sizeof(header), offset);
 
         return ERROR_IO;
     }
@@ -447,7 +498,8 @@
                     packetSize);
 
             if (n < (ssize_t)packetSize) {
-                LOGV("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
+                LOGV("failed to read %d bytes at 0x%016llx",
+                     packetSize, dataOffset);
                 return ERROR_IO;
             }
 
@@ -563,9 +615,66 @@
 
     mFirstDataOffset = mOffset + mCurrentPageSize;
 
+    off64_t size;
+    uint64_t lastGranulePosition;
+    if (!(mSource->flags() & DataSource::kIsCachingDataSource)
+            && mSource->getSize(&size) == OK
+            && findPrevGranulePosition(size, &lastGranulePosition) == OK) {
+        // Let's assume it's cheap to seek to the end.
+        // The granule position of the final page in the stream will
+        // give us the exact duration of the content, something that
+        // we can only approximate using avg. bitrate if seeking to
+        // the end is too expensive or impossible (live streaming).
+
+        int64_t durationUs = lastGranulePosition * 1000000ll / mVi.rate;
+
+        mMeta->setInt64(kKeyDuration, durationUs);
+
+        buildTableOfContents();
+    }
+
     return OK;
 }
 
+void MyVorbisExtractor::buildTableOfContents() {
+    off64_t offset = mFirstDataOffset;
+    Page page;
+    ssize_t pageSize;
+    while ((pageSize = readPage(offset, &page)) > 0) {
+        mTableOfContents.push();
+
+        TOCEntry &entry =
+            mTableOfContents.editItemAt(mTableOfContents.size() - 1);
+
+        entry.mPageOffset = offset;
+        entry.mTimeUs = page.mGranulePosition * 1000000ll / mVi.rate;
+
+        offset += (size_t)pageSize;
+    }
+
+    // Limit the maximum amount of RAM we spend on the table of contents,
+    // if necessary thin out the table evenly to trim it down to maximum
+    // size.
+
+    static const size_t kMaxTOCSize = 8192;
+    static const size_t kMaxNumTOCEntries = kMaxTOCSize / sizeof(TOCEntry);
+
+    size_t numerator = mTableOfContents.size();
+
+    if (numerator > kMaxNumTOCEntries) {
+        size_t denom = numerator - kMaxNumTOCEntries;
+
+        size_t accum = 0;
+        for (ssize_t i = mTableOfContents.size() - 1; i >= 0; --i) {
+            accum += denom;
+            if (accum >= numerator) {
+                mTableOfContents.removeAt(i);
+                accum -= numerator;
+            }
+        }
+    }
+}
+
 status_t MyVorbisExtractor::verifyHeader(
         MediaBuffer *buffer, uint8_t type) {
     const uint8_t *data =
diff --git a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
index 8b0250a..d443b7c 100644
--- a/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
+++ b/media/libstagefright/codecs/mp3dec/src/pvmp3_decode_header.cpp
@@ -121,9 +121,11 @@
     uint32  temp;
 
     /*
-     *  Verify that at least the header is complete
+     * Verify that at least the header is complete
+     * Note that SYNC_WORD_LNGTH is in unit of bits, but inputBufferCurrentLength
+     * is in unit of bytes.
      */
-    if (inputStream->inputBufferCurrentLength < (SYNC_WORD_LNGTH + 21))
+    if (inputStream->inputBufferCurrentLength < ((SYNC_WORD_LNGTH + 21) >> 3))
     {
         return NO_ENOUGH_MAIN_DATA_ERROR;
     }
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 6056739..7d4bc6e 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -32,6 +32,7 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
+#include <media/IStreamSource.h>
 #include <utils/KeyedVector.h>
 
 namespace android {
@@ -49,7 +50,9 @@
             unsigned pid, unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity(DiscontinuityType type);
+    void signalDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
+
     void signalEOS(status_t finalResult);
 
     sp<MediaSource> getSource(SourceType type);
@@ -83,7 +86,9 @@
             unsigned payload_unit_start_indicator,
             ABitReader *br);
 
-    void signalDiscontinuity(DiscontinuityType type);
+    void signalDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
+
     void signalEOS(status_t finalResult);
 
     sp<MediaSource> getSource(SourceType type);
@@ -100,6 +105,7 @@
     sp<AnotherPacketSource> mSource;
     bool mPayloadStarted;
     DiscontinuityType mPendingDiscontinuity;
+    sp<AMessage> mPendingDiscontinuityExtra;
 
     ElementaryStreamQueue mQueue;
 
@@ -112,7 +118,8 @@
 
     void extractAACFrames(const sp<ABuffer> &buffer);
 
-    void deferDiscontinuity(DiscontinuityType type);
+    void deferDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
 
     DISALLOW_EVIL_CONSTRUCTORS(Stream);
 };
@@ -150,9 +157,10 @@
     return true;
 }
 
-void ATSParser::Program::signalDiscontinuity(DiscontinuityType type) {
+void ATSParser::Program::signalDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
     for (size_t i = 0; i < mStreams.size(); ++i) {
-        mStreams.editValueAt(i)->signalDiscontinuity(type);
+        mStreams.editValueAt(i)->signalDiscontinuity(type, extra);
     }
 }
 
@@ -283,7 +291,8 @@
             mStreams.add(info.mPID, stream);
 
             if (PIDsChanged) {
-                stream->signalDiscontinuity(DISCONTINUITY_FORMATCHANGE);
+                sp<AMessage> extra;
+                stream->signalDiscontinuity(DISCONTINUITY_FORMATCHANGE, extra);
             }
         }
     }
@@ -360,13 +369,25 @@
     size_t payloadSizeBits = br->numBitsLeft();
     CHECK_EQ(payloadSizeBits % 8, 0u);
 
-    CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity());
+    size_t neededSize = mBuffer->size() + payloadSizeBits / 8;
+    if (mBuffer->capacity() < neededSize) {
+        // Increment in multiples of 64K.
+        neededSize = (neededSize + 65535) & ~65535;
+
+        LOGI("resizing buffer to %d bytes", neededSize);
+
+        sp<ABuffer> newBuffer = new ABuffer(neededSize);
+        memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
+        newBuffer->setRange(0, mBuffer->size());
+        mBuffer = newBuffer;
+    }
 
     memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
     mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
 }
 
-void ATSParser::Stream::signalDiscontinuity(DiscontinuityType type) {
+void ATSParser::Stream::signalDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
     mPayloadStarted = false;
     mBuffer->setRange(0, 0);
 
@@ -378,10 +399,21 @@
 
             mQueue.clear(!isASeek);
 
+            uint64_t resumeAtPTS;
+            if (extra != NULL
+                    && extra->findInt64(
+                        IStreamListener::kKeyResumeAtPTS,
+                        (int64_t *)&resumeAtPTS)) {
+                int64_t resumeAtMediaTimeUs =
+                    mProgram->convertPTSToTimestamp(resumeAtPTS);
+
+                extra->setInt64("resume-at-mediatimeUs", resumeAtMediaTimeUs);
+            }
+
             if (mSource != NULL) {
-                mSource->queueDiscontinuity(type);
+                mSource->queueDiscontinuity(type, extra);
             } else {
-                deferDiscontinuity(type);
+                deferDiscontinuity(type, extra);
             }
             break;
         }
@@ -392,10 +424,12 @@
     }
 }
 
-void ATSParser::Stream::deferDiscontinuity(DiscontinuityType type) {
+void ATSParser::Stream::deferDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
     if (type > mPendingDiscontinuity) {
         // Only upgrade discontinuities.
         mPendingDiscontinuity = type;
+        mPendingDiscontinuityExtra = extra;
     }
 }
 
@@ -596,8 +630,10 @@
                 mSource = new AnotherPacketSource(meta);
 
                 if (mPendingDiscontinuity != DISCONTINUITY_NONE) {
-                    mSource->queueDiscontinuity(mPendingDiscontinuity);
+                    mSource->queueDiscontinuity(
+                            mPendingDiscontinuity, mPendingDiscontinuityExtra);
                     mPendingDiscontinuity = DISCONTINUITY_NONE;
+                    mPendingDiscontinuityExtra.clear();
                 }
 
                 mSource->queueAccessUnit(accessUnit);
@@ -639,9 +675,10 @@
     parseTS(&br);
 }
 
-void ATSParser::signalDiscontinuity(DiscontinuityType type) {
+void ATSParser::signalDiscontinuity(
+        DiscontinuityType type, const sp<AMessage> &extra) {
     for (size_t i = 0; i < mPrograms.size(); ++i) {
-        mPrograms.editItemAt(i)->signalDiscontinuity(type);
+        mPrograms.editItemAt(i)->signalDiscontinuity(type, extra);
     }
 }
 
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 455f9d5..3936f05 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -41,7 +41,10 @@
     ATSParser();
 
     void feedTSPacket(const void *data, size_t size);
-    void signalDiscontinuity(DiscontinuityType type);
+
+    void signalDiscontinuity(
+            DiscontinuityType type, const sp<AMessage> &extra);
+
     void signalEOS(status_t finalResult);
 
     enum SourceType {
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 0ad883b..59de17e 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -136,9 +136,11 @@
 }
 
 void AnotherPacketSource::queueDiscontinuity(
-        ATSParser::DiscontinuityType type) {
+        ATSParser::DiscontinuityType type,
+        const sp<AMessage> &extra) {
     sp<ABuffer> buffer = new ABuffer(0);
     buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type));
+    buffer->meta()->setMessage("extra", extra);
 
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 6fe93f8..439c7853 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -46,7 +46,10 @@
     status_t nextBufferTime(int64_t *timeUs);
 
     void queueAccessUnit(const sp<ABuffer> &buffer);
-    void queueDiscontinuity(ATSParser::DiscontinuityType type);
+
+    void queueDiscontinuity(
+            ATSParser::DiscontinuityType type, const sp<AMessage> &extra);
+
     void signalEOS(status_t result);
 
     status_t dequeueAccessUnit(sp<ABuffer> *buffer);
diff --git a/native/include/android/looper.h b/native/include/android/looper.h
index a9d8426..24e3967 100644
--- a/native/include/android/looper.h
+++ b/native/include/android/looper.h
@@ -148,7 +148,8 @@
 
 /**
  * For callback-based event loops, this is the prototype of the function
- * that is called.  It is given the file descriptor it is associated with,
+ * that is called when a file descriptor event occurs.
+ * It is given the file descriptor it is associated with,
  * a bitmask of the poll events that were triggered (typically ALOOPER_EVENT_INPUT),
  * and the data pointer that was originally supplied.
  *
diff --git a/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml b/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
index fbb3c78..677988d 100644
--- a/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
+++ b/packages/SystemUI/res/layout-xlarge/status_bar_settings_view.xml
@@ -79,7 +79,7 @@
         <TextView
                 android:id="@+id/rotate_label"
                 style="@style/StatusBarPanelSettingsContents"
-                android:text="@string/status_bar_settings_rotation_lock"
+                android:text="@string/status_bar_settings_auto_rotation"
                 />
         <Switch
                 android:id="@+id/rotate_checkbox"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0923570..ebd48e7 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -81,7 +81,7 @@
     <string name="status_bar_settings_airplane">Airplane mode</string>
 
     <!-- Label in system panel saying the device will use the orientation sensor to rotate [CHAR LIMIT=30] -->
-    <string name="status_bar_settings_rotation_lock">Lock screen orientation</string>
+    <string name="status_bar_settings_auto_rotation">Auto-rotate screen</string>
 
     <!-- Abbreviation / label for mute brightness mode button. Should be all caps. [CHAR LIMIT=6] -->
     <string name="status_bar_settings_mute_label">MUTE</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java
index b0a6d7a..5ac5ad0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AutoRotateController.java
@@ -35,39 +35,39 @@
     private Context mContext;
     private CompoundButton mCheckBox;
 
-    private boolean mLockRotation;
+    private boolean mAutoRotation;
 
     public AutoRotateController(Context context, CompoundButton checkbox) {
         mContext = context;
-        mLockRotation = getLockRotation();
+        mAutoRotation = getAutoRotation();
         mCheckBox = checkbox;
-        checkbox.setChecked(mLockRotation);
+        checkbox.setChecked(mAutoRotation);
         checkbox.setOnCheckedChangeListener(this);
     }
 
     public void onCheckedChanged(CompoundButton view, boolean checked) {
-        if (checked != mLockRotation) {
-            setLockRotation(checked);
+        if (checked != mAutoRotation) {
+            setAutoRotation(checked);
         }
     }
 
-    private boolean getLockRotation() {
+    private boolean getAutoRotation() {
         ContentResolver cr = mContext.getContentResolver();
-        return 0 == Settings.System.getInt(cr, Settings.System.ACCELEROMETER_ROTATION, 0);
+        return 0 != Settings.System.getInt(cr, Settings.System.ACCELEROMETER_ROTATION, 0);
     }
 
-    private void setLockRotation(final boolean locked) {
-        mLockRotation = locked;
+    private void setAutoRotation(final boolean autorotate) {
+        mAutoRotation = autorotate;
         AsyncTask.execute(new Runnable() {
                 public void run() {
                     try {
                         IWindowManager wm = IWindowManager.Stub.asInterface(
                                 ServiceManager.getService(Context.WINDOW_SERVICE));
                         ContentResolver cr = mContext.getContentResolver();
-                        if (locked) {
-                            wm.freezeRotation();
-                        } else {
+                        if (autorotate) {
                             wm.thawRotation();
+                        } else {
+                            wm.freezeRotation();
                         }
                     } catch (RemoteException exc) {
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 35ae118..326cd74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -105,6 +105,8 @@
     private int mInetCondition = 0;
     private static final int INET_CONDITION_THRESHOLD = 50;
 
+    private boolean mAirplaneMode = false;
+
     // our ui
     Context mContext;
     ArrayList<ImageView> mPhoneSignalIconViews = new ArrayList<ImageView>();
@@ -170,8 +172,12 @@
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
         filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+        filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         context.registerReceiver(this, filter);
 
+        // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
+        updateAirplaneMode();
+
         // yuck
         mBatteryStats = BatteryStatsService.getService();
     }
@@ -228,6 +234,9 @@
             refreshViews();
         } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
             refreshViews();
+        } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
+            updateAirplaneMode();
+            refreshViews();
         }
     }
 
@@ -343,18 +352,17 @@
         return (! "wifi-only".equals(SystemProperties.get("ro.carrier")));
     }
 
+
+    private void updateAirplaneMode() {
+        mAirplaneMode = (Settings.System.getInt(mContext.getContentResolver(),
+            Settings.System.AIRPLANE_MODE_ON, 0) == 1);
+    }
+
     private final void updateTelephonySignalStrength() {
-        // Display signal strength while in "emergency calls only" mode
-        if (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly())) {
+        if (!hasService()) {
             //Slog.d(TAG, "updateTelephonySignalStrength: no service");
-            if (Settings.System.getInt(mContext.getContentResolver(),
-                    Settings.System.AIRPLANE_MODE_ON, 0) == 1) {
-                mPhoneSignalIconId = R.drawable.stat_sys_signal_flightmode;
-                mDataSignalIconId = R.drawable.stat_sys_signal_flightmode;
-            } else {
-                mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
-                mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null
-            }
+            mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
+            mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null
         } else {
             if (mSignalStrength == null) {
                 mPhoneSignalIconId = R.drawable.stat_sys_signal_null;
@@ -719,6 +727,12 @@
             label = mContext.getString(R.string.bluetooth_tethered);
             combinedSignalIconId = mBluetoothTetherIconId;
             dataTypeIconId = 0;
+        } else if (mAirplaneMode &&
+                (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) {
+            // Only display the flight-mode icon if not in "emergency calls only" mode.
+            label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
+            combinedSignalIconId = R.drawable.stat_sys_signal_flightmode;
+            dataTypeIconId = 0;
         } else {
             label = context.getString(R.string.status_bar_settings_signal_meter_disconnected);
             // On devices without mobile radios, we want to show the wifi icon
@@ -732,6 +746,7 @@
                     + Integer.toHexString(combinedSignalIconId)
                     + "/" + getResourceName(combinedSignalIconId)
                     + " dataDirectionOverlayIconId=0x" + Integer.toHexString(dataDirectionOverlayIconId)
+                    + " mAirplaneMode=" + mAirplaneMode
                     + " mDataActivity=" + mDataActivity
                     + " mPhoneSignalIconId=0x" + Integer.toHexString(mPhoneSignalIconId)
                     + " mDataDirectionIconId=0x" + Integer.toHexString(mDataDirectionIconId)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
index 2ec2af0..8a88792 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -164,7 +164,7 @@
     }
 
     public void setNotificationCount(int n) {
-        Slog.d(TAG, "notificationCount=" + n);
+//        Slog.d(TAG, "notificationCount=" + n);
         if (!mShowing) {
             // just do it, already
             setContentFrameVisible(n > 0, false);
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 886b85f..2fda3aa 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -163,6 +163,12 @@
      */
     private Configuration mConfiguration;
 
+    private Runnable mRecreateRunnable = new Runnable() {
+        public void run() {
+            recreateScreens();
+        }
+    };
+
     /**
      * @return Whether we are stuck on the lock screen because the sim is
      *   missing.
@@ -244,7 +250,8 @@
 
             public void recreateMe(Configuration config) {
                 mConfiguration = config;
-                recreateScreens();
+                removeCallbacks(mRecreateRunnable);
+                post(mRecreateRunnable);
             }
 
             public void takeEmergencyCallAction() {
@@ -463,6 +470,12 @@
     }
 
     @Override
+    protected void onDetachedFromWindow() {
+        removeCallbacks(mRecreateRunnable);
+        super.onDetachedFromWindow();
+    }
+
+    @Override
     public void wakeWhenReadyTq(int keyCode) {
         if (DEBUG) Log.d(TAG, "onWakeKey");
         if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp
index bfc80db..74be4e0 100644
--- a/services/audioflinger/AudioPolicyManagerBase.cpp
+++ b/services/audioflinger/AudioPolicyManagerBase.cpp
@@ -122,12 +122,12 @@
         // request routing change if necessary
         uint32_t newDevice = getNewDevice(mHardwareOutput, false);
 #ifdef WITH_A2DP
+        checkA2dpSuspend();
         checkOutputForAllStrategies();
         // A2DP outputs must be closed after checkOutputForAllStrategies() is executed
         if (state == AudioSystem::DEVICE_STATE_UNAVAILABLE && AudioSystem::isA2dpDevice(device)) {
             closeA2dpOutputs();
         }
-        checkA2dpSuspend();
 #endif
         updateDeviceForStrategy();
         setOutputDevice(mHardwareOutput, newDevice);
@@ -269,8 +269,8 @@
     // check for device and output changes triggered by new phone state
     newDevice = getNewDevice(mHardwareOutput, false);
 #ifdef WITH_A2DP
-    checkOutputForAllStrategies();
     checkA2dpSuspend();
+    checkOutputForAllStrategies();
 #endif
     updateDeviceForStrategy();
 
@@ -378,8 +378,8 @@
     // check for device and output changes triggered by new phone state
     uint32_t newDevice = getNewDevice(mHardwareOutput, false);
 #ifdef WITH_A2DP
-    checkOutputForAllStrategies();
     checkA2dpSuspend();
+    checkOutputForAllStrategies();
 #endif
     updateDeviceForStrategy();
     setOutputDevice(mHardwareOutput, newDevice);
@@ -1624,7 +1624,7 @@
             if (device) break;
 #ifdef WITH_A2DP
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to A2DP
-            if (!isInCall()) {
+            if (!isInCall() && !mA2dpSuspended) {
                 device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
                 if (device) break;
                 device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
@@ -1647,7 +1647,7 @@
 #ifdef WITH_A2DP
             // when not in a phone call, phone strategy should route STREAM_VOICE_CALL to
             // A2DP speaker when forcing to speaker output
-            if (!isInCall()) {
+            if (!isInCall() && !mA2dpSuspended) {
                 device = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
                 if (device) break;
             }
@@ -1687,7 +1687,7 @@
             device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_WIRED_HEADSET;
         }
 #ifdef WITH_A2DP
-        if ((mA2dpOutput != 0) &&
+        if ((mA2dpOutput != 0) && !mA2dpSuspended &&
                 (strategy != STRATEGY_SONIFICATION || a2dpUsedForSonification())) {
             if (device2 == 0) {
                 device2 = mAvailableOutputDevices & AudioSystem::DEVICE_OUT_BLUETOOTH_A2DP;
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index a865d9f..c3c143c 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -399,6 +399,17 @@
     }
 }
 
+void InputReader::fadePointer() {
+    { // acquire device registry reader lock
+        RWLock::AutoRLock _rl(mDeviceRegistryLock);
+
+        for (size_t i = 0; i < mDevices.size(); i++) {
+            InputDevice* device = mDevices.valueAt(i);
+            device->fadePointer();
+        }
+    } // release device registry reader lock
+}
+
 void InputReader::getInputConfiguration(InputConfiguration* outConfiguration) {
     { // acquire state lock
         AutoMutex _l(mStateLock);
@@ -695,6 +706,14 @@
     return result;
 }
 
+void InputDevice::fadePointer() {
+    size_t numMappers = mMappers.size();
+    for (size_t i = 0; i < numMappers; i++) {
+        InputMapper* mapper = mMappers[i];
+        mapper->fadePointer();
+    }
+}
+
 
 // --- InputMapper ---
 
@@ -739,6 +758,9 @@
     return 0;
 }
 
+void InputMapper::fadePointer() {
+}
+
 void InputMapper::dumpRawAbsoluteAxisInfo(String8& dump,
         const RawAbsoluteAxisInfo& axis, const char* name) {
     if (axis.valid) {
@@ -967,6 +989,10 @@
         getContext()->updateGlobalMetaState();
     }
 
+    if (down && !isMetaKey(keyCode)) {
+        getContext()->fadePointer();
+    }
+
     if (policyFlags & POLICY_FLAG_FUNCTION) {
         newMetaState |= AMETA_FUNCTION_ON;
     }
@@ -1348,6 +1374,9 @@
         } else {
             hscroll = 0;
         }
+        if (hscroll != 0 || vscroll != 0) {
+            mPointerController->unfade();
+        }
     } // release lock
 
     int32_t metaState = mContext->getGlobalMetaState();
@@ -1376,6 +1405,13 @@
     }
 }
 
+void CursorInputMapper::fadePointer() {
+    { // acquire lock
+        AutoMutex _l(mLock);
+        mPointerController->fade();
+    } // release lock
+}
+
 
 // --- TouchInputMapper ---
 
@@ -2275,7 +2311,6 @@
     uint32_t policyFlags = 0;
 
     // Preprocess pointer data.
-
     if (mParameters.useBadTouchFilter) {
         if (applyBadTouchFilter()) {
             havePointerIds = false;
@@ -2303,8 +2338,12 @@
         savedTouch = & mCurrentTouch;
     }
 
-    // Process touches and virtual keys.
+    // Hide the pointer on an initial down.
+    if (mLastTouch.pointerCount == 0 && mCurrentTouch.pointerCount != 0) {
+        getContext()->fadePointer();
+    }
 
+    // Process touches and virtual keys.
     TouchResult touchResult = consumeOffScreenTouches(when, policyFlags);
     if (touchResult == DISPATCH_TOUCH) {
         detectGestures(when);
@@ -2312,7 +2351,6 @@
     }
 
     // Copy current touch to last touch in preparation for the next cycle.
-
     if (touchResult == DROP_STROKE) {
         mLastTouch.clear();
     } else {
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index cf41535..b344ffe 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -157,6 +157,8 @@
     virtual bool shouldDropVirtualKey(nsecs_t now,
             InputDevice* device, int32_t keyCode, int32_t scanCode) = 0;
 
+    virtual void fadePointer() = 0;
+
     virtual InputReaderPolicyInterface* getPolicy() = 0;
     virtual InputDispatcherInterface* getDispatcher() = 0;
     virtual EventHubInterface* getEventHub() = 0;
@@ -241,6 +243,8 @@
     virtual void updateGlobalMetaState();
     virtual int32_t getGlobalMetaState();
 
+    virtual void fadePointer();
+
     InputConfiguration mInputConfiguration;
     void updateInputConfiguration();
 
@@ -299,6 +303,8 @@
 
     int32_t getMetaState();
 
+    void fadePointer();
+
     inline const PropertyMap& getConfiguration() {
         return mConfiguration;
     }
@@ -351,6 +357,8 @@
 
     virtual int32_t getMetaState();
 
+    virtual void fadePointer();
+
 protected:
     InputDevice* mDevice;
     InputReaderContext* mContext;
@@ -459,6 +467,8 @@
 
     virtual int32_t getScanCodeState(uint32_t sourceMask, int32_t scanCode);
 
+    virtual void fadePointer();
+
 private:
     // Amount that trackball needs to move in order to generate a key event.
     static const int32_t TRACKBALL_MOVEMENT_THRESHOLD = 6;
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index 92af51e..954872b 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -35,8 +35,22 @@
 
 // --- PointerController ---
 
-PointerController::PointerController(int32_t pointerLayer) :
-    mPointerLayer(pointerLayer) {
+// Time to wait before starting the fade when the pointer is inactive.
+static const nsecs_t INACTIVITY_FADE_DELAY_TIME_NORMAL = 15 * 1000 * 1000000LL; // 15 seconds
+static const nsecs_t INACTIVITY_FADE_DELAY_TIME_SHORT = 3 * 1000 * 1000000LL; // 3 seconds
+
+// Time to spend fading out the pointer completely.
+static const nsecs_t FADE_DURATION = 500 * 1000000LL; // 500 ms
+
+// Time to wait between frames.
+static const nsecs_t FADE_FRAME_INTERVAL = 1000000000LL / 60;
+
+// Amount to subtract from alpha per frame.
+static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION;
+
+
+PointerController::PointerController(const sp<Looper>& looper, int32_t pointerLayer) :
+        mLooper(looper), mPointerLayer(pointerLayer) {
     AutoMutex _l(mLock);
 
     mLocked.displayWidth = -1;
@@ -51,12 +65,19 @@
     mLocked.iconHotSpotX = 0;
     mLocked.iconHotSpotY = 0;
 
+    mLocked.fadeAlpha = 1;
+    mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL;
+
     mLocked.wantVisible = false;
     mLocked.visible = false;
     mLocked.drawn = false;
+
+    mHandler = new WeakMessageHandler(this);
 }
 
 PointerController::~PointerController() {
+    mLooper->removeMessages(mHandler);
+
     if (mSurfaceControl != NULL) {
         mSurfaceControl->clear();
         mSurfaceControl.clear();
@@ -120,7 +141,7 @@
 
     if (mLocked.buttonState != buttonState) {
         mLocked.buttonState = buttonState;
-        mLocked.wantVisible = true;
+        unfadeBeforeUpdateLocked();
         updateLocked();
     }
 }
@@ -157,7 +178,7 @@
         } else {
             mLocked.pointerY = y;
         }
-        mLocked.wantVisible = true;
+        unfadeBeforeUpdateLocked();
         updateLocked();
     }
 }
@@ -169,6 +190,29 @@
     *outY = mLocked.pointerY;
 }
 
+void PointerController::fade() {
+    AutoMutex _l(mLock);
+
+    startFadeLocked();
+}
+
+void PointerController::unfade() {
+    AutoMutex _l(mLock);
+
+    if (unfadeBeforeUpdateLocked()) {
+        updateLocked();
+    }
+}
+
+void PointerController::setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay) {
+    AutoMutex _l(mLock);
+
+    if (mLocked.inactivityFadeDelay != inactivityFadeDelay) {
+        mLocked.inactivityFadeDelay = inactivityFadeDelay;
+        startInactivityFadeDelayLocked();
+    }
+}
+
 void PointerController::updateLocked() {
     bool wantVisibleAndHavePointerIcon = mLocked.wantVisible && mLocked.iconBitmap;
 
@@ -201,6 +245,12 @@
             goto CloseTransaction;
         }
 
+        status = mSurfaceControl->setAlpha(mLocked.fadeAlpha);
+        if (status) {
+            LOGE("Error %d setting pointer surface alpha.", status);
+            goto CloseTransaction;
+        }
+
         if (!mLocked.visible) {
             status = mSurfaceControl->setLayer(mPointerLayer);
             if (status) {
@@ -412,4 +462,64 @@
     return true;
 }
 
+void PointerController::handleMessage(const Message& message) {
+    switch (message.what) {
+    case MSG_FADE_STEP: {
+        AutoMutex _l(mLock);
+        fadeStepLocked();
+        break;
+    }
+    }
+}
+
+bool PointerController::unfadeBeforeUpdateLocked() {
+    sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
+
+    if (isFadingLocked()) {
+        mLocked.wantVisible = true;
+        mLocked.fadeAlpha = 1;
+        return true; // update required to effect the unfade
+    }
+    return false; // update not required
+}
+
+void PointerController::startFadeLocked() {
+    if (!isFadingLocked()) {
+        sendFadeStepMessageDelayedLocked(0);
+    }
+}
+
+void PointerController::startInactivityFadeDelayLocked() {
+    if (!isFadingLocked()) {
+        sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked());
+    }
+}
+
+void PointerController::fadeStepLocked() {
+    if (mLocked.wantVisible) {
+        mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME;
+        if (mLocked.fadeAlpha < 0) {
+            mLocked.fadeAlpha = 0;
+            mLocked.wantVisible = false;
+        } else {
+            sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL);
+        }
+        updateLocked();
+    }
+}
+
+bool PointerController::isFadingLocked() {
+    return !mLocked.wantVisible || mLocked.fadeAlpha != 1;
+}
+
+nsecs_t PointerController::getInactivityFadeDelayTimeLocked() {
+    return mLocked.inactivityFadeDelay == INACTIVITY_FADE_DELAY_SHORT
+            ? INACTIVITY_FADE_DELAY_TIME_SHORT : INACTIVITY_FADE_DELAY_TIME_NORMAL;
+}
+
+void PointerController::sendFadeStepMessageDelayedLocked(nsecs_t delayTime) {
+    mLooper->removeMessages(mHandler, MSG_FADE_STEP);
+    mLooper->sendMessageDelayed(delayTime, mHandler, Message(MSG_FADE_STEP));
+}
+
 } // namespace android
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index a2a9955..e28dd7d 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -18,7 +18,9 @@
 #define _UI_POINTER_CONTROLLER_H
 
 #include <ui/DisplayInfo.h>
+#include <ui/Input.h>
 #include <utils/RefBase.h>
+#include <utils/Looper.h>
 #include <utils/String8.h>
 
 #include <surfaceflinger/Surface.h>
@@ -64,6 +66,12 @@
 
     /* Gets the absolute location of the pointer. */
     virtual void getPosition(float* outX, float* outY) const = 0;
+
+    /* Fades the pointer out now. */
+    virtual void fade() = 0;
+
+    /* Makes the pointer visible if it has faded out. */
+    virtual void unfade() = 0;
 };
 
 
@@ -72,12 +80,17 @@
  *
  * Handles pointer acceleration and animation.
  */
-class PointerController : public PointerControllerInterface {
+class PointerController : public PointerControllerInterface, public MessageHandler {
 protected:
     virtual ~PointerController();
 
 public:
-    PointerController(int32_t pointerLayer);
+    enum InactivityFadeDelay {
+        INACTIVITY_FADE_DELAY_NORMAL = 0,
+        INACTIVITY_FADE_DELAY_SHORT = 1,
+    };
+
+    PointerController(const sp<Looper>& looper, int32_t pointerLayer);
 
     virtual bool getBounds(float* outMinX, float* outMinY,
             float* outMaxX, float* outMaxY) const;
@@ -86,14 +99,22 @@
     virtual uint32_t getButtonState() const;
     virtual void setPosition(float x, float y);
     virtual void getPosition(float* outX, float* outY) const;
+    virtual void fade();
+    virtual void unfade();
 
     void setDisplaySize(int32_t width, int32_t height);
     void setDisplayOrientation(int32_t orientation);
     void setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY);
+    void setInactivityFadeDelay(InactivityFadeDelay inactivityFadeDelay);
 
 private:
+    enum {
+        MSG_FADE_STEP = 0,
+    };
+
     mutable Mutex mLock;
 
+    sp<Looper> mLooper;
     int32_t mPointerLayer;
     sp<SurfaceComposerClient> mSurfaceComposerClient;
     sp<SurfaceControl> mSurfaceControl;
@@ -111,17 +132,31 @@
         float iconHotSpotX;
         float iconHotSpotY;
 
+        float fadeAlpha;
+        InactivityFadeDelay inactivityFadeDelay;
+
         bool wantVisible;
         bool visible;
         bool drawn;
     } mLocked;
 
+    sp<WeakMessageHandler> mHandler;
+
     bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const;
     void setPositionLocked(float x, float y);
     void updateLocked();
     bool createSurfaceIfNeededLocked();
     bool drawPointerIfNeededLocked();
     bool resizeSurfaceLocked(int32_t width, int32_t height);
+
+    void handleMessage(const Message& message);
+    bool unfadeBeforeUpdateLocked();
+    void startFadeLocked();
+    void startInactivityFadeDelayLocked();
+    void fadeStepLocked();
+    bool isFadingLocked();
+    nsecs_t getInactivityFadeDelayTimeLocked();
+    void sendFadeStepMessageDelayedLocked(nsecs_t delayTime);
 };
 
 } // namespace android
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index fac71bb..864241e 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -79,6 +79,12 @@
         *outX = 0;
         *outY = 0;
     }
+
+    virtual void fade() {
+    }
+
+    virtual void unfade() {
+    }
 };
 
 
@@ -743,6 +749,9 @@
             InputDevice* device, int32_t keyCode, int32_t scanCode) {
         return false;
     }
+
+    virtual void fadePointer() {
+    }
 };
 
 
@@ -875,6 +884,9 @@
     virtual int32_t getMetaState() {
         return mMetaState;
     }
+
+    virtual void fadePointer() {
+    }
 };
 
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index c061a83..b7d0a8f 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -286,8 +286,8 @@
                     com.android.internal.R.string.config_default_dns_server);
         }
         try {
-            mDefaultDns = InetAddress.getByName(dns);
-        } catch (UnknownHostException e) {
+            mDefaultDns = NetworkUtils.numericToInetAddress(dns);
+        } catch (IllegalArgumentException e) {
             loge("Error setting defaultDns using " + dns);
         }
 
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index eaf68b0..44f5df2 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -270,9 +270,9 @@
             InetAddress addr = null;
             int prefixLength = 0;
             try {
-                addr = InetAddress.getByName(st.nextToken(" "));
-            } catch (UnknownHostException uhe) {
-                Slog.e(TAG, "Failed to parse ipaddr", uhe);
+                addr = NetworkUtils.numericToInetAddress(st.nextToken(" "));
+            } catch (IllegalArgumentException iae) {
+                Slog.e(TAG, "Failed to parse ipaddr", iae);
             }
 
             try {
@@ -451,7 +451,7 @@
         try {
             String cmd = "tether dns set";
             for (String s : dns) {
-                cmd += " " + InetAddress.getByName(s).getHostAddress();
+                cmd += " " + NetworkUtils.numericToInetAddress(s).getHostAddress();
             }
             try {
                 mConnector.doCommand(cmd);
@@ -459,7 +459,7 @@
                 throw new IllegalStateException(
                         "Unable to communicate to native daemon for setting tether dns");
             }
-        } catch (UnknownHostException e) {
+        } catch (IllegalArgumentException e) {
             throw new IllegalStateException("Error resolving dns name", e);
         }
     }
@@ -519,11 +519,11 @@
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
             mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
-                    InetAddress.getByName(localAddr).getHostAddress(),
-                    InetAddress.getByName(remoteAddr).getHostAddress(),
-                    InetAddress.getByName(dns1Addr).getHostAddress(),
-                    InetAddress.getByName(dns2Addr).getHostAddress()));
-        } catch (UnknownHostException e) {
+                    NetworkUtils.numericToInetAddress(localAddr).getHostAddress(),
+                    NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(),
+                    NetworkUtils.numericToInetAddress(dns1Addr).getHostAddress(),
+                    NetworkUtils.numericToInetAddress(dns2Addr).getHostAddress()));
+        } catch (IllegalArgumentException e) {
             throw new IllegalStateException("Error resolving addr", e);
         } catch (NativeDaemonConnectorException e) {
             throw new IllegalStateException("Error communicating to native daemon to attach pppd", e);
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index 52f84eb..15f22c0 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -26,6 +26,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
 import android.net.SntpClient;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -58,6 +60,7 @@
 
     private static final int EVENT_AUTO_TIME_CHANGED = 1;
     private static final int EVENT_POLL_NETWORK_TIME = 2;
+    private static final int EVENT_WIFI_CONNECTED = 3;
 
     /** Normal polling frequency */
     private static final long POLLING_INTERVAL_MS = 24L * 60 * 60 * 1000; // 24 hrs
@@ -113,6 +116,7 @@
 
         registerForTelephonyIntents();
         registerForAlarms();
+        registerForConnectivityIntents();
 
         mThread = new HandlerThread(TAG);
         mThread.start();
@@ -162,6 +166,12 @@
             }, new IntentFilter(ACTION_POLL));
     }
 
+    private void registerForConnectivityIntents() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mContext.registerReceiver(mConnectivityReceiver, intentFilter);
+    }
+
     private void onPollNetworkTime(int event) {
         // If Automatic time is not set, don't bother.
         if (!isAutomaticTimeRequested()) return;
@@ -253,6 +263,27 @@
         }
     };
 
+    /** Receiver for ConnectivityManager events */
+    private BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(action)) {
+                // There is connectivity
+                NetworkInfo netInfo = (NetworkInfo)intent.getParcelableExtra(
+                        ConnectivityManager.EXTRA_NETWORK_INFO);
+                if (netInfo != null) {
+                    // Verify that it's a WIFI connection
+                    if (netInfo.getState() == NetworkInfo.State.CONNECTED &&
+                            netInfo.getType() == ConnectivityManager.TYPE_WIFI ) {
+                        mHandler.obtainMessage(EVENT_WIFI_CONNECTED).sendToTarget();
+                    }
+                }
+            }
+        }
+    };
+
     /** Handler to do the network accesses on */
     private class MyHandler extends Handler {
 
@@ -265,6 +296,7 @@
             switch (msg.what) {
                 case EVENT_AUTO_TIME_CHANGED:
                 case EVENT_POLL_NETWORK_TIME:
+                case EVENT_WIFI_CONNECTED:
                     onPollNetworkTime(msg.what);
                     break;
             }
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 1eb5141..5853696 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -34,6 +34,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
+import android.net.NetworkUtils;
 import android.os.Binder;
 import android.os.Environment;
 import android.os.Handler;
@@ -567,7 +568,7 @@
                 try {
                     ifcg = service.getInterfaceConfig(iface);
                     if (ifcg != null) {
-                        InetAddress addr = InetAddress.getByName(USB_NEAR_IFACE_ADDR);
+                        InetAddress addr = NetworkUtils.numericToInetAddress(USB_NEAR_IFACE_ADDR);
                         ifcg.addr = new LinkAddress(addr, USB_PREFIX_LENGTH);
                         if (enabled) {
                             ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up");
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 80a2a96..326eca7 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -19,7 +19,6 @@
 import com.android.internal.util.XmlUtils;
 
 import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -31,6 +30,8 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Environment;
+import android.os.Looper;
+import android.os.MessageQueue;
 import android.os.SystemProperties;
 import android.util.Slog;
 import android.util.Xml;
@@ -41,13 +42,10 @@
 import android.view.Surface;
 import android.view.WindowManager;
 
-import java.io.BufferedReader;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.IOException;
-import java.io.InputStreamReader;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
@@ -63,7 +61,7 @@
     private final Context mContext;
     private final WindowManagerService mWindowManagerService;
     
-    private static native void nativeInit(Callbacks callbacks);
+    private static native void nativeInit(Callbacks callbacks, MessageQueue messageQueue);
     private static native void nativeStart();
     private static native void nativeSetDisplaySize(int displayId, int width, int height);
     private static native void nativeSetDisplayOrientation(int displayId, int rotation);
@@ -83,6 +81,7 @@
             int injectorPid, int injectorUid, int syncMode, int timeoutMillis);
     private static native void nativeSetInputWindows(InputWindow[] windows);
     private static native void nativeSetInputDispatchMode(boolean enabled, boolean frozen);
+    private static native void nativeSetSystemUiVisibility(int visibility);
     private static native void nativeSetFocusedApplication(InputApplication application);
     private static native InputDevice nativeGetInputDevice(int deviceId);
     private static native void nativeGetInputConfiguration(Configuration configuration);
@@ -120,15 +119,12 @@
     public InputManager(Context context, WindowManagerService windowManagerService) {
         this.mContext = context;
         this.mWindowManagerService = windowManagerService;
-        
         this.mCallbacks = new Callbacks();
-        
-        init();
-    }
-    
-    private void init() {
+
+        Looper looper = windowManagerService.mH.getLooper();
+
         Slog.i(TAG, "Initializing input manager");
-        nativeInit(mCallbacks);
+        nativeInit(mCallbacks, looper.getQueue());
     }
     
     public void start() {
@@ -338,7 +334,11 @@
     public void setInputDispatchMode(boolean enabled, boolean frozen) {
         nativeSetInputDispatchMode(enabled, frozen);
     }
-    
+
+    public void setSystemUiVisibility(int visibility) {
+        nativeSetSystemUiVisibility(visibility);
+    }
+
     /**
      * Atomically transfers touch focus from one window to another as identified by
      * their input channels.  It is possible for multiple windows to have
@@ -361,7 +361,7 @@
         }
         return nativeTransferTouchFocus(fromChannel, toChannel);
     }
-    
+
     public void dump(PrintWriter pw) {
         String dumpStr = nativeDump();
         if (dumpStr != null) {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 0920d07..b5d84e8 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -8456,6 +8456,7 @@
 
     @Override
     public void statusBarVisibilityChanged(int visibility) {
+        mInputManager.setSystemUiVisibility(visibility);
         synchronized (mWindowMap) {
             final int N = mWindows.size();
             for (int i = 0; i < N; i++) {
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 0a50ff8..5ef234a 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -31,11 +31,13 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include <utils/Log.h>
+#include <utils/Looper.h>
 #include <utils/threads.h>
 
 #include <input/InputManager.h>
 #include <input/PointerController.h>
 
+#include <android_os_MessageQueue.h>
 #include <android_view_KeyEvent.h>
 #include <android_view_MotionEvent.h>
 #include <android_view_InputChannel.h>
@@ -136,7 +138,7 @@
     virtual ~NativeInputManager();
 
 public:
-    NativeInputManager(jobject callbacksObj);
+    NativeInputManager(jobject callbacksObj, const sp<Looper>& looper);
 
     inline sp<InputManager> getInputManager() const { return mInputManager; }
 
@@ -152,6 +154,7 @@
     void setInputWindows(JNIEnv* env, jobjectArray windowObjArray);
     void setFocusedApplication(JNIEnv* env, jobject applicationObj);
     void setInputDispatchMode(bool enabled, bool frozen);
+    void setSystemUiVisibility(int32_t visibility);
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -188,6 +191,7 @@
     sp<InputManager> mInputManager;
 
     jobject mCallbacksObj;
+    sp<Looper> mLooper;
 
     // Cached filtering policies.
     int32_t mFilterTouchEvents;
@@ -203,10 +207,15 @@
         int32_t displayWidth, displayHeight; // -1 when initialized
         int32_t displayOrientation;
 
+        // System UI visibility.
+        int32_t systemUiVisibility;
+
         // Pointer controller singleton, created and destroyed as needed.
         wp<PointerController> pointerController;
     } mLocked;
 
+    void updateInactivityFadeDelayLocked(const sp<PointerController>& controller);
+
     // Power manager interactions.
     bool isScreenOn();
     bool isScreenBright();
@@ -220,9 +229,10 @@
 
 
 
-NativeInputManager::NativeInputManager(jobject callbacksObj) :
-    mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
-    mMaxEventsPerSecond(-1) {
+NativeInputManager::NativeInputManager(jobject callbacksObj, const sp<Looper>& looper) :
+        mLooper(looper),
+        mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1), mVirtualKeyQuietTime(-1),
+        mMaxEventsPerSecond(-1) {
     JNIEnv* env = jniEnv();
 
     mCallbacksObj = env->NewGlobalRef(callbacksObj);
@@ -232,6 +242,8 @@
         mLocked.displayWidth = -1;
         mLocked.displayHeight = -1;
         mLocked.displayOrientation = ROTATION_0;
+
+        mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE;
     }
 
     sp<EventHub> eventHub = new EventHub();
@@ -408,7 +420,7 @@
             layer = -1;
         }
 
-        controller = new PointerController(layer);
+        controller = new PointerController(mLooper, layer);
         mLocked.pointerController = controller;
 
         controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight);
@@ -428,6 +440,8 @@
             }
             env->DeleteLocalRef(iconObj);
         }
+
+        updateInactivityFadeDelayLocked(controller);
     }
     return controller;
 }
@@ -571,6 +585,26 @@
     mInputManager->getDispatcher()->setInputDispatchMode(enabled, frozen);
 }
 
+void NativeInputManager::setSystemUiVisibility(int32_t visibility) {
+    AutoMutex _l(mLock);
+
+    if (mLocked.systemUiVisibility != visibility) {
+        mLocked.systemUiVisibility = visibility;
+
+        sp<PointerController> controller = mLocked.pointerController.promote();
+        if (controller != NULL) {
+            updateInactivityFadeDelayLocked(controller);
+        }
+    }
+}
+
+void NativeInputManager::updateInactivityFadeDelayLocked(const sp<PointerController>& controller) {
+    bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
+    controller->setInactivityFadeDelay(lightsOut
+            ? PointerController::INACTIVITY_FADE_DELAY_SHORT
+            : PointerController::INACTIVITY_FADE_DELAY_NORMAL);
+}
+
 bool NativeInputManager::isScreenOn() {
     return android_server_PowerManagerService_isScreenOn();
 }
@@ -751,9 +785,10 @@
 }
 
 static void android_server_InputManager_nativeInit(JNIEnv* env, jclass clazz,
-        jobject callbacks) {
+        jobject callbacks, jobject messageQueueObj) {
     if (gNativeInputManager == NULL) {
-        gNativeInputManager = new NativeInputManager(callbacks);
+        sp<Looper> looper = android_os_MessageQueue_getLooper(env, messageQueueObj);
+        gNativeInputManager = new NativeInputManager(callbacks, looper);
     } else {
         LOGE("Input manager already initialized.");
         jniThrowRuntimeException(env, "Input manager already initialized.");
@@ -972,6 +1007,15 @@
     gNativeInputManager->setInputDispatchMode(enabled, frozen);
 }
 
+static void android_server_InputManager_nativeSetSystemUiVisibility(JNIEnv* env,
+        jclass clazz, jint visibility) {
+    if (checkInputManagerUnitialized(env)) {
+        return;
+    }
+
+    gNativeInputManager->setSystemUiVisibility(visibility);
+}
+
 static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env,
         jclass clazz, jint deviceId) {
     if (checkInputManagerUnitialized(env)) {
@@ -1079,7 +1123,7 @@
 
 static JNINativeMethod gInputManagerMethods[] = {
     /* name, signature, funcPtr */
-    { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;)V",
+    { "nativeInit", "(Lcom/android/server/wm/InputManager$Callbacks;Landroid/os/MessageQueue;)V",
             (void*) android_server_InputManager_nativeInit },
     { "nativeStart", "()V",
             (void*) android_server_InputManager_nativeStart },
@@ -1108,6 +1152,8 @@
             (void*) android_server_InputManager_nativeSetFocusedApplication },
     { "nativeSetInputDispatchMode", "(ZZ)V",
             (void*) android_server_InputManager_nativeSetInputDispatchMode },
+    { "nativeSetSystemUiVisibility", "(I)V",
+            (void*) android_server_InputManager_nativeSetSystemUiVisibility },
     { "nativeGetInputDevice", "(I)Landroid/view/InputDevice;",
             (void*) android_server_InputManager_nativeGetInputDevice },
     { "nativeGetInputDeviceIds", "()[I",
diff --git a/telephony/java/com/android/internal/telephony/DataConnection.java b/telephony/java/com/android/internal/telephony/DataConnection.java
index e0b9603..89513fd 100644
--- a/telephony/java/com/android/internal/telephony/DataConnection.java
+++ b/telephony/java/com/android/internal/telephony/DataConnection.java
@@ -23,6 +23,7 @@
 import android.net.LinkAddress;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
+import android.net.NetworkUtils;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemProperties;
@@ -415,11 +416,13 @@
                             } else {
                                 addrPrefixLen = 0;
                             }
-                            if (!InetAddress.isNumeric(addr)) {
+                            InetAddress ia;
+                            try {
+                                ia = NetworkUtils.numericToInetAddress(addr);
+                            } catch (IllegalArgumentException e) {
                                 EventLogTags.writeBadIpAddress(addr);
                                 throw new UnknownHostException("Non-numeric ip addr=" + addr);
                             }
-                            InetAddress ia = InetAddress.getByName(addr);
                             if (addrPrefixLen == 0) {
                                 // Assume point to point
                                 addrPrefixLen = (ia instanceof Inet4Address) ? 32 : 128;
@@ -434,11 +437,13 @@
                     }
                     if (response.dnses != null && response.dnses.length > 0) {
                         for (String addr : response.dnses) {
-                            if (!InetAddress.isNumeric(addr)) {
+                            InetAddress ia;
+                            try {
+                                ia = NetworkUtils.numericToInetAddress(addr);
+                            } catch (IllegalArgumentException e) {
                                 EventLogTags.writePdpBadDnsAddress("dns=" + addr); 
                                 throw new UnknownHostException("Non-numeric dns addr=" + addr);
                             }
-                            InetAddress ia = InetAddress.getByName(addr);
                             linkProperties.addDns(ia);
                         }
                         result = SetupResult.SUCCESS;
@@ -448,12 +453,14 @@
                         dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2");
                         if (isDnsOk(dnsServers)) {
                             for (String dnsAddr : dnsServers) {
-                                if (!InetAddress.isNumeric(dnsAddr)) {
+                                InetAddress ia;
+                                try {
+                                    ia = NetworkUtils.numericToInetAddress(dnsAddr);
+                                } catch (IllegalArgumentException e) {
                                     EventLogTags.writePdpBadDnsAddress("dnsAddr=" + dnsAddr);
                                     throw new UnknownHostException("Non-numeric dns addr="
                                                 + dnsAddr);
                                 }
-                                InetAddress ia = InetAddress.getByName(dnsAddr);
                                 linkProperties.addDns(ia);
                             }
                             result = SetupResult.SUCCESS;
@@ -476,11 +483,13 @@
                         }
                     }
                     for (String addr : response.gateways) {
-                        if (!InetAddress.isNumeric(addr)) {
+                        InetAddress ia;
+                        try {
+                            ia = NetworkUtils.numericToInetAddress(addr);
+                        } catch (IllegalArgumentException e) {
                             EventLogTags.writePdpBadDnsAddress("gateway=" + addr);
                             throw new UnknownHostException("Non-numeric gateway addr=" + addr);
                         }
-                        InetAddress ia = InetAddress.getByName(addr);
                         linkProperties.addGateway(ia);
                     }
                     result = SetupResult.SUCCESS;
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index a6b54d8..20c8969 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -16,41 +16,47 @@
 
 package android.net.rtp;
 
+import android.media.AudioManager;
+
 import java.util.HashMap;
 import java.util.Map;
 
 /**
- * An AudioGroup acts as a router connected to the speaker, the microphone, and
- * {@link AudioStream}s. Its execution loop consists of four steps. First, for
- * each AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its
- * incoming packets and stores in its buffer. Then, if the microphone is
- * enabled, processes the recorded audio and stores in its buffer. Third, if the
- * speaker is enabled, mixes and playbacks buffers of all AudioStreams. Finally,
- * for each AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all
- * other buffers and sends back the encoded packets. An AudioGroup does nothing
- * if there is no AudioStream in it.
+ * An AudioGroup is an audio hub for the speaker, the microphone, and
+ * {@link AudioStream}s. Each of these components can be logically turned on
+ * or off by calling {@link #setMode(int)} or {@link RtpStream#setMode(int)}.
+ * The AudioGroup will go through these components and process them one by one
+ * within its execution loop. The loop consists of four steps. First, for each
+ * AudioStream not in {@link RtpStream#MODE_SEND_ONLY}, decodes its incoming
+ * packets and stores in its buffer. Then, if the microphone is enabled,
+ * processes the recorded audio and stores in its buffer. Third, if the speaker
+ * is enabled, mixes all AudioStream buffers and plays back. Finally, for each
+ * AudioStream not in {@link RtpStream#MODE_RECEIVE_ONLY}, mixes all other
+ * buffers and sends back the encoded packets. An AudioGroup does nothing if
+ * there is no AudioStream in it.
  *
  * <p>Few things must be noticed before using these classes. The performance is
  * highly related to the system load and the network bandwidth. Usually a
  * simpler {@link AudioCodec} costs fewer CPU cycles but requires more network
- * bandwidth, and vise versa. Using two AudioStreams at the same time not only
- * doubles the load but also the bandwidth. The condition varies from one device
- * to another, and developers must choose the right combination in order to get
- * the best result.
+ * bandwidth, and vise versa. Using two AudioStreams at the same time doubles
+ * not only the load but also the bandwidth. The condition varies from one
+ * device to another, and developers should choose the right combination in
+ * order to get the best result.</p>
  *
  * <p>It is sometimes useful to keep multiple AudioGroups at the same time. For
  * example, a Voice over IP (VoIP) application might want to put a conference
  * call on hold in order to make a new call but still allow people in the
- * previous call to talk to each other. This can be done easily using two
+ * conference call talking to each other. This can be done easily using two
  * AudioGroups, but there are some limitations. Since the speaker and the
- * microphone are shared globally, only one AudioGroup is allowed to run in
- * modes other than {@link #MODE_ON_HOLD}. In addition, before adding an
- * AudioStream into an AudioGroup, one should always put all other AudioGroups
- * into {@link #MODE_ON_HOLD}. That will make sure the audio driver correctly
- * initialized.</p>
+ * microphone are globally shared resources, only one AudioGroup at a time is
+ * allowed to run in a mode other than {@link #MODE_ON_HOLD}. The others will
+ * be unable to acquire these resources and fail silently.</p>
  *
  * <p class="note">Using this class requires
- * {@link android.Manifest.permission#RECORD_AUDIO} permission.</p>
+ * {@link android.Manifest.permission#RECORD_AUDIO} permission. Developers
+ * should set the audio mode to {@link AudioManager#MODE_IN_COMMUNICATION}
+ * using {@link AudioManager#setMode(int)} and change it back when none of
+ * the AudioGroups is in use.</p>
  *
  * @see AudioStream
  * @hide
@@ -58,13 +64,13 @@
 public class AudioGroup {
     /**
      * This mode is similar to {@link #MODE_NORMAL} except the speaker and
-     * the microphone are disabled.
+     * the microphone are both disabled.
      */
     public static final int MODE_ON_HOLD = 0;
 
     /**
      * This mode is similar to {@link #MODE_NORMAL} except the microphone is
-     * muted.
+     * disabled.
      */
     public static final int MODE_MUTED = 1;
 
@@ -137,20 +143,18 @@
     private native void nativeSetMode(int mode);
 
     // Package-private method used by AudioStream.join().
-    void add(AudioStream stream, AudioCodec codec, int dtmfType) {
-        synchronized (this) {
-            if (!mStreams.containsKey(stream)) {
-                try {
-                    int socket = stream.dup();
-                    String codecSpec = String.format("%d %s %s", codec.type,
-                            codec.rtpmap, codec.fmtp);
-                    nativeAdd(stream.getMode(), socket,
-                            stream.getRemoteAddress().getHostAddress(),
-                            stream.getRemotePort(), codecSpec, dtmfType);
-                    mStreams.put(stream, socket);
-                } catch (NullPointerException e) {
-                    throw new IllegalStateException(e);
-                }
+    synchronized void add(AudioStream stream, AudioCodec codec, int dtmfType) {
+        if (!mStreams.containsKey(stream)) {
+            try {
+                int socket = stream.dup();
+                String codecSpec = String.format("%d %s %s", codec.type,
+                        codec.rtpmap, codec.fmtp);
+                nativeAdd(stream.getMode(), socket,
+                        stream.getRemoteAddress().getHostAddress(),
+                        stream.getRemotePort(), codecSpec, dtmfType);
+                mStreams.put(stream, socket);
+            } catch (NullPointerException e) {
+                throw new IllegalStateException(e);
             }
         }
     }
@@ -159,12 +163,10 @@
             int remotePort, String codecSpec, int dtmfType);
 
     // Package-private method used by AudioStream.join().
-    void remove(AudioStream stream) {
-        synchronized (this) {
-            Integer socket = mStreams.remove(stream);
-            if (socket != null) {
-                nativeRemove(socket);
-            }
+    synchronized void remove(AudioStream stream) {
+        Integer socket = mStreams.remove(stream);
+        if (socket != null) {
+            nativeRemove(socket);
         }
     }
 
diff --git a/voip/java/android/net/rtp/AudioStream.java b/voip/java/android/net/rtp/AudioStream.java
index 0edae6b..b45cc5e 100644
--- a/voip/java/android/net/rtp/AudioStream.java
+++ b/voip/java/android/net/rtp/AudioStream.java
@@ -27,8 +27,8 @@
  * configured {@link AudioCodec}. On the other side, An {@link AudioGroup}
  * represents a local endpoint which mixes all the AudioStreams and optionally
  * interacts with the speaker and the microphone at the same time. The simplest
- * usage includes one for each endpoints. For other combinations, users should
- * be aware of the limitations described in {@link AudioGroup}.
+ * usage includes one for each endpoints. For other combinations, developers
+ * should be aware of the limitations described in {@link AudioGroup}.
  *
  * <p>An AudioStream becomes busy when it joins an AudioGroup. In this case most
  * of the setter methods are disabled. This is designed to ease the task of
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
index 169b2d6..6b79210 100644
--- a/wifi/java/android/net/wifi/SupplicantState.java
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -152,6 +152,26 @@
         return state != UNINITIALIZED && state != INVALID;
     }
 
+    static boolean isConnecting(SupplicantState state) {
+        switch(state) {
+            case ASSOCIATING:
+            case ASSOCIATED:
+            case FOUR_WAY_HANDSHAKE:
+            case GROUP_HANDSHAKE:
+            case COMPLETED:
+                return true;
+            case DISCONNECTED:
+            case INACTIVE:
+            case SCANNING:
+            case DORMANT:
+            case UNINITIALIZED:
+            case INVALID:
+                return false;
+            default:
+                throw new IllegalArgumentException("Unknown supplicant state");
+        }
+    }
+
     /** Implement the Parcelable interface {@hide} */
     public int describeContents() {
         return 0;
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index d411715..f6317f5 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -686,13 +686,15 @@
                         } else if (key.equals(IP_ASSIGNMENT_KEY)) {
                             ipAssignment = IpAssignment.valueOf(in.readUTF());
                         } else if (key.equals(LINK_ADDRESS_KEY)) {
-                            LinkAddress linkAddr = new LinkAddress(InetAddress.getByName(
-                                    in.readUTF()), in.readInt());
+                            LinkAddress linkAddr = new LinkAddress(
+                                    NetworkUtils.numericToInetAddress(in.readUTF()), in.readInt());
                             linkProperties.addLinkAddress(linkAddr);
                         } else if (key.equals(GATEWAY_KEY)) {
-                            linkProperties.addGateway(InetAddress.getByName(in.readUTF()));
+                            linkProperties.addGateway(
+                                    NetworkUtils.numericToInetAddress(in.readUTF()));
                         } else if (key.equals(DNS_KEY)) {
-                            linkProperties.addDns(InetAddress.getByName(in.readUTF()));
+                            linkProperties.addDns(
+                                    NetworkUtils.numericToInetAddress(in.readUTF()));
                         } else if (key.equals(PROXY_SETTINGS_KEY)) {
                             proxySettings = ProxySettings.valueOf(in.readUTF());
                         } else if (key.equals(PROXY_HOST_KEY)) {
@@ -706,7 +708,7 @@
                         } else {
                             Log.e(TAG, "Ignore unknown key " + key + "while reading");
                         }
-                    } catch (UnknownHostException e) {
+                    } catch (IllegalArgumentException e) {
                         Log.e(TAG, "Ignore invalid address while reading" + e);
                     }
                 } while (true);
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index d6f8e51..589d88c 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -344,6 +344,9 @@
      */
     private static final long DEFAULT_SCAN_INTERVAL_MS = 60 * 1000; /* 1 minute */
 
+    private static final int MIN_RSSI = -200;
+    private static final int MAX_RSSI = 256;
+
     /* Default parent state */
     private HierarchicalState mDefaultState = new DefaultState();
     /* Temporary initial state */
@@ -996,7 +999,8 @@
                         ifcg = service.getInterfaceConfig(intf);
                         if (ifcg != null) {
                             /* IP/netmask: 192.168.43.1/255.255.255.0 */
-                            ifcg.addr = new LinkAddress(InetAddress.getByName("192.168.43.1"), 24);
+                            ifcg.addr = new LinkAddress(NetworkUtils.numericToInetAddress(
+                                    "192.168.43.1"), 24);
                             ifcg.interfaceFlags = "[up]";
 
                             service.setInterfaceConfig(intf, ifcg);
@@ -1238,7 +1242,7 @@
      */
     private void fetchRssiAndLinkSpeedNative() {
         int newRssi = WifiNative.getRssiCommand();
-        if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values
+        if (newRssi != -1 && MIN_RSSI < newRssi && newRssi < MAX_RSSI) { // screen out invalid values
             /* some implementations avoid negative values by adding 256
              * so we need to adjust for that here.
              */
@@ -1263,7 +1267,7 @@
             }
             mLastSignalLevel = newSignalLevel;
         } else {
-            mWifiInfo.setRssi(-200);
+            mWifiInfo.setRssi(MIN_RSSI);
         }
         int newLinkSpeed = WifiNative.getLinkSpeedCommand();
         if (newLinkSpeed != -1) {
@@ -1385,15 +1389,17 @@
         /* Disable interface */
         NetworkUtils.disableInterface(mInterfaceName);
 
-        /* send event to CM & network change broadcast */
-        setNetworkDetailedState(DetailedState.DISCONNECTED);
-        sendNetworkStateChangeBroadcast(mLastBssid);
-
         /* Reset data structures */
         mWifiInfo.setInetAddress(null);
         mWifiInfo.setBSSID(null);
         mWifiInfo.setSSID(null);
         mWifiInfo.setNetworkId(-1);
+        mWifiInfo.setRssi(MIN_RSSI);
+        mWifiInfo.setLinkSpeed(-1);
+
+        /* send event to CM & network change broadcast */
+        setNetworkDetailedState(DetailedState.DISCONNECTED);
+        sendNetworkStateChangeBroadcast(mLastBssid);
 
         /* Clear network properties */
         mLinkProperties.clear();
@@ -2362,7 +2368,10 @@
                     // 50023 supplicant_state_changed (custom|1|5)
                     EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
                     mWifiInfo.setSupplicantState(state);
-                    mWifiInfo.setNetworkId(stateChangeResult.networkId);
+                    // Network id is only valid when we start connecting
+                    if (SupplicantState.isConnecting(state)) {
+                        mWifiInfo.setNetworkId(stateChangeResult.networkId);
+                    }
                     if (state == SupplicantState.ASSOCIATING) {
                         /* BSSID is valid only in ASSOCIATING state */
                         mWifiInfo.setBSSID(stateChangeResult.BSSID);
@@ -2740,6 +2749,15 @@
             }
             return HANDLED;
         }
+        @Override
+        public void exit() {
+            /* If a scan result is pending in connected state, the supplicant
+             * is in SCAN_ONLY_MODE. Restore CONNECT_MODE on exit
+             */
+            if (mScanResultIsPending) {
+                WifiNative.setScanResultHandlingCommand(CONNECT_MODE);
+            }
+        }
     }
 
     class DisconnectingState extends HierarchicalState {