Merge "Wake screen from external HID peripherals."
diff --git a/api/current.xml b/api/current.xml
index f98edab..c1d2f66 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -220707,6 +220707,17 @@
 <parameter name="autoScale" type="boolean">
 </parameter>
 </method>
+<method name="buildLayer"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="cancelLongPress"
  return="void"
  abstract="false"
@@ -233470,6 +233481,17 @@
  visibility="protected"
 >
 </method>
+<method name="getBackgroundColor"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getDetachWallpaper"
  return="boolean"
  abstract="false"
@@ -233755,6 +233777,19 @@
 <parameter name="listener" type="android.view.animation.Animation.AnimationListener">
 </parameter>
 </method>
+<method name="setBackgroundColor"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="bg" type="int">
+</parameter>
+</method>
 <method name="setDetachWallpaper"
  return="void"
  abstract="false"
@@ -266669,7 +266704,7 @@
  deprecated="not deprecated"
  visibility="public"
 >
-<parameter name="t" type="T">
+<parameter name="arg0" type="T">
 </parameter>
 </method>
 </interface>
diff --git a/core/java/android/animation/LayoutTransition.java b/core/java/android/animation/LayoutTransition.java
index 8b59554..22dd3c7 100644
--- a/core/java/android/animation/LayoutTransition.java
+++ b/core/java/android/animation/LayoutTransition.java
@@ -168,7 +168,9 @@
      */
     private final HashMap<View, Animator> pendingAnimations = new HashMap<View, Animator>();
     private final HashMap<View, Animator> currentChangingAnimations = new HashMap<View, Animator>();
-    private final HashMap<View, Animator> currentVisibilityAnimations =
+    private final HashMap<View, Animator> currentAppearingAnimations =
+            new HashMap<View, Animator>();
+    private final HashMap<View, Animator> currentDisappearingAnimations =
             new HashMap<View, Animator>();
 
     /**
@@ -709,7 +711,8 @@
      * @return true if any animations in the transition are running.
      */
     public boolean isRunning() {
-        return (currentChangingAnimations.size() > 0 || currentVisibilityAnimations.size() > 0);
+        return (currentChangingAnimations.size() > 0 || currentAppearingAnimations.size() > 0 ||
+                currentDisappearingAnimations.size() > 0);
     }
 
     /**
@@ -721,17 +724,74 @@
      * @hide
      */
     public void cancel() {
-        HashMap<View, Animator> currentAnimCopy =
-                (HashMap<View, Animator>) currentChangingAnimations.clone();
-        for (Animator anim : currentAnimCopy.values()) {
-            anim.cancel();
+        if (currentChangingAnimations.size() > 0) {
+            HashMap<View, Animator> currentAnimCopy =
+                    (HashMap<View, Animator>) currentChangingAnimations.clone();
+            for (Animator anim : currentAnimCopy.values()) {
+                anim.cancel();
+            }
+            currentChangingAnimations.clear();
         }
-        currentChangingAnimations.clear();
-        currentAnimCopy = (HashMap<View, Animator>) currentVisibilityAnimations.clone();
-        for (Animator anim : currentAnimCopy.values()) {
-            anim.end();
+        if (currentAppearingAnimations.size() > 0) {
+            HashMap<View, Animator> currentAnimCopy =
+                    (HashMap<View, Animator>) currentAppearingAnimations.clone();
+            for (Animator anim : currentAnimCopy.values()) {
+                anim.end();
+            }
+            currentAppearingAnimations.clear();
         }
-        currentVisibilityAnimations.clear();
+        if (currentDisappearingAnimations.size() > 0) {
+            HashMap<View, Animator> currentAnimCopy =
+                    (HashMap<View, Animator>) currentDisappearingAnimations.clone();
+            for (Animator anim : currentAnimCopy.values()) {
+                anim.end();
+            }
+            currentDisappearingAnimations.clear();
+        }
+    }
+
+    /**
+     * Cancels the specified type of transition. Note that we cancel() the changing animations
+     * but end() the visibility animations. This is because this method is currently called
+     * in the context of starting a new transition, so we want to move things from their mid-
+     * transition positions, but we want them to have their end-transition visibility.
+     *
+     * @hide
+     */
+    public void cancel(int transitionType) {
+        switch (transitionType) {
+            case CHANGE_APPEARING:
+            case CHANGE_DISAPPEARING:
+                if (currentChangingAnimations.size() > 0) {
+                    HashMap<View, Animator> currentAnimCopy =
+                            (HashMap<View, Animator>) currentChangingAnimations.clone();
+                    for (Animator anim : currentAnimCopy.values()) {
+                        anim.cancel();
+                    }
+                    currentChangingAnimations.clear();
+                }
+                break;
+            case APPEARING:
+                if (currentAppearingAnimations.size() > 0) {
+                    HashMap<View, Animator> currentAnimCopy =
+                            (HashMap<View, Animator>) currentAppearingAnimations.clone();
+                    for (Animator anim : currentAnimCopy.values()) {
+                        anim.end();
+                    }
+                    currentAppearingAnimations.clear();
+                }
+                break;
+            case DISAPPEARING:
+                if (currentDisappearingAnimations.size() > 0) {
+                    HashMap<View, Animator> currentAnimCopy =
+                            (HashMap<View, Animator>) currentDisappearingAnimations.clone();
+                    for (Animator anim : currentAnimCopy.values()) {
+                        anim.end();
+                    }
+                    currentDisappearingAnimations.clear();
+                }
+                break;
+        }
     }
 
     /**
@@ -741,7 +801,7 @@
      * @param child The View being added to the ViewGroup.
      */
     private void runAppearingTransition(final ViewGroup parent, final View child) {
-        Animator currentAnimation = currentVisibilityAnimations.get(child);
+        Animator currentAnimation = currentDisappearingAnimations.get(child);
         if (currentAnimation != null) {
             currentAnimation.cancel();
         }
@@ -764,14 +824,14 @@
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator anim) {
-                    currentVisibilityAnimations.remove(child);
+                    currentAppearingAnimations.remove(child);
                     for (TransitionListener listener : mListeners) {
                         listener.endTransition(LayoutTransition.this, parent, child, APPEARING);
                     }
                 }
             });
         }
-        currentVisibilityAnimations.put(child, anim);
+        currentAppearingAnimations.put(child, anim);
         anim.start();
     }
 
@@ -782,7 +842,7 @@
      * @param child The View being removed from the ViewGroup.
      */
     private void runDisappearingTransition(final ViewGroup parent, final View child) {
-        Animator currentAnimation = currentVisibilityAnimations.get(child);
+        Animator currentAnimation = currentAppearingAnimations.get(child);
         if (currentAnimation != null) {
             currentAnimation.cancel();
         }
@@ -802,7 +862,7 @@
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator anim) {
-                    currentVisibilityAnimations.remove(child);
+                    currentDisappearingAnimations.remove(child);
                     for (TransitionListener listener : mListeners) {
                         listener.endTransition(LayoutTransition.this, parent, child, DISAPPEARING);
                     }
@@ -812,7 +872,7 @@
         if (anim instanceof ObjectAnimator) {
             ((ObjectAnimator) anim).setCurrentPlayTime(0);
         }
-        currentVisibilityAnimations.put(child, anim);
+        currentDisappearingAnimations.put(child, anim);
         anim.start();
     }
 
