Merge "Make sure to go to the right Settings panel when tapping a notification"
diff --git a/api/current.txt b/api/current.txt
index 033cccb..9758433 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11296,9 +11296,33 @@
   public class EffectFactory {
     method public android.media.effect.Effect createEffect(java.lang.String);
     method public static boolean isEffectSupported(java.lang.String);
-    field public static final java.lang.String EFFECT_BRIGHTNESS = "BrightnessEffect";
-    field public static final java.lang.String EFFECT_CONTRAST = "ContrastEffect";
-    field public static final java.lang.String EFFECT_FISHEYE = "FisheyeEffect";
+    field public static final java.lang.String EFFECT_AUTOFIX = "android.media.effect.effects.AutoFixEffect";
+    field public static final java.lang.String EFFECT_BACKDROPPER = "android.media.effect.effects.BackDropperEffect";
+    field public static final java.lang.String EFFECT_BLACKWHITE = "android.media.effect.effects.BlackWhiteEffect";
+    field public static final java.lang.String EFFECT_BRIGHTNESS = "android.media.effect.effects.BrightnessEffect";
+    field public static final java.lang.String EFFECT_CONTRAST = "android.media.effect.effects.ContrastEffect";
+    field public static final java.lang.String EFFECT_CROP = "android.media.effect.effects.CropEffect";
+    field public static final java.lang.String EFFECT_CROSSPROCESS = "android.media.effect.effects.CrossProcessEffect";
+    field public static final java.lang.String EFFECT_DOCUMENTARY = "android.media.effect.effects.DocumentaryEffect";
+    field public static final java.lang.String EFFECT_DOODLE = "android.media.effect.effects.DoodleEffect";
+    field public static final java.lang.String EFFECT_DUOTONE = "android.media.effect.effects.DuotoneEffect";
+    field public static final java.lang.String EFFECT_FILLLIGHT = "android.media.effect.effects.FillLightEffect";
+    field public static final java.lang.String EFFECT_FISHEYE = "android.media.effect.effects.FisheyeEffect";
+    field public static final java.lang.String EFFECT_FLIP = "android.media.effect.effects.FlipEffect";
+    field public static final java.lang.String EFFECT_GRAIN = "android.media.effect.effects.GrainEffect";
+    field public static final java.lang.String EFFECT_GRAYSCALE = "android.media.effect.effects.GrayscaleEffect";
+    field public static final java.lang.String EFFECT_LOMOISH = "android.media.effect.effects.LomoishEffect";
+    field public static final java.lang.String EFFECT_NEGATIVE = "android.media.effect.effects.NegativeEffect";
+    field public static final java.lang.String EFFECT_POSTERIZE = "android.media.effect.effects.PosterizeEffect";
+    field public static final java.lang.String EFFECT_REDEYE = "android.media.effect.effects.RedEyeEffect";
+    field public static final java.lang.String EFFECT_ROTATE = "android.media.effect.effects.RotateEffect";
+    field public static final java.lang.String EFFECT_SATURATE = "android.media.effect.effects.SaturateEffect";
+    field public static final java.lang.String EFFECT_SEPIA = "android.media.effect.effects.SepiaEffect";
+    field public static final java.lang.String EFFECT_SHARPEN = "android.media.effect.effects.SharpenEffect";
+    field public static final java.lang.String EFFECT_STRAIGHTEN = "android.media.effect.effects.StraightenEffect";
+    field public static final java.lang.String EFFECT_TEMPERATURE = "android.media.effect.effects.ColorTemperatureEffect";
+    field public static final java.lang.String EFFECT_TINT = "android.media.effect.effects.TintEffect";
+    field public static final java.lang.String EFFECT_VIGNETTE = "android.media.effect.effects.VignetteEffect";
   }
 
   public abstract interface EffectUpdateListener {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 6fb7965..e3075d7 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -1943,7 +1943,6 @@
         // we are back active so skip it.
         unscheduleGcIdler();
 
-        Slog.i(TAG, "Launch: profileFd=" + r.profileFile + " stop=" + r.autoStopProfiler);
         if (r.profileFd != null) {
             mBoundApplication.setProfiler(r.profileFile, r.profileFd);
             mBoundApplication.startProfiling();
diff --git a/core/java/android/app/ProgressDialog.java b/core/java/android/app/ProgressDialog.java
index d421173..f1a04f8 100644
--- a/core/java/android/app/ProgressDialog.java
+++ b/core/java/android/app/ProgressDialog.java
@@ -343,7 +343,7 @@
     
     private void onProgressChanged() {
         if (mProgressStyle == STYLE_HORIZONTAL) {
-            if (!mViewUpdateHandler.hasMessages(0)) {
+            if (mViewUpdateHandler != null && !mViewUpdateHandler.hasMessages(0)) {
                 mViewUpdateHandler.sendEmptyMessage(0);
             }
         }
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index a7b0037..83d1bda 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfoInternal;
-import android.net.LinkAddress;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
@@ -30,7 +29,6 @@
 import android.os.Message;
 import android.util.Log;
 
-import java.net.InetAddress;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -184,11 +182,14 @@
         return -1;
     }
 
-    /**
-     * @param enabled
-     */
-    public void setDataEnable(boolean enabled) {
-        android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+    @Override
+    public void setUserDataEnable(boolean enabled) {
+        Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
+    }
+
+    @Override
+    public void setPolicyDataEnable(boolean enabled) {
+        Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
     }
 
     /**
diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java
index 6115fef..3e27b0d 100644
--- a/core/java/android/net/DnsPinger.java
+++ b/core/java/android/net/DnsPinger.java
@@ -67,7 +67,7 @@
     private final Context mContext;
     private final int mConnectionType;
     private final Handler mTarget;
-    private final InetAddress mDefaultDns;
+    private final ArrayList<InetAddress> mDefaultDns;
     private String TAG;
 
     private static final int BASE = Protocol.BASE_DNS_PINGER;
@@ -113,7 +113,8 @@
             throw new IllegalArgumentException("Invalid connectionType in constructor: "
                     + connectionType);
         }
-        mDefaultDns = getDefaultDns();
+        mDefaultDns = new ArrayList<InetAddress>();
+        mDefaultDns.add(getDefaultDns());
         mEventCounter = 0;
     }
 
@@ -213,17 +214,16 @@
                 for (ActivePing activePing : mActivePings)
                     activePing.socket.close();
                 mActivePings.clear();
-                removeMessages(ACTION_PING_DNS);
                 break;
         }
     }
 
     /**
-     * @return The first DNS in the link properties of the specified connection
-     *         type or the default system DNS if the link properties has null
-     *         dns set. Should not be null.
+     * Returns a list of DNS addresses, coming from either the link properties of the
+     * specified connection or the default system DNS if the link properties has no dnses.
+     * @return a non-empty non-null list
      */
-    public InetAddress getDns() {
+    public List<InetAddress> getDnsList() {
         LinkProperties curLinkProps = getCurrentLinkProperties();
         if (curLinkProps == null) {
             Slog.e(TAG, "getCurLinkProperties:: LP for type" + mConnectionType + " is null!");
@@ -236,7 +236,7 @@
             return mDefaultDns;
         }
 
-        return dnses.iterator().next();
+        return new ArrayList<InetAddress>(dnses);
     }
 
     /**
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index e39725a..9f0f9cd 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -19,9 +19,6 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Message;
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo;
-import android.net.LinkProperties;
 import android.util.Slog;
 
 /**
@@ -168,7 +165,14 @@
         return true;
     }
 
-    public void setDataEnable(boolean enabled) {
+    @Override
+    public void setUserDataEnable(boolean enabled) {
+        // ignored
+    }
+
+    @Override
+    public void setPolicyDataEnable(boolean enabled) {
+        // ignored
     }
 
     @Override
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index b035c51..21ecc22 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -17,15 +17,7 @@
 package android.net;
 
 import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.DhcpInfoInternal;
-import android.net.LinkAddress;
-import android.net.LinkCapabilities;
-import android.net.LinkProperties;
-import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkStateTracker;
-import android.net.NetworkUtils;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
@@ -34,7 +26,6 @@
 import android.os.ServiceManager;
 import android.util.Log;
 
-import java.net.InetAddress;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -302,11 +293,14 @@
         return -1;
     }
 
-    /**
-     * @param enabled
-     */
-    public void setDataEnable(boolean enabled) {
-        Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+    @Override
+    public void setUserDataEnable(boolean enabled) {
+        Log.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
+    }
+
+    @Override
+    public void setPolicyDataEnable(boolean enabled) {
+        Log.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
     }
 
     /**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 1b95b60..c9553c0 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -64,9 +64,11 @@
     boolean requestRouteToHostAddress(int networkType, in byte[] hostAddress);
 
     boolean getMobileDataEnabled();
-
     void setMobileDataEnabled(boolean enabled);
 
+    /** Policy control over specific {@link NetworkStateTracker}. */
+    void setPolicyDataEnable(int networkType, boolean enabled);
+
     int tether(String iface);
 
     int untether(String iface);
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 5501f38..5929cfb 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -16,19 +16,26 @@
 
 package android.net;
 
+import static com.android.internal.telephony.DataConnectionTracker.CMD_SET_POLICY_DATA_ENABLE;
+import static com.android.internal.telephony.DataConnectionTracker.CMD_SET_USER_DATA_ENABLE;
+import static com.android.internal.telephony.DataConnectionTracker.DISABLED;
+import static com.android.internal.telephony.DataConnectionTracker.ENABLED;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.NetworkInfo.DetailedState;
 import android.os.Bundle;
-import android.os.HandlerThread;
+import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
 import android.os.ServiceManager;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Slog;
 
 import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.ITelephony;
@@ -36,13 +43,6 @@
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.AsyncChannel;
 
-import android.net.NetworkInfo.DetailedState;
-import android.net.NetworkInfo;
-import android.net.LinkProperties;
-import android.telephony.TelephonyManager;
-import android.util.Slog;
-import android.text.TextUtils;
-
 /**
  * Track the state of mobile data connectivity. This is done by
  * receiving broadcast intents from the Phone process whenever
@@ -452,17 +452,22 @@
         return false;
     }
 
-    /**
-     * @param enabled
-     */
-    public void setDataEnable(boolean enabled) {
-        try {
-            if (DBG) log("setDataEnable: E enabled=" + enabled);
-            mDataConnectionTrackerAc.sendMessage(DataConnectionTracker.CMD_SET_DATA_ENABLE,
-                    enabled ? DataConnectionTracker.ENABLED : DataConnectionTracker.DISABLED);
-            if (VDBG) log("setDataEnable: X enabled=" + enabled);
-        } catch (Exception e) {
-            loge("setDataEnable: X mAc was null" + e);
+    @Override
+    public void setUserDataEnable(boolean enabled) {
+        if (DBG) log("setUserDataEnable: E enabled=" + enabled);
+        final AsyncChannel channel = mDataConnectionTrackerAc;
+        if (channel != null) {
+            channel.sendMessage(CMD_SET_USER_DATA_ENABLE, enabled ? ENABLED : DISABLED);
+        }
+        if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
+    }
+
+    @Override
+    public void setPolicyDataEnable(boolean enabled) {
+        if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")");
+        final AsyncChannel channel = mDataConnectionTrackerAc;
+        if (channel != null) {
+            channel.sendMessage(CMD_SET_POLICY_DATA_ENABLE, enabled ? ENABLED : DISABLED);
         }
     }
 
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index f53063d..1735592 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -136,9 +136,17 @@
     public boolean isAvailable();
 
     /**
-     * @param enabled
+     * User control of data connection through this network, typically persisted
+     * internally.
      */
-    public void setDataEnable(boolean enabled);
+    public void setUserDataEnable(boolean enabled);
+
+    /**
+     * Policy control of data connection through this network, typically not
+     * persisted internally. Usually used when {@link NetworkPolicy#limitBytes}
+     * is passed.
+     */
+    public void setPolicyDataEnable(boolean enabled);
 
     /**
      * -------------------------------------------------------------
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
index 17f0c1b..8bac6bd 100644
--- a/core/java/android/preference/SwitchPreference.java
+++ b/core/java/android/preference/SwitchPreference.java
@@ -41,14 +41,16 @@
     private CharSequence mSwitchOff;
     private final Listener mListener = new Listener();
 
-    private class Listener implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
-        @Override
-        public void onClick(View v) {
-            SwitchPreference.this.onClick();
-        }
-
+    private class Listener implements CompoundButton.OnCheckedChangeListener {
         @Override
         public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            if (!callChangeListener(isChecked)) {
+                // Listener didn't like it, change it back.
+                // CompoundButton will make sure we don't recurse.
+                buttonView.setChecked(!isChecked);
+                return;
+            }
+
             SwitchPreference.this.setChecked(isChecked);
         }
     }
@@ -111,12 +113,6 @@
                 switchView.setTextOff(mSwitchOff);
                 switchView.setOnCheckedChangeListener(mListener);
             }
-
-            if (checkableView.hasFocusable()) {
-                // This is a focusable list item. Attach a click handler to toggle the button
-                // for the rest of the item.
-                view.setOnClickListener(mListener);
-            }
         }
 
         syncSummaryView(view);
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index c5a924b..886edaf 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -202,6 +202,42 @@
         public static final String GEOCODED_LOCATION = "geocoded_location";
 
         /**
+         * The cached URI to look up the contact associated with the phone number, if it exists.
+         * This value is not guaranteed to be current, if the contact information
+         * associated with this number has changed.
+         * <P>Type: TEXT</P>
+         * @hide
+         */
+        public static final String CACHED_LOOKUP_URI = "lookup_uri";
+
+        /**
+         * The cached phone number of the contact which matches this entry, if it exists.
+         * This value is not guaranteed to be current, if the contact information
+         * associated with this number has changed.
+         * <P>Type: TEXT</P>
+         * @hide
+         */
+        public static final String CACHED_MATCHED_NUMBER = "matched_number";
+
+        /**
+         * The cached normalized version of the phone number, if it exists.
+         * This value is not guaranteed to be current, if the contact information
+         * associated with this number has changed.
+         * <P>Type: TEXT</P>
+         * @hide
+         */
+        public static final String CACHED_NORMALIZED_NUMBER = "normalized_number";
+
+        /**
+         * The cached photo id of the picture associated with the phone number, if it exists.
+         * This value is not guaranteed to be current, if the contact information
+         * associated with this number has changed.
+         * <P>Type: INTEGER (long)</P>
+         * @hide
+         */
+        public static final String CACHED_PHOTO_ID = "photo_id";
+
+        /**
          * Adds a call to the call log.
          *
          * @param ci the CallerInfo object to get the target contact from.  Can be null
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 03cba3d..15c57e6 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3768,12 +3768,21 @@
 
 
         /**
-         * The {@link ComponentName} string of the service to be used as the spell checker
+         * The {@link ComponentName} string of the selected spell checker service which is
+         * one of the services managed by the text service manager.
+         *
+         * @hide
+         */
+        public static final String SELECTED_SPELL_CHECKER = "selected_spell_checker";
+
+        /**
+         * The {@link ComponentName} string of the selected subtype of the selected spell checker
          * service which is one of the services managed by the text service manager.
          *
          * @hide
          */
-        public static final String SPELL_CHECKER_SERVICE = "spell_checker_service";
+        public static final String SELECTED_SPELL_CHECKER_SUBTYPE =
+                "selected_spell_checker_subtype";
 
         /**
          * What happens when the user presses the Power button while in-call
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c7bf8e3..0dc781f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -218,4 +218,9 @@
      * Called by the settings application to temporarily set the pointer speed.
      */
     void setPointerSpeed(int speed);
+
+    /**
+     * Block until all windows the window manager knows about have been drawn.
+     */
+    void waitForAllDrawn();
 }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6b09049..fdd9b2c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -177,7 +177,8 @@
             @ViewDebug.IntToString(from = TYPE_SYSTEM_ERROR, to = "TYPE_SYSTEM_ERROR"),
             @ViewDebug.IntToString(from = TYPE_INPUT_METHOD, to = "TYPE_INPUT_METHOD"),
             @ViewDebug.IntToString(from = TYPE_INPUT_METHOD_DIALOG, to = "TYPE_INPUT_METHOD_DIALOG"),
-            @ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY, to = "TYPE_SECURE_SYSTEM_OVERLAY")
+            @ViewDebug.IntToString(from = TYPE_SECURE_SYSTEM_OVERLAY, to = "TYPE_SECURE_SYSTEM_OVERLAY"),
+            @ViewDebug.IntToString(from = TYPE_BOOT_PROGRESS, to = "TYPE_BOOT_PROGRESS")
         })
         public int type;
     
@@ -401,6 +402,13 @@
         public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;
 
         /**
+         * Window type: The boot progress dialog, goes on top of everything
+         * in the world.
+         * @hide
+         */
+        public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index b7dfabc..f4b9252 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -116,7 +116,8 @@
 
     /**
      * Indicates whether the animation transformation should be applied before the
-     * animation starts.
+     * animation starts. The value of this variable is only relevant if mFillEnabled is true;
+     * otherwise it is assumed to be true.
      */
     boolean mFillBefore = true;
 
@@ -127,7 +128,7 @@
     boolean mFillAfter = false;
 
     /**
-     * Indicates whether fillAfter should be taken into account.
+     * Indicates whether fillBefore should be taken into account.
      */
     boolean mFillEnabled = false;    
 
@@ -505,9 +506,9 @@
     }
 
     /**
-     * If fillEnabled is true, this animation will apply fillBefore and fillAfter.
+     * If fillEnabled is true, this animation will apply the value of fillBefore.
      *
-     * @return true if the animation will take fillBefore and fillAfter into account
+     * @return true if the animation will take fillBefore into account
      * @attr ref android.R.styleable#Animation_fillEnabled
      */
     public boolean isFillEnabled() {
@@ -515,11 +516,11 @@
     }
 
     /**
-     * If fillEnabled is true, the animation will apply the value of fillBefore and
-     * fillAfter. Otherwise, fillBefore and fillAfter are ignored and the animation
-     * transformation is always applied.
+     * If fillEnabled is true, the animation will apply the value of fillBefore.
+     * Otherwise, fillBefore is ignored and the animation
+     * transformation is always applied until the animation ends.
      *
-     * @param fillEnabled true if the animation should take fillBefore and fillAfter into account
+     * @param fillEnabled true if the animation should take the value of fillBefore into account
      * @attr ref android.R.styleable#Animation_fillEnabled
      *
      * @see #setFillBefore(boolean)
@@ -531,7 +532,8 @@
 
     /**
      * If fillBefore is true, this animation will apply its transformation
-     * before the start time of the animation. Defaults to true if not set.
+     * before the start time of the animation. Defaults to true if
+     * {@link #setFillEnabled(boolean)} is not set to true.
      * Note that this applies when using an {@link
      * android.view.animation.AnimationSet AnimationSet} to chain
      * animations. The transformation is not applied before the AnimationSet
@@ -549,10 +551,9 @@
     /**
      * If fillAfter is true, the transformation that this animation performed
      * will persist when it is finished. Defaults to false if not set.
-     * Note that this applies when using an {@link
+     * Note that this applies to individual animations and when using an {@link
      * android.view.animation.AnimationSet AnimationSet} to chain
-     * animations. The transformation is not applied before the AnimationSet
-     * itself starts.
+     * animations.
      *
      * @param fillAfter true if the animation should apply its transformation after it ends
      * @attr ref android.R.styleable#Animation_fillAfter
@@ -674,7 +675,9 @@
 
     /**
      * If fillBefore is true, this animation will apply its transformation
-     * before the start time of the animation.
+     * before the start time of the animation. If fillBefore is false and
+     * {@link #isFillEnabled() fillEnabled} is true, the transformation will not be applied until
+     * the start time of the animation.
      *
      * @return true if the animation applies its transformation before it starts
      * @attr ref android.R.styleable#Animation_fillBefore
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index d60ce4f..bb13052 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -135,11 +135,40 @@
     public void setCurrentSpellChecker(SpellCheckerInfo sci) {
         try {
             if (sci == null) {
-                throw new NullPointerException("SpellCheckerInfo is null");
+                throw new NullPointerException("SpellCheckerInfo is null.");
             }
-            sService.setCurrentSpellChecker(sci.getId());
+            sService.setCurrentSpellChecker(null, sci.getId());
         } catch (RemoteException e) {
             Log.e(TAG, "Error in setCurrentSpellChecker: " + e);
         }
     }
+
+    /**
+     * @hide
+     */
+    public SpellCheckerSubtype getCurrentSpellCheckerSubtype() {
+        try {
+            // Passing null as a locale for ICS
+            return sService.getCurrentSpellCheckerSubtype(null);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in getCurrentSpellCheckerSubtype: " + e);
+            return null;
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public void setSpellCheckerSubtype(SpellCheckerSubtype subtype) {
+        try {
+            if (subtype == null) {
+                throw new NullPointerException("SpellCheckerSubtype is null.");
+            }
+            sService.setCurrentSpellCheckerSubtype(null, subtype.hashCode());
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error in setSpellCheckerSubtype:" + e);
+        }
+    }
+
+
 }
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index bb4b2a3..cc30c176 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.os.Bundle;
 import android.view.textservice.SpellCheckerInfo;
+import android.view.textservice.SpellCheckerSubtype;
 
 /**
  * Interface to the text service manager.
@@ -29,10 +30,12 @@
  */
 interface ITextServicesManager {
     SpellCheckerInfo getCurrentSpellChecker(String locale);
+    SpellCheckerSubtype getCurrentSpellCheckerSubtype(String locale);
     oneway void getSpellCheckerService(String sciId, in String locale,
             in ITextServicesSessionListener tsListener,
             in ISpellCheckerSessionListener scListener, in Bundle bundle);
     oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener);
-    oneway void setCurrentSpellChecker(String sciId);
+    oneway void setCurrentSpellChecker(String locale, String sciId);
+    oneway void setCurrentSpellCheckerSubtype(String locale, int hashCode);
     SpellCheckerInfo[] getEnabledSpellCheckers();
 }
diff --git a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
index d3baa2b..aa9fa45 100644
--- a/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
+++ b/core/java/com/android/internal/widget/multiwaveview/TargetDrawable.java
@@ -25,6 +25,8 @@
 
 public class TargetDrawable {
     private static final String TAG = "TargetDrawable";
+    private static final boolean DEBUG = false;
+
     public static final int[] STATE_ACTIVE =
             { android.R.attr.state_enabled, android.R.attr.state_active };
     public static final int[] STATE_INACTIVE =
@@ -139,11 +141,13 @@
                 maxWidth = Math.max(maxWidth, childDrawable.getIntrinsicWidth());
                 maxHeight = Math.max(maxHeight, childDrawable.getIntrinsicHeight());
             }
-            Log.v(TAG, "union of childDrawable rects " + d + " to: " + maxWidth + "x" + maxHeight);
+            if (DEBUG) Log.v(TAG, "union of childDrawable rects " + d + " to: "
+                        + maxWidth + "x" + maxHeight);
             d.setBounds(0, 0, maxWidth, maxHeight);
             for (int i = 0; i < d.getStateCount(); i++) {
                 Drawable childDrawable = d.getStateDrawable(i);
-                Log.v(TAG, "sizing drawable " + childDrawable + " to: " + maxWidth + "x" + maxHeight);
+                if (DEBUG) Log.v(TAG, "sizing drawable " + childDrawable + " to: "
+                            + maxWidth + "x" + maxHeight);
                 childDrawable.setBounds(0, 0, maxWidth, maxHeight);
             }
         } else if (mDrawable != null) {
diff --git a/core/res/res/drawable-hdpi/notify_panel_notification_icon_bg.png b/core/res/res/drawable-hdpi/notify_panel_notification_icon_bg.png
index f5b762e..6f37a22 100644
--- a/core/res/res/drawable-hdpi/notify_panel_notification_icon_bg.png
+++ b/core/res/res/drawable-hdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/notify_panel_notification_icon_bg.png b/core/res/res/drawable-mdpi/notify_panel_notification_icon_bg.png
index f0bdfb0..c286875 100644
--- a/core/res/res/drawable-mdpi/notify_panel_notification_icon_bg.png
+++ b/core/res/res/drawable-mdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/notify_panel_notification_icon_bg.png b/core/res/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
index 4cc515e..9128e62 100644
--- a/core/res/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
+++ b/core/res/res/drawable-xhdpi/notify_panel_notification_icon_bg.png
Binary files differ
diff --git a/core/res/res/drawable/notify_panel_notification_icon_bg_tile.xml b/core/res/res/drawable/notify_panel_notification_icon_bg_tile.xml
new file mode 100644
index 0000000..fa3c398
--- /dev/null
+++ b/core/res/res/drawable/notify_panel_notification_icon_bg_tile.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<bitmap
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tileMode="repeat"
+    android:src="@android:drawable/notify_panel_notification_icon_bg"
+    />
diff --git a/core/res/res/layout/preference_widget_switch.xml b/core/res/res/layout/preference_widget_switch.xml
index ed4ed57..83ef097 100644
--- a/core/res/res/layout/preference_widget_switch.xml
+++ b/core/res/res/layout/preference_widget_switch.xml
@@ -22,5 +22,4 @@
     android:layout_height="wrap_content"
     android:layout_gravity="center"
     android:padding="16dip"
-    android:focusable="false"
-    android:clickable="false" />
+    android:focusable="false" />
diff --git a/core/res/res/layout/status_bar_latest_event_content.xml b/core/res/res/layout/status_bar_latest_event_content.xml
index 0dc6741..ec1bc81 100644
--- a/core/res/res/layout/status_bar_latest_event_content.xml
+++ b/core/res/res/layout/status_bar_latest_event_content.xml
@@ -6,7 +6,7 @@
     <ImageView android:id="@+id/icon"
         android:layout_width="@dimen/notification_large_icon_width"
         android:layout_height="@dimen/notification_large_icon_height"
-        android:background="@drawable/notify_panel_notification_icon_bg"
+        android:background="@android:drawable/notify_panel_notification_icon_bg_tile"
         android:scaleType="center"
         />
     <include layout="@layout/status_bar_latest_event_content_large_icon" 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8db6b4f..bb61c72 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3934,15 +3934,14 @@
     <declare-styleable name="Animation">
         <!-- Defines the interpolator used to smooth the animation movement in time. -->
         <attr name="interpolator" />
-        <!-- When set to true, fillAfter is taken into account. -->
+        <!-- When set to true, the value of fillBefore is taken into account. -->
         <attr name="fillEnabled" format="boolean" />
-        <!-- When set to true, the animation transformation is applied before the animation has
-             started. The default value is true. If fillEnabled is not set to true, fillBefore
-             is assumed to be true. -->
+        <!-- When set to true or when fillEnabled is not set to true, the animation transformation
+             is applied before the animation has started. The default value is true. -->
         <attr name="fillBefore" format="boolean" />
         <!-- When set to true, the animation transformation is applied after the animation is
-             over. The default value is false. If fillEnabled is not set to true and the animation
-             is not set on a View, fillAfter is assumed to be true. -->
+             over. The default value is false. If fillEnabled is not set to true and the
+             animation is not set on a View, fillAfter is assumed to be true.-->
         <attr name="fillAfter" format="boolean" />
         <!-- Amount of time (in milliseconds) for the animation to run. -->
         <attr name="duration" />
@@ -4100,7 +4099,6 @@
     <declare-styleable name="Animator">
         <!-- Defines the interpolator used to smooth the animation movement in time. -->
         <attr name="interpolator" />
-        <!-- When set to true, fillAfter is taken into account. -->
         <!-- Amount of time (in milliseconds) for the animation to run. -->
         <attr name="duration" />
         <!-- Delay in milliseconds before the animation runs, once start time is reached. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c80923d..8eaac8c 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3163,6 +3163,8 @@
     <string name="data_usage_4g_limit_title">4G data disabled</string>
     <!-- Notification title when mobile data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
     <string name="data_usage_mobile_limit_title">Mobile data disabled</string>
+    <!-- Notification title when Wi-Fi data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
+    <string name="data_usage_wifi_limit_title">Wi-Fi data disabled</string>
     <!-- Notification body when data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] -->
     <string name="data_usage_limit_body">Touch to enable</string>
 
@@ -3172,6 +3174,8 @@
     <string name="data_usage_4g_limit_snoozed_title">4G data limit exceeded</string>
     <!-- Notification title when mobile data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
     <string name="data_usage_mobile_limit_snoozed_title">Mobile data limit exceeded</string>
+    <!-- Notification title when Wi-Fi data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
+    <string name="data_usage_wifi_limit_snoozed_title">Wi-Fi data limit exceeded</string>
     <!-- Notification body when data usage has exceeded limit threshold. [CHAR LIMIT=32] -->
     <string name="data_usage_limit_snoozed_body"><xliff:g id="size" example="3.8GB">%s</xliff:g> over specified limit</string>
 
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 54d04aa..ff45fa3 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -351,13 +351,13 @@
 
 // ----------------------------------------------------------------------------
 
-status_t Surface::lock(SurfaceInfo* other, Region* dirtyIn) {
+status_t Surface::lock(SurfaceInfo* other, Region* inOutDirtyRegion) {
     ANativeWindow_Buffer outBuffer;
 
     ARect temp;
     ARect* inOutDirtyBounds = NULL;
-    if (dirtyIn) {
-        temp = dirtyIn->getBounds();
+    if (inOutDirtyRegion) {
+        temp = inOutDirtyRegion->getBounds();
         inOutDirtyBounds = &temp;
     }
 
@@ -371,6 +371,11 @@
         other->format = uint32_t(outBuffer.format);
         other->bits = outBuffer.bits;
     }
+
+    if (inOutDirtyRegion) {
+        inOutDirtyRegion->set( static_cast<Rect const&>(temp) );
+    }
+
     return err;
 }
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e89d6ec..04f3c58 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1493,7 +1493,8 @@
         const float y = (int) floorf(dstTop + mSnapshot->transform->getTranslateY() + 0.5f);
 
         GLenum filter = GL_NEAREST;
-        if (u1 > 0.0f || u2 < 1.0f || v1 > 0.0f || v2 < 1.0f) {
+        // Enable linear filtering if the source rectangle is scaled
+        if (srcRight - srcLeft != dstRight - dstLeft || srcBottom - srcTop != dstBottom - dstTop) {
             filter = GL_LINEAR;
         }
         texture->setFilter(filter, filter, true);
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e3ef717..25c4200 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1761,6 +1761,60 @@
         }
     }
 
+    /**
+     * @hide
+     * Registers a remote control display that will be sent information by remote control clients.
+     * @param rcd
+     */
+    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
+        if (rcd == null) {
+            return;
+        }
+        IAudioService service = getService();
+        try {
+            service.registerRemoteControlDisplay(rcd);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in registerRemoteControlDisplay " + e);
+        }
+    }
+
+    /**
+     * @hide
+     * Unregisters a remote control display that was sent information by remote control clients.
+     * @param rcd
+     */
+    public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
+        if (rcd == null) {
+            return;
+        }
+        IAudioService service = getService();
+        try {
+            service.unregisterRemoteControlDisplay(rcd);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in unregisterRemoteControlDisplay " + e);
+        }
+    }
+
+    /**
+     * @hide
+     * Sets the artwork size a remote control display expects when receiving bitmaps.
+     * @param rcd
+     * @param w the maximum width of the expected bitmap. Negative values indicate it is
+     *   useless to send artwork.
+     * @param h the maximum height of the expected bitmap. Negative values indicate it is
+     *   useless to send artwork.
+     */
+    public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
+        if (rcd == null) {
+            return;
+        }
+        IAudioService service = getService();
+        try {
+            service.remoteControlDisplayUsesBitmapSize(rcd, w, h);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in remoteControlDisplayUsesBitmapSize " + e);
+        }
+    }
 
     // FIXME remove because we are not using intents anymore between AudioService and RcDisplay
     /**
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
index 19ea202..d000906 100644
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -39,4 +39,9 @@
     void setTransportControlFlags(int generationId, int transportControlFlags);
 
     void setArtwork(int generationId, in Bitmap artwork);
+
+    /**
+     * To combine metadata text and artwork in one binder call
+     */
+    void setAllMetadata(int generationId, in Bundle metadata, in Bitmap artwork);
 }
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index bfe08b9..e6331ce 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -240,109 +240,144 @@
      * Class used to modify metadata in a {@link RemoteControlClient} object.
      */
     public class MetadataEditor {
+        protected boolean mMetadataChanged;
+        protected boolean mArtworkChanged;
+        protected Bitmap mEditorArtwork;
+        protected Bundle mEditorMetadata;
+        private boolean mApplied = false;
 
-        private MetadataEditor() { /* only use factory */ }
+        // only use RemoteControlClient.editMetadata() to get a MetadataEditor instance
+        private MetadataEditor() { }
+        /**
+         * @hide
+         */
+        public Object clone() throws CloneNotSupportedException {
+            throw new CloneNotSupportedException();
+        }
 
-        public MetadataEditor putString(int key, String value) {
+        /**
+         * Adds textual information to be displayed.
+         * Note that none of the information added after {@link #apply()} has been called,
+         * will be displayed.
+         * @param key the identifier of a the metadata field to set. Valid values are
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
+         * @param value the text for the given key, or null to signify there is no valid
+         *      information for the field.
+         * @return      FIXME description
+         */
+        public synchronized MetadataEditor putString(int key, String value) {
+            if (mApplied) {
+                Log.e(TAG, "Can't edit a previously applied MetadataEditor");
+                return this;
+            }
+            mEditorMetadata.putString(String.valueOf(key), value);
+            mMetadataChanged = true;
             return this;
         }
 
-        public MetadataEditor putBitmap(int key, Bitmap bitmap) {
-            return this;
-        }
+        /**
+         * The metadata key for the content artwork / album art.
+         */
+        public final int METADATA_KEY_ARTWORK = 100;
 
-        public void clear() {
-
-        }
-
-        public void apply() {
-
-        }
-    }
-
-    public MetadataEditor editMetadata(boolean startEmpty) {
-        return (new MetadataEditor());
-    }
-
-
-    /**
-     * @hide
-     * FIXME migrate this functionality under MetadataEditor
-     * Start collecting information to be displayed.
-     * Use {@link #commitMetadata()} to signal the end of the collection which has been created
-     *  through one or multiple calls to {@link #addMetadataString(int, int, String)}.
-     */
-    public void startMetadata() {
-        synchronized(mCacheLock) {
-            mMetadata.clear();
-        }
-    }
-
-    /**
-     * @hide
-     * FIXME migrate this functionality under MetadataEditor
-     * Adds textual information to be displayed.
-     * Note that none of the information added before {@link #startMetadata()},
-     * and after {@link #commitMetadata()} has been called, will be displayed.
-     * @param key the identifier of a the metadata field to set. Valid values are
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER},
-     *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
-     * @param value the String for the field value, or null to signify there is no valid
-     *      information for the field.
-     */
-    public void addMetadataString(int key, String value) {
-        synchronized(mCacheLock) {
-            // store locally
-            mMetadata.putString(String.valueOf(key), value);
-        }
-    }
-
-    /**
-     * @hide
-     * FIXME migrate this functionality under MetadataEditor
-     * Marks all the metadata previously set with {@link #addMetadataString(int, int, String)} as
-     * eligible to be displayed.
-     */
-    public void commitMetadata() {
-        synchronized(mCacheLock) {
-            // send to remote control display if conditions are met
-            sendMetadata_syncCacheLock();
-        }
-    }
-
-    /**
-     * @hide
-     * FIXME migrate this functionality under MetadataEditor
-     * Sets the album / artwork picture to be displayed on the remote control.
-     * @param artwork the bitmap for the artwork, or null if there isn't any.
-     * @see android.graphics.Bitmap
-     */
-    public void setArtwork(Bitmap artwork) {
-        synchronized(mCacheLock) {
-            // resize and store locally
-            if (mArtworkExpectedWidth > 0) {
-                mArtwork = scaleBitmapIfTooBig(artwork,
+        /**
+         * Sets the album / artwork picture to be displayed on the remote control.
+         * @param key FIXME description
+         * @param bitmap the bitmap for the artwork, or null if there isn't any.
+         * @return FIXME description
+         * @see android.graphics.Bitmap
+         */
+        public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap) {
+            if (mApplied) {
+                Log.e(TAG, "Can't edit a previously applied MetadataEditor");
+                return this;
+            }
+            if (key != METADATA_KEY_ARTWORK) {
+                return this;
+            }
+            if ((mArtworkExpectedWidth > 0) && (mArtworkExpectedHeight > 0)) {
+                mEditorArtwork = scaleBitmapIfTooBig(bitmap,
                         mArtworkExpectedWidth, mArtworkExpectedHeight);
             } else {
                 // no valid resize dimensions, store as is
-                mArtwork = artwork;
+                mEditorArtwork = bitmap;
             }
-            // send to remote control display if conditions are met
-            sendArtwork_syncCacheLock();
+            mArtworkChanged = true;
+            return this;
         }
+
+        /**
+         * FIXME description
+         */
+        public synchronized void clear() {
+            if (mApplied) {
+                Log.e(TAG, "Can't clear a previously applied MetadataEditor");
+                return;
+            }
+            mEditorMetadata.clear();
+            mEditorArtwork = null;
+        }
+
+        /**
+         * FIXME description
+         */
+        public synchronized void apply() {
+            if (mApplied) {
+                Log.e(TAG, "Can't apply a previously applied MetadataEditor");
+                return;
+            }
+            synchronized(mCacheLock) {
+                // assign the edited data
+                mMetadata = new Bundle(mEditorMetadata);
+                mArtwork = mEditorArtwork;
+                if (mMetadataChanged & mArtworkChanged) {
+                    // send to remote control display if conditions are met
+                    sendMetadataWithArtwork_syncCacheLock();
+                } else if (mMetadataChanged) {
+                    // send to remote control display if conditions are met
+                    sendMetadata_syncCacheLock();
+                } else if (mArtworkChanged) {
+                    // send to remote control display if conditions are met
+                    sendArtwork_syncCacheLock();
+                }
+                mApplied = true;
+            }
+        }
+    }
+
+    /**
+     * FIXME description
+     * @param startEmpty
+     * @return
+     */
+    public MetadataEditor editMetadata(boolean startEmpty) {
+        MetadataEditor editor = new MetadataEditor();
+        if (startEmpty) {
+            editor.mEditorMetadata = new Bundle();
+            editor.mEditorArtwork = null;
+            editor.mMetadataChanged = true;
+            editor.mArtworkChanged = true;
+        } else {
+            editor.mEditorMetadata = new Bundle(mMetadata);
+            editor.mEditorArtwork = mArtwork;
+            editor.mMetadataChanged = false;
+            editor.mArtworkChanged = false;
+        }
+        return editor;
     }
 
     /**
@@ -402,9 +437,13 @@
     /**
      * Cache for the artwork bitmap.
      * Access synchronized on mCacheLock
+     * Artwork and metadata are not kept in one Bundle because the bitmap sometimes needs to be
+     * accessed to be resized, in which case a copy will be made. This would add overhead in
+     * Bundle operations.
      */
     private Bitmap mArtwork;
     private final int ARTWORK_DEFAULT_SIZE = 256;
+    private final int ARTWORK_INVALID_SIZE = -1;
     private int mArtworkExpectedWidth = ARTWORK_DEFAULT_SIZE;
     private int mArtworkExpectedHeight = ARTWORK_DEFAULT_SIZE;
     /**
@@ -417,6 +456,7 @@
      * Access synchronized on mCacheLock
      */
     private Bundle mMetadata = new Bundle();
+
     /**
      * The current remote control client generation ID across the system
      */
@@ -567,15 +607,19 @@
         }
     }
 
+    private void detachFromDisplay_syncCacheLock() {
+        mRcDisplay = null;
+        mArtworkExpectedWidth = ARTWORK_INVALID_SIZE;
+        mArtworkExpectedHeight = ARTWORK_INVALID_SIZE;
+    }
+
     private void sendPlaybackState_syncCacheLock() {
         if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
             try {
                 mRcDisplay.setPlaybackState(mInternalClientGenId, mPlaybackState);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error in setPlaybackState(), dead display "+e);
-                mRcDisplay = null;
-                mArtworkExpectedWidth = -1;
-                mArtworkExpectedHeight = -1;
+                detachFromDisplay_syncCacheLock();
             }
         }
     }
