Merge "get rid of uses of getISurfaceTexture"
diff --git a/Android.mk b/Android.mk
index c8576a0..b9cd7bf 100644
--- a/Android.mk
+++ b/Android.mk
@@ -69,6 +69,7 @@
 	core/java/android/app/IAlarmManager.aidl \
 	core/java/android/app/IBackupAgent.aidl \
 	core/java/android/app/IInstrumentationWatcher.aidl \
+	core/java/android/app/INotificationListener.aidl \
 	core/java/android/app/INotificationManager.aidl \
 	core/java/android/app/IProcessObserver.aidl \
 	core/java/android/app/ISearchManager.aidl \
diff --git a/api/current.txt b/api/current.txt
index 50438e6..d661daa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1174,6 +1174,7 @@
     field public static final int windowMinWidthMinor = 16843607; // 0x1010357
     field public static final int windowNoDisplay = 16843294; // 0x101021e
     field public static final int windowNoTitle = 16842838; // 0x1010056
+    field public static final int windowOverscan = 16843727; // 0x10103cf
     field public static final int windowShowAnimation = 16842934; // 0x10100b6
     field public static final int windowShowWallpaper = 16843410; // 0x1010292
     field public static final int windowSoftInputMode = 16843307; // 0x101022b
@@ -1693,6 +1694,7 @@
     field public static final int Theme_Black = 16973832; // 0x1030008
     field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
     field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
+    field public static final int Theme_Black_NoTitleBar_Overscan = 16974303; // 0x10301df
     field public static final int Theme_DeviceDefault = 16974120; // 0x1030128
     field public static final int Theme_DeviceDefault_Dialog = 16974126; // 0x103012e
     field public static final int Theme_DeviceDefault_DialogWhenLarge = 16974134; // 0x1030136
@@ -1711,9 +1713,11 @@
     field public static final int Theme_DeviceDefault_Light_Dialog_NoActionBar_MinWidth = 16974133; // 0x1030135
     field public static final int Theme_DeviceDefault_Light_NoActionBar = 16974124; // 0x103012c
     field public static final int Theme_DeviceDefault_Light_NoActionBar_Fullscreen = 16974125; // 0x103012d
+    field public static final int Theme_DeviceDefault_Light_NoActionBar_Overscan = 16974307; // 0x10301e3
     field public static final int Theme_DeviceDefault_Light_Panel = 16974139; // 0x103013b
     field public static final int Theme_DeviceDefault_NoActionBar = 16974121; // 0x1030129
     field public static final int Theme_DeviceDefault_NoActionBar_Fullscreen = 16974122; // 0x103012a
+    field public static final int Theme_DeviceDefault_NoActionBar_Overscan = 16974306; // 0x10301e2
     field public static final int Theme_DeviceDefault_Panel = 16974138; // 0x103013a
     field public static final int Theme_DeviceDefault_Wallpaper = 16974140; // 0x103013c
     field public static final int Theme_DeviceDefault_Wallpaper_NoTitleBar = 16974141; // 0x103013d
@@ -1736,9 +1740,11 @@
     field public static final int Theme_Holo_Light_Dialog_NoActionBar_MinWidth = 16973942; // 0x1030076
     field public static final int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0
     field public static final int Theme_Holo_Light_NoActionBar_Fullscreen = 16974065; // 0x10300f1
+    field public static final int Theme_Holo_Light_NoActionBar_Overscan = 16974305; // 0x10301e1
     field public static final int Theme_Holo_Light_Panel = 16973948; // 0x103007c
     field public static final int Theme_Holo_NoActionBar = 16973932; // 0x103006c
     field public static final int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d
+    field public static final int Theme_Holo_NoActionBar_Overscan = 16974304; // 0x10301e0
     field public static final int Theme_Holo_Panel = 16973947; // 0x103007b
     field public static final int Theme_Holo_Wallpaper = 16973949; // 0x103007d
     field public static final int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e
@@ -1746,12 +1752,14 @@
     field public static final int Theme_Light = 16973836; // 0x103000c
     field public static final int Theme_Light_NoTitleBar = 16973837; // 0x103000d
     field public static final int Theme_Light_NoTitleBar_Fullscreen = 16973838; // 0x103000e
+    field public static final int Theme_Light_NoTitleBar_Overscan = 16974302; // 0x10301de
     field public static final int Theme_Light_Panel = 16973914; // 0x103005a
     field public static final int Theme_Light_WallpaperSettings = 16973922; // 0x1030062
     field public static final int Theme_NoDisplay = 16973909; // 0x1030055
     field public static final int Theme_NoTitleBar = 16973830; // 0x1030006
     field public static final int Theme_NoTitleBar_Fullscreen = 16973831; // 0x1030007
     field public static final int Theme_NoTitleBar_OverlayActionModes = 16973930; // 0x103006a
+    field public static final int Theme_NoTitleBar_Overscan = 16974301; // 0x10301dd
     field public static final int Theme_Panel = 16973913; // 0x1030059
     field public static final int Theme_Translucent = 16973839; // 0x103000f
     field public static final int Theme_Translucent_NoTitleBar = 16973840; // 0x1030010
@@ -5840,6 +5848,8 @@
     field public static final java.lang.String ACTION_GTALK_SERVICE_CONNECTED = "android.intent.action.GTALK_CONNECTED";
     field public static final java.lang.String ACTION_GTALK_SERVICE_DISCONNECTED = "android.intent.action.GTALK_DISCONNECTED";
     field public static final java.lang.String ACTION_HEADSET_PLUG = "android.intent.action.HEADSET_PLUG";
+    field public static final java.lang.String ACTION_IDLE_MAINTENANCE_END = "android.intent.action.ACTION_IDLE_MAINTENANCE_END";
+    field public static final java.lang.String ACTION_IDLE_MAINTENANCE_START = "android.intent.action.ACTION_IDLE_MAINTENANCE_START";
     field public static final java.lang.String ACTION_INPUT_METHOD_CHANGED = "android.intent.action.INPUT_METHOD_CHANGED";
     field public static final java.lang.String ACTION_INSERT = "android.intent.action.INSERT";
     field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
@@ -11441,6 +11451,11 @@
     field public static final int MPEG4ProfileSimpleFBA = 128; // 0x80
     field public static final int MPEG4ProfileSimpleFace = 64; // 0x40
     field public static final int MPEG4ProfileSimpleScalable = 2; // 0x2
+    field public static final int VP8Level_Version0 = 1; // 0x1
+    field public static final int VP8Level_Version1 = 2; // 0x2
+    field public static final int VP8Level_Version2 = 4; // 0x4
+    field public static final int VP8Level_Version3 = 8; // 0x8
+    field public static final int VP8ProfileMain = 1; // 0x1
     field public int level;
     field public int profile;
   }
@@ -22961,9 +22976,14 @@
 
   public class EasyEditSpan implements android.text.ParcelableSpan {
     ctor public EasyEditSpan();
+    ctor public EasyEditSpan(android.app.PendingIntent);
+    ctor public EasyEditSpan(android.os.Parcel);
     method public int describeContents();
     method public int getSpanTypeId();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String EXTRA_TEXT_CHANGED_TYPE = "android.text.style.EXTRA_TEXT_CHANGED_TYPE";
+    field public static final int TEXT_DELETED = 1; // 0x1
+    field public static final int TEXT_MODIFIED = 2; // 0x2
   }
 
   public class ForegroundColorSpan extends android.text.style.CharacterStyle implements android.text.ParcelableSpan android.text.style.UpdateAppearance {
@@ -26245,6 +26265,7 @@
     field public static final int FLAG_IGNORE_CHEEK_PRESSES = 32768; // 0x8000
     field public static final int FLAG_KEEP_SCREEN_ON = 128; // 0x80
     field public static final int FLAG_LAYOUT_INSET_DECOR = 65536; // 0x10000
+    field public static final int FLAG_LAYOUT_IN_OVERSCAN = 33554432; // 0x2000000
     field public static final int FLAG_LAYOUT_IN_SCREEN = 256; // 0x100
     field public static final int FLAG_LAYOUT_NO_LIMITS = 512; // 0x200
     field public static final int FLAG_NOT_FOCUSABLE = 8; // 0x8
diff --git a/cmds/am/Android.mk b/cmds/am/Android.mk
index 8b321b3..f8350dc 100644
--- a/cmds/am/Android.mk
+++ b/cmds/am/Android.mk
@@ -1,31 +1,15 @@
 # Copyright 2008 The Android Open Source Project
 #
 LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
 
+include $(CLEAR_VARS)
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 LOCAL_MODULE := am
 include $(BUILD_JAVA_LIBRARY)
 
 include $(CLEAR_VARS)
-ALL_PREBUILT += $(TARGET_OUT)/bin/am
-$(TARGET_OUT)/bin/am : $(LOCAL_PATH)/am | $(ACP)
-	$(transform-prebuilt-to-target)
-
-NOTICE_FILE := NOTICE
-files_noticed := bin/am
-
-# Generate rules for a single file. The argument is the file path relative to
-# the installation root
-define make-notice-file
-
-$(TARGET_OUT_NOTICE_FILES)/src/$(1).txt: $(LOCAL_PATH)/$(NOTICE_FILE)
-	@echo Notice file: $$< -- $$@
-	@mkdir -p $$(dir $$@)
-	@cat $$< >> $$@
-
-$(TARGET_OUT_NOTICE_FILES)/hash-timestamp: $(TARGET_OUT_NOTICE_FILES)/src/$(1).txt
-
-endef
-
-$(foreach file,$(files_noticed),$(eval $(call make-notice-file,$(file))))
+LOCAL_MODULE := am
+LOCAL_SRC_FILES := am
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 3c1fbfe..9fa7dbb 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -26,7 +26,6 @@
 import android.app.Instrumentation;
 import android.app.UiAutomationConnection;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.IIntentReceiver;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
@@ -39,7 +38,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.util.AndroidException;
-import android.view.Display;
 import android.view.IWindowManager;
 
 import java.io.BufferedReader;
@@ -134,10 +132,6 @@
             runMonitor();
         } else if (op.equals("screen-compat")) {
             runScreenCompat();
-        } else if (op.equals("display-size")) {
-            runDisplaySize();
-        } else if (op.equals("display-density")) {
-            runDisplayDensity();
         } else if (op.equals("to-uri")) {
             runToUri(false);
         } else if (op.equals("to-intent-uri")) {
@@ -1201,82 +1195,6 @@
         } while (packageName != null);
     }
 
-    private void runDisplaySize() throws Exception {
-        String size = nextArgRequired();
-        int w, h;
-        if ("reset".equals(size)) {
-            w = h = -1;
-        } else {
-            int div = size.indexOf('x');
-            if (div <= 0 || div >= (size.length()-1)) {
-                System.err.println("Error: bad size " + size);
-                return;
-            }
-            String wstr = size.substring(0, div);
-            String hstr = size.substring(div+1);
-            try {
-                w = Integer.parseInt(wstr);
-                h = Integer.parseInt(hstr);
-            } catch (NumberFormatException e) {
-                System.err.println("Error: bad number " + e);
-                return;
-            }
-        }
-
-        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
-                Context.WINDOW_SERVICE));
-        if (wm == null) {
-            System.err.println(NO_SYSTEM_ERROR_CODE);
-            throw new AndroidException("Can't connect to window manager; is the system running?");
-        }
-
-        try {
-            if (w >= 0 && h >= 0) {
-                // TODO(multidisplay): For now Configuration only applies to main screen.
-                wm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
-            } else {
-                wm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
-            }
-        } catch (RemoteException e) {
-        }
-    }
-
-    private void runDisplayDensity() throws Exception {
-        String densityStr = nextArgRequired();
-        int density;
-        if ("reset".equals(densityStr)) {
-            density = -1;
-        } else {
-            try {
-                density = Integer.parseInt(densityStr);
-            } catch (NumberFormatException e) {
-                System.err.println("Error: bad number " + e);
-                return;
-            }
-            if (density < 72) {
-                System.err.println("Error: density must be >= 72");
-                return;
-            }
-        }
-
-        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
-                Context.WINDOW_SERVICE));
-        if (wm == null) {
-            System.err.println(NO_SYSTEM_ERROR_CODE);
-            throw new AndroidException("Can't connect to window manager; is the system running?");
-        }
-
-        try {
-            if (density > 0) {
-                // TODO(multidisplay): For now Configuration only applies to main screen.
-                wm.setForcedDisplayDensity(Display.DEFAULT_DISPLAY, density);
-            } else {
-                wm.clearForcedDisplayDensity(Display.DEFAULT_DISPLAY);
-            }
-        } catch (RemoteException e) {
-        }
-    }
-
     private void runToUri(boolean intentScheme) throws Exception {
         Intent intent = makeIntent(UserHandle.USER_CURRENT);
         System.out.println(intent.toUri(intentScheme ? Intent.URI_INTENT_SCHEME : 0));
@@ -1454,8 +1372,6 @@
                 "       am clear-debug-app\n" +
                 "       am monitor [--gdb <port>]\n" +
                 "       am screen-compat [on|off] <PACKAGE>\n" +
-                "       am display-size [reset|WxH]\n" +
-                "       am display-density [reset|DENSITY]\n" +
                 "       am to-uri [INTENT]\n" +
                 "       am to-intent-uri [INTENT]\n" +
                 "       am switch-user <USER_ID>\n" +
@@ -1524,17 +1440,13 @@
                 "am clear-debug-app: clear the previously set-debug-app.\n" +
                 "\n" +
                 "am bug-report: request bug report generation; will launch UI\n" +
-                "    when done to select where it should be delivered." +
+                "    when done to select where it should be delivered.\n" +
                 "\n" +
                 "am monitor: start monitoring for crashes or ANRs.\n" +
                 "    --gdb: start gdbserv on the given port at crash/ANR\n" +
                 "\n" +
                 "am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
                 "\n" +
-                "am display-size: override display size.\n" +
-                "\n" +
-                "am display-density: override display density.\n" +
-                "\n" +
                 "am to-uri: print the given Intent specification as a URI.\n" +
                 "\n" +
                 "am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
diff --git a/cmds/content/Android.mk b/cmds/content/Android.mk
index 88c46f2..9302e2f 100644
--- a/cmds/content/Android.mk
+++ b/cmds/content/Android.mk
@@ -1,33 +1,15 @@
 # Copyright 2012 The Android Open Source Project
 
 LOCAL_PATH:= $(call my-dir)
+
 include $(CLEAR_VARS)
-
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
 LOCAL_MODULE := content
-
 include $(BUILD_JAVA_LIBRARY)
 
 include $(CLEAR_VARS)
-ALL_PREBUILT += $(TARGET_OUT)/bin/content
-$(TARGET_OUT)/bin/content : $(LOCAL_PATH)/content | $(ACP)
-	$(transform-prebuilt-to-target)
-
-NOTICE_FILE := NOTICE
-files_noticed := bin/content
-
-# Generate rules for a single file. The argument is the file path relative to
-# the installation root
-define make-notice-file
-
-$(TARGET_OUT_NOTICE_FILES)/src/$(1).txt: $(LOCAL_PATH)/$(NOTICE_FILE)
-	@echo Notice file: $$< -- $$@
-	@mkdir -p $$(dir $$@)
-	@cat $$< >> $$@
-
-$(TARGET_OUT_NOTICE_FILES)/hash-timestamp: $(TARGET_OUT_NOTICE_FILES)/src/$(1).txt
-
-endef
-
-$(foreach file,$(files_noticed),$(eval $(call make-notice-file,$(file))))
+LOCAL_MODULE := content
+LOCAL_SRC_FILES := content
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
diff --git a/cmds/wm/Android.mk b/cmds/wm/Android.mk
new file mode 100644
index 0000000..3f3795f
--- /dev/null
+++ b/cmds/wm/Android.mk
@@ -0,0 +1,15 @@
+# Copyright 2013 The Android Open Source Project
+#
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_MODULE := wm
+include $(BUILD_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := wm
+LOCAL_SRC_FILES := wm
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_PREBUILT)
diff --git a/cmds/wm/MODULE_LICENSE_APACHE2 b/cmds/wm/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/cmds/wm/MODULE_LICENSE_APACHE2
diff --git a/cmds/wm/NOTICE b/cmds/wm/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/cmds/wm/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
new file mode 100644
index 0000000..f48764f
--- /dev/null
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -0,0 +1,241 @@
+/*
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+
+package com.android.commands.wm;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.AndroidException;
+import android.view.Display;
+import android.view.IWindowManager;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class Wm {
+
+    private IWindowManager mWm;
+    private String[] mArgs;
+    private int mNextArg;
+    private String mCurArgData;
+
+    // These are magic strings understood by the Eclipse plugin.
+    private static final String FATAL_ERROR_CODE = "Error type 1";
+    private static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
+    private static final String NO_CLASS_ERROR_CODE = "Error type 3";
+
+    /**
+     * Command-line entry point.
+     *
+     * @param args The command-line arguments
+     */
+    public static void main(String[] args) {
+        try {
+            (new Wm()).run(args);
+        } catch (IllegalArgumentException e) {
+            showUsage();
+            System.err.println("Error: " + e.getMessage());
+        } catch (Exception e) {
+            e.printStackTrace(System.err);
+            System.exit(1);
+        }
+    }
+
+    private void run(String[] args) throws Exception {
+        if (args.length < 1) {
+            showUsage();
+            return;
+        }
+
+        mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
+                        Context.WINDOW_SERVICE));
+        if (mWm == null) {
+            System.err.println(NO_SYSTEM_ERROR_CODE);
+            throw new AndroidException("Can't connect to window manager; is the system running?");
+        }
+
+        mArgs = args;
+        String op = args[0];
+        mNextArg = 1;
+
+        if (op.equals("size")) {
+            runDisplaySize();
+        } else if (op.equals("density")) {
+            runDisplayDensity();
+        } else if (op.equals("overscan")) {
+            runDisplayOverscan();
+        } else {
+            throw new IllegalArgumentException("Unknown command: " + op);
+        }
+    }
+
+    private void runDisplaySize() throws Exception {
+        String size = nextArgRequired();
+        int w, h;
+        if ("reset".equals(size)) {
+            w = h = -1;
+        } else {
+            int div = size.indexOf('x');
+            if (div <= 0 || div >= (size.length()-1)) {
+                System.err.println("Error: bad size " + size);
+                return;
+            }
+            String wstr = size.substring(0, div);
+            String hstr = size.substring(div+1);
+            try {
+                w = Integer.parseInt(wstr);
+                h = Integer.parseInt(hstr);
+            } catch (NumberFormatException e) {
+                System.err.println("Error: bad number " + e);
+                return;
+            }
+        }
+
+        try {
+            if (w >= 0 && h >= 0) {
+                // TODO(multidisplay): For now Configuration only applies to main screen.
+                mWm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
+            } else {
+                mWm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
+    private void runDisplayDensity() throws Exception {
+        String densityStr = nextArgRequired();
+        int density;
+        if ("reset".equals(densityStr)) {
+            density = -1;
+        } else {
+            try {
+                density = Integer.parseInt(densityStr);
+            } catch (NumberFormatException e) {
+                System.err.println("Error: bad number " + e);
+                return;
+            }
+            if (density < 72) {
+                System.err.println("Error: density must be >= 72");
+                return;
+            }
+        }
+
+        try {
+            if (density > 0) {
+                // TODO(multidisplay): For now Configuration only applies to main screen.
+                mWm.setForcedDisplayDensity(Display.DEFAULT_DISPLAY, density);
+            } else {
+                mWm.clearForcedDisplayDensity(Display.DEFAULT_DISPLAY);
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
+    private void runDisplayOverscan() throws Exception {
+        String overscanStr = nextArgRequired();
+        Rect rect = new Rect();
+        int density;
+        if ("reset".equals(overscanStr)) {
+            rect.set(0, 0, 0, 0);
+        } else {
+            final Pattern FLATTENED_PATTERN = Pattern.compile(
+                    "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
+            Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
+            if (!matcher.matches()) {
+                System.err.println("Error: bad rectangle arg: " + overscanStr);
+                return;
+            }
+            rect.left = Integer.parseInt(matcher.group(1));
+            rect.top = Integer.parseInt(matcher.group(2));
+            rect.right = Integer.parseInt(matcher.group(3));
+            rect.bottom = Integer.parseInt(matcher.group(4));
+        }
+
+        try {
+            mWm.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, rect.bottom);
+        } catch (RemoteException e) {
+        }
+    }
+
+    private String nextOption() {
+        if (mCurArgData != null) {
+            String prev = mArgs[mNextArg - 1];
+            throw new IllegalArgumentException("No argument expected after \"" + prev + "\"");
+        }
+        if (mNextArg >= mArgs.length) {
+            return null;
+        }
+        String arg = mArgs[mNextArg];
+        if (!arg.startsWith("-")) {
+            return null;
+        }
+        mNextArg++;
+        if (arg.equals("--")) {
+            return null;
+        }
+        if (arg.length() > 1 && arg.charAt(1) != '-') {
+            if (arg.length() > 2) {
+                mCurArgData = arg.substring(2);
+                return arg.substring(0, 2);
+            } else {
+                mCurArgData = null;
+                return arg;
+            }
+        }
+        mCurArgData = null;
+        return arg;
+    }
+
+    private String nextArg() {
+        if (mCurArgData != null) {
+            String arg = mCurArgData;
+            mCurArgData = null;
+            return arg;
+        } else if (mNextArg < mArgs.length) {
+            return mArgs[mNextArg++];
+        } else {
+            return null;
+        }
+    }
+
+    private String nextArgRequired() {
+        String arg = nextArg();
+        if (arg == null) {
+            String prev = mArgs[mNextArg - 1];
+            throw new IllegalArgumentException("Argument expected after \"" + prev + "\"");
+        }
+        return arg;
+    }
+
+    private static void showUsage() {
+        System.err.println(
+                "usage: wm [subcommand] [options]\n" +
+                "       wm size [reset|WxH]\n" +
+                "       wm density [reset|DENSITY]\n" +
+                "       wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
+                "\n" +
+                "wm size: override display size.\n" +
+                "\n" +
+                "wm density: override display density.\n" +
+                "\n" +
+                "wm overscan: set overscan area for display.\n"
+                );
+    }
+}
diff --git a/cmds/wm/wm b/cmds/wm/wm
new file mode 100755
index 0000000..f7a5bc7
--- /dev/null
+++ b/cmds/wm/wm
@@ -0,0 +1,6 @@
+# Script to start "wm" on the device, which has a very rudimentary
+# shell.
+#
+base=/system
+export CLASSPATH=$base/framework/wm.jar
+exec app_process $base/bin com.android.commands.wm.Wm "$@"
diff --git a/core/java/android/app/INotificationListener.aidl b/core/java/android/app/INotificationListener.aidl
new file mode 100644
index 0000000..f010a2a
--- /dev/null
+++ b/core/java/android/app/INotificationListener.aidl
@@ -0,0 +1,26 @@
+/**
+ * Copyright (c) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import com.android.internal.statusbar.StatusBarNotification;
+
+/** @hide */
+oneway interface INotificationListener
+{
+    void onNotificationPosted(in StatusBarNotification notification);
+    void onNotificationRemoved(in StatusBarNotification notification);
+}
\ No newline at end of file
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 1f4c81d..14bcc0d 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -17,6 +17,7 @@
 
 package android.app;
 
+import android.app.INotificationListener;
 import android.app.ITransientNotification;
 import android.app.Notification;
 import android.content.Intent;
@@ -39,5 +40,8 @@
 
     StatusBarNotification[] getActiveNotifications(String callingPkg);
     StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count);
+
+    void registerListener(in INotificationListener listener, int userid);
+    void unregisterListener(in INotificationListener listener, int userid);
 }
 
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4a0ee48..ebca041 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -448,7 +448,7 @@
      * Structure to encapsulate an "action", including title and icon, that can be attached to a Notification.
      * @hide
      */
-    private static class Action implements Parcelable {
+    public static class Action implements Parcelable {
         public int icon;
         public CharSequence title;
         public PendingIntent actionIntent;
@@ -500,7 +500,10 @@
         };
     }
 
-    private Action[] actions;
+    /**
+     * @hide
+     */
+    public Action[] actions;
 
     /**
      * Constructs a Notification object with default values.
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 2897ee0..37804e9 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -41,7 +41,7 @@
  * you are granting it the right to perform the operation you have specified
  * as if the other application was yourself (with the same permissions and
  * identity).  As such, you should be careful about how you build the PendingIntent:
- * often, for example, the base Intent you supply will have the component
+ * almost always, for example, the base Intent you supply should have the component
  * name explicitly set to one of your own components, to ensure it is ultimately
  * sent there and nowhere else.
  *
@@ -200,6 +200,11 @@
      * existing activity, so you must use the {@link Intent#FLAG_ACTIVITY_NEW_TASK
      * Intent.FLAG_ACTIVITY_NEW_TASK} launch flag in the Intent.
      *
+     * <p class="note">For security reasons, the {@link android.content.Intent}
+     * you supply here should almost always be an <em>explicit intent</em>,
+     * that is specify an explicit component to be delivered to through
+     * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p>
+     *
      * @param context The Context in which this PendingIntent should start
      * the activity.
      * @param requestCode Private request code for the sender (currently
@@ -227,6 +232,11 @@
      * existing activity, so you must use the {@link Intent#FLAG_ACTIVITY_NEW_TASK
      * Intent.FLAG_ACTIVITY_NEW_TASK} launch flag in the Intent.
      *
+     * <p class="note">For security reasons, the {@link android.content.Intent}
+     * you supply here should almost always be an <em>explicit intent</em>,
+     * that is specify an explicit component to be delivered to through
+     * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p>
+     *
      * @param context The Context in which this PendingIntent should start
      * the activity.
      * @param requestCode Private request code for the sender (currently
@@ -313,6 +323,11 @@
      * UI the user actually sees when the intents are started.
      * </p>
      *
+     * <p class="note">For security reasons, the {@link android.content.Intent} objects
+     * you supply here should almost always be <em>explicit intents</em>,
+     * that is specify an explicit component to be delivered to through
+     * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p>
+     *
      * @param context The Context in which this PendingIntent should start
      * the activity.
      * @param requestCode Private request code for the sender (currently
@@ -359,6 +374,11 @@
      * UI the user actually sees when the intents are started.
      * </p>
      *
+     * <p class="note">For security reasons, the {@link android.content.Intent} objects
+     * you supply here should almost always be <em>explicit intents</em>,
+     * that is specify an explicit component to be delivered to through
+     * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p>
+     *
      * @param context The Context in which this PendingIntent should start
      * the activity.
      * @param requestCode Private request code for the sender (currently
@@ -423,6 +443,11 @@
      * Retrieve a PendingIntent that will perform a broadcast, like calling
      * {@link Context#sendBroadcast(Intent) Context.sendBroadcast()}.
      *
+     * <p class="note">For security reasons, the {@link android.content.Intent}
+     * you supply here should almost always be an <em>explicit intent</em>,
+     * that is specify an explicit component to be delivered to through
+     * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p>
+     *
      * @param context The Context in which this PendingIntent should perform
      * the broadcast.
      * @param requestCode Private request code for the sender (currently
@@ -473,6 +498,11 @@
      * {@link Context#startService Context.startService()}.  The start
      * arguments given to the service will come from the extras of the Intent.
      *
+     * <p class="note">For security reasons, the {@link android.content.Intent}
+     * you supply here should almost always be an <em>explicit intent</em>,
+     * that is specify an explicit component to be delivered to through
+     * {@link Intent#setClass(android.content.Context, Class)} Intent.setClass</p>
+     *
      * @param context The Context in which this PendingIntent should start
      * the service.
      * @param requestCode Private request code for the sender (currently
@@ -707,6 +737,15 @@
      * sending the Intent.  The returned string is supplied by the system, so
      * that an application can not spoof its package.
      *
+     * <p class="note">Be careful about how you use this.  All this tells you is
+     * who created the PendingIntent.  It does <strong>not</strong> tell you who
+     * handed the PendingIntent to you: that is, PendingIntent objects are intended to be
+     * passed between applications, so the PendingIntent you receive from an application
+     * could actually be one it received from another application, meaning the result
+     * you get here will identify the original application.  Because of this, you should
+     * only use this information to identify who you expect to be interacting with
+     * through a {@link #send} call, not who gave you the PendingIntent.</p>
+     *
      * @return The package name of the PendingIntent, or null if there is
      * none associated with it.
      */
@@ -726,6 +765,15 @@
      * sending the Intent.  The returned integer is supplied by the system, so
      * that an application can not spoof its uid.
      *
+     * <p class="note">Be careful about how you use this.  All this tells you is
+     * who created the PendingIntent.  It does <strong>not</strong> tell you who
+     * handed the PendingIntent to you: that is, PendingIntent objects are intended to be
+     * passed between applications, so the PendingIntent you receive from an application
+     * could actually be one it received from another application, meaning the result
+     * you get here will identify the original application.  Because of this, you should
+     * only use this information to identify who you expect to be interacting with
+     * through a {@link #send} call, not who gave you the PendingIntent.</p>
+     *
      * @return The uid of the PendingIntent, or -1 if there is
      * none associated with it.
      */
@@ -747,6 +795,15 @@
      * {@link android.os.Process#myUserHandle() Process.myUserHandle()} for
      * more explanation of user handles.
      *
+     * <p class="note">Be careful about how you use this.  All this tells you is
+     * who created the PendingIntent.  It does <strong>not</strong> tell you who
+     * handed the PendingIntent to you: that is, PendingIntent objects are intended to be
+     * passed between applications, so the PendingIntent you receive from an application
+     * could actually be one it received from another application, meaning the result
+     * you get here will identify the original application.  Because of this, you should
+     * only use this information to identify who you expect to be interacting with
+     * through a {@link #send} call, not who gave you the PendingIntent.</p>
+     *
      * @return The user handle of the PendingIntent, or null if there is
      * none associated with it.
      */
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index fa3bf4d..a470e70 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -31,6 +31,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.DisplayMetrics;
+import android.util.Log;
 import android.util.TypedValue;
 import android.widget.RemoteViews;
 import android.widget.RemoteViews.OnClickHandler;
@@ -55,38 +56,39 @@
 
     Context mContext;
     String mPackageName;
+    Handler mHandler;
+    int mHostId;
+    Callbacks mCallbacks = new Callbacks();
+    final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>();
+    private OnClickHandler mOnClickHandler;
 
     class Callbacks extends IAppWidgetHost.Stub {
-        public void updateAppWidget(int appWidgetId, RemoteViews views) {
+        public void updateAppWidget(int appWidgetId, RemoteViews views, int userId) {
             if (isLocalBinder() && views != null) {
                 views = views.clone();
-                views.setUser(mUser);
+                views.setUser(new UserHandle(userId));
             }
-            Message msg = mHandler.obtainMessage(HANDLE_UPDATE);
-            msg.arg1 = appWidgetId;
-            msg.obj = views;
+            Message msg = mHandler.obtainMessage(HANDLE_UPDATE, appWidgetId, userId, views);
             msg.sendToTarget();
         }
 
-        public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) {
+        public void providerChanged(int appWidgetId, AppWidgetProviderInfo info, int userId) {
             if (isLocalBinder() && info != null) {
                 info = info.clone();
             }
-            Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED);
-            msg.arg1 = appWidgetId;
-            msg.obj = info;
+            Message msg = mHandler.obtainMessage(HANDLE_PROVIDER_CHANGED,
+                    appWidgetId, userId, info);
             msg.sendToTarget();
         }
 
-        public void providersChanged() {
-            Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED);
+        public void providersChanged(int userId) {
+            Message msg = mHandler.obtainMessage(HANDLE_PROVIDERS_CHANGED, userId, 0);
             msg.sendToTarget();
         }
 
-        public void viewDataChanged(int appWidgetId, int viewId) {
-            Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED);
-            msg.arg1 = appWidgetId;
-            msg.arg2 = viewId;
+        public void viewDataChanged(int appWidgetId, int viewId, int userId) {
+            Message msg = mHandler.obtainMessage(HANDLE_VIEW_DATA_CHANGED,
+                    appWidgetId, viewId, userId);
             msg.sendToTarget();
         }
     }
@@ -99,7 +101,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case HANDLE_UPDATE: {
-                    updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj);
+                    updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj, msg.arg2);
                     break;
                 }
                 case HANDLE_PROVIDER_CHANGED: {
@@ -107,26 +109,17 @@
                     break;
                 }
                 case HANDLE_PROVIDERS_CHANGED: {
-                    onProvidersChanged();
+                    onProvidersChanged(msg.arg1);
                     break;
                 }
                 case HANDLE_VIEW_DATA_CHANGED: {
-                    viewDataChanged(msg.arg1, msg.arg2);
+                    viewDataChanged(msg.arg1, msg.arg2, (Integer) msg.obj);
                     break;
                 }
             }
         }
     }
 
-    Handler mHandler;
-
-    int mHostId;
-    Callbacks mCallbacks = new Callbacks();
-    final HashMap<Integer,AppWidgetHostView> mViews = new HashMap<Integer, AppWidgetHostView>();
-    private OnClickHandler mOnClickHandler;
-    // Optionally set by lockscreen
-    private UserHandle mUser;
-
     public AppWidgetHost(Context context, int hostId) {
         this(context, hostId, null, context.getMainLooper());
     }
@@ -140,14 +133,9 @@
         mOnClickHandler = handler;
         mHandler = new UpdateHandler(looper);
         mDisplayMetrics = context.getResources().getDisplayMetrics();
-        mUser = Process.myUserHandle();
         bindService();
     }
 
-    /** @hide */
-    public void setUserId(int userId) {
-        mUser = new UserHandle(userId);
-    }
 
     private static void bindService() {
         synchronized (sServiceLock) {
@@ -163,23 +151,15 @@
      * becomes visible, i.e. from onStart() in your Activity.
      */
     public void startListening() {
-        startListeningAsUser(UserHandle.myUserId());
-    }
-
-    /**
-     * Start receiving onAppWidgetChanged calls for your AppWidgets.  Call this when your activity
-     * becomes visible, i.e. from onStart() in your Activity.
-     * @hide
-     */
-    public void startListeningAsUser(int userId) {
         int[] updatedIds;
         ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
 
+        final int userId = mContext.getUserId();
         try {
             if (mPackageName == null) {
                 mPackageName = mContext.getPackageName();
             }
-            updatedIds = sService.startListeningAsUser(
+            updatedIds = sService.startListening(
                     mCallbacks, mPackageName, mHostId, updatedViews, userId);
         }
         catch (RemoteException e) {
@@ -191,7 +171,7 @@
             if (updatedViews.get(i) != null) {
                 updatedViews.get(i).setUser(new UserHandle(userId));
             }
-            updateAppWidgetView(updatedIds[i], updatedViews.get(i));
+            updateAppWidgetView(updatedIds[i], updatedViews.get(i), userId);
         }
     }
 
@@ -201,26 +181,14 @@
      */
     public void stopListening() {
         try {
-            sService.stopListeningAsUser(mHostId, UserHandle.myUserId());
+            sService.stopListening(mHostId, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
-    }
 
-    /**
-     * Stop receiving onAppWidgetChanged calls for your AppWidgets.  Call this when your activity is
-     * no longer visible, i.e. from onStop() in your Activity.
-     * @hide
-     */
-    public void stopListeningAsUser(int userId) {
-        try {
-            sService.stopListeningAsUser(mHostId, userId);
-        }
-        catch (RemoteException e) {
-            throw new RuntimeException("system server dead?", e);
-        }
-        // Also clear the views
+        // This is here because keyguard needs it since it'll be switching users after this call.
+        // If it turns out other apps need to call this often, we should re-think how this works.
         clearViews();
     }
 
@@ -230,11 +198,12 @@
      * @return a appWidgetId
      */
     public int allocateAppWidgetId() {
+
         try {
             if (mPackageName == null) {
                 mPackageName = mContext.getPackageName();
             }
-            return sService.allocateAppWidgetId(mPackageName, mHostId);
+            return sService.allocateAppWidgetId(mPackageName, mHostId, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -247,7 +216,7 @@
      * @return a appWidgetId
      * @hide
      */
-    public static int allocateAppWidgetIdForSystem(int hostId) {
+    public static int allocateAppWidgetIdForSystem(int hostId, int userId) {
         checkCallerIsSystem();
         try {
             if (sService == null) {
@@ -256,7 +225,7 @@
             Context systemContext =
                     (Context) ActivityThread.currentActivityThread().getSystemContext();
             String packageName = systemContext.getPackageName();
-            return sService.allocateAppWidgetId(packageName, hostId);
+            return sService.allocateAppWidgetId(packageName, hostId, userId);
         } catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
@@ -272,7 +241,7 @@
             if (sService == null) {
                 bindService();
             }
-            return sService.getAppWidgetIdsForHost(mHostId);
+            return sService.getAppWidgetIdsForHost(mHostId, mContext.getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
@@ -297,7 +266,7 @@
         synchronized (mViews) {
             mViews.remove(appWidgetId);
             try {
-                sService.deleteAppWidgetId(appWidgetId);
+                sService.deleteAppWidgetId(appWidgetId, mContext.getUserId());
             }
             catch (RemoteException e) {
                 throw new RuntimeException("system server dead?", e);
@@ -309,13 +278,13 @@
      * Stop listening to changes for this AppWidget.
      * @hide
      */
-    public static void deleteAppWidgetIdForSystem(int appWidgetId) {
+    public static void deleteAppWidgetIdForSystem(int appWidgetId, int userId) {
         checkCallerIsSystem();
         try {
             if (sService == null) {
                 bindService();
             }
-            sService.deleteAppWidgetId(appWidgetId);
+            sService.deleteAppWidgetId(appWidgetId, userId);
         } catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
@@ -331,7 +300,7 @@
      */
     public void deleteHost() {
         try {
-            sService.deleteHost(mHostId);
+            sService.deleteHost(mHostId, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -347,8 +316,16 @@
      * </ul>
      */
     public static void deleteAllHosts() {
+        deleteAllHosts(UserHandle.myUserId());
+    }
+
+    /**
+     * Private method containing a userId
+     * @hide
+     */
+    public static void deleteAllHosts(int userId) {
         try {
-            sService.deleteAllHosts();
+            sService.deleteAllHosts(userId);
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -361,8 +338,9 @@
      */
     public final AppWidgetHostView createView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
+        final int userId = context.getUserId();
         AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget);
-        view.setUserId(mUser.getIdentifier());
+        view.setUserId(userId);
         view.setOnClickHandler(mOnClickHandler);
         view.setAppWidget(appWidgetId, appWidget);
         synchronized (mViews) {
@@ -370,9 +348,9 @@
         }
         RemoteViews views;
         try {
-            views = sService.getAppWidgetViews(appWidgetId);
+            views = sService.getAppWidgetViews(appWidgetId, userId);
             if (views != null) {
-                views.setUser(mUser);
+                views.setUser(new UserHandle(mContext.getUserId()));
             }
         } catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -422,10 +400,20 @@
      * are added, updated or removed, or widget components are enabled or disabled.)
      */
     protected void onProvidersChanged() {
-        // Do nothing
+        onProvidersChanged(mContext.getUserId());
     }
 
-    void updateAppWidgetView(int appWidgetId, RemoteViews views) {
+    /**
+     * Private method containing a userId
+     * @hide
+     */
+    protected void onProvidersChanged(int userId) {
+        checkUserMatch(userId);
+        // Does nothing
+    }
+
+    void updateAppWidgetView(int appWidgetId, RemoteViews views, int userId) {
+        checkUserMatch(userId);
         AppWidgetHostView v;
         synchronized (mViews) {
             v = mViews.get(appWidgetId);
@@ -435,7 +423,8 @@
         }
     }
 
-    void viewDataChanged(int appWidgetId, int viewId) {
+    void viewDataChanged(int appWidgetId, int viewId, int userId) {
+        checkUserMatch(userId);
         AppWidgetHostView v;
         synchronized (mViews) {
             v = mViews.get(appWidgetId);
@@ -445,6 +434,16 @@
         }
     }
 
+    // Ensure that the userId passed to us agrees with the one associated with this instance
+    // of AppWidgetHost.
+    // TODO: This should be removed in production code.
+    private void checkUserMatch(int userId) {
+        if (userId != mContext.getUserId()) {
+            throw new IllegalStateException(
+                "User ids don't match, userId=" + userId + ", mUserId=" + mContext.getUserId());
+        }
+    }
+
     /**
      * Clear the list of Views that have been created by this AppWidgetHost.
      */
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 6b1c3e2..e68d23a 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -16,6 +16,7 @@
 
 package android.appwidget;
 
+import android.app.ActivityManagerNative;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -268,8 +269,8 @@
     /**
      * Sent when the custom extras for an AppWidget change.
      *
-     * @see AppWidgetProvider#onAppWidgetOptionsChanged 
-     *      AppWidgetProvider.onAppWidgetOptionsChanged(Context context, 
+     * @see AppWidgetProvider#onAppWidgetOptionsChanged
+     *      AppWidgetProvider.onAppWidgetOptionsChanged(Context context,
      *      AppWidgetManager appWidgetManager, int appWidgetId, Bundle newExtras)
      */
     public static final String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS";
@@ -352,7 +353,7 @@
      * It is okay to call this method both inside an {@link #ACTION_APPWIDGET_UPDATE} broadcast,
      * and outside of the handler.
      * This method will only work when called from the uid that owns the AppWidget provider.
-     * 
+     *
      * <p>
      * The total Bitmap memory used by the RemoteViews object cannot exceed that required to
      * fill the screen 1.5 times, ie. (screen width x screen height x 4 x 1.5) bytes.
@@ -362,7 +363,7 @@
      */
     public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
         try {
-            sService.updateAppWidgetIds(appWidgetIds, views);
+            sService.updateAppWidgetIds(appWidgetIds, views, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -382,7 +383,7 @@
      */
     public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
         try {
-            sService.updateAppWidgetOptions(appWidgetId, options);
+            sService.updateAppWidgetOptions(appWidgetId, options, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -402,7 +403,7 @@
      */
     public Bundle getAppWidgetOptions(int appWidgetId) {
         try {
-            return sService.getAppWidgetOptions(appWidgetId);
+            return sService.getAppWidgetOptions(appWidgetId, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -436,7 +437,7 @@
      * Perform an incremental update or command on the widget(s) specified by appWidgetIds.
      *
      * This update  differs from {@link #updateAppWidget(int[], RemoteViews)} in that the
-     * RemoteViews object which is passed is understood to be an incomplete representation of the 
+     * RemoteViews object which is passed is understood to be an incomplete representation of the
      * widget, and hence does not replace the cached representation of the widget. As of API
      * level 17, the new properties set within the views objects will be appended to the cached
      * representation of the widget, and hence will persist.
@@ -458,7 +459,7 @@
      */
     public void partiallyUpdateAppWidget(int[] appWidgetIds, RemoteViews views) {
         try {
-            sService.partiallyUpdateAppWidgetIds(appWidgetIds, views);
+            sService.partiallyUpdateAppWidgetIds(appWidgetIds, views, mContext.getUserId());
         } catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
         }
@@ -507,7 +508,7 @@
      */
     public void updateAppWidget(ComponentName provider, RemoteViews views) {
         try {
-            sService.updateAppWidgetProvider(provider, views);
+            sService.updateAppWidgetProvider(provider, views, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -523,7 +524,7 @@
      */
     public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
         try {
-            sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId);
+            sService.notifyAppWidgetViewDataChanged(appWidgetIds, viewId, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -557,7 +558,8 @@
      */
     public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
         try {
-            List<AppWidgetProviderInfo> providers = sService.getInstalledProviders(categoryFilter);
+            List<AppWidgetProviderInfo> providers = sService.getInstalledProviders(categoryFilter,
+                    mContext.getUserId());
             for (AppWidgetProviderInfo info : providers) {
                 // Converting complex to dp.
                 info.minWidth =
@@ -584,7 +586,8 @@
      */
     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
         try {
-            AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId);
+            AppWidgetProviderInfo info = sService.getAppWidgetInfo(appWidgetId,
+                    mContext.getUserId());
             if (info != null) {
                 // Converting complex to dp.
                 info.minWidth =
@@ -617,7 +620,7 @@
      */
     public void bindAppWidgetId(int appWidgetId, ComponentName provider) {
         try {
-            sService.bindAppWidgetId(appWidgetId, provider, null);
+            sService.bindAppWidgetId(appWidgetId, provider, null, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -641,7 +644,7 @@
      */
     public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options) {
         try {
-            sService.bindAppWidgetId(appWidgetId, provider, options);
+            sService.bindAppWidgetId(appWidgetId, provider, options, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -667,7 +670,7 @@
         }
         try {
             return sService.bindAppWidgetIdIfAllowed(
-                    mContext.getPackageName(), appWidgetId, provider, null);
+                    mContext.getPackageName(), appWidgetId, provider, null, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -696,8 +699,8 @@
             return false;
         }
         try {
-            return sService.bindAppWidgetIdIfAllowed(
-                    mContext.getPackageName(), appWidgetId, provider, options);
+            return sService.bindAppWidgetIdIfAllowed(mContext.getPackageName(), appWidgetId,
+                    provider, options, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -715,7 +718,7 @@
      */
     public boolean hasBindAppWidgetPermission(String packageName) {
         try {
-            return sService.hasBindAppWidgetPermission(packageName);
+            return sService.hasBindAppWidgetPermission(packageName, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -733,7 +736,7 @@
      */
     public void setBindAppWidgetPermission(String packageName, boolean permission) {
         try {
-            sService.setBindAppWidgetPermission(packageName, permission);
+            sService.setBindAppWidgetPermission(packageName, permission, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
@@ -794,7 +797,7 @@
      */
     public int[] getAppWidgetIds(ComponentName provider) {
         try {
-            return sService.getAppWidgetIds(provider);
+            return sService.getAppWidgetIds(provider, mContext.getUserId());
         }
         catch (RemoteException e) {
             throw new RuntimeException("system server dead?", e);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 90ee0ac..e9b800d 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -871,6 +871,18 @@
      * their responsibility to close it when done.  That is, the implementation
      * of this method should create a new ParcelFileDescriptor for each call.
      *
+     * <p class="note">For use in Intents, you will want to implement {@link #getType}
+     * to return the appropriate MIME type for the data returned here with
+     * the same URI.  This will allow intent resolution to automatically determine the data MIME
+     * type and select the appropriate matching targets as part of its operation.</p>
+     *
+     * <p class="note">For better interoperability with other applications, it is recommended
+     * that for any URIs that can be opened, you also support queries on them
+     * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+     * You may also want to support other common columns if you have additional meta-data
+     * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+     * in {@link android.provider.MediaStore.MediaColumns}.</p>
+     *
      * @param uri The URI whose file is to be opened.
      * @param mode Access mode for the file.  May be "r" for read-only access,
      * "rw" for read and write access, or "rwt" for read and write access
@@ -886,6 +898,7 @@
      *
      * @see #openAssetFile(Uri, String)
      * @see #openFileHelper(Uri, String)
+     * @see #getType(android.net.Uri)
      */
     public ParcelFileDescriptor openFile(Uri uri, String mode)
             throws FileNotFoundException {
@@ -913,6 +926,15 @@
      * {@link AssetFileDescriptor#UNKNOWN_LENGTH} to be compatible with
      * applications that can not handle sub-sections of files.</p>
      *
+     * <p class="note">For use in Intents, you will want to implement {@link #getType}
+     * to return the appropriate MIME type for the data returned here with
+     * the same URI.  This will allow intent resolution to automatically determine the data MIME
+     * type and select the appropriate matching targets as part of its operation.</p>
+     *
+     * <p class="note">For better interoperability with other applications, it is recommended
+     * that for any URIs that can be opened, you also support queries on them
+     * containing at least the columns specified by {@link android.provider.OpenableColumns}.</p>
+     *
      * @param uri The URI whose file is to be opened.
      * @param mode Access mode for the file.  May be "r" for read-only access,
      * "w" for write-only access (erasing whatever data is currently in
@@ -930,6 +952,7 @@
      * 
      * @see #openFile(Uri, String)
      * @see #openFileHelper(Uri, String)
+     * @see #getType(android.net.Uri)
      */
     public AssetFileDescriptor openAssetFile(Uri uri, String mode)
             throws FileNotFoundException {
@@ -1009,12 +1032,19 @@
      * perform data conversions to generate data of the desired type.
      *
      * <p>The default implementation compares the given mimeType against the
-     * result of {@link #getType(Uri)} and, if the match, simple calls
+     * result of {@link #getType(Uri)} and, if they match, simply calls
      * {@link #openAssetFile(Uri, String)}.
      *
      * <p>See {@link ClipData} for examples of the use and implementation
      * of this method.
      *
+     * <p class="note">For better interoperability with other applications, it is recommended
+     * that for any URIs that can be opened, you also support queries on them
+     * containing at least the columns specified by {@link android.provider.OpenableColumns}.
+     * You may also want to support other common columns if you have additional meta-data
+     * to supply, such as {@link android.provider.MediaStore.MediaColumns#DATE_ADDED}
+     * in {@link android.provider.MediaStore.MediaColumns}.</p>
+     *
      * @param uri The data in the content provider being queried.
      * @param mimeTypeFilter The type of data the client desires.  May be
      * a pattern, such as *\/*, if the caller does not have specific type
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c964af4..8a9eed2 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2691,6 +2691,14 @@
             throws PackageManager.NameNotFoundException;
 
     /**
+     * Get the userId associated with this context
+     * @return user id
+     *
+     * @hide
+     */
+    public abstract int getUserId();
+
+    /**
      * Return a new Context object for the current Context but whose resources
      * are adjusted to match the given Configuration.  Each call to this method
      * returns a new instance of a Context object; Context objects are not
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 736dd99..2f1bf8c 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -623,6 +623,12 @@
         return mBase.createPackageContextAsUser(packageName, flags, user);
     }
 
+    /** @hide */
+    @Override
+    public int getUserId() {
+        return mBase.getUserId();
+    }
+
     @Override
     public Context createConfigurationContext(Configuration overrideConfiguration) {
         return mBase.createConfigurationContext(overrideConfiguration);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 434946c..f8ff8d1 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2319,6 +2319,61 @@
             "android.intent.action.DOCK_EVENT";
 
     /**
+     * Broadcast Action: A broadcast when idle maintenance can be started.
+     * This means that the user is not interacting with the device and is
+     * not expected to do so soon. Typical use of the idle maintenance is
+     * to perform somehow expensive tasks that can be postponed at a moment
+     * when they will not degrade user experience.
+     * <p>
+     * <p class="note">In order to keep the device responsive in case of an
+     * unexpected user interaction, implementations of a maintenance task
+     * should be interruptible. In such a scenario a broadcast with action
+     * {@link #ACTION_IDLE_MAINTENANCE_END} will be sent. In other words, you
+     * should not do the maintenance work in
+     * {@link BroadcastReceiver#onReceive(Context, Intent)}, rather start a
+     * maintenance service by {@link Context#startService(Intent)}. Also
+     * you should hold a wake lock while your maintenance service is running
+     * to prevent the device going to sleep.
+     * </p>
+     * <p>
+     * <p class="note">This is a protected intent that can only be sent by
+     * the system.
+     * </p>
+     *
+     * @see #ACTION_IDLE_MAINTENANCE_END
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_IDLE_MAINTENANCE_START =
+            "android.intent.action.ACTION_IDLE_MAINTENANCE_START";
+
+    /**
+     * Broadcast Action:  A broadcast when idle maintenance should be stopped.
+     * This means that the user was not interacting with the device as a result
+     * of which a broadcast with action {@link #ACTION_IDLE_MAINTENANCE_START}
+     * was sent and now the user started interacting with the device. Typical
+     * use of the idle maintenance is to perform somehow expensive tasks that
+     * can be postponed at a moment when they will not degrade user experience.
+     * <p>
+     * <p class="note">In order to keep the device responsive in case of an
+     * unexpected user interaction, implementations of a maintenance task
+     * should be interruptible. Hence, on receiving a broadcast with this
+     * action, the maintenance task should be interrupted as soon as possible.
+     * In other words, you should not do the maintenance work in
+     * {@link BroadcastReceiver#onReceive(Context, Intent)}, rather stop the
+     * maintenance service that was started on receiving of
+     * {@link #ACTION_IDLE_MAINTENANCE_START}.Also you should release the wake
+     * lock you acquired when your maintenance service started.
+     * </p>
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see #ACTION_IDLE_MAINTENANCE_START
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_IDLE_MAINTENANCE_END =
+            "android.intent.action.ACTION_IDLE_MAINTENANCE_END";
+
+    /**
      * Broadcast Action: a remote intent is to be broadcasted.
      *
      * A remote intent is used for remote RPC between devices. The remote intent
@@ -2489,6 +2544,14 @@
     public static final String ACTION_QUICK_CLOCK =
             "android.intent.action.QUICK_CLOCK";
 
+    /**
+     * Broadcast Action: This is broadcast when a user action should request the
+     * brightness setting dialog.
+     * @hide
+     */
+    public static final String ACTION_SHOW_BRIGHTNESS_DIALOG =
+            "android.intent.action.SHOW_BRIGHTNESS_DIALOG";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent categories (see addCategory()).
@@ -2610,7 +2673,8 @@
     public static final String CATEGORY_SAMPLE_CODE = "android.intent.category.SAMPLE_CODE";
     /**
      * Used to indicate that a GET_CONTENT intent only wants URIs that can be opened with
-     * ContentResolver.openInputStream. Openable URIs must support the columns in OpenableColumns
+     * ContentResolver.openInputStream. Openable URIs must support the columns in
+     * {@link android.provider.OpenableColumns}
      * when queried, though it is allowable for those columns to be blank.
      */
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 3b0d846..5e65b59 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -86,7 +86,8 @@
  * <strong>data scheme+authority+path</strong> if specified) must match.
  *
  * <p><strong>Action</strong> matches if any of the given values match the
- * Intent action, <em>or</em> if no actions were specified in the filter.
+ * Intent action; if the filter specifies no actions, then it will only match
+ * Intents that do not contain an action.
  *
  * <p><strong>Data Type</strong> matches if any of the given values match the
  * Intent type.  The Intent
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 000c56c..1f44e49 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -70,6 +70,7 @@
      * For a disconnect event, the boolean extra EXTRA_NO_CONNECTIVITY
      * is set to {@code true} if there are no connected networks at all.
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
 
     /**
@@ -78,6 +79,7 @@
      *
      * @hide
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String CONNECTIVITY_ACTION_IMMEDIATE =
             "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE";
 
@@ -198,6 +200,7 @@
      * the network and it's condition.
      * @hide
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String INET_CONDITION_ACTION =
             "android.net.conn.INET_CONDITION_ACTION";
 
@@ -206,6 +209,7 @@
      * TODO - finish the doc
      * @hide
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_TETHER_STATE_CHANGED =
             "android.net.conn.TETHER_STATE_CHANGED";
 
@@ -233,6 +237,7 @@
      * notification.
      * @hide
      */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_CAPTIVE_PORTAL_TEST_COMPLETED =
             "android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED";
     /**
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 1508d10..2ab9bf8 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -757,7 +757,7 @@
                     break;
 
                 case EASY_EDIT_SPAN:
-                    readSpan(p, sp, new EasyEditSpan());
+                    readSpan(p, sp, new EasyEditSpan(p));
                     break;
 
                 case LOCALE_SPAN:
diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java
index 2feb719..03b4f60 100644
--- a/core/java/android/text/style/EasyEditSpan.java
+++ b/core/java/android/text/style/EasyEditSpan.java
@@ -16,6 +16,7 @@
 
 package android.text.style;
 
+import android.app.PendingIntent;
 import android.os.Parcel;
 import android.text.ParcelableSpan;
 import android.text.TextUtils;
@@ -25,12 +26,62 @@
  * Provides an easy way to edit a portion of text.
  * <p>
  * The {@link TextView} uses this span to allow the user to delete a chuck of text in one click.
- * the text. {@link TextView} removes this span as soon as the text is edited, or the cursor moves.
+ * <p>
+ * {@link TextView} removes the span when the user deletes the whole text or modifies it.
+ * <p>
+ * This span can be also used to receive notification when the user deletes or modifies the text;
  */
 public class EasyEditSpan implements ParcelableSpan {
 
+    /**
+     * The extra key field in the pending intent that describes how the text changed.
+     *
+     * @see #TEXT_DELETED
+     * @see #TEXT_MODIFIED
+     * @see #getPendingIntent()
+     */
+    public static final String EXTRA_TEXT_CHANGED_TYPE =
+            "android.text.style.EXTRA_TEXT_CHANGED_TYPE";
+
+    /**
+     * The value of {@link #EXTRA_TEXT_CHANGED_TYPE} when the text wrapped by this span is deleted.
+     */
+    public static final int TEXT_DELETED = 1;
+
+    /**
+     * The value of {@link #EXTRA_TEXT_CHANGED_TYPE} when the text wrapped by this span is modified.
+     */
+    public static final int TEXT_MODIFIED = 2;
+
+    private final PendingIntent mPendingIntent;
+
+    private boolean mDeleteEnabled;
+
+    /**
+     * Creates the span. No intent is sent when the wrapped text is modified or
+     * deleted.
+     */
     public EasyEditSpan() {
-        // Empty
+        mPendingIntent = null;
+        mDeleteEnabled = true;
+    }
+
+    /**
+     * @param pendingIntent The intent will be sent when the wrapped text is deleted or modified.
+     *                      When the pending intent is sent, {@link #EXTRA_TEXT_CHANGED_TYPE} is
+     *                      added in the intent to describe how the text changed.
+     */
+    public EasyEditSpan(PendingIntent pendingIntent) {
+        mPendingIntent = pendingIntent;
+        mDeleteEnabled = true;
+    }
+
+    /**
+     * Constructor called from {@link TextUtils} to restore the span.
+     */
+    public EasyEditSpan(Parcel source) {
+        mPendingIntent = source.readParcelable(null);
+        mDeleteEnabled = (source.readByte() == 1);
     }
 
     @Override
@@ -40,11 +91,39 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        // Empty
+        dest.writeParcelable(mPendingIntent, 0);
+        dest.writeByte((byte) (mDeleteEnabled ? 1 : 0));
     }
 
     @Override
     public int getSpanTypeId() {
         return TextUtils.EASY_EDIT_SPAN;
     }
+
+    /**
+     * @return True if the {@link TextView} should offer the ability to delete the text.
+     *
+     * @hide
+     */
+    public boolean isDeleteEnabled() {
+        return mDeleteEnabled;
+    }
+
+    /**
+     * Enables or disables the deletion of the text.
+     *
+     * @hide
+     */
+    public void setDeleteEnabled(boolean value) {
+        mDeleteEnabled = value;
+    }
+
+    /**
+     * @return the pending intent to send when the wrapped text is deleted or modified.
+     *
+     * @hide
+     */
+    public PendingIntent getPendingIntent() {
+        return mPendingIntent;
+    }
 }
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 152827d..9522112 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -169,10 +169,10 @@
      * </ul>
      */
     public static final Pattern PHONE
-        = Pattern.compile(                                  // sdd = space, dot, or dash
-                "(\\+[0-9]+[\\- \\.]*)?"                    // +<digits><sdd>*
-                + "(\\([0-9]+\\)[\\- \\.]*)?"               // (<digits>)<sdd>*
-                + "([0-9][0-9\\- \\.][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit> 
+        = Pattern.compile(                      // sdd = space, dot, or dash
+                "(\\+[0-9]+[\\- \\.]*)?"        // +<digits><sdd>*
+                + "(\\([0-9]+\\)[\\- \\.]*)?"   // (<digits>)<sdd>*
+                + "([0-9][0-9\\- \\.]+[0-9])"); // <digit><digit|sdd>+<digit>
 
     /**
      *  Convenience method to take all of the non-null matching groups in a
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 758abb5..e6a7950 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -437,6 +437,20 @@
     }
 
     /**
+     * @hide
+     * Return a rectangle defining the insets of the overscan region of the display.
+     * Each field of the rectangle is the number of pixels the overscan area extends
+     * into the display on that side.
+     */
+    public void getOverscanInsets(Rect outRect) {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            outRect.set(mDisplayInfo.overscanLeft, mDisplayInfo.overscanTop,
+                    mDisplayInfo.overscanRight, mDisplayInfo.overscanBottom);
+        }
+    }
+
+    /**
      * Returns the rotation of the screen from its "natural" orientation.
      * The returned value may be {@link Surface#ROTATION_0 Surface.ROTATION_0}
      * (no rotation), {@link Surface#ROTATION_90 Surface.ROTATION_90},
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 305fd5c..9fcd9b1 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -109,6 +109,30 @@
     public int logicalHeight;
 
     /**
+     * @hide
+     * Number of overscan pixels on the left side of the display.
+     */
+    public int overscanLeft;
+
+    /**
+     * @hide
+     * Number of overscan pixels on the top side of the display.
+     */
+    public int overscanTop;
+
+    /**
+     * @hide
+     * Number of overscan pixels on the right side of the display.
+     */
+    public int overscanRight;
+
+    /**
+     * @hide
+     * Number of overscan pixels on the bottom side of the display.
+     */
+    public int overscanBottom;
+
+    /**
      * The rotation of the display relative to its natural orientation.
      * May be one of {@link android.view.Surface#ROTATION_0},
      * {@link android.view.Surface#ROTATION_90}, {@link android.view.Surface#ROTATION_180},
@@ -196,6 +220,10 @@
                 && largestNominalAppHeight == other.largestNominalAppHeight
                 && logicalWidth == other.logicalWidth
                 && logicalHeight == other.logicalHeight
+                && overscanLeft == other.overscanLeft
+                && overscanTop == other.overscanTop
+                && overscanRight == other.overscanRight
+                && overscanBottom == other.overscanBottom
                 && rotation == other.rotation
                 && refreshRate == other.refreshRate
                 && logicalDensityDpi == other.logicalDensityDpi
@@ -222,6 +250,10 @@
         largestNominalAppHeight = other.largestNominalAppHeight;
         logicalWidth = other.logicalWidth;
         logicalHeight = other.logicalHeight;
+        overscanLeft = other.overscanLeft;
+        overscanTop = other.overscanTop;
+        overscanRight = other.overscanRight;
+        overscanBottom = other.overscanBottom;
         rotation = other.rotation;
         refreshRate = other.refreshRate;
         logicalDensityDpi = other.logicalDensityDpi;
@@ -243,6 +275,10 @@
         largestNominalAppHeight = source.readInt();
         logicalWidth = source.readInt();
         logicalHeight = source.readInt();
+        overscanLeft = source.readInt();
+        overscanTop = source.readInt();
+        overscanRight = source.readInt();
+        overscanBottom = source.readInt();
         rotation = source.readInt();
         refreshRate = source.readFloat();
         logicalDensityDpi = source.readInt();
@@ -265,6 +301,10 @@
         dest.writeInt(largestNominalAppHeight);
         dest.writeInt(logicalWidth);
         dest.writeInt(logicalHeight);
+        dest.writeInt(overscanLeft);
+        dest.writeInt(overscanTop);
+        dest.writeInt(overscanRight);
+        dest.writeInt(overscanBottom);
         dest.writeInt(rotation);
         dest.writeFloat(refreshRate);
         dest.writeInt(logicalDensityDpi);
@@ -318,18 +358,55 @@
     // For debugging purposes
     @Override
     public String toString() {
-        return "DisplayInfo{\"" + name + "\", app " + appWidth + " x " + appHeight
-                + ", real " + logicalWidth + " x " + logicalHeight
-                + ", largest app " + largestNominalAppWidth + " x " + largestNominalAppHeight
-                + ", smallest app " + smallestNominalAppWidth + " x " + smallestNominalAppHeight
-                + ", " + refreshRate + " fps"
-                + ", rotation " + rotation
-                + ", density " + logicalDensityDpi
-                + ", " + physicalXDpi + " x " + physicalYDpi + " dpi"
-                + ", layerStack " + layerStack
-                + ", type " + Display.typeToString(type)
-                + ", address " + address
-                + flagsToString(flags) + "}";
+        StringBuilder sb = new StringBuilder();
+        sb.append("DisplayInfo{\"");
+        sb.append(name);
+        sb.append("\", app ");
+        sb.append(appWidth);
+        sb.append(" x ");
+        sb.append(appHeight);
+        sb.append(", real ");
+        sb.append(logicalWidth);
+        sb.append(" x ");
+        sb.append(logicalHeight);
+        if (overscanLeft != 0 || overscanTop != 0 || overscanRight != 0 || overscanBottom != 0) {
+            sb.append(", overscan (");
+            sb.append(overscanLeft);
+            sb.append(",");
+            sb.append(overscanTop);
+            sb.append(",");
+            sb.append(overscanRight);
+            sb.append(",");
+            sb.append(overscanBottom);
+            sb.append(")");
+        }
+        sb.append(", largest app ");
+        sb.append(largestNominalAppWidth);
+        sb.append(" x ");
+        sb.append(largestNominalAppHeight);
+        sb.append(", smallest app ");
+        sb.append(smallestNominalAppWidth);
+        sb.append(" x ");
+        sb.append(smallestNominalAppHeight);
+        sb.append(", ");
+        sb.append(refreshRate);
+        sb.append(" fps, rotation");
+        sb.append(rotation);
+        sb.append(", density ");
+        sb.append(logicalDensityDpi);
+        sb.append(" (");
+        sb.append(physicalXDpi);
+        sb.append(" x ");
+        sb.append(physicalYDpi);
+        sb.append(") dpi, layerStack ");
+        sb.append(layerStack);
+        sb.append(", type ");
+        sb.append(Display.typeToString(type));
+        sb.append(", address ");
+        sb.append(address);
+        sb.append(flagsToString(flags));
+        sb.append("}");
+        return sb.toString();
     }
 
     private static String flagsToString(int flags) {
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java
index fa683c0..3bad98e 100644
--- a/core/java/android/view/DisplayList.java
+++ b/core/java/android/view/DisplayList.java
@@ -60,7 +60,6 @@
  *
  * <h3>Rendering a display list on a View</h3>
  * <pre class="prettyprint">
- *     @Override
  *     protected void onDraw(Canvas canvas) {
  *         if (canvas.isHardwareAccelerated()) {
  *             HardwareCanvas hardwareCanvas = (HardwareCanvas) canvas;
@@ -102,7 +101,6 @@
  *         }
  *     }
  *
- *     @Override
  *     protected void onDraw(Canvas canvas) {
  *         if (canvas.isHardwareAccelerated()) {
  *             HardwareCanvas hardwareCanvas = (HardwareCanvas) canvas;
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 9c44737..7ff8d09 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -937,6 +937,8 @@
 
     @Override
     public void drawLines(float[] pts, int offset, int count, Paint paint) {
+        if (count < 4) return;
+
         if ((offset | count) < 0 || offset + count > pts.length) {
             throw new IllegalArgumentException("The lines array must contain 4 elements per line.");
         }
@@ -1060,6 +1062,8 @@
 
     @Override
     public void drawPoints(float[] pts, int offset, int count, Paint paint) {
+        if (count < 2) return;
+
         int modifiers = setupModifiers(paint, MODIFIER_COLOR_FILTER | MODIFIER_SHADER);
         try {
             nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index c6e145f..7929112 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -444,16 +444,6 @@
     /**
      * Creates a new display list that can be used to record batches of
      * drawing operations.
-     *
-     * @return A new display list.
-     */
-    public DisplayList createDisplayList() {
-        return createDisplayList(null);
-    }
-
-    /**
-     * Creates a new display list that can be used to record batches of
-     * drawing operations.
      * 
      * @param name The name of the display list, used for debugging purpose. May be null.
      * 
@@ -1356,31 +1346,25 @@
 
                     dirty = beginFrame(canvas, dirty, surfaceState);
 
+                    DisplayList displayList = buildDisplayList(view, canvas);
+
                     int saveCount = 0;
                     int status = DisplayList.STATUS_DONE;
 
                     try {
-                        view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
-                                == View.PFLAG_INVALIDATED;
-                        view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
-
-                        long buildDisplayListStartTime = startBuildDisplayListProfiling();
-                        canvas.clearLayerUpdates();
-
-                        DisplayList displayList = buildDisplayList(view);
                         status = prepareFrame(dirty);
 
                         saveCount = canvas.save();
                         callbacks.onHardwarePreDraw(canvas);
 
-                        endBuildDisplayListProfiling(buildDisplayListStartTime);
-
                         if (displayList != null) {
                             status = drawDisplayList(attachInfo, canvas, displayList, status);
                         } else {
                             // Shouldn't reach here
                             view.draw(canvas);
                         }
+                    } catch (Exception e) {
+                        Log.e(LOG_TAG, "An error has occurred while drawing:", e);
                     } finally {
                         callbacks.onHardwarePostDraw(canvas);
                         canvas.restoreToCount(saveCount);
@@ -1408,6 +1392,23 @@
             return false;
         }
 
+        private DisplayList buildDisplayList(View view, HardwareCanvas canvas) {
+            view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
+                    == View.PFLAG_INVALIDATED;
+            view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
+
+            long buildDisplayListStartTime = startBuildDisplayListProfiling();
+            canvas.clearLayerUpdates();
+
+            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
+            DisplayList displayList = view.getDisplayList();
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+
+            endBuildDisplayListProfiling(buildDisplayListStartTime);
+
+            return displayList;
+        }
+
         abstract void drawProfileData(View.AttachInfo attachInfo);
 
         private Rect beginFrame(HardwareCanvas canvas, Rect dirty, int surfaceState) {
@@ -1455,17 +1456,6 @@
             }
         }
 
-        private static DisplayList buildDisplayList(View view) {
-            DisplayList displayList;
-            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList");
-            try {
-                displayList = view.getDisplayList();
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-            }
-            return displayList;
-        }
-
         private int prepareFrame(Rect dirty) {
             int status;
             Trace.traceBegin(Trace.TRACE_TAG_VIEW, "prepareFrame");
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 885327c..e4ecb5c 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -65,6 +65,8 @@
     void setForcedDisplayDensity(int displayId, int density);
     void clearForcedDisplayDensity(int displayId);
 
+    void setOverscan(int displayId, int left, int top, int right, int bottom);
+
     // Is the device configured to have a full system bar for larger screens?
     boolean hasSystemNavBar();
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 6a67d8b..d236561 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -735,20 +735,20 @@
         /**
          * <p>Indicates whether this window should be hardware accelerated.
          * Requesting hardware acceleration does not guarantee it will happen.</p>
-         * 
+         *
          * <p>This flag can be controlled programmatically <em>only</em> to enable
          * hardware acceleration. To enable hardware acceleration for a given
          * window programmatically, do the following:</p>
-         * 
+         *
          * <pre>
          * Window w = activity.getWindow(); // in Activity's onCreate() for instance
          * w.setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
          *         WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
          * </pre>
-         * 
+         *
          * <p>It is important to remember that this flag <strong>must</strong>
          * be set before setting the content view of your activity or dialog.</p>
-         * 
+         *
          * <p>This flag cannot be used to disable hardware acceleration after it
          * was enabled in your manifest using
          * {@link android.R.attr#hardwareAccelerated}. If you need to selectively
@@ -756,13 +756,19 @@
          * for instance), make sure it is turned off in your manifest and enable it
          * on your activity or dialog when you need it instead, using the method
          * described above.</p>
-         * 
+         *
          * <p>This flag is automatically set by the system if the
          * {@link android.R.attr#hardwareAccelerated android:hardwareAccelerated}
          * XML attribute is set to true on an activity or on the application.</p>
          */
         public static final int FLAG_HARDWARE_ACCELERATED = 0x01000000;
 
+        /** Window flag: allow window contents to extend in to the screen's
+         * overscan area, if there is one.  The window should still correctly
+         * position its contents to take the overscan area into account.
+         */
+        public static final int FLAG_LAYOUT_IN_OVERSCAN = 0x02000000;
+
         // ----- HIDDEN FLAGS.
         // These start at the high bit and go down.
 
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index b5d216a..192eded 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -458,6 +458,12 @@
     public void setInitialDisplaySize(Display display, int width, int height, int density);
 
     /**
+     * Called by window manager to set the overscan region that should be used for the
+     * given display.
+     */
+    public void setDisplayOverscan(Display display, int left, int top, int right, int bottom);
+
+    /**
      * Check permissions when adding a window.
      * 
      * @param attrs The window's LayoutParams.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 862e2c8..dc305a5 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -20,6 +20,8 @@
 import com.android.internal.widget.EditableInputConnection;
 
 import android.R;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
 import android.content.ClipData;
 import android.content.ClipData.Item;
 import android.content.Context;
@@ -1890,10 +1892,23 @@
 
                 // Make sure there is only at most one EasyEditSpan in the text
                 if (mPopupWindow.mEasyEditSpan != null) {
-                    text.removeSpan(mPopupWindow.mEasyEditSpan);
+                    mPopupWindow.mEasyEditSpan.setDeleteEnabled(false);
                 }
 
                 mPopupWindow.setEasyEditSpan((EasyEditSpan) span);
+                mPopupWindow.setOnDeleteListener(new EasyEditDeleteListener() {
+                    @Override
+                    public void onDeleteClick(EasyEditSpan span) {
+                        Editable editable = (Editable) mTextView.getText();
+                        int start = editable.getSpanStart(span);
+                        int end = editable.getSpanEnd(span);
+                        if (start >= 0 && end >= 0) {
+                            sendNotification(EasyEditSpan.TEXT_DELETED, span);
+                            mTextView.deleteText_internal(start, end);
+                        }
+                        editable.removeSpan(span);
+                    }
+                });
 
                 if (mTextView.getWindowVisibility() != View.VISIBLE) {
                     // The window is not visible yet, ignore the text change.
@@ -1927,8 +1942,10 @@
         @Override
         public void onSpanChanged(Spannable text, Object span, int previousStart, int previousEnd,
                 int newStart, int newEnd) {
-            if (mPopupWindow != null && span == mPopupWindow.mEasyEditSpan) {
-                text.removeSpan(mPopupWindow.mEasyEditSpan);
+            if (mPopupWindow != null && span instanceof EasyEditSpan) {
+                EasyEditSpan easyEditSpan = (EasyEditSpan) span;
+                sendNotification(EasyEditSpan.TEXT_MODIFIED, easyEditSpan);
+                text.removeSpan(easyEditSpan);
             }
         }
 
@@ -1938,6 +1955,31 @@
                 mTextView.removeCallbacks(mHidePopup);
             }
         }
+
+        private void sendNotification(int textChangedType, EasyEditSpan span) {
+            try {
+                PendingIntent pendingIntent = span.getPendingIntent();
+                if (pendingIntent != null) {
+                    Intent intent = new Intent();
+                    intent.putExtra(EasyEditSpan.EXTRA_TEXT_CHANGED_TYPE, textChangedType);
+                    pendingIntent.send(mTextView.getContext(), 0, intent);
+                }
+            } catch (CanceledException e) {
+                // This should not happen, as we should try to send the intent only once.
+                Log.w(TAG, "PendingIntent for notification cannot be sent", e);
+            }
+        }
+    }
+
+    /**
+     * Listens for the delete event triggered by {@link EasyEditPopupWindow}.
+     */
+    private interface EasyEditDeleteListener {
+
+        /**
+         * Clicks the delete pop-up.
+         */
+        void onDeleteClick(EasyEditSpan span);
     }
 
     /**
@@ -1950,6 +1992,7 @@
                 com.android.internal.R.layout.text_edit_action_popup_text;
         private TextView mDeleteTextView;
         private EasyEditSpan mEasyEditSpan;
+        private EasyEditDeleteListener mOnDeleteListener;
 
         @Override
         protected void createPopupWindow() {
@@ -1984,19 +2027,29 @@
             mEasyEditSpan = easyEditSpan;
         }
 
+        private void setOnDeleteListener(EasyEditDeleteListener listener) {
+            mOnDeleteListener = listener;
+        }
+
         @Override
         public void onClick(View view) {
-            if (view == mDeleteTextView) {
-                Editable editable = (Editable) mTextView.getText();
-                int start = editable.getSpanStart(mEasyEditSpan);
-                int end = editable.getSpanEnd(mEasyEditSpan);
-                if (start >= 0 && end >= 0) {
-                    mTextView.deleteText_internal(start, end);
-                }
+            if (view == mDeleteTextView
+                    && mEasyEditSpan != null && mEasyEditSpan.isDeleteEnabled()
+                    && mOnDeleteListener != null) {
+                mOnDeleteListener.onDeleteClick(mEasyEditSpan);
             }
         }
 
         @Override
+        public void hide() {
+            if (mEasyEditSpan != null) {
+                mEasyEditSpan.setDeleteEnabled(false);
+            }
+            mOnDeleteListener = null;
+            super.hide();
+        }
+
+        @Override
         protected int getTextOffset() {
             // Place the pop-up at the end of the span
             Editable editable = (Editable) mTextView.getText();
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
index 78b4466..6d51d38 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetHost.aidl
@@ -22,9 +22,9 @@
 
 /** {@hide} */
 oneway interface IAppWidgetHost {
-    void updateAppWidget(int appWidgetId, in RemoteViews views);
-    void providerChanged(int appWidgetId, in AppWidgetProviderInfo info);
-    void providersChanged();
-    void viewDataChanged(int appWidgetId, int viewId);
+    void updateAppWidget(int appWidgetId, in RemoteViews views, int userId);
+    void providerChanged(int appWidgetId, in AppWidgetProviderInfo info, int userId);
+    void providersChanged(int userId);
+    void viewDataChanged(int appWidgetId, int viewId, int userId);
 }
 
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index e685e63..7ddd5d2 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -26,42 +26,39 @@
 
 /** {@hide} */
 interface IAppWidgetService {
-    
+
     //
     // for AppWidgetHost
     //
     int[] startListening(IAppWidgetHost host, String packageName, int hostId,
-            out List<RemoteViews> updatedViews);
-    int[] startListeningAsUser(IAppWidgetHost host, String packageName, int hostId,
             out List<RemoteViews> updatedViews, int userId);
-    void stopListening(int hostId);
-    void stopListeningAsUser(int hostId, int userId);
-    int allocateAppWidgetId(String packageName, int hostId);
-    void deleteAppWidgetId(int appWidgetId);
-    void deleteHost(int hostId);
-    void deleteAllHosts();
-    RemoteViews getAppWidgetViews(int appWidgetId);
-    int[] getAppWidgetIdsForHost(int hostId);
+    void stopListening(int hostId, int userId);
+    int allocateAppWidgetId(String packageName, int hostId, int userId);
+    void deleteAppWidgetId(int appWidgetId, int userId);
+    void deleteHost(int hostId, int userId);
+    void deleteAllHosts(int userId);
+    RemoteViews getAppWidgetViews(int appWidgetId, int userId);
+    int[] getAppWidgetIdsForHost(int hostId, int userId);
 
     //
     // for AppWidgetManager
     //
-    void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
-    void updateAppWidgetOptions(int appWidgetId, in Bundle extras);
-    Bundle getAppWidgetOptions(int appWidgetId);
-    void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views);
-    void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views);
-    void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId);
-    List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter);
-    AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId);
-    boolean hasBindAppWidgetPermission(in String packageName);
-    void setBindAppWidgetPermission(in String packageName, in boolean permission);
-    void bindAppWidgetId(int appWidgetId, in ComponentName provider, in Bundle options);
-    boolean bindAppWidgetIdIfAllowed(
-            in String packageName, int appWidgetId, in ComponentName provider, in Bundle options);
+    void updateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views, int userId);
+    void updateAppWidgetOptions(int appWidgetId, in Bundle extras, int userId);
+    Bundle getAppWidgetOptions(int appWidgetId, int userId);
+    void partiallyUpdateAppWidgetIds(in int[] appWidgetIds, in RemoteViews views, int userId);
+    void updateAppWidgetProvider(in ComponentName provider, in RemoteViews views, int userId);
+    void notifyAppWidgetViewDataChanged(in int[] appWidgetIds, int viewId, int userId);
+    List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter, int userId);
+    AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId, int userId);
+    boolean hasBindAppWidgetPermission(in String packageName, int userId);
+    void setBindAppWidgetPermission(in String packageName, in boolean permission, int userId);
+    void bindAppWidgetId(int appWidgetId, in ComponentName provider, in Bundle options, int userId);
+    boolean bindAppWidgetIdIfAllowed(in String packageName, int appWidgetId,
+            in ComponentName provider, in Bundle options, int userId);
     void bindRemoteViewsService(int appWidgetId, in Intent intent, in IBinder connection, int userId);
     void unbindRemoteViewsService(int appWidgetId, in Intent intent, int userId);
-    int[] getAppWidgetIds(in ComponentName provider);
+    int[] getAppWidgetIds(in ComponentName provider, int userId);
 
 }
 
diff --git a/core/java/com/android/internal/statusbar/INotificationListener.java b/core/java/com/android/internal/statusbar/INotificationListener.java
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/core/java/com/android/internal/statusbar/INotificationListener.java
diff --git a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
index 0280a0b..ba113a3 100644
--- a/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
+++ b/core/java/com/android/internal/widget/SizeAdaptiveLayout.java
@@ -121,12 +121,6 @@
         mTransitionAnimation.addListener(mAnimatorListener);
     }
 
-    @Override
-    public void setLayoutDirection(int layoutDirection) {
-        super.setLayoutDirection(layoutDirection);
-        mModestyPanel.setLayoutDirection(layoutDirection);
-    }
-
     /**
      * Visible for testing
      * @hide
@@ -246,7 +240,6 @@
                                                            View.MeasureSpec.EXACTLY);
         mActiveChild = selectActiveChild(measureSpec);
         mActiveChild.setVisibility(View.VISIBLE);
-        mActiveChild.setLayoutDirection(getLayoutDirection());
 
         if (mLastActive != mActiveChild && mLastActive != null) {
             if (DEBUG) Log.d(TAG, this + " changed children from: " + mLastActive +
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d77b504..8a53cc3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -170,6 +170,9 @@
     <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
     <protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
 
+    <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
+    <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
+
     <!-- ====================================== -->
     <!-- Permissions for things that cost money -->
     <!-- ====================================== -->
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000..57141d5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000..8e28906
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..efdab73
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000..307b86d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000..2e28431
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000..7a83451
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..9afc912
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000..7d03855
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_dark.9.png
new file mode 100644
index 0000000..21f1e2c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_light.9.png
new file mode 100644
index 0000000..600b861
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_dark.9.png
new file mode 100644
index 0000000..c11619c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_light.9.png
new file mode 100644
index 0000000..91be10f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_dark.9.png
new file mode 100644
index 0000000..c921487
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_light.9.png
new file mode 100644
index 0000000..4da6c6e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_dark.9.png
new file mode 100644
index 0000000..9c7d294
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_light.9.png
new file mode 100644
index 0000000..c9f493a
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000..02c799e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000..286157c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..030f723
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000..a5b10d2
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000..10faec10
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000..62a70ed
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..633b8d2
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000..59ff556
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_dark.9.png
new file mode 100644
index 0000000..09d5aa4
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_light.9.png
new file mode 100644
index 0000000..c320ea0
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_dark.9.png
new file mode 100644
index 0000000..a5f28fd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_light.9.png
new file mode 100644
index 0000000..9e0d39c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_dark.9.png
new file mode 100644
index 0000000..24d928f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_light.9.png
new file mode 100644
index 0000000..596ba2d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_dark.9.png
new file mode 100644
index 0000000..55b1af7
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_light.9.png
new file mode 100644
index 0000000..bb0486a
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000..6f41b24
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000..5a96fc1
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..96a6da5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000..849d795
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000..3d6d0f9
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000..1cbaf6c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..878e90d
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000..f25acc2
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_dark.9.png
new file mode 100644
index 0000000..8d89c86
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_light.9.png
new file mode 100644
index 0000000..6e3ca08
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_dark.9.png
new file mode 100644
index 0000000..2204091
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_light.9.png
new file mode 100644
index 0000000..3e6684e
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_dark.9.png
new file mode 100644
index 0000000..5129dee
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_light.9.png
new file mode 100644
index 0000000..0f0289b
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_dark.9.png
new file mode 100644
index 0000000..795820b
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_light.9.png
new file mode 100644
index 0000000..830edfd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/spinner_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 70797cb..7edb530 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -776,7 +776,7 @@
     <string name="lockscreen_password_wrong" msgid="5737815393253165301">"Thử lại"</string>
     <string name="faceunlock_multiple_failures" msgid="754137583022792429">"Đã vượt quá số lần Mở khóa bằng khuôn mặt tối đa"</string>
     <string name="lockscreen_plugged_in" msgid="8057762828355572315">"Đang sạc, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
-    <string name="lockscreen_charged" msgid="321635745684060624">"Bị tính phí"</string>
+    <string name="lockscreen_charged" msgid="321635745684060624">"Pin đầy"</string>
     <string name="lockscreen_battery_short" msgid="4477264849386850266">"<xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
     <string name="lockscreen_low_battery" msgid="1482873981919249740">"Kết nối bộ sạc của bạn."</string>
     <string name="lockscreen_missing_sim_message_short" msgid="5099439277819215399">"Không có thẻ SIM nào"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a3e5b2c..c73ff81 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -312,6 +312,8 @@
         <attr name="windowNoTitle" format="boolean" />
         <!-- Flag indicating whether this window should fill the entire screen. -->
         <attr name="windowFullscreen" format="boolean" />
+        <!-- Flag indicating whether this window should extend into overscan region. -->
+        <attr name="windowOverscan" format="boolean" />
         <!-- Flag indicating whether this is a floating window. -->
         <attr name="windowIsFloating" format="boolean" />
         <!-- Flag indicating whether this is a translucent window. -->
@@ -1562,6 +1564,7 @@
         <attr name="windowFrame" />
         <attr name="windowNoTitle" />
         <attr name="windowFullscreen" />
+        <attr name="windowOverscan" />
         <attr name="windowIsFloating" />
         <attr name="windowIsTranslucent" />
         <attr name="windowShowWallpaper" />
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0d80082..42e5cf1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2036,4 +2036,18 @@
   <public type="attr" name="mipMap" id="0x010103cd" />
   <public type="attr" name="mirrorForRtl" id="0x010103ce" />
 
+    <!-- ===============================================================
+         Resources added in version 19 of the platform
+         =============================================================== -->
+      <eat-comment />
+
+  <public type="attr" name="windowOverscan" />
+  <public type="style" name="Theme.NoTitleBar.Overscan" />
+  <public type="style" name="Theme.Light.NoTitleBar.Overscan" />
+  <public type="style" name="Theme.Black.NoTitleBar.Overscan" />
+  <public type="style" name="Theme.Holo.NoActionBar.Overscan" />
+  <public type="style" name="Theme.Holo.Light.NoActionBar.Overscan" />
+  <public type="style" name="Theme.DeviceDefault.NoActionBar.Overscan" />
+  <public type="style" name="Theme.DeviceDefault.Light.NoActionBar.Overscan" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 00c6f6d..5fc26fc 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3512,8 +3512,8 @@
     <string name="vpn_lockdown_connected">Always-on VPN connected</string>
     <!-- Notification title when error connecting to lockdown VPN. -->
     <string name="vpn_lockdown_error">Always-on VPN error</string>
-    <!-- Notification body that indicates user can touch to cycle lockdown VPN connection. -->
-    <string name="vpn_lockdown_reset">Touch to reset connection</string>
+    <!-- Notification body that indicates user can touch to configure lockdown VPN connection. -->
+    <string name="vpn_lockdown_config">Touch to configure</string>
 
     <!-- Localized strings for WebView -->
     <!-- Label for button in a WebView that will open a chooser to choose a file to upload -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 412d4b9..42ce336 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1516,6 +1516,7 @@
   <java-symbol type="color" name="config_defaultNotificationColor" />
   <java-symbol type="drawable" name="ic_notification_ime_default" />
   <java-symbol type="drawable" name="ic_notify_wifidisplay" />
+  <java-symbol type="drawable" name="ic_menu_refresh" />
   <java-symbol type="drawable" name="stat_notify_car_mode" />
   <java-symbol type="drawable" name="stat_notify_disabled" />
   <java-symbol type="drawable" name="stat_notify_disk_full" />
@@ -1649,7 +1650,7 @@
   <java-symbol type="string" name="vpn_lockdown_connecting" />
   <java-symbol type="string" name="vpn_lockdown_connected" />
   <java-symbol type="string" name="vpn_lockdown_error" />
-  <java-symbol type="string" name="vpn_lockdown_reset" />
+  <java-symbol type="string" name="vpn_lockdown_config" />
   <java-symbol type="string" name="wallpaper_binding_label" />
   <java-symbol type="string" name="wifi_display_notification_title" />
   <java-symbol type="string" name="wifi_display_notification_message" />
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 75850dd..411419b 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -166,6 +166,7 @@
         <item name="windowFrame">@null</item>
         <item name="windowNoTitle">false</item>
         <item name="windowFullscreen">false</item>
+        <item name="windowOverscan">false</item>
         <item name="windowIsFloating">false</item>
         <item name="windowContentOverlay">@null</item>
         <item name="windowShowWallpaper">false</item>
@@ -403,6 +404,14 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
     
+    <!-- Variant of {@link #Theme} that has no title bar and no status bar and extending
+        into the display overscan region. -->
+    <style name="Theme.NoTitleBar.Overscan">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowOverscan">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
     <!-- Theme for a light background with dark text on top.  Set your activity
          to this theme if you would like such an appearance.  As with the
          default theme, you should try to assume little more than that the
@@ -495,6 +504,14 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
     
+    <!-- Variant of {@link #Theme_Light} that has no title bar and
+         no status bar and extending into the display overscan region. -->
+    <style name="Theme.Light.NoTitleBar.Overscan">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowOverscan">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
     <!-- Variant on {@link #Theme} that ensures the background is
          completely black.  This is useful for things like image viewers and
          media players.   If you want the normal (dark background) theme
@@ -516,6 +533,14 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
     
+    <!-- Variant of {@link #Theme_Black} that has no title bar and
+         no status bar and extending into the display overscan region. -->
+    <style name="Theme.Black.NoTitleBar.Overscan">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowOverscan">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
     <!-- Theme for windows that want to have the user's selected
          wallpaper appear behind them (for API level 10 and lower).  -->
     <style name="Theme.Wallpaper">
@@ -1010,6 +1035,7 @@
         <item name="windowFrame">@null</item>
         <item name="windowNoTitle">false</item>
         <item name="windowFullscreen">false</item>
+        <item name="windowOverscan">false</item>
         <item name="windowIsFloating">false</item>
         <item name="windowContentOverlay">@null</item>
         <item name="windowShowWallpaper">false</item>
@@ -1324,6 +1350,7 @@
         <item name="windowFrame">@null</item>
         <item name="windowNoTitle">false</item>
         <item name="windowFullscreen">false</item>
+        <item name="windowOverscan">false</item>
         <item name="windowIsFloating">false</item>
         <item name="android:windowContentOverlay">@android:drawable/ab_solid_shadow_holo</item>
         <item name="windowShowWallpaper">false</item>
@@ -1563,6 +1590,14 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
 
+    <!-- Variant of the holographic (dark) theme that has no title bar and fills
+         the entire screen and extends into the display overscan region. -->
+    <style name="Theme.Holo.NoActionBar.Overscan">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowOverscan">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
     <!-- Variant of the holographic (light) theme with no action bar. -->
     <style name="Theme.Holo.Light.NoActionBar">
         <item name="android:windowActionBar">false</item>
@@ -1576,6 +1611,14 @@
         <item name="android:windowContentOverlay">@null</item>
     </style>
  
+    <!-- Variant of the holographic (light) theme that has no title bar and fills
+         the entire screen and extends into the display overscan region. -->
+    <style name="Theme.Holo.Light.NoActionBar.Overscan">
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowOverscan">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+    </style>
+
     <!-- Dialog themes for Holo -->
     <eat-comment />
 
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 4178cc4..71aa5e6 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -202,12 +202,17 @@
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
     <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Holo.NoActionBar" >
-
     </style>
+
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar -->
     <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Holo.NoActionBar.Fullscreen" >
-
     </style>
+
+    <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
+    extending in to overscan region. -->
+    <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Holo.NoActionBar.Overscan" >
+    </style>
+
     <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
     <style name="Theme.DeviceDefault.Light" parent="Theme.Holo.Light" >
         <!-- Text styles -->
@@ -356,11 +361,14 @@
     </style>
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
     <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Holo.Light.NoActionBar" >
-
     </style>
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar -->
     <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Holo.Light.NoActionBar.Fullscreen" >
-
+    </style>
+    <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
+    and extending in to overscan region-->
+    <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan"
+           parent="Theme.Holo.Light.NoActionBar.Overscan" >
     </style>
     <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
     floating (not fill the entire screen), and puts a frame around its contents. You can set this
diff --git a/core/tests/coretests/src/android/util/PatternsTest.java b/core/tests/coretests/src/android/util/PatternsTest.java
index 9519b9f..ebdbb0e 100644
--- a/core/tests/coretests/src/android/util/PatternsTest.java
+++ b/core/tests/coretests/src/android/util/PatternsTest.java
@@ -156,6 +156,8 @@
                 "Me: 16505551212 this\n",
                 "Me: 6505551212 this\n",
                 "Me: 5551212 this\n",
+                "Me: 2211 this\n",
+                "Me: 112 this\n",
 
                 "Me: 1-650-555-1212 this\n",
                 "Me: (650) 555-1212 this\n",
diff --git a/docs/html/about/versions/android-4.2.jd b/docs/html/about/versions/android-4.2.jd
index 13ee872..b02c1d1 100644
--- a/docs/html/about/versions/android-4.2.jd
+++ b/docs/html/about/versions/android-4.2.jd
@@ -111,6 +111,20 @@
 android:minSdkVersion}</a> is lower than 17, your app is not able to modify the settings that have
 moved to {@link android.provider.Settings.Global} when running on Android 4.2 and higher.</p>
   </li>
+
+  <li>If your app uses {@link android.webkit.WebView}, Android 4.2 adds an additional layer of
+  security so you can more safely <b>bind JavaScript to your
+  Android code</b>. If you set your
+  <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a>
+  to 17 or higher, you must now add the {@code @JavascriptInterface} annotation to any method that you
+  want available to your JavaScript (the method must also be public). If you do not provide the
+  annotation, the method is not accessible by a web page in your {@link android.webkit.WebView}
+  when running on Android 4.2 or higher. If you set the
+  <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a>
+  to 16 or lower, the annotation is not required, but we recommend that you update your target version
+  and add the annotation for additional security.
+    <p>Read more about <a href="{@docRoot}guide/webapps/webview.html#BindingJavaScript">binding
+    JavaScript code to Android code</a>.</p></li>
 </ul>
 
 
diff --git a/docs/html/distribute/googleplay/about/distribution.jd b/docs/html/distribute/googleplay/about/distribution.jd
index e09e300..78883b4 100644
--- a/docs/html/distribute/googleplay/about/distribution.jd
+++ b/docs/html/distribute/googleplay/about/distribution.jd
@@ -18,23 +18,24 @@
 binary.</p>
 
 <p>Later, as you add features or address code issues, you can publish an updated
-binary at any time. Google Play makes the new version available immediately and
+binary at any time. Google Play makes the new version available almost immediately and
 notifies existing customers that an update is ready for download. To streamline
 the rollout across your customer base, Google Play also lets users accept
 automatic updates of your app, so that your updates are delivered and installed
 as soon as you publish them.</p>
 
+
 <h2 id="targeting">Reaching the customers you want</h2>
 
+<div class="figure-right" style="width:400px;">
+<img src="{@docRoot}images/gp-dc-countries.png" class="frame">
+</div>
+
 <p>Google Play does more than connect your app with users&mdash;it helps you
 reach the broadest possible distribution across the Android ecosystem, while
 making sure that your app is only available to the audience that you want to
 reach.</p>
 
-<div style="float:right;margin-left:18px;border:1px solid #DDD;margin:1.5em;">
-<img src="{@docRoot}images/gp-dc-countries.png" style="width:400px;padding:4px;margin-bottom:0;">
-</div>
-
 <h3 id="geotargeting">Geographic targeting</h3>
 
 <p>You can use controls in the Google Play Developer Console to easily
@@ -47,16 +48,15 @@
 and carrier targeting at any time just by saving changes in the Google Play
 Developer Console.</p>
 
+<div class="figure-right" style="width:400px;">
+<img src="{@docRoot}images/gp-supported-dev-requirements.png" class="frame">
+</div>
+
 <p>To help you market to users around the world, you
 can <a href="{@docRoot}distribute/googleplay/publish/preparing.html#localize">localize
 your store listing</a>, including app details and description,
 promotional graphics, screenshots, and more.</p>
 
-<div style="float:right;margin-left:18px;border:1px solid #DDD;margin:1.5em;">
-<img src="{@docRoot}images/gp-supported-dev-requirements.png"
-style="width:400px;padding:4px;margin-bottom:0;">
-</div>
-
 <h3 id="captargeting">Capabilities targeting</h3>
 
 <p>Google Play also lets you control distribution according to device features
diff --git a/docs/html/distribute/googleplay/about/monetizing.jd b/docs/html/distribute/googleplay/about/monetizing.jd
index 8bafd53..8233a31 100644
--- a/docs/html/distribute/googleplay/about/monetizing.jd
+++ b/docs/html/distribute/googleplay/about/monetizing.jd
@@ -116,8 +116,8 @@
 
 <h2 id="buyer-currency" style="margin-top:1.5em;">Flexible pricing in the currencies of your customers</h2>
 
-<div style="float:right;margin-left:18px;border:1px solid #DDD;">
-<img src="{@docRoot}images/gp-buyer-currency.png" style="width:240px;padding:4px;margin-bottom:1em;">
+<div class="figure-right" style="width:250px;">
+<img src="{@docRoot}images/gp-buyer-currency.png" class="frame">
 </div>
 
 <p>Google Play gives you complete control over how you price your products. You
diff --git a/docs/html/distribute/googleplay/publish/console.jd b/docs/html/distribute/googleplay/publish/console.jd
index af2b7e8..069b2d2 100644
--- a/docs/html/distribute/googleplay/publish/console.jd
+++ b/docs/html/distribute/googleplay/publish/console.jd
@@ -1,4 +1,4 @@
-page.title=The Developer Console
+page.title=Developer Console
 @jd:body
 
 
@@ -9,41 +9,41 @@
 tools on Google Play. This sections below introduce a few of the key areas
 you'll find in the Developer Console.</p>
 
-<div style="width:610px;margin-left:">
-<div style="width:610px;border:1px solid #DDD;">
-<img src="{@docRoot}images/gp-dc-home.png" style="width:600px;padding:4px;margin-bottom:0em;">
-</div>
-<p class="image-caption" style="padding:.5em"><span
-style="font-weight:500;">Developer Console home page</span>: Gives you a quick
-overview of your apps, lets you jump to stats, reviews, or product details, or
+<div class="figure" style="width:756px;">
+<img src="{@docRoot}images/gp-dc-home.png" class="frame">
+<p class="img-caption"><strong>All applications page</strong>: Gives you a quick
+overview of your apps, lets you jump to stats, reviews, and product details, or
 upload a new app. </p>
 </div>
 
-<h3 id="profile">Your Developer Profile</h3>
-
-<div style="width:408px;float:right;margin:1.5em;">
-<div style="width:410px;border:1px solid #DDD;">
-<img src="{@docRoot}images/gp-dc-profile.png" style="width:400px;padding:4px;margin-bottom:0em;">
-</div>
-<p class="image-caption" style="padding:.5em"><span
-style="font-weight:500;">Developer profile</span>: Specifies your developer
-identity and contact information, stores your developer key, and more.</p>
+<div class="figure-right" style="width:450px;">
+<img src="{@docRoot}images/gp-dc-profile.png" class="frame">
+<p class="img-caption"><strong>Account details page</strong>: Specifies your developer
+identity and contact information, accounts for app testing, and more.</p>
 </div>
 
-<p>Your developer profile identifies you to Google Play and to your customers.
-During registration you can provide information for your profile, but you can go
-back at any time to edit the information and change your settings. </p>
+<h3 id="profile">Your account details</h3>
+
+<p>The account details page is where you specify basic information about yourself
+or your company in a developer profile. The information in your developer profile
+is important because it identifies you to Google Play and also to your customers.</p>
+
+<p>During registration you must provide the information for your profile, but you can
+go back at any time to edit the information and change your settings. </p>
 
 <p>Your developer profile contains:</p>
 <ul>
-<li>Your developer name &mdash; the name you want to show users on your product
-details page and elsewhere on Google Play. 
+<li>Your developer name &mdash; the name you want to show users on your store
+listing page and elsewhere on Google Play. </li>
 <li>Your developer contact information &mdash; how Google can contact you if
-needed (this information isn't exposed to users.
-<li>Merchant information, in-app billing information.</li>
-<li>Your developer public key for licensing and In-app Billing.</li>
+needed (this information isn't exposed to users).</li>
+<li>Your developer website URL &mdash; shown to users on your store listing page
+so they can learn more about your company or products.</li>
 </ul>
 
+<p>On the account details page you can also register for a merchant account, set
+up test accounts for Google Play licensing, and more. </p>
+
 <h3 id="user-accounts">Multiple user accounts</h3>
 
 <p>If you are working with a team, you can set up multiple user accounts to
@@ -53,6 +53,14 @@
 have access to. For example, an owner can grant users access to publishing and
 app configuration, but not access to financial reports. </p>
 
+
+<div class="figure-right" style="width:450px;">
+<img src="{@docRoot}images/gp-dc-details.png" class="frame">
+<p class="img-caption"><strong>Store listing page</strong>: Lets you upload your
+graphic assets, description, support information, and other information to
+create the store listing page for a specific app.</p>
+</div>
+
 <h3 id="merchant">Linking your Merchant Account</h3>
 
 <p>If you want to sell apps or in-app products, you can link your Google
@@ -60,20 +68,10 @@
 Checkout account for financial and tax identification and monthly payouts of
 sales. </p>
 
-<div style="width:410px;float:right;margin:1.5em;">
-<div style="width:410px;border:1px solid #DDD;">
-<img src="{@docRoot}images/gp-dc-details.png" style="width:400px;padding:4px;margin-bottom:0em;">
-</div>
-<p class="image-caption" style="padding:.5em"><span
-style="font-weight:500;">Product details page</span>: Lets you upload your
-graphic assets, description, support information, and other information to
-create the product details page for a specific app.</p>
-</div>
-
-<h3 id="details">Your product and listing details</h3>
+<h3 id="details">Your store listing details</h3>
 
 <p>The Developer Console lets you set up a colorful storefront page for your app
-called the <em>product details page</em>. Your product details page is the home
+called the <em>Store Listing page</em>. Your Store Listing page is the home
 for your app in Google Play &mdash; it's the page users see on their mobile
 phones or on the web when they want to learn about your app and download it.
 </p>
@@ -87,10 +85,10 @@
 
 <p>From the Developer Console you can quickly upload a release-ready APK and
 publish it when you're ready. The app is a <em>draft</em> until you publish it,
-at which time Google Play makes your product details page and app available to
+at which time Google Play makes your store listing page and app available to
 users. You can unpublish the app at any time.</p>
 
-<h3 id="controls">Distribution Controls</h3>
+<h3 id="controls">Distribution controls</h3>
 
 <p>In the Developer Console you can manage what countries and territories the
 app is distributed to and, for some countries, you can choose what carriers you
@@ -99,7 +97,7 @@
 <p>You can also see the list of devices that your app is currently available to,
 based on any distribution rules declared in its manifest file.</p>
 
-<h3 id="profile">Selling and pricing your Products</h3>
+<h3 id="selling">Selling and pricing your products</h3>
 
 <p>The Developer Console gives you tools to set prices for your apps and in-app
 products. Your app can either be free to download or priced (charged before
@@ -115,25 +113,25 @@
 <li>If you publish your app as free, <span style="font-weight:500;">it must
 remain free</span>. Free apps can be downloaded by any users in Google
 Play.</li>
-<li>If you publish it as priced, you can change it to free, Priced apps can be
-purchased and downloaded only by users who have registered a forms of payment
+<li>If you publish it as priced, you can later change it to free. Priced apps can be
+purchased and downloaded only by users who have registered a form of payment
 in Google Play.</li>
 </ul>
 
 <p>In addition, you can sell in-app products and subscriptions in your app,
-whether it is free or priced. You can set prices separately for priced apps,
+whether the app is free or priced. You can set prices separately for priced apps,
 in-app products, and subscriptions.</p>
 
 <p>If you are selling a priced app or in-app products or subscriptions, the
 Developer Console lets you set prices in a large number of different currencies.
-When users around the world visit your product details page, they see the price
+When users around the world visit your store listing, they see the price
 of your app in their own currency. For most countries, the price you set is the
 final price charged to users, inclusive of taxes. </p>
 
 <p>To help you manage your prices, the Developer Console provides an autofill
 capability that uses recent exchange rates to populate the prices in all
 supported currencies. You can change prices for apps and in-app products at any
-time, just by saving changes in the Develoer Console.</p>
+time, just by saving changes in the Developer Console.</p>
 
 <h3>In-app Billing</h3>
 
@@ -144,29 +142,30 @@
 <a href="{@docRoot}google/play/billing/index.html">In-app Billing</span></a>
 developer documentation.</p></div></div>
 
-<p><a href="{@docRoot}google/play/billing/index.html">In-app Billing</a> is a Google Play service that lets you monetize your apps in more ways by selling in-app products and subscriptions. In-app products are one-time purchases, while  subscriptions are recurring charges on an monthly or annual basis.</p>
+<p><a href="{@docRoot}google/play/billing/index.html">In-app Billing</a> is
+a Google Play service that lets you monetize your apps in more ways by selling
+in-app products and subscriptions. In-app products are one-time purchases, while
+subscriptions are recurring charges on an monthly or annual basis.</p>
 
 <p>From the Developer Console you can create product lists for in-app
 products and subscriptions, set prices, and publish.</p>
 
-<div style="width:410px;float:right;margin:1.5em;">
-<div style="width:410px;border:1px solid #DDD;">
-<img src="{@docRoot}images/gp-dc-reviews.png" style="width:400px;padding:4px;margin-bottom:0em;">
-</div>
-<p class="image-caption" style="padding:.5em"><span style="font-weight:500;">User
-reviews page</span>: Gives you access to user reviews for a specific app.
+<div class="figure-right" style="width:410px;">
+<img src="{@docRoot}images/gp-dc-reviews.png" class="frame">
+<p class="img-caption"><strong>User
+reviews page</strong>: Gives you access to user reviews for a specific app.
 You can filter  reviews in a number of ways to locate issues more easily
 and support your customers more effectively.</p>
 </div>
 
-<h3>User reviews and error reports</h3>
+<h3>User reviews and crash reports</h3>
 
 <p>Google Play makes it easy for users to submit reviews of your app for the
 benefit of other users. The reviews are also extremely important to you, since
 they give you usability feedback, support requests, and important functionality
 issues direct from your customers. </p>
 
-<p>The Developer console also lets you see error reports, with stack trace and
+<p>The Developer Console also lets you see crash reports, with stack trace and
 other data, submitted automatically from Android devices, for debugging and
 improving your app.</p>
 
@@ -177,7 +176,8 @@
 
 <p>You can view installations of your app measured by unique users, as well as
 by unique devices. For user installations, you can view active installs, total
-installs, and daily installs and uninstalls. For devices, you can see active
+installs, daily installs and uninstalls, and metrics about user ratings.
+For devices, you can see active
 installs as well as daily installs, uninstalls, and upgrades.</p>
 
 <p>You can zoom into the installation numbers along several dimensions,
@@ -191,12 +191,8 @@
 specific points (such as individual platform versions or languages) to the
 timeline.</p>
 
-<div style="width:610px;margin:1.5em;margin-left:0">
-<div style="width:610px;border:1px solid #DDD;">
-<img src="{@docRoot}images/gp-dc-stats.png" 
-style="width:600px;padding:4px;margin-bottom:0em;">
-</div>
-<p class="image-caption" style="padding:.5em"><span style="font-weight:500;">App
-installation statistics page</span>: Shows you a variety of statistics about a
-specific app's installation performance over time.</p>
+<div style="width:530px;">
+<img src="{@docRoot}images/gp-dc-stats.png" class="frame">
+<p class="img-caption"><strong>App statistics page</strong>: Shows you a variety
+of statistics about a specific app's installation performance over time.</p>
 </div>
diff --git a/docs/html/distribute/googleplay/publish/preparing.jd b/docs/html/distribute/googleplay/publish/preparing.jd
index e93b211..0925f3c 100644
--- a/docs/html/distribute/googleplay/publish/preparing.jd
+++ b/docs/html/distribute/googleplay/publish/preparing.jd
@@ -2,7 +2,7 @@
 @jd:body
 
 <div id="qv-wrapper"><div id="qv">
-<h2>Checklist:</h2>
+<h2>Checklist</h2>
 <ol>
 <li><a href="#process">1. Understand the publishing process</a></li>
 <li><a href="#policies">2. Understand Google Play policies</a></li>
@@ -399,14 +399,6 @@
 them for you. Screen shots and videos are also very important, because they show
 what your app looks like, how it's used or played, and what makes it different.</p>
 
-<div class="sidebox-wrapper">
-<div class="sidebox">
-<h3>Localize your promotional graphics and videos<span class="new"> new!</span></h3>
-<p>Google Play now lets you provide different promotional graphics for each
-language you support. Localizing your graphics helps you reach your global
-user base more effectively and is highly recommended.</p>
-</div>
-</div>
 <p>All of your graphic assets should be designed so that they are easy to see
 and highlight your app or brand in a colorful, interesting way. The assets
 should reference the same logo and icon as users will actually find in the All
@@ -417,8 +409,8 @@
 <p>To help you market your app more effectively to a global audience, Google
 Play lets you create localized versions of your promotional graphics,
 screenshots, and videos and upload them to the Developer Console. When a user
-visits your app's store listing, Google Play displays the promotional graphic
-and video that you've provided for the user's language.</p>
+visits your app's store listing, Google Play displays the promotional graphic,
+screenshots and video that you've provided for the user's language.</p>
 
 <p>To localize your promotional graphics, you can translate any embedded text, use
 different imagery or presentation, or change your marketing approach to best address the needs
@@ -427,11 +419,11 @@
 and add it to a localized version of the promotional graphic.</p>
 
 <p>Because your localized graphic assets and videos are so important, you should get
-started on creating them and localizing them as needed, well in advance of your target
+started on creating them and localizing them well in advance of your target
 publishing date. </p>
 
-<p class="note"><strong>Note:</strong> Localized promotional graphics and videos are supported
-in the Developer Console Preview only.</p>
+<p class="note"><strong>Note:</strong> Localized promotional graphics and videos
+are supported only in the new Developer Console design.</p>
 
 <table>
 <tr>
@@ -555,7 +547,7 @@
 <h2 id="final-checks">16. Final checks and publishing</h2> 
 
 <p>When you think you are ready to publish, sign in to the Developer Console and take a few moments for a few
-final checks:</p>
+final checks.</p>
 
 <p>Make sure that: </p>
 
diff --git a/docs/html/distribute/googleplay/publish/register.jd b/docs/html/distribute/googleplay/publish/register.jd
index 335d335..dd73898 100644
--- a/docs/html/distribute/googleplay/publish/register.jd
+++ b/docs/html/distribute/googleplay/publish/register.jd
@@ -46,10 +46,15 @@
 
 
 <ol>
-<li>Visit the Google Play Developer Console at <a href="https://play.google.com/apps/publish/">https://play.google.com/apps/publish/</a>.
-<li>Enter basic information about your <strong>developer identity</strong> &mdash; developer name, email address, and so on. You can modify this information later.</li>
-<li>Read and accept the <strong>Developer Distribution Agreement</strong> that applies to your country or region. Note that apps and store listings that you publish on Google Play must comply with the Developer Program Policies and US export law,</li>
-<li>Pay a <strong>$25 USD registration fee</strong> using Google Checkout. If you don't have a Google Checkout account, you can quickly set one up during the process.</li>
+<li>Visit the Google Play Developer Console at <a
+href="https://play.google.com/apps/publish/">https://play.google.com/apps/publish/</a>.
+<li>Enter basic information about your <strong>developer identity</strong> &mdash; developer
+name, email address, and so on. You can modify this information later.</li>
+<li>Read and accept the <strong>Developer Distribution Agreement</strong> that applies to your
+country or region. Note that apps and store listings that you publish on Google Play must comply
+with the Developer Program Policies and US export law,</li>
+<li>Pay a <strong>$25 USD registration fee</strong> using Google Checkout. If you don't have
+a Google Checkout account, you can quickly set one up during the process.</li>
 </ol>
 
 <p>When your registration is verified, you’ll be notified at the email address you specified during registration. </p>
@@ -61,12 +66,15 @@
 <p>To set up a Merchant account from the Developer Console:</p>
 
 <ol>
-<li><strong>Sign in</strong> to your Google Play Developer Console at <a href="https://play.google.com/apps/publish/">https://play.google.com/apps/publish/</a>
-<li>Click on the "<strong>Edit profile</strong>" link.
-<li>Select "<strong>Setup a Merchant Account at Google Checkout</strong>".</li>
+<li><strong>Sign in</strong> to your Google Play Developer Console at
+<a href="https://play.google.com/apps/publish/">https://play.google.com/apps/publish/</a>
+<li>Open <strong>Financial reports</strong> <img src="{@docRoot}images/distribute/console-reports.png"
+  style="vertical-align:baseline;margin:0"> on the side navigation.
+<li>Click <strong>Setup a Merchant Account now</strong>.</li>
 </ol>
 
-<p>This will take you to the Google Checkout site to sign up as a Merchant; you'll need to have information about your business handy to complete this step.</p>
+<p>This takes you to the Google Wallet site to sign up as a Merchant;
+you'll need information about your business available to complete this step.</p>
 
 <h3>Explore the Developer Console</h3>
 <p>When your registration is verified, you can sign in to your Developer Console, which will be the home for your app publishing operations and tools on Google Play. </p>
diff --git a/docs/html/google/play/billing/billing_admin.jd b/docs/html/google/play/billing/billing_admin.jd
index cfa7a30..91883da1 100644
--- a/docs/html/google/play/billing/billing_admin.jd
+++ b/docs/html/google/play/billing/billing_admin.jd
@@ -492,7 +492,7 @@
 <div style="margin:1em;">
 <img style="border:1px solid #ddd;padding-bottom:.5em" src="{@docRoot}images/in-app-billing/billing_app_key.png" xheight="510" id="figure4" />
 <p class="img-caption" style="padding-left:.5em;">
-  <strong>Figure 4.</strong> You can find the license key for each app in the <strong>Services & APIs</strong> panel.
+  <strong>Figure 4.</strong> You can find the license key for each app in the <strong>Services &amp; APIs</strong> panel.
 </p>
 </div>
 
diff --git a/docs/html/google/play/billing/v2/billing_integrate.jd b/docs/html/google/play/billing/v2/billing_integrate.jd
index 7b748a3..f2846f0 100755
--- a/docs/html/google/play/billing/v2/billing_integrate.jd
+++ b/docs/html/google/play/billing/v2/billing_integrate.jd
@@ -170,15 +170,14 @@
 following:</p>
 
 <ol>
-  <li><strong>Add your Google Play public key to the sample application code.</strong>
+  <li><strong>Add your app's public key to the sample application code.</strong>
     <p>This enables the application to verify the signature of the transaction information that is
     returned from Google Play. To add your public key to the sample application code, do the
     following:</p>
     <ol>
-      <li>Log in to your Google Play <a href="http://play.google.com/apps/publish">publisher
-      account</a>.</li>
-      <li>On the upper left part of the page, under your name, click <strong>Edit
-      Profile</strong>.</li>
+      <li>Log in to your Google Play <a href="http://play.google.com/apps/publish">Developer
+      console</a>.</li>
+      <li>On the upper left part of the page, All Applications, click the application name.</strong>.</li>
       <li>On the Edit Profile page, scroll down to the <strong>Licensing &amp; In-app
       Billing</strong> panel.</li>
       <li>Copy your public key.</li>
@@ -1044,14 +1043,14 @@
 
 <p>You will need to use your Google Play public key to perform the signature verification. The
 following procedure shows you how to retrieve Base64-encoded public key from the Google Play
-publisher site.</p>
+Developer Console.</p>
 
 <ol>
   <li>Log in to your <a href="http://play.google.com/apps/publish">publisher account</a>.</li>
-  <li>On the upper left part of the page, under your name, click <strong>Edit profile</strong>.</li>
-  <li>On the Edit Profile page, scroll down to the Licensing &amp; In-app Billing panel (see figure
-  2).</li>
-  <li>Copy your public key.</li>
+  <li>On the upper left part of the page, click <strong>All applications</strong> and then click
+  the app name in the listing.</li>
+  <li>Click <em>Services &amp; APIs</em> and find "Your License Key for this Application" on the page. </li>
+  <li>Copy the app's public key.</li>
 </ol>
 
 <p class="caution"><strong>Important</strong>: To keep your public key safe from malicious users and
@@ -1060,11 +1059,13 @@
 actual key. The key itself is not secret information, but you do not want to make it easy for a
 hacker or malicious user to replace the public key with another key.</p>
 
-<img src="{@docRoot}images/billing_public_key.png" height="510" id="figure2" />
-<p class="img-caption">
-  <strong>Figure 2.</strong> The Licensing and In-app Billing panel of your account's Edit Profile
-  page lets you see your public key.
-</p>
+<div style="width:640px;">
+<img src="{@docRoot}images/licensing_public_key.png" class="frame">
+<p class="img-caption"><strong>Figure
+2.</strong> An app's license key is available from the Services &amp; APIs page in
+the Developer Console.</p>
+</div>
+
 
 <h2 id="billing-implement">Modifying Your Application Code</h2>
 
diff --git a/docs/html/google/play/licensing/setting-up.jd b/docs/html/google/play/licensing/setting-up.jd
index 1d4e775..d83f91b 100644
--- a/docs/html/google/play/licensing/setting-up.jd
+++ b/docs/html/google/play/licensing/setting-up.jd
@@ -32,29 +32,25 @@
 </div>
 
 <p>Before you start adding license verification to your application, you need to set up your Google
-Play publishing account, your development environment, and test accounts required to verify
+Play publishing account, your development environment, and any test accounts required to verify
 your implementation.</p>
 
 
 <h2 id="account">Setting Up a Publisher Account</h2>
 
 <p>If you don't already have a publisher account for Google Play, you need to register for one
-using your Google account and agree to the terms of service on the Google Play publisher site:</p>
-
-<p style="margin-left:2em;"><a
-href="http://play.google.com/apps/publish">http://play.google.com/apps/publish</a>
-</p>
+using your Google account and agree to the Google Play terms of service.</p>
 
 <p>For more information, see <a
 href="{@docRoot}distribute/googleplay/publish/register.html">Get Started with Publishing</a>.</p>
 
-<p>If you already have a publisher account on Google Play, use your existing
-account to set up licensing.</p>
+<p>If you already have a publisher account on Google Play, use your
+Developer Console to set up licensing.</p>
 
-<p>Using your publisher account on Google Play, you can:</p>
+<p>Using the Google Play Developer Console, you can:</p>
 
 <ul>
-<li>Obtain a public key for licensing</li>
+<li>Obtain an app-specific public key for licensing</li>
 <li>Debug and test an application's licensing implementation, prior to
 publishing the application</li>
 <li>Publish the applications to which you have added licensing support</li>
@@ -63,33 +59,35 @@
 <h4>Administrative settings for licensing</h4>
 
 <p>You can manage several
-administrative controls for Google Play licensing on the publisher site. The controls are available
-in the Edit Profile page, in the "Licensing" panel, shown in figure 1. The controls
+administrative controls for Google Play licensing in the Developer Console. The controls
 let you: </p>
 
 <ul>
 <li>Set up multiple "test accounts," identified by email address. The licensing
 server allows users signed in to test accounts on a device or emulator to send
-license checks and receive static test responses.</li>
-<li>Obtain the account's public key for licensing. When you are implementing
-licensing in an application, you must copy the public key string into the
-application.</li>
+license checks and receive static test responses. You can set up accounts in the
+Account Details page of the Developer Console.</li>
 <li>Configure static test responses that the server sends, when it receives a
 license check for an application uploaded to the publisher account, from a user
-signed in to the publisher account or a test account.</li>
+signed in to the publisher account or a test account. You can set test responses
+in the Account Details page of the Developer Console.</li>
+<li>Obtain the app's public key for licensing. When you are implementing
+licensing in an application, you must copy the public key string into the
+application. You can obtain the app's public key for licensing in the Services
+& APIs page (under All Applications).</li>
 </ul>
 
-
-<img src="{@docRoot}images/licensing_public_key.png" alt=""/>
-<p class="img-caption"><strong>Figure 1.</strong> The Licensing
-panel of your account's Edit Profile page lets you manage administrative
-settings for licensing.</p>
+<div style="width:640px;">
+<img src="{@docRoot}images/licensing_public_key.png" class="frame">
+<p class="img-caption"><strong>Figure
+2.</strong> An app's license key is available from the Services &amp; APIs page in
+the Developer Console.</p>
+</div>
 
 <p>For more information about how to work with test accounts and static test
 responses, see <a href="#test-env">Setting Up a Testing Environment</a>, below.
 
 
-
 <h2 id="dev-setup">Setting Up the Development Environment</h2>
 
 <p>Setting up your environment for licensing involves these tasks:</p>
@@ -432,9 +430,9 @@
 
 <h2 id="test-env">Setting Up the Testing Environment</h2>
 
-<p>The Google Play publisher site provides configuration tools that let you
+<p>The Google Play Developer Console provides configuration tools that let you
 and others test licensing on your application before it is published. As you are
-implementing licensing, you can make use of the publisher site tools to test
+implementing licensing, you can make use of the Developer Console tools to test
 your application's Policy and handling of different licensing responses and
 error conditions.</p>
 
@@ -487,10 +485,12 @@
 Response Codes</a> in the <a
 href="{@docRoot}google/play/licensing/licensing-reference.html">Licensing Reference</a>.</p>
 
-<img src="{@docRoot}images/licensing_test_response.png" alt=""/>
-<p class="img-caption"><strong>Figure 4.</strong> The Licensing
-panel of your account's Edit Profile page, showing the Test Accounts field and the
-Test Response menu.</p>
+<div style="width:640px;">
+<img src="{@docRoot}images/licensing_test_response.png" class="frame">
+<p class="img-caption"><strong>Figure 4.</strong> The License Testing
+panel of your Account details page lets you set up test accounts and
+manage test responses.</p>
+</div>
 
 <p>Note that the test response that you configure applies account-wide &mdash;
 that is, it applies not to a single application, but to <em>all</em>
@@ -516,7 +516,7 @@
 <p>In some cases, you might want to let multiple teams of developers test
 licensing on applications that will ultimately be published through your
 publisher account, but without giving them access to your publisher account's
-sign-in credentials. To meet that need, the Google Play publisher site lets
+sign-in credentials. To meet that need, the Google Play Developer Console lets
 you set up one or more optional <em>test accounts</em> &mdash; accounts that are
 authorized to query the licensing server and receive static test responses from
 your publisher account.</p>
@@ -609,13 +609,13 @@
 
 <p>The licensing server handles static test responses in the normal way,
 including signing the license response data, adding extras parameters, and so
-on. To support developers who are implementing licensing using test accounts,
+on. To support developers who are implementing licensing using test accounts
 rather than the publisher account, you will need to distribute
-your public key to them. Developers without access to the publisher site do not
-have access to your public key, and without the key they won't be able to
-verify license responses. </p>
+the app's public key for licensing to them. Developers without access to the
+Developer Console do not have access to the app's public key, and without
+the key they won't be able to verify license responses. </p>
 
-<p>Note that if you decide to generate a new licensing key pair for your account
+<p>Note that if you decide to generate a new licensing key pair for the app
 for some reason, you need to notify all users of test accounts. For
 testers, you can embed the new key in the application package and distribute it
 to users. For developers, you will need to distribute the new key to them
@@ -661,7 +661,7 @@
 
 <p>Signing in using a publisher account offers the advantage of letting your
 applications receive static test responses even before the applications are
-uploaded to the publisher site.</p>
+uploaded to the Developer Console.</p>
 
 <p>If you are part of a larger organization or are working with external groups
 on applications that will be published through your site, you will more likely
diff --git a/docs/html/guide/webapps/webview.jd b/docs/html/guide/webapps/webview.jd
index f8b2a1d..d2b2532 100644
--- a/docs/html/guide/webapps/webview.jd
+++ b/docs/html/guide/webapps/webview.jd
@@ -178,8 +178,8 @@
 href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a>
 to 17 or higher, <strong>you
 must add the {@code &#64;JavascriptInterface} annotation</strong> to any method that you want
-available your web page code (the method must also be public). If you do not provide the
-annotation, then the method will not accessible by your web page when running on Android 4.2 or
+available to your JavaScript (the method must also be public). If you do not provide the
+annotation, the method is not accessible by your web page when running on Android 4.2 or
 higher.</p>
 
 <p>In this example, the {@code WebAppInterface} class allows the web page to create a {@link
diff --git a/docs/html/images/billing_public_key.png b/docs/html/images/billing_public_key.png
deleted file mode 100644
index a0620f8..0000000
--- a/docs/html/images/billing_public_key.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/distribute/console-apps.png b/docs/html/images/distribute/console-apps.png
new file mode 100644
index 0000000..129de67
--- /dev/null
+++ b/docs/html/images/distribute/console-apps.png
Binary files differ
diff --git a/docs/html/images/distribute/console-reports.png b/docs/html/images/distribute/console-reports.png
new file mode 100644
index 0000000..d7fcb99
--- /dev/null
+++ b/docs/html/images/distribute/console-reports.png
Binary files differ
diff --git a/docs/html/images/distribute/console-settings.png b/docs/html/images/distribute/console-settings.png
new file mode 100644
index 0000000..4b5d4a6
--- /dev/null
+++ b/docs/html/images/distribute/console-settings.png
Binary files differ
diff --git a/docs/html/images/gp-buyer-currency.png b/docs/html/images/gp-buyer-currency.png
index 51b8108..96d7e65 100644
--- a/docs/html/images/gp-buyer-currency.png
+++ b/docs/html/images/gp-buyer-currency.png
Binary files differ
diff --git a/docs/html/images/gp-dc-countries.png b/docs/html/images/gp-dc-countries.png
index 00d0d5e..72ce796 100644
--- a/docs/html/images/gp-dc-countries.png
+++ b/docs/html/images/gp-dc-countries.png
Binary files differ
diff --git a/docs/html/images/gp-dc-details.png b/docs/html/images/gp-dc-details.png
index 567567e..5b7eba4 100644
--- a/docs/html/images/gp-dc-details.png
+++ b/docs/html/images/gp-dc-details.png
Binary files differ
diff --git a/docs/html/images/gp-dc-home.png b/docs/html/images/gp-dc-home.png
index 381d0db..5ed46c9 100644
--- a/docs/html/images/gp-dc-home.png
+++ b/docs/html/images/gp-dc-home.png
Binary files differ
diff --git a/docs/html/images/gp-dc-profile.png b/docs/html/images/gp-dc-profile.png
index e526369..e254e5d 100644
--- a/docs/html/images/gp-dc-profile.png
+++ b/docs/html/images/gp-dc-profile.png
Binary files differ
diff --git a/docs/html/images/gp-dc-reviews.png b/docs/html/images/gp-dc-reviews.png
index cab175a..4290136 100644
--- a/docs/html/images/gp-dc-reviews.png
+++ b/docs/html/images/gp-dc-reviews.png
Binary files differ
diff --git a/docs/html/images/gp-dc-stats-mini.png b/docs/html/images/gp-dc-stats-mini.png
index d29a270..211b5ea 100644
--- a/docs/html/images/gp-dc-stats-mini.png
+++ b/docs/html/images/gp-dc-stats-mini.png
Binary files differ
diff --git a/docs/html/images/gp-dc-stats.png b/docs/html/images/gp-dc-stats.png
index 06f88e5..7df6266 100644
--- a/docs/html/images/gp-dc-stats.png
+++ b/docs/html/images/gp-dc-stats.png
Binary files differ
diff --git a/docs/html/images/gp-devconsole-home.png b/docs/html/images/gp-devconsole-home.png
index 1d758fd..a86f591 100644
--- a/docs/html/images/gp-devconsole-home.png
+++ b/docs/html/images/gp-devconsole-home.png
Binary files differ
diff --git a/docs/html/images/gp-supported-dev-requirements.png b/docs/html/images/gp-supported-dev-requirements.png
index d84f34e..c38b8aa 100644
--- a/docs/html/images/gp-supported-dev-requirements.png
+++ b/docs/html/images/gp-supported-dev-requirements.png
Binary files differ
diff --git a/docs/html/images/licensing_public_key.png b/docs/html/images/licensing_public_key.png
index 1630209..a3cd785 100644
--- a/docs/html/images/licensing_public_key.png
+++ b/docs/html/images/licensing_public_key.png
Binary files differ
diff --git a/docs/html/images/licensing_test_response.png b/docs/html/images/licensing_test_response.png
index ead2152..219ae24 100644
--- a/docs/html/images/licensing_test_response.png
+++ b/docs/html/images/licensing_test_response.png
Binary files differ
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 8cbf299..e94ddae 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1199,7 +1199,7 @@
      * meshHeight+1 vertices down. The verts array is accessed in row-major
      * order, so that the first meshWidth+1 vertices are distributed across the
      * top of the bitmap from left to right. A more general version of this
-     * methid is drawVertices().
+     * method is drawVertices().
      *
      * @param bitmap The bitmap to draw using the mesh
      * @param meshWidth The number of columns in the mesh. Nothing is drawn if
@@ -1208,7 +1208,7 @@
      *                   this is 0
      * @param verts Array of x,y pairs, specifying where the mesh should be
      *              drawn. There must be at least
-     *              (meshWidth+1) * (meshHeight+1) * 2 + meshOffset values
+     *              (meshWidth+1) * (meshHeight+1) * 2 + vertOffset values
      *              in the array
      * @param vertOffset Number of verts elements to skip before drawing
      * @param colors May be null. Specifies a color at each vertex, which is
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index 6fab8da..398f719 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -436,13 +436,8 @@
         Caches::getInstance().eventMark(strlen(op->name()), op->name());
 #endif
 
-        if (deferredList) {
-            drawGlStatus |= op->replay(renderer, dirty, flags,
-                    saveCount, level, mCaching, mMultipliedAlpha, *deferredList);
-        } else {
-            drawGlStatus |= op->replay(renderer, dirty, flags,
-                    saveCount, level, mCaching, mMultipliedAlpha);
-        }
+        drawGlStatus |= op->replay(renderer, dirty, flags,
+                saveCount, level, mCaching, mMultipliedAlpha, deferredList);
         logBuffer.writeCommand(level, op->name());
     }
 
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 8e80647..1bae0ff 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -74,14 +74,11 @@
         kOpLogFlag_JSON = 0x2 // TODO: add?
     };
 
-    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha) = 0;
-
-    // same as replay above, but draw operations will defer into the deferredList if possible
-    // NOTE: colorfilters, paintfilters, shaders, shadow, and complex clips prevent deferral
+    // If a DeferredDisplayList is supplied, DrawOps will be stored until the list is flushed
+    // NOTE: complex clips and layers prevent deferral
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
             uint32_t level, bool caching, int multipliedAlpha,
-            DeferredDisplayList& deferredList) = 0;
+            DeferredDisplayList* deferredList) = 0;
 
     virtual void output(int level, uint32_t flags = 0) = 0;
 
@@ -96,22 +93,16 @@
 
     virtual ~StateOp() {}
 
-    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha) {
-        applyState(renderer, saveCount);
-        return DrawGlInfo::kStatusDone;
-    }
-
     /**
      * State operations are applied directly to the renderer, but can cause the deferred drawing op
      * list to flush
      */
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList* deferredList) {
         status_t status = DrawGlInfo::kStatusDone;
-        if (requiresDrawOpFlush()) {
+        if (deferredList && requiresDrawOpFlush()) {
             // will be setting renderer state that affects ops in deferredList, so flush list first
-            status |= deferredList.flush(renderer, dirty, flags, level);
+            status |= deferredList->flush(renderer, dirty, flags, level);
         }
         applyState(renderer, saveCount);
         return status;
@@ -131,23 +122,14 @@
     DrawOp(SkPaint* paint)
             : mPaint(paint), mQuickRejected(false) {}
 
-    virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha) {
-        if (mQuickRejected && CC_LIKELY(flags & DisplayList::kReplayFlag_ClipChildren)) {
-            return DrawGlInfo::kStatusDone;
-        }
-
-        return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
-    }
-
     /** Draw operations are stored in the deferredList with information necessary for playback */
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList* deferredList) {
         if (mQuickRejected && CC_LIKELY(flags & DisplayList::kReplayFlag_ClipChildren)) {
             return DrawGlInfo::kStatusDone;
         }
 
-        if (renderer.disallowDeferral()) {
+        if (!deferredList || renderer.disallowDeferral()) {
             // dispatch draw immediately, since the renderer's state is too complex for deferral
             return applyDraw(renderer, dirty, level, caching, multipliedAlpha);
         }
@@ -161,7 +143,7 @@
 
         if (!renderer.storeDisplayState(state)) {
             // op wasn't quick-rejected, so defer
-            deferredList.add(this, renderer.disallowReorder());
+            deferredList->add(this, renderer.disallowReorder());
         }
 
         return DrawGlInfo::kStatusDone;
@@ -184,7 +166,6 @@
 
     float strokeWidthOutset() { return mPaint->getStrokeWidth() * 0.5f; }
 
-public:
     /**
      * Stores the relevant canvas state of the object between deferral and replay (if the canvas
      * state supports being stored) See OpenGLRenderer::simpleClipAndState()
@@ -204,7 +185,20 @@
     DrawBoundedOp(float left, float top, float right, float bottom, SkPaint* paint)
             : DrawOp(paint), mLocalBounds(left, top, right, bottom) {}
 
-    // default constructor for area, to be overridden in child constructor body
+    // Calculates bounds as smallest rect encompassing all points
+    // NOTE: requires at least 1 vertex, and doesn't account for stroke size (should be handled in
+    // subclass' constructor)
+    DrawBoundedOp(const float* points, int count, SkPaint* paint)
+            : DrawOp(paint), mLocalBounds(points[0], points[1], points[0], points[1]) {
+        for (int i = 2; i < count; i += 2) {
+            mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
+            mLocalBounds.right = fmaxf(mLocalBounds.right, points[i]);
+            mLocalBounds.top = fminf(mLocalBounds.top, points[i + 1]);
+            mLocalBounds.bottom = fmaxf(mLocalBounds.bottom, points[i + 1]);
+        }
+    }
+
+    // default empty constructor for bounds, to be overridden in child constructor body
     DrawBoundedOp(SkPaint* paint)
             : DrawOp(paint) {}
 
@@ -740,11 +734,12 @@
     }
 };
 
-class DrawBitmapMeshOp : public DrawOp {
+class DrawBitmapMeshOp : public DrawBoundedOp {
 public:
     DrawBitmapMeshOp(SkBitmap* bitmap, int meshWidth, int meshHeight,
             float* vertices, int* colors, SkPaint* paint)
-            : DrawOp(paint), mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
+            : DrawBoundedOp(vertices, 2 * (meshWidth + 1) * (meshHeight + 1), paint),
+            mBitmap(bitmap), mMeshWidth(meshWidth), mMeshHeight(meshHeight),
             mVertices(vertices), mColors(colors) {}
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
@@ -873,10 +868,11 @@
     virtual const char* name() { return "DrawRect"; }
 };
 
-class DrawRectsOp : public DrawOp {
+class DrawRectsOp : public DrawBoundedOp {
 public:
     DrawRectsOp(const float* rects, int count, SkPaint* paint)
-            : DrawOp(paint), mRects(rects), mCount(count) {}
+            : DrawBoundedOp(rects, count, paint),
+            mRects(rects), mCount(count) {}
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
             bool caching, int multipliedAlpha) {
@@ -1022,13 +1018,8 @@
 class DrawLinesOp : public DrawBoundedOp {
 public:
     DrawLinesOp(float* points, int count, SkPaint* paint)
-            : DrawBoundedOp(paint), mPoints(points), mCount(count) {
-        for (int i = 0; i < count; i += 2) {
-            mLocalBounds.left = fminf(mLocalBounds.left, points[i]);
-            mLocalBounds.right = fmaxf(mLocalBounds.right, points[i]);
-            mLocalBounds.top = fminf(mLocalBounds.top, points[i+1]);
-            mLocalBounds.bottom = fmaxf(mLocalBounds.bottom, points[i+1]);
-        }
+            : DrawBoundedOp(points, count, paint),
+            mPoints(points), mCount(count) {
         mLocalBounds.outset(strokeWidthOutset());
     }
 
@@ -1199,26 +1190,23 @@
     Functor* mFunctor;
 };
 
-class DrawDisplayListOp : public DrawOp {
+class DrawDisplayListOp : public DrawBoundedOp {
 public:
     DrawDisplayListOp(DisplayList* displayList, int flags)
-            : DrawOp(0), mDisplayList(displayList), mFlags(flags) {}
+            : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0),
+            mDisplayList(displayList), mFlags(flags) {}
 
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, int saveCount,
-            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList& deferredList) {
+            uint32_t level, bool caching, int multipliedAlpha, DeferredDisplayList* deferredList) {
         if (mDisplayList && mDisplayList->isRenderable()) {
-            return mDisplayList->replay(renderer, dirty, mFlags, level + 1, &deferredList);
+            return mDisplayList->replay(renderer, dirty, mFlags, level + 1, deferredList);
         }
         return DrawGlInfo::kStatusDone;
     }
 
+    // NOT USED, since replay is overridden
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty, uint32_t level,
-            bool caching, int multipliedAlpha) {
-        if (mDisplayList && mDisplayList->isRenderable()) {
-            return mDisplayList->replay(renderer, dirty, mFlags, level + 1);
-        }
-        return DrawGlInfo::kStatusDone;
-    }
+            bool caching, int multipliedAlpha) { return DrawGlInfo::kStatusDone; }
 
     virtual void output(int level, uint32_t flags) {
         OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 5d5e6a5..1d9eb0e 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -797,7 +797,7 @@
     mRsScript->blur(ain, aout);
 
     // replace the original image's pointer, avoiding a copy back to the original buffer
-    delete *image;
+    free(*image);
     *image = outImage;
 }
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index e5fd7b9..6b614a7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1235,6 +1235,8 @@
             // quick rejected
             return true;
         }
+    } else {
+        state.mBounds.set(currentClip);
     }
 
     state.mClip.set(currentClip);
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index db7bd48..f1f35bd 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -222,7 +222,7 @@
         }
 
         // Cleanup shadow
-        delete shadow.image;
+        free(shadow.image);
     }
 
     return texture;
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 4398642..f6593e0 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -191,17 +191,27 @@
         public static final int AACObjectHE_PS      = 29;
         public static final int AACObjectELD        = 39;
 
+        // from OMX_VIDEO_VP8LEVELTYPE
+        public static final int VP8Level_Version0 = 0x01;
+        public static final int VP8Level_Version1 = 0x02;
+        public static final int VP8Level_Version2 = 0x04;
+        public static final int VP8Level_Version3 = 0x08;
+
+        // from OMX_VIDEO_VP8PROFILETYPE
+        public static final int VP8ProfileMain = 0x01;
+
+
         /**
          * Defined in the OpenMAX IL specs, depending on the type of media
          * this can be OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE,
-         * or OMX_VIDEO_MPEG4PROFILETYPE.
+         * OMX_VIDEO_MPEG4PROFILETYPE or OMX_VIDEO_VP8PROFILETYPE.
          */
         public int profile;
 
         /**
          * Defined in the OpenMAX IL specs, depending on the type of media
          * this can be OMX_VIDEO_AVCLEVELTYPE, OMX_VIDEO_H263LEVELTYPE
-         * or OMX_VIDEO_MPEG4LEVELTYPE.
+         * OMX_VIDEO_MPEG4LEVELTYPE or OMX_VIDEO_VP8LEVELTYPE.
          */
         public int level;
     };
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 7bd9049..11f4180 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -948,7 +948,12 @@
 
     private void setDataSource(String path, String[] keys, String[] values)
             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
-        File file = new File(path);
+        final Uri uri = Uri.parse(path);
+        if ("file".equals(uri.getScheme())) {
+            path = uri.getPath();
+        }
+
+        final File file = new File(path);
         if (file.exists()) {
             FileInputStream is = new FileInputStream(file);
             FileDescriptor fd = is.getFD();
diff --git a/packages/BackupRestoreConfirmation/res/values-sw/strings.xml b/packages/BackupRestoreConfirmation/res/values-sw/strings.xml
index 493c168..24d9c39 100644
--- a/packages/BackupRestoreConfirmation/res/values-sw/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-sw/strings.xml
@@ -17,7 +17,7 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="backup_confirm_title" msgid="827563724209303345">"Kuhifadhi kikamilifu"</string>
-    <string name="restore_confirm_title" msgid="5469365809567486602">"Kurejeza kamili"</string>
+    <string name="restore_confirm_title" msgid="5469365809567486602">"Kurejesha kila kitu"</string>
     <string name="backup_confirm_text" msgid="1878021282758896593">"Chelezo kamili la data iliyounganishwa kwenye eneo kazi la kompyuta limeombwa. Unataka kuruhusu hii kutendeka?"\n\n" Ikiwa hukuomba chelezo mwenyewe, usikubali uendeshaji kuendelea."</string>
     <string name="allow_backup_button_label" msgid="4217228747769644068">"Cheleza data yangu"</string>
     <string name="deny_backup_button_label" msgid="6009119115581097708">"Usicheleze"</string>
@@ -32,7 +32,7 @@
     <string name="restore_enc_password_text" msgid="6140898525580710823">"Ikiwa data iliyorejeshwa upya, tafadhali ingiza nenosiri lililo hapo chini:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Inaanza kuhifadhi..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Imemaliza kuhifadhi"</string>
-    <string name="toast_restore_started" msgid="7881679218971277385">"Inaanza kurejeza..."</string>
-    <string name="toast_restore_ended" msgid="1764041639199696132">"Kurejeza kumekamilika"</string>
+    <string name="toast_restore_started" msgid="7881679218971277385">"Inaanza kurejesha..."</string>
+    <string name="toast_restore_ended" msgid="1764041639199696132">"Kurejesha kumekamilika"</string>
     <string name="toast_timeout" msgid="5276598587087626877">"Muda wa uendeshaji umeisha"</string>
 </resources>
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index b85121e..66080f3 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -10,6 +10,7 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
+    <uses-permission android:name="android.permission.DUMP" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
 
     <uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index f7b1d78..b547d99 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -27,7 +27,7 @@
         android:paddingRight="10dp"
         android:src="@drawable/ic_qs_brightness_auto_off"
         />
-    <com.android.systemui.statusbar.policy.ToggleSlider
+    <com.android.systemui.settings.ToggleSlider
         android:id="@+id/brightness_slider"
         android:layout_width="0dp"
         android:layout_height="40dp"
diff --git a/packages/SystemUI/res/layout/system_bar_settings_view.xml b/packages/SystemUI/res/layout/system_bar_settings_view.xml
index 3e959d5..4987dd9 100644
--- a/packages/SystemUI/res/layout/system_bar_settings_view.xml
+++ b/packages/SystemUI/res/layout/system_bar_settings_view.xml
@@ -100,7 +100,7 @@
                 style="@style/SystemBarPanelSettingsIcon"
                 android:src="@drawable/ic_sysbar_brightness"
                 />
-        <com.android.systemui.statusbar.policy.ToggleSlider
+        <com.android.systemui.settings.ToggleSlider
                 android:id="@+id/brightness"
                 android:layout_width="0dp"
                 android:layout_height="fill_parent"
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index 79f9650..8d949a5 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -485,14 +485,12 @@
 
             glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
-            if (!mEgl.eglSwapBuffers(mEglDisplay, mEglSurface)) {
-                throw new RuntimeException("Cannot swap buffers");
-            }
+            boolean status = mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
             checkEglError();
 
             finishGL();
 
-            return true;
+            return status;
         }
 
         private FloatBuffer createMesh(int left, int top, float right, float bottom) {
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index ceb8654..8e1773a 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -154,6 +154,13 @@
         return Math.max(mMinAlpha, result);
     }
 
+    private void updateAlphaFromOffset(View animView, boolean dismissable) {
+        if (FADE_OUT_DURING_SWIPE && dismissable) {
+            animView.setAlpha(getAlphaForOffset(animView));
+        }
+        invalidateGlobalRegion(animView);
+    }
+
     // invalidate the view's own bounds all the way up the view hierarchy
     public static void invalidateGlobalRegion(View view) {
         invalidateGlobalRegion(
@@ -290,10 +297,7 @@
         });
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
-                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
-                    animView.setAlpha(getAlphaForOffset(animView));
-                }
-                invalidateGlobalRegion(animView);
+                updateAlphaFromOffset(animView, canAnimViewBeDismissed);
             }
         });
         anim.start();
@@ -307,10 +311,12 @@
         anim.setDuration(duration);
         anim.addUpdateListener(new AnimatorUpdateListener() {
             public void onAnimationUpdate(ValueAnimator animation) {
-                if (FADE_OUT_DURING_SWIPE && canAnimViewBeDismissed) {
-                    animView.setAlpha(getAlphaForOffset(animView));
-                }
-                invalidateGlobalRegion(animView);
+                updateAlphaFromOffset(animView, canAnimViewBeDismissed);
+            }
+        });
+        anim.addListener(new AnimatorListenerAdapter() {
+            public void onAnimationEnd(Animator animator) {
+                updateAlphaFromOffset(animView, canAnimViewBeDismissed);
             }
         });
         anim.start();
@@ -347,10 +353,8 @@
                         }
                     }
                     setTranslation(mCurrAnimView, delta);
-                    if (FADE_OUT_DURING_SWIPE && mCanCurrViewBeDimissed) {
-                        mCurrAnimView.setAlpha(getAlphaForOffset(mCurrAnimView));
-                    }
-                    invalidateGlobalRegion(mCurrView);
+
+                    updateAlphaFromOffset(mCurrAnimView, mCanCurrViewBeDimissed);
                 }
                 break;
             case MotionEvent.ACTION_UP:
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
index 427fe91..1f3e942 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java
@@ -44,6 +44,7 @@
             0, // system bar or status bar, filled in below.
             com.android.systemui.power.PowerUI.class,
             com.android.systemui.media.RingtonePlayer.class,
+            com.android.systemui.settings.SettingsUI.class,
         };
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
new file mode 100644
index 0000000..fdeead1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.IPowerManager;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Slog;
+import android.view.IWindowManager;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+
+public class BrightnessController implements ToggleSlider.Listener {
+    private static final String TAG = "StatusBar.BrightnessController";
+
+    private final int mMinimumBacklight;
+    private final int mMaximumBacklight;
+
+    private final Context mContext;
+    private final ImageView mIcon;
+    private final ToggleSlider mControl;
+    private final boolean mAutomaticAvailable;
+    private final IPowerManager mPower;
+    private final CurrentUserTracker mUserTracker;
+    private final Handler mHandler;
+    private final BrightnessObserver mBrightnessObserver;
+
+    private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks =
+            new ArrayList<BrightnessStateChangeCallback>();
+
+    public interface BrightnessStateChangeCallback {
+        public void onBrightnessLevelChanged();
+    }
+
+    /** ContentObserver to watch brightness **/
+    private class BrightnessObserver extends ContentObserver {
+
+        private final Uri BRIGHTNESS_MODE_URI =
+                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
+        private final Uri BRIGHTNESS_URI =
+                Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
+
+        public BrightnessObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            onChange(selfChange, null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (selfChange) return;
+            if (BRIGHTNESS_MODE_URI.equals(uri)) {
+                updateMode();
+            } else if (BRIGHTNESS_URI.equals(uri)) {
+                updateSlider();
+            } else {
+                updateMode();
+                updateSlider();
+            }
+            for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
+                cb.onBrightnessLevelChanged();
+            }
+        }
+
+        public void startObserving() {
+            final ContentResolver cr = mContext.getContentResolver();
+            cr.unregisterContentObserver(this);
+            cr.registerContentObserver(
+                    BRIGHTNESS_MODE_URI,
+                    false, this, UserHandle.USER_ALL);
+            cr.registerContentObserver(
+                    BRIGHTNESS_URI,
+                    false, this, UserHandle.USER_ALL);
+        }
+
+        public void stopObserving() {
+            final ContentResolver cr = mContext.getContentResolver();
+            cr.unregisterContentObserver(this);
+        }
+
+    }
+
+    public BrightnessController(Context context, ImageView icon, ToggleSlider control) {
+        mContext = context;
+        mIcon = icon;
+        mControl = control;
+        mHandler = new Handler();
+        mUserTracker = new CurrentUserTracker(mContext) {
+            @Override
+            public void onUserSwitched(int newUserId) {
+                updateMode();
+                updateSlider();
+            }
+        };
+        mBrightnessObserver = new BrightnessObserver(mHandler);
+        mBrightnessObserver.startObserving();
+
+        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+        mMinimumBacklight = pm.getMinimumScreenBrightnessSetting();
+        mMaximumBacklight = pm.getMaximumScreenBrightnessSetting();
+
+        mAutomaticAvailable = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_automatic_brightness_available);
+        mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
+
+        // Update the slider and mode before attaching the listener so we don't receive the
+        // onChanged notifications for the initial values.
+        updateMode();
+        updateSlider();
+
+        control.setOnChangedListener(this);
+    }
+
+    public void addStateChangedCallback(BrightnessStateChangeCallback cb) {
+        mChangeCallbacks.add(cb);
+    }
+
+    public boolean removeStateChangedCallback(BrightnessStateChangeCallback cb) {
+        return mChangeCallbacks.remove(cb);
+    }
+
+    @Override
+    public void onInit(ToggleSlider control) {
+        // Do nothing
+    }
+
+    /** Unregister all call backs, both to and from the controller */
+    public void unregisterCallbacks() {
+        mBrightnessObserver.stopObserving();
+        mChangeCallbacks.clear();
+        mUserTracker.stopTracking();
+    }
+
+    public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
+        setMode(automatic ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
+                : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+        updateIcon(automatic);
+        if (!automatic) {
+            final int val = value + mMinimumBacklight;
+            setBrightness(val);
+            if (!tracking) {
+                AsyncTask.execute(new Runnable() {
+                        public void run() {
+                            Settings.System.putIntForUser(mContext.getContentResolver(),
+                                    Settings.System.SCREEN_BRIGHTNESS, val,
+                                    UserHandle.USER_CURRENT);
+                        }
+                    });
+            }
+        }
+
+        for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
+            cb.onBrightnessLevelChanged();
+        }
+    }
+
+    private void setMode(int mode) {
+        Settings.System.putIntForUser(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE, mode,
+                mUserTracker.getCurrentUserId());
+    }
+
+    private void setBrightness(int brightness) {
+        try {
+            mPower.setTemporaryScreenBrightnessSettingOverride(brightness);
+        } catch (RemoteException ex) {
+        }
+    }
+
+    private void updateIcon(boolean automatic) {
+        if (mIcon != null) {
+            mIcon.setImageResource(automatic ?
+                    com.android.systemui.R.drawable.ic_qs_brightness_auto_on :
+                    com.android.systemui.R.drawable.ic_qs_brightness_auto_off);
+        }
+    }
+
+    /** Fetch the brightness mode from the system settings and update the icon */
+    private void updateMode() {
+        if (mAutomaticAvailable) {
+            int automatic;
+            try {
+                automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
+                        Settings.System.SCREEN_BRIGHTNESS_MODE,
+                        UserHandle.USER_CURRENT);
+            } catch (SettingNotFoundException snfe) {
+                automatic = 0;
+            }
+            mControl.setChecked(automatic != 0);
+            updateIcon(automatic != 0);
+        } else {
+            mControl.setChecked(false);
+            updateIcon(false /*automatic*/);
+        }
+    }
+
+    /** Fetch the brightness from the system settings and update the slider */
+    private void updateSlider() {
+        int value;
+        try {
+            value = Settings.System.getIntForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS,
+                    UserHandle.USER_CURRENT);
+        } catch (SettingNotFoundException ex) {
+            value = mMaximumBacklight;
+        }
+        mControl.setMax(mMaximumBacklight - mMinimumBacklight);
+        mControl.setValue(value - mMinimumBacklight);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
new file mode 100644
index 0000000..1b05084
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings;
+
+import com.android.systemui.R;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import java.lang.Runnable;
+
+/** A dialog that provides controls for adjusting the screen brightness. */
+public class BrightnessDialog extends Dialog implements
+        BrightnessController.BrightnessStateChangeCallback {
+
+    private static final String TAG = "BrightnessDialog";
+    private static final boolean DEBUG = false;
+
+    protected Handler mHandler = new Handler();
+
+    private BrightnessController mBrightnessController;
+    private final int mBrightnessDialogLongTimeout;
+    private final int mBrightnessDialogShortTimeout;
+
+    private final Runnable mDismissDialogRunnable = new Runnable() {
+        public void run() {
+            if (BrightnessDialog.this.isShowing()) {
+                BrightnessDialog.this.dismiss();
+            }
+        };
+    };
+
+
+    public BrightnessDialog(Context ctx) {
+        super(ctx);
+        Resources r = ctx.getResources();
+        mBrightnessDialogLongTimeout =
+                r.getInteger(R.integer.quick_settings_brightness_dialog_long_timeout);
+        mBrightnessDialogShortTimeout =
+                r.getInteger(R.integer.quick_settings_brightness_dialog_short_timeout);
+    }
+
+
+    /**
+     * Create the brightness dialog and any resources that are used for the
+     * entire lifetime of the dialog.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Window window = getWindow();
+        window.setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+        window.getAttributes().privateFlags |=
+                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+        window.requestFeature(Window.FEATURE_NO_TITLE);
+
+        setContentView(R.layout.quick_settings_brightness_dialog);
+        setCanceledOnTouchOutside(true);
+    }
+
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mBrightnessController = new BrightnessController(getContext(),
+                (ImageView) findViewById(R.id.brightness_icon),
+                (ToggleSlider) findViewById(R.id.brightness_slider));
+        dismissBrightnessDialog(mBrightnessDialogLongTimeout);
+        mBrightnessController.addStateChangedCallback(this);
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mBrightnessController.unregisterCallbacks();
+        removeAllBrightnessDialogCallbacks();
+    }
+
+    public void onBrightnessLevelChanged() {
+        dismissBrightnessDialog(mBrightnessDialogShortTimeout);
+    }
+
+    private void dismissBrightnessDialog(int timeout) {
+        removeAllBrightnessDialogCallbacks();
+        mHandler.postDelayed(mDismissDialogRunnable, timeout);
+    }
+
+    private void removeAllBrightnessDialogCallbacks() {
+        mHandler.removeCallbacks(mDismissDialogRunnable);
+    }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
new file mode 100644
index 0000000..122f81e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+
+public abstract class CurrentUserTracker extends BroadcastReceiver {
+
+    private Context mContext;
+    private int mCurrentUserId;
+
+    public CurrentUserTracker(Context context) {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+        context.registerReceiver(this, filter);
+        mCurrentUserId = ActivityManager.getCurrentUser();
+        mContext = context;
+    }
+
+    public int getCurrentUserId() {
+        return mCurrentUserId;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+            int oldUserId = mCurrentUserId;
+            mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+            if (oldUserId != mCurrentUserId) {
+                onUserSwitched(mCurrentUserId);
+            }
+        }
+    }
+
+    public void stopTracking() {
+        mContext.unregisterReceiver(this);
+    }
+
+    public abstract void onUserSwitched(int newUserId);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java b/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java
new file mode 100644
index 0000000..f65123a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/settings/SettingsUI.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.settings;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.util.Slog;
+
+import com.android.systemui.SystemUI;
+
+public class SettingsUI extends SystemUI {
+    private static final String TAG = "SettingsUI";
+    private static final boolean DEBUG = false;
+
+    private final Handler mHandler = new Handler();
+    private BrightnessDialog mBrightnessDialog;
+
+    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) {
+                if (DEBUG) Slog.d(TAG, "showing brightness dialog");
+
+                if (mBrightnessDialog == null) {
+                    mBrightnessDialog = new BrightnessDialog(mContext);
+                    mBrightnessDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
+                        @Override
+                        public void onDismiss(DialogInterface dialog) {
+                            mBrightnessDialog = null;
+                        }
+                    });
+                }
+
+                if (!mBrightnessDialog.isShowing()) {
+                    mBrightnessDialog.show();
+                }
+
+            } else {
+                Slog.w(TAG, "unknown intent: " + intent);
+            }
+        }
+    };
+
+    public void start() {
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
+        mContext.registerReceiver(mIntentReceiver, filter, null, mHandler);
+    }
+
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.print("mBrightnessDialog=");
+        pw.println(mBrightnessDialog == null ? "null" : mBrightnessDialog.toString());
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
similarity index 96%
rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java
rename to packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
index 39f8fcc..c7c361c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/ToggleSlider.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010 The Android Open Source Project
+ * Copyright (C) 2013 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.policy;
+package com.android.systemui.settings;
 
 import android.content.Context;
 import android.content.res.Resources;
@@ -31,7 +31,7 @@
 
 import com.android.systemui.R;
 
-public class ToggleSlider extends RelativeLayout 
+public class ToggleSlider extends RelativeLayout
         implements CompoundButton.OnCheckedChangeListener, SeekBar.OnSeekBarChangeListener {
     private static final String TAG = "StatusBar.ToggleSlider";
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index f941f89..4599dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -462,7 +462,6 @@
         mSearchPanelView.setOnTouchListener(
                  new TouchOutsideListener(MSG_CLOSE_SEARCH_PANEL, mSearchPanelView));
         mSearchPanelView.setVisibility(View.GONE);
-        mSearchPanelView.setLayoutDirection(mLayoutDirection);
 
         WindowManager.LayoutParams lp = getSearchLayoutParams(mSearchPanelView.getLayoutParams());
 
@@ -739,7 +738,6 @@
         LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(
                 Context.LAYOUT_INFLATER_SERVICE);
         View row = inflater.inflate(R.layout.status_bar_notification_row, parent, false);
-        row.setLayoutDirection(mLayoutDirection);
 
         // for blaming (see SwipeHelper.setLongPressListener)
         row.setTag(sbn.pkg);
@@ -787,7 +785,6 @@
             params.minHeight = minHeight;
             params.maxHeight = minHeight;
             adaptive.addView(expandedOneU, params);
-            expandedOneU.setLayoutDirection(mLayoutDirection);
         }
         if (expandedLarge != null) {
             SizeAdaptiveLayout.LayoutParams params =
@@ -795,7 +792,6 @@
             params.minHeight = minHeight+1;
             params.maxHeight = maxHeight;
             adaptive.addView(expandedLarge, params);
-            expandedLarge.setLayoutDirection(mLayoutDirection);
         }
         row.setDrawingCacheEnabled(true);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 7b80abc..988951c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -149,9 +149,17 @@
             }
         }
         public float getXVelocity() {
+            if (Float.isNaN(mVX)) {
+                Slog.v("FlingTracker", "warning: vx=NaN");
+                // XXX: should return 0
+            }
             return mVX;
         }
         public float getYVelocity() {
+            if (Float.isNaN(mVY)) {
+                Slog.v("FlingTracker", "warning: vx=NaN");
+                // XXX: should return 0
+            }
             return mVY;
         }
         public void recycle() {
@@ -284,6 +292,9 @@
                     || ((mRubberbanding || !mClosing) && mExpandedHeight == fh)) {
                 post(mStopAnimator);
             }
+        } else {
+            Slog.v(TAG, "animationTick called with dtms=" + dtms + "; nothing to do (h="
+                    + mExpandedHeight + " v=" + mVel + ")");
         }
     }
 
@@ -374,7 +385,7 @@
                         case MotionEvent.ACTION_MOVE:
                             final float h = rawY - mAbsPos[1] - mTouchOffset;
                             if (h > mPeekHeight) {
-                                if (mPeekAnimator != null && mPeekAnimator.isRunning()) {
+                                if (mPeekAnimator != null && mPeekAnimator.isStarted()) {
                                     mPeekAnimator.cancel();
                                 }
                                 mJustPeeked = false;
@@ -505,7 +516,7 @@
     public void setExpandedHeight(float height) {
         if (DEBUG) LOG("setExpandedHeight(%.1f)", height);
         mRubberbanding = false;
-        if (mTimeAnimator.isRunning()) {
+        if (mTimeAnimator.isStarted()) {
             post(mStopAnimator);
         }
         setExpandedHeightInternal(height);
@@ -519,6 +530,11 @@
     }
 
     public void setExpandedHeightInternal(float h) {
+        if (Float.isNaN(h)) {
+            Slog.v(TAG, "setExpandedHeightInternal: warning: h=NaN");
+            // XXX: should set h to 0
+        }
+
         float fh = getFullHeight();
         if (fh == 0) {
             // Hmm, full height hasn't been computed yet
@@ -526,6 +542,7 @@
 
         if (h < 0) h = 0;
         if (!(mRubberbandingEnabled && (mTracking || mRubberbanding)) && h > fh) h = fh;
+
         mExpandedHeight = h;
 
         if (DEBUG) LOG("setExpansion: height=%.1f fh=%.1f tracking=%s rubber=%s", h, fh, mTracking?"T":"f", mRubberbanding?"T":"f");
@@ -548,6 +565,10 @@
     }
 
     public void setExpandedFraction(float frac) {
+        if (Float.isNaN(frac)) {
+            Slog.v(TAG, "setExpandedFraction: frac=NaN");
+            // XXX: set frac to 0 to defend
+        }
         setExpandedHeight(getFullHeight() * frac);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 9b1c1db..024faf7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -348,7 +348,6 @@
                 }
                 return mStatusBarWindow.onTouchEvent(event);
             }});
-        mStatusBarWindow.setLayoutDirection(mLayoutDirection);
 
         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
         mStatusBarView.setBar(this);
@@ -380,7 +379,6 @@
             mIntruderAlertView = (IntruderAlertView) View.inflate(context, R.layout.intruder_alert, null);
             mIntruderAlertView.setVisibility(View.GONE);
             mIntruderAlertView.setBar(this);
-            mIntruderAlertView.setLayoutDirection(mLayoutDirection);
         }
         if (MULTIUSER_DEBUG) {
             mNotificationPanelDebugText = (TextView) mNotificationPanel.findViewById(R.id.header_debug_info);
@@ -398,7 +396,6 @@
 
                 mNavigationBarView.setDisabledFlags(mDisabled);
                 mNavigationBarView.setBar(this);
-                mNavigationBarView.setLayoutDirection(mLayoutDirection);
             }
         } catch (RemoteException ex) {
             // no window manager? good luck with that
@@ -963,11 +960,6 @@
 
     @Override
     protected void refreshLayout(int layoutDirection) {
-        mStatusBarWindow.setLayoutDirection(layoutDirection);
-        if (ENABLE_INTRUDERS) {
-            mIntruderAlertView.setLayoutDirection(layoutDirection);
-        }
-
         if (mNavigationBarView != null) {
             mNavigationBarView.setLayoutDirection(layoutDirection);
         }
@@ -1030,7 +1022,6 @@
             View v = toShow.get(i);
             if (v.getParent() == null) {
                 mPile.addView(v, i);
-                v.setLayoutDirection(mLayoutDirection);
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 4ff3862..60e22c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -27,10 +27,8 @@
 import com.android.systemui.statusbar.phone.QuickSettingsModel.WifiState;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
-import com.android.systemui.statusbar.policy.BrightnessController;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
-import com.android.systemui.statusbar.policy.ToggleSlider;
 
 import android.app.ActivityManagerNative;
 import android.app.AlertDialog;
@@ -70,7 +68,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.Window;
 import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.widget.ImageView;
@@ -100,13 +97,8 @@
     private BluetoothAdapter mBluetoothAdapter;
     private WifiManager mWifiManager;
 
-    private BrightnessController mBrightnessController;
     private BluetoothController mBluetoothController;
 
-    private Dialog mBrightnessDialog;
-    private int mBrightnessDialogShortTimeout;
-    private int mBrightnessDialogLongTimeout;
-
     private AsyncTask<Void, Void, Pair<String, Drawable>> mUserInfoTask;
 
     private LevelListDrawable mBatteryLevels;
@@ -146,10 +138,6 @@
         mBatteryLevels = (LevelListDrawable) r.getDrawable(R.drawable.qs_sys_battery);
         mChargingBatteryLevels =
                 (LevelListDrawable) r.getDrawable(R.drawable.qs_sys_battery_charging);
-        mBrightnessDialogLongTimeout =
-                r.getInteger(R.integer.quick_settings_brightness_dialog_long_timeout);
-        mBrightnessDialogShortTimeout =
-                r.getInteger(R.integer.quick_settings_brightness_dialog_short_timeout);
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED);
@@ -352,7 +340,6 @@
                 TextView tv = (TextView) view.findViewById(R.id.brightness_textview);
                 tv.setCompoundDrawablesWithIntrinsicBounds(0, state.iconId, 0, 0);
                 tv.setText(state.label);
-                dismissBrightnessDialog(mBrightnessDialogShortTimeout);
             }
         });
         parent.addView(brightnessTile);
@@ -776,72 +763,12 @@
         }
         ((QuickSettingsContainerView)mContainerView).updateResources();
         mContainerView.requestLayout();
-
-        // Reset the dialog
-        boolean isBrightnessDialogVisible = false;
-        if (mBrightnessDialog != null) {
-            removeAllBrightnessDialogCallbacks();
-
-            isBrightnessDialogVisible = mBrightnessDialog.isShowing();
-            mBrightnessDialog.dismiss();
-        }
-        mBrightnessDialog = null;
-        if (isBrightnessDialogVisible) {
-            showBrightnessDialog();
-        }
     }
 
-    private void removeAllBrightnessDialogCallbacks() {
-        mHandler.removeCallbacks(mDismissBrightnessDialogRunnable);
-    }
-
-    private Runnable mDismissBrightnessDialogRunnable = new Runnable() {
-        public void run() {
-            if (mBrightnessDialog != null && mBrightnessDialog.isShowing()) {
-                mBrightnessDialog.dismiss();
-            }
-            removeAllBrightnessDialogCallbacks();
-        };
-    };
 
     private void showBrightnessDialog() {
-        if (mBrightnessDialog == null) {
-            mBrightnessDialog = new Dialog(mContext);
-            mBrightnessDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
-            mBrightnessDialog.setContentView(R.layout.quick_settings_brightness_dialog);
-            mBrightnessDialog.setCanceledOnTouchOutside(true);
-
-            mBrightnessController = new BrightnessController(mContext,
-                    (ImageView) mBrightnessDialog.findViewById(R.id.brightness_icon),
-                    (ToggleSlider) mBrightnessDialog.findViewById(R.id.brightness_slider));
-            mBrightnessController.addStateChangedCallback(mModel);
-            mBrightnessDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
-                @Override
-                public void onDismiss(DialogInterface dialog) {
-                    mBrightnessController = null;
-                }
-            });
-
-            mBrightnessDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
-            mBrightnessDialog.getWindow().getAttributes().privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-            mBrightnessDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
-        }
-        if (!mBrightnessDialog.isShowing()) {
-            try {
-                WindowManagerGlobal.getWindowManagerService().dismissKeyguard();
-            } catch (RemoteException e) {
-            }
-            mBrightnessDialog.show();
-            dismissBrightnessDialog(mBrightnessDialogLongTimeout);
-        }
-    }
-
-    private void dismissBrightnessDialog(int timeout) {
-        removeAllBrightnessDialogCallbacks();
-        if (mBrightnessDialog != null) {
-            mHandler.postDelayed(mDismissBrightnessDialogRunnable, timeout);
-        }
+        Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
+        mContext.sendBroadcast(intent);
     }
 
     private void showBugreportDialog() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
index 1037137..435ea4c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettingsModel.java
@@ -42,9 +42,9 @@
 
 import com.android.internal.view.RotationPolicy;
 import com.android.systemui.R;
+import com.android.systemui.settings.CurrentUserTracker;
+import com.android.systemui.settings.BrightnessController.BrightnessStateChangeCallback;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
-import com.android.systemui.statusbar.policy.BrightnessController.BrightnessStateChangeCallback;
-import com.android.systemui.statusbar.policy.CurrentUserTracker;
 import com.android.systemui.statusbar.policy.LocationController.LocationGpsStateChangeCallback;
 import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback;
 
@@ -239,10 +239,12 @@
         mContext = context;
         mHandler = new Handler();
         mUserTracker = new CurrentUserTracker(mContext) {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                super.onReceive(context, intent);
-                onUserSwitched();
+            public void onUserSwitched(int newUserId) {
+                mBrightnessObserver.startObserving();
+                onRotationLockChanged();
+                onBrightnessLevelChanged();
+                onNextAlarmChanged();
+                onBugreportChanged();
             }
         };
 
@@ -705,13 +707,4 @@
     void refreshBrightnessTile() {
         onBrightnessLevelChanged();
     }
-
-    // User switch: need to update visuals of all tiles known to have per-user state
-    void onUserSwitched() {
-        mBrightnessObserver.startObserving();
-        onRotationLockChanged();
-        onBrightnessLevelChanged();
-        onNextAlarmChanged();
-        onBugreportChanged();
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java
deleted file mode 100644
index e18b28a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessController.java
+++ /dev/null
@@ -1,153 +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.
- */
-
-package com.android.systemui.statusbar.policy;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.os.AsyncTask;
-import android.os.IPowerManager;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.util.Slog;
-import android.view.IWindowManager;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-
-import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
-
-import java.util.ArrayList;
-
-public class BrightnessController implements ToggleSlider.Listener {
-    private static final String TAG = "StatusBar.BrightnessController";
-
-    private final int mMinimumBacklight;
-    private final int mMaximumBacklight;
-
-    private final Context mContext;
-    private final ImageView mIcon;
-    private final ToggleSlider mControl;
-    private final boolean mAutomaticAvailable;
-    private final IPowerManager mPower;
-    private final CurrentUserTracker mUserTracker;
-
-    private ArrayList<BrightnessStateChangeCallback> mChangeCallbacks =
-            new ArrayList<BrightnessStateChangeCallback>();
-
-    public interface BrightnessStateChangeCallback {
-        public void onBrightnessLevelChanged();
-    }
-
-    public BrightnessController(Context context, ImageView icon, ToggleSlider control) {
-        mContext = context;
-        mIcon = icon;
-        mControl = control;
-        mUserTracker = new CurrentUserTracker(mContext);
-
-        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        mMinimumBacklight = pm.getMinimumScreenBrightnessSetting();
-        mMaximumBacklight = pm.getMaximumScreenBrightnessSetting();
-
-        mAutomaticAvailable = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_automatic_brightness_available);
-        mPower = IPowerManager.Stub.asInterface(ServiceManager.getService("power"));
-
-        control.setOnChangedListener(this);
-    }
-
-    public void addStateChangedCallback(BrightnessStateChangeCallback cb) {
-        mChangeCallbacks.add(cb);
-    }
-
-    @Override
-    public void onInit(ToggleSlider control) {
-        if (mAutomaticAvailable) {
-            int automatic;
-            try {
-                automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS_MODE,
-                        mUserTracker.getCurrentUserId());
-            } catch (SettingNotFoundException snfe) {
-                automatic = 0;
-            }
-            control.setChecked(automatic != 0);
-            updateIcon(automatic != 0);
-        } else {
-            control.setChecked(false);
-            updateIcon(false /*automatic*/);
-            //control.hideToggle();
-        }
-        
-        int value;
-        try {
-            value = Settings.System.getIntForUser(mContext.getContentResolver(),
-                    Settings.System.SCREEN_BRIGHTNESS,
-                    mUserTracker.getCurrentUserId());
-        } catch (SettingNotFoundException ex) {
-            value = mMaximumBacklight;
-        }
-
-        control.setMax(mMaximumBacklight - mMinimumBacklight);
-        control.setValue(value - mMinimumBacklight);
-    }
-
-    public void onChanged(ToggleSlider view, boolean tracking, boolean automatic, int value) {
-        setMode(automatic ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC
-                : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
-        updateIcon(automatic);
-        if (!automatic) {
-            final int val = value + mMinimumBacklight;
-            setBrightness(val);
-            if (!tracking) {
-                AsyncTask.execute(new Runnable() {
-                        public void run() {
-                            Settings.System.putIntForUser(mContext.getContentResolver(),
-                                    Settings.System.SCREEN_BRIGHTNESS, val,
-                                    mUserTracker.getCurrentUserId());
-                        }
-                    });
-            }
-        }
-
-        for (BrightnessStateChangeCallback cb : mChangeCallbacks) {
-            cb.onBrightnessLevelChanged();
-        }
-    }
-
-    private void setMode(int mode) {
-        Settings.System.putIntForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE, mode,
-                mUserTracker.getCurrentUserId());
-    }
-    
-    private void setBrightness(int brightness) {
-        try {
-            mPower.setTemporaryScreenBrightnessSettingOverride(brightness);
-        } catch (RemoteException ex) {
-        }        
-    }
-
-    private void updateIcon(boolean automatic) {
-        if (mIcon != null) {
-            mIcon.setImageResource(automatic ?
-                    com.android.systemui.R.drawable.ic_qs_brightness_auto_on :
-                    com.android.systemui.R.drawable.ic_qs_brightness_auto_off);
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java
index 6fee432..70f9ac8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/VolumeController.java
@@ -27,6 +27,8 @@
 import android.view.IWindowManager;
 import android.widget.CompoundButton;
 
+import com.android.systemui.settings.ToggleSlider;
+
 public class VolumeController implements ToggleSlider.Listener {
     private static final String TAG = "StatusBar.VolumeController";
     private static final int STREAM = AudioManager.STREAM_NOTIFICATION;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
index f71842e..e0dcbcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/SettingsView.java
@@ -30,11 +30,11 @@
 import android.widget.TextView;
 
 import com.android.systemui.R;
+import com.android.systemui.settings.BrightnessController;
+import com.android.systemui.settings.ToggleSlider;
 import com.android.systemui.statusbar.policy.AirplaneModeController;
 import com.android.systemui.statusbar.policy.AutoRotateController;
-import com.android.systemui.statusbar.policy.BrightnessController;
 import com.android.systemui.statusbar.policy.DoNotDisturbController;
-import com.android.systemui.statusbar.policy.ToggleSlider;
 import com.android.systemui.statusbar.policy.VolumeController;
 
 public class SettingsView extends LinearLayout implements View.OnClickListener {
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 ccd87f7..3d6bfe7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -247,7 +247,6 @@
         mNotificationPanel.show(false, false);
         mNotificationPanel.setOnTouchListener(
                 new TouchOutsideListener(MSG_CLOSE_NOTIFICATION_PANEL, mNotificationPanel));
-        mNotificationPanel.setLayoutDirection(mLayoutDirection);
 
         // the battery icon
         mBatteryController.addIconView((ImageView)mNotificationPanel.findViewById(R.id.battery));
@@ -313,7 +312,6 @@
         mInputMethodsPanel.setOnTouchListener(new TouchOutsideListener(
                 MSG_CLOSE_INPUT_METHODS_PANEL, mInputMethodsPanel));
         mInputMethodsPanel.setImeSwitchButton(mInputMethodSwitchButton);
-        mInputMethodsPanel.setLayoutDirection(mLayoutDirection);
         mStatusBarView.setIgnoreChildren(2, mInputMethodSwitchButton, mInputMethodsPanel);
         lp = new WindowManager.LayoutParams(
                 ViewGroup.LayoutParams.WRAP_CONTENT,
@@ -337,7 +335,6 @@
                 MSG_CLOSE_COMPAT_MODE_PANEL, mCompatModePanel));
         mCompatModePanel.setTrigger(mCompatModeButton);
         mCompatModePanel.setVisibility(View.GONE);
-        mCompatModePanel.setLayoutDirection(mLayoutDirection);
         mStatusBarView.setIgnoreChildren(3, mCompatModeButton, mCompatModePanel);
         lp = new WindowManager.LayoutParams(
                 250,
@@ -395,13 +392,7 @@
 
     @Override
     protected void refreshLayout(int layoutDirection) {
-        mStatusBarView.setLayoutDirection(layoutDirection);
-        if (mCompatibilityHelpDialog != null) {
-            mCompatibilityHelpDialog.setLayoutDirection(layoutDirection);
-        }
         mNotificationPanel.refreshLayout(layoutDirection);
-        mInputMethodsPanel.setLayoutDirection(layoutDirection);
-        mCompatModePanel.setLayoutDirection(layoutDirection);
     }
 
     protected void loadDimens() {
@@ -463,7 +454,6 @@
         final TabletStatusBarView sb = (TabletStatusBarView)View.inflate(
                 context, R.layout.system_bar, null);
         mStatusBarView = sb;
-        mStatusBarView.setLayoutDirection(mLayoutDirection);
 
         sb.setHandler(mHandler);
 
@@ -1134,7 +1124,6 @@
         }
 
         mCompatibilityHelpDialog = View.inflate(mContext, R.layout.compat_mode_help, null);
-        mCompatibilityHelpDialog.setLayoutDirection(mLayoutDirection);
         View button = mCompatibilityHelpDialog.findViewById(R.id.button);
 
         button.setOnClickListener(new View.OnClickListener() {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 88a0ef3..e1d9b73 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -20,11 +20,7 @@
 import static android.view.View.MeasureSpec.getMode;
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
+import static android.view.WindowManager.LayoutParams.*;
 
 import com.android.internal.view.RootViewSurfaceTaker;
 import com.android.internal.view.StandaloneActionMode;
@@ -2639,7 +2635,11 @@
         }
 
         if (a.getBoolean(com.android.internal.R.styleable.Window_windowFullscreen, false)) {
-            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN&(~getForcedWindowFlags()));
+            setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));
+        }
+
+        if (a.getBoolean(com.android.internal.R.styleable.Window_windowOverscan, false)) {
+            setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));
         }
 
         if (a.getBoolean(com.android.internal.R.styleable.Window_windowShowWallpaper, false)) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 9cb54a9..acbde9b 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -101,55 +101,7 @@
 import java.io.IOException;
 import java.io.PrintWriter;
 
-import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
-import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
-import static android.view.WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAG;
-import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
-import static android.view.WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_PHONE;
-import static android.view.WindowManager.LayoutParams.TYPE_POINTER;
-import static android.view.WindowManager.LayoutParams.TYPE_PRIORITY_PHONE;
-import static android.view.WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
-import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
-import static android.view.WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND;
-import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.*;
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN;
 import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
@@ -353,10 +305,17 @@
     PointerLocationView mPointerLocationView;
     InputChannel mPointerLocationInputChannel;
 
-    // The current size of the screen; really; (ir)regardless of whether the status
-    // bar can be hidden or not
+    // The current size of the screen; really; extends into the overscan area of
+    // the screen and doesn't account for any system elements like the status bar.
+    int mOverscanScreenLeft, mOverscanScreenTop;
+    int mOverscanScreenWidth, mOverscanScreenHeight;
+    // The current visible size of the screen; really; (ir)regardless of whether the status
+    // bar can be hidden but not extending into the overscan area.
     int mUnrestrictedScreenLeft, mUnrestrictedScreenTop;
     int mUnrestrictedScreenWidth, mUnrestrictedScreenHeight;
+    // Like mOverscanScreen*, but allowed to move into the overscan region where appropriate.
+    int mRestrictedOverscanScreenLeft, mRestrictedOverscanScreenTop;
+    int mRestrictedOverscanScreenWidth, mRestrictedOverscanScreenHeight;
     // The current size of the screen; these may be different than (0,0)-(dw,dh)
     // if the status bar can't be hidden; in that case it effectively carves out
     // that area of the display from all other windows.
@@ -455,6 +414,11 @@
     int mPortraitRotation = 0;   // default portrait rotation
     int mUpsideDownRotation = 0; // "other" portrait rotation
 
+    int mOverscanLeft = 0;
+    int mOverscanTop = 0;
+    int mOverscanRight = 0;
+    int mOverscanBottom = 0;
+
     // What we do when the user long presses on home
     private int mLongPressOnHomeBehavior = -1;
 
@@ -946,6 +910,9 @@
 
     @Override
     public void setInitialDisplaySize(Display display, int width, int height, int density) {
+        if (display.getDisplayId() != Display.DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can only set the default display");
+        }
         mDisplay = display;
 
         int shortSize, longSize;
@@ -1056,6 +1023,16 @@
         mHdmiRotationLock = SystemProperties.getBoolean("persist.demo.hdmirotationlock", false);
     }
 
+    @Override
+    public void setDisplayOverscan(Display display, int left, int top, int right, int bottom) {
+        if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+            mOverscanLeft = left;
+            mOverscanTop = top;
+            mOverscanRight = right;
+            mOverscanBottom = bottom;
+        }
+    }
+
     public void updateSettings() {
         ContentResolver resolver = mContext.getContentResolver();
         boolean updateRotation = false;
@@ -2388,7 +2365,7 @@
                     contentInset.set(mStableLeft, mStableTop,
                             availRight - mStableRight, availBottom - mStableBottom);
                 }
-            } else if ((fl & FLAG_FULLSCREEN) != 0) {
+            } else if ((fl & FLAG_FULLSCREEN) != 0 || (fl & FLAG_LAYOUT_IN_OVERSCAN) != 0) {
                 contentInset.setEmpty();
             } else if ((systemUiVisibility & (View.SYSTEM_UI_FLAG_FULLSCREEN
                         | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)) == 0) {
@@ -2407,20 +2384,64 @@
     @Override
     public void beginLayoutLw(boolean isDefaultDisplay, int displayWidth, int displayHeight,
                               int displayRotation) {
-        mUnrestrictedScreenLeft = mUnrestrictedScreenTop = 0;
-        mUnrestrictedScreenWidth = displayWidth;
-        mUnrestrictedScreenHeight = displayHeight;
-        mRestrictedScreenLeft = mRestrictedScreenTop = 0;
-        mRestrictedScreenWidth = displayWidth;
-        mRestrictedScreenHeight = displayHeight;
+        final int overscanLeft, overscanTop, overscanRight, overscanBottom;
+        if (isDefaultDisplay) {
+            switch (displayRotation) {
+                case Surface.ROTATION_90:
+                    overscanLeft = mOverscanTop;
+                    overscanTop = mOverscanRight;
+                    overscanRight = mOverscanBottom;
+                    overscanBottom = mOverscanLeft;
+                    break;
+                case Surface.ROTATION_180:
+                    overscanLeft = mOverscanRight;
+                    overscanTop = mOverscanBottom;
+                    overscanRight = mOverscanLeft;
+                    overscanBottom = mOverscanTop;
+                    break;
+                case Surface.ROTATION_270:
+                    overscanLeft = mOverscanBottom;
+                    overscanTop = mOverscanLeft;
+                    overscanRight = mOverscanTop;
+                    overscanBottom = mOverscanRight;
+                    break;
+                default:
+                    overscanLeft = mOverscanLeft;
+                    overscanTop = mOverscanTop;
+                    overscanRight = mOverscanRight;
+                    overscanBottom = mOverscanBottom;
+                    break;
+            }
+        } else {
+            overscanLeft = 0;
+            overscanTop = 0;
+            overscanRight = 0;
+            overscanBottom = 0;
+        }
+        mOverscanScreenLeft = mRestrictedOverscanScreenLeft = 0;
+        mOverscanScreenTop = mRestrictedOverscanScreenTop = 0;
+        mOverscanScreenWidth = mRestrictedOverscanScreenWidth = displayWidth;
+        mOverscanScreenHeight = mRestrictedOverscanScreenHeight = displayHeight;
+        mSystemLeft = 0;
+        mSystemTop = 0;
+        mSystemRight = displayWidth;
+        mSystemBottom = displayHeight;
+        mUnrestrictedScreenLeft = overscanLeft;
+        mUnrestrictedScreenTop = overscanTop;
+        mUnrestrictedScreenWidth = displayWidth - overscanLeft - overscanRight;
+        mUnrestrictedScreenHeight = displayHeight - overscanTop - overscanBottom;
+        mRestrictedScreenLeft = mUnrestrictedScreenLeft;
+        mRestrictedScreenTop = mUnrestrictedScreenTop;
+        mRestrictedScreenWidth = mUnrestrictedScreenWidth;
+        mRestrictedScreenHeight = mUnrestrictedScreenHeight;
         mDockLeft = mContentLeft = mStableLeft = mStableFullscreenLeft
-                = mSystemLeft = mCurLeft = 0;
+                = mCurLeft = mUnrestrictedScreenLeft;
         mDockTop = mContentTop = mStableTop = mStableFullscreenTop
-                = mSystemTop = mCurTop = 0;
+                = mCurTop = mUnrestrictedScreenTop;
         mDockRight = mContentRight = mStableRight = mStableFullscreenRight
-                = mSystemRight = mCurRight = displayWidth;
+                = mCurRight = displayWidth - overscanRight;
         mDockBottom = mContentBottom = mStableBottom = mStableFullscreenBottom
-                = mSystemBottom = mCurBottom = displayHeight;
+                = mCurBottom = displayHeight - overscanBottom;
         mDockLayer = 0x10000000;
         mStatusBarLayer = -1;
 
@@ -2467,13 +2488,15 @@
                 mNavigationBarOnBottom = (!mNavigationBarCanMove || displayWidth < displayHeight);
                 if (mNavigationBarOnBottom) {
                     // It's a system nav bar or a portrait screen; nav bar goes on bottom.
-                    int top = displayHeight - mNavigationBarHeightForRotation[displayRotation];
-                    mTmpNavigationFrame.set(0, top, displayWidth, displayHeight);
+                    int top = displayHeight - overscanBottom
+                            - mNavigationBarHeightForRotation[displayRotation];
+                    mTmpNavigationFrame.set(0, top, displayWidth, displayHeight - overscanBottom);
                     mStableBottom = mStableFullscreenBottom = mTmpNavigationFrame.top;
                     if (navVisible) {
                         mNavigationBar.showLw(true);
                         mDockBottom = mTmpNavigationFrame.top;
-                        mRestrictedScreenHeight = mDockBottom - mDockTop;
+                        mRestrictedScreenHeight = mDockBottom - mRestrictedScreenTop;
+                        mRestrictedOverscanScreenHeight = mDockBottom - mRestrictedOverscanScreenTop;
                     } else {
                         // We currently want to hide the navigation UI.
                         mNavigationBar.hideLw(true);
@@ -2486,13 +2509,15 @@
                     }
                 } else {
                     // Landscape screen; nav bar goes to the right.
-                    int left = displayWidth - mNavigationBarWidthForRotation[displayRotation];
-                    mTmpNavigationFrame.set(left, 0, displayWidth, displayHeight);
+                    int left = displayWidth - overscanRight
+                            - mNavigationBarWidthForRotation[displayRotation];
+                    mTmpNavigationFrame.set(left, 0, displayWidth - overscanRight, displayHeight);
                     mStableRight = mStableFullscreenRight = mTmpNavigationFrame.left;
                     if (navVisible) {
                         mNavigationBar.showLw(true);
                         mDockRight = mTmpNavigationFrame.left;
-                        mRestrictedScreenWidth = mDockRight - mDockLeft;
+                        mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
+                        mRestrictedOverscanScreenWidth = mDockRight - mRestrictedOverscanScreenLeft;
                     } else {
                         // We currently want to hide the navigation UI.
                         mNavigationBar.hideLw(true);
@@ -2524,8 +2549,8 @@
                 // apply any navigation bar insets
                 pf.left = df.left = mUnrestrictedScreenLeft;
                 pf.top = df.top = mUnrestrictedScreenTop;
-                pf.right = df.right = mUnrestrictedScreenWidth - mUnrestrictedScreenLeft;
-                pf.bottom = df.bottom = mUnrestrictedScreenHeight - mUnrestrictedScreenTop;
+                pf.right = df.right = mUnrestrictedScreenWidth + mUnrestrictedScreenLeft;
+                pf.bottom = df.bottom = mUnrestrictedScreenHeight + mUnrestrictedScreenTop;
                 vf.left = mStableLeft;
                 vf.top = mStableTop;
                 vf.right = mStableRight;
@@ -2685,12 +2710,12 @@
                 setAttachedWindowFrames(win, fl, adjust, attached, true, pf, df, cf, vf);
             } else {
                 // Give the window full screen.
-                pf.left = df.left = cf.left = mUnrestrictedScreenLeft;
-                pf.top = df.top = cf.top = mUnrestrictedScreenTop;
+                pf.left = df.left = cf.left = mOverscanScreenLeft;
+                pf.top = df.top = cf.top = mOverscanScreenTop;
                 pf.right = df.right = cf.right
-                        = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+                        = mOverscanScreenLeft + mOverscanScreenWidth;
                 pf.bottom = df.bottom = cf.bottom
-                        = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
+                        = mOverscanScreenTop + mOverscanScreenHeight;
             }
         } else  if (attrs.type == TYPE_INPUT_METHOD) {
             pf.left = df.left = cf.left = vf.left = mDockLeft;
@@ -2729,33 +2754,44 @@
                         pf.top = df.top = mUnrestrictedScreenTop;
                         pf.right = df.right = hasNavBar
                                             ? mRestrictedScreenLeft+mRestrictedScreenWidth
-                                            : mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+                                            : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
                         pf.bottom = df.bottom = hasNavBar
                                               ? mRestrictedScreenTop+mRestrictedScreenHeight
-                                              : mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                                              : mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
 
                         if (DEBUG_LAYOUT) {
                             Log.v(TAG, String.format(
                                         "Laying out status bar window: (%d,%d - %d,%d)",
                                         pf.left, pf.top, pf.right, pf.bottom));
                         }
+                    } else if ((attrs.flags&FLAG_LAYOUT_IN_OVERSCAN) != 0
+                            && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+                            && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+                        // Asking to layout into the overscan region, so give it that pure
+                        // unrestricted area.
+                        pf.left = df.left = mOverscanScreenLeft;
+                        pf.top = df.top = mOverscanScreenTop;
+                        pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
+                        pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
                     } else if (mCanHideNavigationBar
                             && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
                             && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
                             && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                         // Asking for layout as if the nav bar is hidden, lets the
-                        // application extend into the unrestricted screen area.  We
+                        // application extend into the unrestricted overscan screen area.  We
                         // only do this for application windows to ensure no window that
                         // can be above the nav bar can do this.
-                        pf.left = df.left = mUnrestrictedScreenLeft;
-                        pf.top = df.top = mUnrestrictedScreenTop;
-                        pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
-                        pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                        pf.left = df.left = mOverscanScreenLeft;
+                        pf.top = df.top = mOverscanScreenTop;
+                        pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
+                        pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
                     } else {
-                        pf.left = df.left = mRestrictedScreenLeft;
-                        pf.top = df.top = mRestrictedScreenTop;
-                        pf.right = df.right = mRestrictedScreenLeft+mRestrictedScreenWidth;
-                        pf.bottom = df.bottom = mRestrictedScreenTop+mRestrictedScreenHeight;
+                        pf.left = df.left = mRestrictedOverscanScreenLeft;
+                        pf.top = df.top = mRestrictedOverscanScreenTop;
+                        pf.right = df.right = mRestrictedOverscanScreenLeft
+                                + mRestrictedOverscanScreenWidth;
+                        pf.bottom = df.bottom = mRestrictedOverscanScreenTop
+                                + mRestrictedOverscanScreenHeight;
                     }
 
                     if (adjust != SOFT_INPUT_ADJUST_RESIZE) {
@@ -2793,10 +2829,10 @@
                     pf.top = df.top = cf.top = mUnrestrictedScreenTop;
                     pf.right = df.right = cf.right = hasNavBar
                                         ? mRestrictedScreenLeft+mRestrictedScreenWidth
-                                        : mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+                                        : mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
                     pf.bottom = df.bottom = cf.bottom = hasNavBar
                                           ? mRestrictedScreenTop+mRestrictedScreenHeight
-                                          : mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                                          : mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
                     if (DEBUG_LAYOUT) {
                         Log.v(TAG, String.format(
                                     "Laying out IN_SCREEN status bar window: (%d,%d - %d,%d)",
@@ -2807,8 +2843,8 @@
                     // The navigation bar has Real Ultimate Power.
                     pf.left = df.left = mUnrestrictedScreenLeft;
                     pf.top = df.top = mUnrestrictedScreenTop;
-                    pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
-                    pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                    pf.right = df.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
+                    pf.bottom = df.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
                     if (DEBUG_LAYOUT) {
                         Log.v(TAG, String.format(
                                     "Laying out navigation bar window: (%d,%d - %d,%d)",
@@ -2818,18 +2854,37 @@
                                 || attrs.type == TYPE_BOOT_PROGRESS)
                         && ((fl & FLAG_FULLSCREEN) != 0)) {
                     // Fullscreen secure system overlays get what they ask for.
-                    pf.left = df.left = mUnrestrictedScreenLeft;
-                    pf.top = df.top = mUnrestrictedScreenTop;
-                    pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
-                    pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                    pf.left = df.left = mOverscanScreenLeft;
+                    pf.top = df.top = mOverscanScreenTop;
+                    pf.right = df.right = mOverscanScreenLeft + mOverscanScreenWidth;
+                    pf.bottom = df.bottom = mOverscanScreenTop + mOverscanScreenHeight;
                 } else if (attrs.type == TYPE_BOOT_PROGRESS
                         || attrs.type == TYPE_UNIVERSE_BACKGROUND) {
                     // Boot progress screen always covers entire display.
-                    pf.left = df.left = cf.left = mUnrestrictedScreenLeft;
-                    pf.top = df.top = cf.top = mUnrestrictedScreenTop;
-                    pf.right = df.right = cf.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+                    pf.left = df.left = cf.left = mOverscanScreenLeft;
+                    pf.top = df.top = cf.top = mOverscanScreenTop;
+                    pf.right = df.right = cf.right = mOverscanScreenLeft + mOverscanScreenWidth;
                     pf.bottom = df.bottom = cf.bottom
-                            = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                            = mOverscanScreenTop + mOverscanScreenHeight;
+                } else if (attrs.type == WindowManager.LayoutParams.TYPE_WALLPAPER) {
+                    // The wallpaper mostly goes into the overscan region.
+                    pf.left = df.left = cf.left = mRestrictedOverscanScreenLeft;
+                    pf.top = df.top = cf.top = mRestrictedOverscanScreenTop;
+                    pf.right = df.right = cf.right
+                            = mRestrictedOverscanScreenLeft + mRestrictedOverscanScreenWidth;
+                    pf.bottom = df.bottom = cf.bottom
+                            = mRestrictedOverscanScreenTop + mRestrictedOverscanScreenHeight;
+                } else if ((attrs.flags & FLAG_LAYOUT_IN_OVERSCAN) != 0
+                        && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
+                        && attrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+                    // Asking to layout into the overscan region, so give it that pure
+                    // unrestricted area.
+                    pf.left = df.left = cf.left = mOverscanScreenLeft;
+                    pf.top = df.top = cf.top = mOverscanScreenTop;
+                    pf.right = df.right = cf.right
+                            = mOverscanScreenLeft + mOverscanScreenWidth;
+                    pf.bottom = df.bottom = cf.bottom
+                            = mOverscanScreenTop + mOverscanScreenHeight;
                 } else if (mCanHideNavigationBar
                         && (sysUiFl & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) != 0
                         && attrs.type >= WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW
@@ -2843,9 +2898,9 @@
                     // what the screen would be if only laying out to hide the nav bar.
                     pf.left = df.left = cf.left = mUnrestrictedScreenLeft;
                     pf.top = df.top = cf.top = mUnrestrictedScreenTop;
-                    pf.right = df.right = cf.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth;
+                    pf.right = df.right = cf.right = mUnrestrictedScreenLeft + mUnrestrictedScreenWidth;
                     pf.bottom = df.bottom = cf.bottom
-                            = mUnrestrictedScreenTop+mUnrestrictedScreenHeight;
+                            = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
                 } else {
                     pf.left = df.left = cf.left = mRestrictedScreenLeft;
                     pf.top = df.top = cf.top = mRestrictedScreenTop;
@@ -4661,6 +4716,22 @@
         pw.print(prefix); pw.print("mScreenOnEarly="); pw.print(mScreenOnEarly);
                 pw.print(" mScreenOnFully="); pw.print(mScreenOnFully);
                 pw.print(" mOrientationSensorEnabled="); pw.println(mOrientationSensorEnabled);
+        pw.print(prefix); pw.print("mOverscanScreen=("); pw.print(mOverscanScreenLeft);
+                pw.print(","); pw.print(mOverscanScreenTop);
+                pw.print(") "); pw.print(mOverscanScreenWidth);
+                pw.print("x"); pw.println(mOverscanScreenHeight);
+        if (mOverscanLeft != 0 || mOverscanTop != 0
+                || mOverscanRight != 0 || mOverscanBottom != 0) {
+            pw.print(prefix); pw.print("mOverscan left="); pw.print(mOverscanLeft);
+                    pw.print(" top="); pw.print(mOverscanTop);
+                    pw.print(" right="); pw.print(mOverscanRight);
+                    pw.print(" bottom="); pw.println(mOverscanBottom);
+        }
+        pw.print(prefix); pw.print("mRestrictedOverscanScreen=(");
+                pw.print(mRestrictedOverscanScreenLeft);
+                pw.print(","); pw.print(mRestrictedOverscanScreenTop);
+                pw.print(") "); pw.print(mRestrictedOverscanScreenWidth);
+                pw.print("x"); pw.println(mRestrictedOverscanScreenHeight);
         pw.print(prefix); pw.print("mUnrestrictedScreen=("); pw.print(mUnrestrictedScreenLeft);
                 pw.print(","); pw.print(mUnrestrictedScreenTop);
                 pw.print(") "); pw.print(mUnrestrictedScreenWidth);
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
index 27d808b..06f06b5 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardHostView.java
@@ -30,6 +30,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentSender;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Canvas;
@@ -102,8 +103,9 @@
 
     private boolean mUserSetupCompleted;
 
-    // User for whom this host view was created
-    private int mUserId;
+    // User for whom this host view was created.  Final because we should never change the
+    // id without reconstructing an instance of KeyguardHostView. See note below...
+    private final int mUserId;
 
     private KeyguardMultiUserSelectorView mKeyguardMultiUserSelectorView;
 
@@ -132,10 +134,45 @@
     public KeyguardHostView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mLockPatternUtils = new LockPatternUtils(context);
+
+        // Note: This depends on KeyguardHostView getting reconstructed every time the
+        // user switches, since mUserId will be used for the entire session.
+        // Once created, keyguard should *never* re-use this instance with another user.
+        // In other words, mUserId should never change - hence it's marked final.
         mUserId = mLockPatternUtils.getCurrentUser();
-        mAppWidgetHost = new AppWidgetHost(
-                context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper());
-        mAppWidgetHost.setUserId(mUserId);
+
+        Context userContext = null;
+        try {
+            final String packageName = "system";
+            userContext = mContext.createPackageContextAsUser(packageName, 0,
+                    new UserHandle(mUserId));
+
+        } catch (NameNotFoundException e) {
+            e.printStackTrace();
+            // This should never happen, but it's better to have no widgets than to crash.
+            userContext = context;
+        }
+
+        // These need to be created with the user context...
+        mAppWidgetHost = new AppWidgetHost(userContext, APPWIDGET_HOST_ID, mOnClickHandler,
+                Looper.myLooper());
+        mAppWidgetManager = AppWidgetManager.getInstance(userContext);
+
+        cleanupAppWidgetIds();
+
+        mSecurityModel = new KeyguardSecurityModel(context);
+
+        mViewStateManager = new KeyguardViewStateManager(this);
+
+        DevicePolicyManager dpm =
+                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        if (dpm != null) {
+            mDisabledFeatures = getDisabledFeatures(dpm);
+            mCameraDisabled = dpm.getCameraDisabled(null);
+        }
+
+        mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
+
         cleanupAppWidgetIds();
 
         mAppWidgetManager = AppWidgetManager.getInstance(mContext);
@@ -143,14 +180,6 @@
 
         mViewStateManager = new KeyguardViewStateManager(this);
 
-        DevicePolicyManager dpm =
-            (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        if (dpm != null) {
-            mDisabledFeatures = getDisabledFeatures(dpm);
-            mCameraDisabled = dpm.getCameraDisabled(null);
-        }
-
-        mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
         mUserSetupCompleted = Settings.Secure.getIntForUser(mContext.getContentResolver(),
                 Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
 
@@ -172,19 +201,21 @@
             mCleanupAppWidgetsOnBootCompleted = true;
             return;
         }
-        // Clean up appWidgetIds that are bound to lockscreen, but not actually used
-        // This is only to clean up after another bug: we used to not call
-        // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code
-        // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks
-        // that are triggered by deleteAppWidgetId, which is why we're doing this
-        int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets();
-        int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds();
-        for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) {
-            int appWidgetId = appWidgetIdsBoundToHost[i];
-            if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) {
-                Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id "
-                        + appWidgetId);
-                mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+        if (!mSafeModeEnabled && !widgetsDisabledByDpm()) {
+            // Clean up appWidgetIds that are bound to lockscreen, but not actually used
+            // This is only to clean up after another bug: we used to not call
+            // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code
+            // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks
+            // that are triggered by deleteAppWidgetId, which is why we're doing this
+            int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets();
+            int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds();
+            for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) {
+                int appWidgetId = appWidgetIdsBoundToHost[i];
+                if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) {
+                    Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id "
+                            + appWidgetId);
+                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+                }
             }
         }
     }
@@ -351,21 +382,17 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
-        mAppWidgetHost.startListeningAsUser(mUserId);
+        mAppWidgetHost.startListening();
         KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
     }
 
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
-        mAppWidgetHost.stopListeningAsUser(mUserId);
+        mAppWidgetHost.stopListening();
         KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
     }
 
-    private AppWidgetHost getAppWidgetHost() {
-        return mAppWidgetHost;
-    }
-
     void addWidget(AppWidgetHostView view, int pageIndex) {
         mAppWidgetContainer.addWidget(view, pageIndex);
     }
@@ -1016,12 +1043,13 @@
     private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) {
         AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId);
         if (appWidgetInfo != null) {
-            AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
+            AppWidgetHostView view = mAppWidgetHost.createView(mContext, appId, appWidgetInfo);
             addWidget(view, pageIndex);
             return true;
         } else {
             if (updateDbIfFailed) {
-                Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting");
+                Log.w(TAG, "*** AppWidgetInfo for app widget id " + appId + "  was null for user"
+                        + mUserId + ", deleting");
                 mAppWidgetHost.deleteAppWidgetId(appId);
                 mLockPatternUtils.removeAppWidget(appId);
             }
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 06aeb29..d5715a5 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -16,7 +16,7 @@
 
 package com.android.server;
 
-import android.app.ActivityManagerNative;
+import android.app.ActivityManager;
 import android.appwidget.AppWidgetProviderInfo;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -121,107 +121,67 @@
         }, userFilter);
     }
 
-    /**
-     * This returns the user id of the caller, if the caller is not the system process,
-     * otherwise it assumes that the calls are from the lockscreen and hence are meant for the
-     * current user. TODO: Instead, have lockscreen make explicit calls with userId
-     */
-    private int getCallingOrCurrentUserId() {
-        int callingUid = Binder.getCallingUid();
-        // Also check the PID because Settings (power control widget) also runs as System UID
-        if (callingUid == android.os.Process.myUid()
-                && Binder.getCallingPid() == android.os.Process.myPid()) {
-            try {
-                return ActivityManagerNative.getDefault().getCurrentUser().id;
-            } catch (RemoteException re) {
-                return UserHandle.getUserId(callingUid);
-            }
-        } else {
-            return UserHandle.getUserId(callingUid);
-        }
-    }
-
     @Override
-    public int allocateAppWidgetId(String packageName, int hostId) throws RemoteException {
-        return getImplForUser(getCallingOrCurrentUserId()).allocateAppWidgetId(
-                packageName, hostId);
-    }
-
-    @Override
-    public int[] getAppWidgetIdsForHost(int hostId) throws RemoteException {
-        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetIdsForHost(hostId);
-    }
-    
-    @Override
-    public void deleteAppWidgetId(int appWidgetId) throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).deleteAppWidgetId(appWidgetId);
-    }
-
-    @Override
-    public void deleteHost(int hostId) throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).deleteHost(hostId);
-    }
-
-    @Override
-    public void deleteAllHosts() throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).deleteAllHosts();
-    }
-
-    @Override
-    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options)
+    public int allocateAppWidgetId(String packageName, int hostId, int userId)
             throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).bindAppWidgetId(appWidgetId, provider,
-                options);
+        return getImplForUser(userId).allocateAppWidgetId(packageName, hostId);
+    }
+
+    @Override
+    public int[] getAppWidgetIdsForHost(int hostId, int userId) throws RemoteException {
+        return getImplForUser(userId).getAppWidgetIdsForHost(hostId);
+    }
+
+    @Override
+    public void deleteAppWidgetId(int appWidgetId, int userId) throws RemoteException {
+        getImplForUser(userId).deleteAppWidgetId(appWidgetId);
+    }
+
+    @Override
+    public void deleteHost(int hostId, int userId) throws RemoteException {
+        getImplForUser(userId).deleteHost(hostId);
+    }
+
+    @Override
+    public void deleteAllHosts(int userId) throws RemoteException {
+        getImplForUser(userId).deleteAllHosts();
+    }
+
+    @Override
+    public void bindAppWidgetId(int appWidgetId, ComponentName provider, Bundle options, int userId)
+            throws RemoteException {
+        getImplForUser(userId).bindAppWidgetId(appWidgetId, provider, options);
     }
 
     @Override
     public boolean bindAppWidgetIdIfAllowed(
-            String packageName, int appWidgetId, ComponentName provider, Bundle options)
+            String packageName, int appWidgetId, ComponentName provider, Bundle options, int userId)
                     throws RemoteException {
-        return getImplForUser(getCallingOrCurrentUserId()).bindAppWidgetIdIfAllowed(
+        return getImplForUser(userId).bindAppWidgetIdIfAllowed(
                 packageName, appWidgetId, provider, options);
     }
 
     @Override
-    public boolean hasBindAppWidgetPermission(String packageName) throws RemoteException {
-        return getImplForUser(getCallingOrCurrentUserId()).hasBindAppWidgetPermission(
-                packageName);
+    public boolean hasBindAppWidgetPermission(String packageName, int userId)
+            throws RemoteException {
+        return getImplForUser(userId).hasBindAppWidgetPermission(packageName);
     }
 
     @Override
-    public void setBindAppWidgetPermission(String packageName, boolean permission)
+    public void setBindAppWidgetPermission(String packageName, boolean permission, int userId)
             throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).setBindAppWidgetPermission(
-                packageName, permission);
+        getImplForUser(userId).setBindAppWidgetPermission(packageName, permission);
     }
 
     @Override
     public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection,
             int userId) throws RemoteException {
-        if (Binder.getCallingPid() != android.os.Process.myPid()
-                && userId != UserHandle.getCallingUserId()) {
-            throw new SecurityException("Call from non-system process. Calling uid = "
-                    + Binder.getCallingUid());
-        }
-        getImplForUser(userId).bindRemoteViewsService(
-                appWidgetId, intent, connection);
+        getImplForUser(userId).bindRemoteViewsService(appWidgetId, intent, connection);
     }
 
     @Override
     public int[] startListening(IAppWidgetHost host, String packageName, int hostId,
-            List<RemoteViews> updatedViews) throws RemoteException {
-        return getImplForUser(getCallingOrCurrentUserId()).startListening(host,
-                packageName, hostId, updatedViews);
-    }
-
-    @Override
-    public int[] startListeningAsUser(IAppWidgetHost host, String packageName, int hostId,
             List<RemoteViews> updatedViews, int userId) throws RemoteException {
-        if (Binder.getCallingPid() != android.os.Process.myPid()
-                && userId != UserHandle.getCallingUserId()) {
-            throw new SecurityException("Call from non-system process. Calling uid = "
-                    + Binder.getCallingUid());
-        }
         return getImplForUser(userId).startListening(host, packageName, hostId, updatedViews);
     }
 
@@ -250,7 +210,19 @@
         }
     }
 
+    private void checkPermission(int userId) {
+        int realUserId = ActivityManager.handleIncomingUser(
+                Binder.getCallingPid(),
+                Binder.getCallingUid(),
+                userId,
+                false, /* allowAll */
+                true, /* requireFull */
+                this.getClass().getSimpleName(),
+                this.getClass().getPackage().getName());
+    }
+
     private AppWidgetServiceImpl getImplForUser(int userId) {
+        checkPermission(userId);
         boolean sendInitial = false;
         AppWidgetServiceImpl service;
         synchronized (mAppWidgetServices) {
@@ -272,86 +244,73 @@
     }
 
     @Override
-    public int[] getAppWidgetIds(ComponentName provider) throws RemoteException {
-        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetIds(provider);
+    public int[] getAppWidgetIds(ComponentName provider, int userId) throws RemoteException {
+        return getImplForUser(userId).getAppWidgetIds(provider);
     }
 
     @Override
-    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) throws RemoteException {
-        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetInfo(appWidgetId);
-    }
-
-    @Override
-    public RemoteViews getAppWidgetViews(int appWidgetId) throws RemoteException {
-        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetViews(appWidgetId);
-    }
-
-    @Override
-    public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
-        getImplForUser(getCallingOrCurrentUserId()).updateAppWidgetOptions(appWidgetId, options);
-    }
-
-    @Override
-    public Bundle getAppWidgetOptions(int appWidgetId) {
-        return getImplForUser(getCallingOrCurrentUserId()).getAppWidgetOptions(appWidgetId);
-    }
-
-    @Override
-    public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter)
+    public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId, int userId)
             throws RemoteException {
-        return getImplForUser(getCallingOrCurrentUserId()).getInstalledProviders(categoryFilter);
+        return getImplForUser(userId).getAppWidgetInfo(appWidgetId);
     }
 
     @Override
-    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId)
+    public RemoteViews getAppWidgetViews(int appWidgetId, int userId) throws RemoteException {
+        return getImplForUser(userId).getAppWidgetViews(appWidgetId);
+    }
+
+    @Override
+    public void updateAppWidgetOptions(int appWidgetId, Bundle options, int userId) {
+        getImplForUser(userId).updateAppWidgetOptions(appWidgetId, options);
+    }
+
+    @Override
+    public Bundle getAppWidgetOptions(int appWidgetId, int userId) {
+        return getImplForUser(userId).getAppWidgetOptions(appWidgetId);
+    }
+
+    @Override
+    public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter, int userId)
             throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).notifyAppWidgetViewDataChanged(
+        return getImplForUser(userId).getInstalledProviders(categoryFilter);
+    }
+
+    @Override
+    public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId, int userId)
+            throws RemoteException {
+        getImplForUser(userId).notifyAppWidgetViewDataChanged(
                 appWidgetIds, viewId);
     }
 
     @Override
-    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views)
+    public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views, int userId)
             throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).partiallyUpdateAppWidgetIds(
+        getImplForUser(userId).partiallyUpdateAppWidgetIds(
                 appWidgetIds, views);
     }
 
     @Override
-    public void stopListening(int hostId) throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).stopListening(hostId);
-    }
-
-    @Override
-    public void stopListeningAsUser(int hostId, int userId) throws RemoteException {
-        if (Binder.getCallingPid() != android.os.Process.myPid()
-                && userId != UserHandle.getCallingUserId()) {
-            throw new SecurityException("Call from non-system process. Calling uid = "
-                    + Binder.getCallingUid());
-        }
+    public void stopListening(int hostId, int userId) throws RemoteException {
         getImplForUser(userId).stopListening(hostId);
     }
 
     @Override
     public void unbindRemoteViewsService(int appWidgetId, Intent intent, int userId)
             throws RemoteException {
-        if (Binder.getCallingPid() != android.os.Process.myPid()
-                && userId != UserHandle.getCallingUserId()) {
-            throw new SecurityException("Call from non-system process. Calling uid = "
-                    + Binder.getCallingUid());
-        }
         getImplForUser(userId).unbindRemoteViewsService(
                 appWidgetId, intent);
     }
 
     @Override
-    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).updateAppWidgetIds(appWidgetIds, views);
+    public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views, int userId)
+            throws RemoteException {
+        getImplForUser(userId).updateAppWidgetIds(appWidgetIds, views);
     }
 
     @Override
-    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views)
+    public void updateAppWidgetProvider(ComponentName provider, RemoteViews views, int userId)
             throws RemoteException {
-        getImplForUser(getCallingOrCurrentUserId()).updateAppWidgetProvider(provider, views);
+        getImplForUser(userId).updateAppWidgetProvider(provider, views);
     }
 
     @Override
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index d1829ab..6eea928 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -1046,7 +1046,7 @@
             if (id.host.callbacks != null) {
                 try {
                     // the lock is held, but this is a oneway call
-                    id.host.callbacks.updateAppWidget(id.appWidgetId, views);
+                    id.host.callbacks.updateAppWidget(id.appWidgetId, views, mUserId);
                 } catch (RemoteException e) {
                     // It failed; remove the callback. No need to prune because
                     // we know that this host is still referenced by this instance.
@@ -1065,7 +1065,7 @@
             if (id.host.callbacks != null) {
                 try {
                     // the lock is held, but this is a oneway call
-                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId);
+                    id.host.callbacks.viewDataChanged(id.appWidgetId, viewId, mUserId);
                 } catch (RemoteException e) {
                     // It failed; remove the callback. No need to prune because
                     // we know that this host is still referenced by this instance.
@@ -1934,7 +1934,8 @@
                                 id.views = null;
                                 if (id.host != null && id.host.callbacks != null) {
                                     try {
-                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info);
+                                        id.host.callbacks.providerChanged(id.appWidgetId, p.info,
+                                                mUserId);
                                     } catch (RemoteException ex) {
                                         // It failed; remove the callback. No need to prune because
                                         // we know that this host is still referenced by this
@@ -2001,7 +2002,7 @@
             Host host = mHosts.get(i);
             try {
                 if (host.callbacks != null) {
-                    host.callbacks.providersChanged();
+                    host.callbacks.providersChanged(mUserId);
                 }
             } catch (RemoteException ex) {
                 // It failed; remove the callback. No need to prune because
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index c31cde7..6d817a1 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,7 +16,6 @@
 
 package com.android.server;
 
-import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
 import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
@@ -32,7 +31,6 @@
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
 
-import android.app.Activity;
 import android.bluetooth.BluetoothTetheringDataTracker;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -88,7 +86,6 @@
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.text.TextUtils;
-import android.util.EventLog;
 import android.util.Slog;
 import android.util.SparseIntArray;
 
@@ -3336,7 +3333,10 @@
 
     @Override
     public boolean updateLockdownVpn() {
-        enforceSystemUid();
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            Slog.w(TAG, "Lockdown VPN only available to AID_SYSTEM");
+            return false;
+        }
 
         // Tear down existing lockdown if profile was removed
         mLockdownEnabled = LockdownVpnTracker.isEnabled();
@@ -3387,11 +3387,4 @@
             throw new IllegalStateException("Unavailable in lockdown mode");
         }
     }
-
-    private static void enforceSystemUid() {
-        final int uid = Binder.getCallingUid();
-        if (uid != Process.SYSTEM_UID) {
-            throw new SecurityException("Only available to AID_SYSTEM");
-        }
-    }
 }
diff --git a/services/java/com/android/server/IdleMaintenanceService.java b/services/java/com/android/server/IdleMaintenanceService.java
new file mode 100644
index 0000000..a7442f6
--- /dev/null
+++ b/services/java/com/android/server/IdleMaintenanceService.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.BatteryManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.util.Log;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+
+/**
+ * This service observes the device state and when applicable sends
+ * broadcasts at the beginning and at the end of a period during which
+ * observers can perform idle maintenance tasks. Typical use of the
+ * idle maintenance is to perform somehow expensive tasks that can be
+ * postponed to a moment when they will not degrade user experience.
+ *
+ * The current implementation is very simple. The start of a maintenance
+ * window is announced if: the screen is off or showing a dream AND the
+ * battery level is more than twenty percent AND at least one hour passed
+ * since the screen went off or a dream started (i.e. since the last user
+ * activity).
+ *
+ * The end of a maintenance window is announced only if: a start was
+ * announced AND the screen turned on or a dream was stopped.
+ */
+public class IdleMaintenanceService extends BroadcastReceiver {
+
+    private final boolean DEBUG = false;
+
+    private static final String LOG_TAG = IdleMaintenanceService.class.getSimpleName();
+
+    private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
+
+    private static final int MIN_IDLE_MAINTENANCE_START_BATTERY_LEVEL = 20; // percent
+
+    private static final long MIN_IDLE_MAINTENANCE_START_USER_INACTIVITY = 60 * 60 * 1000; // 1 hour
+
+    private final Intent mIdleMaintenanceStartIntent =
+            new Intent(Intent.ACTION_IDLE_MAINTENANCE_START);
+
+    private final Intent mIdleMaintenanceEndIntent =
+            new Intent(Intent.ACTION_IDLE_MAINTENANCE_END);
+
+    private final Context mContext;
+
+    private final WakeLock mWakeLock;
+
+    private final Handler mHandler;
+
+    private final Calendar mTempCalendar = Calendar.getInstance();
+
+    private final Calendar mLastIdleMaintenanceStartTime = Calendar.getInstance();
+
+    private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
+
+    private int mBatteryLevel;
+
+    private boolean mIdleMaintenanceStarted;
+
+    public IdleMaintenanceService(Context context) {
+        mContext = context;
+
+        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
+
+        mHandler = new Handler(mContext.getMainLooper());
+
+        // Move one day back so we can run maintenance the first day after starting.
+        final int prevDayOfYear = mLastIdleMaintenanceStartTime.get(Calendar.DAY_OF_YEAR) - 1;
+        mLastIdleMaintenanceStartTime.set(Calendar.DAY_OF_YEAR, prevDayOfYear);
+
+        register(mContext.getMainLooper());
+    }
+
+    public void register(Looper looper) {
+        IntentFilter intentFilter = new IntentFilter();
+
+        // Battery actions.
+        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
+
+        // Screen actions.
+        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+
+        // Dream actions.
+        intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
+        intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
+
+        mContext.registerReceiverAsUser(this, UserHandle.ALL,
+                intentFilter, null, new Handler(looper));
+    }
+
+    private void updateIdleMaintenanceState() {
+        if (mIdleMaintenanceStarted) {
+            // Idle maintenance can be interrupted only by
+            // a change of the device state.
+            if (!deviceStatePermitsIdleMaintenance()) {
+                mIdleMaintenanceStarted = false;
+                sendIdleMaintenanceEndIntent();
+            }
+        } else if (deviceStatePermitsIdleMaintenance()
+                && lastUserActivityPermitsIdleMaintenanceStart()
+                && lastRunPermitsIdleMaintenanceStart()) {
+            mIdleMaintenanceStarted = true;
+            mLastIdleMaintenanceStartTime.setTimeInMillis(System.currentTimeMillis());
+            sendIdleMaintenanceStartIntent();
+        }
+    }
+
+    private void sendIdleMaintenanceStartIntent() {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_START);
+        }
+        mWakeLock.acquire();
+        mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceStartIntent, UserHandle.ALL,
+                null, this, mHandler, Activity.RESULT_OK, null, null);
+    }
+
+    private void sendIdleMaintenanceEndIntent() {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "Broadcasting " + Intent.ACTION_IDLE_MAINTENANCE_END);
+        }
+        mWakeLock.acquire();
+        mContext.sendOrderedBroadcastAsUser(mIdleMaintenanceEndIntent, UserHandle.ALL,
+                null, this, mHandler, Activity.RESULT_OK, null, null);
+    }
+
+    private boolean deviceStatePermitsIdleMaintenance() {
+        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
+                && mBatteryLevel > MIN_IDLE_MAINTENANCE_START_BATTERY_LEVEL);
+    }
+
+    private boolean lastUserActivityPermitsIdleMaintenanceStart() {
+        return (SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
+                > MIN_IDLE_MAINTENANCE_START_USER_INACTIVITY);
+    }
+
+    private boolean lastRunPermitsIdleMaintenanceStart() {
+        Calendar now = mTempCalendar;
+        // Not setting the Locale since we do not care of locale
+        // specific properties such as the first day of the week.
+        now.setTimeZone(TimeZone.getDefault());
+        now.setTimeInMillis(System.currentTimeMillis());
+
+        Calendar lastRun = mLastIdleMaintenanceStartTime;
+        // Not setting the Locale since we do not care of locale
+        // specific properties such as the first day of the week.
+        lastRun.setTimeZone(TimeZone.getDefault());
+
+        return now.get(Calendar.DAY_OF_YEAR) != lastRun.get(Calendar.DAY_OF_YEAR);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (DEBUG) {
+            Log.i(LOG_TAG, intent.getAction());
+        }
+        String action = intent.getAction();
+        if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
+            final int maxBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_SCALE);
+            final int currBatteryLevel = intent.getExtras().getInt(BatteryManager.EXTRA_LEVEL);
+            mBatteryLevel = (int) (((float) maxBatteryLevel / 100) * currBatteryLevel);
+        } else if (Intent.ACTION_SCREEN_ON.equals(action)
+                || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
+            mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
+        } else if (Intent.ACTION_SCREEN_OFF.equals(action)
+                || Intent.ACTION_DREAMING_STARTED.equals(action)) {
+            mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
+        } else if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)
+                || Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
+            mWakeLock.release();
+            return;
+        }
+        updateIdleMaintenanceState();
+    }
+}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 13bf39f..9f2685b 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -26,6 +26,7 @@
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.INotificationManager;
+import android.app.INotificationListener;
 import android.app.ITransientNotification;
 import android.app.Notification;
 import android.app.PendingIntent;
@@ -151,6 +152,7 @@
     private boolean mInCall = false;
     private boolean mNotificationPulseEnabled;
 
+    // used as a mutex for access to all active notifications & listeners
     private final ArrayList<NotificationRecord> mNotificationList =
             new ArrayList<NotificationRecord>();
 
@@ -161,6 +163,8 @@
 
     private final AppOpsManager mAppOps;
 
+    private ArrayList<NotificationListenerInfo> mListeners = new ArrayList<NotificationListenerInfo>();
+
     // Notification control database. For now just contains disabled packages.
     private AtomicFile mPolicyFile;
     private HashSet<String> mBlockedPackages = new HashSet<String>();
@@ -174,6 +178,38 @@
     private static final String TAG_PACKAGE = "package";
     private static final String ATTR_NAME = "name";
 
+    private class NotificationListenerInfo implements DeathRecipient {
+        INotificationListener listener;
+        int userid;
+        public NotificationListenerInfo(INotificationListener listener, int userid) {
+            this.listener = listener;
+            this.userid = userid;
+        }
+
+        public void notifyPostedIfUserMatch(StatusBarNotification sbn) {
+            if (this.userid != sbn.getUserId()) return;
+            try {
+                listener.onNotificationPosted(sbn);
+            } catch (RemoteException ex) {
+                // not there?
+            }
+        }
+
+        public void notifyRemovedIfUserMatch(StatusBarNotification sbn) {
+            if (this.userid != sbn.getUserId()) return;
+            try {
+                listener.onNotificationRemoved(sbn);
+            } catch (RemoteException ex) {
+                // not there?
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            unregisterListener(this.listener, this.userid);
+        }
+    }
+
     private static class Archive {
         static final int BUFFER_SIZE = 1000;
         ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
@@ -406,6 +442,56 @@
         return tmp;
     }
 
+    @Override
+    public void registerListener(final INotificationListener listener, final int userid) {
+        checkCallerIsSystem();
+        synchronized (mNotificationList) {
+            try {
+                NotificationListenerInfo info = new NotificationListenerInfo(listener, userid);
+                listener.asBinder().linkToDeath(info, 0);
+                mListeners.add(info);
+            } catch (RemoteException e) {
+                // already dead
+            }
+        }
+    }
+
+    @Override
+    public void unregisterListener(INotificationListener listener, int userid) {
+        checkCallerIsSystem();
+        synchronized (mNotificationList) {
+            final int N = mListeners.size();
+            for (int i=N-1; i>=0; i--) {
+                final NotificationListenerInfo info = mListeners.get(i);
+                if (info.listener == listener && info.userid == userid) {
+                    mListeners.remove(listener);
+                }
+            }
+        }
+    }
+
+    private void notifyPostedLocked(NotificationRecord n) {
+        final StatusBarNotification sbn = n.sbn;
+        for (final NotificationListenerInfo info : mListeners) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    info.notifyPostedIfUserMatch(sbn);
+                }});
+        }
+    }
+
+    private void notifyRemovedLocked(NotificationRecord n) {
+        final StatusBarNotification sbn = n.sbn;
+        for (final NotificationListenerInfo info : mListeners) {
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    info.notifyRemovedIfUserMatch(sbn);
+                }});
+        }
+    }
+
     public static final class NotificationRecord
     {
         final StatusBarNotification sbn;
@@ -1165,6 +1251,8 @@
 
                 // finally, keep some of this information around for later use
                 mArchive.record(n);
+
+                notifyPostedLocked(r);
             } else {
                 Slog.e(TAG, "Ignoring notification with icon==0: " + notification);
                 if (old != null && old.statusBarKey != null) {
@@ -1175,6 +1263,8 @@
                     finally {
                         Binder.restoreCallingIdentity(identity);
                     }
+
+                    notifyRemovedLocked(r);
                 }
                 return; // do not play sounds, show lights, etc. for invalid notifications
             }
@@ -1341,6 +1431,7 @@
                 Binder.restoreCallingIdentity(identity);
             }
             r.statusBarKey = null;
+            notifyRemovedLocked(r);
         }
 
         // sound
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 39ee47e..8ef247e 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -62,6 +62,7 @@
 import com.android.server.power.ShutdownThread;
 import com.android.server.search.SearchManagerService;
 import com.android.server.usb.UsbService;
+import com.android.server.wifi.WifiService;
 import com.android.server.wm.WindowManagerService;
 
 import dalvik.system.VMRuntime;
@@ -255,7 +256,7 @@
             }
 
             ActivityManagerService.setSystemProcess();
-            
+
             Slog.i(TAG, "User Service");
             ServiceManager.addService(Context.USER_SERVICE,
                     UserManagerService.getInstance());
@@ -736,6 +737,13 @@
                     reportWtf("starting DreamManagerService", e);
                 }
             }
+
+            try {
+                Slog.i(TAG, "IdleMaintenanceService");
+                new IdleMaintenanceService(context);
+            } catch (Throwable e) {
+                reportWtf("starting IdleMaintenanceService", e);
+            }
         }
 
         // Before things start rolling, be sure we have decided whether
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9c518a1..fd5e79a 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -934,12 +934,10 @@
      * @param service The service.
      * @return True if the service was removed, false otherwise.
      */
-    private void removeServiceLocked(Service service) {
-        UserState userState = getUserStateLocked(service.mUserId);
+    private void removeServiceLocked(Service service, UserState userState) {
         userState.mBoundServices.remove(service);
         userState.mComponentNameToServiceMap.remove(service.mComponentName);
         service.unlinkToOwnDeath();
-        service.dispose();
     }
 
     /**
@@ -1672,11 +1670,9 @@
         public boolean bindLocked() {
             UserState userState = getUserStateLocked(mUserId);
             if (!mIsAutomation) {
-                if (mService == null) {
-                    if (mContext.bindServiceAsUser(mIntent, this, Context.BIND_AUTO_CREATE,
-                            new UserHandle(mUserId))) {
-                        userState.mBindingServices.add(mComponentName);
-                    }
+                if (mService == null && mContext.bindServiceAsUser(
+                        mIntent, this, Context.BIND_AUTO_CREATE, new UserHandle(mUserId))) {
+                    userState.mBindingServices.add(mComponentName);
                 }
             } else {
                 userState.mBindingServices.add(mComponentName);
@@ -1697,14 +1693,15 @@
             if (mService == null) {
                 return false;
             }
+            UserState userState = getUserStateLocked(mUserId);
             if (!mIsAutomation) {
                 mContext.unbindService(this);
             } else {
-                UserState userState = getUserStateLocked(mUserId);
                 userState.mUiAutomationService = null;
                 userState.mUiAutomationServiceClient = null;
             }
-            removeServiceLocked(this);
+            removeServiceLocked(this, userState);
+            dispose();
             return true;
         }
 
@@ -1750,11 +1747,11 @@
                 mService = service;
                 mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
                 UserState userState = getUserStateLocked(mUserId);
+                addServiceLocked(this, userState);
                 if (!userState.mBindingServices.contains(mComponentName)) {
                     binderDied();
                 } else {
                     userState.mBindingServices.remove(mComponentName);
-                    addServiceLocked(this, userState);
                     onUserStateChangedLocked(userState);
                 }
             }
@@ -2106,9 +2103,10 @@
 
         public void binderDied() {
             synchronized (mLock) {
-                // The death recipient is unregistered in tryRemoveServiceLocked
-                removeServiceLocked(this);
                 UserState userState = getUserStateLocked(mUserId);
+                // The death recipient is unregistered in removeServiceLocked
+                removeServiceLocked(this, userState);
+                dispose();
                 if (mIsAutomation) {
                     // We no longer have an automation service, so restore
                     // the state based on values in the settings database.
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index c1b406e..a2f3372 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -471,6 +471,8 @@
     void setTask(TaskRecord newTask, ThumbnailHolder newThumbHolder, boolean isRoot) {
         if (inHistory && !finishing) {
             if (task != null) {
+                // TODO: If this is the last ActivityRecord in task, remove from ActivityStack.
+                task.removeActivity(this);
                 task.numActivities--;
             }
             if (newTask != null) {
@@ -505,6 +507,7 @@
             inHistory = false;
             if (task != null && !finishing) {
                 task.numActivities--;
+                task = null;
             }
             clearOptionsLocked();
         }
@@ -539,7 +542,7 @@
         ActivityResult r = new ActivityResult(from, resultWho,
         		requestCode, resultCode, resultData);
         if (results == null) {
-            results = new ArrayList();
+            results = new ArrayList<ResultInfo>();
         }
         results.add(r);
     }
@@ -950,6 +953,8 @@
         StringBuilder sb = new StringBuilder(128);
         sb.append("ActivityRecord{");
         sb.append(Integer.toHexString(System.identityHashCode(this)));
+        sb.append(" t");
+        sb.append(task.taskId);
         sb.append(" u");
         sb.append(userId);
         sb.append(' ');
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index c54cdaa..30a7e23 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -54,6 +54,7 @@
 import android.graphics.Bitmap.Config;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -66,6 +67,7 @@
 import android.util.EventLog;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Display;
 
 import java.io.FileDescriptor;
@@ -98,7 +100,8 @@
     static final boolean DEBUG_APP = false;
 
     static final boolean VALIDATE_TOKENS = ActivityManagerService.VALIDATE_TOKENS;
-    
+    static final boolean VALIDATE_TASK_REPLACE = true;
+
     // How long we wait until giving up on the last activity telling us it
     // is idle.
     static final int IDLE_TIMEOUT = 10*1000;
@@ -137,7 +140,7 @@
     // Set to false to disable the preview that is shown while a new activity
     // is being started.
     static final boolean SHOW_APP_STARTING_PREVIEW = true;
-    
+
     enum ActivityState {
         INITIALIZING,
         RESUMED,
@@ -154,7 +157,7 @@
     final boolean mMainStack;
     
     final Context mContext;
-    
+
     /**
      * The back history of all previous (and possibly still
      * running) activities.  It contains #ActivityRecord objects.
@@ -162,6 +165,17 @@
     private final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>();
 
     /**
+     * The back history of all previous (and possibly still
+     * running) activities.  It contains #TaskRecord objects.
+     */
+    private ArrayList<TaskRecord> mTaskHistory = new ArrayList<TaskRecord>();
+
+    /**
+     * Mapping from taskId to TaskRecord
+     */
+    private SparseArray<TaskRecord> mTaskIdToTaskRecord = new SparseArray<TaskRecord>();
+
+    /**
      * Used for validating app tokens with window manager.
      */
     final ArrayList<TaskGroup> mValidateAppTokens = new ArrayList<TaskGroup>();
@@ -331,6 +345,8 @@
 
     final Handler mHandler;
 
+    String mLastHistoryModifier;
+
     final class ActivityStackHandler extends Handler {
         //public Handler() {
         //    if (localLOGV) Slog.v(TAG, "Handler started!");
@@ -457,26 +473,66 @@
     }
 
     final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
+        ActivityRecord newAr = newTopRunningActivityLocked(notTop);
+
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
             if (!r.finishing && r != notTop && okToShow(r)) {
+                if (VALIDATE_TASK_REPLACE && newAr != r) logHistories(
+                        "topRunningActivityLocked", true);
                 return r;
             }
             i--;
         }
+        if (VALIDATE_TASK_REPLACE && newAr != null) Slog.w(TAG,
+                "topRunningActivityLocked: mismatch: newAr!=null");
+        return null;
+    }
+
+    final ActivityRecord newTopRunningActivityLocked(ActivityRecord notTop) {
+        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
+            final TaskRecord task = mTaskHistory.get(i);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int j = activities.size() - 1; j >= 0; --j) {
+                ActivityRecord r = activities.get(j);
+                if (!r.finishing && r != notTop && okToShow(r)) {
+                    return r;
+                }
+            }
+        }
         return null;
     }
 
     final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+        ActivityRecord newAr = newTopRunningNonDelayedActivityLocked(notTop);
+
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
             if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) {
+                if (VALIDATE_TASK_REPLACE && newAr != r) Slog.w(TAG,
+                    "topRunningNonDelayedActivityLocked: mismatch: newAr=" + newAr + " r=" + r);
                 return r;
             }
             i--;
         }
+        if (VALIDATE_TASK_REPLACE && newAr != null) Slog.w(TAG,
+                "topRunningNonDelayedActivityLocked: mismatch: newAr!=null");
+        return null;
+    }
+
+    final ActivityRecord newTopRunningNonDelayedActivityLocked(ActivityRecord notTop) {
+        for (int i = mTaskHistory.size() - 1; i >= 0; --i) {
+            final TaskRecord task = mTaskHistory.get(i);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int j = activities.size() - 1; j >= 0; --j) {
+                ActivityRecord r = activities.get(j);
+                if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) {
+                    return r;
+                }
+            }
+        }
         return null;
     }
 
@@ -490,16 +546,40 @@
      * @return Returns the HistoryRecord of the next activity on the stack.
      */
     final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) {
+        ActivityRecord newAr = newTopRunningActivityLocked(token, taskId);
+
         int i = mHistory.size()-1;
         while (i >= 0) {
             ActivityRecord r = mHistory.get(i);
             // Note: the taskId check depends on real taskId fields being non-zero
             if (!r.finishing && (token != r.appToken) && (taskId != r.task.taskId)
                     && okToShow(r)) {
+                if (VALIDATE_TASK_REPLACE && newAr != r) Slog.w(TAG,
+                        "topRunningActivityLocked(token): mismatch: newAr=" + newAr + " r=" + r);
                 return r;
             }
             i--;
         }
+        if (VALIDATE_TASK_REPLACE && newAr != null) Slog.w(TAG,
+                "topRunningActivityLocked(token): mismatch: newAr!=null");
+        return null;
+    }
+
+    final ActivityRecord newTopRunningActivityLocked(IBinder token, int taskId) {
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            TaskRecord task = mTaskHistory.get(taskNdx);
+            if (task.taskId == taskId) {
+                continue;
+            }
+            ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int i = activities.size() - 1; i >= 0; --i) {
+                final ActivityRecord r = activities.get(i);
+                // Note: the taskId check depends on real taskId fields being non-zero
+                if (!r.finishing && (token != r.appToken) && okToShow(r)) {
+                    return r;
+                }
+            }
+        }
         return null;
     }
 
@@ -508,30 +588,69 @@
     }
 
     final ActivityRecord isInStackLocked(IBinder token) {
+        ActivityRecord newAr = newIsInStackLocked(token);
+
         ActivityRecord r = ActivityRecord.forToken(token);
         if (mHistory.contains(r)) {
+            if (VALIDATE_TASK_REPLACE && newAr != r) Slog.w(TAG,
+                    "isInStackLocked: mismatch: newAr=" + newAr + " r=" + r);
             return r;
         }
+        if (VALIDATE_TASK_REPLACE && newAr != null) Slog.w(TAG,
+                "isInStackLocked: mismatch: newAr!=null");
+        return null;
+    }
+
+    final ActivityRecord newIsInStackLocked(IBinder token) {
+        final ActivityRecord r = ActivityRecord.forToken(token);
+        if (r != null) {
+            final TaskRecord task = r.task;
+            if (mTaskHistory.contains(task) && task.mActivities.contains(r)) {
+                return r;
+            }
+        }
         return null;
     }
 
     int getTaskForActivityLocked(IBinder token, boolean onlyRoot) {
+        int newTaskId = newGetTaskForActivityLocked(token, onlyRoot);
+
         TaskRecord lastTask = null;
         final int N = mHistory.size();
         for (int i = 0; i < N; i++) {
             ActivityRecord r = mHistory.get(i);
             if (r.appToken == token) {
                 if (!onlyRoot || lastTask != r.task) {
+                    if (VALIDATE_TASK_REPLACE && newTaskId != r.task.taskId) Slog.w(TAG,
+                            "getTaskForActivityLocked: mismatch: new=" + newTaskId
+                            + " taskId=" + r.task.taskId);
                     return r.task.taskId;
                 }
+                if (VALIDATE_TASK_REPLACE && newTaskId != -1) Slog.w(TAG,
+                    "getTaskForActivityLocked: mismatch: newTaskId=" + newTaskId + " not -1.");
                 return -1;
             }
             lastTask = r.task;
         }
 
+        if (VALIDATE_TASK_REPLACE && newTaskId != -1) Slog.w(TAG,
+            "getTaskForActivityLocked: mismatch at end: newTaskId=" + newTaskId + " not -1.");
         return -1;
     }
 
+    int newGetTaskForActivityLocked(IBinder token, boolean onlyRoot) {
+        final ActivityRecord r = ActivityRecord.forToken(token);
+        if (r == null) {
+            return -1;
+        }
+        final TaskRecord task = r.task;
+        switch (task.mActivities.indexOf(r)) {
+            case -1: return -1;
+            case 0: return task.taskId;
+            default: return onlyRoot ? -1 : task.taskId;
+        }
+    }
+
     private final boolean updateLRUListLocked(ActivityRecord r) {
         final boolean hadit = mLRUActivities.remove(r);
         mLRUActivities.add(r);
@@ -624,17 +743,29 @@
      * @return whether there are any activities for the specified user.
      */
     final boolean switchUserLocked(int userId, UserStartedState uss) {
+        if (VALIDATE_TOKENS) {
+            validateAppTokensLocked();
+        }
+        final boolean newResult = newSwitchUserLocked(userId, uss);
+
         mCurrentUser = userId;
         mStartingUsers.add(uss);
 
         // Only one activity? Nothing to do...
-        if (mHistory.size() < 2)
+        if (mHistory.size() < 2) {
+            if (VALIDATE_TASK_REPLACE && newResult) Slog.w(TAG,
+                    "switchUserLocked: mismatch: " + newResult + " " + false);
             return false;
+        }
 
         boolean haveActivities = false;
         // Check if the top activity is from the new user.
         ActivityRecord top = mHistory.get(mHistory.size() - 1);
-        if (top.userId == userId) return true;
+        if (top.userId == userId) {
+            if (VALIDATE_TASK_REPLACE && !newResult) Slog.w(TAG,
+                    "switchUserLocked: mismatch: " + newResult + " " + true);
+            return true;
+        }
         // Otherwise, move the user's activities to the top.
         int N = mHistory.size();
         int i = 0;
@@ -651,7 +782,44 @@
             }
         }
         // Transition from the old top to the new top
+        if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
+                "switchUserLocked: calling resumeTopActivity " + top);
         resumeTopActivityLocked(top);
+        if (VALIDATE_TASK_REPLACE && (newResult != haveActivities)) Slog.w(TAG,
+                    "switchUserLocked: mismatch: " + newResult + " " + haveActivities);
+        return haveActivities;
+    }
+
+    /*
+     * Move the activities around in the stack to bring a user to the foreground.
+     * @return whether there are any activities for the specified user.
+     */
+    final boolean newSwitchUserLocked(int userId, UserStartedState uss) {
+//        mStartingUsers.add(uss);
+        if (mCurrentUser == userId) {
+            return true;
+        }
+        mCurrentUser = userId;
+
+        // Move userId's tasks to the top.
+        boolean haveActivities = false;
+        TaskRecord task = null;
+        int index = mTaskHistory.size();
+        for (int i = 0; i < index; ++i) {
+            task = mTaskHistory.get(i);
+            if (task.userId == userId) {
+                haveActivities = true;
+                mTaskHistory.remove(i);
+                mTaskHistory.add(task);
+                --index;
+            }
+        }
+
+        // task is now the original topmost TaskRecord. Transition from the old top to the new top.
+        ActivityRecord top = task != null ? task.getTopActivity() : null;
+        if (VALIDATE_TASK_REPLACE) Slog.w(TAG,
+                "newSwitchUserLocked: would call resumeTopActivity " + top);
+//        resumeTopActivityLocked(top);
         return haveActivities;
     }
 
@@ -889,6 +1057,9 @@
             mGoingToSleep.release();
         }
         // Ensure activities are no longer sleeping.
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords(true);
+        }
         for (int i=mHistory.size()-1; i>=0; i--) {
             ActivityRecord r = mHistory.get(i);
             r.setSleeping(false);
@@ -933,6 +1104,9 @@
 
             // Make sure any stopped but visible activities are now sleeping.
             // This ensures that the activity's onStop() is called.
+            if (VALIDATE_TASK_REPLACE) {
+                verifyActivityRecords(true);
+            }
             for (int i=mHistory.size()-1; i>=0; i--) {
                 ActivityRecord r = mHistory.get(i);
                 if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) {
@@ -1077,6 +1251,9 @@
         ActivityRecord r = null;
 
         synchronized (mService) {
+            if (VALIDATE_TASK_REPLACE) {
+                verifyActivityRecords(true);
+            }
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 r = mHistory.get(index);
@@ -1094,6 +1271,9 @@
         ActivityRecord r = null;
 
         synchronized (mService) {
+            if (VALIDATE_TASK_REPLACE) {
+                verifyActivityRecords(true);
+            }
             int index = indexOfTokenLocked(token);
             if (index >= 0) {
                 r = mHistory.get(index);
@@ -1306,6 +1486,9 @@
 
         // If the top activity is not fullscreen, then we need to
         // make sure any activities under it are now visible.
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords(true);
+        }
         final int count = mHistory.size();
         int i = count-1;
         while (mHistory.get(i) != top) {
@@ -1842,8 +2025,21 @@
         return true;
     }
 
+    /** Temporary until startActivityLocked is rewritten for tasks. */
+    private int convertAddPos(int addPos) {
+        final int taskId = mHistory.get(addPos).task.taskId;
+        addPos--;
+        int taskOffset = 0;
+        while (addPos >= 0 && taskId == mHistory.get(addPos).task.taskId) {
+            ++taskOffset;
+            --addPos;
+        }
+        return taskOffset;
+    }
+
     private final void startActivityLocked(ActivityRecord r, boolean newTask,
             boolean doResume, boolean keepCurTransition, Bundle options) {
+        mLastHistoryModifier = "startActivityLocked";
         final int NH = mHistory.size();
 
         int addPos = -1;
@@ -1868,13 +2064,15 @@
                             Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos,
                                     here);
                         }
+                        r.task.addActivityToTop(r);
                         mHistory.add(addPos, r);
                         r.putInHistory();
-                        mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
-                                r.info.screenOrientation, r.fullscreen,
+                        mService.mWindowManager.addAppToken(convertAddPos(addPos), r.appToken,
+                                r.task.taskId, r.info.screenOrientation, r.fullscreen,
                                 (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
                         if (VALIDATE_TOKENS) {
                             validateAppTokensLocked();
+                            verifyActivityRecords(true);
                         }
                         ActivityOptions.abort(options);
                         return;
@@ -1907,9 +2105,15 @@
             here.fillInStackTrace();
             Slog.i(TAG, "Adding activity " + r + " to stack at " + addPos, here);
         }
+        r.task.addActivityToTop(r);
         mHistory.add(addPos, r);
         r.putInHistory();
         r.frontOfTask = newTask;
+        if (VALIDATE_TASK_REPLACE) {
+            if (verifyActivityRecords(false)) {
+                Slog.w(TAG, "startActivityLocked: addPos=" + addPos);
+            }
+        }
         if (NH > 0) {
             // We want to show the starting preview window if we are
             // switching to a new task, or the next activity's process is
@@ -1935,8 +2139,8 @@
                 mNoAnimActivities.remove(r);
             }
             r.updateOptionsLocked(options);
-            mService.mWindowManager.addAppToken(
-                    addPos, r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen,
+            mService.mWindowManager.addAppToken(convertAddPos(addPos),
+                    r.appToken, r.task.taskId, r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
             boolean doShow = true;
             if (newTask) {
@@ -1974,7 +2178,7 @@
         } else {
             // If this is the first activity, don't do any fancy animations,
             // because there is nothing for it to animate on top of.
-            mService.mWindowManager.addAppToken(addPos, r.appToken, r.task.taskId,
+            mService.mWindowManager.addAppToken(convertAddPos(addPos), r.appToken, r.task.taskId,
                     r.info.screenOrientation, r.fullscreen,
                     (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
             ActivityOptions.abort(options);
@@ -1986,6 +2190,11 @@
         if (doResume) {
             resumeTopActivityLocked(null);
         }
+        if (VALIDATE_TASK_REPLACE) {
+            if (verifyActivityRecords(true)) {
+                Slog.w(TAG, "startActivityLocked: addPos=" + addPos);
+            }
+        }
     }
 
     final void validateAppTokensLocked() {
@@ -2012,6 +2221,8 @@
      */
     private final ActivityRecord resetTaskIfNeededLocked(ActivityRecord taskTop,
             ActivityRecord newActivity) {
+        mLastHistoryModifier = "resetTaskIfNeededLocked";
+
         boolean forceReset = (newActivity.info.flags
                 &ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0;
         if (ACTIVITY_INACTIVE_RESET_TIME > 0
@@ -2107,8 +2318,8 @@
                             if (mService.mCurTask <= 0) {
                                 mService.mCurTask = 1;
                             }
-                            target.setTask(new TaskRecord(mService.mCurTask, target.info, null),
-                                    null, false);
+                            target.setTask(createTaskRecord(mService.mCurTask, target.info, null,
+                                    false), null, false);
                             target.task.affinityIntent = target.intent;
                             if (DEBUG_TASKS) Slog.v(TAG, "Start pushing activity " + target
                                     + " out to new task " + target.task);
@@ -2149,6 +2360,7 @@
                             dstPos++;
                             i++;
                         }
+                        rebuildTaskHistory();
                         mService.mWindowManager.moveTaskToBottom(taskId);
                         if (VALIDATE_TOKENS) {
                             validateAppTokensLocked();
@@ -2172,8 +2384,7 @@
                             // like these are all in the reply chain.
                             replyChainEnd = targetI+1;
                             while (replyChainEnd < mHistory.size() &&
-                                    (mHistory.get(
-                                                replyChainEnd)).task == task) {
+                                    (mHistory.get(replyChainEnd)).task == task) {
                                 replyChainEnd++;
                             }
                             replyChainEnd--;
@@ -2298,6 +2509,7 @@
                                 + " in to resetting task " + task);
                         mService.mWindowManager.setAppGroupId(p.appToken, taskId);
                     }
+                    rebuildTaskHistory();
                     // TODO: This is wrong because it doesn't take lastReparentPos into account.
                     mService.mWindowManager.moveTaskToTop(taskId);
                     if (VALIDATE_TOKENS) {
@@ -2346,6 +2558,9 @@
             }
         }
 
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords(true);
+        }
         return taskTop;
     }
     
@@ -2510,6 +2725,7 @@
      */
     private final ActivityRecord moveActivityToFrontLocked(int where) {
         ActivityRecord newTop = mHistory.remove(where);
+        newMoveActivityToFrontLocked(newTop);
         int top = mHistory.size();
         ActivityRecord oldTop = mHistory.get(top-1);
         if (DEBUG_ADD_REMOVE) {
@@ -2519,6 +2735,17 @@
                     + top, here);
         }
         mHistory.add(top, newTop);
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords(true);
+        }
+        return newTop;
+    }
+
+    private final ActivityRecord newMoveActivityToFrontLocked(ActivityRecord newTop) {
+        final TaskRecord task = newTop.task;
+        ActivityRecord oldTop = task.getTopActivity();
+        task.mActivities.remove(newTop);
+        task.mActivities.add(newTop);
         oldTop.frontOfTask = false;
         newTop.frontOfTask = true;
         return newTop;
@@ -2529,6 +2756,7 @@
             String resultWho, int requestCode,
             int callingPid, int callingUid, String callingPackage, int startFlags, Bundle options,
             boolean componentSpecified, ActivityRecord[] outActivity) {
+        mLastHistoryModifier = "startActivityLocked(IApplicationThread)";
 
         int err = ActivityManager.START_SUCCESS;
 
@@ -3011,7 +3239,7 @@
                 if (mService.mCurTask <= 0) {
                     mService.mCurTask = 1;
                 }
-                r.setTask(new TaskRecord(mService.mCurTask, r.info, intent), null, true);
+                r.setTask(createTaskRecord(mService.mCurTask, r.info, intent, true), null, true);
                 if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                         + " in new task " + r.task);
             } else {
@@ -3075,7 +3303,7 @@
                 N > 0 ? mHistory.get(N-1) : null;
             r.setTask(prev != null
                     ? prev.task
-                    : new TaskRecord(mService.mCurTask, r.info, intent), null, true);
+                    : createTaskRecord(mService.mCurTask, r.info, intent, true), null, true);
             if (DEBUG_TASKS) Slog.v(TAG, "Starting new activity " + r
                     + " in new guessed " + r.task);
         }
@@ -4160,6 +4388,10 @@
             here.fillInStackTrace();
             Slog.i(TAG, "Removing activity " + r + " from stack");
         }
+        final TaskRecord task = r.task;
+        if (task != null) {
+            task.removeActivity(r);
+        }
         mHistory.remove(r);
         r.takeFromHistory();
         removeTimeoutsForActivityLocked(r);
@@ -4460,6 +4692,7 @@
      * of the stack.
      */
     final void moveHomeToFrontLocked() {
+        newMoveHomeToFrontLocked();
         TaskRecord homeTask = null;
         for (int i=mHistory.size()-1; i>=0; i--) {
             ActivityRecord hr = mHistory.get(i);
@@ -4469,6 +4702,23 @@
             }
         }
         if (homeTask != null) {
+//            moveTaskToFrontLocked(homeTask, null, null);
+        }
+    }
+
+    final void newMoveHomeToFrontLocked() {
+        TaskRecord homeTask = null;
+        for (int taskNdx = mTaskHistory.size() - 1; homeTask == null && taskNdx >= 0; --taskNdx) {
+            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.isHomeActivity) {
+                    homeTask = r.task;
+                    break;
+                }
+            }
+        }
+        if (homeTask != null) {
             moveTaskToFrontLocked(homeTask, null, null);
         }
     }
@@ -4505,31 +4755,19 @@
     }
 
     final void moveTaskToFrontLocked(TaskRecord tr, ActivityRecord reason, Bundle options) {
-        if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
 
         final int task = tr.taskId;
         int top = mHistory.size()-1;
 
         if (top < 0 || (mHistory.get(top)).task.taskId == task) {
             // nothing to do!
-            if (reason != null &&
-                    (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-                ActivityOptions.abort(options);
-            } else {
-                updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
-            }
             return;
         }
 
-        ArrayList<IBinder> moved = new ArrayList<IBinder>();
-
-        // Applying the affinities may have removed entries from the history,
-        // so get the size again.
-        top = mHistory.size()-1;
-        int pos = top;
 
         // Shift all activities with this task up to the top
         // of the stack, keeping them in the same internal order.
+        int pos = top;
         while (pos >= 0) {
             ActivityRecord r = mHistory.get(pos);
             if (localLOGV) Slog.v(
@@ -4543,18 +4781,37 @@
                 }
                 mHistory.remove(pos);
                 mHistory.add(top, r);
-                moved.add(0, r.appToken);
                 top--;
             }
             pos--;
         }
+        //
+        // Start new code here! Delete everything above.
+        //
+        if (DEBUG_SWITCH) Slog.v(TAG, "moveTaskToFront: " + tr);
 
-        if (DEBUG_TRANSITION) Slog.v(TAG,
-                "Prepare to front transition: task=" + tr);
+        final int numTasks = mTaskHistory.size();
+        final int index = mTaskHistory.indexOf(tr);
+        if (numTasks == 0 || index < 0 || index == numTasks - 1)  {
+            // nothing to do!
+            if (reason != null &&
+                    (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
+                ActivityOptions.abort(options);
+            } else {
+                updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
+            }
+            return;
+        }
+
+        // Shift all activities with this task up to the top
+        // of the stack, keeping them in the same internal order.
+        mTaskHistory.remove(tr);
+        mTaskHistory.add(tr);
+
+        if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr);
         if (reason != null &&
                 (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-            mService.mWindowManager.prepareAppTransition(
-                    AppTransition.TRANSIT_NONE, false);
+            mService.mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
             ActivityRecord r = topRunningActivityLocked(null);
             if (r != null) {
                 mNoAnimActivities.add(r);
@@ -4565,12 +4822,16 @@
         }
 
         mService.mWindowManager.moveTaskToTop(task);
-        if (VALIDATE_TOKENS) {
-            validateAppTokensLocked();
-        }
 
         finishTaskMoveLocked(task);
         EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, task);
+
+        if (VALIDATE_TOKENS) {
+            validateAppTokensLocked();
+        }
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords(true);
+        }
     }
 
     private final void finishTaskMoveLocked(int task) {
@@ -4589,6 +4850,42 @@
      * @return Returns true if the move completed, false if not.
      */
     final boolean moveTaskToBackLocked(int task, ActivityRecord reason) {
+        if (!newMoveTaskToBackLocked(task, reason)) {
+            return false;
+        }
+
+        final int N = mHistory.size();
+        int bottom = 0;
+        int pos = 0;
+
+        // Shift all activities with this task down to the bottom
+        // of the stack, keeping them in the same internal order.
+        while (pos < N) {
+            ActivityRecord r = mHistory.get(pos);
+            if (localLOGV) Slog.v(
+                TAG, "At " + pos + " ckp " + r.task + ": " + r);
+            if (r.task.taskId == task) {
+                if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1));
+                if (DEBUG_ADD_REMOVE) {
+                    RuntimeException here = new RuntimeException("here");
+                    here.fillInStackTrace();
+                    Slog.i(TAG, "Removing and adding activity " + r + " to stack at "
+                            + bottom, here);
+                }
+                mHistory.remove(pos);
+                mHistory.add(bottom, r);
+                bottom++;
+            }
+            pos++;
+        }
+        if (VALIDATE_TASK_REPLACE) {
+            verifyActivityRecords(true);
+        }
+
+        return true;
+    }
+
+    final boolean newMoveTaskToBackLocked(int task, ActivityRecord reason) {
         Slog.i(TAG, "moveTaskToBack: " + task);
         
         // If we have a watcher, preflight the move before committing to it.  First check
@@ -4613,41 +4910,16 @@
             }
         }
 
-        ArrayList<IBinder> moved = new ArrayList<IBinder>();
-
         if (DEBUG_TRANSITION) Slog.v(TAG,
                 "Prepare to back transition: task=" + task);
-        
-        final int N = mHistory.size();
-        int bottom = 0;
-        int pos = 0;
 
-        // Shift all activities with this task down to the bottom
-        // of the stack, keeping them in the same internal order.
-        while (pos < N) {
-            ActivityRecord r = mHistory.get(pos);
-            if (localLOGV) Slog.v(
-                TAG, "At " + pos + " ckp " + r.task + ": " + r);
-            if (r.task.taskId == task) {
-                if (localLOGV) Slog.v(TAG, "Removing and adding at " + (N-1));
-                if (DEBUG_ADD_REMOVE) {
-                    RuntimeException here = new RuntimeException("here");
-                    here.fillInStackTrace();
-                    Slog.i(TAG, "Removing and adding activity " + r + " to stack at "
-                            + bottom, here);
-                }
-                mHistory.remove(pos);
-                mHistory.add(bottom, r);
-                moved.add(r.appToken);
-                bottom++;
-            }
-            pos++;
-        }
+        final TaskRecord tr = mTaskIdToTaskRecord.get(task);
+        mTaskHistory.remove(tr);
+        mTaskHistory.add(0, tr);
 
         if (reason != null &&
                 (reason.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
-            mService.mWindowManager.prepareAppTransition(
-                    AppTransition.TRANSIT_NONE, false);
+            mService.mWindowManager.prepareAppTransition(AppTransition.TRANSIT_NONE, false);
             ActivityRecord r = topRunningActivityLocked(null);
             if (r != null) {
                 mNoAnimActivities.add(r);
@@ -5169,4 +5441,83 @@
 
         return starting;
     }
+
+    void rebuildTaskHistory() {
+        mTaskHistory.clear();
+        final int numActivities = mHistory.size();
+        TaskRecord task = null;
+        for (int i = 0; i < numActivities; ++i) {
+            final ActivityRecord r = mHistory.get(i);
+            if (r.task != task) {
+                task = r.task;
+                task.mActivities.clear();
+                mTaskHistory.add(task);
+            }
+            task.mActivities.add(r);
+        }
+    }
+
+    boolean verifyActivityRecords(boolean rebuild) {
+        final int numHistory = mHistory.size();
+        int historyNdx = 0;
+
+        final int numTasks = mTaskHistory.size();
+        int taskNdx;
+        for (taskNdx = historyNdx = 0; taskNdx < numTasks; ++taskNdx) {
+            final TaskRecord task = mTaskHistory.get(taskNdx);
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            final int numActivities = activities.size();
+            int activityNdx;
+            for (activityNdx = 0;
+                    activityNdx < numActivities && historyNdx < numHistory;
+                    ++activityNdx, ++historyNdx) {
+                ActivityRecord r1 = mHistory.get(historyNdx);
+                ActivityRecord r2 = activities.get(activityNdx);
+                if (r1 != r2) {
+                    break;
+                }
+            }
+            if (activityNdx != numActivities) {
+                // either a mismatch or mHistory ran out before mTaskHistory.
+                break;
+            }
+        }
+        if (taskNdx != numTasks || historyNdx != numHistory) {
+            logHistories("verifyActivityRecords", rebuild);
+            return true;
+        }
+        return false;
+    }
+
+    private void logHistories(String caller, boolean rebuild) {
+        Slog.w(TAG, "Mismatch! " + caller + "  mHistory=" + mHistory);
+        ArrayList<ArrayList<ActivityRecord>> nestedRecords =
+                new ArrayList<ArrayList<ActivityRecord>>();
+        for (TaskRecord task : mTaskHistory) {
+            nestedRecords.add(task.mActivities);
+        }
+        Slog.w(TAG, "Mismatch! " + caller + " mTaskHistory" + nestedRecords);
+        Slog.w(TAG, "Mismatch! " + caller + " lastHistoryModifier=" + mLastHistoryModifier
+                + " Caller=" + Debug.getCallers(4));
+        if (rebuild) {
+            rebuildTaskHistory();
+        }
+    }
+
+    private TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent,
+            boolean toTop) {
+        TaskRecord oldTask = mTaskIdToTaskRecord.get(taskId);
+        if (oldTask != null) {
+            Slog.w(TAG, "createTaskRecord: Reusing taskId=" + taskId + " without removing");
+            mTaskHistory.remove(oldTask);
+        }
+        TaskRecord task = new TaskRecord(taskId, info, intent);
+        mTaskIdToTaskRecord.put(taskId, task);
+        if (toTop) {
+            mTaskHistory.add(task);
+        } else {
+            mTaskHistory.add(0, task);
+        }
+        return task;
+    }
 }
diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index 1bae9ca..f9b0d4c 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -23,6 +23,7 @@
 import android.util.Slog;
 
 import java.io.PrintWriter;
+import java.util.ArrayList;
 
 class TaskRecord extends ThumbnailHolder {
     final int taskId;       // Unique identifier for this task.
@@ -39,7 +40,11 @@
 
     String stringName;      // caching of toString() result.
     int userId;             // user for which this task was created
-    
+
+    int numFullscreen;      // Number of fullscreen activities.
+
+    final ArrayList<ActivityRecord> mActivities = new ArrayList<ActivityRecord>();
+
     TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
         taskId = _taskId;
         affinity = info.taskAffinity;
@@ -104,12 +109,63 @@
             userId = UserHandle.getUserId(info.applicationInfo.uid);
         }
     }
-    
+
+    ActivityRecord getTopActivity() {
+        for (int i = mActivities.size() - 1; i >= 0; --i) {
+            final ActivityRecord r = mActivities.get(i);
+            if (r.finishing) {
+                continue;
+            }
+            return r;
+        }
+        return null;
+    }
+
+    void addActivityAtBottom(ActivityRecord r) {
+        if (!mActivities.remove(r) && r.fullscreen) {
+            // Was not previously in list.
+            numFullscreen++;
+        }
+        mActivities.add(0, r);
+    }
+
+    void addActivityToTop(ActivityRecord r) {
+        if (!mActivities.remove(r) && r.fullscreen) {
+            // Was not previously in list.
+            numFullscreen++;
+        }
+        // TODO: This only matters to achieve identical results as mHistory. Later we won't need
+        // to skip over finishing activities.
+        int i;
+        for (i = mActivities.size() - 1; i >= 0; --i) {
+            if (!mActivities.get(i).finishing) {
+                break;
+            }
+        }
+        if (i >= 0) {
+            // Add below finishing activities.
+            mActivities.add(i + 1, r);
+        } else {
+            // All activities are finishing, add to top.
+            mActivities.add(r);
+        }
+    }
+
+    /** @return true if this was the last activity in the task */
+    boolean removeActivity(ActivityRecord r) {
+        if (mActivities.remove(r) && r.fullscreen) {
+            // Was previously in list.
+            numFullscreen--;
+        }
+        return mActivities.size() == 0;
+    }
+
     void dump(PrintWriter pw, String prefix) {
         if (numActivities != 0 || rootWasReset || userId != 0) {
             pw.print(prefix); pw.print("numActivities="); pw.print(numActivities);
                     pw.print(" rootWasReset="); pw.print(rootWasReset);
-                    pw.print(" userId="); pw.println(userId);
+                    pw.print(" userId="); pw.print(userId);
+                    pw.print(" numFullscreen="); pw.println(numFullscreen);
         }
         if (affinity != null) {
             pw.print(prefix); pw.print("affinity="); pw.println(affinity);
@@ -136,6 +192,7 @@
             pw.print(prefix); pw.print("realActivity=");
             pw.println(realActivity.flattenToShortString());
         }
+        pw.print(prefix); pw.print("Activities="); pw.println(mActivities);
         if (!askedCompatMode) {
             pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode);
         }
@@ -146,6 +203,7 @@
                 pw.print((getInactiveDuration()/1000)); pw.println("s)");
     }
 
+    @Override
     public String toString() {
         if (stringName != null) {
             return stringName;
@@ -156,19 +214,21 @@
         sb.append(" #");
         sb.append(taskId);
         if (affinity != null) {
-            sb.append(" A ");
+            sb.append(" A=");
             sb.append(affinity);
         } else if (intent != null) {
-            sb.append(" I ");
+            sb.append(" I=");
             sb.append(intent.getComponent().flattenToShortString());
         } else if (affinityIntent != null) {
-            sb.append(" aI ");
+            sb.append(" aI=");
             sb.append(affinityIntent.getComponent().flattenToShortString());
         } else {
             sb.append(" ??");
         }
-        sb.append(" U ");
+        sb.append(" U=");
         sb.append(userId);
+        sb.append(" sz=");
+        sb.append(mActivities.size());
         sb.append('}');
         return stringName = sb.toString();
     }
diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java
index 813c9c7..17b0662 100644
--- a/services/java/com/android/server/display/DisplayManagerService.java
+++ b/services/java/com/android/server/display/DisplayManagerService.java
@@ -324,6 +324,18 @@
     }
 
     /**
+     * Sets the overscan insets for a particular display.
+     */
+    public void setOverscan(int displayId, int left, int top, int right, int bottom) {
+        synchronized (mSyncRoot) {
+            LogicalDisplay display = mLogicalDisplays.get(displayId);
+            if (display != null) {
+                display.setOverscan(left, top, right, bottom);
+            }
+        }
+    }
+
+    /**
      * Called by the window manager to perform traversals while holding a
      * surface flinger transaction.
      */
diff --git a/services/java/com/android/server/display/LogicalDisplay.java b/services/java/com/android/server/display/LogicalDisplay.java
index 1583137..424ec36 100644
--- a/services/java/com/android/server/display/LogicalDisplay.java
+++ b/services/java/com/android/server/display/LogicalDisplay.java
@@ -143,6 +143,19 @@
         }
     }
 
+    public void setOverscan(int left, int top, int right, int bottom) {
+        mInfo.overscanLeft = left;
+        mInfo.overscanTop = top;
+        mInfo.overscanRight = right;
+        mInfo.overscanBottom = bottom;
+        if (mOverrideDisplayInfo != null) {
+            mOverrideDisplayInfo.overscanLeft = left;
+            mOverrideDisplayInfo.overscanTop = top;
+            mOverrideDisplayInfo.overscanRight = right;
+            mOverrideDisplayInfo.overscanBottom = bottom;
+        }
+    }
+
     /**
      * Returns true if the logical display is in a valid state.
      * This method should be checked after calling {@link #updateLocked} to handle the
diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java
index f32dd09..5b6e485 100644
--- a/services/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/java/com/android/server/net/LockdownVpnTracker.java
@@ -56,7 +56,9 @@
     private static final int MAX_ERROR_COUNT = 4;
 
     private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET";
+
     private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
+    private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN";
 
     private final Context mContext;
     private final INetworkManagementService mNetService;
@@ -66,7 +68,8 @@
 
     private final Object mStateLock = new Object();
 
-    private PendingIntent mResetIntent;
+    private final PendingIntent mConfigIntent;
+    private final PendingIntent mResetIntent;
 
     private String mAcceptedEgressIface;
     private String mAcceptedIface;
@@ -86,6 +89,10 @@
         mVpn = Preconditions.checkNotNull(vpn);
         mProfile = Preconditions.checkNotNull(profile);
 
+        final Intent configIntent = new Intent(ACTION_VPN_SETTINGS);
+        configIntent.putExtra(EXTRA_PICK_LOCKDOWN, true);
+        mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0);
+
         final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET);
         resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
         mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0);
@@ -193,6 +200,7 @@
             // TODO: support non-standard port numbers
             mNetService.setFirewallEgressDestRule(mProfile.server, 500, true);
             mNetService.setFirewallEgressDestRule(mProfile.server, 4500, true);
+            mNetService.setFirewallEgressDestRule(mProfile.server, 1701, true);
         } catch (RemoteException e) {
             throw new RuntimeException("Problem setting firewall rules", e);
         }
@@ -218,6 +226,7 @@
         try {
             mNetService.setFirewallEgressDestRule(mProfile.server, 500, false);
             mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false);
+            mNetService.setFirewallEgressDestRule(mProfile.server, 1701, false);
         } catch (RemoteException e) {
             throw new RuntimeException("Problem setting firewall rules", e);
         }
@@ -281,10 +290,13 @@
         builder.setWhen(0);
         builder.setSmallIcon(iconRes);
         builder.setContentTitle(mContext.getString(titleRes));
-        builder.setContentText(mContext.getString(R.string.vpn_lockdown_reset));
-        builder.setContentIntent(mResetIntent);
+        builder.setContentText(mContext.getString(R.string.vpn_lockdown_config));
+        builder.setContentIntent(mConfigIntent);
         builder.setPriority(Notification.PRIORITY_LOW);
         builder.setOngoing(true);
+        builder.addAction(
+                R.drawable.ic_menu_refresh, mContext.getString(R.string.reset), mResetIntent);
+
         NotificationManager.from(mContext).notify(TAG, 0, builder.build());
     }
 
diff --git a/services/java/com/android/server/wifi/WifiNotificationController.java b/services/java/com/android/server/wifi/WifiNotificationController.java
new file mode 100644
index 0000000..17ef7c8
--- /dev/null
+++ b/services/java/com/android/server/wifi/WifiNotificationController.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.TaskStackBuilder;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.NetworkInfo;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiStateMachine;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/* Takes care of handling the "open wi-fi network available" notification @hide */
+final class WifiNotificationController {
+    /**
+     * The icon to show in the 'available networks' notification. This will also
+     * be the ID of the Notification given to the NotificationManager.
+     */
+    private static final int ICON_NETWORKS_AVAILABLE =
+            com.android.internal.R.drawable.stat_notify_wifi_in_range;
+    /**
+     * When a notification is shown, we wait this amount before possibly showing it again.
+     */
+    private final long NOTIFICATION_REPEAT_DELAY_MS;
+    /**
+     * Whether the user has set the setting to show the 'available networks' notification.
+     */
+    private boolean mNotificationEnabled;
+    /**
+     * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
+     */
+    private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
+    /**
+     * The {@link System#currentTimeMillis()} must be at least this value for us
+     * to show the notification again.
+     */
+    private long mNotificationRepeatTime;
+    /**
+     * The Notification object given to the NotificationManager.
+     */
+    private Notification mNotification;
+    /**
+     * Whether the notification is being shown, as set by us. That is, if the
+     * user cancels the notification, we will not receive the callback so this
+     * will still be true. We only guarantee if this is false, then the
+     * notification is not showing.
+     */
+    private boolean mNotificationShown;
+    /**
+     * The number of continuous scans that must occur before consider the
+     * supplicant in a scanning state. This allows supplicant to associate with
+     * remembered networks that are in the scan results.
+     */
+    private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
+    /**
+     * The number of scans since the last network state change. When this
+     * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
+     * supplicant to actually be scanning. When the network state changes to
+     * something other than scanning, we reset this to 0.
+     */
+    private int mNumScansSinceNetworkStateChange;
+
+    private final Context mContext;
+    private final WifiStateMachine mWifiStateMachine;
+    private NetworkInfo mNetworkInfo;
+
+    WifiNotificationController(Context context, WifiStateMachine wsm) {
+        mContext = context;
+        mWifiStateMachine = wsm;
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+                            resetNotification();
+                        } else if (intent.getAction().equals(
+                                WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                            mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+                                    WifiManager.EXTRA_NETWORK_INFO);
+                            // reset & clear notification on a network connect & disconnect
+                            switch(mNetworkInfo.getDetailedState()) {
+                                case CONNECTED:
+                                case DISCONNECTED:
+                                case CAPTIVE_PORTAL_CHECK:
+                                    resetNotification();
+                                    break;
+                            }
+                        } else if (intent.getAction().equals(
+                                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+                            checkAndSetNotification(mNetworkInfo,
+                                    mWifiStateMachine.syncGetScanResultsList());
+                        }
+                    }
+                }, filter);
+
+        // Setting is in seconds
+        NOTIFICATION_REPEAT_DELAY_MS = Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
+        mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
+        mNotificationEnabledSettingObserver.register();
+    }
+
+    private synchronized void checkAndSetNotification(NetworkInfo networkInfo,
+            List<ScanResult> scanResults) {
+        // TODO: unregister broadcast so we do not have to check here
+        // If we shouldn't place a notification on available networks, then
+        // don't bother doing any of the following
+        if (!mNotificationEnabled) return;
+        if (networkInfo == null) return;
+
+        NetworkInfo.State state = networkInfo.getState();
+        if ((state == NetworkInfo.State.DISCONNECTED)
+                || (state == NetworkInfo.State.UNKNOWN)) {
+            if (scanResults != null) {
+                int numOpenNetworks = 0;
+                for (int i = scanResults.size() - 1; i >= 0; i--) {
+                    ScanResult scanResult = scanResults.get(i);
+
+                    //A capability of [ESS] represents an open access point
+                    //that is available for an STA to connect
+                    if (scanResult.capabilities != null &&
+                            scanResult.capabilities.equals("[ESS]")) {
+                        numOpenNetworks++;
+                    }
+                }
+
+                if (numOpenNetworks > 0) {
+                    if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
+                        /*
+                         * We've scanned continuously at least
+                         * NUM_SCANS_BEFORE_NOTIFICATION times. The user
+                         * probably does not have a remembered network in range,
+                         * since otherwise supplicant would have tried to
+                         * associate and thus resetting this counter.
+                         */
+                        setNotificationVisible(true, numOpenNetworks, false, 0);
+                    }
+                    return;
+                }
+            }
+        }
+
+        // No open networks in range, remove the notification
+        setNotificationVisible(false, 0, false, 0);
+    }
+
+    /**
+     * Clears variables related to tracking whether a notification has been
+     * shown recently and clears the current notification.
+     */
+    private synchronized void resetNotification() {
+        mNotificationRepeatTime = 0;
+        mNumScansSinceNetworkStateChange = 0;
+        setNotificationVisible(false, 0, false, 0);
+    }
+
+    /**
+     * Display or don't display a notification that there are open Wi-Fi networks.
+     * @param visible {@code true} if notification should be visible, {@code false} otherwise
+     * @param numNetworks the number networks seen
+     * @param force {@code true} to force notification to be shown/not-shown,
+     * even if it is already shown/not-shown.
+     * @param delay time in milliseconds after which the notification should be made
+     * visible or invisible.
+     */
+    private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
+            int delay) {
+
+        // Since we use auto cancel on the notification, when the
+        // mNetworksAvailableNotificationShown is true, the notification may
+        // have actually been canceled.  However, when it is false we know
+        // for sure that it is not being shown (it will not be shown any other
+        // place than here)
+
+        // If it should be hidden and it is already hidden, then noop
+        if (!visible && !mNotificationShown && !force) {
+            return;
+        }
+
+        NotificationManager notificationManager = (NotificationManager) mContext
+                .getSystemService(Context.NOTIFICATION_SERVICE);
+
+        Message message;
+        if (visible) {
+
+            // Not enough time has passed to show the notification again
+            if (System.currentTimeMillis() < mNotificationRepeatTime) {
+                return;
+            }
+
+            if (mNotification == null) {
+                // Cache the Notification object.
+                mNotification = new Notification();
+                mNotification.when = 0;
+                mNotification.icon = ICON_NETWORKS_AVAILABLE;
+                mNotification.flags = Notification.FLAG_AUTO_CANCEL;
+                mNotification.contentIntent = TaskStackBuilder.create(mContext)
+                        .addNextIntentWithParentStack(
+                                new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK))
+                        .getPendingIntent(0, 0, null, UserHandle.CURRENT);
+            }
+
+            CharSequence title = mContext.getResources().getQuantityText(
+                    com.android.internal.R.plurals.wifi_available, numNetworks);
+            CharSequence details = mContext.getResources().getQuantityText(
+                    com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
+            mNotification.tickerText = title;
+            mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
+
+            mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
+
+            notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, mNotification,
+                    UserHandle.ALL);
+        } else {
+            notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL);
+        }
+
+        mNotificationShown = visible;
+    }
+
+    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("mNotificationEnabled " + mNotificationEnabled);
+        pw.println("mNotificationRepeatTime " + mNotificationRepeatTime);
+        pw.println("mNotificationShown " + mNotificationShown);
+        pw.println("mNumScansSinceNetworkStateChange " + mNumScansSinceNetworkStateChange);
+    }
+
+    private class NotificationEnabledSettingObserver extends ContentObserver {
+        public NotificationEnabledSettingObserver(Handler handler) {
+            super(handler);
+        }
+
+        public void register() {
+            ContentResolver cr = mContext.getContentResolver();
+            cr.registerContentObserver(Settings.Global.getUriFor(
+                    Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
+            synchronized (WifiNotificationController.this) {
+                mNotificationEnabled = getValue();
+            }
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            super.onChange(selfChange);
+
+            synchronized (WifiNotificationController.this) {
+                mNotificationEnabled = getValue();
+                resetNotification();
+            }
+        }
+
+        private boolean getValue() {
+            return Settings.Global.getInt(mContext.getContentResolver(),
+                    Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
+        }
+    }
+
+}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
similarity index 70%
rename from services/java/com/android/server/WifiService.java
rename to services/java/com/android/server/wifi/WifiService.java
index ad6eb4d..3c14e3d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -14,45 +14,33 @@
  * limitations under the License.
  */
 
-package com.android.server;
+package com.android.server.wifi;
 
 import android.app.ActivityManager;
 import android.app.AlarmManager;
 import android.app.AppOpsManager;
-import android.app.Notification;
-import android.app.NotificationManager;
 import android.app.PendingIntent;
-import android.app.TaskStackBuilder;
 import android.bluetooth.BluetoothAdapter;
 import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.database.ContentObserver;
 import android.net.wifi.IWifiManager;
 import android.net.wifi.ScanResult;
-import android.net.wifi.SupplicantState;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiStateMachine;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiWatchdogStateMachine;
-import android.net.wifi.WifiConfiguration.KeyMgmt;
-import android.net.wifi.WpsInfo;
-import android.net.wifi.WpsResult;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
 import android.net.DhcpResults;
 import android.net.LinkAddress;
-import android.net.LinkProperties;
 import android.net.NetworkInfo;
-import android.net.NetworkInfo.State;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
-import android.net.TrafficStats;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Messenger;
@@ -61,12 +49,10 @@
 import android.os.INetworkManagementService;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.util.Log;
 import android.util.Slog;
 
@@ -75,11 +61,7 @@
 import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicBoolean;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.telephony.TelephonyIntents;
@@ -97,13 +79,13 @@
 // as a SM to track soft AP/client/adhoc bring up based
 // on device idle state, airplane mode and boot.
 
-public class WifiService extends IWifiManager.Stub {
+public final class WifiService extends IWifiManager.Stub {
     private static final String TAG = "WifiService";
     private static final boolean DBG = false;
 
     private final WifiStateMachine mWifiStateMachine;
 
-    private Context mContext;
+    private final Context mContext;
 
     private AlarmManager mAlarmManager;
     private PendingIntent mIdleIntent;
@@ -130,19 +112,14 @@
     private final IBatteryStats mBatteryStats;
     private final AppOpsManager mAppOps;
 
-    private boolean mEnableTrafficStatsPoll = false;
-    private int mTrafficStatsPollToken = 0;
-    private long mTxPkts;
-    private long mRxPkts;
-    /* Tracks last reported data activity */
-    private int mDataActivity;
     private String mInterfaceName;
 
-    /**
-     * Interval in milliseconds between polling for traffic
-     * statistics
-     */
-    private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
+    /* Tracks the open wi-fi network notification */
+    private WifiNotificationController mNotificationController;
+    /* Polls traffic stats and notifies clients */
+    private WifiTrafficPoller mTrafficPoller;
+    /* Tracks the persisted states for wi-fi & airplane mode */
+    private WifiSettingsStore mSettingsStore;
 
     /**
      * See {@link Settings.Global#WIFI_IDLE_MS}. This is the default value if a
@@ -156,78 +133,14 @@
     private static final String ACTION_DEVICE_IDLE =
             "com.android.server.WifiManager.action.DEVICE_IDLE";
 
-    private static final int WIFI_DISABLED                  = 0;
-    private static final int WIFI_ENABLED                   = 1;
-    /* Wifi enabled while in airplane mode */
-    private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE = 2;
-    /* Wifi disabled due to airplane mode on */
-    private static final int WIFI_DISABLED_AIRPLANE_ON      = 3;
-
-    /* Persisted state that tracks the wifi & airplane interaction from settings */
-    private AtomicInteger mPersistWifiState = new AtomicInteger(WIFI_DISABLED);
-    /* Tracks current airplane mode state */
-    private AtomicBoolean mAirplaneModeOn = new AtomicBoolean(false);
-    /* Tracks whether wifi is enabled from WifiStateMachine's perspective */
-    private boolean mWifiEnabled;
-
     /* The work source (UID) that triggered the current WIFI scan, synchronized
      * on this */
     private WorkSource mScanWorkSource;
 
     private boolean mIsReceiverRegistered = false;
 
-
     NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", "");
 
-    // Variables relating to the 'available networks' notification
-    /**
-     * The icon to show in the 'available networks' notification. This will also
-     * be the ID of the Notification given to the NotificationManager.
-     */
-    private static final int ICON_NETWORKS_AVAILABLE =
-            com.android.internal.R.drawable.stat_notify_wifi_in_range;
-    /**
-     * When a notification is shown, we wait this amount before possibly showing it again.
-     */
-    private final long NOTIFICATION_REPEAT_DELAY_MS;
-    /**
-     * Whether the user has set the setting to show the 'available networks' notification.
-     */
-    private boolean mNotificationEnabled;
-    /**
-     * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
-     */
-    private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
-    /**
-     * The {@link System#currentTimeMillis()} must be at least this value for us
-     * to show the notification again.
-     */
-    private long mNotificationRepeatTime;
-    /**
-     * The Notification object given to the NotificationManager.
-     */
-    private Notification mNotification;
-    /**
-     * Whether the notification is being shown, as set by us. That is, if the
-     * user cancels the notification, we will not receive the callback so this
-     * will still be true. We only guarantee if this is false, then the
-     * notification is not showing.
-     */
-    private boolean mNotificationShown;
-    /**
-     * The number of continuous scans that must occur before consider the
-     * supplicant in a scanning state. This allows supplicant to associate with
-     * remembered networks that are in the scan results.
-     */
-    private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
-    /**
-     * The number of scans since the last network state change. When this
-     * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
-     * supplicant to actually be scanning. When the network state changes to
-     * something other than scanning, we reset this to 0.
-     */
-    private int mNumScansSinceNetworkStateChange;
-
     /**
      * Asynchronous channel to WifiStateMachine
      */
@@ -241,9 +154,9 @@
     /**
      * Handles client connections
      */
-    private class AsyncServiceHandler extends Handler {
+    private class ClientHandler extends Handler {
 
-        AsyncServiceHandler(android.os.Looper looper) {
+        ClientHandler(android.os.Looper looper) {
             super(looper);
         }
 
@@ -273,48 +186,13 @@
                     ac.connect(mContext, this, msg.replyTo);
                     break;
                 }
-                case WifiManager.ENABLE_TRAFFIC_STATS_POLL: {
-                    mEnableTrafficStatsPoll = (msg.arg1 == 1);
-                    mTrafficStatsPollToken++;
-                    if (mEnableTrafficStatsPoll) {
-                        notifyOnDataActivity();
-                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
-                                mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
-                    }
-                    break;
-                }
-                case WifiManager.TRAFFIC_STATS_POLL: {
-                    if (msg.arg1 == mTrafficStatsPollToken) {
-                        notifyOnDataActivity();
-                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
-                                mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
-                    }
-                    break;
-                }
-                case WifiManager.CONNECT_NETWORK: {
-                    mWifiStateMachine.sendMessage(Message.obtain(msg));
-                    break;
-                }
-                case WifiManager.SAVE_NETWORK: {
-                    mWifiStateMachine.sendMessage(Message.obtain(msg));
-                    break;
-                }
-                case WifiManager.FORGET_NETWORK: {
-                    mWifiStateMachine.sendMessage(Message.obtain(msg));
-                    break;
-                }
-                case WifiManager.START_WPS: {
-                    mWifiStateMachine.sendMessage(Message.obtain(msg));
-                    break;
-                }
-                case WifiManager.CANCEL_WPS: {
-                    mWifiStateMachine.sendMessage(Message.obtain(msg));
-                    break;
-                }
-                case WifiManager.DISABLE_NETWORK: {
-                    mWifiStateMachine.sendMessage(Message.obtain(msg));
-                    break;
-                }
+                /* Client commands are forwarded to state machine */
+                case WifiManager.CONNECT_NETWORK:
+                case WifiManager.SAVE_NETWORK:
+                case WifiManager.FORGET_NETWORK:
+                case WifiManager.START_WPS:
+                case WifiManager.CANCEL_WPS:
+                case WifiManager.DISABLE_NETWORK:
                 case WifiManager.RSSI_PKTCNT_FETCH: {
                     mWifiStateMachine.sendMessage(Message.obtain(msg));
                     break;
@@ -326,7 +204,7 @@
             }
         }
     }
-    private AsyncServiceHandler mAsyncServiceHandler;
+    private ClientHandler mClientHandler;
 
     /**
      * Handles interaction with WifiStateMachine
@@ -375,7 +253,7 @@
     private final WorkSource mTmpWorkSource = new WorkSource();
     private WifiWatchdogStateMachine mWifiWatchdogStateMachine;
 
-    WifiService(Context context) {
+    public WifiService(Context context) {
         mContext = context;
 
         mInterfaceName =  SystemProperties.get("wifi.interface", "wlan0");
@@ -389,65 +267,36 @@
         Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null);
         mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0);
 
+        mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);
+        mTrafficPoller = new WifiTrafficPoller(mContext, mClients, mInterfaceName);
+        mSettingsStore = new WifiSettingsStore(mContext);
+
         mContext.registerReceiver(
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
-                        mAirplaneModeOn.set(isAirplaneModeOn());
-                        handleAirplaneModeToggled(mAirplaneModeOn.get());
-                        updateWifiState();
+                        if (mSettingsStore.handleAirplaneModeToggled()) {
+                            updateWifiState();
+                        }
                     }
                 },
                 new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED));
 
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
-        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
-
         mContext.registerReceiver(
                 new BroadcastReceiver() {
                     @Override
                     public void onReceive(Context context, Intent intent) {
-                        if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
-                            int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
-                                    WifiManager.WIFI_STATE_DISABLED);
-
-                            mWifiEnabled = (wifiState == WifiManager.WIFI_STATE_ENABLED);
-
-                           // reset & clear notification on any wifi state change
-                            resetNotification();
-                        } else if (intent.getAction().equals(
-                                WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-                            mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
-                                    WifiManager.EXTRA_NETWORK_INFO);
-                            // reset & clear notification on a network connect & disconnect
-                            switch(mNetworkInfo.getDetailedState()) {
-                                case CONNECTED:
-                                case DISCONNECTED:
-                                case CAPTIVE_PORTAL_CHECK:
-                                    evaluateTrafficStatsPolling();
-                                    resetNotification();
-                                    break;
-                            }
-                        } else if (intent.getAction().equals(
+                        if (intent.getAction().equals(
                                 WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                             noteScanEnd();
-                            checkAndSetNotification();
                         }
                     }
-                }, filter);
+                }, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
 
         HandlerThread wifiThread = new HandlerThread("WifiService");
         wifiThread.start();
-        mAsyncServiceHandler = new AsyncServiceHandler(wifiThread.getLooper());
+        mClientHandler = new ClientHandler(wifiThread.getLooper());
         mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());
-
-        // Setting is in seconds
-        NOTIFICATION_REPEAT_DELAY_MS = Settings.Global.getInt(context.getContentResolver(),
-                Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
-        mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
-        mNotificationEnabledSettingObserver.register();
     }
 
     /** Tell battery stats about a new WIFI scan */
@@ -495,10 +344,8 @@
      * This function is used only at boot time
      */
     public void checkAndStartWifi() {
-        mAirplaneModeOn.set(isAirplaneModeOn());
-        mPersistWifiState.set(getPersistedWifiState());
-        /* Start if Wi-Fi should be enabled or the saved state indicates Wi-Fi was on */
-        boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState();
+        /* Check if wi-fi needs to be enabled */
+        boolean wifiEnabled = mSettingsStore.shouldWifiBeEnabled();
         Slog.i(TAG, "WifiService starting up with Wi-Fi " +
                 (wifiEnabled ? "enabled" : "disabled"));
 
@@ -511,75 +358,6 @@
 
     }
 
-    private boolean testAndClearWifiSavedState() {
-        final ContentResolver cr = mContext.getContentResolver();
-        int wifiSavedState = 0;
-        try {
-            wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE);
-            if(wifiSavedState == 1)
-                Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
-        } catch (Settings.SettingNotFoundException e) {
-            ;
-        }
-        return (wifiSavedState == 1);
-    }
-
-    private int getPersistedWifiState() {
-        final ContentResolver cr = mContext.getContentResolver();
-        try {
-            return Settings.Global.getInt(cr, Settings.Global.WIFI_ON);
-        } catch (Settings.SettingNotFoundException e) {
-            Settings.Global.putInt(cr, Settings.Global.WIFI_ON, WIFI_DISABLED);
-            return WIFI_DISABLED;
-        }
-    }
-
-    private boolean shouldWifiBeEnabled() {
-        if (mAirplaneModeOn.get()) {
-            return mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE;
-        } else {
-            return mPersistWifiState.get() != WIFI_DISABLED;
-        }
-    }
-
-    private void handleWifiToggled(boolean wifiEnabled) {
-        boolean airplaneEnabled = mAirplaneModeOn.get() && isAirplaneToggleable();
-        if (wifiEnabled) {
-            if (airplaneEnabled) {
-                persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE);
-            } else {
-                persistWifiState(WIFI_ENABLED);
-            }
-        } else {
-            // When wifi state is disabled, we do not care
-            // if airplane mode is on or not. The scenario of
-            // wifi being disabled due to airplane mode being turned on
-            // is handled handleAirplaneModeToggled()
-            persistWifiState(WIFI_DISABLED);
-        }
-    }
-
-    private void handleAirplaneModeToggled(boolean airplaneEnabled) {
-        if (airplaneEnabled) {
-            // Wifi disabled due to airplane on
-            if (mWifiEnabled) {
-                persistWifiState(WIFI_DISABLED_AIRPLANE_ON);
-            }
-        } else {
-            /* On airplane mode disable, restore wifi state if necessary */
-            if (testAndClearWifiSavedState() ||
-                    mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE) {
-                persistWifiState(WIFI_ENABLED);
-            }
-        }
-    }
-
-    private void persistWifiState(int state) {
-        final ContentResolver cr = mContext.getContentResolver();
-        mPersistWifiState.set(state);
-        Settings.Global.putInt(cr, Settings.Global.WIFI_ON, state);
-    }
-
     /**
      * see {@link android.net.wifi.WifiManager#pingSupplicant()}
      * @return {@code true} if the operation succeeds, {@code false} otherwise
@@ -640,24 +418,28 @@
             Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");
         }
 
-        if (enable) {
-            reportStartWorkSource();
-        }
-        mWifiStateMachine.setWifiEnabled(enable);
-
         /*
-         * Caller might not have WRITE_SECURE_SETTINGS,
-         * only CHANGE_WIFI_STATE is enforced
-         */
+        * Caller might not have WRITE_SECURE_SETTINGS,
+        * only CHANGE_WIFI_STATE is enforced
+        */
 
         long ident = Binder.clearCallingIdentity();
         try {
-            handleWifiToggled(enable);
+            if (! mSettingsStore.handleWifiToggled(enable)) {
+                // Nothing to do if wifi cannot be toggled
+                return true;
+            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
 
         if (enable) {
+            reportStartWorkSource();
+        }
+
+        mWifiStateMachine.setWifiEnabled(enable);
+
+        if (enable) {
             if (!mIsReceiverRegistered) {
                 registerForBroadcasts();
                 mIsReceiverRegistered = true;
@@ -1047,7 +829,7 @@
     public Messenger getWifiServiceMessenger() {
         enforceAccessPermission();
         enforceChangePermission();
-        return new Messenger(mAsyncServiceHandler);
+        return new Messenger(mClientHandler);
     }
 
     /** Get a reference to WifiStateMachine handler for AsyncChannel communication */
@@ -1082,14 +864,12 @@
                 }
                 mAlarmManager.cancel(mIdleIntent);
                 mScreenOff = false;
-                evaluateTrafficStatsPolling();
                 setDeviceIdleAndUpdateWifi(false);
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 if (DBG) {
                     Slog.d(TAG, "ACTION_SCREEN_OFF");
                 }
                 mScreenOff = true;
-                evaluateTrafficStatsPolling();
                 /*
                  * Set a timer to put Wi-Fi to sleep, but only if the screen is off
                  * AND the "stay on while plugged in" setting doesn't match the
@@ -1221,11 +1001,11 @@
         }
 
         /* Disable tethering when airplane mode is enabled */
-        if (mAirplaneModeOn.get()) {
+        if (mSettingsStore.isAirplaneModeOn()) {
             mWifiStateMachine.setWifiApEnabled(null, false);
         }
 
-        if (shouldWifiBeEnabled()) {
+        if (mSettingsStore.shouldWifiBeEnabled()) {
             if (wifiShouldBeStarted) {
                 reportStartWorkSource();
                 mWifiStateMachine.setWifiEnabled(true);
@@ -1253,30 +1033,6 @@
         mContext.registerReceiver(mReceiver, intentFilter);
     }
 
-    private boolean isAirplaneSensitive() {
-        String airplaneModeRadios = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_RADIOS);
-        return airplaneModeRadios == null
-            || airplaneModeRadios.contains(Settings.Global.RADIO_WIFI);
-    }
-
-    private boolean isAirplaneToggleable() {
-        String toggleableRadios = Settings.Global.getString(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
-        return toggleableRadios != null
-            && toggleableRadios.contains(Settings.Global.RADIO_WIFI);
-    }
-
-    /**
-     * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is
-     * currently on.
-     * @return {@code true} if airplane mode is on.
-     */
-    private boolean isAirplaneModeOn() {
-        return isAirplaneSensitive() && Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
-    }
-
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -1296,18 +1052,9 @@
         pw.println("mEmergencyCallbackMode " + mEmergencyCallbackMode);
         pw.println("mMulticastEnabled " + mMulticastEnabled);
         pw.println("mMulticastDisabled " + mMulticastDisabled);
-        pw.println("mEnableTrafficStatsPoll " + mEnableTrafficStatsPoll);
-        pw.println("mTrafficStatsPollToken " + mTrafficStatsPollToken);
-        pw.println("mTxPkts " + mTxPkts);
-        pw.println("mRxPkts " + mRxPkts);
-        pw.println("mDataActivity " + mDataActivity);
-        pw.println("mPersistWifiState " + mPersistWifiState.get());
-        pw.println("mAirplaneModeOn " + mAirplaneModeOn.get());
-        pw.println("mWifiEnabled " + mWifiEnabled);
-        pw.println("mNotificationEnabled " + mNotificationEnabled);
-        pw.println("mNotificationRepeatTime " + mNotificationRepeatTime);
-        pw.println("mNotificationShown " + mNotificationShown);
-        pw.println("mNumScansSinceNetworkStateChange " + mNumScansSinceNetworkStateChange);
+        mSettingsStore.dump(fd, pw, args);
+        mNotificationController.dump(fd, pw, args);
+        mTrafficPoller.dump(fd, pw, args);
 
         pw.println("Latest scan results:");
         List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
@@ -1700,194 +1447,4 @@
             return (mMulticasters.size() > 0);
         }
     }
-
-    /**
-     * Evaluate if traffic stats polling is needed based on
-     * connection and screen on status
-     */
-    private void evaluateTrafficStatsPolling() {
-        Message msg;
-        if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED && !mScreenOff) {
-            msg = Message.obtain(mAsyncServiceHandler,
-                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 1, 0);
-        } else {
-            msg = Message.obtain(mAsyncServiceHandler,
-                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 0, 0);
-        }
-        msg.sendToTarget();
-    }
-
-    private void notifyOnDataActivity() {
-        long sent, received;
-        long preTxPkts = mTxPkts, preRxPkts = mRxPkts;
-        int dataActivity = WifiManager.DATA_ACTIVITY_NONE;
-
-        mTxPkts = TrafficStats.getTxPackets(mInterfaceName);
-        mRxPkts = TrafficStats.getRxPackets(mInterfaceName);
-
-        if (preTxPkts > 0 || preRxPkts > 0) {
-            sent = mTxPkts - preTxPkts;
-            received = mRxPkts - preRxPkts;
-            if (sent > 0) {
-                dataActivity |= WifiManager.DATA_ACTIVITY_OUT;
-            }
-            if (received > 0) {
-                dataActivity |= WifiManager.DATA_ACTIVITY_IN;
-            }
-
-            if (dataActivity != mDataActivity && !mScreenOff) {
-                mDataActivity = dataActivity;
-                for (AsyncChannel client : mClients) {
-                    client.sendMessage(WifiManager.DATA_ACTIVITY_NOTIFICATION, mDataActivity);
-                }
-            }
-        }
-    }
-
-
-    private void checkAndSetNotification() {
-        // If we shouldn't place a notification on available networks, then
-        // don't bother doing any of the following
-        if (!mNotificationEnabled) return;
-
-        State state = mNetworkInfo.getState();
-        if ((state == NetworkInfo.State.DISCONNECTED)
-                || (state == NetworkInfo.State.UNKNOWN)) {
-            // Look for an open network
-            List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList();
-            if (scanResults != null) {
-                int numOpenNetworks = 0;
-                for (int i = scanResults.size() - 1; i >= 0; i--) {
-                    ScanResult scanResult = scanResults.get(i);
-
-                    //A capability of [ESS] represents an open access point
-                    //that is available for an STA to connect
-                    if (scanResult.capabilities != null &&
-                            scanResult.capabilities.equals("[ESS]")) {
-                        numOpenNetworks++;
-                    }
-                }
-
-                if (numOpenNetworks > 0) {
-                    if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
-                        /*
-                         * We've scanned continuously at least
-                         * NUM_SCANS_BEFORE_NOTIFICATION times. The user
-                         * probably does not have a remembered network in range,
-                         * since otherwise supplicant would have tried to
-                         * associate and thus resetting this counter.
-                         */
-                        setNotificationVisible(true, numOpenNetworks, false, 0);
-                    }
-                    return;
-                }
-            }
-        }
-
-        // No open networks in range, remove the notification
-        setNotificationVisible(false, 0, false, 0);
-    }
-
-    /**
-     * Clears variables related to tracking whether a notification has been
-     * shown recently and clears the current notification.
-     */
-    private void resetNotification() {
-        mNotificationRepeatTime = 0;
-        mNumScansSinceNetworkStateChange = 0;
-        setNotificationVisible(false, 0, false, 0);
-    }
-
-    /**
-     * Display or don't display a notification that there are open Wi-Fi networks.
-     * @param visible {@code true} if notification should be visible, {@code false} otherwise
-     * @param numNetworks the number networks seen
-     * @param force {@code true} to force notification to be shown/not-shown,
-     * even if it is already shown/not-shown.
-     * @param delay time in milliseconds after which the notification should be made
-     * visible or invisible.
-     */
-    private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
-            int delay) {
-
-        // Since we use auto cancel on the notification, when the
-        // mNetworksAvailableNotificationShown is true, the notification may
-        // have actually been canceled.  However, when it is false we know
-        // for sure that it is not being shown (it will not be shown any other
-        // place than here)
-
-        // If it should be hidden and it is already hidden, then noop
-        if (!visible && !mNotificationShown && !force) {
-            return;
-        }
-
-        NotificationManager notificationManager = (NotificationManager) mContext
-                .getSystemService(Context.NOTIFICATION_SERVICE);
-
-        Message message;
-        if (visible) {
-
-            // Not enough time has passed to show the notification again
-            if (System.currentTimeMillis() < mNotificationRepeatTime) {
-                return;
-            }
-
-            if (mNotification == null) {
-                // Cache the Notification object.
-                mNotification = new Notification();
-                mNotification.when = 0;
-                mNotification.icon = ICON_NETWORKS_AVAILABLE;
-                mNotification.flags = Notification.FLAG_AUTO_CANCEL;
-                mNotification.contentIntent = TaskStackBuilder.create(mContext)
-                        .addNextIntentWithParentStack(
-                                new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK))
-                        .getPendingIntent(0, 0, null, UserHandle.CURRENT);
-            }
-
-            CharSequence title = mContext.getResources().getQuantityText(
-                    com.android.internal.R.plurals.wifi_available, numNetworks);
-            CharSequence details = mContext.getResources().getQuantityText(
-                    com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
-            mNotification.tickerText = title;
-            mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
-
-            mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
-
-            notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, mNotification,
-                    UserHandle.ALL);
-        } else {
-            notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL);
-        }
-
-        mNotificationShown = visible;
-    }
-
-    private class NotificationEnabledSettingObserver extends ContentObserver {
-
-        public NotificationEnabledSettingObserver(Handler handler) {
-            super(handler);
-        }
-
-        public void register() {
-            ContentResolver cr = mContext.getContentResolver();
-            cr.registerContentObserver(Settings.Global.getUriFor(
-                Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
-            mNotificationEnabled = getValue();
-        }
-
-        @Override
-        public void onChange(boolean selfChange) {
-            super.onChange(selfChange);
-
-            mNotificationEnabled = getValue();
-            resetNotification();
-        }
-
-        private boolean getValue() {
-            return Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
-        }
-    }
-
-
 }
diff --git a/services/java/com/android/server/wifi/WifiSettingsStore.java b/services/java/com/android/server/wifi/WifiSettingsStore.java
new file mode 100644
index 0000000..d7c8752
--- /dev/null
+++ b/services/java/com/android/server/wifi/WifiSettingsStore.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/* Tracks persisted settings for Wi-Fi and airplane mode interaction */
+final class WifiSettingsStore {
+    /* Values tracked in Settings.Global.WIFI_ON */
+    private static final int WIFI_DISABLED                      = 0;
+    private static final int WIFI_ENABLED                       = 1;
+    /* Wifi enabled while in airplane mode */
+    private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE     = 2;
+    /* Wifi disabled due to airplane mode on */
+    private static final int WIFI_DISABLED_AIRPLANE_ON          = 3;
+
+    /* Persisted state that tracks the wifi & airplane interaction from settings */
+    private int mPersistWifiState = WIFI_DISABLED;
+    /* Tracks current airplane mode state */
+    private boolean mAirplaneModeOn = false;
+    /* Tracks whether wifi is enabled from WifiStateMachine's perspective */
+    private final Context mContext;
+
+    /* Tracks if we have checked the saved wi-fi state after boot */
+    private boolean mCheckSavedStateAtBoot = false;
+
+    WifiSettingsStore(Context context) {
+        mContext = context;
+        mAirplaneModeOn = getPersistedAirplaneModeOn();
+        mPersistWifiState = getPersistedWifiState();
+    }
+
+    synchronized boolean shouldWifiBeEnabled() {
+        if (!mCheckSavedStateAtBoot) {
+            mCheckSavedStateAtBoot = true;
+            if (testAndClearWifiSavedState()) return true;
+        }
+
+        if (mAirplaneModeOn) {
+            return mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE;
+        } else {
+            return mPersistWifiState != WIFI_DISABLED;
+        }
+    }
+
+    /**
+     * Returns true if airplane mode is currently on.
+     * @return {@code true} if airplane mode is on.
+     */
+    synchronized boolean isAirplaneModeOn() {
+       return mAirplaneModeOn;
+    }
+
+    synchronized boolean handleWifiToggled(boolean wifiEnabled) {
+        // Can Wi-Fi be toggled in airplane mode ?
+        if (mAirplaneModeOn && !isAirplaneToggleable()) {
+            return false;
+        }
+
+        if (wifiEnabled) {
+            if (mAirplaneModeOn) {
+                persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE);
+            } else {
+                persistWifiState(WIFI_ENABLED);
+            }
+        } else {
+            // When wifi state is disabled, we do not care
+            // if airplane mode is on or not. The scenario of
+            // wifi being disabled due to airplane mode being turned on
+            // is handled handleAirplaneModeToggled()
+            persistWifiState(WIFI_DISABLED);
+        }
+        return true;
+    }
+
+    synchronized boolean handleAirplaneModeToggled() {
+        // Is Wi-Fi sensitive to airplane mode changes ?
+        if (!isAirplaneSensitive()) {
+            return false;
+        }
+
+        mAirplaneModeOn = getPersistedAirplaneModeOn();
+        if (mAirplaneModeOn) {
+            // Wifi disabled due to airplane on
+            if (mPersistWifiState == WIFI_ENABLED) {
+                persistWifiState(WIFI_DISABLED_AIRPLANE_ON);
+            }
+        } else {
+            /* On airplane mode disable, restore wifi state if necessary */
+            if (testAndClearWifiSavedState() ||
+                    mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE) {
+                persistWifiState(WIFI_ENABLED);
+            }
+        }
+        return true;
+    }
+
+    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("mPersistWifiState " + mPersistWifiState);
+        pw.println("mAirplaneModeOn " + mAirplaneModeOn);
+    }
+
+    private void persistWifiState(int state) {
+        final ContentResolver cr = mContext.getContentResolver();
+        mPersistWifiState = state;
+        Settings.Global.putInt(cr, Settings.Global.WIFI_ON, state);
+    }
+
+    /* Does Wi-Fi need to be disabled when airplane mode is on ? */
+    private boolean isAirplaneSensitive() {
+        String airplaneModeRadios = Settings.Global.getString(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_RADIOS);
+        return airplaneModeRadios == null
+                || airplaneModeRadios.contains(Settings.Global.RADIO_WIFI);
+    }
+
+    /* Is Wi-Fi allowed to be re-enabled while airplane mode is on ? */
+    private boolean isAirplaneToggleable() {
+        String toggleableRadios = Settings.Global.getString(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
+        return toggleableRadios != null
+                && toggleableRadios.contains(Settings.Global.RADIO_WIFI);
+    }
+
+     /* After a reboot, we restore wi-fi to be on if it was turned off temporarily for tethering.
+      * The settings app tracks the saved state, but the framework has to check it at boot to
+      * make sure the wi-fi is turned on in case it was turned off for the purpose of tethering.
+      *
+      * Note that this is not part of the regular WIFI_ON setting because this only needs to
+      * be controlled through the settings app and not the Wi-Fi public API.
+      */
+    private boolean testAndClearWifiSavedState() {
+        final ContentResolver cr = mContext.getContentResolver();
+        int wifiSavedState = 0;
+        try {
+            wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE);
+            if(wifiSavedState == 1)
+                Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
+        } catch (Settings.SettingNotFoundException e) {
+            ;
+        }
+        return (wifiSavedState == 1);
+    }
+
+    private int getPersistedWifiState() {
+        final ContentResolver cr = mContext.getContentResolver();
+        try {
+            return Settings.Global.getInt(cr, Settings.Global.WIFI_ON);
+        } catch (Settings.SettingNotFoundException e) {
+            Settings.Global.putInt(cr, Settings.Global.WIFI_ON, WIFI_DISABLED);
+            return WIFI_DISABLED;
+        }
+    }
+
+    private boolean getPersistedAirplaneModeOn() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+    }
+}
diff --git a/services/java/com/android/server/wifi/WifiTrafficPoller.java b/services/java/com/android/server/wifi/WifiTrafficPoller.java
new file mode 100644
index 0000000..3fcb6c1
--- /dev/null
+++ b/services/java/com/android/server/wifi/WifiTrafficPoller.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.NetworkInfo;
+import static android.net.NetworkInfo.DetailedState.CONNECTED;
+import android.net.TrafficStats;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.Message;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.android.internal.util.AsyncChannel;
+
+/* Polls for traffic stats and notifies the clients */
+final class WifiTrafficPoller {
+    /**
+     * Interval in milliseconds between polling for traffic
+     * statistics
+     */
+    private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000;
+
+    private boolean mEnableTrafficStatsPoll = false;
+    private int mTrafficStatsPollToken = 0;
+    private long mTxPkts;
+    private long mRxPkts;
+    /* Tracks last reported data activity */
+    private int mDataActivity;
+
+    private final List<AsyncChannel> mClients;
+    // err on the side of updating at boot since screen on broadcast may be missed
+    // the first time
+    private AtomicBoolean mScreenOn = new AtomicBoolean(true);
+    private final TrafficHandler mTrafficHandler;
+    private NetworkInfo mNetworkInfo;
+    private final String mInterface;
+
+    WifiTrafficPoller(Context context, List<AsyncChannel> clients, String iface) {
+        mClients = clients;
+        mInterface = iface;
+        mTrafficHandler = new TrafficHandler();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(Intent.ACTION_SCREEN_OFF);
+        filter.addAction(Intent.ACTION_SCREEN_ON);
+
+        context.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        if (intent.getAction().equals(
+                                WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+                            mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
+                                    WifiManager.EXTRA_NETWORK_INFO);
+                        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
+                            mScreenOn.set(false);
+                        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
+                            mScreenOn.set(true);
+                        }
+                        evaluateTrafficStatsPolling();
+                    }
+                }, filter);
+    }
+
+
+    private class TrafficHandler extends Handler {
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case WifiManager.ENABLE_TRAFFIC_STATS_POLL: {
+                    mEnableTrafficStatsPoll = (msg.arg1 == 1);
+                    mTrafficStatsPollToken++;
+                    if (mEnableTrafficStatsPoll) {
+                        notifyOnDataActivity();
+                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
+                                mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
+                    }
+                    break;
+                }
+                case WifiManager.TRAFFIC_STATS_POLL: {
+                    if (msg.arg1 == mTrafficStatsPollToken) {
+                        notifyOnDataActivity();
+                        sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL,
+                                mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS);
+                    }
+                    break;
+                }
+            }
+
+        }
+    }
+
+    private void evaluateTrafficStatsPolling() {
+        Message msg;
+        if (mNetworkInfo == null) return;
+        if (mNetworkInfo.getDetailedState() == CONNECTED && mScreenOn.get()) {
+            msg = Message.obtain(mTrafficHandler,
+                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 1, 0);
+        } else {
+            msg = Message.obtain(mTrafficHandler,
+                    WifiManager.ENABLE_TRAFFIC_STATS_POLL, 0, 0);
+        }
+        msg.sendToTarget();
+    }
+
+    private void notifyOnDataActivity() {
+        long sent, received;
+        long preTxPkts = mTxPkts, preRxPkts = mRxPkts;
+        int dataActivity = WifiManager.DATA_ACTIVITY_NONE;
+
+        mTxPkts = TrafficStats.getTxPackets(mInterface);
+        mRxPkts = TrafficStats.getRxPackets(mInterface);
+
+        if (preTxPkts > 0 || preRxPkts > 0) {
+            sent = mTxPkts - preTxPkts;
+            received = mRxPkts - preRxPkts;
+            if (sent > 0) {
+                dataActivity |= WifiManager.DATA_ACTIVITY_OUT;
+            }
+            if (received > 0) {
+                dataActivity |= WifiManager.DATA_ACTIVITY_IN;
+            }
+
+            if (dataActivity != mDataActivity && mScreenOn.get()) {
+                mDataActivity = dataActivity;
+                for (AsyncChannel client : mClients) {
+                    client.sendMessage(WifiManager.DATA_ACTIVITY_NOTIFICATION, mDataActivity);
+                }
+            }
+        }
+    }
+
+    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("mEnableTrafficStatsPoll " + mEnableTrafficStatsPoll);
+        pw.println("mTrafficStatsPollToken " + mTrafficStatsPollToken);
+        pw.println("mTxPkts " + mTxPkts);
+        pw.println("mRxPkts " + mRxPkts);
+        pw.println("mDataActivity " + mDataActivity);
+    }
+
+}
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index 89e0f17..6aae202 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -135,7 +135,7 @@
             mTaskIdToTaskList.put(wtoken.groupId, task);
             mTaskLists.add(task);
         } else {
-            task.mAppTokens.add(wtoken);
+            task.mAppTokens.add(addPos, wtoken);
         }
     }
 
@@ -186,51 +186,10 @@
         return mTmpAppIterator;
     }
 
-    class TaskListsIterator implements Iterator<TaskList> {
-        private int mCur;
-        private boolean mReverse;
-
-        TaskListsIterator() {
-            this(false);
-        }
-
-        TaskListsIterator(boolean reverse) {
-            reset(reverse);
-        }
-
-        void reset(boolean reverse) {
-            mReverse = reverse;
-            mCur = reverse ? mTaskLists.size() - 1 : 0;
-        }
-
-        @Override
-        public boolean hasNext() {
-            if (mReverse) {
-                return mCur >= 0;
-            }
-            return mCur < mTaskLists.size();
-        }
-
-        @Override
-        public TaskList next() {
-            if (hasNext()) {
-                TaskList taskList = mTaskLists.get(mCur);
-                mCur += (mReverse ? -1 : 1);
-                return taskList;
-            }
-            throw new NoSuchElementException();
-        }
-
-        @Override
-        public void remove() {
-            throw new IllegalArgumentException();
-        }
-    }
-
     class AppTokenIterator implements Iterator<AppWindowToken> {
-        final TaskListsIterator mIterator = new TaskListsIterator();
         boolean mReverse;
-        int mCur;
+        int mTasksNdx;
+        int mActivityNdx;
         TaskList mTaskList;
 
         public AppTokenIterator() {
@@ -243,14 +202,23 @@
 
         void reset(boolean reverse) {
             mReverse = reverse;
-            mIterator.reset(reverse);
+            mTasksNdx = reverse ? mTaskLists.size() - 1 : 0;
             getNextTaskList();
         }
 
         private void getNextTaskList() {
-            if (mIterator.hasNext()) {
-                mTaskList = mIterator.next();
-                mCur = mReverse ? mTaskList.mAppTokens.size() - 1 : 0;
+            if (mReverse) {
+                if (mTasksNdx >= 0) {
+                    mTaskList = mTaskLists.get(mTasksNdx);
+                    --mTasksNdx;
+                    mActivityNdx = mTaskList.mAppTokens.size() - 1;
+                }
+            } else {
+                if (mTasksNdx < mTaskLists.size()) {
+                    mTaskList = mTaskLists.get(mTasksNdx);
+                    ++mTasksNdx;
+                    mActivityNdx = 0;
+                }
             }
         }
 
@@ -260,16 +228,16 @@
                 return false;
             }
             if (mReverse) {
-                return mCur >= 0;
+                return mActivityNdx >= 0;
             }
-            return mCur < mTaskList.mAppTokens.size();
+            return mActivityNdx < mTaskList.mAppTokens.size();
         }
 
         @Override
         public AppWindowToken next() {
             if (hasNext()) {
-                AppWindowToken wtoken = mTaskList.mAppTokens.get(mCur);
-                mCur += mReverse ? -1 : 1;
+                AppWindowToken wtoken = mTaskList.mAppTokens.get(mActivityNdx);
+                mActivityNdx += mReverse ? -1 : 1;
                 if (!hasNext()) {
                     getNextTaskList();
                 }
@@ -285,12 +253,15 @@
 
         int size() {
             int size = 0;
-            final TaskListsIterator iterator = new TaskListsIterator();
-            while (iterator.hasNext()) {
-                size += iterator.next().mAppTokens.size();
+            for (int i = mTaskLists.size() - 1; i >= 0; --i) {
+                size += mTaskLists.get(i).mAppTokens.size();
             }
             return size;
         }
+
+        @Override public String toString() {
+            return mTaskLists.toString();
+        }
     }
 
     public void dump(String prefix, PrintWriter pw) {
@@ -316,6 +287,7 @@
             pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
             pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
             pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
+            pw.print(subPrefix); pw.print("layoutNeeded="); pw.println(layoutNeeded);
             AppTokenIterator iterator = getTmpAppIterator(REVERSE_ITERATOR);
             int ndx = iterator.size() - 1;
             if (ndx >= 0) {
@@ -360,7 +332,6 @@
                     pw.println();
                 }
             }
-        pw.print(subPrefix); pw.print("layoutNeeded="); pw.println(layoutNeeded);
         pw.println();
     }
 }
diff --git a/services/java/com/android/server/wm/DisplaySettings.java b/services/java/com/android/server/wm/DisplaySettings.java
new file mode 100644
index 0000000..34d1a64
--- /dev/null
+++ b/services/java/com/android/server/wm/DisplaySettings.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Environment;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * Current persistent settings about a display
+ */
+public class DisplaySettings {
+    private static final String TAG = WindowManagerService.TAG;
+
+    private final Context mContext;
+    private final AtomicFile mFile;
+    private final HashMap<String, Entry> mEntries = new HashMap<String, Entry>();
+
+    public static class Entry {
+        public final String name;
+        public int overscanLeft;
+        public int overscanTop;
+        public int overscanRight;
+        public int overscanBottom;
+
+        public Entry(String _name) {
+            name = _name;
+        }
+    }
+
+    public DisplaySettings(Context context) {
+        mContext = context;
+        File dataDir = Environment.getDataDirectory();
+        File systemDir = new File(dataDir, "system");
+        mFile = new AtomicFile(new File(systemDir, "display_settings.xml"));
+    }
+
+    public void getOverscanLocked(String name, Rect outRect) {
+        Entry entry = mEntries.get(name);
+        if (entry != null) {
+            outRect.left = entry.overscanLeft;
+            outRect.top = entry.overscanTop;
+            outRect.right = entry.overscanRight;
+            outRect.bottom = entry.overscanBottom;
+        } else {
+            outRect.set(0, 0, 0, 0);
+        }
+    }
+
+    public void setOverscanLocked(String name, int left, int top, int right, int bottom) {
+        if (left == 0 && top == 0 && right == 0 && bottom == 0) {
+            // Right now all we are storing is overscan; if there is no overscan,
+            // we have no need for the entry.
+            mEntries.remove(name);
+            return;
+        }
+        Entry entry = mEntries.get(name);
+        if (entry == null) {
+            entry = new Entry(name);
+            mEntries.put(name, entry);
+        }
+        entry.overscanLeft = left;
+        entry.overscanTop = top;
+        entry.overscanRight = right;
+        entry.overscanBottom = bottom;
+    }
+
+    public void readSettingsLocked() {
+        FileInputStream stream;
+        try {
+            stream = mFile.openRead();
+        } catch (FileNotFoundException e) {
+            Slog.i(TAG, "No existing display settings " + mFile.getBaseFile()
+                    + "; starting empty");
+            return;
+        }
+        boolean success = false;
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, null);
+            int type;
+            while ((type = parser.next()) != XmlPullParser.START_TAG
+                    && type != XmlPullParser.END_DOCUMENT) {
+                ;
+            }
+
+            if (type != XmlPullParser.START_TAG) {
+                throw new IllegalStateException("no start tag found");
+            }
+
+            int outerDepth = parser.getDepth();
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+
+                String tagName = parser.getName();
+                if (tagName.equals("display")) {
+                    readDisplay(parser);
+                } else {
+                    Slog.w(TAG, "Unknown element under <display-settings>: "
+                            + parser.getName());
+                    XmlUtils.skipCurrentTag(parser);
+                }
+            }
+            success = true;
+        } catch (IllegalStateException e) {
+            Slog.w(TAG, "Failed parsing " + e);
+        } catch (NullPointerException e) {
+            Slog.w(TAG, "Failed parsing " + e);
+        } catch (NumberFormatException e) {
+            Slog.w(TAG, "Failed parsing " + e);
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Failed parsing " + e);
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed parsing " + e);
+        } catch (IndexOutOfBoundsException e) {
+            Slog.w(TAG, "Failed parsing " + e);
+        } finally {
+            if (!success) {
+                mEntries.clear();
+            }
+            try {
+                stream.close();
+            } catch (IOException e) {
+            }
+        }
+    }
+
+    private int getIntAttribute(XmlPullParser parser, String name) {
+        try {
+            String str = parser.getAttributeValue(null, name);
+            return str != null ? Integer.parseInt(str) : 0;
+        } catch (NumberFormatException e) {
+            return 0;
+        }
+    }
+
+    private void readDisplay(XmlPullParser parser) throws NumberFormatException,
+            XmlPullParserException, IOException {
+        String name = parser.getAttributeValue(null, "name");
+        if (name != null) {
+            Entry entry = new Entry(name);
+            entry.overscanLeft = getIntAttribute(parser, "overscanLeft");
+            entry.overscanTop = getIntAttribute(parser, "overscanTop");
+            entry.overscanRight = getIntAttribute(parser, "overscanRight");
+            entry.overscanBottom = getIntAttribute(parser, "overscanBottom");
+            mEntries.put(name, entry);
+        }
+        XmlUtils.skipCurrentTag(parser);
+    }
+
+    public void writeSettingsLocked() {
+        FileOutputStream stream;
+        try {
+            stream = mFile.startWrite();
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to write display settings: " + e);
+            return;
+        }
+
+        try {
+            XmlSerializer out = new FastXmlSerializer();
+            out.setOutput(stream, "utf-8");
+            out.startDocument(null, true);
+            out.startTag(null, "display-settings");
+
+            for (Entry entry : mEntries.values()) {
+                out.startTag(null, "display");
+                out.attribute(null, "name", entry.name);
+                if (entry.overscanLeft != 0) {
+                    out.attribute(null, "overscanLeft", Integer.toString(entry.overscanLeft));
+                }
+                if (entry.overscanTop != 0) {
+                    out.attribute(null, "overscanTop", Integer.toString(entry.overscanTop));
+                }
+                if (entry.overscanRight != 0) {
+                    out.attribute(null, "overscanRight", Integer.toString(entry.overscanRight));
+                }
+                if (entry.overscanBottom != 0) {
+                    out.attribute(null, "overscanBottom", Integer.toString(entry.overscanBottom));
+                }
+                out.endTag(null, "display");
+            }
+
+            out.endTag(null, "display-settings");
+            out.endDocument();
+            mFile.finishWrite(stream);
+        } catch (IOException e) {
+            Slog.w(TAG, "Failed to write display settings, restoring backup.", e);
+            mFile.failWrite(stream);
+        }
+    }
+}
diff --git a/services/java/com/android/server/wm/TaskGroup.java b/services/java/com/android/server/wm/TaskGroup.java
index 5e82af2..1f1dd58 100644
--- a/services/java/com/android/server/wm/TaskGroup.java
+++ b/services/java/com/android/server/wm/TaskGroup.java
@@ -23,4 +23,9 @@
 public class TaskGroup {
     public int taskId = -1;
     public ArrayList<IApplicationToken> tokens = new ArrayList<IApplicationToken>();
+
+    @Override
+    public String toString() {
+        return "id=" + taskId + " tokens=" + tokens;
+    }
 }
diff --git a/services/java/com/android/server/wm/TaskList.java b/services/java/com/android/server/wm/TaskList.java
index 88791f2..67dfa4f 100644
--- a/services/java/com/android/server/wm/TaskList.java
+++ b/services/java/com/android/server/wm/TaskList.java
@@ -27,4 +27,9 @@
         mAppTokens.add(wtoken);
         mDisplayContent = displayContent;
     }
+
+    @Override
+    public String toString() {
+        return "id=" + taskId + " appTokens=" + mAppTokens;
+    }
 }
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index d38273d..c2213b3 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -320,6 +320,8 @@
 
     final AppOpsManager mAppOps;
 
+    final DisplaySettings mDisplaySettings;
+
     /**
      * All currently active sessions with clients.
      */
@@ -731,6 +733,8 @@
                 com.android.internal.R.bool.config_sf_limitedAlpha);
         mDisplayManagerService = displayManager;
         mHeadless = displayManager.isHeadless();
+        mDisplaySettings = new DisplaySettings(context);
+        mDisplaySettings.readSettingsLocked();
 
         mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
         mDisplayManager.registerDisplayListener(this, null);
@@ -3120,8 +3124,9 @@
                 return;
             }
 
+            boolean mismatch = false;
             AppTokenIterator iterator = displayContent.getTmpAppIterator(REVERSE_ITERATOR);
-            for ( ; t >= 0; --t) {
+            for ( ; t >= 0 && !mismatch; --t) {
                 task = tasks.get(t);
                 List<IApplicationToken> tokens = task.tokens;
                 int v = task.tokens.size() - 1;
@@ -3133,28 +3138,26 @@
                     return;
                 }
 
-                while (v >= 0 && iterator.hasNext()) {
+                while (v >= 0) {
+                    if (!iterator.hasNext()) {
+                        mismatch = true;
+                        break;
+                    }
                     AppWindowToken atoken = iterator.next();
                     if (atoken.removed) {
                         continue;
                     }
                     if (tokens.get(v) != atoken.token) {
-                        Slog.w(TAG, "Tokens out of sync: external is " + tokens.get(v)
-                              + " @ " + v + ", internal is " + atoken.token);
+                        mismatch = true;
+                        break;
                     }
                     v--;
                 }
-                while (v >= 0) {
-                    Slog.w(TAG, "External token not found: " + tokens.get(v) + " @ " + v);
-                    v--;
-                }
             }
 
-            while (iterator.hasNext()) {
-                AppWindowToken atoken = iterator.next();
-                if (!atoken.removed) {
-                    Slog.w(TAG, "Invalid internal atoken: " + atoken.token);
-                }
+            if (mismatch || iterator.hasNext()) {
+                Slog.w(TAG, "validateAppTokens: Mismatch! ActivityManager=" + tasks);
+                Slog.w(TAG, "validateAppTokens: Mismatch! WindowManager=" + iterator);
             }
         }
     }
@@ -3333,12 +3336,17 @@
         }
 
         synchronized(mWindowMap) {
-            AppWindowToken atoken = findAppWindowToken(token);
+            final AppWindowToken atoken = findAppWindowToken(token);
             if (atoken == null) {
                 Slog.w(TAG, "Attempted to set group id of non-existing app token: " + token);
                 return;
             }
-            mTaskIdToDisplayContents.get(atoken.groupId).setAppTaskId(atoken, groupId);
+            DisplayContent displayContent = mTaskIdToDisplayContents.get(atoken.groupId);
+            if (displayContent == null) {
+                Slog.w(TAG, "setAppGroupId: No DisplayContent for taskId=" + atoken.groupId);
+                displayContent = getDefaultDisplayContentLocked();
+            }
+            displayContent.setAppTaskId(atoken, groupId);
         }
     }
 
@@ -5313,8 +5321,14 @@
 
         if (DEBUG_ORIENTATION) Slog.v(TAG, "freezeRotation: mRotation=" + mRotation);
 
-        mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED,
-                rotation == -1 ? mRotation : rotation);
+        long origId = Binder.clearCallingIdentity();
+        try {
+            mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_LOCKED,
+                    rotation == -1 ? mRotation : rotation);
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
         updateRotationUnchecked(false, false);
     }
 
@@ -5331,7 +5345,14 @@
 
         if (DEBUG_ORIENTATION) Slog.v(TAG, "thawRotation: mRotation=" + mRotation);
 
-        mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE, 777); // rot not used
+        long origId = Binder.clearCallingIdentity();
+        try {
+            mPolicy.setUserRotationMode(WindowManagerPolicy.USER_ROTATION_FREE,
+                    777); // rot not used
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+
         updateRotationUnchecked(false, false);
     }
 
@@ -7089,6 +7110,15 @@
 
     @Override
     public void setForcedDisplaySize(int displayId, int width, int height) {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
+                PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " +
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        }
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can only set the default display");
+        }
         synchronized(mWindowMap) {
             // Set some sort of reasonable bounds on the size of the display that we
             // will try to emulate.
@@ -7160,6 +7190,15 @@
 
     @Override
     public void clearForcedDisplaySize(int displayId) {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
+                PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " +
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        }
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can only set the default display");
+        }
         synchronized(mWindowMap) {
             final DisplayContent displayContent = getDisplayContentLocked(displayId);
             if (displayContent != null) {
@@ -7173,6 +7212,15 @@
 
     @Override
     public void setForcedDisplayDensity(int displayId, int density) {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
+                PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " +
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        }
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can only set the default display");
+        }
         synchronized(mWindowMap) {
             final DisplayContent displayContent = getDisplayContentLocked(displayId);
             if (displayContent != null) {
@@ -7195,6 +7243,15 @@
 
     @Override
     public void clearForcedDisplayDensity(int displayId) {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
+                PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " +
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        }
+        if (displayId != Display.DEFAULT_DISPLAY) {
+            throw new IllegalArgumentException("Can only set the default display");
+        }
         synchronized(mWindowMap) {
             final DisplayContent displayContent = getDisplayContentLocked(displayId);
             if (displayContent != null) {
@@ -7234,6 +7291,34 @@
     }
 
     @Override
+    public void setOverscan(int displayId, int left, int top, int right, int bottom) {
+        if (mContext.checkCallingOrSelfPermission(
+                android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
+                PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Must hold permission " +
+                    android.Manifest.permission.WRITE_SECURE_SETTINGS);
+        }
+        synchronized(mWindowMap) {
+            DisplayContent displayContent = getDisplayContentLocked(displayId);
+            if (displayContent != null) {
+                mDisplayManagerService.setOverscan(displayId, left, top, right, bottom);
+                final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+                synchronized(displayContent.mDisplaySizeLock) {
+                    displayInfo.overscanLeft = left;
+                    displayInfo.overscanTop = top;
+                    displayInfo.overscanRight = right;
+                    displayInfo.overscanBottom = bottom;
+                }
+                mPolicy.setDisplayOverscan(displayContent.getDisplay(), left, top, right, bottom);
+                displayContent.layoutNeeded = true;
+                mDisplaySettings.setOverscanLocked(displayInfo.name, left, top, right, bottom);
+                mDisplaySettings.writeSettingsLocked();
+                performLayoutAndPlaceSurfacesLocked();
+            }
+        }
+    }
+
+    @Override
     public boolean hasSystemNavBar() {
         return mPolicy.hasSystemNavBar();
     }
@@ -9694,7 +9779,7 @@
         if (dumpAll) {
             pw.print("  mSystemDecorRect="); pw.print(mSystemDecorRect.toShortString());
                     pw.print(" mSystemDecorLayer="); pw.print(mSystemDecorLayer);
-                    pw.print(" mScreenRecr="); pw.println(mScreenRect.toShortString());
+                    pw.print(" mScreenRect="); pw.println(mScreenRect.toShortString());
             if (mLastStatusBarVisibility != 0) {
                 pw.print("  mLastStatusBarVisibility=0x");
                         pw.println(Integer.toHexString(mLastStatusBarVisibility));
@@ -9973,12 +10058,28 @@
         }
     }
 
+    private DisplayContent newDisplayContentLocked(final Display display) {
+        DisplayContent displayContent = new DisplayContent(display);
+        mDisplayContents.put(display.getDisplayId(), displayContent);
+        final Rect rect = new Rect();
+        DisplayInfo info = displayContent.getDisplayInfo();
+        mDisplaySettings.getOverscanLocked(info.name, rect);
+        info.overscanLeft = rect.left;
+        info.overscanTop = rect.top;
+        info.overscanRight = rect.right;
+        info.overscanBottom = rect.bottom;
+        mDisplayManagerService.setOverscan(display.getDisplayId(), rect.left, rect.top,
+                rect.right, rect.bottom);
+        mPolicy.setDisplayOverscan(displayContent.getDisplay(), rect.left, rect.top,
+                rect.right, rect.bottom);
+        return displayContent;
+    }
+
     public void createDisplayContentLocked(final Display display) {
         if (display == null) {
             throw new IllegalArgumentException("getDisplayContent: display must not be null");
         }
-        final DisplayContent displayContent = new DisplayContent(display);
-        mDisplayContents.put(display.getDisplayId(), displayContent);
+        newDisplayContentLocked(display);
     }
 
     /**
@@ -9992,8 +10093,7 @@
         if (displayContent == null) {
             final Display display = mDisplayManager.getDisplay(displayId);
             if (display != null) {
-                displayContent = new DisplayContent(display);
-                mDisplayContents.put(displayId, displayContent);
+                displayContent = newDisplayContentLocked(display);
             }
         }
         return displayContent;
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 81a2c14..4ff3899 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -10,7 +10,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     easymocklib \
     guava \
-    littlemock
+    mockito-target
 
 LOCAL_JAVA_LIBRARIES := android.test.runner services
 
diff --git a/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
index f14569c..ffd1568 100644
--- a/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
+++ b/services/tests/servicestests/src/com/android/server/BroadcastInterceptingContext.java
@@ -22,6 +22,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Handler;
+import android.os.UserHandle;
 
 import com.google.common.collect.Lists;
 import com.google.common.util.concurrent.AbstractFuture;
@@ -133,12 +134,28 @@
     }
 
     @Override
+    public void sendBroadcast(Intent intent, String receiverPermission) {
+        sendBroadcast(intent);
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user) {
+        sendBroadcast(intent);
+    }
+
+    @Override
+    public void sendBroadcastAsUser(Intent intent, UserHandle user,
+            String receiverPermission) {
+        sendBroadcast(intent);
+    }
+
+    @Override
     public void sendStickyBroadcast(Intent intent) {
         sendBroadcast(intent);
     }
 
     @Override
-    public void sendBroadcast(Intent intent, String receiverPermission) {
+    public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
         sendBroadcast(intent);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 93ea6a2..4ae013b 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -21,16 +21,15 @@
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.getNetworkTypeName;
 import static android.net.NetworkStateTracker.EVENT_STATE_CHANGED;
-import static com.google.testing.littlemock.LittleMock.anyInt;
-import static com.google.testing.littlemock.LittleMock.createCaptor;
-import static com.google.testing.littlemock.LittleMock.doNothing;
-import static com.google.testing.littlemock.LittleMock.doReturn;
-import static com.google.testing.littlemock.LittleMock.doThrow;
-import static com.google.testing.littlemock.LittleMock.eq;
-import static com.google.testing.littlemock.LittleMock.isA;
-import static com.google.testing.littlemock.LittleMock.mock;
-import static com.google.testing.littlemock.LittleMock.reset;
-import static com.google.testing.littlemock.LittleMock.verify;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isA;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
 
 import android.content.Context;
 import android.net.INetworkPolicyManager;
@@ -48,7 +47,7 @@
 import android.util.Log;
 import android.util.LogPrinter;
 
-import com.google.testing.littlemock.ArgumentCaptor;
+import org.mockito.ArgumentCaptor;
 
 import java.net.InetAddress;
 import java.util.concurrent.Future;
@@ -128,7 +127,7 @@
         doReturn(mWifi.tracker)
                 .when(mNetFactory).createTracker(eq(TYPE_WIFI), isA(NetworkConfig.class));
 
-        final ArgumentCaptor<Handler> trackerHandler = createCaptor();
+        final ArgumentCaptor<Handler> trackerHandler = ArgumentCaptor.forClass(Handler.class);
         doNothing().when(mMobile.tracker)
                 .startMonitoring(isA(Context.class), trackerHandler.capture());
 
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index cfc6bd7..29d6e4d 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -551,6 +551,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** {@hide} */
+    @Override
+    public int getUserId() {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public Context createConfigurationContext(Configuration overrideConfiguration) {
         throw new UnsupportedOperationException();
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index ed44b04..278413e 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -110,6 +110,12 @@
     }
 
     @Override
+    public void setOverscan(int displayId, int left, int top, int right, int bottom)
+            throws RemoteException {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
     public void closeSystemDialogs(String arg0) throws RemoteException {
         // TODO Auto-generated method stub
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 8b643c0..21bef1c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1398,4 +1398,12 @@
         // pass
         return null;
     }
+
+    /**
+     * @hide
+     */
+    @Override
+    public int getUserId() {
+        return 0; // not used
+    }
 }