@@ -826,9 +886,10 @@
      * @param child The View being added to the ViewGroup.
      */
     public void addChild(ViewGroup parent, View child) {
-        if (isRunning()) {
-            cancel();
-        }
+        // Want disappearing animations to finish up before proceeding
+        cancel(DISAPPEARING);
+        // Also, cancel changing animations so that we start fresh ones from current locations
+        cancel(CHANGE_APPEARING);
         if (mListeners != null) {
             for (TransitionListener listener : mListeners) {
                 listener.startTransition(this, parent, child, APPEARING);
@@ -861,9 +922,10 @@
      * @param child The View being removed from the ViewGroup.
      */
     public void removeChild(ViewGroup parent, View child) {
-        if (isRunning()) {
-            cancel();
-        }
+        // Want appearing animations to finish up before proceeding
+        cancel(APPEARING);
+        // Also, cancel changing animations so that we start fresh ones from current locations
+        cancel(CHANGE_DISAPPEARING);
         if (mListeners != null) {
             for (TransitionListener listener : mListeners) {
                 listener.startTransition(this, parent, child, DISAPPEARING);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8f9a76b..b409f2f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -98,7 +98,6 @@
 import java.util.regex.Pattern;
 
 import dalvik.system.CloseGuard;
-import dalvik.system.SamplingProfiler;
 
 final class SuperNotCalledException extends AndroidRuntimeException {
     public SuperNotCalledException(String msg) {
@@ -355,6 +354,7 @@
         boolean restrictedBackupMode;
         Configuration config;
         boolean handlingProfiling;
+        Bundle coreSettings;
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
         }
@@ -552,7 +552,7 @@
                 ComponentName instrumentationName, String profileFile,
                 Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
                 int debugMode, boolean isRestrictedBackupMode, Configuration config,
-                Map<String, IBinder> services) {
+                Map<String, IBinder> services, Bundle coreSettings) {
 
             if (services != null) {
                 // Setup the service cache in the ServiceManager
@@ -570,6 +570,7 @@
             data.debugMode = debugMode;
             data.restrictedBackupMode = isRestrictedBackupMode;
             data.config = config;
+            data.coreSettings = coreSettings;
             queueOrSendMessage(H.BIND_APPLICATION, data);
         }
 
@@ -896,6 +897,10 @@
         private void printRow(PrintWriter pw, String format, Object...objs) {
             pw.println(String.format(format, objs));
         }
+
+        public void setCoreSettings(Bundle settings) {
+            queueOrSendMessage(H.SET_CORE_SETTINGS, settings);
+        }
     }
 
     private final class H extends Handler {
@@ -937,6 +942,7 @@
         public static final int DUMP_HEAP               = 135;
         public static final int DUMP_ACTIVITY           = 136;
         public static final int SLEEPING                = 137;
+        public static final int SET_CORE_SETTINGS       = 138;
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
                 switch (code) {
@@ -978,6 +984,7 @@
                     case DUMP_HEAP: return "DUMP_HEAP";
                     case DUMP_ACTIVITY: return "DUMP_ACTIVITY";
                     case SLEEPING: return "SLEEPING";
+                    case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS";
                 }
             }
             return "(unknown)";
@@ -1113,6 +1120,9 @@
                 case SLEEPING:
                     handleSleeping((IBinder)msg.obj, msg.arg1 != 0);
                     break;
+                case SET_CORE_SETTINGS:
+                    handleSetCoreSettings((Bundle) msg.obj);
+                    break;
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
         }
@@ -2709,6 +2719,14 @@
         }
     }
 
+    private void handleSetCoreSettings(Bundle coreSettings) {
+        if (mBoundApplication != null) {
+            synchronized (mBoundApplication) {
+                mBoundApplication.coreSettings = coreSettings;
+            }
+        }
+    }
+
     private final void deliverResults(ActivityClientRecord r, List<ResultInfo> results) {
         final int N = results.size();
         for (int i=0; i<N; i++) {
@@ -3971,6 +3989,20 @@
         }
     }
 
+    public int getIntCoreSetting(String key, int defaultValue) {
+        if (mBoundApplication == null) {
+            return defaultValue;
+        }
+        synchronized (mBoundApplication) {
+            Bundle coreSettings = mBoundApplication.coreSettings;
+            if (coreSettings != null) {
+                return coreSettings.getInt(key, defaultValue);
+            } else {
+                return defaultValue;
+            }
+        }
+    }
+
     public static final void main(String[] args) {
         SamplingProfilerIntegration.start();
 
diff --git a/core/java/android/app/AppGlobals.java b/core/java/android/app/AppGlobals.java
index 9a39129..55515b8 100644
--- a/core/java/android/app/AppGlobals.java
+++ b/core/java/android/app/AppGlobals.java
@@ -38,12 +38,23 @@
     public static String getInitialPackage() {
         return ActivityThread.currentPackageName();
     }
-    
+
     /**
      * Return the raw interface to the package manager.
-     * @return
+     * @return The package manager.
      */
     public static IPackageManager getPackageManager() {
         return ActivityThread.getPackageManager();
     }
+
+    /**
+     * Gets the value of an integer core setting.
+     *
+     * @param key The setting key.
+     * @param defaultValue The setting default value.
+     * @return The core settings.
+     */
+    public static int getIntCoreSetting(String key, int defaultValue) {
+        return ActivityThread.currentActivityThread().getIntCoreSetting(key, defaultValue);
+    }
 }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index ef92933..aa26b04 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -257,10 +257,11 @@
             boolean restrictedBackupMode = (data.readInt() != 0);
             Configuration config = Configuration.CREATOR.createFromParcel(data);
             HashMap<String, IBinder> services = data.readHashMap(null);
+            Bundle coreSettings = data.readBundle();
             bindApplication(packageName, info,
                             providers, testName, profileName,
                             testArgs, testWatcher, testMode, restrictedBackupMode,
-                            config, services);
+                            config, services, coreSettings);
             return true;
         }
         
@@ -454,6 +455,13 @@
             }
             return true;
         }
+
+        case SET_CORE_SETTINGS: {
+            data.enforceInterface(IApplicationThread.descriptor);
+            Bundle settings = data.readBundle();
+            setCoreSettings(settings);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -712,7 +720,7 @@
             List<ProviderInfo> providers, ComponentName testName,
             String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode,
             boolean restrictedBackupMode, Configuration config,
-            Map<String, IBinder> services) throws RemoteException {
+            Map<String, IBinder> services, Bundle coreSettings) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeString(packageName);
@@ -731,6 +739,7 @@
         data.writeInt(restrictedBackupMode ? 1 : 0);
         config.writeToParcel(data, 0);
         data.writeMap(services);
+        data.writeBundle(coreSettings);
         mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -938,4 +947,11 @@
         mRemote.transact(DUMP_ACTIVITY_TRANSACTION, data, null, 0);
         data.recycle();
     }
+
+    public void setCoreSettings(Bundle coreSettings) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeBundle(coreSettings);
+        mRemote.transact(SET_CORE_SETTINGS, data, null, IBinder.FLAG_ONEWAY);
+    }
 }
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 16c3c5c..55177a9 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -82,7 +82,8 @@
     void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
             ComponentName testName, String profileName, Bundle testArguments, 
             IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
-            Configuration config, Map<String, IBinder> services) throws RemoteException;
+            Configuration config, Map<String, IBinder> services,
+            Bundle coreSettings) throws RemoteException;
     void scheduleExit() throws RemoteException;
     void scheduleSuicide() throws RemoteException;
     void requestThumbnail(IBinder token) throws RemoteException;
@@ -110,6 +111,7 @@
     void scheduleCrash(String msg) throws RemoteException;
     void dumpActivity(FileDescriptor fd, IBinder servicetoken, String prefix, String[] args)
             throws RemoteException;
+    void setCoreSettings(Bundle coreSettings) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -151,4 +153,5 @@
     int DUMP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
     int CLEAR_DNS_CACHE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
     int SET_HTTP_PROXY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