@@ -586,9 +630,7 @@
                 mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error in sendPlaybackState(), dead display "+e);
-                mRcDisplay = null;
-                mArtworkExpectedWidth = -1;
-                mArtworkExpectedHeight = -1;
+                detachFromDisplay_syncCacheLock();
             }
         }
     }
@@ -600,9 +642,7 @@
                         mTransportControlFlags);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error in sendTransportControlFlags(), dead display "+e);
-                mRcDisplay = null;
-                mArtworkExpectedWidth = -1;
-                mArtworkExpectedHeight = -1;
+                detachFromDisplay_syncCacheLock();
             }
         }
     }
@@ -617,9 +657,22 @@
                 mRcDisplay.setArtwork(mInternalClientGenId, mArtwork);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error in sendArtwork(), dead display "+e);
-                mRcDisplay = null;
-                mArtworkExpectedWidth = -1;
-                mArtworkExpectedHeight = -1;
+                detachFromDisplay_syncCacheLock();
+            }
+        }
+    }
+
+    private void sendMetadataWithArtwork_syncCacheLock() {
+        if ((mCurrentClientGenId == mInternalClientGenId) && (mRcDisplay != null)) {
+            // even though we have already scaled in setArtwork(), when this client needs to
+            // send the bitmap, there might be newer and smaller expected dimensions, so we have
+            // to check again.
+            mArtwork = scaleBitmapIfTooBig(mArtwork, mArtworkExpectedWidth, mArtworkExpectedHeight);
+            try {
+                mRcDisplay.setAllMetadata(mInternalClientGenId, mMetadata, mArtwork);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error in setAllMetadata(), dead display "+e);
+                detachFromDisplay_syncCacheLock();
             }
         }
     }
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
index 92e84c2..09f91f5 100644
--- a/media/libstagefright/MP3Extractor.cpp
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -52,7 +52,10 @@
         *post_id3_pos = 0;
     }
 
+    bool resync_from_head = false;
     if (*inout_pos == 0) {
+        resync_from_head = true;
+
         // Skip an optional ID3 header if syncing at the very beginning
         // of the datasource.
 
@@ -137,22 +140,20 @@
 
         uint32_t header = U32_AT(tmp);
 
-        if (match_header != 0 && (header & kMask) != (match_header & kMask)) {
-            ++pos;
-            ++tmp;
-            --remainingBytes;
-            continue;
-        }
-
         size_t frame_size;
-        int sample_rate, num_channels, bitrate;
-        if (!GetMPEGAudioFrameSize(
-                    header, &frame_size,
-                    &sample_rate, &num_channels, &bitrate)) {
-            ++pos;
-            ++tmp;
-            --remainingBytes;
-            continue;
+        if ((match_header != 0 && (header & kMask) != (match_header & kMask))
+                || !GetMPEGAudioFrameSize(header, &frame_size)) {
+            if (resync_from_head) {
+                // This isn't a valid mp3 file because it failed to detect
+                // a header while a valid mp3 file should have a valid
+                // header here.
+                break;
+            } else {
+                ++pos;
+                ++tmp;
+                --remainingBytes;
+                continue;
+            }
         }
 
         LOGV("found possible 1st frame at %lld (header = 0x%08x)", pos, header);
diff --git a/packages/SystemUI/res/anim/recent_appear.xml b/packages/SystemUI/res/anim/recent_appear.xml
index 4400d9d..20fe052 100644
--- a/packages/SystemUI/res/anim/recent_appear.xml
+++ b/packages/SystemUI/res/anim/recent_appear.xml
@@ -16,5 +16,5 @@
 
 <alpha xmlns:android="http://schemas.android.com/apk/res/android"
     android:fromAlpha="0.0" android:toAlpha="1.0"
-    android:duration="@android:integer/config_shortAnimTime"
+    android:duration="@android:integer/config_mediumAnimTime"
     />
diff --git a/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png b/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png
deleted file mode 100644
index 3b826a9..0000000
--- a/packages/SystemUI/res/drawable-hdpi/status_bar_ticker_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png b/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png
deleted file mode 100644
index 9999598..0000000
--- a/packages/SystemUI/res/drawable-mdpi/status_bar_ticker_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/status_bar_ticker_tile.png b/packages/SystemUI/res/drawable-xhdpi/status_bar_ticker_tile.png
deleted file mode 100644
index 6585ad6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/status_bar_ticker_tile.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/status_bar_ticker_background.xml b/packages/SystemUI/res/drawable/status_bar_ticker_background.xml
index 83524a6..7cb64c0 100644
--- a/packages/SystemUI/res/drawable/status_bar_ticker_background.xml
+++ b/packages/SystemUI/res/drawable/status_bar_ticker_background.xml
@@ -17,5 +17,5 @@
 <bitmap
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:tileMode="repeat"
-    android:src="@drawable/status_bar_ticker_tile"
+    android:src="@*android:drawable/notify_panel_notification_icon_bg"
     />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index b63afbe..b5274a3 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -28,14 +28,15 @@
 
     <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="55dp"
+        android:layout_height="52dp"
         android:paddingTop="3dp"
         android:paddingBottom="5dp"
         android:paddingRight="3dp"
         >
         <com.android.systemui.statusbar.policy.DateView android:id="@+id/date"
             android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
-            android:textColor="@android:color/holo_blue_bright"
+            android:textColor="@android:color/holo_blue_light"
+            android:textStyle="normal"
             android:layout_width="wrap_content"
             android:layout_height="match_parent"
             android:layout_alignParentLeft="true"
@@ -77,7 +78,7 @@
 
     <View
         android:layout_width="match_parent"
-        android:layout_height="3dp"
+        android:layout_height="2dp"
         android:background="@drawable/status_bar_hr"
         />
 
@@ -92,7 +93,7 @@
             android:textAppearance="@android:style/TextAppearance.Large"
             android:padding="8dp"
             android:layout_gravity="top"
-            android:gravity="center"
+            android:gravity="left"
             android:text="@string/status_bar_no_notifications_title"
             />
 
diff --git a/packages/SystemUI/res/layout/status_bar_icon.xml b/packages/SystemUI/res/layout/status_bar_icon.xml
index 21d606f..d2ebf9e 100644
--- a/packages/SystemUI/res/layout/status_bar_icon.xml
+++ b/packages/SystemUI/res/layout/status_bar_icon.xml
@@ -19,7 +19,7 @@
 -->
 
 <!-- The icons are a fixed size so an app can't mess everything up with bogus images -->
-<!-- TODO: the icons are hard coded to 25x25 pixels.  Their size should come froem a theme -->
+<!-- TODO: the icons are hard coded to 25x25 pixels.  Their size should come from a theme -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 
     android:layout_width="25dp" 
     android:layout_height="25dp"
@@ -43,4 +43,4 @@
         android:textStyle="bold"
         />
 
-</FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index ca584ab..d627dc4 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -32,13 +32,14 @@
         android:layout_alignParentRight="true"
         android:focusable="true"
         android:clickable="true"
+        android:background="@drawable/notification_item_background_color"
         />
 
     <View
         android:layout_width="match_parent"
-        android:layout_height="1dp"
+        android:layout_height="@dimen/notification_divider_height"
         android:layout_alignParentBottom="true"
-        android:background="@android:drawable/divider_horizontal_dark"
+        android:background="@drawable/status_bar_notification_row_background_color"
         />
 
 </RelativeLayout>
diff --git a/packages/SystemUI/res/values-hdpi/dimens.xml b/packages/SystemUI/res/values-hdpi/dimens.xml
index 741b75a..287e0d1 100644
--- a/packages/SystemUI/res/values-hdpi/dimens.xml
+++ b/packages/SystemUI/res/values-hdpi/dimens.xml
@@ -21,4 +21,12 @@
     <dimen name="recents_thumbnail_bg_padding_top">7px</dimen>
     <dimen name="recents_thumbnail_bg_padding_right">6px</dimen>
     <dimen name="recents_thumbnail_bg_padding_bottom">6px</dimen>
+
+    <!-- thickness (height) of each notification row, including any separators or padding -->
+    <!-- Note: this is 64dip + 1px divider = 97px. -->
+    <dimen name="notification_height">97px</dimen>
+
+    <!-- thickness (height) of dividers between each notification row; see math for
+         notification_height above -->
+    <dimen name="notification_divider_height">1px</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index fd5fe7a..5d14fa8 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -18,8 +18,9 @@
 -->
 <resources>
     <drawable name="notification_number_text_color">#ffffffff</drawable>
-    <drawable name="notification_item_background_color">#ff000000</drawable>
+    <drawable name="notification_item_background_color">#ff111111</drawable>
     <drawable name="ticker_background_color">#ff1d1d1d</drawable>
-    <drawable name="status_bar_background">#000000</drawable>
+    <drawable name="status_bar_background">#ff000000</drawable>
     <drawable name="status_bar_recents_background">#b3000000</drawable>
+    <drawable name="status_bar_notification_row_background_color">#ff000000</drawable>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ef9b8dd..6db5fc4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -60,4 +60,7 @@
 
     <!-- gap on either side of status bar notification icons -->
     <dimen name="status_bar_icon_padding">0dp</dimen>
+
+    <!-- thickness (height) of dividers between each notification row -->
+    <dimen name="notification_divider_height">1dp</dimen>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
index 2d327c4..9749a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java
@@ -29,6 +29,7 @@
     // should group this into a multi-property animation
     private static final int OPEN_DURATION = 136;
     private static final int CLOSE_DURATION = 250;
+    private static final int SCRIM_DURATION = 400;
     private static final String TAG = RecentsPanelView.TAG;
     private static final boolean DEBUG = RecentsPanelView.DEBUG;
 
@@ -71,12 +72,14 @@
         posAnim.setInterpolator(appearing
                 ? new android.view.animation.DecelerateInterpolator(2.5f)
                 : new android.view.animation.AccelerateInterpolator(2.5f));
+        posAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
 
         Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha",
                 mContentView.getAlpha(), appearing ? 1.0f : 0.0f);
         glowAnim.setInterpolator(appearing
                 ? new android.view.animation.AccelerateInterpolator(1.0f)
                 : new android.view.animation.DecelerateInterpolator(1.0f));
+        glowAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
 
         mContentAnim = new AnimatorSet();
         final Builder builder = mContentAnim.play(glowAnim).with(posAnim);
@@ -84,9 +87,9 @@
         if (background != null) {
             Animator bgAnim = ObjectAnimator.ofInt(background,
                 "alpha", appearing ? 0 : 255, appearing ? 255 : 0);
+            bgAnim.setDuration(appearing ? SCRIM_DURATION : CLOSE_DURATION);
             builder.with(bgAnim);
         }
-        mContentAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION);
         mContentAnim.addListener(this);
         if (mListener != null) {
             mContentAnim.addListener(mListener);
diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
index 8c03ef8..9cc2c29 100644
--- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java
@@ -39,7 +39,6 @@
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Handler;
@@ -497,7 +496,7 @@
         synchronized (ad) {
             ad.mLabel = label;
             ad.mIcon = icon;
-            ad.setThumbnail(thumbs.mainThumbnail);
+            ad.setThumbnail(thumbs != null ? thumbs.mainThumbnail : null);
         }
     }
 
@@ -591,7 +590,7 @@
                             ActivityDescription ad = descriptions.get(i);
                             loadActivityDescription(ad, i);
                             long now = SystemClock.uptimeMillis();
-                            nextTime += 200;
+                            nextTime += 150;
                             if (nextTime > now) {
                                 try {
                                     Thread.sleep(nextTime-now);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 76dec5e2..dd59667 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -181,7 +181,9 @@
     private InputMethodsPanel mInputMethodsPanel;
     private CompatModePanel mCompatModePanel;
 
-    int mSystemUiVisibility = 0;
+    private int mSystemUiVisibility = 0;
+    // used to notify status bar for suppressing notification LED
+    private boolean mPanelSlightlyVisible;
 
     public Context getContext() { return mContext; }
 
@@ -642,6 +644,7 @@
                                         editor.putBoolean(Prefs.DO_NOT_DISTURB_PREF, false);
                                         editor.apply();
                                         animateCollapse();
+                                        visibilityChanged(false);
                                     }
                                 });
                             }
@@ -740,6 +743,7 @@
                 case MSG_HIDE_CHROME:
                     if (DEBUG) Slog.d(TAG, "showing shadows (lights out)");
                     animateCollapse();
+                    visibilityChanged(false);
                     mBarContents.setVisibility(View.GONE);
                     mShadow.setVisibility(View.VISIBLE);
                     mSystemUiVisibility |= View.SYSTEM_UI_FLAG_LOW_PROFILE;
@@ -752,15 +756,6 @@
         }
     }
 
-    private void notifyLightsChanged(boolean shown) {
-        try {
-            Slog.d(TAG, "lights " + (shown?"on":"out"));
-            mWindowManager.statusBarVisibilityChanged(
-                    shown ? View.STATUS_BAR_VISIBLE : View.STATUS_BAR_HIDDEN);
-        } catch (RemoteException ex) {
-        }
-    }
-
     public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) {
         if (DEBUG) Slog.d(TAG, "addIcon(" + slot + ") -> " + icon);
     }
@@ -934,6 +929,7 @@
             if ((state & StatusBarManager.DISABLE_EXPAND) != 0) {
                 Slog.i(TAG, "DISABLE_EXPAND: yes");
                 animateCollapse();
+                visibilityChanged(false);
             }
         }
         if ((diff & StatusBarManager.DISABLE_NOTIFICATION_ICONS) != 0) {
@@ -1036,6 +1032,24 @@
         }
     }
 
+    /**
+     * The LEDs are turned o)ff when the notification panel is shown, even just a little bit.
+     * This was added last-minute and is inconsistent with the way the rest of the notifications
+     * are handled, because the notification isn't really cancelled.  The lights are just
+     * turned off.  If any other notifications happen, the lights will turn back on.  Steve says
+     * this is what he wants. (see bug 1131461)
+     */
+    void visibilityChanged(boolean visible) {
+        if (mPanelSlightlyVisible != visible) {
+            mPanelSlightlyVisible = visible;
+            try {
+                mBarService.onPanelRevealed();
+            } catch (RemoteException ex) {
+                // Won't fail unless the world has ended.
+            }
+        }
+    }
+
     private void notifyUiVisibilityChanged() {
         try {
             mWindowManager.statusBarVisibilityChanged(mSystemUiVisibility);
@@ -1305,6 +1319,7 @@
 
             // close the shade if it was open
             animateCollapse();
+            visibilityChanged(false);
 
             // If this click was on the intruder alert, hide that instead
 //            mHandler.sendEmptyMessage(MSG_HIDE_INTRUDER);
@@ -1383,6 +1398,7 @@
                         // require a little more oomph once we're already in peekaboo mode
                         if (mVT.getYVelocity() < -mNotificationFlingVelocity) {
                             animateExpand();
+                            visibilityChanged(true);
                             hilite(false);
                             mVT.recycle();
                             mVT = null;
@@ -1400,6 +1416,7 @@
                          // dragging off the bottom doesn't count
                          && (int)event.getY() < v.getBottom()) {
                             animateExpand();
+                            visibilityChanged(true);
                             v.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
                             v.playSoundEffect(SoundEffectConstants.CLICK);
                         }
@@ -1783,6 +1800,7 @@
             // system process is dead if we're here.
         }
         animateCollapse();
+        visibilityChanged(false);
     }
 
     public void userActivity() {
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index afa92f1..9629702 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -43,7 +43,7 @@
  *
  */
 class KeyguardStatusViewManager implements OnClickListener {
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
     private static final String TAG = "KeyguardStatusView";
 
     public static final int LOCK_ICON = 0; // R.drawable.ic_lock_idle_lock;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
index 431f8e0..5661116 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java
@@ -116,6 +116,7 @@
     private static final int KEYGUARD_DONE_AUTHENTICATING = 11;
     private static final int SET_HIDDEN = 12;
     private static final int KEYGUARD_TIMEOUT = 13;
+    private static final int REPORT_SHOW_DONE = 14;
 
     /**
      * The default amount of time we stay awake (used for all key input)
@@ -238,6 +239,8 @@
 
     private boolean mScreenOn = false;
 
+    private boolean mShowPending = false;
+
     // last known state of the cellular connection
     private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
 
@@ -306,7 +309,7 @@
         synchronized (this) {
             if (DEBUG) Log.d(TAG, "onSystemReady");
             mSystemReady = true;
-            doKeyguard();
+            doKeyguardLocked();
         }
     }
 
@@ -363,7 +366,7 @@
                 if (timeout <= 0) {
                     // Lock now
                     mSuppressNextLockSound = true;
-                    doKeyguard();
+                    doKeyguardLocked();
                 } else {
                     // Lock in the future
                     long when = SystemClock.elapsedRealtime() + timeout;
@@ -379,7 +382,19 @@
             } else if (why == WindowManagerPolicy.OFF_BECAUSE_OF_PROX_SENSOR) {
                 // Do not enable the keyguard if the prox sensor forced the screen off.
             } else {
-                doKeyguard();
+                if (!doKeyguardLocked() && why == WindowManagerPolicy.OFF_BECAUSE_OF_USER) {
+                    // The user has explicitly turned off the screen, causing it
+                    // to lock.  We want to block here until the keyguard window
+                    // has shown, so the power manager won't complete the screen
+                    // off flow until that point, so we know it won't turn *on*
+                    // the screen until this is done.
+                    while (mShowPending) {
+                        try {
+                            wait();
+                        } catch (InterruptedException e) {
+                        }
+                    }
+                }
             }
         }
     }
@@ -553,56 +568,58 @@
     }
 
     /**
-     * Enable the keyguard if the settings are appropriate.
+     * Enable the keyguard if the settings are appropriate.  Return true if all
+     * work that will happen is done; returns false if the caller can wait for
+     * the keyguard to be shown.
      */
-    private void doKeyguard() {
-        synchronized (this) {
-            // if another app is disabling us, don't show
-            if (!mExternallyEnabled) {
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
+    private boolean doKeyguardLocked() {
+        // if another app is disabling us, don't show
+        if (!mExternallyEnabled) {
+            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
 
-                // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
-                // for an occasional ugly flicker in this situation:
-                // 1) receive a call with the screen on (no keyguard) or make a call
-                // 2) screen times out
-                // 3) user hits key to turn screen back on
-                // instead, we reenable the keyguard when we know the screen is off and the call
-                // ends (see the broadcast receiver below)
-                // TODO: clean this up when we have better support at the window manager level
-                // for apps that wish to be on top of the keyguard
-                return;
-            }
-
-            // if the keyguard is already showing, don't bother
-            if (mKeyguardViewManager.isShowing()) {
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
-                return;
-            }
-
-            // if the setup wizard hasn't run yet, don't show
-            final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
-                    false);
-            final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
-            final IccCard.State state = mUpdateMonitor.getSimState();
-            final boolean lockedOrMissing = state.isPinLocked()
-                    || ((state == IccCard.State.ABSENT
-                            || state == IccCard.State.PERM_DISABLED)
-                            && requireSim);
-
-            if (!lockedOrMissing && !provisioned) {
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
-                        + " and the sim is not locked or missing");
-                return;
-            }
-
-            if (mLockPatternUtils.isLockScreenDisabled()) {
-                if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
-                return;
-            }
-
-            if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
-            showLocked();
+            // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
+            // for an occasional ugly flicker in this situation:
+            // 1) receive a call with the screen on (no keyguard) or make a call
+            // 2) screen times out
+            // 3) user hits key to turn screen back on
+            // instead, we reenable the keyguard when we know the screen is off and the call
+            // ends (see the broadcast receiver below)
+            // TODO: clean this up when we have better support at the window manager level
+            // for apps that wish to be on top of the keyguard
+            return true;
         }
+
+        // if the keyguard is already showing, don't bother
+        if (mKeyguardViewManager.isShowing()) {
+            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
+            return true;
+        }
+
+        // if the setup wizard hasn't run yet, don't show
+        final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",
+                false);
+        final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();
+        final IccCard.State state = mUpdateMonitor.getSimState();
+        final boolean lockedOrMissing = state.isPinLocked()
+                || ((state == IccCard.State.ABSENT
+                        || state == IccCard.State.PERM_DISABLED)
+                        && requireSim);
+
+        if (!lockedOrMissing && !provisioned) {
+            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+                    + " and the sim is not locked or missing");
+            return true;
+        }
+
+        if (mLockPatternUtils.isLockScreenDisabled()) {
+            if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");
+            return true;
+        }
+
+        if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
+        mShowPending = true;
+        showLocked();
+        return false;
     }
 
     /**
@@ -696,41 +713,49 @@
             case ABSENT:
                 // only force lock screen in case of missing sim if user hasn't
                 // gone through setup wizard
-                if (!mUpdateMonitor.isDeviceProvisioned()) {
-                    if (!isShowing()) {
-                        if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
-                                + " we need to show the keyguard since the "
-                                + "device isn't provisioned yet.");
-                        doKeyguard();
-                    } else {
-                        resetStateLocked();
+                synchronized (this) {
+                    if (!mUpdateMonitor.isDeviceProvisioned()) {
+                        if (!isShowing()) {
+                            if (DEBUG) Log.d(TAG, "ICC_ABSENT isn't showing,"
+                                    + " we need to show the keyguard since the "
+                                    + "device isn't provisioned yet.");
+                            doKeyguardLocked();
+                        } else {
+                            resetStateLocked();
+                        }
                     }
                 }
                 break;
             case PIN_REQUIRED:
             case PUK_REQUIRED:
-                if (!isShowing()) {
-                    if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't showing, we need "
-                            + "to show the keyguard so the user can enter their sim pin");
-                    doKeyguard();
-                } else {
-                    resetStateLocked();
+                synchronized (this) {
+                    if (!isShowing()) {
+                        if (DEBUG) Log.d(TAG, "INTENT_VALUE_ICC_LOCKED and keygaurd isn't showing, we need "
+                                + "to show the keyguard so the user can enter their sim pin");
+                        doKeyguardLocked();
+                    } else {
+                        resetStateLocked();
+                    }
                 }
                 break;
             case PERM_DISABLED:
-                if (!isShowing()) {
-                    if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
-                          + "keygaurd isn't showing.");
-                    doKeyguard();
-                } else {
-                    if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
-                          + "show permanently disabled message in lockscreen.");
-                    resetStateLocked();
+                synchronized (this) {
+                    if (!isShowing()) {
+                        if (DEBUG) Log.d(TAG, "PERM_DISABLED and "
+                              + "keygaurd isn't showing.");
+                        doKeyguardLocked();
+                    } else {
+                        if (DEBUG) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+                              + "show permanently disabled message in lockscreen.");
+                        resetStateLocked();
+                    }
                 }
                 break;
             case READY:
-                if (isShowing()) {
-                    resetStateLocked();
+                synchronized (this) {
+                    if (isShowing()) {
+                        resetStateLocked();
+                    }
                 }
                 break;
         }
@@ -751,27 +776,31 @@
                 if (DEBUG) Log.d(TAG, "received DELAYED_KEYGUARD_ACTION with seq = "
                         + sequence + ", mDelayedShowingSequence = " + mDelayedShowingSequence);
 
-                if (mDelayedShowingSequence == sequence) {
-                    // Don't play lockscreen SFX if the screen went off due to
-                    // timeout.
-                    mSuppressNextLockSound = true;
-
-                    doKeyguard();
+                synchronized (KeyguardViewMediator.this) {
+                    if (mDelayedShowingSequence == sequence) {
+                        // Don't play lockscreen SFX if the screen went off due to
+                        // timeout.
+                        mSuppressNextLockSound = true;
+    
+                        doKeyguardLocked();
+                    }
                 }
             } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
                 mPhoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
 
-                if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)  // call ending
-                        && !mScreenOn                           // screen off
-                        && mExternallyEnabled) {                // not disabled by any app
-
-                    // note: this is a way to gracefully reenable the keyguard when the call
-                    // ends and the screen is off without always reenabling the keyguard
-                    // each time the screen turns off while in call (and having an occasional ugly
-                    // flicker while turning back on the screen and disabling the keyguard again).
-                    if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
-                            + "keyguard is showing");
-                    doKeyguard();
+                synchronized (KeyguardViewMediator.this) {
+                    if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)  // call ending
+                            && !mScreenOn                           // screen off
+                            && mExternallyEnabled) {                // not disabled by any app
+    
+                        // note: this is a way to gracefully reenable the keyguard when the call
+                        // ends and the screen is off without always reenabling the keyguard
+                        // each time the screen turns off while in call (and having an occasional ugly
+                        // flicker while turning back on the screen and disabling the keyguard again).
+                        if (DEBUG) Log.d(TAG, "screen is off and call ended, let's make sure the "
+                                + "keyguard is showing");
+                        doKeyguardLocked();
+                    }
                 }
             }
         }
@@ -962,7 +991,15 @@
                     handleSetHidden(msg.arg1 != 0);
                     break;
                 case KEYGUARD_TIMEOUT:
-                    doKeyguard();
+                    synchronized (KeyguardViewMediator.this) {
+                        doKeyguardLocked();
+                    }
+                    break;
+                case REPORT_SHOW_DONE:
+                    synchronized (KeyguardViewMediator.this) {
+                        mShowPending = false;
+                        KeyguardViewMediator.this.notifyAll();
+                    }
                     break;
             }
         }
@@ -1062,8 +1099,6 @@
             if (DEBUG) Log.d(TAG, "handleShow");
             if (!mSystemReady) return;
 
-            playSounds(true);
-
             mKeyguardViewManager.show();
             mShowing = true;
             adjustUserActivityLocked();
@@ -1072,7 +1107,17 @@
                 ActivityManagerNative.getDefault().closeSystemDialogs("lock");
             } catch (RemoteException e) {
             }
+
+            // Do this at the end to not slow down display of the keyguard.
+            playSounds(true);
+
             mShowKeyguardWakeLock.release();
+
+            // We won't say the show is done yet because the view hierarchy
+            // still needs to do the traversal.  Posting this message allows
+            // us to hold off until that is done.
+            Message msg = mHandler.obtainMessage(REPORT_SHOW_DONE);
+            mHandler.sendMessage(msg);
         }
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1d5fbc0a..be129a8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -120,6 +120,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
 import android.view.WindowManagerImpl;
 import android.view.WindowManagerPolicy;
 import android.view.KeyCharacterMap.FallbackAction;
@@ -197,8 +198,9 @@
     // things in here CAN NOT take focus, but are shown on top of everything else.
     static final int SYSTEM_OVERLAY_LAYER = 20;
     static final int SECURE_SYSTEM_OVERLAY_LAYER = 21;
+    static final int BOOT_PROGRESS_LAYER = 22;
     // the (mouse) pointer layer
-    static final int POINTER_LAYER = 22;
+    static final int POINTER_LAYER = 23;
 
     static final int APPLICATION_MEDIA_SUBLAYER = -2;
     static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
@@ -1095,6 +1097,8 @@
             return POINTER_LAYER;
         case TYPE_NAVIGATION_BAR:
             return NAVIGATION_BAR_LAYER;
+        case TYPE_BOOT_PROGRESS:
+            return BOOT_PROGRESS_LAYER;
         }
         Log.e(TAG, "Unknown window type: " + type);
         return APPLICATION_LAYER;
@@ -2797,13 +2801,26 @@
     /** {@inheritDoc} */
     public void screenTurnedOff(int why) {
         EventLog.writeEvent(70000, 0);
-        mKeyguardMediator.onScreenTurnedOff(why);
         synchronized (mLock) {
             mScreenOn = false;
+        }
+        mKeyguardMediator.onScreenTurnedOff(why);
+        synchronized (mLock) {
             updateOrientationListenerLp();
             updateLockScreenTimeout();
             updateScreenSaverTimeoutLocked();
         }
+        try {
+            mWindowManager.waitForAllDrawn();
+        } catch (RemoteException e) {
+        }
+        // Wait for one frame to give surface flinger time to do its
+        // compositing.  Yes this is a hack, but I am really not up right now for
+        // implementing some mechanism to block until SF is done. :p
+        try {
+            Thread.sleep(20);
+        } catch (InterruptedException e) {
+        }
     }
 
     /** {@inheritDoc} */