+    int SET_CORE_SETTINGS = IBinder.FIRST_CALL_TRANSACTION+39;
 }
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 1718189..6deb5a0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2698,6 +2698,12 @@
             "accessibility_web_content_key_bindings";
 
         /**
+         * The timout for considering a press to be a long press in milliseconds.
+         * @hide
+         */
+        public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
+
+        /**
          * Setting to always use the default text-to-speech settings regardless
          * of the application settings.
          * 1 = override application settings,
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index fc01ef2..53fa7c2 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -1169,6 +1169,30 @@
         return ret;
     }
 
+    /**
+     * Returns the next cursor position in the run.  This avoids placing the cursor between
+     * surrogates, between characters that form conjuncts, between base characters and combining
+     * marks, or within a reordering cluster.
+     *
+     * <p>The context is the shaping context for cursor movement, generally the bounds of the metric
+     * span enclosing the cursor in the direction of movement.
+     * <code>contextStart</code>, <code>contextEnd</code> and <code>offset</code> are relative to
+     * the start of the string.</p>
+     *
+     * <p>If cursorOpt is CURSOR_AT and the offset is not a valid cursor position,
+     * this returns -1.  Otherwise this will never return a value before contextStart or after
+     * contextEnd.</p>
+     *
+     * @param contextStart the start index of the context
+     * @param contextEnd the (non-inclusive) end index of the context
+     * @param flags either DIRECTION_RTL or DIRECTION_LTR
+     * @param offset the cursor position to move from
+     * @param cursorOpt how to move the cursor, one of CURSOR_AFTER,
+     * CURSOR_AT_OR_AFTER, CURSOR_BEFORE,
+     * CURSOR_AT_OR_BEFORE, or CURSOR_AT
+     * @param p the Paint object that is requesting this information
+     * @return the offset of the next position, or -1
+     */
     public int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset,
             int cursorOpt, Paint p) {
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 2170d72..32c9e27 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8488,6 +8488,7 @@
      *         {@link #LAYER_TYPE_HARDWARE}
      *
      * @see #setLayerType(int, android.graphics.Paint)
+     * @see #buildLayer() 
      * @see #LAYER_TYPE_NONE
      * @see #LAYER_TYPE_SOFTWARE
      * @see #LAYER_TYPE_HARDWARE
@@ -8497,6 +8498,36 @@
     }
 
     /**
+     * Forces this view's layer to be created and this view to be rendered
+     * into its layer. If this view's layer type is set to {@link #LAYER_TYPE_NONE},
+     * invoking this method will have no effect.
+     * 
+     * This method can for instance be used to render a view into its layer before
+     * starting an animation. If this view is complex, rendering into the layer
+     * before starting the animation will avoid skipping frames.
+     * 
+     * @throws IllegalStateException If this view is not attached to a window
+     * 
+     * @see #setLayerType(int, android.graphics.Paint) 
+     */
+    public void buildLayer() {
+        if (mLayerType == LAYER_TYPE_NONE) return;
+
+        if (mAttachInfo == null) {
+            throw new IllegalStateException("This view must be attached to a window first");
+        }
+
+        switch (mLayerType) {
+            case LAYER_TYPE_HARDWARE:
+                getHardwareLayer();
+                break;
+            case LAYER_TYPE_SOFTWARE:
+                buildDrawingCache(true);
+                break;
+        }
+    }
+
+    /**
      * <p>Returns a hardware layer that can be used to draw this view again
      * without executing its draw method.</p>
      *
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index cc4e89c..d95c5b0 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -16,8 +16,11 @@
 
 package android.view;
 
+import android.app.AppGlobals;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.os.Bundle;
+import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.SparseArray;
 
@@ -74,7 +77,7 @@
      * Defines the duration in milliseconds before a press turns into
      * a long press
      */
-    private static final int LONG_PRESS_TIMEOUT = 500;
+    private static final int DEFAULTLONG_PRESS_TIMEOUT = 500;
     
     /**
      * Defines the duration in milliseconds a user needs to hold down the
@@ -320,15 +323,16 @@
     public static int getPressedStateDuration() {
         return PRESSED_STATE_DURATION;
     }
-    
+
     /**
      * @return the duration in milliseconds before a press turns into
      * a long press
      */
     public static int getLongPressTimeout() {
-        return LONG_PRESS_TIMEOUT;
+        return AppGlobals.getIntCoreSetting(Settings.Secure.LONG_PRESS_TIMEOUT,
+                DEFAULTLONG_PRESS_TIMEOUT);
     }
-    
+
     /**
      * @return the duration in milliseconds we will wait to see if a touch event
      * is a tap or a scroll. If the user does not move within this interval, it is
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index af4c221..3153ac5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3001,8 +3001,10 @@
     private void addViewInner(View child, int index, LayoutParams params,
             boolean preventRequestLayout) {
 
-        if (mTransition != null && mTransition.isRunning()) {
-            mTransition.cancel();
+        if (mTransition != null) {
+            // Don't prevent other add transitions from completing, but cancel remove
+            // transitions to let them complete the process before we add to the container
+            mTransition.cancel(LayoutTransition.DISAPPEARING);
         }
 
         if (child.getParent() != null) {
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 13d0ec1..87c759c 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -181,6 +181,11 @@
     private int mZAdjustment;
 
     /**
+     * Desired background color behind animation.
+     */
+    private int mBackgroundColor;
+
+    /**
      * scalefactor to apply to pivot points, etc. during animation. Subclasses retrieve the
      * value via getScaleFactor().
      */
@@ -236,6 +241,8 @@
 
         setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL));
         
+        setBackgroundColor(a.getInt(com.android.internal.R.styleable.Animation_background, 0));
+
         setDetachWallpaper(a.getBoolean(com.android.internal.R.styleable.Animation_detachWallpaper, false));
         
         ensureInterpolator();
@@ -568,6 +575,16 @@
     }
     
     /**
+     * Set background behind animation.
+     *
+     * @param bg The background color.  If 0, no background.  Currently must
+     * be black, with any desired alpha level.
+     */
+    public void setBackgroundColor(int bg) {
+        mBackgroundColor = bg;
+    }
+
+    /**
      * The scale factor is set by the call to <code>getTransformation</code>. Overrides of 
      * {@link #getTransformation(long, Transformation, float)} will get this value
      * directly. Overrides of {@link #applyTransformation(float, Transformation)} can
@@ -690,6 +707,13 @@
     }
 
     /**
+     * Returns the background color behind the animation.
+     */
+    public int getBackgroundColor() {
+        return mBackgroundColor;
+    }
+
+    /**
      * Return value of {@link #setDetachWallpaper(boolean)}.
      * @attr ref android.R.styleable#Animation_detachWallpaper
      */
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 88c8b2a..28f64a9 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -227,10 +227,13 @@
         assert density > 0;
 
         if (Math.abs(density - mDefaultScale) > MINIMUM_SCALE_INCREMENT) {
+            // Remember the current zoom density before it gets changed.
+            final float originalDefault = mDefaultScale;
             // set the new default density
             setDefaultZoomScale(density);
+            float scaleChange = (originalDefault > 0.0) ? density / originalDefault: 1.0f;
             // adjust the scale if it falls outside the new zoom bounds
-            setZoomScale(mActualScale, true);
+            setZoomScale(mActualScale * scaleChange, true);
         }
     }
 