@@ -3092,7 +3109,7 @@
                     mBootMsgDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                     mBootMsgDialog.setIndeterminate(true);
                     mBootMsgDialog.getWindow().setType(
-                            WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY);
+                            WindowManager.LayoutParams.TYPE_BOOT_PROGRESS);
                     mBootMsgDialog.getWindow().addFlags(
                             WindowManager.LayoutParams.FLAG_DIM_BEHIND
                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index f6ce44c..cf167ae 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -1831,8 +1831,8 @@
         const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
         bool resumeWithAppendedMotionSample) {
 #if DEBUG_DISPATCH_CYCLE
-    LOGD("channel '%s' ~ prepareDispatchCycle - flags=%d, "
-            "xOffset=%f, yOffset=%f, scaleFactor=%f"
+    LOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
+            "xOffset=%f, yOffset=%f, scaleFactor=%f, "
             "pointerIds=0x%x, "
             "resumeWithAppendedMotionSample=%s",
             connection->getInputChannelName(), inputTarget->flags,
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 40c85fc..bfcf8e0 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -1220,6 +1220,9 @@
     mBtnToolAirbrush = device->isKeyPressed(BTN_TOOL_AIRBRUSH);
     mBtnToolMouse = device->isKeyPressed(BTN_TOOL_MOUSE);
     mBtnToolLens = device->isKeyPressed(BTN_TOOL_LENS);
+    mBtnToolDoubleTap = device->isKeyPressed(BTN_TOOL_DOUBLETAP);
+    mBtnToolTripleTap = device->isKeyPressed(BTN_TOOL_TRIPLETAP);
+    mBtnToolQuadTap = device->isKeyPressed(BTN_TOOL_QUADTAP);
 }
 
 void TouchButtonAccumulator::clearButtons() {
@@ -1234,6 +1237,9 @@
     mBtnToolAirbrush = 0;
     mBtnToolMouse = 0;
     mBtnToolLens = 0;
+    mBtnToolDoubleTap = 0;
+    mBtnToolTripleTap = 0;
+    mBtnToolQuadTap = 0;
 }
 
 void TouchButtonAccumulator::process(const RawEvent* rawEvent) {
@@ -1272,6 +1278,15 @@
         case BTN_TOOL_LENS:
             mBtnToolLens = rawEvent->value;
             break;
+        case BTN_TOOL_DOUBLETAP:
+            mBtnToolDoubleTap = rawEvent->value;
+            break;
+        case BTN_TOOL_TRIPLETAP:
+            mBtnToolTripleTap = rawEvent->value;
+            break;
+        case BTN_TOOL_QUADTAP:
+            mBtnToolQuadTap = rawEvent->value;
+            break;
         }
     }
 }
@@ -1297,7 +1312,7 @@
     if (mBtnToolPen || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush) {
         return AMOTION_EVENT_TOOL_TYPE_STYLUS;
     }
-    if (mBtnToolFinger) {
+    if (mBtnToolFinger || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap) {
         return AMOTION_EVENT_TOOL_TYPE_FINGER;
     }
     return AMOTION_EVENT_TOOL_TYPE_UNKNOWN;
@@ -1306,7 +1321,8 @@
 bool TouchButtonAccumulator::isToolActive() const {
     return mBtnTouch || mBtnToolFinger || mBtnToolPen || mBtnToolRubber
             || mBtnToolBrush || mBtnToolPencil || mBtnToolAirbrush
-            || mBtnToolMouse || mBtnToolLens;
+            || mBtnToolMouse || mBtnToolLens
+            || mBtnToolDoubleTap || mBtnToolTripleTap || mBtnToolQuadTap;
 }
 
 bool TouchButtonAccumulator::isHovering() const {
@@ -2172,6 +2188,7 @@
     }
     nsecs_t downTime = mDownTime;
     bool buttonsChanged = currentButtonState != lastButtonState;
+    bool buttonsPressed = currentButtonState & ~lastButtonState;
 
     float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale;
     float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale;
@@ -2233,7 +2250,7 @@
     // the device in your pocket.
     // TODO: Use the input device configuration to control this behavior more finely.
     uint32_t policyFlags = 0;
-    if (getDevice()->isExternal()) {
+    if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) {
         policyFlags |= POLICY_FLAG_WAKE_DROPPED;
     }
 
@@ -3329,9 +3346,12 @@
 
         // Handle policy on initial down or hover events.
         uint32_t policyFlags = 0;
-        if (mLastRawPointerData.pointerCount == 0 && mCurrentRawPointerData.pointerCount != 0) {
+        bool initialDown = mLastRawPointerData.pointerCount == 0
+                && mCurrentRawPointerData.pointerCount != 0;
+        bool buttonsPressed = mCurrentButtonState & ~mLastButtonState;
+        if (initialDown || buttonsPressed) {
+            // If this is a touch screen, hide the pointer on an initial down.
             if (mDeviceMode == DEVICE_MODE_DIRECT) {
-                // If this is a touch screen, hide the pointer on an initial down.
                 getContext()->fadePointer();
             }
 
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 76d77f1..bad96df 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -596,6 +596,9 @@
     bool mBtnToolAirbrush;
     bool mBtnToolMouse;
     bool mBtnToolLens;
+    bool mBtnToolDoubleTap;
+    bool mBtnToolTripleTap;
+    bool mBtnToolQuadTap;
 
     void clearButtons();
 };
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 4796958..32f948b 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -3377,8 +3377,32 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
 
-    // finger
+    // double-tap
     processKey(mapper, BTN_TOOL_LENS, 0);
+    processKey(mapper, BTN_TOOL_DOUBLETAP, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // triple-tap
+    processKey(mapper, BTN_TOOL_DOUBLETAP, 0);
+    processKey(mapper, BTN_TOOL_TRIPLETAP, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // quad-tap
+    processKey(mapper, BTN_TOOL_TRIPLETAP, 0);
+    processKey(mapper, BTN_TOOL_QUADTAP, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // finger
+    processKey(mapper, BTN_TOOL_QUADTAP, 0);
     processKey(mapper, BTN_TOOL_FINGER, 1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
@@ -4766,8 +4790,32 @@
     ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
     ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_MOUSE, motionArgs.pointerProperties[0].toolType);
 
-    // finger
+    // double-tap
     processKey(mapper, BTN_TOOL_LENS, 0);
+    processKey(mapper, BTN_TOOL_DOUBLETAP, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // triple-tap
+    processKey(mapper, BTN_TOOL_DOUBLETAP, 0);
+    processKey(mapper, BTN_TOOL_TRIPLETAP, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // quad-tap
+    processKey(mapper, BTN_TOOL_TRIPLETAP, 0);
+    processKey(mapper, BTN_TOOL_QUADTAP, 1);
+    processSync(mapper);
+    ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
+    ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, motionArgs.action);
+    ASSERT_EQ(AMOTION_EVENT_TOOL_TYPE_FINGER, motionArgs.pointerProperties[0].toolType);
+
+    // finger
+    processKey(mapper, BTN_TOOL_QUADTAP, 0);
     processKey(mapper, BTN_TOOL_FINGER, 1);
     processSync(mapper);
     ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs));
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 1341dd4..bfca851 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -72,7 +72,6 @@
 import com.android.internal.telephony.Phone;
 import com.android.server.connectivity.Tethering;
 import com.android.server.connectivity.Vpn;
-
 import com.google.android.collect.Lists;
 import com.google.android.collect.Sets;
 
@@ -89,7 +88,6 @@
 import java.util.GregorianCalendar;
 import java.util.HashSet;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * @hide
@@ -251,6 +249,12 @@
     private static final int EVENT_SEND_STICKY_BROADCAST_INTENT =
             MAX_NETWORK_STATE_TRACKER_EVENT + 12;
 
+    /**
+     * Used internally to
+     * {@link NetworkStateTracker#setPolicyDataEnable(boolean)}.
+     */
+    private static final int EVENT_SET_POLICY_DATA_ENABLE = MAX_NETWORK_STATE_TRACKER_EVENT + 13;
+
     private Handler mHandler;
 
     // list of DeathRecipients used to make sure features are turned off when
@@ -1282,7 +1286,25 @@
             if (VDBG) {
                 log(mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
             }
-            mNetTrackers[ConnectivityManager.TYPE_MOBILE].setDataEnable(enabled);
+            mNetTrackers[ConnectivityManager.TYPE_MOBILE].setUserDataEnable(enabled);
+        }
+    }
+
+    @Override
+    public void setPolicyDataEnable(int networkType, boolean enabled) {
+        // only someone like NPMS should only be calling us
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        mHandler.sendMessage(mHandler.obtainMessage(
+                EVENT_SET_POLICY_DATA_ENABLE, networkType, (enabled ? ENABLED : DISABLED)));
+    }
+
+    private void handleSetPolicyDataEnable(int networkType, boolean enabled) {
+        if (isNetworkTypeValid(networkType)) {
+            final NetworkStateTracker tracker = mNetTrackers[networkType];
+            if (tracker != null) {
+                tracker.setPolicyDataEnable(enabled);
+            }
         }
     }
 
@@ -2263,6 +2285,11 @@
                     sendStickyBroadcast(intent);
                     break;
                 }
+                case EVENT_SET_POLICY_DATA_ENABLE: {
+                    final int networkType = msg.arg1;
+                    final boolean enabled = msg.arg2 == ENABLED;
+                    handleSetPolicyDataEnable(networkType, enabled);
+                }
             }
         }
     }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 6edb132..7a3a344 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -101,13 +101,8 @@
     private Vibrator mVibrator = new Vibrator();
 
     // for enabling and disabling notification pulse behavior
-    private boolean mScreenOn = true;
     private boolean mInCall = false;
     private boolean mNotificationPulseEnabled;
-    // This is true if we have received a new notification while the screen is off
-    // (that is, if mLedNotification was set while the screen was off)
-    // This is reset to false when the screen is turned on.
-    private boolean mPendingPulseNotification;
 
     private final ArrayList<NotificationRecord> mNotificationList =
             new ArrayList<NotificationRecord>();
@@ -349,12 +344,6 @@
                         cancelAllNotificationsInt(pkgName, 0, 0, !queryRestart);
                     }
                 }
-            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
-                mScreenOn = true;
-                updateNotificationPulse();
-            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
-                mScreenOn = false;
-                updateNotificationPulse();
             } else if (action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED)) {
                 mInCall = (intent.getStringExtra(TelephonyManager.EXTRA_STATE).equals(TelephonyManager.EXTRA_STATE_OFFHOOK));
                 updateNotificationPulse();
@@ -1059,11 +1048,6 @@
     // lock on mNotificationList
     private void updateLightsLocked()
     {
-        // clear pending pulse notification if screen is on
-        if (mScreenOn || mLedNotification == null) {
-            mPendingPulseNotification = false;
-        }
-
         // handle notification lights
         if (mLedNotification == null) {
             // get next notification, if any
@@ -1071,14 +1055,10 @@
             if (n > 0) {
                 mLedNotification = mLights.get(n-1);
             }
-            if (mLedNotification != null && !mScreenOn) {
-                mPendingPulseNotification = true;
-            }
         }
 
-        // we only flash if screen is off and persistent pulsing is enabled
-        // and we are not currently in a call
-        if (!mPendingPulseNotification || mScreenOn || mInCall) {
+        // Don't flash while we are in a call
+        if (mLedNotification == null || mInCall) {
             mNotificationLight.turnOff();
         } else {
             int ledARGB = mLedNotification.notification.ledARGB;
diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java
index d80a2cd..cbd986f 100644
--- a/services/java/com/android/server/PowerManagerService.java
+++ b/services/java/com/android/server/PowerManagerService.java
@@ -161,6 +161,7 @@
     private int mStayOnConditions = 0;
     private final int[] mBroadcastQueue = new int[] { -1, -1, -1 };
     private final int[] mBroadcastWhy = new int[3];
+    private boolean mBroadcastingScreenOff = false;
     private int mPartialCount = 0;
     private int mPowerState;
     // mScreenOffReason can be WindowManagerPolicy.OFF_BECAUSE_OF_USER,
@@ -1342,6 +1343,10 @@
             mBroadcastWakeLock.release();
         }
 
+        // The broadcast queue has changed; make sure the screen is on if it
+        // is now possible for it to be.
+        updateNativePowerStateLocked();
+
         // Now send the message.
         if (index >= 0) {
             // Acquire the broadcast wake lock before changing the power
@@ -1370,6 +1375,9 @@
                         mBroadcastWhy[i] = mBroadcastWhy[i+1];
                     }
                     policy = getPolicyLocked();
+                    if (value == 0) {
+                        mBroadcastingScreenOff = true;
+                    }
                 }
                 if (value == 1) {
                     mScreenOnStart = SystemClock.uptimeMillis();
@@ -1412,6 +1420,8 @@
                         synchronized (mLocks) {
                             EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3,
                                     mBroadcastWakeLock.mCount);
+                            mBroadcastingScreenOff = false;
+                            updateNativePowerStateLocked();
                             mBroadcastWakeLock.release();
                         }
                     }
@@ -1442,6 +1452,10 @@
             synchronized (mLocks) {
                 EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_DONE, 0,
                         SystemClock.uptimeMillis() - mScreenOffStart, mBroadcastWakeLock.mCount);
+                synchronized (mLocks) {
+                    mBroadcastingScreenOff = false;
+                    updateNativePowerStateLocked();
+                }
                 mBroadcastWakeLock.release();
             }
         }
@@ -1768,6 +1782,22 @@
     }
     
     private void updateNativePowerStateLocked() {
+        if ((mPowerState & SCREEN_ON_BIT) != 0) {
+            // Don't turn screen on if we are currently reporting a screen off.
+            // This is to avoid letting the screen go on before things like the
+            // lock screen have been displayed due to it going off.
+            if (mBroadcastingScreenOff) {
+                // Currently broadcasting that the screen is off.  Don't
+                // allow screen to go on until that is done.
+                return;
+            }
+            for (int i=0; i<mBroadcastQueue.length; i++) {
+                if (mBroadcastQueue[i] == 0) {
+                    // A screen off is currently enqueued.
+                    return;
+                }
+            }
+        }
         nativeSetPowerState(
                 (mPowerState & SCREEN_ON_BIT) != 0,
                 (mPowerState & SCREEN_BRIGHT) == SCREEN_BRIGHT);
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index 90824a6..18ddabd 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -41,6 +41,7 @@
 import android.text.TextUtils;
 import android.util.Slog;
 import android.view.textservice.SpellCheckerInfo;
+import android.view.textservice.SpellCheckerSubtype;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -174,7 +175,7 @@
         synchronized (mSpellCheckerMap) {
             String curSpellCheckerId =
                     Settings.Secure.getString(mContext.getContentResolver(),
-                            Settings.Secure.SPELL_CHECKER_SERVICE);
+                            Settings.Secure.SELECTED_SPELL_CHECKER);
             if (DBG) {
                 Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId);
             }
@@ -185,6 +186,35 @@
         }
     }
 
+    // TODO: Save SpellCheckerSubtype by supported languages.
+    @Override
+    public SpellCheckerSubtype getCurrentSpellCheckerSubtype(String locale) {
+        synchronized (mSpellCheckerMap) {
+            final String subtypeHashCodeStr =
+                    Settings.Secure.getString(mContext.getContentResolver(),
+                            Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE);
+            if (DBG) {
+                Slog.w(TAG, "getCurrentSpellChecker: " + subtypeHashCodeStr);
+            }
+            final SpellCheckerInfo sci = getCurrentSpellChecker(null);
+            if (sci.getSubtypeCount() == 0) {
+                return null;
+            }
+            if (TextUtils.isEmpty(subtypeHashCodeStr)) {
+                // Return the first Subtype if there is no settings for the current subtype.
+                return sci.getSubtypeAt(0);
+            }
+            final int hashCode = Integer.valueOf(subtypeHashCodeStr);
+            for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+                final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
+                if (scs.hashCode() == hashCode) {
+                    return scs;
+                }
+            }
+            return sci.getSubtypeAt(0);
+        }
+    }
+
     @Override
     public void getSpellCheckerService(String sciId, String locale,
             ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
@@ -301,7 +331,7 @@
     }
 
     @Override
-    public void setCurrentSpellChecker(String sciId) {
+    public void setCurrentSpellChecker(String locale, String sciId) {
         synchronized(mSpellCheckerMap) {
             if (mContext.checkCallingOrSelfPermission(
                     android.Manifest.permission.WRITE_SECURE_SETTINGS)
@@ -314,6 +344,20 @@
         }
     }
 
+    @Override
+    public void setCurrentSpellCheckerSubtype(String locale, int hashCode) {
+        synchronized(mSpellCheckerMap) {
+            if (mContext.checkCallingOrSelfPermission(
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException(
+                        "Requires permission "
+                        + android.Manifest.permission.WRITE_SECURE_SETTINGS);
+            }
+            setCurrentSpellCheckerLocked(hashCode);
+        }
+    }
+
     private void setCurrentSpellCheckerLocked(String sciId) {
         if (DBG) {
             Slog.w(TAG, "setCurrentSpellChecker: " + sciId);
@@ -322,7 +366,32 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             Settings.Secure.putString(mContext.getContentResolver(),
-                    Settings.Secure.SPELL_CHECKER_SERVICE, sciId);
+                    Settings.Secure.SELECTED_SPELL_CHECKER, sciId);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    private void setCurrentSpellCheckerLocked(int hashCode) {
+        if (DBG) {
+            Slog.w(TAG, "setCurrentSpellCheckerSubtype: " + hashCode);
+        }
+        final SpellCheckerInfo sci = getCurrentSpellChecker(null);
+        if (sci == null) return;
+        boolean found = false;
+        for (int i = 0; i < sci.getSubtypeCount(); ++i) {
+            if(sci.getSubtypeAt(i).hashCode() == hashCode) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            return;
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode));
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index b817598..c935733 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -6564,7 +6564,7 @@
                             i--;
                         }
                     }
-                    
+
                     for (int i=0; i<ris.size(); i++) {
                         ActivityInfo ai = ris.get(i).activityInfo;
                         ComponentName comp = new ComponentName(ai.packageName, ai.name);
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 6f0779f..4ad0f45 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -3163,7 +3163,7 @@
 
                 //Slog.i(TAG, "IDLE: mBooted=" + mBooted + ", fromTimeout=" + fromTimeout);
                 if (mMainStack) {
-                    if (!mService.mBooted && !fromTimeout) {
+                    if (!mService.mBooted) {
                         mService.mBooted = true;
                         enableScreen = true;
                     }
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index e61a7f4..87129ea 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -116,6 +116,8 @@
         if (!askedCompatMode) {
             pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
         }
+        pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail);
+                pw.print(" lastDescription="); pw.println(lastDescription);
         pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime);
                 pw.print(" (inactive for ");
                 pw.print((getInactiveDuration()/1000)); pw.println("s)");
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 6b65e07..55e0678 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -388,6 +388,7 @@
         private final VpnConfig mConfig;
         private final String[] mDaemons;
         private final String[][] mArguments;
+        private final LocalSocket[] mSockets;
         private final String mOuterInterface;
         private final LegacyVpnInfo mInfo;
 
@@ -398,6 +399,7 @@
             mConfig = config;
             mDaemons = new String[] {"racoon", "mtpd"};
             mArguments = new String[][] {racoon, mtpd};
+            mSockets = new LocalSocket[mDaemons.length];
             mInfo = new LegacyVpnInfo();
 
             // This is the interface which VPN is running on.
@@ -416,10 +418,14 @@
         }
 
         public void exit() {
-            // We assume that everything is reset after the daemons die.
+            // We assume that everything is reset after stopping the daemons.
             interrupt();
-            for (String daemon : mDaemons) {
-                SystemProperties.set("ctl.stop", daemon);
+            for (LocalSocket socket : mSockets) {
+                try {
+                    socket.close();
+                } catch (Exception e) {
+                    // ignore
+                }
             }
         }
 
@@ -462,15 +468,10 @@
                 checkpoint(false);
                 mInfo.state = LegacyVpnInfo.STATE_INITIALIZING;
 
-                // First stop the daemons.
-                for (String daemon : mDaemons) {
-                    SystemProperties.set("ctl.stop", daemon);
-                }
-
                 // Wait for the daemons to stop.
                 for (String daemon : mDaemons) {
                     String key = "init.svc." + daemon;
-                    while (!"stopped".equals(SystemProperties.get(key))) {
+                    while (!"stopped".equals(SystemProperties.get(key, "stopped"))) {
                         checkpoint(true);
                     }
                 }
@@ -511,27 +512,27 @@
                     }
 
                     // Create the control socket.
-                    LocalSocket socket = new LocalSocket();
+                    mSockets[i] = new LocalSocket();
                     LocalSocketAddress address = new LocalSocketAddress(
                             daemon, LocalSocketAddress.Namespace.RESERVED);
 
                     // Wait for the socket to connect.
                     while (true) {
                         try {
-                            socket.connect(address);
+                            mSockets[i].connect(address);
                             break;
                         } catch (Exception e) {
                             // ignore
                         }
                         checkpoint(true);
                     }
-                    socket.setSoTimeout(500);
+                    mSockets[i].setSoTimeout(500);
 
                     // Send over the arguments.
-                    OutputStream out = socket.getOutputStream();
+                    OutputStream out = mSockets[i].getOutputStream();
                     for (String argument : arguments) {
                         byte[] bytes = argument.getBytes(Charsets.UTF_8);
-                        if (bytes.length > 0xFFFF) {
+                        if (bytes.length >= 0xFFFF) {
                             throw new IllegalArgumentException("Argument is too large");
                         }
                         out.write(bytes.length >> 8);
@@ -539,11 +540,12 @@
                         out.write(bytes);
                         checkpoint(false);
                     }
+                    out.write(0xFF);
+                    out.write(0xFF);
                     out.flush();
-                    socket.shutdownOutput();
 
                     // Wait for End-of-File.
-                    InputStream in = socket.getInputStream();
+                    InputStream in = mSockets[i].getInputStream();
                     while (true) {
                         try {
                             if (in.read() == -1) {
@@ -554,7 +556,6 @@
                         }
                         checkpoint(true);
                     }
-                    socket.close();
                 }
 
                 // Wait for the daemons to create the new state.
@@ -631,6 +632,13 @@
                 Log.i(TAG, "Aborting", e);
                 exit();
             } finally {
+                // Kill the daemons if they fail to stop.
+                if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) {
+                    for (String daemon : mDaemons) {
+                        SystemProperties.set("ctl.stop", daemon);
+                    }
+                }
+
                 // Do not leave an unstable state.
                 if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING ||
                         mInfo.state == LegacyVpnInfo.STATE_CONNECTING) {
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 14d9665..84880f9 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -27,7 +27,10 @@
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.EXTRA_UID;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.SNOOZE_NEVER;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -40,8 +43,11 @@
 import static android.net.NetworkPolicyManager.dumpPolicy;
 import static android.net.NetworkPolicyManager.dumpRules;
 import static android.net.NetworkPolicyManager.isUidValidForPolicy;
+import static android.net.NetworkTemplate.MATCH_ETHERNET;
 import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER;
 import static android.net.NetworkTemplate.MATCH_MOBILE_4G;
+import static android.net.NetworkTemplate.MATCH_MOBILE_ALL;
+import static android.net.NetworkTemplate.MATCH_WIFI;
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static com.android.internal.util.Preconditions.checkNotNull;
@@ -104,6 +110,7 @@
 import com.android.internal.R;
 import com.android.internal.os.AtomicFile;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.Objects;
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 import com.google.android.collect.Sets;
@@ -129,8 +136,8 @@
 import libcore.io.IoUtils;
 
 /**
- * Service that maintains low-level network policy rules and collects usage
- * statistics to drive those rules.
+ * Service that maintains low-level network policy rules, using
+ * {@link NetworkStatsService} statistics to drive those rules.
  * <p>
  * Derives active rules by combining a given policy with other system status,
  * and delivers to listeners, such as {@link ConnectivityManager}, for
@@ -195,6 +202,8 @@
     private volatile boolean mScreenOn;
     private volatile boolean mRestrictBackground;
 
+    private final boolean mSuppressDefaultPolicy;
+
     /** Defined network policies. */
     private HashMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = Maps.newHashMap();
     /** Currently active network rules for ifaces. */
@@ -210,6 +219,11 @@
     /** Set of over-limit templates that have been notified. */
     private HashSet<NetworkTemplate> mOverLimitNotified = Sets.newHashSet();
 
+    /** Set of currently active {@link Notification} tags. */
+    private HashSet<String> mActiveNotifs = Sets.newHashSet();
+    /** Current values from {@link #setPolicyDataEnable(int, boolean)}. */
+    private SparseBooleanArray mActiveNetworkEnabled = new SparseBooleanArray();
+
     /** Foreground at both UID and PID granularity. */
     private SparseBooleanArray mUidForeground = new SparseBooleanArray();
     private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
@@ -232,7 +246,7 @@
             IPowerManager powerManager, INetworkStatsService networkStats,
             INetworkManagementService networkManagement) {
         this(context, activityManager, powerManager, networkStats, networkManagement,
-                NtpTrustedTime.getInstance(context), getSystemDir());
+                NtpTrustedTime.getInstance(context), getSystemDir(), false);
     }
 
     private static File getSystemDir() {
@@ -241,8 +255,8 @@
 
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             IPowerManager powerManager, INetworkStatsService networkStats,
-            INetworkManagementService networkManagement,
-            TrustedTime time, File systemDir) {
+            INetworkManagementService networkManagement, TrustedTime time, File systemDir,
+            boolean suppressDefaultPolicy) {
         mContext = checkNotNull(context, "missing context");
         mActivityManager = checkNotNull(activityManager, "missing activityManager");
         mPowerManager = checkNotNull(powerManager, "missing powerManager");
@@ -254,6 +268,8 @@
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
 
+        mSuppressDefaultPolicy = suppressDefaultPolicy;
+
         mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
     }
 
@@ -408,6 +424,7 @@
             // READ_NETWORK_USAGE_HISTORY permission above.
 
             synchronized (mRulesLock) {
+                updateNetworkEnabledLocked();
                 updateNotificationsLocked();
             }
         }
@@ -446,6 +463,7 @@
                         Slog.w(TAG, "problem updating network stats");
                     }
 
+                    updateNetworkEnabledLocked();
                     updateNotificationsLocked();
                 }
             }
@@ -459,74 +477,70 @@
     private void updateNotificationsLocked() {
         if (LOGV) Slog.v(TAG, "updateNotificationsLocked()");
 
-        // try refreshing time source when stale
-        if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
-            mTime.forceRefresh();
-        }
-
-        final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
-                : System.currentTimeMillis();
+        // keep track of previously active notifications
+        final HashSet<String> beforeNotifs = Sets.newHashSet();
+        beforeNotifs.addAll(mActiveNotifs);
+        mActiveNotifs.clear();
 
         // TODO: when switching to kernel notifications, compute next future
         // cycle boundary to recompute notifications.
 
         // examine stats for each active policy
-        for (NetworkPolicy policy : mNetworkRules.keySet()) {
+        final long currentTime = currentTimeMillis(true);
+        for (NetworkPolicy policy : mNetworkPolicy.values()) {
+            // ignore policies that aren't relevant to user
+            if (!isTemplateRelevant(policy.template)) continue;
+
             final long start = computeLastCycleBoundary(currentTime, policy);
             final long end = currentTime;
 
-            final long totalBytes;
-            try {
-                final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
-                        policy.template, start, end);
-                final NetworkStats.Entry entry = stats.getValues(0, null);
-                totalBytes = entry.rxBytes + entry.txBytes;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "problem reading summary for template " + policy.template);
-                continue;
-            }
+            final long totalBytes = getTotalBytes(policy.template, start, end);
+            if (totalBytes == UNKNOWN_BYTES) continue;
 
             if (policy.limitBytes != LIMIT_DISABLED && totalBytes >= policy.limitBytes) {
-                cancelNotification(policy, TYPE_WARNING);
-
                 if (policy.lastSnooze >= start) {
-                    cancelNotification(policy, TYPE_LIMIT);
                     enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
                 } else {
-                    cancelNotification(policy, TYPE_LIMIT_SNOOZED);
                     enqueueNotification(policy, TYPE_LIMIT, totalBytes);
                     notifyOverLimitLocked(policy.template);
                 }
 
             } else {
-                cancelNotification(policy, TYPE_LIMIT);
-                cancelNotification(policy, TYPE_LIMIT_SNOOZED);
                 notifyUnderLimitLocked(policy.template);
 
                 if (policy.warningBytes != WARNING_DISABLED && totalBytes >= policy.warningBytes) {
                     enqueueNotification(policy, TYPE_WARNING, totalBytes);
-                } else {
-                    cancelNotification(policy, TYPE_WARNING);
                 }
             }
         }
 
-        // clear notifications for non-active policies
-        for (NetworkPolicy policy : mNetworkPolicy.values()) {
-            if (!mNetworkRules.containsKey(policy)) {
-                cancelNotification(policy, TYPE_WARNING);
-                cancelNotification(policy, TYPE_LIMIT);
-                cancelNotification(policy, TYPE_LIMIT_SNOOZED);
-                notifyUnderLimitLocked(policy.template);
-            }
-        }
-
         // ongoing notification when restricting background data
         if (mRestrictBackground) {
             enqueueRestrictedNotification(TAG_ALLOW_BACKGROUND);
-        } else {
-            cancelNotification(TAG_ALLOW_BACKGROUND);
         }
+
+        // cancel stale notifications that we didn't renew above
+        for (String tag : beforeNotifs) {
+            if (!mActiveNotifs.contains(tag)) {
+                cancelNotification(tag);
+            }
+        }
+    }
+
+    /**
+     * Test if given {@link NetworkTemplate} is relevant to user based on
+     * current device state, such as when {@link #getActiveSubscriberId()}
+     * matches. This is regardless of data connection status.
+     */
+    private boolean isTemplateRelevant(NetworkTemplate template) {
+        switch (template.getMatchRule()) {
+            case MATCH_MOBILE_3G_LOWER:
+            case MATCH_MOBILE_4G:
+            case MATCH_MOBILE_ALL:
+                // mobile templates are relevant when subscriberid is active
+                return Objects.equal(getActiveSubscriberId(), template.getSubscriberId());
+        }
+        return true;
     }
 
     /**
@@ -590,9 +604,15 @@
                     case MATCH_MOBILE_4G:
                         title = res.getText(R.string.data_usage_4g_limit_title);
                         break;
-                    default:
+                    case MATCH_MOBILE_ALL:
                         title = res.getText(R.string.data_usage_mobile_limit_title);
                         break;
+                    case MATCH_WIFI:
+                        title = res.getText(R.string.data_usage_wifi_limit_title);
+                        break;
+                    default:
+                        title = null;
+                        break;
                 }
 
                 builder.setSmallIcon(com.android.internal.R.drawable.ic_menu_block);
@@ -618,9 +638,15 @@
                     case MATCH_MOBILE_4G:
                         title = res.getText(R.string.data_usage_4g_limit_snoozed_title);
                         break;
-                    default:
+                    case MATCH_MOBILE_ALL:
                         title = res.getText(R.string.data_usage_mobile_limit_snoozed_title);
                         break;
+                    case MATCH_WIFI:
+                        title = res.getText(R.string.data_usage_wifi_limit_snoozed_title);
+                        break;
+                    default:
+                        title = null;
+                        break;
                 }
 
                 builder.setSmallIcon(R.drawable.ic_menu_info_details);
@@ -641,6 +667,7 @@
             final int[] idReceived = new int[1];
             mNotifManager.enqueueNotificationWithTag(
                     packageName, tag, 0x0, builder.getNotification(), idReceived);
+            mActiveNotifs.add(tag);
         } catch (RemoteException e) {
             Slog.w(TAG, "problem during enqueueNotification: " + e);
         }
@@ -674,19 +701,12 @@
             final int[] idReceived = new int[1];
             mNotifManager.enqueueNotificationWithTag(packageName, tag,
                     0x0, builder.getNotification(), idReceived);
+            mActiveNotifs.add(tag);
         } catch (RemoteException e) {
             Slog.w(TAG, "problem during enqueueNotification: " + e);
         }
     }
 
-    /**
-     * Cancel any notification for combined {@link NetworkPolicy} and specific
-     * type, like {@link #TYPE_LIMIT}.
-     */
-    private void cancelNotification(NetworkPolicy policy, int type) {
-        cancelNotification(buildNotificationTag(policy, type));
-    }
-
     private void cancelNotification(String tag) {
         // TODO: move to NotificationManager once we can mock it
         try {
@@ -709,6 +729,7 @@
             // permission above.
             synchronized (mRulesLock) {
                 ensureActiveMobilePolicyLocked();
+                updateNetworkEnabledLocked();
                 updateNetworkRulesLocked();
                 updateNotificationsLocked();
             }
@@ -716,6 +737,65 @@
     };
 
     /**
+     * Proactively control network data connections when they exceed
+     * {@link NetworkPolicy#limitBytes}.
+     */
+    private void updateNetworkEnabledLocked() {
+        if (LOGV) Slog.v(TAG, "updateNetworkEnabledLocked()");
+
+        // TODO: reset any policy-disabled networks when any policy is removed
+        // completely, which is currently rare case.
+
+        final long currentTime = currentTimeMillis(true);
+        for (NetworkPolicy policy : mNetworkPolicy.values()) {
+            // shortcut when policy has no limit
+            if (policy.limitBytes == LIMIT_DISABLED) {
+                setNetworkTemplateEnabled(policy.template, true);
+                continue;
+            }
+
+            final long start = computeLastCycleBoundary(currentTime, policy);
+            final long end = currentTime;
+
+            final long totalBytes = getTotalBytes(policy.template, start, end);
+            if (totalBytes == UNKNOWN_BYTES) continue;
+
+            // disable data connection when over limit and not snoozed
+            final boolean overLimit = policy.limitBytes != LIMIT_DISABLED
+                    && totalBytes > policy.limitBytes && policy.lastSnooze < start;
+            setNetworkTemplateEnabled(policy.template, !overLimit);
+        }
+    }
+
+    /**
+     * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)}
+     * for the given {@link NetworkTemplate}.
+     */
+    private void setNetworkTemplateEnabled(NetworkTemplate template, boolean enabled) {
+        if (LOGD) Slog.d(TAG, "setting template=" + template + " enabled=" + enabled);
+        switch (template.getMatchRule()) {
+            case MATCH_MOBILE_3G_LOWER:
+            case MATCH_MOBILE_4G:
+            case MATCH_MOBILE_ALL:
+                // TODO: offer more granular control over radio states once
+                // 4965893 is available.
+                if (Objects.equal(getActiveSubscriberId(), template.getSubscriberId())) {
+                    setPolicyDataEnable(TYPE_MOBILE, enabled);
+                    setPolicyDataEnable(TYPE_WIMAX, enabled);
+                }
+                break;
+            case MATCH_WIFI:
+                setPolicyDataEnable(TYPE_WIFI, enabled);
+                break;
+            case MATCH_ETHERNET:
+                setPolicyDataEnable(TYPE_ETHERNET, enabled);
+                break;
+            default:
+                throw new IllegalArgumentException("unexpected template");
+        }
+    }
+
+    /**
      * Examine all connected {@link NetworkState}, looking for
      * {@link NetworkPolicy} that need to be enforced. When matches found, set
      * remaining quota based on usage cycle and historical stats.
@@ -763,34 +843,19 @@
             }
         }
 
-        // try refreshing time source when stale
-        if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
-            mTime.forceRefresh();
-        }
-
-        final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
-                : System.currentTimeMillis();
-
         final HashSet<String> newMeteredIfaces = Sets.newHashSet();
 
         // apply each policy that we found ifaces for; compute remaining data
         // based on current cycle and historical stats, and push to kernel.
+        final long currentTime = currentTimeMillis(true);
         for (NetworkPolicy policy : mNetworkRules.keySet()) {
             final String[] ifaces = mNetworkRules.get(policy);
 
             final long start = computeLastCycleBoundary(currentTime, policy);
             final long end = currentTime;
 
-            final NetworkStats stats;
-            final long total;
-            try {
-                stats = mNetworkStats.getSummaryForNetwork(policy.template, start, end);
-                final NetworkStats.Entry entry = stats.getValues(0, null);
-                total = entry.rxBytes + entry.txBytes;
-            } catch (RemoteException e) {
-                Slog.w(TAG, "problem reading summary for template " + policy.template);
-                continue;
-            }
+            final long totalBytes = getTotalBytes(policy.template, start, end);
+            if (totalBytes == UNKNOWN_BYTES) continue;
 
             if (LOGD) {
                 Slog.d(TAG, "applying policy " + policy.toString() + " to ifaces "
@@ -798,11 +863,18 @@
             }
 
             final boolean hasLimit = policy.limitBytes != LIMIT_DISABLED;
-            final boolean hasWarning = policy.warningBytes != WARNING_DISABLED;
-
             if (hasLimit) {
-                // remaining "quota" is based on usage in current cycle
-                final long quotaBytes = Math.max(0, policy.limitBytes - total);
+                final long quotaBytes;
+                if (policy.lastSnooze >= start) {
+                    // snoozing past quota, but we still need to restrict apps,
+                    // so push really high quota.
+                    quotaBytes = Long.MAX_VALUE;
+                } else {
+                    // remaining "quota" bytes are based on total usage in
+                    // current cycle. kernel doesn't like 0-byte rules, so we
+                    // set 1-byte quota and disable the radio later.
+                    quotaBytes = Math.max(1, policy.limitBytes - totalBytes);
+                }
 
                 if (ifaces.length > 1) {
                     // TODO: switch to shared quota once NMS supports
@@ -811,10 +883,8 @@
 
                 for (String iface : ifaces) {
                     removeInterfaceQuota(iface);
-                    if (quotaBytes > 0) {
-                        setInterfaceQuota(iface, quotaBytes);
-                        newMeteredIfaces.add(iface);
-                    }
+                    setInterfaceQuota(iface, quotaBytes);
+                    newMeteredIfaces.add(iface);
                 }
             }
         }
@@ -837,6 +907,8 @@
      */
     private void ensureActiveMobilePolicyLocked() {
         if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyLocked()");
+        if (mSuppressDefaultPolicy) return;
+
         final String subscriberId = getActiveSubscriberId();
         final NetworkIdentity probeIdent = new NetworkIdentity(
                 TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, false);
@@ -1073,6 +1145,7 @@
                 mNetworkPolicy.put(policy.template, policy);
             }
 
+            updateNetworkEnabledLocked();
             updateNetworkRulesLocked();
             updateNotificationsLocked();
             writePolicyLocked();
@@ -1093,14 +1166,7 @@
     public void snoozePolicy(NetworkTemplate template) {
         mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
 
-        // try refreshing time source when stale
-        if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
-            mTime.forceRefresh();
-        }
-
-        final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
-                : System.currentTimeMillis();
-
+        final long currentTime = currentTimeMillis(true);
         synchronized (mRulesLock) {
             // find and snooze local policy that matches
             final NetworkPolicy policy = mNetworkPolicy.get(template);
@@ -1110,6 +1176,7 @@
 
             policy.lastSnooze = currentTime;
 
+            updateNetworkEnabledLocked();
             updateNetworkRulesLocked();
             updateNotificationsLocked();
             writePolicyLocked();
@@ -1173,22 +1240,14 @@
             return null;
         }
 
-        final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
-                : System.currentTimeMillis();
+        final long currentTime = currentTimeMillis(false);
 
         final long start = computeLastCycleBoundary(currentTime, policy);
         final long end = currentTime;
 
         // find total bytes used under policy
-        long totalBytes = 0;
-        try {
-            final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
-                    policy.template, start, end);
-            final NetworkStats.Entry entry = stats.getValues(0, null);
-            totalBytes = entry.rxBytes + entry.txBytes;
-        } catch (RemoteException e) {
-            Slog.w(TAG, "problem reading summary for template " + policy.template);
-        }
+        final long totalBytes = getTotalBytes(policy.template, start, end);
+        if (totalBytes == UNKNOWN_BYTES) return null;
 
         // report soft and hard limits under policy
         final long softLimitBytes = policy.warningBytes != WARNING_DISABLED ? policy.warningBytes