@@ -629,7 +632,7 @@
     }
 
     /* package */ float getZoomOverviewScale() {
-        return computeScaleWithLimits(mWebView.getViewWidth() * mInvZoomOverviewWidth);
+        return mWebView.getViewWidth() * mInvZoomOverviewWidth;
     }
 
     public boolean isInZoomOverview() {
@@ -882,6 +885,7 @@
 
         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.
@@ -956,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;
@@ -971,7 +979,7 @@
                 scale = overviewScale;
                 if (!settings.getUseWideViewPort()
                     || !settings.getLoadWithOverviewMode()) {
-                    scale = Math.max(viewState.mTextWrapScale, scale);
+                    scale = Math.max(mDefaultScale, scale);
                 }
                 if (settings.isNarrowColumnLayout() &&
                     settings.getUseFixedViewport()) {
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index b479543..66d982f 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -18,7 +18,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:detachWallpaper="true" android:shareInterpolator="false">
+        android:background="#ff000000" android:shareInterpolator="false">
     <scale 	android:fromXScale="1.0" android:toXScale="1.0"
             android:fromYScale="0.95" android:toYScale="1.0"
             android:pivotX="50%p" android:pivotY="50%p"
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index e561e97..312946b 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -18,7 +18,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:detachWallpaper="true" android:shareInterpolator="false">
+        android:background="#ff000000" android:shareInterpolator="false">
     <scale android:fromXScale="1.0" android:toXScale="1.0"
             android:fromYScale="1.0" android:toYScale="0.0"
             android:pivotX="50%p" android:pivotY="50%p"
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index b3b3fd1..3dda797 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -18,7 +18,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:detachWallpaper="true" android:shareInterpolator="false">
+        android:background="#ff000000" android:shareInterpolator="false">
     <scale android:fromXScale="1.0" android:toXScale="1.0"
             android:fromYScale=".9" android:toYScale="1.0"
             android:pivotX="50%p" android:pivotY="50%p"
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index 763b581..e377c2a 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -18,7 +18,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-        android:detachWallpaper="true" android:shareInterpolator="false">
+        android:background="#ff000000" android:shareInterpolator="false">
     <scale android:fromXScale="1.0" android:toXScale="1.0"
             android:fromYScale="1.0" android:toYScale="0.0"
             android:pivotX="50%p" android:pivotY="50%p"
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/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 3b225a4..d3b1062 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3596,6 +3596,10 @@
                  content for the duration of the animation. -->
             <enum name="bottom" value="-1" />
         </attr>
+        <!-- Special background behind animation.  Only for use with window
+             animations.  Can only be a color, and only black.  If 0, the
+             default, there is no background. -->
+        <attr name="background" />
         <!-- Special option for window animations: if this window is on top
              of a wallpaper, don't animate the wallpaper with it. -->
         <attr name="detachWallpaper" format="boolean" />
diff --git a/data/sounds/effects/ogg/Lock.ogg b/data/sounds/effects/ogg/Lock.ogg
index 2e57d9e..a25513f 100644
--- a/data/sounds/effects/ogg/Lock.ogg
+++ b/data/sounds/effects/ogg/Lock.ogg
Binary files differ
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/drm/java/android/drm/DrmManagerClient.java b/drm/java/android/drm/DrmManagerClient.java
index 6caf678..c541456 100644
--- a/drm/java/android/drm/DrmManagerClient.java
+++ b/drm/java/android/drm/DrmManagerClient.java
@@ -479,6 +479,9 @@
      */
     public int acquireRights(DrmInfoRequest drmInfoRequest) {
         DrmInfo drmInfo = acquireDrmInfo(drmInfoRequest);
+        if (null == drmInfo) {
+            return ERROR_UNKNOWN;
+        }
         return processDrmInfo(drmInfo);
     }
 
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/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index cce9129..7956788 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -55,17 +55,25 @@
     virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
     virtual sp<IOMX>            getOMX() = 0;
 
-    // codecs usage tracking for the battery app
+    // codecs and audio devices usage tracking for the battery app
     enum BatteryDataBits {
         // tracking audio codec
-        kBatteryDataTrackAudio          = 1,
+        kBatteryDataTrackAudio          = 0x1,
         // tracking video codec
-        kBatteryDataTrackVideo          = 2,
+        kBatteryDataTrackVideo          = 0x2,
         // codec is started, otherwise codec is paused
-        kBatteryDataCodecStarted        = 4,
+        kBatteryDataCodecStarted        = 0x4,
         // tracking decoder (for media player),
         // otherwise tracking encoder (for media recorder)
-        kBatteryDataTrackDecoder        = 8,
+        kBatteryDataTrackDecoder        = 0x8,
+        // start to play an audio on an audio device
+        kBatteryDataAudioFlingerStart   = 0x10,
+        // stop/pause the audio playback
+        kBatteryDataAudioFlingerStop    = 0x20,
+        // audio is rounted to speaker
+        kBatteryDataSpeakerOn           = 0x40,
+        // audio is rounted to devices other than speaker
+        kBatteryDataOtherAudioDeviceOn  = 0x80,
     };
 
     virtual void addBatteryData(uint32_t params) = 0;
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/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/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/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/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index ec6188f..a42cca5 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -214,6 +214,15 @@
 {
     LOGV("MediaPlayerService created");
     mNextConnId = 1;
+
+    mBatteryAudio.refCount = 0;
+    for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+        mBatteryAudio.deviceOn[i] = 0;
+        mBatteryAudio.lastTime[i] = 0;
+        mBatteryAudio.totalTime[i] = 0;
+    }
+    // speaker is on by default
+    mBatteryAudio.deviceOn[SPEAKER] = 1;
 }
 
 MediaPlayerService::~MediaPlayerService()
@@ -1777,12 +1786,88 @@
 void MediaPlayerService::addBatteryData(uint32_t params)
 {
     Mutex::Autolock lock(mLock);
+
+    int32_t time = systemTime() / 1000000L;
+
+    // change audio output devices. This notification comes from AudioFlinger
+    if ((params & kBatteryDataSpeakerOn)
+            || (params & kBatteryDataOtherAudioDeviceOn)) {
+
+        int deviceOn[NUM_AUDIO_DEVICES];
+        for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+            deviceOn[i] = 0;
+        }
+
+        if ((params & kBatteryDataSpeakerOn)
+                && (params & kBatteryDataOtherAudioDeviceOn)) {
+            deviceOn[SPEAKER_AND_OTHER] = 1;
+        } else if (params & kBatteryDataSpeakerOn) {
+            deviceOn[SPEAKER] = 1;
+        } else {
+            deviceOn[OTHER_AUDIO_DEVICE] = 1;
+        }
+
+        for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+            if (mBatteryAudio.deviceOn[i] != deviceOn[i]){
+
+                if (mBatteryAudio.refCount > 0) { // if playing audio
+                    if (!deviceOn[i]) {
+                        mBatteryAudio.lastTime[i] += time;
+                        mBatteryAudio.totalTime[i] += mBatteryAudio.lastTime[i];
+                        mBatteryAudio.lastTime[i] = 0;
+                    } else {
+                        mBatteryAudio.lastTime[i] = 0 - time;
+                    }
+                }
+
+                mBatteryAudio.deviceOn[i] = deviceOn[i];
+            }
+        }
+        return;
+    }
+
+    // an sudio stream is started
+    if (params & kBatteryDataAudioFlingerStart) {
+        // record the start time only if currently no other audio
+        // is being played
+        if (mBatteryAudio.refCount == 0) {
+            for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+                if (mBatteryAudio.deviceOn[i]) {
+                    mBatteryAudio.lastTime[i] -= time;
+                }
+            }
+        }
+
+        mBatteryAudio.refCount ++;
+        return;
+
+    } else if (params & kBatteryDataAudioFlingerStop) {
+        if (mBatteryAudio.refCount <= 0) {
+            LOGW("Battery track warning: refCount is <= 0");
+            return;
+        }
+
+        // record the stop time only if currently this is the only
+        // audio being played
+        if (mBatteryAudio.refCount == 1) {
+            for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+                if (mBatteryAudio.deviceOn[i]) {
+                    mBatteryAudio.lastTime[i] += time;
+                    mBatteryAudio.totalTime[i] += mBatteryAudio.lastTime[i];
+                    mBatteryAudio.lastTime[i] = 0;
+                }
+            }
+        }
+
+        mBatteryAudio.refCount --;
+        return;
+    }
+
     int uid = IPCThreadState::self()->getCallingUid();
     if (uid == AID_MEDIA) {
         return;
     }
     int index = mBatteryData.indexOfKey(uid);
-    int32_t time = systemTime() / 1000000L;
 
     if (index < 0) { // create a new entry for this UID
         BatteryUsageInfo info;
@@ -1792,7 +1877,10 @@
         info.videoLastTime = 0;
         info.refCount = 0;
 
-        mBatteryData.add(uid, info);
+        if (mBatteryData.add(uid, info) == NO_MEMORY) {
+            LOGE("Battery track error: no memory for new app");
+            return;
+        }
     }
 
     BatteryUsageInfo &info = mBatteryData.editValueFor(uid);
@@ -1837,6 +1925,26 @@
 
 status_t MediaPlayerService::pullBatteryData(Parcel* reply) {
     Mutex::Autolock lock(mLock);
+
+    // audio output devices usage
+    int32_t time = systemTime() / 1000000L; //in ms
+    int32_t totalTime;
+
+    for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+        totalTime = mBatteryAudio.totalTime[i];
+
+        if (mBatteryAudio.deviceOn[i]
+            && (mBatteryAudio.lastTime[i] != 0)) {
+                int32_t tmpTime = mBatteryAudio.lastTime[i] + time;
+                totalTime += tmpTime;
+        }
+
+        reply->writeInt32(totalTime);
+        // reset the total time
+        mBatteryAudio.totalTime[i] = 0;
+   }
+
+    // codec usage
     BatteryUsageInfo info;
     int size = mBatteryData.size();
 
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 1175ed0..ff6ccf54 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -225,6 +225,25 @@
     };
     KeyedVector<int, BatteryUsageInfo>    mBatteryData;
 