@@ -1481,12 +1540,54 @@
         }
     }
 
+    /**
+     * Control {@link IConnectivityManager#setPolicyDataEnable(int, boolean)},
+     * dispatching only when actually changed.
+     */
+    private void setPolicyDataEnable(int networkType, boolean enabled) {
+        synchronized (mActiveNetworkEnabled) {
+            final boolean prevEnabled = mActiveNetworkEnabled.get(networkType, true);
+            if (prevEnabled == enabled) return;
+
+            try {
+                mConnManager.setPolicyDataEnable(networkType, enabled);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "problem setting network enabled", e);
+            }
+
+            mActiveNetworkEnabled.put(networkType, enabled);
+        }
+    }
+
     private String getActiveSubscriberId() {
         final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
                 Context.TELEPHONY_SERVICE);
         return telephony.getSubscriberId();
     }
 
+    private static final long UNKNOWN_BYTES = -1;
+
+    private long getTotalBytes(NetworkTemplate template, long start, long end) {
+        try {
+            final NetworkStats stats = mNetworkStats.getSummaryForNetwork(
+                    template, start, end);
+            final NetworkStats.Entry entry = stats.getValues(0, null);
+            return entry.rxBytes + entry.txBytes;
+        } catch (RemoteException e) {
+            Slog.w(TAG, "problem reading summary for template " + template);
+            return UNKNOWN_BYTES;
+        }
+    }
+
+    private long currentTimeMillis(boolean allowRefresh) {
+        // try refreshing time source when stale
+        if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE && allowRefresh) {
+            mTime.forceRefresh();
+        }
+
+        return mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis();
+    }
+
     private static Intent buildAllowBackgroundDataIntent() {
         return new Intent(ACTION_ALLOW_BACKGROUND);
     }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index dc5555e..e258b1a 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -50,11 +50,9 @@
 import android.Manifest;
 import android.app.ActivityManagerNative;
 import android.app.IActivityManager;
-import android.app.ProgressDialog;
 import android.app.StatusBarManager;
 import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -164,6 +162,8 @@
     static final boolean DEBUG_REORDER = false;
     static final boolean DEBUG_WALLPAPER = false;
     static final boolean DEBUG_DRAG = false;
+    static final boolean DEBUG_SCREEN_ON = false;
+    static final boolean DEBUG_SCREENSHOT = false;
     static final boolean SHOW_SURFACE_ALLOC = false;
     static final boolean SHOW_TRANSACTIONS = false;
     static final boolean HIDE_STACK_CRAWLS = true;
@@ -408,6 +408,7 @@
     boolean mSafeMode;
     boolean mDisplayEnabled = false;
     boolean mSystemBooted = false;
+    boolean mForceDisplayEnabled = false;
     boolean mShowingBootMessages = false;
     int mInitialDisplayWidth = 0;
     int mInitialDisplayHeight = 0;
@@ -2189,7 +2190,7 @@
         // to hold off on removing the window until the animation is done.
         // If the display is frozen, just remove immediately, since the
         // animation wouldn't be seen.
-        if (win.mSurface != null && !mDisplayFrozen && mPolicy.isScreenOn()) {
+        if (win.mSurface != null && !mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
             // If we are not currently running the exit animation, we
             // need to see about starting one.
             if (wasVisible=win.isWinVisibleLw()) {
@@ -2536,6 +2537,12 @@
             win.mRelayoutCalled = true;
             final int oldVisibility = win.mViewVisibility;
             win.mViewVisibility = viewVisibility;
+            if (DEBUG_SCREEN_ON) {
+                RuntimeException stack = new RuntimeException();
+                stack.fillInStackTrace();
+                Slog.i(TAG, "Relayout " + win + ": oldVis=" + oldVisibility
+                        + " newVis=" + viewVisibility, stack);
+            }
             if (viewVisibility == View.VISIBLE &&
                     (win.mAppToken == null || !win.mAppToken.clientHidden)) {
                 displayed = !win.isVisibleLw();
@@ -2556,7 +2563,7 @@
                 if (displayed) {
                     if (win.mSurface != null && !win.mDrawPending
                             && !win.mCommitDrawPending && !mDisplayFrozen
-                            && mPolicy.isScreenOn()) {
+                            && mDisplayEnabled && mPolicy.isScreenOn()) {
                         applyEnterAnimationLocked(win);
                     }
                     if ((win.mAttrs.flags
@@ -2849,7 +2856,7 @@
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
         // is running.
-        if (!mDisplayFrozen && mPolicy.isScreenOn()) {
+        if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
             int anim = mPolicy.selectAnimationLw(win, transit);
             int attr = -1;
             Animation a = null;
@@ -2935,7 +2942,7 @@
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
         // is running.
-        if (!mDisplayFrozen && mPolicy.isScreenOn()) {
+        if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
             Animation a;
             if (mNextAppTransitionPackage != null) {
                 a = loadAnimation(mNextAppTransitionPackage, enter ?
@@ -3501,7 +3508,7 @@
             if (DEBUG_APP_TRANSITIONS) Slog.v(
                     TAG, "Prepare app transition: transit=" + transit
                     + " mNextAppTransition=" + mNextAppTransition);
-            if (!mDisplayFrozen && mPolicy.isScreenOn()) {
+            if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()) {
                 if (mNextAppTransition == WindowManagerPolicy.TRANSIT_UNSET
                         || mNextAppTransition == WindowManagerPolicy.TRANSIT_NONE) {
                     mNextAppTransition = transit;
@@ -3585,7 +3592,7 @@
             // If the display is frozen, we won't do anything until the
             // actual window is displayed so there is no reason to put in
             // the starting window.
-            if (mDisplayFrozen || !mPolicy.isScreenOn()) {
+            if (mDisplayFrozen || !mDisplayEnabled || !mPolicy.isScreenOn()) {
                 return;
             }
 
@@ -3867,7 +3874,7 @@
 
             // If we are preparing an app transition, then delay changing
             // the visibility of this token until we execute that transition.
-            if (!mDisplayFrozen && mPolicy.isScreenOn()
+            if (!mDisplayFrozen && mDisplayEnabled && mPolicy.isScreenOn()
                     && mNextAppTransition != WindowManagerPolicy.TRANSIT_UNSET) {
                 // Already in requested state, don't do anything more.
                 if (wtoken.hiddenRequested != visible) {
@@ -4688,6 +4695,10 @@
             }
             mSystemBooted = true;
             hideBootMessagesLocked();
+            // If the screen still doesn't come up after 30 seconds, give
+            // up and turn it on.
+            Message msg = mH.obtainMessage(H.BOOT_TIMEOUT);
+            mH.sendMessageDelayed(msg, 30*1000);
         }
 
         performEnableScreen();
@@ -4703,6 +4714,17 @@
         mH.sendMessage(mH.obtainMessage(H.ENABLE_SCREEN));
     }
 
+    public void performBootTimeout() {
+        synchronized(mWindowMap) {
+            if (mDisplayEnabled) {
+                return;
+            }
+            Slog.w(TAG, "***** BOOT TIMEOUT: forcing display enabled");
+            mForceDisplayEnabled = true;
+        }
+        performEnableScreen();
+    }
+
     public void performEnableScreen() {
         synchronized(mWindowMap) {
             if (mDisplayEnabled) {
@@ -4712,19 +4734,55 @@
                 return;
             }
 
-            // Don't enable the screen until all existing windows
-            // have been drawn.
-            final int N = mWindows.size();
-            for (int i=0; i<N; i++) {
-                WindowState w = mWindows.get(i);
-                if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+            if (!mForceDisplayEnabled) {
+                // Don't enable the screen until all existing windows
+                // have been drawn.
+                boolean haveBootMsg = false;
+                boolean haveApp = false;
+                boolean haveWallpaper = false;
+                boolean haveKeyguard = false;
+                final int N = mWindows.size();
+                for (int i=0; i<N; i++) {
+                    WindowState w = mWindows.get(i);
+                    if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+                        return;
+                    }
+                    if (w.isDrawnLw()) {
+                        if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_BOOT_PROGRESS) {
+                            haveBootMsg = true;
+                        } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_APPLICATION) {
+                            haveApp = true;
+                        } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) {
+                            haveWallpaper = true;
+                        } else if (w.mAttrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD) {
+                            haveKeyguard = true;
+                        }
+                    }
+                }
+
+                if (DEBUG_SCREEN_ON) {
+                    Slog.i(TAG, "******** booted=" + mSystemBooted + " msg=" + mShowingBootMessages
+                            + " haveBoot=" + haveBootMsg + " haveApp=" + haveApp
+                            + " haveWall=" + haveWallpaper + " haveKeyguard=" + haveKeyguard);
+                }
+
+                // If we are turning on the screen to show the boot message,
+                // don't do it until the boot message is actually displayed.
+                if (!mSystemBooted && !haveBootMsg) {
+                    return;
+                }
+    
+                // If we are turning on the screen after the boot is completed
+                // normally, don't do so until we have the application and
+                // wallpaper.
+                if (mSystemBooted && ((!haveApp && !haveKeyguard) || !haveWallpaper)) {
                     return;
                 }
             }
 
             mDisplayEnabled = true;
+            if (DEBUG_SCREEN_ON) Slog.i(TAG, "******************** ENABLING SCREEN!");
             if (false) {
-                Slog.i(TAG, "ENABLING SCREEN!");
                 StringWriter sw = new StringWriter();
                 PrintWriter pw = new PrintWriter(sw);
                 this.dump(null, pw, null);
@@ -4939,6 +4997,14 @@
                 dh = tmp;
                 rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
             }
+            if (DEBUG_SCREENSHOT) {
+                Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer);
+                for (int i=0; i<mWindows.size(); i++) {
+                    Slog.i(TAG, mWindows.get(i) + ": " + mWindows.get(i).mLayer
+                            + " animLayer=" + mWindows.get(i).mAnimLayer
+                            + " surfaceLayer=" + mWindows.get(i).mSurfaceLayer);
+                }
+            }
             rawss = Surface.screenshot(dw, dh, 0, maxLayer);
         }
 
@@ -6239,6 +6305,7 @@
         public static final int DRAG_START_TIMEOUT = 20;
         public static final int DRAG_END_TIMEOUT = 21;
         public static final int REPORT_HARD_KEYBOARD_STATUS_CHANGE = 22;
+        public static final int BOOT_TIMEOUT = 23;
 
         private Session mLastReportedHold;
 
@@ -6549,6 +6616,11 @@
                     break;
                 }
 
+                case BOOT_TIMEOUT: {
+                    performBootTimeout();
+                    break;
+                }
+
                 case APP_FREEZE_TIMEOUT: {
                     synchronized (mWindowMap) {
                         Slog.w(TAG, "App freeze timeout expired.");
@@ -8300,7 +8372,7 @@
 
             if (mDimAnimator != null && mDimAnimator.mDimShown) {
                 animating |= mDimAnimator.updateSurface(dimming, currentTime,
-                        mDisplayFrozen || !mPolicy.isScreenOn());
+                        mDisplayFrozen || !mDisplayEnabled || !mPolicy.isScreenOn());
             }
 
             if (!blurring && mBlurShown) {
@@ -8498,12 +8570,44 @@
                 mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
             }
         }
-        
+
+        mWindowMap.notifyAll();
+
         // Check to see if we are now in a state where the screen should
         // be enabled, because the window obscured flags have changed.
         enableScreenIfNeededLocked();
     }
-    
+
+    public void waitForAllDrawn() {
+        synchronized (mWindowMap) {
+            while (true) {
+                final int N = mWindows.size();
+                boolean okay = true;
+                for (int i=0; i<N && okay; i++) {
+                    WindowState w = mWindows.get(i);
+                    if (DEBUG_SCREEN_ON) {
+                        Slog.i(TAG, "Window " + w + " vis=" + w.isVisibleLw()
+                                + " obscured=" + w.mObscured + " drawn=" + w.isDrawnLw());
+                    }
+                    if (w.isVisibleLw() && !w.mObscured && !w.isDrawnLw()) {
+                        if (DEBUG_SCREEN_ON) {
+                            Slog.i(TAG, "Window not yet drawn: " + w);
+                        }
+                        okay = false;
+                        break;
+                    }
+                }
+                if (okay) {
+                    return;
+                }
+                try {
+                    mWindowMap.wait();
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
     /**
      * Must be called with the main window manager lock held.
      */
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 19c7ddd..8b2485a 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -244,9 +244,6 @@
     }
 }
 
-static inline uint16_t pack565(int r, int g, int b) {
-    return (r<<11)|(g<<5)|b;
-}
 void Layer::onDraw(const Region& clip) const
 {
     if (CC_UNLIKELY(mActiveBuffer == 0)) {
@@ -260,7 +257,8 @@
 
         // figure out if there is something below us
         Region under;
-        const SurfaceFlinger::LayerVector& drawingLayers(mFlinger->mDrawingState.layersSortedByZ);
+        const SurfaceFlinger::LayerVector& drawingLayers(
+                mFlinger->mDrawingState.layersSortedByZ);
         const size_t count = drawingLayers.size();
         for (size_t i=0 ; i<count ; ++i) {
             const sp<LayerBase>& layer(drawingLayers[i]);
@@ -276,7 +274,7 @@
         return;
     }
 
-    GLenum target = mSurfaceTexture->getCurrentTextureTarget();
+    const GLenum target = GL_TEXTURE_EXTERNAL_OES;
     glBindTexture(target, mTextureName);
     if (getFiltering() || needsFiltering() || isFixedSize() || isCropped()) {
         // TODO: we could be more subtle with isFixedSize()
@@ -439,9 +437,8 @@
             recomputeVisibleRegions = true;
         }
 
-        const GLenum target(mSurfaceTexture->getCurrentTextureTarget());
-        glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameterx(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
         // update the layer size and release freeze-lock
         const Layer::State& front(drawingState());
@@ -541,9 +538,9 @@
     snprintf(buffer, SIZE,
             "      "
             "format=%2d, activeBuffer=[%4ux%4u:%4u,%3X],"
-            " freezeLock=%p, queued-frames=%d\n",
+            " freezeLock=%p, transform-hint=0x%02x, queued-frames=%d\n",
             mFormat, w0, h0, s0,f0,
-            getFreezeLock().get(), mQueuedFrames);
+            getFreezeLock().get(), getTransformHint(), mQueuedFrames);
 
     result.append(buffer);
 
@@ -562,6 +559,17 @@
     return usage;
 }
 
+uint32_t Layer::getTransformHint() const {
+    uint32_t orientation = 0;
+    if (!mFlinger->mDebugDisableTransformHint) {
+        orientation = getOrientation();
+        if (orientation & Transform::ROT_INVALID) {
+            orientation = 0;
+        }
+    }
+    return orientation;
+}
+
 // ---------------------------------------------------------------------------
 
 
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 5f0be80..d06a35f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -89,6 +89,7 @@
     void onFrameQueued();
     virtual sp<ISurface> createSurface();
     uint32_t getEffectiveUsage(uint32_t usage) const;
+    uint32_t getTransformHint() const;
     bool isCropped() const;
     static bool getOpacityForFormat(uint32_t format);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 1f27a70..0080202 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -93,6 +93,7 @@
         mDebugBackground(0),
         mDebugDDMS(0),
         mDebugDisableHWC(0),
+        mDebugDisableTransformHint(0),
         mDebugInSwapBuffers(0),
         mLastSwapBufferTime(0),
         mDebugInTransaction(0),
@@ -1689,6 +1690,12 @@
                 invalidateHwcGeometry();
                 repaintEverything();
                 return NO_ERROR;
+            case 1009:  // toggle use of transform hint
+                n = data.readInt32();
+                mDebugDisableTransformHint = n ? 1 : 0;
+                invalidateHwcGeometry();
+                repaintEverything();
+                return NO_ERROR;
             case 1010:  // interrogate.
                 reply->writeInt32(0);
                 reply->writeInt32(0);
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d68e484..5f8eb08 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -374,6 +374,7 @@
                 int                         mDebugBackground;
                 int                         mDebugDDMS;
                 int                         mDebugDisableHWC;
+                int                         mDebugDisableTransformHint;
                 volatile nsecs_t            mDebugInSwapBuffers;
                 nsecs_t                     mLastSwapBufferTime;
                 volatile nsecs_t            mDebugInTransaction;
diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp
index 79cd0c3..4390ca1 100644
--- a/services/surfaceflinger/SurfaceTextureLayer.cpp
+++ b/services/surfaceflinger/SurfaceTextureLayer.cpp
@@ -57,16 +57,10 @@
 
     status_t res = SurfaceTexture::queueBuffer(buf, timestamp,
             outWidth, outHeight, outTransform);
-
     sp<Layer> layer(mLayer.promote());
     if (layer != NULL) {
-        uint32_t orientation = layer->getOrientation();
-        if (orientation & Transform::ROT_INVALID) {
-            orientation = 0;
-        }
-        *outTransform = orientation;
+        *outTransform = layer->getTransformHint();
     }
-
     return res;
 }
 
diff --git a/services/surfaceflinger/TextureManager.cpp b/services/surfaceflinger/TextureManager.cpp
deleted file mode 100644
index bb63c37..0000000
--- a/services/surfaceflinger/TextureManager.cpp
+++ /dev/null
@@ -1,341 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-#include <utils/Log.h>
-
-#include <ui/GraphicBuffer.h>
-
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-#include <hardware/hardware.h>
-
-#include "clz.h"
-#include "DisplayHardware/DisplayHardware.h"
-#include "GLExtensions.h"
-#include "TextureManager.h"
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-TextureManager::TextureManager()
-    : mGLExtensions(GLExtensions::getInstance())
-{
-}
-
-GLenum TextureManager::getTextureTarget(const Image* image) {
-#if defined(GL_OES_EGL_image_external)
-    switch (image->target) {
-        case Texture::TEXTURE_EXTERNAL:
-            return GL_TEXTURE_EXTERNAL_OES;
-    }
-#endif
-    return GL_TEXTURE_2D;
-}
-
-status_t TextureManager::initTexture(Texture* texture)
-{
-    if (texture->name != -1UL)
-        return INVALID_OPERATION;
-
-    GLuint textureName = -1;
-    glGenTextures(1, &textureName);
-    texture->name = textureName;
-    texture->width = 0;
-    texture->height = 0;
-
-    const GLenum target = GL_TEXTURE_2D;
-    glBindTexture(target, textureName);
-    glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
-    return NO_ERROR;
-}
-
-status_t TextureManager::initTexture(Image* pImage, int32_t format)
-{
-    if (pImage->name != -1UL)
-        return INVALID_OPERATION;
-
-    GLuint textureName = -1;
-    glGenTextures(1, &textureName);
-    pImage->name = textureName;
-    pImage->width = 0;
-    pImage->height = 0;
-
-    GLenum target = GL_TEXTURE_2D;
-#if defined(GL_OES_EGL_image_external)
-    if (GLExtensions::getInstance().haveTextureExternal()) {
-        if (format && isYuvFormat(format)) {
-            target = GL_TEXTURE_EXTERNAL_OES;
-            pImage->target = Texture::TEXTURE_EXTERNAL;
-        }
-    }
-#endif
-
-    glBindTexture(target, textureName);
-    glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-    glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
-    return NO_ERROR;
-}
-
-bool TextureManager::isSupportedYuvFormat(int format)
-{
-    switch (format) {
-    case HAL_PIXEL_FORMAT_YV12:
-        return true;
-    }
-    return false;
-}
-
-bool TextureManager::isYuvFormat(int format)
-{
-    switch (format) {
-    // supported YUV formats
-    case HAL_PIXEL_FORMAT_YV12:
-    // Legacy/deprecated YUV formats
-    case HAL_PIXEL_FORMAT_YCbCr_422_SP:
-    case HAL_PIXEL_FORMAT_YCrCb_420_SP:
-    case HAL_PIXEL_FORMAT_YCbCr_422_I:
-        return true;
-    }
-
-    // Any OEM format needs to be considered
-    if (format>=0x100 && format<=0x1FF)
-        return true;
-
-    return false;
-}
-
-status_t TextureManager::initEglImage(Image* pImage,
-        EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
-{
-    status_t err = NO_ERROR;
-    if (!pImage->dirty) return err;
-
-    // free the previous image
-    if (pImage->image != EGL_NO_IMAGE_KHR) {
-        eglDestroyImageKHR(dpy, pImage->image);
-        pImage->image = EGL_NO_IMAGE_KHR;
-    }
-
-    // construct an EGL_NATIVE_BUFFER_ANDROID
-    ANativeWindowBuffer* clientBuf = buffer->getNativeBuffer();
-
-    // create the new EGLImageKHR
-    const EGLint attrs[] = {
-            EGL_IMAGE_PRESERVED_KHR,    EGL_TRUE,
-            EGL_NONE,                   EGL_NONE
-    };
-    pImage->image = eglCreateImageKHR(
-            dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
-            (EGLClientBuffer)clientBuf, attrs);
-
-    if (pImage->image != EGL_NO_IMAGE_KHR) {
-        if (pImage->name == -1UL) {
-            initTexture(pImage, buffer->format);
-        }
-        const GLenum target = getTextureTarget(pImage);
-        glBindTexture(target, pImage->name);
-        glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image);
-        GLint error = glGetError();
-        if (error != GL_NO_ERROR) {
-            LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
-                    pImage->image, error);
-            err = INVALID_OPERATION;
-        } else {
-            // Everything went okay!
-            pImage->dirty  = false;
-            pImage->width  = clientBuf->width;
-            pImage->height = clientBuf->height;
-        }
-    } else {
-        LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
-        err = INVALID_OPERATION;
-    }
-    return err;
-}
-
-status_t TextureManager::loadTexture(Texture* texture,
-        const Region& dirty, const GGLSurface& t)
-{
-    if (texture->name == -1UL) {
-        status_t err = initTexture(texture);
-        LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
-        if (err != NO_ERROR) return err;
-    }
-
-    if (texture->target != Texture::TEXTURE_2D)
-        return INVALID_OPERATION;
-
-    glBindTexture(GL_TEXTURE_2D, texture->name);
-
-    /*
-     * In OpenGL ES we can't specify a stride with glTexImage2D (however,
-     * GL_UNPACK_ALIGNMENT is a limited form of stride).
-     * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
-     * need to do something reasonable (here creating a bigger texture).
-     *
-     * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
-     *
-     * This situation doesn't happen often, but some h/w have a limitation
-     * for their framebuffer (eg: must be multiple of 8 pixels), and
-     * we need to take that into account when using these buffers as
-     * textures.
-     *
-     * This should never be a problem with POT textures
-     */
-
-    int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
-    unpack = 1 << ((unpack > 3) ? 3 : unpack);
-    glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
-
-    /*
-     * round to POT if needed
-     */
-    if (!mGLExtensions.haveNpot()) {
-        texture->NPOTAdjust = true;
-    }
-
-    if (texture->NPOTAdjust) {
-        // find the smallest power-of-two that will accommodate our surface
-        texture->potWidth  = 1 << (31 - clz(t.width));
-        texture->potHeight = 1 << (31 - clz(t.height));
-        if (texture->potWidth  < t.width)  texture->potWidth  <<= 1;
-        if (texture->potHeight < t.height) texture->potHeight <<= 1;
-        texture->wScale = float(t.width)  / texture->potWidth;
-        texture->hScale = float(t.height) / texture->potHeight;
-    } else {
-        texture->potWidth  = t.width;
-        texture->potHeight = t.height;
-    }
-
-    Rect bounds(dirty.bounds());
-    GLvoid* data = 0;
-    if (texture->width != t.width || texture->height != t.height) {
-        texture->width  = t.width;
-        texture->height = t.height;
-
-        // texture size changed, we need to create a new one
-        bounds.set(Rect(t.width, t.height));
-        if (t.width  == texture->potWidth &&
-            t.height == texture->potHeight) {
-            // we can do it one pass
-            data = t.data;
-        }
-
-        if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
-            glTexImage2D(GL_TEXTURE_2D, 0,
-                    GL_RGB, texture->potWidth, texture->potHeight, 0,
-                    GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
-        } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
-            glTexImage2D(GL_TEXTURE_2D, 0,
-                    GL_RGBA, texture->potWidth, texture->potHeight, 0,
-                    GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
-        } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
-                   t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
-            glTexImage2D(GL_TEXTURE_2D, 0,
-                    GL_RGBA, texture->potWidth, texture->potHeight, 0,
-                    GL_RGBA, GL_UNSIGNED_BYTE, data);
-        } else if (isSupportedYuvFormat(t.format)) {
-            // just show the Y plane of YUV buffers
-            glTexImage2D(GL_TEXTURE_2D, 0,
-                    GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
-                    GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
-        } else {
-            // oops, we don't handle this format!
-            LOGE("texture=%d, using format %d, which is not "
-                 "supported by the GL", texture->name, t.format);
-        }
-    }
-    if (!data) {
-        if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
-            glTexSubImage2D(GL_TEXTURE_2D, 0,
-                    0, bounds.top, t.width, bounds.height(),
-                    GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
-                    t.data + bounds.top*t.stride*2);
-        } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
-            glTexSubImage2D(GL_TEXTURE_2D, 0,
-                    0, bounds.top, t.width, bounds.height(),
-                    GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
-                    t.data + bounds.top*t.stride*2);
-        } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
-                   t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
-            glTexSubImage2D(GL_TEXTURE_2D, 0,
-                    0, bounds.top, t.width, bounds.height(),
-                    GL_RGBA, GL_UNSIGNED_BYTE,
-                    t.data + bounds.top*t.stride*4);
-        } else if (isSupportedYuvFormat(t.format)) {
-            // just show the Y plane of YUV buffers
-            glTexSubImage2D(GL_TEXTURE_2D, 0,
-                    0, bounds.top, t.width, bounds.height(),
-                    GL_LUMINANCE, GL_UNSIGNED_BYTE,
-                    t.data + bounds.top*t.stride);
-        }
-    }
-    return NO_ERROR;
-}
-
-void TextureManager::activateTexture(const Texture& texture, bool filter)
-{
-    const GLenum target = getTextureTarget(&texture);
-    if (target == GL_TEXTURE_2D) {
-        glBindTexture(GL_TEXTURE_2D, texture.name);
-        glEnable(GL_TEXTURE_2D);
-#if defined(GL_OES_EGL_image_external)
-        if (GLExtensions::getInstance().haveTextureExternal()) {
-            glDisable(GL_TEXTURE_EXTERNAL_OES);
-        }
-    } else {
-        glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name);
-        glEnable(GL_TEXTURE_EXTERNAL_OES);
-        glDisable(GL_TEXTURE_2D);
-#endif
-    }
-
-    if (filter) {
-        glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    } else {
-        glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
-        glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-    }
-}
-
-void TextureManager::deactivateTextures()
-{
-    glDisable(GL_TEXTURE_2D);
-#if defined(GL_OES_EGL_image_external)
-    if (GLExtensions::getInstance().haveTextureExternal()) {
-        glDisable(GL_TEXTURE_EXTERNAL_OES);
-    }
-#endif
-}
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h
deleted file mode 100644
index 18c43486..0000000
--- a/services/surfaceflinger/TextureManager.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_TEXTURE_MANAGER_H
-#define ANDROID_TEXTURE_MANAGER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <EGL/egl.h>
-#include <EGL/eglext.h>
-#include <GLES/gl.h>
-
-#include <ui/Region.h>
-
-#include <pixelflinger/pixelflinger.h>
-
-namespace android {
-
-// ---------------------------------------------------------------------------
-
-class GLExtensions;
-class GraphicBuffer;
-
-// ---------------------------------------------------------------------------
-
-struct Image {
-    enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 };
-    Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0),
-        dirty(1), target(TEXTURE_2D) { }
-    GLuint        name;
-    EGLImageKHR   image;
-    GLuint        width;
-    GLuint        height;
-    unsigned      dirty     : 1;
-    unsigned      target    : 1;
-};
-
-struct Texture : public Image {
-    Texture() : Image(), NPOTAdjust(0) { }
-    GLuint      potWidth;
-    GLuint      potHeight;
-    GLfloat     wScale;
-    GLfloat     hScale;
-    unsigned    NPOTAdjust  : 1;
-};
-
-// ---------------------------------------------------------------------------
-
-class TextureManager {
-    const GLExtensions& mGLExtensions;
-    static status_t initTexture(Image* texture, int32_t format);
-    static status_t initTexture(Texture* texture);
-    static bool isSupportedYuvFormat(int format);
-    static bool isYuvFormat(int format);
-    static GLenum getTextureTarget(const Image* pImage);
-public:
-
-    TextureManager();
-
-    // load bitmap data into the active buffer
-    status_t loadTexture(Texture* texture,
-            const Region& dirty, const GGLSurface& t);
-
-    // make active buffer an EGLImage if needed
-    status_t initEglImage(Image* texture,
-            EGLDisplay dpy, const sp<GraphicBuffer>& buffer);
-
-    // activate a texture
-    static void activateTexture(const Texture& texture, bool filter);
-
-    // deactivate a texture
-    static void deactivateTextures();
-};
-
-// ---------------------------------------------------------------------------
-
-}; // namespace android
-
-#endif // ANDROID_TEXTURE_MANAGER_H
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 845aa3f..f67d251 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -79,6 +79,7 @@
 import org.easymock.Capture;
 import org.easymock.EasyMock;
 import org.easymock.IAnswer;
+import org.easymock.IExpectationSetters;
 
 import java.io.File;
 import java.util.LinkedHashSet;
@@ -87,6 +88,8 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
+import libcore.io.IoUtils;
+
 /**
  * Tests for {@link NetworkPolicyManagerService}.
  */
@@ -117,6 +120,9 @@
 
     private Binder mStubBinder = new Binder();
 
+    private long mStartTime;
+    private long mElapsedRealtime;
+
     private static final int UID_A = android.os.Process.FIRST_APPLICATION_UID + 800;
     private static final int UID_B = android.os.Process.FIRST_APPLICATION_UID + 801;
 
@@ -128,6 +134,8 @@
     public void setUp() throws Exception {
         super.setUp();
 
+        setCurrentTimeMillis(TEST_START);
+
         // intercept various broadcasts, and pretend that uids have packages
         mServiceContext = new BroadcastInterceptingContext(getContext()) {
             @Override
@@ -160,8 +168,8 @@
         };
 
         mPolicyDir = getContext().getFilesDir();
-        for (File file : mPolicyDir.listFiles()) {
-            file.delete();
+        if (mPolicyDir.exists()) {
+            IoUtils.deleteContents(mPolicyDir);
         }
 
         mActivityManager = createMock(IActivityManager.class);
@@ -173,9 +181,8 @@
         mConnManager = createMock(IConnectivityManager.class);
         mNotifManager = createMock(INotificationManager.class);
 
-        mService = new NetworkPolicyManagerService(
-                mServiceContext, mActivityManager, mPowerManager, mStatsService,
-                mNetworkManager, mTime, mPolicyDir);
+        mService = new NetworkPolicyManagerService(mServiceContext, mActivityManager, mPowerManager,
+                mStatsService, mNetworkManager, mTime, mPolicyDir, true);
         mService.bindConnectivityManager(mConnManager);
         mService.bindNotificationManager(mNotifManager);
 
@@ -198,7 +205,7 @@
 
         // expect to answer screen status during systemReady()
         expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
-        expectTime(System.currentTimeMillis());
+        expectCurrentTime();
 
         replay();
         mService.systemReady();
@@ -485,7 +492,6 @@
     }
 
     public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
-        long elapsedRealtime = 0;
         NetworkState[] state = null;
         NetworkStats stats = null;
         Future<Void> future;
@@ -494,11 +500,13 @@
         final long TIME_MAR_10 = 1173484800000L;
         final int CYCLE_DAY = 15;
 
+        setCurrentTimeMillis(TIME_MAR_10);
+
         // first, pretend that wifi network comes online. no policy active,
         // which means we shouldn't push limit to interface.
         state = new NetworkState[] { buildWifi() };
         expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
-        expectTime(TIME_MAR_10 + elapsedRealtime);
+        expectCurrentTime();
         expectClearNotifications();
         future = expectMeteredIfacesChanged();
 
@@ -510,10 +518,10 @@
         // now change cycle to be on 15th, and test in early march, to verify we
         // pick cycle day in previous month.
         expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
-        expectTime(TIME_MAR_10 + elapsedRealtime);
+        expectCurrentTime();
 
         // pretend that 512 bytes total have happened
-        stats = new NetworkStats(elapsedRealtime, 1)
+        stats = new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 256L, 2L, 256L, 2L);
         expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
                 .andReturn(stats).atLeastOnce();
@@ -521,8 +529,6 @@
         // TODO: consider making strongly ordered mock
         expectRemoveInterfaceQuota(TEST_IFACE);
         expectSetInterfaceQuota(TEST_IFACE, 1536L);
-        expectRemoveInterfaceAlert(TEST_IFACE);
-        expectSetInterfaceAlert(TEST_IFACE, 512L);
 
         expectClearNotifications();
         future = expectMeteredIfacesChanged(TEST_IFACE);
@@ -558,8 +564,6 @@
     }
 
     public void testOverWarningLimitNotification() throws Exception {
-        long elapsedRealtime = 0;
-        long currentTime = 0;
         NetworkState[] state = null;
         NetworkStats stats = null;
         Future<Void> future;
@@ -569,14 +573,18 @@
         final long TIME_MAR_10 = 1173484800000L;
         final int CYCLE_DAY = 15;
 
+        setCurrentTimeMillis(TIME_MAR_10);
+
         // assign wifi policy
-        elapsedRealtime = 0;
-        currentTime = TIME_MAR_10 + elapsedRealtime;
         state = new NetworkState[] {};
+        stats = new NetworkStats(getElapsedRealtime(), 1)
+                .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
 
         {
-            expectTime(currentTime);
+            expectCurrentTime();
             expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+            expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
+                    .andReturn(stats).atLeastOnce();
 
             expectClearNotifications();
             future = expectMeteredIfacesChanged();
@@ -589,22 +597,19 @@
         }
 
         // bring up wifi network
-        elapsedRealtime += MINUTE_IN_MILLIS;
-        currentTime = TIME_MAR_10 + elapsedRealtime;
-        stats = new NetworkStats(elapsedRealtime, 1)
-                .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
+        incrementCurrentTime(MINUTE_IN_MILLIS);
         state = new NetworkState[] { buildWifi() };
+        stats = new NetworkStats(getElapsedRealtime(), 1)
+                .addIfaceValues(TEST_IFACE, 0L, 0L, 0L, 0L);
 
         {
-            expectTime(currentTime);
+            expectCurrentTime();
             expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
-            expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+            expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
                     .andReturn(stats).atLeastOnce();
 
             expectRemoveInterfaceQuota(TEST_IFACE);
             expectSetInterfaceQuota(TEST_IFACE, 2048L);
-            expectRemoveInterfaceAlert(TEST_IFACE);
-            expectSetInterfaceAlert(TEST_IFACE, 1024L);
 
             expectClearNotifications();
             future = expectMeteredIfacesChanged(TEST_IFACE);
@@ -616,14 +621,13 @@
         }
 
         // go over warning, which should kick notification
-        elapsedRealtime += MINUTE_IN_MILLIS;
-        currentTime = TIME_MAR_10 + elapsedRealtime;
-        stats = new NetworkStats(elapsedRealtime, 1)
+        incrementCurrentTime(MINUTE_IN_MILLIS);
+        stats = new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 1536L, 15L, 0L, 0L);
 
         {
-            expectTime(currentTime);
-            expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+            expectCurrentTime();
+            expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
                     .andReturn(stats).atLeastOnce();
 
             expectForceUpdate();
@@ -637,15 +641,15 @@
         }
 
         // go over limit, which should kick notification and dialog
-        elapsedRealtime += MINUTE_IN_MILLIS;
-        currentTime = TIME_MAR_10 + elapsedRealtime;
-        stats = new NetworkStats(elapsedRealtime, 1)
+        incrementCurrentTime(MINUTE_IN_MILLIS);
+        stats = new NetworkStats(getElapsedRealtime(), 1)
                 .addIfaceValues(TEST_IFACE, 5120L, 512L, 0L, 0L);
 
         {
-            expectTime(currentTime);
-            expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+            expectCurrentTime();
+            expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
                     .andReturn(stats).atLeastOnce();
+            expectPolicyDataEnable(TYPE_WIFI, false).atLeastOnce();
 
             expectForceUpdate();
             expectClearNotifications();
@@ -658,21 +662,23 @@
         }
 
         // now snooze policy, which should remove quota
-        elapsedRealtime += MINUTE_IN_MILLIS;
-        currentTime = TIME_MAR_10 + elapsedRealtime;
+        incrementCurrentTime(MINUTE_IN_MILLIS);
 
         {
-            expectTime(currentTime);
+            expectCurrentTime();
             expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
-            expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTime))
+            expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, currentTimeMillis()))
                     .andReturn(stats).atLeastOnce();
+            expectPolicyDataEnable(TYPE_WIFI, true).atLeastOnce();
 
+            // snoozed interface still has high quota so background data is
+            // still restricted.
             expectRemoveInterfaceQuota(TEST_IFACE);
-            expectRemoveInterfaceAlert(TEST_IFACE);
+            expectSetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE);
 
             expectClearNotifications();
             tag = expectEnqueueNotification();
-            future = expectMeteredIfacesChanged();
+            future = expectMeteredIfacesChanged(TEST_IFACE);
 
             replay();
             mService.snoozePolicy(sTemplateWifi);
@@ -700,10 +706,10 @@
         return new NetworkState(info, prop, null);
     }
 
-    private void expectTime(long currentTime) throws Exception {
+    private void expectCurrentTime() throws Exception {
         expect(mTime.forceRefresh()).andReturn(false).anyTimes();
         expect(mTime.hasCache()).andReturn(true).anyTimes();
-        expect(mTime.currentTimeMillis()).andReturn(currentTime).anyTimes();
+        expect(mTime.currentTimeMillis()).andReturn(currentTimeMillis()).anyTimes();
         expect(mTime.getCacheAge()).andReturn(0L).anyTimes();
         expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
     }
@@ -770,6 +776,12 @@
         return future;
     }
 
+    private <T> IExpectationSetters<T> expectPolicyDataEnable(int type, boolean enabled)
+            throws Exception {
+        mConnManager.setPolicyDataEnable(type, enabled);
+        return expectLastCall();
+    }
+
     private static class FutureAnswer extends AbstractFuture<Void> implements IAnswer<Void> {
         @Override
         public Void get() throws InterruptedException, ExecutionException {
@@ -818,6 +830,23 @@
                 Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
     }
 
+    private long getElapsedRealtime() {
+        return mElapsedRealtime;
+    }
+
+    private void setCurrentTimeMillis(long currentTimeMillis) {
+        mStartTime = currentTimeMillis;
+        mElapsedRealtime = 0L;
+    }
+
+    private long currentTimeMillis() {
+        return mStartTime + mElapsedRealtime;
+    }
+
+    private void incrementCurrentTime(long duration) {
+        mElapsedRealtime += duration;
+    }
+
     private void replay() {
         EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
                 mNetworkManager, mTime, mConnManager, mNotifManager);
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 3bd78e04..42ea4f29 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -36,8 +36,8 @@
 import android.os.SystemProperties;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
-import android.telephony.ServiceState;
 import android.provider.Settings.SettingNotFoundException;
+import android.telephony.ServiceState;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -126,9 +126,10 @@
     protected static final int EVENT_RESTART_RADIO = BASE + 26;
     protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = BASE + 27;
     protected static final int EVENT_RESET_DONE = BASE + 28;
-    public static final int CMD_SET_DATA_ENABLE = BASE + 29;
+    public static final int CMD_SET_USER_DATA_ENABLE = BASE + 29;
     public static final int EVENT_CLEAN_UP_ALL_CONNECTIONS = BASE + 30;
     public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
+    public static final int CMD_SET_POLICY_DATA_ENABLE = BASE + 32;
 
     /***** Constants *****/
 
@@ -153,6 +154,8 @@
     protected static final int APN_DELAY_MILLIS =
                                 SystemProperties.getInt("persist.radio.apn_delay", 5000);
 
+    protected Object mDataEnabledLock = new Object();
+
     // responds to the setInternalDataEnabled call - used internally to turn off data
     // for example during emergency calls
     protected boolean mInternalDataEnabled = true;
@@ -160,11 +163,12 @@
     // responds to public (user) API to enable/disable data use
     // independent of mInternalDataEnabled and requests for APN access
     // persisted
-    protected boolean mDataEnabled = true;
+    protected boolean mUserDataEnabled = true;
+    protected boolean mPolicyDataEnabled = true;
 
-    protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
+    private boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
 
-    protected int enabledCount = 0;
+    private int enabledCount = 0;
 
     /* Currently requested APN type (TODO: This should probably be a parameter not a member) */
     protected String mRequestedApnType = Phone.APN_TYPE_DEFAULT;
@@ -408,8 +412,8 @@
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(INTENT_SET_FAIL_DATA_SETUP_COUNTER);
 
-        mDataEnabled = Settings.Secure.getInt(mPhone.getContext().getContentResolver(),
-                Settings.Secure.MOBILE_DATA, 1) == 1;
+        mUserDataEnabled = Settings.Secure.getInt(
+                mPhone.getContext().getContentResolver(), Settings.Secure.MOBILE_DATA, 1) == 1;
 
         // TODO: Why is this registering the phone as the receiver of the intent
         //       and not its own handler?
@@ -630,13 +634,12 @@
                 onResetDone((AsyncResult) msg.obj);
                 break;
             }