+    enum {
+        SPEAKER,
+        OTHER_AUDIO_DEVICE,
+        SPEAKER_AND_OTHER,
+        NUM_AUDIO_DEVICES
+    };
+
+    struct BatteryAudioFlingerUsageInfo {
+        int refCount; // how many audio streams are being played
+        int deviceOn[NUM_AUDIO_DEVICES]; // whether the device is currently used
+        int32_t lastTime[NUM_AUDIO_DEVICES]; // in ms
+        // totalTime[]: total time of audio output devices usage
+        int32_t totalTime[NUM_AUDIO_DEVICES]; // in ms
+    };
+
+    // This varialble is used to record the usage of audio output device
+    // for battery app
+    BatteryAudioFlingerUsageInfo mBatteryAudio;
+
     // Collect info of the codec usage from media player and media recorder
     virtual void                addBatteryData(uint32_t params);
     // API for the Battery app to pull the data of codecs usage
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/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/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 2df6d68..bf06f947 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -121,4 +121,8 @@
     <integer name="def_download_manager_max_bytes_over_mobile">-1</integer>
     <!-- Default for Settings.Secure.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE. <=0 if no limit -->
     <integer name="def_download_manager_recommended_max_bytes_over_mobile">-1</integer>
+
+    <!-- Default for Settings.Secure.LONG_PRESS_TIMEOUT_MILLIS -->
+    <integer name="def_long_press_timeout_millis">500</integer>
+
 </resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index f336f06..d901c2c 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -819,6 +819,24 @@
              upgradeVersion = 64;
          }
 
+        if (upgradeVersion == 64) {
+            // New setting to configure the long press timeout.
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadIntegerSetting(stmt, Settings.Secure.LONG_PRESS_TIMEOUT,
+                        R.integer.def_long_press_timeout_millis);
+                stmt.close();
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 65;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -1332,6 +1350,9 @@
                 loadSetting(stmt, Settings.Secure.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
                         Integer.toString(recommendedMaxBytes));
             }
+
+            loadIntegerSetting(stmt, Settings.Secure.LONG_PRESS_TIMEOUT,
+                    R.integer.def_long_press_timeout_millis);
         } finally {
             if (stmt != null) stmt.close();
         }
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index a07ebfc..2b08ab5 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -24,6 +24,7 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <utils/Log.h>
 #include <binder/Parcel.h>
@@ -35,6 +36,7 @@
 
 #include <media/AudioTrack.h>
 #include <media/AudioRecord.h>
+#include <media/IMediaPlayerService.h>
 
 #include <private/media/AudioTrackShared.h>
 #include <private/media/AudioEffectShared.h>
@@ -121,6 +123,19 @@
 #endif
 }
 
+// To collect the amplifier usage
+static void addBatteryData(uint32_t params) {
+    sp<IBinder> binder =
+        defaultServiceManager()->getService(String16("media.player"));
+    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+    if (service.get() == NULL) {
+        LOGW("Cannot connect to the MediaPlayerService for battery tracking");
+        return;
+    }
+
+    service->addBatteryData(params);
+}
+
 // ----------------------------------------------------------------------------
 
 AudioFlinger::AudioFlinger()
@@ -1833,6 +1848,27 @@
             }
         }
         if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) {
+            // when changing the audio output device, call addBatteryData to notify
+            // the change
+            if (mDevice != value) {
+                uint32_t params = 0;
+                // check whether speaker is on
+                if (value & AudioSystem::DEVICE_OUT_SPEAKER) {
+                    params |= IMediaPlayerService::kBatteryDataSpeakerOn;
+                }
+
+                int deviceWithoutSpeaker
+                    = AudioSystem::DEVICE_OUT_ALL & ~AudioSystem::DEVICE_OUT_SPEAKER;
+                // check if any other device (except speaker) is on
+                if (value & deviceWithoutSpeaker ) {
+                    params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn;
+                }
+
+                if (params != 0) {
+                    addBatteryData(params);
+                }
+            }
+
             // forward device change to effects that have requested to be
             // aware of attached audio device.
             mDevice = (uint32_t)value;
@@ -2831,6 +2867,9 @@
                     AudioSystem::stopOutput(thread->id(),
                                             (AudioSystem::stream_type)mStreamType,
                                             mSessionId);
+
+                    // to track the speaker usage
+                    addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
                 }
                 AudioSystem::releaseOutput(thread->id());
             }
@@ -2941,6 +2980,11 @@
                                               (AudioSystem::stream_type)mStreamType,
                                               mSessionId);
             thread->mLock.lock();
+
+            // to track the speaker usage
+            if (status == NO_ERROR) {
+                addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart);
+            }
         }
         if (status == NO_ERROR) {
             PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
@@ -2976,6 +3020,9 @@
                                     (AudioSystem::stream_type)mStreamType,
                                     mSessionId);
             thread->mLock.lock();
+
+            // to track the speaker usage
+            addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
         }
     }
 }
@@ -2995,6 +3042,9 @@
                                         (AudioSystem::stream_type)mStreamType,
                                         mSessionId);
                 thread->mLock.lock();
+
+                // to track the speaker usage
+                addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStop);
             }
         }
     }
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/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e6dfb7f..40417b1 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -717,6 +717,8 @@
     final private SparseArray<HashMap<Uri, UriPermission>> mGrantedUriPermissions
             = new SparseArray<HashMap<Uri, UriPermission>>();
 