-            case CMD_SET_DATA_ENABLE: {
-                boolean enabled = (msg.arg1 == ENABLED) ? true : false;
-                if (DBG) log("CMD_SET_DATA_ENABLE enabled=" + enabled);
-                onSetDataEnabled(enabled);
+            case CMD_SET_USER_DATA_ENABLE: {
+                final boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+                if (DBG) log("CMD_SET_USER_DATA_ENABLE enabled=" + enabled);
+                onSetUserDataEnabled(enabled);
                 break;
             }
-
             case CMD_SET_DEPENDENCY_MET: {
                 boolean met = (msg.arg1 == ENABLED) ? true : false;
                 if (DBG) log("CMD_SET_DEPENDENCY_MET met=" + met);
@@ -649,7 +652,11 @@
                 }
                 break;
             }
-
+            case CMD_SET_POLICY_DATA_ENABLE: {
+                final boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+                onSetPolicyDataEnabled(enabled);
+                break;
+            }
             default:
                 Log.e("DATA", "Unidentified event msg=" + msg);
                 break;
@@ -662,8 +669,12 @@
      * @return {@code false} if data connectivity has been explicitly disabled,
      *         {@code true} otherwise.
      */
-    public synchronized boolean getAnyDataEnabled() {
-        boolean result = (mInternalDataEnabled && mDataEnabled && (enabledCount != 0));
+    public boolean getAnyDataEnabled() {
+        final boolean result;
+        synchronized (mDataEnabledLock) {
+            result = (mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled
+                    && (enabledCount != 0));
+        }
         if (!result && DBG) log("getAnyDataEnabled " + result);
         return result;
     }
@@ -985,18 +996,18 @@
         return true;
     }
 
-    protected void onSetInternalDataEnabled(boolean enable) {
-        boolean prevEnabled = getAnyDataEnabled();
-        if (mInternalDataEnabled != enable) {
-            synchronized (this) {
-                mInternalDataEnabled = enable;
-            }
-            if (prevEnabled != getAnyDataEnabled()) {
-                if (!prevEnabled) {
-                    resetAllRetryCounts();
-                    onTrySetupData(Phone.REASON_DATA_ENABLED);
-                } else {
-                    cleanUpAllConnections(null);
+    protected void onSetInternalDataEnabled(boolean enabled) {
+        synchronized (mDataEnabledLock) {
+            final boolean prevEnabled = getAnyDataEnabled();
+            if (mInternalDataEnabled != enabled) {
+                mInternalDataEnabled = enabled;
+                if (prevEnabled != getAnyDataEnabled()) {
+                    if (!prevEnabled) {
+                        resetAllRetryCounts();
+                        onTrySetupData(Phone.REASON_DATA_ENABLED);
+                    } else {
+                        cleanUpAllConnections(null);
+                    }
                 }
             }
         }
@@ -1010,20 +1021,20 @@
 
     public abstract boolean isAnyActiveDataConnections();
 
-    protected void onSetDataEnabled(boolean enable) {
-        boolean prevEnabled = getAnyDataEnabled();
-        if (mDataEnabled != enable) {
-            synchronized (this) {
-                mDataEnabled = enable;
-            }
-            Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
-                    Settings.Secure.MOBILE_DATA, enable ? 1 : 0);
-            if (prevEnabled != getAnyDataEnabled()) {
-                if (!prevEnabled) {
-                    resetAllRetryCounts();
-                    onTrySetupData(Phone.REASON_DATA_ENABLED);
-                } else {
-                    onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+    protected void onSetUserDataEnabled(boolean enabled) {
+        synchronized (mDataEnabledLock) {
+            final boolean prevEnabled = getAnyDataEnabled();
+            if (mUserDataEnabled != enabled) {
+                mUserDataEnabled = enabled;
+                Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
+                        Settings.Secure.MOBILE_DATA, enabled ? 1 : 0);
+                if (prevEnabled != getAnyDataEnabled()) {
+                    if (!prevEnabled) {
+                        resetAllRetryCounts();
+                        onTrySetupData(Phone.REASON_DATA_ENABLED);
+                    } else {
+                        onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+                    }
                 }
             }
         }
@@ -1032,6 +1043,23 @@
     protected void onSetDependencyMet(String apnType, boolean met) {
     }
 
+    protected void onSetPolicyDataEnabled(boolean enabled) {
+        synchronized (mDataEnabledLock) {
+            final boolean prevEnabled = getAnyDataEnabled();
+            if (mPolicyDataEnabled != enabled) {
+                mPolicyDataEnabled = enabled;
+                if (prevEnabled != getAnyDataEnabled()) {
+                    if (!prevEnabled) {
+                        resetAllRetryCounts();
+                        onTrySetupData(Phone.REASON_DATA_ENABLED);
+                    } else {
+                        onCleanUpAllConnections(Phone.REASON_DATA_DISABLED);
+                    }
+                }
+            }
+        }
+    }
+
     protected String getReryConfig(boolean forDefault) {
         int rt = mPhone.getServiceState().getRadioTechnology();
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index 800615c..1a077d00e 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -175,6 +175,11 @@
 
     @Override
     protected boolean isDataAllowed() {
+        final boolean internalDataEnabled;
+        synchronized (mDataEnabledLock) {
+            internalDataEnabled = mInternalDataEnabled;
+        }
+
         int psState = mCdmaPhone.mSST.getCurrentDataConnectionState();
         boolean roaming = (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled());
         boolean desiredPowerState = mCdmaPhone.mSST.getDesiredPowerState();
@@ -187,7 +192,7 @@
                     (mCdmaPhone.mSST.isConcurrentVoiceAndDataAllowed() ||
                             mPhone.getState() == Phone.State.IDLE) &&
                     !roaming &&
-                    mInternalDataEnabled &&
+                    internalDataEnabled &&
                     desiredPowerState &&
                     !mPendingRestartRadio &&
                     !mCdmaPhone.needsOtaServiceProvisioning();
@@ -205,7 +210,7 @@
                 reason += " - concurrentVoiceAndData not allowed and state= " + mPhone.getState();
             }
             if (roaming) reason += " - Roaming";
-            if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
+            if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
             if (!desiredPowerState) reason += " - desiredPowerState= false";
             if (mPendingRestartRadio) reason += " - mPendingRestartRadio= true";
             if (mCdmaPhone.needsOtaServiceProvisioning()) reason += " - needs Provisioning";
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 3236901..c8671c1 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -27,14 +27,14 @@
 import android.database.Cursor;
 import android.net.ConnectivityManager;
 import android.net.LinkAddress;
+import android.net.LinkCapabilities;
+import android.net.LinkProperties;
 import android.net.LinkProperties.CompareResult;
+import android.net.NetworkConfig;
 import android.net.NetworkUtils;
 import android.net.ProxyProperties;
 import android.net.TrafficStats;
 import android.net.Uri;
-import android.net.LinkCapabilities;
-import android.net.LinkProperties;
-import android.net.NetworkConfig;
 import android.os.AsyncResult;
 import android.os.Message;
 import android.os.SystemClock;
@@ -49,34 +49,27 @@
 import android.text.TextUtils;
 import android.util.EventLog;
 import android.util.Log;
-import android.preference.PreferenceManager;
 
-import com.android.internal.R;
 import com.android.internal.telephony.ApnContext;
 import com.android.internal.telephony.ApnSetting;
 import com.android.internal.telephony.DataCallState;
 import com.android.internal.telephony.DataConnection;
+import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.DataConnection.UpdateLinkPropertyResult;
 import com.android.internal.telephony.DataConnectionAc;
 import com.android.internal.telephony.DataConnectionTracker;
+import com.android.internal.telephony.EventLogTags;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneBase;
-import com.android.internal.telephony.RetryManager;
-import com.android.internal.telephony.EventLogTags;
-import com.android.internal.telephony.DataConnection.FailCause;
 import com.android.internal.telephony.RILConstants;
+import com.android.internal.telephony.RetryManager;
 import com.android.internal.util.AsyncChannel;
 
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.List;
-import java.util.Map;
 import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * {@hide}
@@ -523,16 +516,18 @@
      * {@code true} otherwise.
      */
     @Override
-    public synchronized boolean getAnyDataEnabled() {
-        if (!(mInternalDataEnabled && mDataEnabled)) return false;
-        for (ApnContext apnContext : mApnContexts.values()) {
-            // Make sure we dont have a context that going down
-            // and is explicitly disabled.
-            if (isDataAllowed(apnContext)) {
-                return true;
+    public boolean getAnyDataEnabled() {
+        synchronized (mDataEnabledLock) {
+            if (!(mInternalDataEnabled && mUserDataEnabled && mPolicyDataEnabled)) return false;
+            for (ApnContext apnContext : mApnContexts.values()) {
+                // Make sure we dont have a context that going down
+                // and is explicitly disabled.
+                if (isDataAllowed(apnContext)) {
+                    return true;
+                }
             }
+            return false;
         }
-        return false;
     }
 
     private boolean isDataAllowed(ApnContext apnContext) {
@@ -570,6 +565,11 @@
 
     @Override
     protected boolean isDataAllowed() {
+        final boolean internalDataEnabled;
+        synchronized (mDataEnabledLock) {
+            internalDataEnabled = mInternalDataEnabled;
+        }
+
         int gprsState = mPhone.getServiceStateTracker().getCurrentDataConnectionState();
         boolean desiredPowerState = mPhone.getServiceStateTracker().getDesiredPowerState();
 
@@ -577,7 +577,7 @@
                     (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
                     mPhone.mIccRecords.getRecordsLoaded() &&
                     mPhone.getState() == Phone.State.IDLE &&
-                    mInternalDataEnabled &&
+                    internalDataEnabled &&
                     (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
                     !mIsPsRestricted &&
                     desiredPowerState;
@@ -590,7 +590,7 @@
             if (mPhone.getState() != Phone.State.IDLE) {
                 reason += " - PhoneState= " + mPhone.getState();
             }
-            if (!mInternalDataEnabled) reason += " - mInternalDataEnabled= false";
+            if (!internalDataEnabled) reason += " - mInternalDataEnabled= false";
             if (mPhone.getServiceState().getRoaming() && !getDataOnRoamingEnabled()) {
                 reason += " - Roaming and data roaming not enabled";
             }
diff --git a/tests/RenderScriptTests/FBOTest/res/drawable/robot.png b/tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png
similarity index 100%
rename from tests/RenderScriptTests/FBOTest/res/drawable/robot.png
rename to tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png
Binary files differ
diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png
similarity index 100%
rename from tests/RenderScriptTests/ImageProcessing/res/drawable/city.png
rename to tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png
Binary files differ
diff --git a/tests/RenderScriptTests/ModelViewer/res/drawable/robot.png b/tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png
similarity index 100%
rename from tests/RenderScriptTests/ModelViewer/res/drawable/robot.png
rename to tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/checker.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/checker.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/data.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/data.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/flares.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/flares.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/globe.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/globe.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/leaf.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/leaf.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/light1.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/light1.jpg
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/space.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/space.jpg
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/test_pattern.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/test_pattern.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/torusmap.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png
similarity index 100%
rename from tests/RenderScriptTests/PerfTest/res/drawable/torusmap.png
rename to tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png
Binary files differ
diff --git a/tests/RenderScriptTests/tests/res/drawable/test_pattern.png b/tests/RenderScriptTests/tests/res/drawable-nodpi/test_pattern.png
similarity index 100%
rename from tests/RenderScriptTests/tests/res/drawable/test_pattern.png
rename to tests/RenderScriptTests/tests/res/drawable-nodpi/test_pattern.png
Binary files differ
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
index 7d83a9f..ed29a78 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowManager.java
@@ -451,6 +451,10 @@
         return 0;
     }
 
+    public void waitForAllDrawn() {
+        // TODO Auto-generated method stub
+    }
+    
     public IBinder asBinder() {
         // TODO Auto-generated method stub
         return null;
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index c20c716..956c3f2 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -16,23 +16,20 @@
 
 package android.net.wifi;
 
-
-
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.net.ConnectivityManager;
 import android.net.LinkCapabilities;
-import android.net.NetworkInfo;
 import android.net.LinkProperties;
+import android.net.NetworkInfo;
 import android.net.NetworkStateTracker;
 import android.net.wifi.p2p.WifiP2pManager;
 import android.os.Handler;
 import android.os.Message;
+import android.util.Slog;
 
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Track the state of wifi for connectivity service.
@@ -44,6 +41,8 @@
     private static final String NETWORKTYPE = "WIFI";
     private static final String TAG = "WifiStateTracker";
 
+    private static final boolean LOGV = true;
+
     private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
     private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
     private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
@@ -135,11 +134,14 @@
         return mNetworkInfo.isAvailable();
     }
 
-    /**
-     * @param enabled
-     */
-    public void setDataEnable(boolean enabled) {
-        android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+    @Override
+    public void setUserDataEnable(boolean enabled) {
+        Slog.w(TAG, "ignoring setUserDataEnable(" + enabled + ")");
+    }
+
+    @Override
+    public void setPolicyDataEnable(boolean enabled) {
+        Slog.w(TAG, "ignoring setPolicyDataEnable(" + enabled + ")");
     }
 
     /**
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index 4dd856f..274edae 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -46,9 +46,9 @@
 import java.net.HttpURLConnection;
 import java.net.InetAddress;
 import java.net.URL;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 
 /**
  * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
@@ -79,7 +79,7 @@
     private static final int LOW_SIGNAL_CUTOFF = 1;
 
     private static final long DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS = 2 * 60 * 1000;
-    private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 10 * 60 * 1000;
+    private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 30 * 60 * 1000;
     private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000;
 
     private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7;
@@ -661,26 +661,34 @@
     }
 
     class DnsCheckingState extends State {
-        int dnsCheckSuccesses = 0;
-        int dnsCheckTries = 0;
-        String dnsCheckLogStr = "";
-        Set<Integer> ids = new HashSet<Integer>();
+        List<InetAddress> mDnsList;
+        int[] dnsCheckSuccesses;
+        String dnsCheckLogStr;
+        String[] dnsResponseStrs;
+        /** Keeps track of active dns pings.  Map is from pingID to index in mDnsList */
+        HashMap<Integer, Integer> idDnsMap = new HashMap<Integer, Integer>();
 
         @Override
         public void enter() {
-            dnsCheckSuccesses = 0;
-            dnsCheckTries = 0;
-            ids.clear();
-            InetAddress dns = mDnsPinger.getDns();
+            mDnsList = mDnsPinger.getDnsList();
+            int numDnses = mDnsList.size();
+            dnsCheckSuccesses = new int[numDnses];
+            dnsResponseStrs = new String[numDnses];
+            for (int i = 0; i < numDnses; i++)
+                dnsResponseStrs[i] = "";
+
             if (DBG) {
-                Slog.d(WWSM_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime());
                 dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ",
-                        mDnsPinger.getDns(), mConnectionInfo.getSSID());
+                        mDnsList, mConnectionInfo.getSSID());
+                Slog.d(WWSM_TAG, dnsCheckLogStr);
             }
 
+            idDnsMap.clear();
             for (int i=0; i < mNumDnsPings; i++) {
-                ids.add(mDnsPinger.pingDnsAsync(dns, mDnsPingTimeoutMs,
-                        DNS_INTRATEST_PING_INTERVAL * i));
+                for (int j = 0; j < numDnses; j++) {
+                    idDnsMap.put(mDnsPinger.pingDnsAsync(mDnsList.get(j), mDnsPingTimeoutMs,
+                            DNS_INTRATEST_PING_INTERVAL * i), j);
+                }
             }
         }
 
@@ -693,27 +701,24 @@
             int pingID = msg.arg1;
             int pingResponseTime = msg.arg2;
 
-            if (!ids.contains(pingID)) {
+            Integer dnsServerId = idDnsMap.get(pingID);
+            if (dnsServerId == null) {
                 Slog.w(WWSM_TAG, "Received a Dns response with unknown ID!");
                 return HANDLED;
             }
-            ids.remove(pingID);
-            dnsCheckTries++;
+
+            idDnsMap.remove(pingID);
             if (pingResponseTime >= 0)
-                dnsCheckSuccesses++;
+                dnsCheckSuccesses[dnsServerId]++;
 
             if (DBG) {
                 if (pingResponseTime >= 0) {
-                    dnsCheckLogStr += "|" + pingResponseTime;
+                    dnsResponseStrs[dnsServerId] += "|" + pingResponseTime;
                 } else {
-                    dnsCheckLogStr += "|x";
+                    dnsResponseStrs[dnsServerId] += "|x";
                 }
             }
 
-            if (VDBG) {
-                Slog.v(WWSM_TAG, dnsCheckLogStr);
-            }
-
             /**
              * After a full ping count, if we have more responses than this
              * cutoff, the outcome is success; else it is 'failure'.
@@ -723,10 +728,10 @@
              * Our final success count will be at least this big, so we're
              * guaranteed to succeed.
              */
-            if (dnsCheckSuccesses >= mMinDnsResponses) {
+            if (dnsCheckSuccesses[dnsServerId] >= mMinDnsResponses) {
                 // DNS CHECKS OK, NOW WALLED GARDEN
                 if (DBG) {
-                    Slog.d(WWSM_TAG, dnsCheckLogStr + "|  SUCCESS");
+                    Slog.d(WWSM_TAG, makeLogString() + "  SUCCESS");
                 }
 
                 if (!shouldCheckWalledGarden()) {
@@ -748,14 +753,9 @@
                 return HANDLED;
             }
 
-            /**
-             * Our final count will be at most the current count plus the
-             * remaining pings - we're guaranteed to fail.
-             */
-            int remainingChecks = mNumDnsPings - dnsCheckTries;
-            if (remainingChecks + dnsCheckSuccesses < mMinDnsResponses) {
+            if (idDnsMap.isEmpty()) {
                 if (DBG) {
-                    Slog.d(WWSM_TAG, dnsCheckLogStr + "|  FAILURE");
+                    Slog.d(WWSM_TAG, makeLogString() + "  FAILURE");
                 }
                 transitionTo(mDnsCheckFailureState);
                 return HANDLED;
@@ -764,12 +764,18 @@
             return HANDLED;
         }
 
+        private String makeLogString() {
+            String logStr = dnsCheckLogStr;
+            for (String respStr : dnsResponseStrs)
+                logStr += " [" + respStr + "]";
+            return logStr;
+        }
+
         @Override
         public void exit() {
             mDnsPinger.cancelPings();
         }
 
-
         private boolean shouldCheckWalledGarden() {
             if (!mWalledGardenTestEnabled) {
                 if (VDBG)
@@ -809,7 +815,8 @@
         int checkGuard = 0;
         Long lastCheckTime = null;
 
-        int curPingID = 0;
+        /** Keeps track of dns pings.  Map is from pingID to InetAddress used for ping */
+        HashMap<Integer, InetAddress> pingInfoMap = new HashMap<Integer, InetAddress>();
 
         @Override
         public void enter() {
@@ -817,7 +824,7 @@
             signalUnstable = false;
             checkGuard++;
             unstableSignalChecks = false;
-            curPingID = 0;
+            pingInfoMap.clear();
             triggerSingleDnsCheck();
         }
 
@@ -853,32 +860,37 @@
                         return HANDLED;
                     }
                     lastCheckTime = SystemClock.elapsedRealtime();
-                    curPingID = mDnsPinger.pingDnsAsync(mDnsPinger.getDns(),
-                            mDnsPingTimeoutMs, 0);
+                    pingInfoMap.clear();
+                    for (InetAddress curDns: mDnsPinger.getDnsList()) {
+                        pingInfoMap.put(mDnsPinger.pingDnsAsync(curDns, mDnsPingTimeoutMs, 0),
+                                curDns);
+                    }
                     return HANDLED;
                 case DnsPinger.DNS_PING_RESULT:
-                    if ((short) msg.arg1 != curPingID) {
-                        if (VDBG) {
-                            Slog.v(WWSM_TAG, "Received non-matching DnsPing w/ id: " +
-                                    msg.arg1);
-                        }
+                    InetAddress curDnsServer = pingInfoMap.get(msg.arg1);
+                    if (curDnsServer == null) {
                         return HANDLED;
                     }
+                    pingInfoMap.remove(msg.arg1);
                     int responseTime = msg.arg2;
                     if (responseTime >= 0) {
                         if (VDBG) {
-                            Slog.v(WWSM_TAG, "Ran a single DNS ping. Response time: "
-                                    + responseTime);
+                            Slog.v(WWSM_TAG, "Single DNS ping OK. Response time: "
+                                    + responseTime + " from DNS " + curDnsServer);
                         }
+                        pingInfoMap.clear();
 
                         checkGuard++;
                         unstableSignalChecks = false;
                         triggerSingleDnsCheck();
                     } else {
-                        if (DBG) {
-                            Slog.d(WWSM_TAG, "Single dns ping failure. Starting full checks.");
+                        if (pingInfoMap.isEmpty()) {
+                            if (DBG) {
+                                Slog.d(WWSM_TAG, "Single dns ping failure. All dns servers failed, "
+                                        + "starting full checks.");
+                            }
+                            transitionTo(mDnsCheckingState);
                         }
-                        transitionTo(mDnsCheckingState);
                     }
                     return HANDLED;
             }