+    CoreSettingsObserver mCoreSettingsObserver;
+
     /**
      * Thread-local storage used to carry caller permissions over through
      * indirect content-provider access.
@@ -3589,7 +3591,8 @@
                     app.instrumentationClass, app.instrumentationProfileFile,
                     app.instrumentationArguments, app.instrumentationWatcher, testMode, 
                     isRestrictedBackupMode || !normalMode,
-                    mConfiguration, getCommonServicesLocked());
+                    mConfiguration, getCommonServicesLocked(),
+                    mCoreSettingsObserver.getCoreSettingsLocked());
             updateLruProcessLocked(app, false, true);
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
         } catch (Exception e) {
@@ -5772,6 +5775,8 @@
         if (providers != null) {
             mSystemThread.installSystemProviders(providers);
         }
+
+        mSelf.mCoreSettingsObserver = new CoreSettingsObserver(mSelf);
     }
 
     /**
@@ -13030,4 +13035,17 @@
     public void monitor() {
         synchronized (this) { }
     }
+
+    public void onCoreSettingsChange(Bundle settings) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord processRecord = mLruProcesses.get(i);
+            try {
+                if (processRecord.thread != null) {
+                    processRecord.thread.setCoreSettings(settings);
+                }
+            } catch (RemoteException re) {
+                /* ignore */
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/am/CoreSettingsObserver.java b/services/java/com/android/server/am/CoreSettingsObserver.java
new file mode 100644
index 0000000..25db84a
--- /dev/null
+++ b/services/java/com/android/server/am/CoreSettingsObserver.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2006-2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class for watching a set of core settings which the framework
+ * propagates to application processes to avoid multiple lookups and potentially
+ * disk I/O operations. Note: This class assumes that all core settings reside
+ * in {@link Settings.Secure}.
+ */
+class CoreSettingsObserver extends ContentObserver {
+    private static final String LOG_TAG = CoreSettingsObserver.class.getSimpleName();
+
+    // mapping form property name to its type
+    private static final Map<String, Class<?>> sCoreSettingToTypeMap = new HashMap<
+            String, Class<?>>();
+    static {
+        sCoreSettingToTypeMap.put(Settings.Secure.LONG_PRESS_TIMEOUT, int.class);
+        // add other core settings here...
+    }
+
+    private final Bundle mCoreSettings = new Bundle();
+
+    private final ActivityManagerService mActivityManagerService;
+
+    public CoreSettingsObserver(ActivityManagerService activityManagerService) {
+        super(activityManagerService.mHandler);
+        mActivityManagerService = activityManagerService;
+        beginObserveCoreSettings();
+        populateCoreSettings(mCoreSettings);
+    }
+
+    public Bundle getCoreSettingsLocked() {
+        return (Bundle) mCoreSettings.clone();
+    }
+
+    @Override
+    public void onChange(boolean selfChange) {
+        synchronized (mActivityManagerService) {
+            populateCoreSettings(mCoreSettings);
+            mActivityManagerService.onCoreSettingsChange(mCoreSettings);
+        }
+    }
+
+    private void beginObserveCoreSettings() {
+        for (String setting : sCoreSettingToTypeMap.keySet()) {
+            Uri uri = Settings.Secure.getUriFor(setting);
+            mActivityManagerService.mContext.getContentResolver().registerContentObserver(
+                    uri, false, this);
+        }
+    }
+
+    private void populateCoreSettings(Bundle snapshot) {
+        Context context = mActivityManagerService.mContext;
+        for (Map.Entry<String, Class<?>> entry : sCoreSettingToTypeMap.entrySet()) {
+            String setting = entry.getKey();
+            Class<?> type = entry.getValue();
+            try {
+                if (type == String.class) {
+                    String value = Settings.Secure.getString(context.getContentResolver(),
+                            setting);
+                    snapshot.putString(setting, value);
+                } else if (type == int.class) {
+                    int value = Settings.Secure.getInt(context.getContentResolver(),
+                            setting);
+                    snapshot.putInt(setting, value);
+                } else if (type == float.class) {
+                    float value = Settings.Secure.getFloat(context.getContentResolver(),
+                            setting);
+                    snapshot.putFloat(setting, value);
+                } else if (type == long.class) {
+                    long value = Settings.Secure.getLong(context.getContentResolver(),
+                            setting);
+                    snapshot.putLong(setting, value);
+                }
+            } catch (SettingNotFoundException snfe) {
+                Log.w(LOG_TAG, "Cannot find setting \"" + setting + "\"", snfe);
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/usb/UsbService.java b/services/java/com/android/server/usb/UsbService.java
index 94c25e9..8170c61 100644
--- a/services/java/com/android/server/usb/UsbService.java
+++ b/services/java/com/android/server/usb/UsbService.java
@@ -102,8 +102,8 @@
     private boolean mSystemReady;
 
     private UsbAccessory mCurrentAccessory;
-    // functions to restore after exiting accessory mode
-    private final ArrayList<String> mAccessoryRestoreFunctions = new ArrayList<String>();
+    // USB functions that are enabled by default, to restore after exiting accessory mode
+    private final ArrayList<String> mDefaultFunctions = new ArrayList<String>();
 
     private final Context mContext;
     private final Object mLock = new Object();
@@ -118,20 +118,6 @@
         boolean enteringAccessoryMode =
             (mHasUsbAccessory && enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function));
 
-        if (enteringAccessoryMode) {
-            // keep a list of functions to reenable after exiting accessory mode
-            mAccessoryRestoreFunctions.clear();
-            int count = mEnabledFunctions.size();
-            for (int i = 0; i < count; i++) {
-                String f = mEnabledFunctions.get(i);
-                // RNDIS should not be restored and adb is handled automatically
-                if (!UsbManager.USB_FUNCTION_RNDIS.equals(f) &&
-                    !UsbManager.USB_FUNCTION_ADB.equals(f) &&
-                    !UsbManager.USB_FUNCTION_ACCESSORY.equals(f)) {
-                    mAccessoryRestoreFunctions.add(f);
-                }
-            }
-        }
         if (enabled) {
             if (!mEnabledFunctions.contains(function)) {
                 mEnabledFunctions.add(function);
@@ -261,6 +247,11 @@
                 String functionName = files[i].getName();
                 if (value == 1) {
                     mEnabledFunctions.add(functionName);
+                    // adb is enabled/disabled automatically by the adbd daemon,
+                    // so don't treat it as a default function
+                    if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) {
+                        mDefaultFunctions.add(functionName);
+                    }
                 } else {
                     mDisabledFunctions.add(functionName);
                 }
@@ -503,29 +494,24 @@
                 switch (msg.what) {
                     case MSG_UPDATE_STATE:
                         if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
-                            if (mConnected == 0 && mCurrentAccessory != null) {
-                                // turn off accessory mode when we are disconnected
+                            if (mConnected == 0) {
+                                // make sure accessory mode is off, and restore default functions
                                 if (UsbManager.setFunctionEnabled(
                                         UsbManager.USB_FUNCTION_ACCESSORY, false)) {
                                     Log.d(TAG, "exited USB accessory mode");
 
-                                    // restore previously enabled functions
-                                    for (String function : mAccessoryRestoreFunctions) {
+                                    int count = mDefaultFunctions.size();
+                                    for (int i = 0; i < count; i++) {
+                                        String function = mDefaultFunctions.get(i);
                                         if (UsbManager.setFunctionEnabled(function, true)) {
                                             Log.e(TAG, "could not reenable function " + function);
                                         }
                                     }
-                                    mAccessoryRestoreFunctions.clear();
+                                }
 
+                                if (mCurrentAccessory != null) {
                                     mDeviceManager.accessoryDetached(mCurrentAccessory);
                                     mCurrentAccessory = null;
-
-                                    // this will cause an immediate reset of the USB bus,
-                                    // so there is no point in sending the
-                                    // function disabled broadcast.
-                                    return;
-                                } else {
-                                    Log.e(TAG, "could not disable USB_FUNCTION_ACCESSORY");
                                 }
                             }
 
@@ -581,14 +567,18 @@
                 pw.print(mDisabledFunctions.get(i) + " ");
             }
             pw.println("");
+            pw.print("    Default Functions: ");
+            for (int i = 0; i < mDefaultFunctions.size(); i++) {
+                pw.print(mDefaultFunctions.get(i) + " ");
+            }
+            pw.println("");
             pw.println("    mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
+            pw.println("    mCurrentAccessory: " + mCurrentAccessory);
 
             pw.println("  USB Host State:");
             for (String name : mDevices.keySet()) {
                 pw.println("    " + name + ": " + mDevices.get(name));
             }
-            pw.println("  mCurrentAccessory: " + mCurrentAccessory);
-
             mDeviceManager.dump(fd, pw);
         }
     }
diff --git a/services/java/com/android/server/wm/DimAnimator.java b/services/java/com/android/server/wm/DimAnimator.java
index 1fcb869..a266d70 100644
--- a/services/java/com/android/server/wm/DimAnimator.java
+++ b/services/java/com/android/server/wm/DimAnimator.java
@@ -45,7 +45,7 @@
                     + mDimSurface + ": CREATE");
             try {
                 mDimSurface = new Surface(session, 0,
-                        "DimSurface",
+                        "DimAnimator",
                         -1, 16, 16, PixelFormat.OPAQUE,
                         Surface.FX_SURFACE_DIM);
                 mDimSurface.setAlpha(0.0f);
@@ -84,7 +84,7 @@
      * {@link updateSurface} after all windows are examined.
      */
     void updateParameters(Resources res, WindowState w, long currentTime) {
-        mDimSurface.setLayer(w.mAnimLayer-1);
+        mDimSurface.setLayer(w.mAnimLayer - WindowManagerService.LAYER_OFFSET_DIM);
 
         final float target = w.mExiting ? 0 : w.mAttrs.dimAmount;
         if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DIM " + mDimSurface
@@ -177,8 +177,11 @@
         return animating;
     }
 
-    public void printTo(PrintWriter pw) {
-        pw.print("  mDimShown="); pw.print(mDimShown);
+    public void printTo(String prefix, PrintWriter pw) {
+        pw.print(prefix);
+        pw.print("mDimSurface="); pw.println(mDimSurface);
+        pw.print(prefix);
+        pw.print("mDimShown="); pw.print(mDimShown);
         pw.print(" current="); pw.print(mDimCurrentAlpha);
         pw.print(" target="); pw.print(mDimTargetAlpha);
         pw.print(" delta="); pw.print(mDimDeltaPerMs);
diff --git a/services/java/com/android/server/wm/DimSurface.java b/services/java/com/android/server/wm/DimSurface.java
new file mode 100644
index 0000000..084ac6f
--- /dev/null
+++ b/services/java/com/android/server/wm/DimSurface.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.graphics.PixelFormat;
+import android.util.Slog;
+import android.view.Surface;
+import android.view.SurfaceSession;
+
+import java.io.PrintWriter;
+
+class DimSurface {
+    Surface mDimSurface;
+    boolean mDimShown = false;
+    int mDimColor = 0;
+    int mLayer = -1;
+    int mLastDimWidth, mLastDimHeight;
+
+    DimSurface(SurfaceSession session) {
+        if (mDimSurface == null) {
+            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DIM "
+                    + mDimSurface + ": CREATE");
+            try {
+                mDimSurface = new Surface(session, 0,
+                        "DimSurface",
+                        -1, 16, 16, PixelFormat.OPAQUE,
+                        Surface.FX_SURFACE_DIM);
+                mDimSurface.setAlpha(0.0f);
+            } catch (Exception e) {
+                Slog.e(WindowManagerService.TAG, "Exception creating Dim surface", e);
+            }
+        }
+    }
+
+    /**
+     * Show the dim surface.
+     */
+    void show(int dw, int dh, int layer, int color) {
+        if (!mDimShown) {
+            if (WindowManagerService.SHOW_TRANSACTIONS) Slog.i(WindowManagerService.TAG, "  DIM " + mDimSurface + ": SHOW pos=(0,0) (" +
+                    dw + "x" + dh + ")");
+            mDimShown = true;
+            try {
+                mLastDimWidth = dw;
+                mLastDimHeight = dh;
+                mDimSurface.setPosition(0, 0);
+                mDimSurface.setSize(dw, dh);
+                mDimSurface.show();
+            } catch (RuntimeException e) {
+                Slog.w(WindowManagerService.TAG, "Failure showing dim surface", e);
+            }
+        } else if (mLastDimWidth != dw || mLastDimHeight != dh || mDimColor != color
+                || mLayer != layer) {
+            mLastDimWidth = dw;
+            mLastDimHeight = dh;
+            mLayer = layer;
+            mDimColor = color;
+            mDimSurface.setSize(dw, dh);
+            mDimSurface.setLayer(layer);
+            mDimSurface.setAlpha(((color>>24)&0xff)/255.0f);
+        }
+    }
+
+    void hide() {
+        if (mDimShown) {
+            mDimShown = false;
+            try {
+                mDimSurface.hide();
+            } catch (RuntimeException e) {
+                Slog.w(WindowManagerService.TAG, "Illegal argument exception hiding dim surface");
+            }
+        }
+    }
+
+    public void printTo(String prefix, PrintWriter pw) {
+        pw.print(prefix); pw.print("mDimSurface="); pw.println(mDimSurface);
+        pw.print(prefix); pw.print("mDimShown="); pw.print(mDimShown);
+                pw.print(" mLayer="); pw.println(mLayer);
+                pw.print(" mDimColor=0x"); pw.println(Integer.toHexString(mDimColor));
+        pw.print(prefix); pw.print("mLastDimWidth="); pw.print(mLastDimWidth);
+                pw.print(" mLastDimWidth="); pw.println(mLastDimWidth);
+    }
+}
diff --git a/services/java/com/android/server/wm/ScreenRotationAnimation.java b/services/java/com/android/server/wm/ScreenRotationAnimation.java
index 4356ce5..fbf1ec3 100644
--- a/services/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -110,18 +110,16 @@
 
         Bitmap screenshot = Surface.screenshot(0, 0);
 
-        if (screenshot != null) {
-            // Screenshot does NOT include rotation!
-            mSnapshotRotation = 0;
-            mWidth = screenshot.getWidth();
-            mHeight = screenshot.getHeight();
-        } else {
-            // Just in case.
-            mSnapshotRotation = display.getRotation();
-            mWidth = mDisplayMetrics.widthPixels;
-            mHeight = mDisplayMetrics.heightPixels;
+        if (screenshot == null) {
+            // Device is not capable of screenshots...  we can't do an animation.
+            return;
         }
 
+        // Screenshot does NOT include rotation!
+        mSnapshotRotation = 0;
+        mWidth = screenshot.getWidth();
+        mHeight = screenshot.getHeight();
+
         mOriginalRotation = display.getRotation();
         mOriginalWidth = mDisplayMetrics.widthPixels;
         mOriginalHeight = mDisplayMetrics.heightPixels;
@@ -150,23 +148,19 @@
                     c = mSurface.lockCanvas(dirty);
                 } catch (IllegalArgumentException e) {
                     Slog.w(TAG, "Unable to lock surface", e);
-                    return;
                 } catch (Surface.OutOfResourcesException e) {
                     Slog.w(TAG, "Unable to lock surface", e);
-                    return;
                 }
                 if (c == null) {
-                    Slog.w(TAG, "Null surface");
+                    Slog.w(TAG, "Null surface canvas");
+                    mSurface.destroy();
+                    mSurface = null;
                     return;
                 }
         
-                if (screenshot != null) {
-                    Paint paint = new Paint(0);
-                    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
-                    c.drawBitmap(screenshot, 0, 0, paint);
-                } else {
-                    c.drawColor(Color.BLACK, PorterDuff.Mode.SRC);
-                }
+                Paint paint = new Paint(0);
+                paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+                c.drawBitmap(screenshot, 0, 0, paint);
 
                 mSurface.unlockCanvasAndPost(c);
             }
@@ -177,12 +171,14 @@
                         "<<< CLOSE TRANSACTION ScreenRotationAnimation");
             }
     
-            if (screenshot != null) {
-                screenshot.recycle();
-            }
+            screenshot.recycle();
         }
     }
 
+    boolean hasScreenshot() {
+        return mSurface != null;
+    }
+
     static int deltaRotation(int oldRotation, int newRotation) {
         int delta = newRotation - oldRotation;
         if (delta < 0) delta += 4;
@@ -250,17 +246,14 @@
      */
     public boolean dismiss(SurfaceSession session, long maxAnimationDuration,
             float animationScale) {
-        // Figure out how the screen has moved from the original rotation.
-        int delta = deltaRotation(mCurRotation, mOriginalRotation);
-        if (false && delta == 0) {
-            // Nothing changed, just remove the snapshot.
-            if (mSurface != null) {
-                mSurface.destroy();
-                mSurface = null;
-            }
+        if (mSurface == null) {
+            // Can't do animation.
             return false;
         }
 
+        // Figure out how the screen has moved from the original rotation.
+        int delta = deltaRotation(mCurRotation, mOriginalRotation);
+
         switch (delta) {
             case Surface.ROTATION_0:
                 mExitAnimation = AnimationUtils.loadAnimation(mContext,
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 15269dc..b5d84e8 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -180,6 +180,16 @@
      */
     static final int WINDOW_LAYER_MULTIPLIER = 5;
 
+    /**
+     * Dim surface layer is immediately below target window.
+     */
+    static final int LAYER_OFFSET_DIM = 1;
+
+    /**
+     * Blur surface layer is immediately below dim layer.
+     */
+    static final int LAYER_OFFSET_BLUR = 2;
+
     /** The maximum length we will accept for a loaded animation duration:
      * this is 10 seconds.
      */
@@ -276,8 +286,6 @@
 
     final IBatteryStats mBatteryStats;
 
-    private static final boolean mInEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
-
     /**
      * All currently active sessions with clients.
      */
@@ -468,6 +476,7 @@
     // visible behind it in case it animates in a way that would allow it to be
     // seen.
     WindowState mWindowDetachedWallpaper = null;
+    DimSurface mWindowAnimationBackgroundSurface = null;
     int mWallpaperAnimLayerAdjustment;
     float mLastWallpaperX = -1;
     float mLastWallpaperY = -1;
@@ -4940,7 +4949,8 @@
             if (mDisplayEnabled) {
                 // NOTE: We disable the rotation in the emulator because
                 //       it doesn't support hardware OpenGL emulation yet.
-                if (CUSTOM_SCREEN_ROTATION && !mInEmulator) {
+                if (CUSTOM_SCREEN_ROTATION && mScreenRotationAnimation != null
+                        && mScreenRotationAnimation.hasScreenshot()) {
                     Surface.freezeDisplay(0);
                     if (!inTransaction) {
                         if (SHOW_TRANSACTIONS) Slog.i(TAG,
@@ -6802,6 +6812,8 @@
                 boolean wallpaperMayChange = false;
                 boolean forceHiding = false;
                 WindowState windowDetachedWallpaper = null;
+                WindowState windowAnimationBackground = null;
+                int windowAnimationBackgroundColor = 0;
 
                 mPolicy.beginAnimationLw(dw, dh);
 
@@ -6851,8 +6863,15 @@
                         // an animating window and take care of a request to run
                         // a detached wallpaper animation.
                         if (nowAnimating) {
-                            if (w.mAnimation != null && w.mAnimation.getDetachWallpaper()) {
-                                windowDetachedWallpaper = w;
+                            if (w.mAnimation != null) {
+                                if (w.mAnimation.getDetachWallpaper()) {
+                                    windowDetachedWallpaper = w;
+                                }
+                                if (w.mAnimation.getBackgroundColor() != 0) {
+                                    windowAnimationBackground = w;
+                                    windowAnimationBackgroundColor =
+                                            w.mAnimation.getBackgroundColor();
+                                }
                             }
                             animating = true;
                         }
@@ -6860,9 +6879,15 @@
                         // If this window's app token is running a detached wallpaper
                         // animation, make a note so we can ensure the wallpaper is
                         // displayed behind it.
-                        if (w.mAppToken != null && w.mAppToken.animation != null
-                                && w.mAppToken.animation.getDetachWallpaper()) {
-                            windowDetachedWallpaper = w;
+                        if (w.mAppToken != null && w.mAppToken.animation != null) {
+                            if (w.mAppToken.animation.getDetachWallpaper()) {
+                                windowDetachedWallpaper = w;
+                            }
+                            if (w.mAppToken.animation.getBackgroundColor() != 0) {
+                                windowAnimationBackground = w;
+                                windowAnimationBackgroundColor =
+                                        w.mAppToken.animation.getBackgroundColor();
+                            }
                         }
 
                         if (wasAnimating && !w.mAnimating && mWallpaperTarget == w) {
@@ -7301,6 +7326,17 @@
                     wallpaperMayChange = true;
                 }
 
+                if (windowAnimationBackgroundColor != 0) {
+                    if (mWindowAnimationBackgroundSurface == null) {
+                        mWindowAnimationBackgroundSurface = new DimSurface(mFxSession);
+                    }
+                    mWindowAnimationBackgroundSurface.show(dw, dh,
+                            windowAnimationBackground.mAnimLayer - LAYER_OFFSET_DIM,
+                            windowAnimationBackgroundColor);
+                } else if (mWindowAnimationBackgroundSurface != null) {
+                    mWindowAnimationBackgroundSurface.hide();
+                }
+
                 if (wallpaperMayChange) {
                     if (DEBUG_WALLPAPER) Slog.v(TAG,
                             "Wallpaper may change!  Adjusting");
@@ -7730,7 +7766,7 @@
                                             dw + "x" + dh + "), layer=" + (w.mAnimLayer-1));
                                     mBlurSurface.setPosition(0, 0);
                                     mBlurSurface.setSize(dw, dh);
-                                    mBlurSurface.setLayer(w.mAnimLayer-2);
+                                    mBlurSurface.setLayer(w.mAnimLayer-LAYER_OFFSET_BLUR);
                                     if (!mBlurShown) {
                                         try {
                                             if (SHOW_TRANSACTIONS) Slog.i(TAG, "  BLUR "
@@ -7779,7 +7815,8 @@
                     // Using the same layer as Dim because they will never be shown at the
                     // same time.  NOTE: we do NOT use mAnimLayer, because we don't
                     // want this surface dragged up in front of stuff that is animating.
-                    mBackgroundFillerSurface.setLayer(mBackgroundFillerTarget.mLayer - 1);
+                    mBackgroundFillerSurface.setLayer(mBackgroundFillerTarget.mLayer
+                            - LAYER_OFFSET_DIM);
                     mBackgroundFillerSurface.show();
                 } catch (RuntimeException e) {
                     Slog.e(TAG, "Exception showing filler surface");
@@ -8294,6 +8331,9 @@
                 mScreenRotationAnimation = new ScreenRotationAnimation(mContext,
                         mDisplay, mFxSession, inTransaction);
             }
+            if (!mScreenRotationAnimation.hasScreenshot()) {
+                Surface.freezeDisplay(0);
+            }
         } else {
             Surface.freezeDisplay(0);
         }
@@ -8316,17 +8356,21 @@
 
         boolean updateRotation = false;
         
-        if (CUSTOM_SCREEN_ROTATION) {
-            if (mScreenRotationAnimation != null) {
-                if (mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
-                        mTransitionAnimationScale)) {
-                    requestAnimationLocked(0);
-                } else {
-                    mScreenRotationAnimation = null;
-                    updateRotation = true;
-                }
+        if (CUSTOM_SCREEN_ROTATION && mScreenRotationAnimation != null
+                && mScreenRotationAnimation.hasScreenshot()) {
+            if (mScreenRotationAnimation.dismiss(mFxSession, MAX_ANIMATION_DURATION,
+                    mTransitionAnimationScale)) {
+                requestAnimationLocked(0);
+            } else {
+                mScreenRotationAnimation = null;
+                updateRotation = true;
             }
         } else {
+            if (mScreenRotationAnimation != null) {
+                mScreenRotationAnimation.kill();
+                mScreenRotationAnimation = null;
+            }
+            updateRotation = true;
             Surface.unfreezeDisplay(0);
         }
 
@@ -8589,6 +8633,10 @@
             if (mWindowDetachedWallpaper != null) {
                 pw.print("  mWindowDetachedWallpaper="); pw.println(mWindowDetachedWallpaper);
             }
+            if (mWindowAnimationBackgroundSurface != null) {
+                pw.println("  mWindowAnimationBackgroundSurface:");
+                mWindowAnimationBackgroundSurface.printTo("    ", pw);
+            }
             pw.print("  mCurConfiguration="); pw.println(this.mCurConfiguration);
             pw.print("  mInTouchMode="); pw.print(mInTouchMode);
                     pw.print(" mLayoutSeq="); pw.println(mLayoutSeq);
@@ -8597,7 +8645,8 @@
             pw.print("  mLayoutNeeded="); pw.print(mLayoutNeeded);
                     pw.print(" mBlurShown="); pw.println(mBlurShown);
             if (mDimAnimator != null) {
-                mDimAnimator.printTo(pw);
+                pw.println("  mDimAnimator:");
+                mDimAnimator.printTo("    ", pw);
             } else {
                 pw.println( "  no DimAnimator ");
             }
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 40882d8..554fa43 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2150,8 +2150,8 @@
     sh = (!sh) ? hw_h : sh;
     const size_t size = sw * sh * 4;
 
-    LOGD("screenshot: sw=%d, sh=%d, minZ=%d, maxZ=%d",
-            sw, sh, minLayerZ, maxLayerZ);
+    //LOGD("screenshot: sw=%d, sh=%d, minZ=%d, maxZ=%d",
+    //        sw, sh, minLayerZ, maxLayerZ);
 
     // make sure to clear all GL error flags
     while ( glGetError() != GL_NO_ERROR ) ;
@@ -2236,7 +2236,7 @@
 
     hw.compositionComplete();
 
-    LOGD("screenshot: result = %s", result<0 ? strerror(result) : "OK");
+    // LOGD("screenshot: result = %s", result<0 ? strerror(result) : "OK");
 
     return result;
 }
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/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index e89858c9..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 */
@@ -1239,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.
              */
@@ -1264,7 +1267,7 @@
             }
             mLastSignalLevel = newSignalLevel;
         } else {
-            mWifiInfo.setRssi(-200);
+            mWifiInfo.setRssi(MIN_RSSI);
         }
         int newLinkSpeed = WifiNative.getLinkSpeedCommand();
         if (newLinkSpeed != -1) {
@@ -1386,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();
@@ -2363,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);
@@ -2741,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 {