Merge "Fix null ActivityOptions when starting an Activity in a new Process."
diff --git a/Android.mk b/Android.mk
index a691987..45e9c76 100644
--- a/Android.mk
+++ b/Android.mk
@@ -136,6 +136,8 @@
 	core/java/android/hardware/ISerialManager.aidl \
 	core/java/android/hardware/display/IDisplayManager.aidl \
 	core/java/android/hardware/display/IDisplayManagerCallback.aidl \
+	core/java/android/hardware/hdmi/IHdmiCecListener.aidl \
+	core/java/android/hardware/hdmi/IHdmiCecService.aidl \
 	core/java/android/hardware/input/IInputManager.aidl \
 	core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
 	core/java/android/hardware/location/IFusedLocationHardware.aidl \
@@ -196,7 +198,6 @@
 	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
 	core/java/android/view/IApplicationToken.aidl \
 	core/java/android/view/IAssetAtlas.aidl \
-	core/java/android/view/IMagnificationCallbacks.aidl \
 	core/java/android/view/IInputFilter.aidl \
 	core/java/android/view/IInputFilterHost.aidl \
 	core/java/android/view/IOnKeyguardExitResult.aidl \
@@ -399,6 +400,7 @@
 	frameworks/base/core/java/android/view/accessibility/AccessibilityEvent.aidl \
 	frameworks/base/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl \
 	frameworks/base/core/java/android/view/accessibility/AccessibilityRecord.aidl \
+	frameworks/base/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl \
 	frameworks/base/core/java/android/view/KeyEvent.aidl \
 	frameworks/base/core/java/android/view/MotionEvent.aidl \
 	frameworks/base/core/java/android/view/Surface.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 448b03d..4f0e603 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -187,6 +187,8 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/print/IPrintClient.*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/media/java/android/media/IMedia*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/view/IMagnificationCallbacks*)
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/api/current.txt b/api/current.txt
index e72354b..4c1f6fa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2328,6 +2328,7 @@
     ctor public AccessibilityService();
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+    method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
     method public abstract void onAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
     method public final android.os.IBinder onBind(android.content.Intent);
     method protected boolean onGesture(int);
@@ -2393,6 +2394,7 @@
     field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
     field public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 32; // 0x20
     field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4
+    field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40
     field public int eventTypes;
     field public int feedbackType;
     field public int flags;
@@ -3154,6 +3156,8 @@
     method public final void setProgressBarIndeterminate(boolean);
     method public final void setProgressBarIndeterminateVisibility(boolean);
     method public final void setProgressBarVisibility(boolean);
+    method public void setRecentsIcon(android.graphics.Bitmap);
+    method public void setRecentsLabel(java.lang.CharSequence);
     method public void setRequestedOrientation(int);
     method public final void setResult(int);
     method public final void setResult(int, android.content.Intent);
@@ -3274,6 +3278,8 @@
     method public void readFromParcel(android.os.Parcel);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public android.graphics.Bitmap activityIcon;
+    field public java.lang.CharSequence activityLabel;
     field public android.content.Intent baseIntent;
     field public java.lang.CharSequence description;
     field public int id;
@@ -4200,6 +4206,20 @@
     method public int describeContents();
     method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final java.lang.String CATEGORY_ALARM = "alarm";
+    field public static final java.lang.String CATEGORY_CALL = "call";
+    field public static final java.lang.String CATEGORY_EMAIL = "email";
+    field public static final java.lang.String CATEGORY_ERROR = "err";
+    field public static final java.lang.String CATEGORY_EVENT = "event";
+    field public static final java.lang.String CATEGORY_MESSAGE = "msg";
+    field public static final java.lang.String CATEGORY_PROGRESS = "progress";
+    field public static final java.lang.String CATEGORY_PROMO = "promo";
+    field public static final java.lang.String CATEGORY_RECOMMENDATION = "recommendation";
+    field public static final java.lang.String CATEGORY_SERVICE = "service";
+    field public static final java.lang.String CATEGORY_SOCIAL = "social";
+    field public static final java.lang.String CATEGORY_STATUS = "status";
+    field public static final java.lang.String CATEGORY_SYSTEM = "sys";
+    field public static final java.lang.String CATEGORY_TRANSPORT = "transport";
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int DEFAULT_ALL = -1; // 0xffffffff
     field public static final int DEFAULT_LIGHTS = 4; // 0x4
@@ -4244,6 +4264,7 @@
     field public android.app.Notification.Action[] actions;
     field public int audioStreamType;
     field public android.widget.RemoteViews bigContentView;
+    field public java.lang.String category;
     field public android.app.PendingIntent contentIntent;
     field public android.widget.RemoteViews contentView;
     field public int defaults;
@@ -4304,6 +4325,7 @@
     method public android.os.Bundle getExtras();
     method public deprecated android.app.Notification getNotification();
     method public android.app.Notification.Builder setAutoCancel(boolean);
+    method public android.app.Notification.Builder setCategory(java.lang.String);
     method public android.app.Notification.Builder setContent(android.widget.RemoteViews);
     method public android.app.Notification.Builder setContentInfo(java.lang.CharSequence);
     method public android.app.Notification.Builder setContentIntent(android.app.PendingIntent);
@@ -4600,6 +4622,7 @@
     method public android.view.accessibility.AccessibilityEvent executeAndWaitForEvent(java.lang.Runnable, android.app.UiAutomation.AccessibilityEventFilter, long) throws java.util.concurrent.TimeoutException;
     method public android.view.accessibility.AccessibilityNodeInfo getRootInActiveWindow();
     method public final android.accessibilityservice.AccessibilityServiceInfo getServiceInfo();
+    method public java.util.List<android.view.accessibility.AccessibilityWindowInfo> getWindows();
     method public boolean injectInputEvent(android.view.InputEvent, boolean);
     method public final boolean performGlobalAction(int);
     method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
@@ -4973,15 +4996,19 @@
     field public static final java.lang.String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
     field public static final java.lang.String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
     field public static final java.lang.String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
+    field public static final java.lang.String ACTION_APPWIDGET_HOST_RESTORED = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
     field public static final java.lang.String ACTION_APPWIDGET_OPTIONS_CHANGED = "android.appwidget.action.APPWIDGET_UPDATE_OPTIONS";
     field public static final java.lang.String ACTION_APPWIDGET_PICK = "android.appwidget.action.APPWIDGET_PICK";
+    field public static final java.lang.String ACTION_APPWIDGET_RESTORED = "android.appwidget.action.APPWIDGET_RESTORED";
     field public static final java.lang.String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
     field public static final java.lang.String EXTRA_APPWIDGET_ID = "appWidgetId";
     field public static final java.lang.String EXTRA_APPWIDGET_IDS = "appWidgetIds";
+    field public static final java.lang.String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds";
     field public static final java.lang.String EXTRA_APPWIDGET_OPTIONS = "appWidgetOptions";
     field public static final java.lang.String EXTRA_APPWIDGET_PROVIDER = "appWidgetProvider";
     field public static final java.lang.String EXTRA_CUSTOM_EXTRAS = "customExtras";
     field public static final java.lang.String EXTRA_CUSTOM_INFO = "customInfo";
+    field public static final java.lang.String EXTRA_HOST_ID = "hostId";
     field public static final int INVALID_APPWIDGET_ID = 0; // 0x0
     field public static final java.lang.String META_DATA_APPWIDGET_PROVIDER = "android.appwidget.provider";
     field public static final java.lang.String OPTION_APPWIDGET_HOST_CATEGORY = "appWidgetCategory";
@@ -4998,6 +5025,7 @@
     method public void onDisabled(android.content.Context);
     method public void onEnabled(android.content.Context);
     method public void onReceive(android.content.Context, android.content.Intent);
+    method public void onRestored(android.content.Context, int[], int[]);
     method public void onUpdate(android.content.Context, android.appwidget.AppWidgetManager, int[]);
   }
 
@@ -11773,6 +11801,54 @@
 
 }
 
+package android.hardware.hdmi {
+
+  public final class HdmiCec {
+    method public static java.lang.String getDefaultDeviceName(int);
+    method public static int getTypeFromAddress(int);
+    method public static boolean isValidAddress(int);
+    method public static boolean isValidType(int);
+    field public static final int ADDR_AUDIO_SYSTEM = 5; // 0x5
+    field public static final int ADDR_BROADCAST = 15; // 0xf
+    field public static final int ADDR_FREE_USE = 14; // 0xe
+    field public static final int ADDR_INVALID = -1; // 0xffffffff
+    field public static final int ADDR_PLAYBACK_1 = 4; // 0x4
+    field public static final int ADDR_PLAYBACK_2 = 8; // 0x8
+    field public static final int ADDR_PLAYBACK_3 = 11; // 0xb
+    field public static final int ADDR_RECORDER_1 = 1; // 0x1
+    field public static final int ADDR_RECORDER_2 = 2; // 0x2
+    field public static final int ADDR_RECORDER_3 = 9; // 0x9
+    field public static final int ADDR_RESERVED_1 = 12; // 0xc
+    field public static final int ADDR_RESERVED_2 = 13; // 0xd
+    field public static final int ADDR_TUNER_1 = 3; // 0x3
+    field public static final int ADDR_TUNER_2 = 6; // 0x6
+    field public static final int ADDR_TUNER_3 = 7; // 0x7
+    field public static final int ADDR_TUNER_4 = 10; // 0xa
+    field public static final int ADDR_TV = 0; // 0x0
+    field public static final int ADDR_UNREGISTERED = 15; // 0xf
+    field public static final int DEVICE_AUDIO_SYSTEM = 5; // 0x5
+    field public static final int DEVICE_INACTIVE = -1; // 0xffffffff
+    field public static final int DEVICE_PLAYBACK = 4; // 0x4
+    field public static final int DEVICE_RECORDER = 1; // 0x1
+    field public static final int DEVICE_RESERVED = 2; // 0x2
+    field public static final int DEVICE_TUNER = 3; // 0x3
+    field public static final int DEVICE_TV = 0; // 0x0
+    field public static final int MESSAGE_ACTIVE_SOURCE = 157; // 0x9d
+  }
+
+  public final class HdmiCecMessage implements android.os.Parcelable {
+    ctor public HdmiCecMessage(int, int, int, byte[]);
+    method public int describeContents();
+    method public int getDestination();
+    method public int getOpcode();
+    method public byte[] getParams();
+    method public int getSource();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
+}
+
 package android.hardware.input {
 
   public final class InputManager {
@@ -19546,8 +19622,10 @@
   public abstract class Vibrator {
     method public abstract void cancel();
     method public abstract boolean hasVibrator();
-    method public abstract void vibrate(long);
-    method public abstract void vibrate(long[], int);
+    method public void vibrate(long);
+    method public void vibrate(long, int);
+    method public void vibrate(long[], int);
+    method public void vibrate(long[], int, int);
   }
 
   public class WorkSource implements android.os.Parcelable {
@@ -30398,6 +30476,7 @@
     field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10
     field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
     field public static final int TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY = 131072; // 0x20000
+    field public static final int TYPE_WINDOWS_CHANGED = 4194304; // 0x400000
     field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
     field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20
   }
@@ -30461,6 +30540,7 @@
     method public int getTextSelectionEnd();
     method public int getTextSelectionStart();
     method public java.lang.String getViewIdResourceName();
+    method public android.view.accessibility.AccessibilityWindowInfo getWindow();
     method public int getWindowId();
     method public boolean isAccessibilityFocused();
     method public boolean isCheckable();
@@ -30607,6 +30687,7 @@
     method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String, int);
     method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
     method public boolean performAction(int, int, android.os.Bundle);
+    field public static final int HOST_VIEW_ID = -1; // 0xffffffff
   }
 
   public class AccessibilityRecord {
@@ -30658,6 +30739,28 @@
     method public void setToIndex(int);
   }
 
+  public final class AccessibilityWindowInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public void getBoundsInScreen(android.graphics.Rect);
+    method public android.view.accessibility.AccessibilityWindowInfo getChild(int);
+    method public int getChildCount();
+    method public int getId();
+    method public int getLayer();
+    method public android.view.accessibility.AccessibilityWindowInfo getParent();
+    method public android.view.accessibility.AccessibilityNodeInfo getRoot();
+    method public int getType();
+    method public boolean isActive();
+    method public boolean isFocused();
+    method public static android.view.accessibility.AccessibilityWindowInfo obtain();
+    method public static android.view.accessibility.AccessibilityWindowInfo obtain(android.view.accessibility.AccessibilityWindowInfo);
+    method public void recycle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int TYPE_APPLICATION = 1; // 0x1
+    field public static final int TYPE_INPUT_METHOD = 2; // 0x2
+    field public static final int TYPE_SYSTEM = 3; // 0x3
+  }
+
   public class CaptioningManager {
     method public void addCaptioningChangeListener(android.view.accessibility.CaptioningManager.CaptioningChangeListener);
     method public final float getFontScale();
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index b9afe40..cb7e1a0 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -1,4 +1,6 @@
 LOCAL_PATH:= $(call my-dir)
+
+# 32-bit app_process
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
@@ -12,9 +14,29 @@
 	libandroid_runtime
 
 LOCAL_MODULE:= app_process
-
+LOCAL_32_BIT_ONLY := true
 include $(BUILD_EXECUTABLE)
 
+ifeq ($(TARGET_IS_64_BIT),true)
+
+# 64-bit app_process64
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	app_main.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libcutils \
+	libutils \
+	liblog \
+	libbinder \
+	libandroid_runtime
+
+LOCAL_MODULE:= app_process64
+LOCAL_NO_2ND_ARCH := true
+include $(BUILD_EXECUTABLE)
+
+endif # TARGET_IS_64_BIT
 
 # Build a variant of app_process binary linked with ASan runtime.
 # ARM-only at the moment.
diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk
index a4e2718..c4fe6cf 100644
--- a/cmds/bootanimation/Android.mk
+++ b/cmds/bootanimation/Android.mk
@@ -21,5 +21,8 @@
 
 LOCAL_MODULE:= bootanimation
 
+ifdef TARGET_32_BIT_SURFACEFLINGER
+LOCAL_32_BIT_ONLY := true
+endif
 
 include $(BUILD_EXECUTABLE)
diff --git a/cmds/bu/src/com/android/commands/bu/Backup.java b/cmds/bu/src/com/android/commands/bu/Backup.java
index 73fd660..2673031 100644
--- a/cmds/bu/src/com/android/commands/bu/Backup.java
+++ b/cmds/bu/src/com/android/commands/bu/Backup.java
@@ -68,6 +68,7 @@
         boolean saveObbs = false;
         boolean saveShared = false;
         boolean doEverything = false;
+        boolean doWidgets = false;
         boolean allIncludesSystem = true;
 
         String arg;
@@ -89,6 +90,10 @@
                     allIncludesSystem = true;
                 } else if ("-nosystem".equals(arg)) {
                     allIncludesSystem = false;
+                } else if ("-widgets".equals(arg)) {
+                    doWidgets = true;
+                } else if ("-nowidgets".equals(arg)) {
+                    doWidgets = false;
                 } else if ("-all".equals(arg)) {
                     doEverything = true;
                 } else {
@@ -114,8 +119,8 @@
         try {
             fd = ParcelFileDescriptor.adoptFd(socketFd);
             String[] packArray = new String[packages.size()];
-            mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doEverything,
-                    allIncludesSystem, packages.toArray(packArray));
+            mBackupManager.fullBackup(fd, saveApks, saveObbs, saveShared, doWidgets,
+                    doEverything, allIncludesSystem, packages.toArray(packArray));
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to invoke backup manager for backup");
         } finally {
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 1e3d5be..b01d92c 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -23,14 +23,18 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
 
 import com.android.internal.os.HandlerCaller;
 
+import java.util.List;
+
 /**
  * An accessibility service runs in the background and receives callbacks by the system
  * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
@@ -180,28 +184,37 @@
  * event generation has settled down.</p>
  * <h3>Event types</h3>
  * <ul>
- * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}
- * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}
- * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}
- * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_CLICKED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_FOCUSED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_SELECTED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_SCROLLED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_ANNOUNCEMENT}</li>
+ * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_START}</li>
+ * <li>{@link AccessibilityEvent#TYPE_GESTURE_DETECTION_END}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_START}</li>
+ * <li>{@link AccessibilityEvent#TYPE_TOUCH_INTERACTION_END}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_WINDOWS_CHANGED}</li>
+ * <li>{@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED}</li>
  * </ul>
  * <h3>Feedback types</h3>
  * <ul>
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}
- * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_VISUAL}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_GENERIC}</li>
+ * <li>{@link AccessibilityServiceInfo#FEEDBACK_BRAILLE}</li>
  * </ul>
  * @see AccessibilityEvent
  * @see AccessibilityServiceInfo
@@ -443,8 +456,41 @@
     }
 
     /**
+     * Gets the windows on the screen. This method returns only the windows
+     * that a sighted user can interact with, as opposed to all windows.
+     * For example, if there is a modal dialog shown and the user cannot touch
+     * anything behind it, then only the modal window will be reported
+     * (assuming it is the top one). For convenience the returned windows
+     * are ordered in a descending layer order, which is the windows that
+     * are higher in the Z-order are reported first.
+     * <p>
+     * <strong>Note:</strong> In order to access the windows your service has
+     * to declare the capability to retrieve window content by setting the
+     * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
+     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+     * Also the service has to opt-in to retrieve the interactive windows by
+     * setting the {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS}
+     * flag.
+     * </p>
+     *
+     * @return The windows if there are windows and the service is can retrieve
+     *         them, otherwise an empty list.
+     */
+    public List<AccessibilityWindowInfo> getWindows() {
+        return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId);
+    }
+
+    /**
      * Gets the root node in the currently active window if this service
-     * can retrieve window content.
+     * can retrieve window content. The active window is the one that the user
+     * is currently touching or the window with input focus, if the user is not
+     * touching any window.
+     * <p>
+     * <strong>Note:</strong> In order to access the root node your service has
+     * to declare the capability to retrieve window content by setting the
+     * {@link android.R.styleable#AccessibilityService_canRetrieveWindowContent}
+     * property in its meta-data. For details refer to {@link #SERVICE_META_DATA}.
+     * </p>
      *
      * @return The root node if this service can retrieve window content.
      */
@@ -584,12 +630,13 @@
 
         static final int NO_ID = -1;
 
-        private static final int DO_SET_SET_CONNECTION = 10;
-        private static final int DO_ON_INTERRUPT = 20;
-        private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
-        private static final int DO_ON_GESTURE = 40;
-        private static final int DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 50;
-        private static final int DO_ON_KEY_EVENT = 60;
+        private static final int DO_SET_SET_CONNECTION = 1;
+        private static final int DO_ON_INTERRUPT = 2;
+        private static final int DO_ON_ACCESSIBILITY_EVENT = 3;
+        private static final int DO_ON_GESTURE = 4;
+        private static final int DO_CLEAR_ACCESSIBILITY_CACHE = 5;
+        private static final int DO_ON_KEY_EVENT = 6;
+        private static final int DO_ON_WINDOWS_CHANGED = 7;
 
         private final HandlerCaller mCaller;
 
@@ -624,8 +671,8 @@
             mCaller.sendMessage(message);
         }
 
-        public void clearAccessibilityNodeInfoCache() {
-            Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
+        public void clearAccessibilityCache() {
+            Message message = mCaller.obtainMessage(DO_CLEAR_ACCESSIBILITY_CACHE);
             mCaller.sendMessage(message);
         }
 
@@ -635,6 +682,13 @@
             mCaller.sendMessage(message);
         }
 
+        @Override
+        public void onWindowsChanged(int[] windowIds) {
+            Message message = mCaller.obtainMessageO(DO_ON_WINDOWS_CHANGED, windowIds);
+            mCaller.sendMessage(message);
+        }
+
+        @Override
         public void executeMessage(Message message) {
             switch (message.what) {
                 case DO_ON_ACCESSIBILITY_EVENT: {
@@ -642,12 +696,19 @@
                     if (event != null) {
                         AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event);
                         mCallback.onAccessibilityEvent(event);
-                        event.recycle();
+                        // Make sure the event is recycled.
+                        try {
+                            event.recycle();
+                        } catch (IllegalStateException ise) {
+                            /* ignore - best effort */
+                        }
                     }
                 } return;
+
                 case DO_ON_INTERRUPT: {
                     mCallback.onInterrupt();
                 } return;
+
                 case DO_SET_SET_CONNECTION: {
                     mConnectionId = message.arg1;
                     IAccessibilityServiceConnection connection =
@@ -658,18 +719,22 @@
                         mCallback.onSetConnectionId(mConnectionId);
                         mCallback.onServiceConnected();
                     } else {
-                        AccessibilityInteractionClient.getInstance().removeConnection(mConnectionId);
+                        AccessibilityInteractionClient.getInstance().removeConnection(
+                                mConnectionId);
                         AccessibilityInteractionClient.getInstance().clearCache();
                         mCallback.onSetConnectionId(AccessibilityInteractionClient.NO_ID);
                     }
                 } return;
+
                 case DO_ON_GESTURE: {
                     final int gestureId = message.arg1;
                     mCallback.onGesture(gestureId);
                 } return;
-                case DO_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: {
+
+                case DO_CLEAR_ACCESSIBILITY_CACHE: {
                     AccessibilityInteractionClient.getInstance().clearCache();
                 } return;
+
                 case DO_ON_KEY_EVENT: {
                     KeyEvent event = (KeyEvent) message.obj;
                     try {
@@ -685,9 +750,40 @@
                             }
                         }
                     } finally {
-                        event.recycle();
+                        // Make sure the event is recycled.
+                        try {
+                            event.recycle();
+                        } catch (IllegalStateException ise) {
+                            /* ignore - best effort */
+                        }
                     }
                 } return;
+
+                case DO_ON_WINDOWS_CHANGED: {
+                    final int[] windowIds = (int[]) message.obj;
+
+                    // Update the cached windows first.
+                    // NOTE: The cache will hold on to the windows so do not recycle.
+                    if (windowIds != null) {
+                        AccessibilityInteractionClient.getInstance().removeWindows(windowIds);
+                    }
+
+                    // Let the client know the windows changed.
+                    AccessibilityEvent event = AccessibilityEvent.obtain(
+                            AccessibilityEvent.TYPE_WINDOWS_CHANGED);
+                    event.setEventTime(SystemClock.uptimeMillis());
+                    event.setSealed(true);
+
+                    mCallback.onAccessibilityEvent(event);
+
+                    // Make sure the event is recycled.
+                    try {
+                        event.recycle();
+                    } catch (IllegalStateException ise) {
+                        /* ignore - best effort */
+                    }
+                } break;
+
                 default :
                     Log.w(LOG_TAG, "Unknown message type " + message.what);
             }
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index bdc4fdde..4f9ba59 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -284,6 +284,27 @@
     public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020;
 
     /**
+     * This flag indicates to the system that the accessibility service wants
+     * to access content of all interactive windows. An interactive window is a
+     * window that can be touched by a sighted user when explore by touch is not
+     * enabled. If this flag is not set your service will not receive
+     * {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED}
+     * events, calling AccessibilityService{@link AccessibilityService#getWindows()
+     * AccessibilityService.getWindows()} will return an empty list, and {@link
+     * AccessibilityNodeInfo#getWindow() AccessibilityNodeInfo.getWindow()} will
+     * return null.
+     * <p>
+     * Services that want to set this flag have to declare the capability
+     * to retrieve window content in their meta-data by setting the attribute
+     * {@link android.R.attr#canRetrieveWindowContent canRetrieveWindowContent} to
+     * true, otherwise this flag will be ignored. For how to declare the meta-data
+     * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
+     * </p>
+     * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent
+     */
+    public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040;
+
+    /**
      * The event types an {@link AccessibilityService} is interested in.
      * <p>
      *   <strong>Can be dynamically set at runtime.</strong>
@@ -302,6 +323,15 @@
      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED
      * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED
      * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED
+     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_START
+     * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_END
+     * @see android.view.accessibility.AccessibilityEvent#TYPE_ANNOUNCEMENT
+     * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_START
+     * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_END
+     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED
+     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
+     * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY
+     * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED
      */
     public int eventTypes;
 
@@ -354,6 +384,7 @@
      * @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY
      * @see #FLAG_REQUEST_FILTER_KEY_EVENTS
      * @see #FLAG_REPORT_VIEW_IDS
+     * @see #FLAG_RETRIEVE_INTERACTIVE_WINDOWS
      */
     public int flags;
 
@@ -449,7 +480,7 @@
                     com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType,
                     0);
             notificationTimeout = asAttributes.getInt(
-                    com.android.internal.R.styleable.AccessibilityService_notificationTimeout, 
+                    com.android.internal.R.styleable.AccessibilityService_notificationTimeout,
                     0);
             flags = asAttributes.getInt(
                     com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0);
@@ -861,6 +892,8 @@
                 return "FLAG_REPORT_VIEW_IDS";
             case FLAG_REQUEST_FILTER_KEY_EVENTS:
                 return "FLAG_REQUEST_FILTER_KEY_EVENTS";
+            case FLAG_RETRIEVE_INTERACTIVE_WINDOWS:
+                return "FLAG_RETRIEVE_INTERACTIVE_WINDOWS";
             default:
                 return null;
         }
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index c5e3d43a..edd8727 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -18,6 +18,7 @@
 
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.KeyEvent;
 
 /**
@@ -35,7 +36,9 @@
 
     void onGesture(int gesture);
 
-    void clearAccessibilityNodeInfoCache();
+    void clearAccessibilityCache();
 
     void onKeyEvent(in KeyEvent event, int sequence);
+
+    void onWindowsChanged(in int[] changedWindowIds);
 }
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 3df06b5..5f7a17d 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -21,6 +21,7 @@
 import android.view.MagnificationSpec;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+import android.view.accessibility.AccessibilityWindowInfo;
 
 /**
  * Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
@@ -53,6 +54,10 @@
         int action, in Bundle arguments, int interactionId,
         IAccessibilityInteractionConnectionCallback callback, long threadId);
 
+    AccessibilityWindowInfo getWindow(int windowId);
+
+    List<AccessibilityWindowInfo> getWindows();
+
     AccessibilityServiceInfo getServiceInfo();
 
     boolean performGlobalAction(int action);
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 7880f39..5338dd0 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -1124,28 +1124,27 @@
         if (!mStartedDelay) {
             mStartedDelay = true;
             mDelayStartTime = currentTime;
-        } else {
-            if (mPaused) {
-                if (mPauseTime < 0) {
-                    mPauseTime = currentTime;
-                }
-                return false;
-            } else if (mResumed) {
-                mResumed = false;
-                if (mPauseTime > 0) {
-                    // Offset by the duration that the animation was paused
-                    mDelayStartTime += (currentTime - mPauseTime);
-                }
+        }
+        if (mPaused) {
+            if (mPauseTime < 0) {
+                mPauseTime = currentTime;
             }
-            long deltaTime = currentTime - mDelayStartTime;
-            if (deltaTime > mStartDelay) {
-                // startDelay ended - start the anim and record the
-                // mStartTime appropriately
-                mStartTime = currentTime - (deltaTime - mStartDelay);
-                mPlayingState = RUNNING;
-                return true;
+            return false;
+        } else if (mResumed) {
+            mResumed = false;
+            if (mPauseTime > 0) {
+                // Offset by the duration that the animation was paused
+                mDelayStartTime += (currentTime - mPauseTime);
             }
         }
+        long deltaTime = currentTime - mDelayStartTime;
+        if (deltaTime > mStartDelay) {
+            // startDelay ended - start the anim and record the
+            // mStartTime appropriately
+            mStartTime = currentTime - (deltaTime - mStartDelay);
+            mPlayingState = RUNNING;
+            return true;
+        }
         return false;
     }
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 606d803..e38bbb3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4701,6 +4701,47 @@
     }
 
     /**
+     * Set a label to be used in the Recents task display. The activities of a task are traversed
+     * in order from the topmost activity to the bottommost. As soon as one activity returns a
+     * non-null Recents label the traversal is ended and that value will be used in
+     * {@link ActivityManager.RecentTaskInfo#activityLabel}
+     *
+     * @see ActivityManager#getRecentTasks
+     *
+     * @param recentsLabel The label to use in the RecentTaskInfo.
+     */
+    public void setRecentsLabel(CharSequence recentsLabel) {
+        try {
+            ActivityManagerNative.getDefault().setRecentsLabel(mToken, recentsLabel);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Set an icon to be used in the Recents task display. The activities of a task are traversed
+     * in order from the topmost activity to the bottommost. As soon as one activity returns a
+     * non-null Recents icon the traversal is ended and that value will be used in
+     * {@link ActivityManager.RecentTaskInfo#activityIcon}.
+     *
+     * @see ActivityManager#getRecentTasks
+     *
+     * @param recentsIcon The Bitmap to use in the RecentTaskInfo.
+     */
+    public void setRecentsIcon(Bitmap recentsIcon) {
+        final Bitmap scaledIcon;
+        if (recentsIcon != null) {
+            final int size = ActivityManager.getLauncherLargeIconSizeInner(this);
+            scaledIcon = Bitmap.createScaledBitmap(recentsIcon, size, size, true);
+        } else {
+            scaledIcon = null;
+        }
+        try {
+            ActivityManagerNative.getDefault().setRecentsIcon(mToken, scaledIcon);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
      * Sets the visibility of the progress bar in the title.
      * <p>
      * In order for the progress bar to be shown, the feature must be requested
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 0787ef1..d386eff 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -510,11 +510,23 @@
         public int stackId;
 
         /**
-         * The id the of the user the task was running as.
+         * The id of the user the task was running as.
          * @hide
          */
         public int userId;
 
+        /**
+         * The label of the highest activity in the task stack to have set a label
+         * {@link Activity#setRecentsLabel}.
+         */
+        public CharSequence activityLabel;
+
+        /**
+         * The Bitmap icon of the highest activity in the task stack to set a Bitmap using
+         * {@link Activity#setRecentsIcon}.
+         */
+        public Bitmap activityIcon;
+
         public RecentTaskInfo() {
         }
 
@@ -536,6 +548,14 @@
             ComponentName.writeToParcel(origActivity, dest);
             TextUtils.writeToParcel(description, dest,
                     Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            TextUtils.writeToParcel(activityLabel, dest,
+                    Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            if (activityIcon == null) {
+                dest.writeInt(0);
+            } else {
+                dest.writeInt(1);
+                activityIcon.writeToParcel(dest, 0);
+            }
             dest.writeInt(stackId);
             dest.writeInt(userId);
         }
@@ -550,6 +570,8 @@
             }
             origActivity = ComponentName.readFromParcel(source);
             description = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            activityLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            activityIcon = source.readInt() > 0 ? Bitmap.CREATOR.createFromParcel(source) : null;
             stackId = source.readInt();
             userId = source.readInt();
         }
@@ -1970,7 +1992,11 @@
      * @return dimensions of square icons in terms of pixels
      */
     public int getLauncherLargeIconSize() {
-        final Resources res = mContext.getResources();
+        return getLauncherLargeIconSizeInner(mContext);
+    }
+
+    static int getLauncherLargeIconSizeInner(Context context) {
+        final Resources res = context.getResources();
         final int size = res.getDimensionPixelSize(android.R.dimen.app_icon_size);
         final int sw = res.getConfiguration().smallestScreenWidthDp;
 
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 373a8a3..707a038 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2128,6 +2128,25 @@
             reply.writeInt(isInLockTaskMode ? 1 : 0);
             return true;
         }
+
+        case SET_RECENTS_LABEL_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            CharSequence recentsLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(data);
+            setRecentsLabel(token, recentsLabel);
+            reply.writeNoException();
+            return true;
+        }
+
+        case SET_RECENTS_ICON_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            Bitmap recentsIcon = data.readInt() != 0
+                    ? Bitmap.CREATOR.createFromParcel(data) : null;
+            setRecentsIcon(token, recentsIcon);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -4899,5 +4918,36 @@
         return isInLockTaskMode;
     }
 
+    public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        TextUtils.writeToParcel(recentsLabel, data, 0);
+        mRemote.transact(SET_RECENTS_LABEL_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException
+    {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        if (recentsBitmap != null) {
+            data.writeInt(1);
+            recentsBitmap.writeToParcel(data, 0);
+        } else {
+            data.writeInt(0);
+        }
+        mRemote.transact(SET_RECENTS_ICON_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 079cf7a..b616c1e 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -780,6 +780,25 @@
         }
     }
 
+    /**
+     * Set a non-persisted restriction on an audio operation at a stream-level.
+     * Restrictions are temporary additional constraints imposed on top of the persisted rules
+     * defined by {@link #setMode}.
+     *
+     * @param code The operation to restrict.
+     * @param stream The {@link android.media.AudioManager} stream type.
+     * @param mode The restriction mode (MODE_IGNORED,MODE_ERRORED) or MODE_ALLOWED to unrestrict.
+     * @param exceptionPackages Optional list of packages to exclude from the restriction.
+     * @hide
+     */
+    public void setRestriction(int code, int stream, int mode, String[] exceptionPackages) {
+        try {
+            final int uid = Binder.getCallingUid();
+            mService.setAudioRestriction(code, stream, uid, mode, exceptionPackages);
+        } catch (RemoteException e) {
+        }
+    }
+
     /** @hide */
     public void resetAllModes() {
         try {
@@ -1009,6 +1028,35 @@
     }
 
     /**
+     * Like {@link #checkOp} but at a stream-level for audio operations.
+     * @hide
+     */
+    public int checkAudioOp(int op, int stream, int uid, String packageName) {
+        try {
+            final int mode = mService.checkAudioOperation(op, stream, uid, packageName);
+            if (mode == MODE_ERRORED) {
+                throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
+            }
+            return mode;
+        } catch (RemoteException e) {
+        }
+        return MODE_IGNORED;
+    }
+
+    /**
+     * Like {@link #checkAudioOp} but instead of throwing a {@link SecurityException} it
+     * returns {@link #MODE_ERRORED}.
+     * @hide
+     */
+    public int checkAudioOpNoThrow(int op, int stream, int uid, String packageName) {
+        try {
+            return mService.checkAudioOperation(op, stream, uid, packageName);
+        } catch (RemoteException e) {
+        }
+        return MODE_IGNORED;
+    }
+
+    /**
      * Make note of an application performing an operation.  Note that you must pass
      * in both the uid and name of the application to be checked; this function will verify
      * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index cb06a42..3b56839 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -436,6 +436,12 @@
     /** @hide */
     public boolean isInLockTaskMode() throws RemoteException;
 
+    /** @hide */
+    public void setRecentsLabel(IBinder token, CharSequence recentsLabel) throws RemoteException;
+
+    /** @hide */
+    public void setRecentsIcon(IBinder token, Bitmap recentsBitmap) throws RemoteException;
+
     /*
      * Private non-Binder interfaces
      */
@@ -735,4 +741,6 @@
     int START_LOCK_TASK_BY_TOKEN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+214;
     int STOP_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+215;
     int IS_IN_LOCK_TASK_MODE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+216;
+    int SET_RECENTS_LABEL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+217;
+    int SET_RECENTS_ICON_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
 }
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index 087f83c..4ca06ed 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -76,8 +76,9 @@
      * @param callbackBinder Binder on which to indicate operation completion,
      *        passed here as a convenience to the agent.
      */
-    void doRestore(in ParcelFileDescriptor data, int appVersionCode,
-            in ParcelFileDescriptor newState, int token, IBackupManager callbackBinder);
+    void doRestore(in ParcelFileDescriptor data,
+            int appVersionCode, in ParcelFileDescriptor newState,
+            int token, IBackupManager callbackBinder);
 
     /**
      * Perform a "full" backup to the given file descriptor.  The output file is presumed
@@ -112,8 +113,15 @@
      * @param path Relative path of the file within its semantic domain.
      * @param mode Access mode of the file system entity, e.g. 0660.
      * @param mtime Last modification time of the file system entity.
+     * @param token Opaque token identifying this transaction.  This must
+     *        be echoed back to the backup service binder once the agent is
+     *        finished restoring the application based on the restore data
+     *        contents.
+     * @param callbackBinder Binder on which to indicate operation completion,
+     *        passed here as a convenience to the agent.
      */
     void doRestoreFile(in ParcelFileDescriptor data, long size,
             int type, String domain, String path, long mode, long mtime,
             int token, IBackupManager callbackBinder);
+
 }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 12a8ff6..13e74da 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -444,41 +444,82 @@
     public static final int VISIBILITY_SECRET = -1;
 
     /**
-     * @hide
-     * Notification type: incoming call (voice or video) or similar synchronous communication request.
+     * Notification category: incoming call (voice or video) or similar synchronous communication request.
      */
-    public static final String KIND_CALL = "android.call";
+    public static final String CATEGORY_CALL = "call";
 
     /**
-     * @hide
-     * Notification type: incoming direct message (SMS, instant message, etc.).
+     * Notification category: incoming direct message (SMS, instant message, etc.).
      */
-    public static final String KIND_MESSAGE = "android.message";
+    public static final String CATEGORY_MESSAGE = "msg";
 
     /**
-     * @hide
-     * Notification type: asynchronous bulk message (email).
+     * Notification category: asynchronous bulk message (email).
      */
-    public static final String KIND_EMAIL = "android.email";
+    public static final String CATEGORY_EMAIL = "email";
 
     /**
-     * @hide
-     * Notification type: calendar event.
+     * Notification category: calendar event.
      */
-    public static final String KIND_EVENT = "android.event";
+    public static final String CATEGORY_EVENT = "event";
 
     /**
-     * @hide
-     * Notification type: promotion or advertisement.
+     * Notification category: promotion or advertisement.
      */
-    public static final String KIND_PROMO = "android.promo";
+    public static final String CATEGORY_PROMO = "promo";
 
     /**
-     * @hide
-     * If this notification matches of one or more special types (see the <code>KIND_*</code>
-     * constants), add them here, best match first.
+     * Notification category: alarm or timer.
      */
-    public String[] kind;
+    public static final String CATEGORY_ALARM = "alarm";
+
+    /**
+     * Notification category: progress of a long-running background operation.
+     */
+    public static final String CATEGORY_PROGRESS = "progress";
+
+    /**
+     * Notification category: social network or sharing update.
+     */
+    public static final String CATEGORY_SOCIAL = "social";
+
+    /**
+     * Notification category: error in background operation or authentication status.
+     */
+    public static final String CATEGORY_ERROR = "err";
+
+    /**
+     * Notification category: media transport control for playback.
+     */
+    public static final String CATEGORY_TRANSPORT = "transport";
+
+    /**
+     * Notification category: system or device status update.  Reserved for system use.
+     */
+    public static final String CATEGORY_SYSTEM = "sys";
+
+    /**
+     * Notification category: indication of running background service.
+     */
+    public static final String CATEGORY_SERVICE = "service";
+
+    /**
+     * Notification category: a specific, timely recommendation for a single thing.
+     * For example, a news app might want to recommend a news story it believes the user will
+     * want to read next.
+     */
+    public static final String CATEGORY_RECOMMENDATION = "recommendation";
+
+    /**
+     * Notification category: ongoing information about device or contextual status.
+     */
+    public static final String CATEGORY_STATUS = "status";
+
+    /**
+     * One of the predefined notification categories (see the <code>CATEGORY_*</code> constants)
+     * that best describes this Notification.  May be used by the system for ranking and filtering.
+     */
+    public String category;
 
     /**
      * Additional semantic data to be carried around with this Notification.
@@ -619,6 +660,13 @@
     public static final String EXTRA_BUILDER_REMOTE_VIEWS = "android.builderRemoteViews";
 
     /**
+     * Allow certain system-generated notifications to appear before the device is provisioned.
+     * Only available to notifications coming from the android package.
+     * @hide
+     */
+    public static final String EXTRA_ALLOW_DURING_SETUP = "android.allowDuringSetup";
+
+    /**
      * Value for {@link #EXTRA_AS_HEADS_UP}.
      * @hide
      */
@@ -815,7 +863,7 @@
 
         priority = parcel.readInt();
 
-        kind = parcel.createStringArray(); // may set kind to null
+        category = parcel.readString();
 
         extras = parcel.readBundle(); // may be null
 
@@ -890,12 +938,7 @@
 
         that.priority = this.priority;
 
-        final String[] thiskind = this.kind;
-        if (thiskind != null) {
-            final int N = thiskind.length;
-            final String[] thatkind = that.kind = new String[N];
-            System.arraycopy(thiskind, 0, thatkind, 0, N);
-        }
+        that.category = this.category;
 
         if (this.extras != null) {
             try {
@@ -1044,7 +1087,7 @@
 
         parcel.writeInt(priority);
 
-        parcel.writeStringArray(kind); // ok for null
+        parcel.writeString(category);
 
         parcel.writeBundle(extras); // null ok
 
@@ -1180,16 +1223,7 @@
         sb.append(Integer.toHexString(this.defaults));
         sb.append(" flags=0x");
         sb.append(Integer.toHexString(this.flags));
-        sb.append(" kind=[");
-        if (this.kind == null) {
-            sb.append("null");
-        } else {
-            for (int i=0; i<this.kind.length; i++) {
-                if (i>0) sb.append(",");
-                sb.append(this.kind[i]);
-            }
-        }
-        sb.append("]");
+        sb.append(" category="); sb.append(this.category);
         if (actions != null) {
             sb.append(" ");
             sb.append(actions.length);
@@ -1271,7 +1305,7 @@
         private int mProgressMax;
         private int mProgress;
         private boolean mProgressIndeterminate;
-        private ArrayList<String> mKindList = new ArrayList<String>(1);
+        private String mCategory;
         private Bundle mExtras;
         private int mPriority;
         private ArrayList<Action> mActions = new ArrayList<Action>(MAX_ACTION_BUTTONS);
@@ -1679,14 +1713,12 @@
         }
 
         /**
-         * @hide
+         * Set the notification category.
          *
-         * Add a kind (category) to this notification. Optional.
-         *
-         * @see Notification#kind
+         * @see Notification#category
          */
-        public Builder addKind(String k) {
-            mKindList.add(k);
+        public Builder setCategory(String category) {
+            mCategory = category;
             return this;
         }
 
@@ -2010,12 +2042,7 @@
             if ((mDefaults & DEFAULT_LIGHTS) != 0) {
                 n.flags |= FLAG_SHOW_LIGHTS;
             }
-            if (mKindList.size() > 0) {
-                n.kind = new String[mKindList.size()];
-                mKindList.toArray(n.kind);
-            } else {
-                n.kind = null;
-            }
+            n.category = mCategory;
             n.priority = mPriority;
             if (mActions.size() > 0) {
                 n.actions = new Action[mActions.size()];
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 498fa42..354a19f 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -36,9 +36,11 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
 
 import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.TimeoutException;
 
 /**
@@ -269,10 +271,10 @@
      * @param action The action to perform.
      * @return Whether the action was successfully performed.
      *
-     * @see AccessibilityService#GLOBAL_ACTION_BACK
-     * @see AccessibilityService#GLOBAL_ACTION_HOME
-     * @see AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
-     * @see AccessibilityService#GLOBAL_ACTION_RECENTS
+     * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_BACK
+     * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_HOME
+     * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_NOTIFICATIONS
+     * @see android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_RECENTS
      */
     public final boolean performGlobalAction(int action) {
         final IAccessibilityServiceConnection connection;
@@ -346,6 +348,33 @@
     }
 
     /**
+     * Gets the windows on the screen. This method returns only the windows
+     * that a sighted user can interact with, as opposed to all windows.
+     * For example, if there is a modal dialog shown and the user cannot touch
+     * anything behind it, then only the modal window will be reported
+     * (assuming it is the top one). For convenience the returned windows
+     * are ordered in a descending layer order, which is the windows that
+     * are higher in the Z-order are reported first.
+     * <p>
+     * <strong>Note:</strong> In order to access the windows you have to opt-in
+     * to retrieve the interactive windows by setting the
+     * {@link AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} flag.
+     * </p>
+     *
+     * @return The windows if there are windows such, otherwise an empty list.
+     */
+    public List<AccessibilityWindowInfo> getWindows() {
+        final int connectionId;
+        synchronized (mLock) {
+            throwIfNotConnectedLocked();
+            connectionId = mConnectionId;
+        }
+        // Calling out without a lock held.
+        return AccessibilityInteractionClient.getInstance()
+                .getWindows(connectionId);
+    }
+
+    /**
      * Gets the root {@link AccessibilityNodeInfo} in the active window.
      *
      * @return The root info.
@@ -632,7 +661,7 @@
      * potentially undesirable actions such as calling 911 or posting on public forums etc.
      *
      * @param enable whether to run in a "monkey" mode or not. Default is not.
-     * @see {@link ActivityManager#isUserAMonkey()}
+     * @see {@link android.app.ActivityManager#isUserAMonkey()}
      */
     public void setRunAsMonkey(boolean enable) {
         synchronized (mLock) {
diff --git a/core/java/android/app/backup/BackupDataOutput.java b/core/java/android/app/backup/BackupDataOutput.java
index 3a070b6..845784f 100644
--- a/core/java/android/app/backup/BackupDataOutput.java
+++ b/core/java/android/app/backup/BackupDataOutput.java
@@ -17,6 +17,7 @@
 package android.app.backup;
 
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -76,13 +77,19 @@
     /**
      * Mark the beginning of one record in the backup data stream. This must be called before
      * {@link #writeEntityData}.
-     * @param key A string key that uniquely identifies the data record within the application
+     * @param key A string key that uniquely identifies the data record within the application.
+     *    Keys whose first character is \uFF00 or higher are not valid.
      * @param dataSize The size in bytes of this record's data.  Passing a dataSize
      *    of -1 indicates that the record under this key should be deleted.
      * @return The number of bytes written to the backup stream
      * @throws IOException if the write failed
      */
     public int writeEntityHeader(String key, int dataSize) throws IOException {
+        if (key != null && key.charAt(0) >= 0xff00) {
+            if (Process.myUid() != Process.SYSTEM_UID) {
+                throw new IllegalArgumentException("Invalid key " + key);
+            }
+        }
         int result = writeEntityHeader_native(mBackupWriter, key, dataSize);
         if (result >= 0) {
             return result;
diff --git a/core/java/android/app/backup/IBackupManager.aidl b/core/java/android/app/backup/IBackupManager.aidl
index 12ee3b6..c629a2e 100644
--- a/core/java/android/app/backup/IBackupManager.aidl
+++ b/core/java/android/app/backup/IBackupManager.aidl
@@ -167,7 +167,7 @@
      *     are to be backed up.  The <code>allApps</code> parameter supersedes this.
      */
     void fullBackup(in ParcelFileDescriptor fd, boolean includeApks, boolean includeObbs,
-            boolean includeShared, boolean allApps, boolean allIncludesSystem,
+            boolean includeShared, boolean doWidgets, boolean allApps, boolean allIncludesSystem,
             in String[] packageNames);
 
     /**
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 8a89cbc..dd3a871 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -214,6 +214,12 @@
     public static final String EXTRA_CUSTOM_INFO = "customInfo";
 
     /**
+     * An intent extra attached to the {@link #ACTION_APPWIDGET_HOST_RESTORED} broadcast,
+     * indicating the integer ID of the host whose widgets have just been restored.
+     */
+    public static final String EXTRA_HOST_ID = "hostId";
+
+    /**
      * An intent extra to pass to the AppWidget picker containing a {@link java.util.List} of
      * {@link android.os.Bundle} objects to mix in to the list of AppWidgets that are
      * installed.  It will be added to the extras object on the {@link android.content.Intent}
@@ -310,6 +316,86 @@
     public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
 
     /**
+     * Sent to providers after AppWidget state related to the provider has been restored from
+     * backup. The intent contains information about how to translate AppWidget ids from the
+     * restored data to their new equivalents.
+     *
+     * <p>The intent will contain the following extras:
+     *
+     * <table>
+     *   <tr>
+     *     <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td>
+     *     <td>The set of appWidgetIds represented in a restored backup that have been successfully
+     *     incorporated into the current environment.  This may be all of the AppWidgets known
+     *     to this application, or just a subset.  Each entry in this array of appWidgetIds has
+     *     a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td>
+     *  </tr>
+     *   <tr>
+     *     <td>{@link #EXTRA_APPWIDGET_IDS}</td>
+     *     <td>The set of appWidgetIds now valid for this application.  The app should look at
+     *     its restored widget configuration and translate each appWidgetId in the
+     *     {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding
+     *     index within this array.</td>
+     *  </tr>
+     * </table>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see {@link #ACTION_APPWIDGET_HOST_RESTORED} for the corresponding host broadcast
+     */
+    public static final String ACTION_APPWIDGET_RESTORED
+            = "android.appwidget.action.APPWIDGET_RESTORED";
+
+    /**
+     * Sent to widget hosts after AppWidget state related to the host has been restored from
+     * backup. The intent contains information about how to translate AppWidget ids from the
+     * restored data to their new equivalents.  If an application maintains multiple separate
+     * widget hosts instances, it will receive this broadcast separately for each one.
+     *
+     * <p>The intent will contain the following extras:
+     *
+     * <table>
+     *   <tr>
+     *     <td>{@link #EXTRA_APPWIDGET_OLD_IDS}</td>
+     *     <td>The set of appWidgetIds represented in a restored backup that have been successfully
+     *     incorporated into the current environment.  This may be all of the AppWidgets known
+     *     to this application, or just a subset.  Each entry in this array of appWidgetIds has
+     *     a corresponding entry in the {@link #EXTRA_APPWIDGET_IDS} extra.</td>
+     *  </tr>
+     *   <tr>
+     *     <td>{@link #EXTRA_APPWIDGET_IDS}</td>
+     *     <td>The set of appWidgetIds now valid for this application.  The app should look at
+     *     its restored widget configuration and translate each appWidgetId in the
+     *     {@link #EXTRA_APPWIDGET_OLD_IDS} array to its new value found at the corresponding
+     *     index within this array.</td>
+     *  </tr>
+     *  <tr>
+     *     <td>{@link #EXTRA_HOST_ID}</td>
+     *     <td>The integer ID of the widget host instance whose state has just been restored.</td>
+     *  </tr>
+     * </table>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @see {@link #ACTION_APPWIDGET_RESTORED} for the corresponding provider broadcast
+     */
+    public static final String ACTION_APPWIDGET_HOST_RESTORED
+            = "android.appwidget.action.APPWIDGET_HOST_RESTORED";
+
+    /**
+     * An intent extra that contains multiple appWidgetIds.  These are id values as
+     * they were provided to the application during a recent restore from backup.  It is
+     * attached to the {@link #ACTION_APPWIDGET_RESTORED} broadcast intent.
+     *
+     * <p>
+     * The value will be an int array that can be retrieved like this:
+     * {@sample frameworks/base/tests/appwidgets/AppWidgetHostTest/src/com/android/tests/appwidgethost/TestAppWidgetProvider.java getExtra_EXTRA_APPWIDGET_IDS}
+     */
+    public static final String EXTRA_APPWIDGET_OLD_IDS = "appWidgetOldIds";
+
+    /**
      * Field for the manifest meta-data tag.
      *
      * @see AppWidgetProviderInfo
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
index edf142b..ab91edf 100644
--- a/core/java/android/appwidget/AppWidgetProvider.java
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -66,15 +66,13 @@
                     this.onUpdate(context, AppWidgetManager.getInstance(context), appWidgetIds);
                 }
             }
-        }
-        else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
+        } else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
             Bundle extras = intent.getExtras();
             if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
                 final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                 this.onDeleted(context, new int[] { appWidgetId });
             }
-        }
-        else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
+        } else if (AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED.equals(action)) {
             Bundle extras = intent.getExtras();
             if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)
                     && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS)) {
@@ -83,19 +81,28 @@
                 this.onAppWidgetOptionsChanged(context, AppWidgetManager.getInstance(context),
                         appWidgetId, widgetExtras);
             }
-        }
-        else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
+        } else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
             this.onEnabled(context);
-        }
-        else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
+        } else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
             this.onDisabled(context);
+        } else if (AppWidgetManager.ACTION_APPWIDGET_RESTORED.equals(action)) {
+            Bundle extras = intent.getExtras();
+            if (extras != null) {
+                int[] oldIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
+                int[] newIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+                if (oldIds != null && oldIds.length > 0) {
+                    this.onRestored(context, oldIds, newIds);
+                    this.onUpdate(context, AppWidgetManager.getInstance(context), newIds);
+                }
+            }
         }
     }
     // END_INCLUDE(onReceive)
 
     /**
-     * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} broadcast when
-     * this AppWidget provider is being asked to provide {@link android.widget.RemoteViews RemoteViews}
+     * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_UPDATE} and
+     * {@link AppWidgetManager#ACTION_APPWIDGET_RESTORED} broadcasts when this AppWidget
+     * provider is being asked to provide {@link android.widget.RemoteViews RemoteViews}
      * for a set of AppWidgets.  Override this method to implement your own AppWidget functionality.
      *
      * {@more}
@@ -123,8 +130,8 @@
      *                  running.
      * @param appWidgetManager A {@link AppWidgetManager} object you can call {@link
      *                  AppWidgetManager#updateAppWidget} on.
-     * @param appWidgetId The appWidgetId of the widget who's size changed.
-     * @param newOptions The appWidgetId of the widget who's size changed.
+     * @param appWidgetId The appWidgetId of the widget whose size changed.
+     * @param newOptions The appWidgetId of the widget whose size changed.
      *
      * @see AppWidgetManager#ACTION_APPWIDGET_OPTIONS_CHANGED
      */
@@ -181,4 +188,24 @@
      */
     public void onDisabled(Context context) {
     }
+
+    /**
+     * Called in response to the {@link AppWidgetManager#ACTION_APPWIDGET_RESTORED} broadcast
+     * when instances of this AppWidget provider have been restored from backup.  If your
+     * provider maintains any persistent data about its widget instances, override this method
+     * to remap the old AppWidgetIds to the new values and update any other app state that may
+     * be relevant.
+     *
+     * <p>This callback will be followed immediately by a call to {@link #onUpdate} so your
+     * provider can immediately generate new RemoteViews suitable for its newly-restored set
+     * of instances.
+     *
+     * {@more}
+     *
+     * @param context
+     * @param oldWidgetIds
+     * @param newWidgetIds
+     */
+    public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 38a71aa..a4374b8 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -19,7 +19,9 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.Context;
+import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -180,43 +182,6 @@
             "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
 
     /**
-     * Activity Action: Show a system activity to request BLE advertising.<br>
-     * If the device is not doing BLE advertising, this activity will start BLE advertising for the
-     * device, otherwise it will continue BLE advertising using the current
-     * {@link BluetoothAdvScanData}. <br>
-     * Note this activity will also request the user to turn on Bluetooth if it's not currently
-     * enabled.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_START_ADVERTISING =
-        "android.bluetooth.adapter.action.START_ADVERTISING";
-
-    /**
-     * Activity Action: Stop the current BLE advertising.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_STOP_ADVERTISING =
-        "android.bluetooth.adapter.action.STOP_ADVERTISING";
-
-    /**
-     * Broadcast Action: Indicate BLE Advertising is started.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED =
-        "android.bluetooth.adapter.action.ADVERTISING_STARTED";
-
-    /**
-     * Broadcast Action: Indicated BLE Advertising is stopped.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED =
-        "android.bluetooth.adapter.action.ADVERTISING_STOPPED";
-
-    /**
      * Activity Action: Show a system activity that allows the user to turn on
      * Bluetooth.
      * <p>This system activity will return once Bluetooth has completed turning
@@ -248,6 +213,22 @@
             "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
 
     /**
+     * Broadcast Action: Indicate BLE Advertising is started.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_BLUETOOTH_ADVERTISING_STARTED =
+            "android.bluetooth.adapter.action.ADVERTISING_STARTED";
+
+    /**
+     * Broadcast Action: Indicated BLE Advertising is stopped.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_BLUETOOTH_ADVERTISING_STOPPED =
+            "android.bluetooth.adapter.action.ADVERTISING_STOPPED";
+
+    /**
      * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
      * intents to request the current scan mode. Possible values are:
      * {@link #SCAN_MODE_NONE},
@@ -383,9 +364,27 @@
     /** The profile is in disconnecting state */
     public static final int STATE_DISCONNECTING = 3;
 
+    /** States for Bluetooth LE advertising */
+    /** @hide */
+    public static final int STATE_ADVERTISE_STARTING = 0;
+    /** @hide */
+    public static final int STATE_ADVERTISE_STARTED = 1;
+    /** @hide */
+    public static final int STATE_ADVERTISE_STOPPING = 2;
+    /** @hide */
+    public static final int STATE_ADVERTISE_STOPPED = 3;
+    /**
+     * Force stopping advertising without callback in case the advertising app dies.
+     * @hide
+     */
+    public static final int STATE_ADVERTISE_FORCE_STOPPING = 4;
+
     /** @hide */
     public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
 
+    /** @hide */
+    public static final int ADVERTISE_CALLBACK_SUCCESS = 0;
+
     private static final int ADDRESS_LENGTH = 17;
 
     /**
@@ -399,7 +398,9 @@
 
     private final Map<LeScanCallback, GattCallbackWrapper> mLeScanClients;
     private BluetoothAdvScanData mBluetoothAdvScanData = null;
-    private GattCallbackWrapper mAdvertisingCallback;
+    private GattCallbackWrapper mAdvertisingGattCallback;
+    private final Handler mHandler;  // Handler to post the advertise callback to run on main thread.
+    private final Object mLock = new Object();
 
     /**
      * Get a handle to the default local Bluetooth adapter.
@@ -435,6 +436,7 @@
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         mManagerService = managerService;
         mLeScanClients = new HashMap<LeScanCallback, GattCallbackWrapper>();
+        mHandler = new Handler(Looper.getMainLooper());
     }
 
     /**
@@ -474,6 +476,7 @@
 
     /**
      * Returns a {@link BluetoothAdvScanData} object representing advertising data.
+     * Data will be reset when bluetooth service is turned off.
      * @hide
      */
     public BluetoothAdvScanData getAdvScanData() {
@@ -494,19 +497,34 @@
       }
     }
 
+    /**
+     * Interface for BLE advertising callback.
+     *
+     * @hide
+     */
+    public interface AdvertiseCallback {
+        /**
+         * Callback when advertise starts.
+         * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
+         */
+        void onAdvertiseStart(int status);
+        /**
+         * Callback when advertise stops.
+         * @param status - {@link #ADVERTISE_CALLBACK_SUCCESS} for success, others for failure.
+         */
+        void onAdvertiseStop(int status);
+    }
 
     /**
      * Start BLE advertising using current {@link BluetoothAdvScanData}.
-     * An app should start advertising by requesting
-     * {@link BluetoothAdapter#ACTION_START_ADVERTISING} instead of calling this method directly.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
      *
-     * @return true if BLE avertising succeeds, false otherwise.
+     * @param callback - {@link AdvertiseCallback}
+     * @return true if BLE advertising succeeds, false otherwise.
      * @hide
      */
-    public boolean startAdvertising() {
+    public boolean startAdvertising(final AdvertiseCallback callback) {
         if (getState() != STATE_ON) return false;
-
         try {
             IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
             if (iGatt == null) {
@@ -516,18 +534,31 @@
             // Restart/reset advertising packets if advertising is in progress.
             if (isAdvertising()) {
                 // Invalid advertising callback.
-                if (mAdvertisingCallback == null || mAdvertisingCallback.mLeHandle == -1) {
+                if (mAdvertisingGattCallback == null || mAdvertisingGattCallback.mLeHandle == -1) {
                     Log.e(TAG, "failed to restart advertising, invalid callback");
                     return false;
                 }
-                iGatt.startAdvertising(mAdvertisingCallback.mLeHandle);
+                iGatt.startAdvertising(mAdvertisingGattCallback.mLeHandle);
+                // Run the callback from main thread.
+                mHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        // callback with status success.
+                        callback.onAdvertiseStart(ADVERTISE_CALLBACK_SUCCESS);
+                    }
+                });
                 return true;
             }
             UUID uuid = UUID.randomUUID();
             GattCallbackWrapper wrapper =
-                new GattCallbackWrapper(this, null, null, GattCallbackWrapper.CALLBACK_TYPE_ADV);
+                new GattCallbackWrapper(this, null, null, callback);
             iGatt.registerClient(new ParcelUuid(uuid), wrapper);
-            mAdvertisingCallback = wrapper;
+            if (!wrapper.advertiseStarted()) {
+                return false;
+            }
+            synchronized (mLock) {
+                mAdvertisingGattCallback = wrapper;
+            }
             return true;
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
@@ -537,25 +568,28 @@
 
     /**
      * Stop BLE advertising.
-     * An app should stop advertising by requesting
-     * {@link BluetoothAdapter#ACTION_STOP_ADVERTISING} instead of calling this method directly.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}
+     *
+     * @param callback - {@link AdvertiseCallback}
      * @return true if BLE advertising stops, false otherwise.
      * @hide
      */
-    public boolean stopAdvertisting() {
+    public boolean stopAdvertising(AdvertiseCallback callback) {
         try {
             IBluetoothGatt iGatt = mManagerService.getBluetoothGatt();
             if (iGatt == null) {
                 // BLE is not supported
                 return false;
             }
-            if (mAdvertisingCallback == null) {
+            if (mAdvertisingGattCallback == null) {
                 // no callback.
                 return false;
             }
-            mAdvertisingCallback.stopAdvertising();
-            mAdvertisingCallback = null;
+            // Make sure same callback is used for start and stop advertising.
+            if (callback != mAdvertisingGattCallback.mAdvertiseCallback) {
+                Log.e(TAG, "must use the same callback for star/stop advertising");
+                return false;
+            }
+            mAdvertisingGattCallback.stopAdvertising();
             return true;
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
@@ -1415,6 +1449,8 @@
                 if (VDBG) Log.d(TAG, "onBluetoothServiceDown: " + mService);
                 synchronized (mManagerCallback) {
                     mService = null;
+                    // Reset bluetooth adv scan data when Gatt service is down.
+                    mBluetoothAdvScanData = null;
                     for (IBluetoothManagerCallback cb : mProxyServiceStateCallbacks ){
                         try {
                             if (cb != null) {
@@ -1689,11 +1725,9 @@
     private static class GattCallbackWrapper extends IBluetoothGattCallback.Stub {
         private static final int LE_CALLBACK_REG_TIMEOUT = 2000;
         private static final int LE_CALLBACK_REG_WAIT_COUNT = 5;
-        private static final int CALLBACK_TYPE_SCAN = 0;
-        private static final int CALLBACK_TYPE_ADV = 1;
 
+        private final AdvertiseCallback mAdvertiseCallback;
         private final LeScanCallback mLeScanCb;
-        private int mCallbackType;
 
         // mLeHandle 0: not registered
         //           -1: scan stopped
@@ -1708,26 +1742,34 @@
             mLeScanCb = leScanCb;
             mScanFilter = uuid;
             mLeHandle = 0;
-            mCallbackType = CALLBACK_TYPE_SCAN;
+            mAdvertiseCallback = null;
         }
 
         public GattCallbackWrapper(BluetoothAdapter bluetoothAdapter, LeScanCallback leScanCb,
-            UUID[] uuid, int type) {
+            UUID[] uuid, AdvertiseCallback callback) {
           mBluetoothAdapter = new WeakReference<BluetoothAdapter>(bluetoothAdapter);
           mLeScanCb = leScanCb;
           mScanFilter = uuid;
           mLeHandle = 0;
-          mCallbackType = type;
+          mAdvertiseCallback = callback;
         }
 
         public boolean scanStarted() {
+            return waitForRegisteration(LE_CALLBACK_REG_WAIT_COUNT);
+        }
+
+        public boolean advertiseStarted() {
+            // Wait for registeration callback.
+            return waitForRegisteration(1);
+        }
+
+        private boolean waitForRegisteration(int maxWaitCount) {
             boolean started = false;
             synchronized(this) {
                 if (mLeHandle == -1) return false;
-
                 int count = 0;
                 // wait for callback registration and LE scan to start
-                while (mLeHandle == 0 && count < LE_CALLBACK_REG_WAIT_COUNT) {
+                while (mLeHandle == 0 && count < maxWaitCount) {
                     try {
                         wait(LE_CALLBACK_REG_TIMEOUT);
                     } catch (InterruptedException e) {
@@ -1751,15 +1793,12 @@
                     try {
                         IBluetoothGatt iGatt = adapter.getBluetoothManager().getBluetoothGatt();
                         iGatt.stopAdvertising();
-                        Log.d(TAG, "unregeistering client " + mLeHandle);
-                        iGatt.unregisterClient(mLeHandle);
                     } catch (RemoteException e) {
-                        Log.e(TAG, "Failed to stop advertising and unregister" + e);
+                        Log.e(TAG, "Failed to stop advertising" + e);
                     }
                 } else {
                     Log.e(TAG, "stopAdvertising, BluetoothAdapter is null");
                 }
-                mLeHandle = -1;
                 notifyAll();
             }
         }
@@ -1805,7 +1844,7 @@
                         BluetoothAdapter adapter = mBluetoothAdapter.get();
                         if (adapter != null) {
                             iGatt = adapter.getBluetoothManager().getBluetoothGatt();
-                            if (mCallbackType == CALLBACK_TYPE_ADV) {
+                            if (mAdvertiseCallback != null) {
                                 iGatt.startAdvertising(mLeHandle);
                             } else {
                               if (mScanFilter == null) {
@@ -1855,7 +1894,7 @@
          * @hide
          */
         public void onScanResult(String address, int rssi, byte[] advData) {
-            if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+            if (VDBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
 
             // Check null in case the scan has been stopped
             synchronized(this) {
@@ -1944,9 +1983,33 @@
             // no op
         }
 
-        public void onListen(int status) {
-            // no op
+        public void onAdvertiseStateChange(int advertiseState, int status) {
+            Log.d(TAG, "on advertise call back, state: " + advertiseState + " status: " + status);
+            if (advertiseState == STATE_ADVERTISE_STARTED) {
+                mAdvertiseCallback.onAdvertiseStart(status);
+            } else {
+                synchronized (this) {
+                    if (status == ADVERTISE_CALLBACK_SUCCESS) {
+                        BluetoothAdapter adapter = mBluetoothAdapter.get();
+                        if (adapter != null) {
+                            try {
+                                IBluetoothGatt iGatt =
+                                        adapter.getBluetoothManager().getBluetoothGatt();
+                                Log.d(TAG, "unregistering client " + mLeHandle);
+                                iGatt.unregisterClient(mLeHandle);
+                                // Reset advertise app handle.
+                                mLeHandle = -1;
+                                adapter.mAdvertisingGattCallback = null;
+                            } catch (RemoteException e) {
+                                Log.e(TAG, "Failed to unregister client" + e);
+                            }
+                        } else {
+                            Log.e(TAG, "cannot unregister client, BluetoothAdapter is null");
+                        }
+                    }
+                }
+                mAdvertiseCallback.onAdvertiseStop(status);
+            }
         }
     }
-
 }
diff --git a/core/java/android/bluetooth/BluetoothAdvScanData.java b/core/java/android/bluetooth/BluetoothAdvScanData.java
index a97b0a8..df2c256 100644
--- a/core/java/android/bluetooth/BluetoothAdvScanData.java
+++ b/core/java/android/bluetooth/BluetoothAdvScanData.java
@@ -77,6 +77,7 @@
     try {
       return mBluetoothGatt.setAdvManufacturerCodeAndData(manufacturerCode, manufacturerData);
     } catch (RemoteException e) {
+      Log.e(TAG, "Unable to set manufacturer id and data.", e);
       return false;
     }
   }
@@ -92,6 +93,7 @@
     try {
       return mBluetoothGatt.setAdvServiceData(serviceData);
     } catch (RemoteException e) {
+      Log.e(TAG, "Unable to set service data.", e);
       return false;
     }
   }
@@ -103,6 +105,7 @@
     try {
       return Collections.unmodifiableList(mBluetoothGatt.getAdvServiceUuids());
     } catch (RemoteException e) {
+      Log.e(TAG, "Unable to get service uuids.", e);
       return null;
     }
   }
@@ -115,6 +118,7 @@
     try {
       return mBluetoothGatt.getAdvManufacturerData();
     } catch (RemoteException e) {
+      Log.e(TAG, "Unable to get manufacturer data.", e);
       return null;
     }
   }
@@ -127,6 +131,7 @@
     try {
       return mBluetoothGatt.getAdvServiceData();
     } catch (RemoteException e) {
+      Log.e(TAG, "Unable to get service data.", e);
       return null;
     }
   }
@@ -140,7 +145,7 @@
       try {
         mBluetoothGatt.removeAdvManufacturerCodeAndData(manufacturerCode);
       } catch (RemoteException e) {
-        Log.e(TAG, e.toString());
+        Log.e(TAG, "Unable to remove manufacturer : " + manufacturerCode, e);
       }
     }
   }
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index e3820a2..39305b0 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,8 +16,6 @@
 
 package android.bluetooth;
 
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -544,6 +542,15 @@
                     Log.w(TAG, "Unhandled exception in callback", ex);
                 }
             }
+
+            /**
+             * Advertise state change callback
+             * @hide
+             */
+            public void onAdvertiseStateChange(int state, int status) {
+                if (DBG) Log.d(TAG, "onAdvertiseStateChange() - state = "
+                        + state + " status=" + status);
+            }
         };
 
     /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
index e3563fc..7c69a06 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl
@@ -63,4 +63,5 @@
                              in int charInstId, in ParcelUuid charUuid,
                              in byte[] value);
     void onReadRemoteRssi(in String address, in int rssi, in int status);
+    oneway void onAdvertiseStateChange(in int advertiseState, in int status);
 }
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 6e2a099..35c86e7 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -45,6 +45,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 
 /**
@@ -2149,10 +2150,20 @@
         private static final String PIXEL_FORMAT_JPEG = "jpeg";
         private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb";
 
-        private HashMap<String, String> mMap;
+        /**
+         * Order matters: Keys that are {@link #set(String, String) set} later
+         * will take precedence over keys that are set earlier (if the two keys
+         * conflict with each other).
+         *
+         * <p>One example is {@link #setPreviewFpsRange(int, int)} , since it
+         * conflicts with {@link #setPreviewFrameRate(int)} whichever key is set later
+         * is the one that will take precedence.
+         * </p>
+         */
+        private final LinkedHashMap<String, String> mMap;
 
         private Parameters() {
-            mMap = new HashMap<String, String>(64);
+            mMap = new LinkedHashMap<String, String>(/*initialCapacity*/64);
         }
 
         /**
@@ -2232,7 +2243,7 @@
                 return;
             }
 
-            mMap.put(key, value);
+            put(key, value);
         }
 
         /**
@@ -2242,7 +2253,18 @@
          * @param value the int value of the parameter
          */
         public void set(String key, int value) {
-            mMap.put(key, Integer.toString(value));
+            put(key, Integer.toString(value));
+        }
+
+        private void put(String key, String value) {
+            /*
+             * Remove the key if it already exists.
+             *
+             * This way setting a new value for an already existing key will always move
+             * that key to be ordered the latest in the map.
+             */
+            mMap.remove(key);
+            mMap.put(key, value);
         }
 
         private void set(String key, List<Area> areas) {
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
new file mode 100644
index 0000000..c95431a
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2014 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.hardware.hdmi;
+
+/**
+ * Defines constants and utility methods related to HDMI-CEC protocol.
+ */
+public final class HdmiCec {
+
+    /** TV device type. */
+    public static final int DEVICE_TV = 0;
+
+    /** Recording device type. */
+    public static final int DEVICE_RECORDER = 1;
+
+    /** Device type reserved for future usage. */
+    public static final int DEVICE_RESERVED = 2;
+
+    /** Tuner device type. */
+    public static final int DEVICE_TUNER = 3;
+
+    /** Playback device type. */
+    public static final int DEVICE_PLAYBACK = 4;
+
+    /** Audio system device type. */
+    public static final int DEVICE_AUDIO_SYSTEM = 5;
+
+    // Value indicating the device is not an active source.
+    public static final int DEVICE_INACTIVE = -1;
+
+    /** Logical address for TV */
+    public static final int ADDR_TV = 0;
+
+    /** Logical address for recorder 1 */
+    public static final int ADDR_RECORDER_1 = 1;
+
+    /** Logical address for recorder 2 */
+    public static final int ADDR_RECORDER_2 = 2;
+
+    /** Logical address for tuner 1 */
+    public static final int ADDR_TUNER_1 = 3;
+
+    /** Logical address for playback 1 */
+    public static final int ADDR_PLAYBACK_1 = 4;
+
+    /** Logical address for audio system */
+    public static final int ADDR_AUDIO_SYSTEM = 5;
+
+    /** Logical address for tuner 2 */
+    public static final int ADDR_TUNER_2 = 6;
+
+    /** Logical address for tuner 3 */
+    public static final int ADDR_TUNER_3 = 7;
+
+    /** Logical address for playback 2 */
+    public static final int ADDR_PLAYBACK_2 = 8;
+
+    /** Logical address for recorder 3 */
+    public static final int ADDR_RECORDER_3 = 9;
+
+    /** Logical address for tuner 4 */
+    public static final int ADDR_TUNER_4 = 10;
+
+    /** Logical address for playback 3 */
+    public static final int ADDR_PLAYBACK_3 = 11;
+
+    /** Logical address reserved for future usage */
+    public static final int ADDR_RESERVED_1 = 12;
+
+    /** Logical address reserved for future usage */
+    public static final int ADDR_RESERVED_2 = 13;
+
+    /** Logical address for TV other than the one assigned with {@link #ADDR_TV} */
+    public static final int ADDR_FREE_USE = 14;
+
+    /** Logical address for devices to which address cannot be allocated */
+    public static final int ADDR_UNREGISTERED = 15;
+
+    /** Logical address used in the destination address field for broadcast messages */
+    public static final int ADDR_BROADCAST = 15;
+
+    /** Logical address used to indicate it is not initialized or invalid. */
+    public static final int ADDR_INVALID = -1;
+
+    // TODO: Complete the list of CEC messages definition.
+    public static final int MESSAGE_ACTIVE_SOURCE = 0x9D;
+
+    private static final int[] ADDRESS_TO_TYPE = {
+        DEVICE_TV,  // ADDR_TV
+        DEVICE_RECORDER,  // ADDR_RECORDER_1
+        DEVICE_RECORDER,  // ADDR_RECORDER_2
+        DEVICE_TUNER,  // ADDR_TUNER_1
+        DEVICE_PLAYBACK,  // ADDR_PLAYBACK_1
+        DEVICE_AUDIO_SYSTEM,  // ADDR_AUDIO_SYSTEM
+        DEVICE_TUNER,  // ADDR_TUNER_2
+        DEVICE_TUNER,  // ADDR_TUNER_3
+        DEVICE_PLAYBACK,  // ADDR_PLAYBACK_2
+        DEVICE_RECORDER,  // ADDR_RECORDER_3
+        DEVICE_TUNER,  // ADDR_TUNER_4
+        DEVICE_PLAYBACK,  // ADDR_PLAYBACK_3
+    };
+
+    private static final String[] DEFAULT_NAMES = {
+        "TV",
+        "Recorder_1",
+        "Recorder_2",
+        "Tuner_1",
+        "Playback_1",
+        "AudioSystem",
+        "Tuner_2",
+        "Tuner_3",
+        "Playback_2",
+        "Recorder_3",
+        "Tuner_4",
+        "Playback_3",
+    };
+
+    private HdmiCec() { }  // Prevents instantiation.
+
+    /**
+     * Check if the given type is valid. A valid type is one of the actual
+     * logical device types defined in the standard ({@link #DEVICE_TV},
+     * {@link #DEVICE_PLAYBACK}, {@link #DEVICE_TUNER}, {@link #DEVICE_RECORDER},
+     * and {@link #DEVICE_AUDIO_SYSTEM}).
+     *
+     * @param type device type
+     * @return true if the given type is valid
+     */
+    public static boolean isValidType(int type) {
+        return (DEVICE_TV <= type && type <= DEVICE_AUDIO_SYSTEM)
+                && type != DEVICE_RESERVED;
+    }
+
+    /**
+     * Check if the given logical address is valid. A logical address is valid
+     * if it is one allocated for an actual device which allows communication
+     * with other logical devices.
+     *
+     * @param address logical address
+     * @return true if the given address is valid
+     */
+    public static boolean isValidAddress(int address) {
+        // TODO: We leave out the address 'free use(14)' for now. Check this later
+        //       again to make sure it is a valid address for communication.
+        return (ADDR_TV <= address && address <= ADDR_PLAYBACK_3);
+    }
+
+    /**
+     * Return the device type for the given logical address.
+     *
+     * @param address logical address
+     * @return device type for the given logical address; DEVICE_INACTIVE
+     *         if the address is not valid.
+     */
+    public static int getTypeFromAddress(int address) {
+        if (isValidAddress(address)) {
+            return ADDRESS_TO_TYPE[address];
+        }
+        return DEVICE_INACTIVE;
+    }
+
+    /**
+     * Return the default device name for a logical address. This is the name
+     * by which the logical device is known to others until a name is
+     * set explicitly using HdmiCecService.setOsdName.
+     *
+     * @param address logical address
+     * @return default device name; empty string if the address is not valid
+     */
+    public static String getDefaultDeviceName(int address) {
+        if (isValidAddress(address)) {
+            return DEFAULT_NAMES[address];
+        }
+        return "";
+    }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.aidl b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl
new file mode 100644
index 0000000..6687ba4
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2014 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.hardware.hdmi;
+
+parcelable HdmiCecMessage;
diff --git a/core/java/android/hardware/hdmi/HdmiCecMessage.java b/core/java/android/hardware/hdmi/HdmiCecMessage.java
new file mode 100644
index 0000000..be94d97
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiCecMessage.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014 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.hardware.hdmi;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/**
+ * A class to encapsulate HDMI-CEC message used for the devices connected via
+ * HDMI cable to communicate with one another. A message is defined by its
+ * source and destination address, command (or opcode), and optional parameters.
+ */
+public final class HdmiCecMessage implements Parcelable {
+
+    private static final int MAX_MESSAGE_LENGTH = 16;
+
+    private final int mSource;
+    private final int mDestination;
+
+    private final int mOpcode;
+    private final byte[] mParams;
+
+    /**
+     * Constructor.
+     */
+    public HdmiCecMessage(int source, int destination, int opcode, byte[] params) {
+        mSource = source;
+        mDestination = destination;
+        mOpcode = opcode;
+        mParams = Arrays.copyOf(params, params.length);
+    }
+
+    /**
+     * Return the source address field of the message. It is the logical address
+     * of the device which generated the message.
+     *
+     * @return source address
+     */
+    public int getSource() {
+        return mSource;
+    }
+
+    /**
+     * Return the destination address field of the message. It is the logical address
+     * of the device to which the message is sent.
+     *
+     * @return destination address
+     */
+    public int getDestination() {
+        return mDestination;
+    }
+
+    /**
+     * Return the opcode field of the message. It is the type of the message that
+     * tells the destination device what to do.
+     *
+     * @return opcode
+     */
+    public int getOpcode() {
+        return mOpcode;
+    }
+
+    /**
+     * Return the parameter field of the message. The contents of parameter varies
+     * from opcode to opcode, and is used together with opcode to describe
+     * the action for the destination device to take.
+     *
+     * @return parameter
+     */
+    public byte[] getParams() {
+        return mParams;
+    }
+
+    /**
+     * Describe the kinds of special objects contained in this Parcelable's
+     * marshalled representation.
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Flatten this object in to a Parcel.
+     *
+     * @param dest The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     *        May be 0 or {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE}.
+     */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mSource);
+        dest.writeInt(mDestination);
+        dest.writeInt(mOpcode);
+        dest.writeInt(mParams.length);
+        dest.writeByteArray(mParams);
+    }
+
+    public static final Parcelable.Creator<HdmiCecMessage> CREATOR
+            = new Parcelable.Creator<HdmiCecMessage>() {
+        /**
+         * Rebuild a HdmiCecMessage previously stored with writeToParcel().
+         * @param p HdmiCecMessage object to read the Rating from
+         * @return a new HdmiCecMessage created from the data in the parcel
+         */
+        public HdmiCecMessage createFromParcel(Parcel p) {
+            int source = p.readInt();
+            int destination = p.readInt();
+            int opcode = p.readInt();
+            byte[] params = new byte[p.readInt()];
+            p.readByteArray(params);
+            return new HdmiCecMessage(source, destination, opcode, params);
+        }
+        public HdmiCecMessage[] newArray(int size) {
+            return new HdmiCecMessage[size];
+        }
+    };
+
+    @Override
+    public String toString() {
+        StringBuffer s = new StringBuffer();
+        s.append(String.format("src: %d dst: %d op: %2X params: ", mSource, mDestination, mOpcode));
+        for (byte data : mParams) {
+            s.append(String.format("%02X ", data));
+        }
+        return s.toString();
+    }
+}
+
diff --git a/core/java/android/hardware/hdmi/IHdmiCecListener.aidl b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
new file mode 100644
index 0000000..d281ce6
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 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.hardware.hdmi;
+
+import android.hardware.hdmi.HdmiCecMessage;
+
+/**
+ * Interface definition for HdmiCecService to do interprocess communcation.
+ *
+ * @hide
+ */
+oneway interface IHdmiCecListener {
+    void onMessageReceived(in HdmiCecMessage message);
+    void onCableStatusChanged(in boolean connected);
+}
diff --git a/core/java/android/hardware/hdmi/IHdmiCecService.aidl b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
new file mode 100644
index 0000000..6fefcf8
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiCecService.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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.hardware.hdmi;
+
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiCecListener;
+import android.os.IBinder;
+
+/**
+ * Binder interface that components running in the appplication process
+ * will use to enable HDMI-CEC protocol exchange with other devices.
+ *
+ * @hide
+ */
+interface IHdmiCecService {
+    IBinder allocateLogicalDevice(int type, IHdmiCecListener listener);
+    void removeServiceListener(IBinder b, IHdmiCecListener listener);
+    void setOsdName(IBinder b, String name);
+    void sendActiveSource(IBinder b);
+    void sendInactiveSource(IBinder b);
+    void sendImageViewOn(IBinder b);
+    void sendTextViewOn(IBinder b);
+    void sendGiveDevicePowerStatus(IBinder b, int address);
+    void sendMessage(IBinder b, in HdmiCecMessage message);
+}
+
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index e3a3830..0c0dfe9 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -853,13 +853,21 @@
             return true;
         }
 
+        /**
+         * @hide
+         */
         @Override
-        public void vibrate(long milliseconds) {
+        public void vibrate(int owningUid, String owningPackage, long milliseconds,
+                int streamHint) {
             vibrate(new long[] { 0, milliseconds}, -1);
         }
 
+        /**
+         * @hide
+         */
         @Override
-        public void vibrate(long[] pattern, int repeat) {
+        public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat,
+                int streamHint) {
             if (repeat >= pattern.length) {
                 throw new ArrayIndexOutOfBoundsException();
             }
@@ -870,22 +878,6 @@
             }
         }
 
-        /**
-         * @hide
-         */
-        @Override
-        public void vibrate(int owningUid, String owningPackage, long milliseconds) {
-            vibrate(milliseconds);
-        }
-
-        /**
-         * @hide
-         */
-        @Override
-        public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
-            vibrate(pattern, repeat);
-        }
-
         @Override
         public void cancel() {
             try {
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 6c61046..10b5d0b 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -183,8 +183,10 @@
         Thread dhcpThread = new Thread(new Runnable() {
             public void run() {
                 DhcpResults dhcpResults = new DhcpResults();
+                mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
                 if (!NetworkUtils.runDhcp(mIface, dhcpResults)) {
                     Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
+                    mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
                     return;
                 }
                 mLinkProperties = dhcpResults.linkProperties;
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 3c3d8ec..a470e88 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -561,6 +561,17 @@
         return false;
     }
 
+
+    public void setInternalDataEnable(boolean enabled) {
+        if (DBG) log("setInternalDataEnable: E enabled=" + enabled);
+        final AsyncChannel channel = mDataConnectionTrackerAc;
+        if (channel != null) {
+            channel.sendMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE,
+                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
+        }
+        if (VDBG) log("setInternalDataEnable: X enabled=" + enabled);
+    }
+
     @Override
     public void setUserDataEnable(boolean enabled) {
         if (DBG) log("setUserDataEnable: E enabled=" + enabled);
diff --git a/core/java/android/os/BatteryProperty.java b/core/java/android/os/BatteryProperty.java
index 346f5de..76b0dc4 100644
--- a/core/java/android/os/BatteryProperty.java
+++ b/core/java/android/os/BatteryProperty.java
@@ -29,6 +29,7 @@
     public static final int BATTERY_PROP_CHARGE_COUNTER = 1;
     public static final int BATTERY_PROP_CURRENT_NOW = 2;
     public static final int BATTERY_PROP_CURRENT_AVG = 3;
+    public static final int BATTERY_PROP_CAPACITY = 4;
 
     public int valueInt;
 
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 7db4ac2..87beb95 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -155,6 +155,7 @@
     private static final String FOREGROUND_DATA = "fg";
     private static final String WAKELOCK_DATA = "wl";
     private static final String KERNEL_WAKELOCK_DATA = "kwl";
+    private static final String WAKEUP_REASON_DATA = "wr";
     private static final String NETWORK_DATA = "nt";
     private static final String USER_ACTIVITY_DATA = "ua";
     private static final String BATTERY_DATA = "bt";
@@ -201,6 +202,25 @@
     }
 
     /**
+     * State for keeping track of long counting information.
+     */
+    public static abstract class LongCounter {
+
+        /**
+         * Returns the count associated with this Counter for the
+         * selected type of statistics.
+         *
+         * @param which one of STATS_TOTAL, STATS_LAST, or STATS_CURRENT
+         */
+        public abstract long getCountLocked(int which);
+
+        /**
+         * Temporary for debugging.
+         */
+        public abstract void logState(Printer pw, String prefix);
+    }
+
+    /**
      * State for keeping track of timing information.
      */
     public static abstract class Timer {
@@ -552,8 +572,8 @@
         // These states always appear directly in the first int token
         // of a delta change; they should be ones that change relatively
         // frequently.
-        public static final int STATE_WAKE_LOCK_FLAG = 1<<31;
-        public static final int STATE_SENSOR_ON_FLAG = 1<<30;
+        public static final int STATE_CPU_RUNNING_FLAG = 1<<31;
+        public static final int STATE_WAKE_LOCK_FLAG = 1<<30;
         public static final int STATE_GPS_ON_FLAG = 1<<29;
         public static final int STATE_WIFI_FULL_LOCK_FLAG = 1<<28;
         public static final int STATE_WIFI_SCAN_FLAG = 1<<27;
@@ -562,21 +582,24 @@
         public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
         // These are on the lower bits used for the command; if they change
         // we need to write another int of data.
-        public static final int STATE_PHONE_SCANNING_FLAG = 1<<23;
+        public static final int STATE_SENSOR_ON_FLAG = 1<<23;
         public static final int STATE_AUDIO_ON_FLAG = 1<<22;
-        public static final int STATE_VIDEO_ON_FLAG = 1<<21;
+        public static final int STATE_PHONE_SCANNING_FLAG = 1<<21;
         public static final int STATE_SCREEN_ON_FLAG = 1<<20;
         public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
         public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
         public static final int STATE_WIFI_ON_FLAG = 1<<17;
         public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16;
-        
+
         public static final int MOST_INTERESTING_STATES =
             STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
             | STATE_GPS_ON_FLAG | STATE_PHONE_IN_CALL_FLAG;
 
         public int states;
 
+        public static final int STATE2_VIDEO_ON_FLAG = 1<<0;
+        public int states2;
+
         // The wake lock that was acquired at this point.
         public HistoryTag wakelockTag;
 
@@ -651,6 +674,7 @@
                     | ((((int)batteryVoltage)<<16)&0xffff0000);
             dest.writeInt(bat);
             dest.writeInt(states);
+            dest.writeInt(states2);
             if (wakelockTag != null) {
                 wakelockTag.writeToParcel(dest, flags);
             }
@@ -678,6 +702,7 @@
             batteryTemperature = (short)(bat2&0xffff);
             batteryVoltage = (char)((bat2>>16)&0xffff);
             states = src.readInt();
+            states2 = src.readInt();
             if ((bat&0x10000000) != 0) {
                 wakelockTag = localWakelockTag;
                 wakelockTag.readFromParcel(src);
@@ -716,6 +741,7 @@
             batteryTemperature = 0;
             batteryVoltage = 0;
             states = 0;
+            states2 = 0;
             wakelockTag = null;
             wakeReasonTag = null;
             eventCode = EVENT_NONE;
@@ -742,6 +768,7 @@
             batteryTemperature = o.batteryTemperature;
             batteryVoltage = o.batteryVoltage;
             states = o.states;
+            states2 = o.states2;
             if (o.wakelockTag != null) {
                 wakelockTag = localWakelockTag;
                 wakelockTag.setTo(o.wakelockTag);
@@ -772,6 +799,7 @@
                     && batteryTemperature == o.batteryTemperature
                     && batteryVoltage == o.batteryVoltage
                     && states == o.states
+                    && states2 == o.states2
                     && currentTime == o.currentTime;
         }
 
@@ -968,6 +996,15 @@
     public abstract int getMobileRadioActiveCount(int which);
 
     /**
+     * Returns the time in microseconds that is the difference between the mobile radio
+     * time we saw based on the elapsed timestamp when going down vs. the given time stamp
+     * from the radio.
+     *
+     * {@hide}
+     */
+    public abstract long getMobileRadioActiveAdjustedTime(int which);
+
+    /**
      * Returns the time in microseconds that the mobile network has been active
      * (in a high power state) but not being able to blame on an app.
      *
@@ -1024,9 +1061,10 @@
      * {@hide}
      */
     public abstract int getPhoneDataConnectionCount(int dataType, int which);
-    
+
     public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
             = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
         new BitDescription(HistoryItem.STATE_WAKE_LOCK_FLAG, "wake_lock", "w"),
         new BitDescription(HistoryItem.STATE_SENSOR_ON_FLAG, "sensor", "s"),
         new BitDescription(HistoryItem.STATE_GPS_ON_FLAG, "gps", "g"),
@@ -1037,7 +1075,6 @@
         new BitDescription(HistoryItem.STATE_WIFI_RUNNING_FLAG, "wifi_running", "Wr"),
         new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning", "Psc"),
         new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
-        new BitDescription(HistoryItem.STATE_VIDEO_ON_FLAG, "video", "v"),
         new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
         new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
         new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
@@ -1060,6 +1097,11 @@
                 SCREEN_BRIGHTNESS_NAMES, SCREEN_BRIGHTNESS_SHORT_NAMES),
     };
 
+    public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS
+            = new BitDescription[] {
+        new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
+    };
+
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
             "null", "proc", "fg", "top", "sync"
     };
@@ -1216,6 +1258,11 @@
     public abstract int getHighDischargeAmountSinceCharge();
 
     /**
+     * Retrieve the discharge amount over the selected discharge period <var>which</var>.
+     */
+    public abstract int getDischargeAmount(int which);
+
+    /**
      * Get the amount the battery has discharged while the screen was on,
      * since the last time power was unplugged.
      */
@@ -1287,6 +1334,8 @@
      */
     public abstract long computeRealtime(long curTime, int which);
     
+    public abstract Map<String, ? extends LongCounter> getWakeupReasonStats();
+
     public abstract Map<String, ? extends Timer> getKernelWakelockStats();
 
     /** Returns the number of different speeds that the CPU can run at */
@@ -1547,7 +1596,8 @@
                 wifiRunningTime / 1000, bluetoothOnTime / 1000,
                 mobileRxTotalBytes, mobileTxTotalBytes, wifiRxTotalBytes, wifiTxTotalBytes,
                 fullWakeLockTimeTotal, partialWakeLockTimeTotal,
-                getInputEventCount(which), getMobileRadioActiveTime(rawRealtime, which));
+                getInputEventCount(which), getMobileRadioActiveTime(rawRealtime, which),
+                getMobileRadioActiveAdjustedTime(which));
         
         // Dump screen brightness stats
         Object[] args = new Object[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -1619,16 +1669,22 @@
         }
         
         if (reqUid < 0) {
-            Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+            Map<String, ? extends Timer> kernelWakelocks = getKernelWakelockStats();
             if (kernelWakelocks.size() > 0) {
-                for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+                for (Map.Entry<String, ? extends Timer> ent : kernelWakelocks.entrySet()) {
                     sb.setLength(0);
                     printWakeLockCheckin(sb, ent.getValue(), rawRealtime, null, which, "");
-    
-                    dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(), 
+                    dumpLine(pw, 0 /* uid */, category, KERNEL_WAKELOCK_DATA, ent.getKey(),
                             sb.toString());
                 }
             }
+            Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+            if (wakeupReasons.size() > 0) {
+                for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+                    dumpLine(pw, 0 /* uid */, category, WAKEUP_REASON_DATA,
+                            "\"" + ent.getKey() + "\"", ent.getValue().getCountLocked(which));
+                }
+            }
         }
         
         BatteryStatsHelper helper = new BatteryStatsHelper(context);
@@ -2104,6 +2160,18 @@
             pw.println(sb.toString());
         }
 
+        final long mobileActiveAdjustedTime = getMobileRadioActiveAdjustedTime(which);
+        if (mobileActiveAdjustedTime != 0) {
+            sb.setLength(0);
+            sb.append(prefix);
+            sb.append("  Mobile radio active adjusted time: ");
+            formatTimeMs(sb, mobileActiveAdjustedTime / 1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(mobileActiveAdjustedTime, whichBatteryRealtime));
+            sb.append(")");
+            pw.println(sb.toString());
+        }
+
         pw.print(prefix);
                 pw.print("  Wi-Fi total received: "); pw.print(formatBytesLocked(wifiRxTotalBytes));
                 pw.print(", sent: "); pw.print(formatBytesLocked(wifiTxTotalBytes));
@@ -2339,24 +2407,49 @@
                     pw.println();
                 }
             }
-        }
 
-        if (timers.size() > 0) {
-            Collections.sort(timers, timerComparator);
-            pw.print(prefix); pw.println("  All partial wake locks:");
-            for (int i=0; i<timers.size(); i++) {
-                TimerEntry timer = timers.get(i);
-                sb.setLength(0);
-                sb.append("  Wake lock ");
-                UserHandle.formatUid(sb, timer.mId);
-                sb.append(" ");
-                sb.append(timer.mName);
-                printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
-                sb.append(" realtime");
-                pw.println(sb.toString());
+            if (timers.size() > 0) {
+                Collections.sort(timers, timerComparator);
+                pw.print(prefix); pw.println("  All partial wake locks:");
+                for (int i=0; i<timers.size(); i++) {
+                    TimerEntry timer = timers.get(i);
+                    sb.setLength(0);
+                    sb.append("  Wake lock ");
+                    UserHandle.formatUid(sb, timer.mId);
+                    sb.append(" ");
+                    sb.append(timer.mName);
+                    printWakeLock(sb, timer.mTimer, rawRealtime, null, which, ": ");
+                    sb.append(" realtime");
+                    pw.println(sb.toString());
+                }
+                timers.clear();
+                pw.println();
             }
-            timers.clear();
-            pw.println();
+
+            Map<String, ? extends LongCounter> wakeupReasons = getWakeupReasonStats();
+            if (wakeupReasons.size() > 0) {
+                pw.print(prefix); pw.println("  All wakeup reasons:");
+                final ArrayList<TimerEntry> reasons = new ArrayList<TimerEntry>();
+                for (Map.Entry<String, ? extends LongCounter> ent : wakeupReasons.entrySet()) {
+                    BatteryStats.LongCounter counter = ent.getValue();
+                    reasons.add(new TimerEntry(ent.getKey(), 0, null,
+                            ent.getValue().getCountLocked(which)));
+                }
+                Collections.sort(reasons, timerComparator);
+                for (int i=0; i<reasons.size(); i++) {
+                    TimerEntry timer = reasons.get(i);
+                    String linePrefix = ": ";
+                    sb.setLength(0);
+                    sb.append(prefix);
+                    sb.append("  Wakeup reason ");
+                    sb.append(timer.mName);
+                    sb.append(": ");
+                    formatTimeMs(sb, timer.mTime);
+                    sb.append("realtime");
+                    pw.println(sb.toString());
+                }
+                pw.println();
+            }
         }
 
         for (int iu=0; iu<NU; iu++) {
@@ -2765,6 +2858,7 @@
 
     public static class HistoryPrinter {
         int oldState = 0;
+        int oldState2 = 0;
         int oldLevel = -1;
         int oldStatus = -1;
         int oldHealth = -1;
@@ -2773,7 +2867,8 @@
         int oldVolt = -1;
         long lastTime = -1;
 
-        public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin) {
+        public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin,
+                boolean verbose) {
             if (!checkin) {
                 pw.print("  ");
                 if (now >= 0) {
@@ -2824,16 +2919,18 @@
                     if (rec.batteryLevel < 10) pw.print("00");
                     else if (rec.batteryLevel < 100) pw.print("0");
                     pw.print(rec.batteryLevel);
-                    pw.print(" ");
-                    if (rec.states < 0) ;
-                    else if (rec.states < 0x10) pw.print("0000000");
-                    else if (rec.states < 0x100) pw.print("000000");
-                    else if (rec.states < 0x1000) pw.print("00000");
-                    else if (rec.states < 0x10000) pw.print("0000");
-                    else if (rec.states < 0x100000) pw.print("000");
-                    else if (rec.states < 0x1000000) pw.print("00");
-                    else if (rec.states < 0x10000000) pw.print("0");
-                    pw.print(Integer.toHexString(rec.states));
+                    if (verbose) {
+                        pw.print(" ");
+                        if (rec.states < 0) ;
+                        else if (rec.states < 0x10) pw.print("0000000");
+                        else if (rec.states < 0x100) pw.print("000000");
+                        else if (rec.states < 0x1000) pw.print("00000");
+                        else if (rec.states < 0x10000) pw.print("0000");
+                        else if (rec.states < 0x100000) pw.print("000");
+                        else if (rec.states < 0x1000000) pw.print("00");
+                        else if (rec.states < 0x10000000) pw.print("0");
+                        pw.print(Integer.toHexString(rec.states));
+                    }
                 } else {
                     if (oldLevel != rec.batteryLevel) {
                         oldLevel = rec.batteryLevel;
@@ -2927,6 +3024,8 @@
                 }
                 printBitDescriptions(pw, oldState, rec.states, rec.wakelockTag,
                         HISTORY_STATE_DESCRIPTIONS, !checkin);
+                printBitDescriptions(pw, oldState2, rec.states2, null,
+                        HISTORY_STATE2_DESCRIPTIONS, !checkin);
                 if (rec.wakeReasonTag != null) {
                     if (checkin) {
                         pw.print(",Wr=");
@@ -3003,6 +3102,7 @@
     public static final int DUMP_CHARGED_ONLY = 1<<1;
     public static final int DUMP_HISTORY_ONLY = 1<<2;
     public static final int DUMP_INCLUDE_HISTORY = 1<<3;
+    public static final int DUMP_VERBOSE = 1<<4;
 
     /**
      * Dumps a human-readable summary of the battery statistics to the given PrintWriter.
@@ -3040,7 +3140,8 @@
                     while (getNextHistoryLocked(rec)) {
                         lastTime = rec.time;
                         if (rec.time >= histStart) {
-                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false);
+                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false,
+                                    (flags&DUMP_VERBOSE) != 0);
                         }
                     }
                     if (histStart >= 0) {
@@ -3057,7 +3158,7 @@
                     pw.println("Old battery History:");
                     HistoryPrinter hprinter = new HistoryPrinter();
                     while (getNextOldHistoryLocked(rec)) {
-                        hprinter.printNextItem(pw, rec, now, false);
+                        hprinter.printNextItem(pw, rec, now, false, (flags&DUMP_VERBOSE) != 0);
                     }
                     pw.println();
                 } finally {
@@ -3143,7 +3244,7 @@
                         if (rec.time >= histStart) {
                             pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
                             pw.print(HISTORY_DATA); pw.print(',');
-                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true);
+                            hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true, false);
                         }
                     }
                     if (histStart >= 0) {
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 54e2c0b..e96398a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -41,6 +41,7 @@
     private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
     private static final String ENV_SECONDARY_STORAGE = "SECONDARY_STORAGE";
     private static final String ENV_ANDROID_ROOT = "ANDROID_ROOT";
+    private static final String ENV_OEM_ROOT = "OEM_ROOT";
 
     /** {@hide} */
     public static final String DIR_ANDROID = "Android";
@@ -55,6 +56,7 @@
     public static final String DIRECTORY_ANDROID = DIR_ANDROID;
 
     private static final File DIR_ANDROID_ROOT = getDirectory(ENV_ANDROID_ROOT, "/system");
+    private static final File DIR_OEM_ROOT = getDirectory(ENV_OEM_ROOT, "/oem");
     private static final File DIR_MEDIA_STORAGE = getDirectory(ENV_MEDIA_STORAGE, "/data/media");
 
     private static final String CANONCIAL_EMULATED_STORAGE_TARGET = getCanonicalPathOrNull(
@@ -205,13 +207,24 @@
     }
 
     /**
-     * Gets the Android root directory.
+     * Return root of the "system" partition holding the core Android OS.
+     * Always present and mounted read-only.
      */
     public static File getRootDirectory() {
         return DIR_ANDROID_ROOT;
     }
 
     /**
+     * Return root directory of the "oem" partition holding OEM customizations,
+     * if any. If present, the partition is mounted read-only.
+     *
+     * @hide
+     */
+    public static File getOemDirectory() {
+        return DIR_OEM_ROOT;
+    }
+
+    /**
      * Gets the system directory available for secure storage.
      * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system).
      * Otherwise, it returns the unencrypted /data/system directory.
diff --git a/core/java/android/os/IVibratorService.aidl b/core/java/android/os/IVibratorService.aidl
index 456ffb1..4854bc0 100644
--- a/core/java/android/os/IVibratorService.aidl
+++ b/core/java/android/os/IVibratorService.aidl
@@ -20,8 +20,8 @@
 interface IVibratorService
 {
     boolean hasVibrator();
-    void vibrate(int uid, String packageName, long milliseconds, IBinder token);
-    void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, IBinder token);
+    void vibrate(int uid, String packageName, long milliseconds, int streamHint, IBinder token);
+    void vibratePattern(int uid, String packageName, in long[] pattern, int repeat, int streamHint, IBinder token);
     void cancelVibrate(IBinder token);
 }
 
diff --git a/core/java/android/os/NullVibrator.java b/core/java/android/os/NullVibrator.java
index af90bdb..536da32 100644
--- a/core/java/android/os/NullVibrator.java
+++ b/core/java/android/os/NullVibrator.java
@@ -36,22 +36,11 @@
         return false;
     }
 
-    @Override
-    public void vibrate(long milliseconds) {
-    }
-
-    @Override
-    public void vibrate(long[] pattern, int repeat) {
-        if (repeat >= pattern.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-    }
-
     /**
      * @hide
      */
     @Override
-    public void vibrate(int owningUid, String owningPackage, long milliseconds) {
+    public void vibrate(int owningUid, String owningPackage, long milliseconds, int streamHint) {
         vibrate(milliseconds);
     }
 
@@ -59,8 +48,11 @@
      * @hide
      */
     @Override
-    public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
-        vibrate(pattern, repeat);
+    public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat,
+            int streamHint) {
+        if (repeat >= pattern.length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
     }
 
     @Override
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 700f80d..13bc4f6 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -16,7 +16,6 @@
 
 package android.os;
 
-import android.app.ActivityThread;
 import android.content.Context;
 import android.util.Log;
 
@@ -28,18 +27,16 @@
 public class SystemVibrator extends Vibrator {
     private static final String TAG = "Vibrator";
 
-    private final String mPackageName;
     private final IVibratorService mService;
     private final Binder mToken = new Binder();
 
     public SystemVibrator() {
-        mPackageName = ActivityThread.currentPackageName();
         mService = IVibratorService.Stub.asInterface(
                 ServiceManager.getService("vibrator"));
     }
 
     public SystemVibrator(Context context) {
-        mPackageName = context.getOpPackageName();
+        super(context);
         mService = IVibratorService.Stub.asInterface(
                 ServiceManager.getService("vibrator"));
     }
@@ -57,27 +54,17 @@
         return false;
     }
 
-    @Override
-    public void vibrate(long milliseconds) {
-        vibrate(Process.myUid(), mPackageName, milliseconds);
-    }
-
-    @Override
-    public void vibrate(long[] pattern, int repeat) {
-        vibrate(Process.myUid(), mPackageName, pattern, repeat);
-    }
-
     /**
      * @hide
      */
     @Override
-    public void vibrate(int owningUid, String owningPackage, long milliseconds) {
+    public void vibrate(int owningUid, String owningPackage, long milliseconds, int streamHint) {
         if (mService == null) {
             Log.w(TAG, "Failed to vibrate; no vibrator service.");
             return;
         }
         try {
-            mService.vibrate(owningUid, owningPackage, milliseconds, mToken);
+            mService.vibrate(owningUid, owningPackage, milliseconds, streamHint, mToken);
         } catch (RemoteException e) {
             Log.w(TAG, "Failed to vibrate.", e);
         }
@@ -87,7 +74,8 @@
      * @hide
      */
     @Override
-    public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat) {
+    public void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat,
+            int streamHint) {
         if (mService == null) {
             Log.w(TAG, "Failed to vibrate; no vibrator service.");
             return;
@@ -97,7 +85,8 @@
         // anyway
         if (repeat < pattern.length) {
             try {
-                mService.vibratePattern(owningUid, owningPackage, pattern, repeat, mToken);
+                mService.vibratePattern(owningUid, owningPackage, pattern, repeat, streamHint,
+                        mToken);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed to vibrate.", e);
             }
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 5d55143..8845ba3 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -16,7 +16,9 @@
 
 package android.os;
 
+import android.app.ActivityThread;
 import android.content.Context;
+import android.media.AudioManager;
 
 /**
  * Class that operates the vibrator on the device.
@@ -28,10 +30,21 @@
  * {@link Context#getSystemService} with {@link Context#VIBRATOR_SERVICE} as the argument.
  */
 public abstract class Vibrator {
+
+    private final String mPackageName;
+
     /**
      * @hide to prevent subclassing from outside of the framework
      */
     public Vibrator() {
+        mPackageName = ActivityThread.currentPackageName();
+    }
+
+    /**
+     * @hide to prevent subclassing from outside of the framework
+     */
+    protected Vibrator(Context context) {
+        mPackageName = context.getOpPackageName();
     }
 
     /**
@@ -40,7 +53,7 @@
      * @return True if the hardware has a vibrator, else false.
      */
     public abstract boolean hasVibrator();
-    
+
     /**
      * Vibrate constantly for the specified period of time.
      * <p>This method requires the caller to hold the permission
@@ -48,7 +61,23 @@
      *
      * @param milliseconds The number of milliseconds to vibrate.
      */
-    public abstract void vibrate(long milliseconds);
+    public void vibrate(long milliseconds) {
+        vibrate(milliseconds, AudioManager.USE_DEFAULT_STREAM_TYPE);
+    }
+
+    /**
+     * Vibrate constantly for the specified period of time.
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#VIBRATE}.
+     *
+     * @param milliseconds The number of milliseconds to vibrate.
+     * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type.
+     *        For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or
+     *        {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls.
+     */
+    public void vibrate(long milliseconds, int streamHint) {
+        vibrate(Process.myUid(), mPackageName, milliseconds, streamHint);
+    }
 
     /**
      * Vibrate with a given pattern.
@@ -70,21 +99,52 @@
      * @param repeat the index into pattern at which to repeat, or -1 if
      *        you don't want to repeat.
      */
-    public abstract void vibrate(long[] pattern, int repeat);
+    public void vibrate(long[] pattern, int repeat) {
+        vibrate(pattern, repeat, AudioManager.USE_DEFAULT_STREAM_TYPE);
+    }
+
+    /**
+     * Vibrate with a given pattern.
+     *
+     * <p>
+     * Pass in an array of ints that are the durations for which to turn on or off
+     * the vibrator in milliseconds.  The first value indicates the number of milliseconds
+     * to wait before turning the vibrator on.  The next value indicates the number of milliseconds
+     * for which to keep the vibrator on before turning it off.  Subsequent values alternate
+     * between durations in milliseconds to turn the vibrator off or to turn the vibrator on.
+     * </p><p>
+     * To cause the pattern to repeat, pass the index into the pattern array at which
+     * to start the repeat, or -1 to disable repeating.
+     * </p>
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#VIBRATE}.
+     *
+     * @param pattern an array of longs of times for which to turn the vibrator on or off.
+     * @param repeat the index into pattern at which to repeat, or -1 if
+     *        you don't want to repeat.
+     * @param streamHint An {@link AudioManager} stream type corresponding to the vibration type.
+     *        For example, specify {@link AudioManager#STREAM_ALARM} for alarm vibrations or
+     *        {@link AudioManager#STREAM_RING} for vibrations associated with incoming calls.
+     */
+    public void vibrate(long[] pattern, int repeat, int streamHint) {
+        vibrate(Process.myUid(), mPackageName, pattern, repeat, streamHint);
+    }
 
     /**
      * @hide
-     * Like {@link #vibrate(long)}, but allowing the caller to specify that
+     * Like {@link #vibrate(long, int)}, but allowing the caller to specify that
      * the vibration is owned by someone else.
      */
-    public abstract void vibrate(int owningUid, String owningPackage, long milliseconds);
+    public abstract void vibrate(int owningUid, String owningPackage,
+            long milliseconds, int streamHint);
 
     /**
      * @hide
-     * Like {@link #vibrate(long[], int)}, but allowing the caller to specify that
+     * Like {@link #vibrate(long[], int, int)}, but allowing the caller to specify that
      * the vibration is owned by someone else.
      */
-    public abstract void vibrate(int owningUid, String owningPackage, long[] pattern, int repeat);
+    public abstract void vibrate(int owningUid, String owningPackage,
+            long[] pattern, int repeat, int streamHint);
 
     /**
      * Turn the vibrator off.
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index b97734e..4180860 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -694,6 +694,36 @@
                 return _result;
             }
 
+            public String getPassword() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_getPassword, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readString();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            public void clearPassword() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_clearPassword, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
             public StorageVolume[] getVolumeList() throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -846,7 +876,11 @@
 
         static final int TRANSACTION_mkdirs = IBinder.FIRST_CALL_TRANSACTION + 34;
 
-        static final int TRANSACTION_getPasswordType = IBinder.FIRST_CALL_TRANSACTION + 36;
+        static final int TRANSACTION_getPasswordType = IBinder.FIRST_CALL_TRANSACTION + 35;
+
+        static final int TRANSACTION_getPassword = IBinder.FIRST_CALL_TRANSACTION + 36;
+
+        static final int TRANSACTION_clearPassword = IBinder.FIRST_CALL_TRANSACTION + 37;
 
         /**
          * Cast an IBinder object into an IMountService interface, generating a
@@ -1208,6 +1242,19 @@
                     reply.writeInt(result);
                     return true;
                 }
+                case TRANSACTION_getPassword: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String result = getPassword();
+                    reply.writeNoException();
+                    reply.writeString(result);
+                    return true;
+                }
+                case TRANSACTION_clearPassword: {
+                    data.enforceInterface(DESCRIPTOR);
+                    clearPassword();
+                    reply.writeNoException();
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -1446,4 +1493,15 @@
      * @return PasswordType
      */
     public int getPasswordType() throws RemoteException;
+
+    /**
+     * Get password from vold
+     * @return password or empty string
+     */
+    public String getPassword() throws RemoteException;
+
+    /**
+     * Securely clear password from vold
+     */
+    public void clearPassword() throws RemoteException;
 }
diff --git a/core/java/android/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
new file mode 100644
index 0000000..719dcea
--- /dev/null
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 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.provider;
+
+import android.content.Context;
+
+import java.util.Locale;
+
+/**
+ * The Indexable data for Search.
+ *
+ * This abstract class defines the common parts for all search indexable data.
+ *
+ * @hide
+ */
+public abstract class SearchIndexableData {
+
+    /**
+     * The context for the data. Will usually allow retrieving some resources.
+     *
+     * @see Context
+     */
+    public Context context;
+
+    /**
+     * The locale for the data
+     */
+    public Locale locale;
+
+    /**
+     * Tells if the data will be included into the search results. This is application specific.
+     */
+    public boolean enabled;
+
+    /**
+     * The rank for the data. This is application specific.
+     */
+    public int rank;
+
+    /**
+     * The class name associated with the data. Generally this is a Fragment class name for
+     * referring where the data is coming from and for launching the associated Fragment for
+     * displaying the data. This is used only when the data is provided "locally".
+     *
+     * If the data is provided "externally", the relevant information come from the
+     * {@link SearchIndexableData#intentAction} and {@link SearchIndexableData#intentTargetPackage}
+     * and {@link SearchIndexableData#intentTargetClass}.
+     *
+     * @see SearchIndexableData#intentAction
+     * @see SearchIndexableData#intentTargetPackage
+     * @see SearchIndexableData#intentTargetClass
+     */
+    public String className;
+
+    /**
+     * The package name for retrieving the icon associated with the data.
+     *
+     * @see SearchIndexableData#iconResId
+     */
+    public String packageName;
+
+    /**
+     * The icon resource ID associated with the data.
+     *
+     * @see SearchIndexableData#packageName
+     */
+    public int iconResId;
+
+    /**
+     * The Intent action associated with the data. This is used when the
+     * {@link SearchIndexableData#className} is not relevant.
+     *
+     * @see SearchIndexableData#intentTargetPackage
+     * @see SearchIndexableData#intentTargetClass
+     */
+    public String intentAction;
+
+    /**
+     * The Intent target package associated with the data.
+     *
+     * @see SearchIndexableData#intentAction
+     * @see SearchIndexableData#intentTargetClass
+     */
+    public String intentTargetPackage;
+
+    /**
+     * The Intent target class associated with the data.
+     *
+     * @see SearchIndexableData#intentAction
+     * @see SearchIndexableData#intentTargetPackage
+     */
+    public String intentTargetClass;
+
+    /**
+     * Default constructor.
+     */
+    public SearchIndexableData() {
+        enabled = true;
+    }
+
+    /**
+     * Constructor with a {@link Context}.
+     *
+     * @param ctx the Context
+     */
+    public SearchIndexableData(Context ctx) {
+        context = ctx;
+        locale = Locale.getDefault();
+        enabled = true;
+    }
+}
diff --git a/core/java/android/provider/SearchIndexableResource.java b/core/java/android/provider/SearchIndexableResource.java
new file mode 100644
index 0000000..ba3bd4f
--- /dev/null
+++ b/core/java/android/provider/SearchIndexableResource.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 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.provider;
+
+import android.content.Context;
+
+/**
+ * Search Indexable Resource.
+ *
+ * This class wraps a set of reference information representing data that can be indexed from a
+ * resource which would typically be a {@link android.preference.PreferenceScreen}.
+ *
+ * xmlResId: the resource ID of a {@link android.preference.PreferenceScreen} XML file.
+ *
+ * @see SearchIndexableData
+ * @see android.preference.PreferenceScreen
+ *
+ * @hide
+ */
+public class SearchIndexableResource extends SearchIndexableData {
+
+    /**
+     * Resource ID of the associated {@link android.preference.PreferenceScreen} XML file.
+     */
+    public int xmlResId;
+
+    /**
+     * Constructor.
+     *
+     * @param rank the rank of the data.
+     * @param xmlResId the resource ID of a {@link android.preference.PreferenceScreen} XML file.
+     * @param className the class name associated with the data (generally a
+     *                  {@link android.app.Fragment}).
+     * @param iconResId the resource ID associated with the data.
+     */
+    public SearchIndexableResource(int rank, int xmlResId, String className, int iconResId) {
+        this.rank = rank;
+        this.xmlResId = xmlResId;
+        this.className = className;
+        this.iconResId = iconResId;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param context the Context associated with the data.
+     */
+    public SearchIndexableResource(Context context) {
+        super(context);
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/provider/SearchIndexablesContract.java b/core/java/android/provider/SearchIndexablesContract.java
new file mode 100644
index 0000000..b8635b8
--- /dev/null
+++ b/core/java/android/provider/SearchIndexablesContract.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2014 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.provider;
+
+import android.content.ContentResolver;
+
+/**
+ * Describe the contract for an Indexable data.
+ *
+ * @hide
+ */
+public class SearchIndexablesContract {
+
+    /**
+     * Intent action used to identify {@link SearchIndexablesProvider}
+     * instances. This is used in the {@code <intent-filter>} of a {@code <provider>}.
+     */
+    public static final String PROVIDER_INTERFACE =
+            "android.content.action.SEARCH_INDEXABLES_PROVIDER";
+
+    private static final String SETTINGS = "settings";
+
+    /**
+     * Indexable references name.
+     */
+    public static final String INDEXABLES_XML_RES = "indexables_xml_res";
+
+    /**
+     * ContentProvider path for indexable xml resources.
+     */
+    public static final String INDEXABLES_XML_RES_PATH = SETTINGS + "/" + INDEXABLES_XML_RES;
+
+    /**
+     * Indexable raw data name.
+     */
+    public static final String INDEXABLES_RAW = "indexables_raw";
+
+    /**
+     * ContentProvider path for indexable raw data.
+     */
+    public static final String INDEXABLES_RAW_PATH = SETTINGS + "/" + INDEXABLES_RAW;
+
+    /**
+     * Indexable xml resources colums.
+     */
+    public static final String[] INDEXABLES_XML_RES_COLUMNS = new String[] {
+            XmlResource.COLUMN_RANK,
+            XmlResource.COLUMN_XML_RESID,
+            XmlResource.COLUMN_CLASS_NAME,
+            XmlResource.COLUMN_ICON_RESID,
+            XmlResource.COLUMN_INTENT_ACTION,
+            XmlResource.COLUMN_INTENT_TARGET_PACKAGE,
+            XmlResource.COLUMN_INTENT_TARGET_CLASS
+    };
+
+    /**
+     * Indexable raw data colums.
+     */
+    public static final String[] INDEXABLES_RAW_COLUMNS = new String[] {
+            RawData.COLUMN_RANK,
+            RawData.COLUMN_TITLE,
+            RawData.COLUMN_SUMMARY,
+            RawData.COLUMN_KEYWORDS,
+            RawData.COLUMN_SCREEN_TITLE,
+            RawData.COLUMN_CLASS_NAME,
+            RawData.COLUMN_ICON_RESID,
+            RawData.COLUMN_INTENT_ACTION,
+            RawData.COLUMN_INTENT_TARGET_PACKAGE,
+            RawData.COLUMN_INTENT_TARGET_CLASS,
+    };
+
+    /**
+     * Constants related to a {@link SearchIndexableResource}.
+     *
+     * This is a description of
+     */
+    public static final class XmlResource extends BaseColumns {
+        private XmlResource() {
+        }
+
+        public static final String MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
+                "/" + INDEXABLES_XML_RES;
+
+        /**
+         * XML resource ID for the {@link android.preference.PreferenceScreen} to load and index.
+         */
+        public static final String COLUMN_XML_RESID = "xmlResId";
+    }
+
+    /**
+     * Constants related to a {@link SearchIndexableData}.
+     *
+     * This is the raw data that is stored into an Index. This is related to
+     * {@link android.preference.Preference} and its attributes like
+     * {@link android.preference.Preference#getTitle()},
+     * {@link android.preference.Preference#getSummary()}, etc.
+     *
+     */
+    public static final class RawData extends BaseColumns {
+        private RawData() {
+        }
+
+        public static final String MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE +
+                "/" + INDEXABLES_RAW;
+
+        /**
+         * Title's raw data.
+         */
+        public static final String COLUMN_TITLE = "title";
+
+        /**
+         * Summary's raw data.
+         */
+        public static final String COLUMN_SUMMARY = "summary";
+
+        /**
+         * Keywords' raw data.
+         */
+        public static final String COLUMN_KEYWORDS = "keywords";
+
+        /**
+         * Fragment's title associated with the raw data.
+         */
+        public static final String COLUMN_SCREEN_TITLE = "screenTitle";
+    }
+
+    /**
+     * The base columns.
+     */
+    private static class BaseColumns {
+        private BaseColumns() {
+        }
+
+        /**
+         * Rank of the data. This is an integer used for ranking the search results. This is
+         * application specific.
+         */
+        public static final String COLUMN_RANK = "rank";
+
+        /**
+         * Class name associated with the data (usually a Fragment class name).
+         */
+        public static final String COLUMN_CLASS_NAME = "className";
+
+        /**
+         * Icon resource ID for the data.
+         */
+        public static final String COLUMN_ICON_RESID = "iconResId";
+
+        /**
+         * Intent action associated with the data.
+         */
+        public static final String COLUMN_INTENT_ACTION = "intentAction";
+
+        /**
+         * Intent target package associated with the data.
+         */
+        public static final String COLUMN_INTENT_TARGET_PACKAGE = "intentTargetPackage";
+
+        /**
+         * Intent target class associated with the data.
+         */
+        public static final String COLUMN_INTENT_TARGET_CLASS = "intentTargetClass";
+    }
+}
diff --git a/core/java/android/provider/SearchIndexablesProvider.java b/core/java/android/provider/SearchIndexablesProvider.java
new file mode 100644
index 0000000..2e358e4
--- /dev/null
+++ b/core/java/android/provider/SearchIndexablesProvider.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014 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.provider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.content.pm.ProviderInfo;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Base class for a search indexable provider. Such provider offers data to be indexed either
+ * as a reference to an XML file (like a {@link android.preference.PreferenceScreen}) or either
+ * as some raw data.
+ *
+ * @see SearchIndexableResource
+ * @see SearchIndexableData
+ * @see SearchIndexablesContract
+ *
+ * To create a search indexables provider, extend this class, then implement the abstract methods,
+ * and add it to your manifest like this:
+ *
+ * <pre class="prettyprint">&lt;manifest&gt;
+ *    ...
+ *    &lt;application&gt;
+ *        ...
+ *        &lt;provider
+ *            android:name="com.example.MyIndexablesProvider"
+ *            android:authorities="com.example.myindexablesprovider"
+ *            android:exported="true"
+ *            android:grantUriPermissions="true"
+ *            android:permission="android.permission.READ_SEARCH_INDEXABLES"
+ *            &lt;intent-filter&gt;
+ *                &lt;action android:name="android.content.action.SEARCH_INDEXABLES_PROVIDER" /&gt;
+ *            &lt;/intent-filter&gt;
+ *        &lt;/provider&gt;
+ *        ...
+ *    &lt;/application&gt;
+ *&lt;/manifest&gt;</pre>
+ * <p>
+ * When defining your provider, you must protect it with
+ * {@link android.Manifest.permission#READ_SEARCH_INDEXABLES}, which is a permission only the system
+ * can obtain.
+ * </p>
+ *
+ * @hide
+ */
+public abstract class SearchIndexablesProvider extends ContentProvider {
+    private static final String TAG = "IndexablesProvider";
+
+    private String mAuthority;
+    private UriMatcher mMatcher;
+
+    private static final int MATCH_RES_CODE = 1;
+    private static final int MATCH_RAW_CODE = 2;
+
+    /**
+     * Implementation is provided by the parent class.
+     */
+    @Override
+    public void attachInfo(Context context, ProviderInfo info) {
+        mAuthority = info.authority;
+
+        mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
+        mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_XML_RES_PATH,
+                MATCH_RES_CODE);
+        mMatcher.addURI(mAuthority, SearchIndexablesContract.INDEXABLES_RAW_PATH,
+                MATCH_RAW_CODE);
+
+        // Sanity check our setup
+        if (!info.exported) {
+            throw new SecurityException("Provider must be exported");
+        }
+        if (!info.grantUriPermissions) {
+            throw new SecurityException("Provider must grantUriPermissions");
+        }
+        if (!android.Manifest.permission.READ_SEARCH_INDEXABLES.equals(info.readPermission)) {
+            throw new SecurityException("Provider must be protected by READ_SEARCH_INDEXABLES");
+        }
+
+        super.attachInfo(context, info);
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+                        String sortOrder) {
+        switch (mMatcher.match(uri)) {
+            case MATCH_RES_CODE:
+                return queryXmlResources(null);
+            case MATCH_RAW_CODE:
+                return queryRawData(null);
+            default:
+                throw new UnsupportedOperationException("Unknown Uri " + uri);
+        }
+    }
+
+    /**
+     * Returns all {@link android.provider.SearchIndexablesContract.XmlResource}.
+     *
+     * Those are usually xml resource ID to some {@link android.preference.PreferenceScreen}.
+     *
+     * @param projection list of {@link android.provider.SearchIndexablesContract.XmlResource}
+     *                   columns to put into the cursor. If {@code null} all supported columns
+     *                   should be included.
+     */
+    public abstract Cursor queryXmlResources(String[] projection);
+
+    /**
+     * Returns all {@link android.provider.SearchIndexablesContract.RawData}.
+     *
+     * Those are raw indexable data.
+     *
+     * @param projection list of {@link android.provider.SearchIndexablesContract.RawData} columns
+     *                   to put into the cursor. If {@code null} all supported columns should be
+     *                   included.
+     */
+    public abstract Cursor queryRawData(String[] projection);
+
+    @Override
+    public String getType(Uri uri) {
+        switch (mMatcher.match(uri)) {
+            case MATCH_RES_CODE:
+                return SearchIndexablesContract.XmlResource.MIME_TYPE;
+            case MATCH_RAW_CODE:
+                return SearchIndexablesContract.RawData.MIME_TYPE;
+            default:
+                throw new IllegalArgumentException("Unknown URI " + uri);
+        }
+    }
+
+    /**
+     * Implementation is provided by the parent class. Throws by default, and
+     * cannot be overriden.
+     */
+    @Override
+    public final Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException("Insert not supported");
+    }
+
+    /**
+     * Implementation is provided by the parent class. Throws by default, and
+     * cannot be overriden.
+     */
+    @Override
+    public final int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("Delete not supported");
+    }
+
+    /**
+     * Implementation is provided by the parent class. Throws by default, and
+     * cannot be overriden.
+     */
+    @Override
+    public final int update(
+            Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("Update not supported");
+    }
+}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 02152fb..327c59a 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1500,11 +1500,13 @@
 
                     try {
                         mService.setCallback(getCallerIdentity(), mCallback);
-                        String[] defaultLanguage = mService.getClientDefaultLanguage();
 
-                        mParams.putString(Engine.KEY_PARAM_LANGUAGE, defaultLanguage[0]);
-                        mParams.putString(Engine.KEY_PARAM_COUNTRY, defaultLanguage[1]);
-                        mParams.putString(Engine.KEY_PARAM_VARIANT, defaultLanguage[2]);
+                        if (mParams.getString(Engine.KEY_PARAM_LANGUAGE) == null) {
+                            String[] defaultLanguage = mService.getClientDefaultLanguage();
+                            mParams.putString(Engine.KEY_PARAM_LANGUAGE, defaultLanguage[0]);
+                            mParams.putString(Engine.KEY_PARAM_COUNTRY, defaultLanguage[1]);
+                            mParams.putString(Engine.KEY_PARAM_VARIANT, defaultLanguage[2]);
+                        }
 
                         Log.i(TAG, "Set up connection to " + mName);
                         return SUCCESS;
diff --git a/core/java/android/util/LongArray.java b/core/java/android/util/LongArray.java
index 290a89b..d5f15f0 100644
--- a/core/java/android/util/LongArray.java
+++ b/core/java/android/util/LongArray.java
@@ -111,7 +111,6 @@
     }
 
     @Override
-    @SuppressWarnings("unchecked")
     public LongArray clone() {
         LongArray clone = null;
         try {
@@ -154,7 +153,8 @@
         if (index >= mSize) {
             throw new ArrayIndexOutOfBoundsException(mSize, index);
         }
-        System.arraycopy(mValues, index, mValues, index + 1, mSize - index);
+        System.arraycopy(mValues, index + 1, mValues, index, mSize - index - 1);
+        mSize--;
     }
 
     /**
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 3859ad4..abae068 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -18,12 +18,14 @@
 
 import android.graphics.Point;
 import android.graphics.Rect;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
+import android.util.LongSparseArray;
 import android.view.View.AttachInfo;
 import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -35,8 +37,11 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Queue;
 
 /**
  * Class for managing accessibility interactions initiated from the system
@@ -47,6 +52,8 @@
  */
 final class AccessibilityInteractionController {
 
+    private static final boolean ENFORCE_NODE_TREE_CONSISTENT = Build.IS_DEBUGGABLE;
+
     private final ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
         new ArrayList<AccessibilityNodeInfo>();
 
@@ -137,7 +144,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             View root = null;
-            if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) {
+            if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                 root = mViewRootImpl.mView;
             } else {
                 root = findViewByAccessibilityId(accessibilityViewId);
@@ -209,7 +216,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             View root = null;
-            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                 root = findViewByAccessibilityId(accessibilityViewId);
             } else {
                 root = mViewRootImpl.mView;
@@ -289,7 +296,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             View root = null;
-            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                 root = findViewByAccessibilityId(accessibilityViewId);
             } else {
                 root = mViewRootImpl.mView;
@@ -297,9 +304,14 @@
             if (root != null && isShown(root)) {
                 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
                 if (provider != null) {
-                    infos = provider.findAccessibilityNodeInfosByText(text,
-                            virtualDescendantId);
-                } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) {
+                    if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                        infos = provider.findAccessibilityNodeInfosByText(text,
+                                virtualDescendantId);
+                    } else {
+                        infos = provider.findAccessibilityNodeInfosByText(text,
+                                AccessibilityNodeProvider.HOST_VIEW_ID);
+                    }
+                } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                     ArrayList<View> foundViews = mTempArrayList;
                     foundViews.clear();
                     root.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT
@@ -316,7 +328,7 @@
                                 if (provider != null) {
                                     List<AccessibilityNodeInfo> infosFromProvider =
                                         provider.findAccessibilityNodeInfosByText(text,
-                                                AccessibilityNodeInfo.UNDEFINED);
+                                                AccessibilityNodeProvider.HOST_VIEW_ID);
                                     if (infosFromProvider != null) {
                                         infos.addAll(infosFromProvider);
                                     }
@@ -391,7 +403,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             View root = null;
-            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                 root = findViewByAccessibilityId(accessibilityViewId);
             } else {
                 root = mViewRootImpl.mView;
@@ -417,7 +429,7 @@
                                 focused = AccessibilityNodeInfo.obtain(
                                         mViewRootImpl.mAccessibilityFocusedVirtualView);
                             }
-                        } else if (virtualDescendantId == View.NO_ID) {
+                        } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                             focused = host.createAccessibilityNodeInfo();
                         }
                     } break;
@@ -500,7 +512,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             View root = null;
-            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                 root = findViewByAccessibilityId(accessibilityViewId);
             } else {
                 root = mViewRootImpl.mView;
@@ -576,7 +588,7 @@
             }
             mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = flags;
             View target = null;
-            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+            if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                 target = findViewByAccessibilityId(accessibilityViewId);
             } else {
                 target = mViewRootImpl.mView;
@@ -584,9 +596,14 @@
             if (target != null && isShown(target)) {
                 AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider();
                 if (provider != null) {
-                    succeeded = provider.performAction(virtualDescendantId, action,
-                            arguments);
-                } else if (virtualDescendantId == View.NO_ID) {
+                    if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                        succeeded = provider.performAction(virtualDescendantId, action,
+                                arguments);
+                    } else {
+                        succeeded = provider.performAction(AccessibilityNodeProvider.HOST_VIEW_ID,
+                                action, arguments);
+                    }
+                } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                     succeeded = target.performAccessibilityAction(action, arguments);
                 }
             }
@@ -734,6 +751,85 @@
                     }
                 }
             }
+            if (ENFORCE_NODE_TREE_CONSISTENT) {
+                enforceNodeTreeConsistent(outInfos);
+            }
+        }
+
+        private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
+            LongSparseArray<AccessibilityNodeInfo> nodeMap =
+                    new LongSparseArray<AccessibilityNodeInfo>();
+            final int nodeCount = nodes.size();
+            for (int i = 0; i < nodeCount; i++) {
+                AccessibilityNodeInfo node = nodes.get(i);
+                nodeMap.put(node.getSourceNodeId(), node);
+            }
+
+            // If the nodes are a tree it does not matter from
+            // which node we start to search for the root.
+            AccessibilityNodeInfo root = nodeMap.valueAt(0);
+            AccessibilityNodeInfo parent = root;
+            while (parent != null) {
+                root = parent;
+                parent = nodeMap.get(parent.getParentNodeId());
+            }
+
+            // Traverse the tree and do some checks.
+            AccessibilityNodeInfo accessFocus = null;
+            AccessibilityNodeInfo inputFocus = null;
+            HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
+            Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
+            fringe.add(root);
+
+            while (!fringe.isEmpty()) {
+                AccessibilityNodeInfo current = fringe.poll();
+
+                // Check for duplicates
+                if (!seen.add(current)) {
+                    throw new IllegalStateException("Duplicate node: "
+                            + current + " in window:"
+                            + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
+                }
+
+                // Check for one accessibility focus.
+                if (current.isAccessibilityFocused()) {
+                    if (accessFocus != null) {
+                        throw new IllegalStateException("Duplicate accessibility focus:"
+                                + current
+                                + " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
+                    } else {
+                        accessFocus = current;
+                    }
+                }
+
+                // Check for one input focus.
+                if (current.isFocused()) {
+                    if (inputFocus != null) {
+                        throw new IllegalStateException("Duplicate input focus: "
+                            + current + " in window:"
+                            + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
+                    } else {
+                        inputFocus = current;
+                    }
+                }
+
+                final int childCount = current.getChildCount();
+                for (int j = 0; j < childCount; j++) {
+                    final long childId = current.getChildId(j);
+                    final AccessibilityNodeInfo child = nodeMap.get(childId);
+                    if (child != null) {
+                        fringe.add(child);
+                    }
+                }
+            }
+
+            // Check for disconnected nodes.
+            for (int j = nodeMap.size() - 1; j >= 0; j--) {
+                AccessibilityNodeInfo info = nodeMap.valueAt(j);
+                if (!seen.contains(info)) {
+                    throw new IllegalStateException("Disconnected node: " + info);
+                }
+            }
         }
 
         private void prefetchPredecessorsOfRealNode(View view,
@@ -774,7 +870,7 @@
                                 info = child.createAccessibilityNodeInfo();
                             } else {
                                 info = provider.createAccessibilityNodeInfo(
-                                        AccessibilityNodeInfo.UNDEFINED);
+                                        AccessibilityNodeProvider.HOST_VIEW_ID);
                             }
                             if (info != null) {
                                 outInfos.add(info);
@@ -814,7 +910,7 @@
                             }
                         } else {
                             AccessibilityNodeInfo info = provider.createAccessibilityNodeInfo(
-                                   AccessibilityNodeInfo.UNDEFINED);
+                                   AccessibilityNodeProvider.HOST_VIEW_ID);
                             if (info != null) {
                                 outInfos.add(info);
                                 addedChildren.put(child, info);
@@ -845,16 +941,22 @@
                 List<AccessibilityNodeInfo> outInfos) {
             long parentNodeId = root.getParentNodeId();
             int accessibilityViewId = AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
-            while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) {
+            while (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
                 if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
                     return;
                 }
                 final int virtualDescendantId =
                     AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
-                if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+                if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
                         || accessibilityViewId == providerHost.getAccessibilityViewId()) {
-                    AccessibilityNodeInfo parent = provider.createAccessibilityNodeInfo(
-                            virtualDescendantId);
+                    final AccessibilityNodeInfo parent;
+                    if (virtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                        parent = provider.createAccessibilityNodeInfo(
+                                virtualDescendantId);
+                    } else {
+                        parent= provider.createAccessibilityNodeInfo(
+                                AccessibilityNodeProvider.HOST_VIEW_ID);
+                    }
                     if (parent != null) {
                         outInfos.add(parent);
                     }
@@ -875,10 +977,15 @@
                 AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
             final int parentVirtualDescendantId =
                 AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
-            if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED
+            if (parentVirtualDescendantId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID
                     || parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
-                AccessibilityNodeInfo parent =
-                    provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
+                final AccessibilityNodeInfo parent;
+                if (parentAccessibilityViewId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
+                    parent = provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
+                } else {
+                    parent = provider.createAccessibilityNodeInfo(
+                            AccessibilityNodeProvider.HOST_VIEW_ID);
+                }
                 if (parent != null) {
                     final int childCount = parent.getChildCount();
                     for (int i = 0; i < childCount; i++) {
diff --git a/core/java/android/view/IMagnificationCallbacks.aidl b/core/java/android/view/IMagnificationCallbacks.aidl
deleted file mode 100644
index 032d073..0000000
--- a/core/java/android/view/IMagnificationCallbacks.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
-** Copyright 2012, 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.view;
-
-import android.graphics.Region;
-
-/**
- * {@hide}
- */
-oneway interface IMagnificationCallbacks {
-    void onMagnifedBoundsChanged(in Region bounds);
-    void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
-    void onRotationChanged(int rotation);
-    void onUserContextChanged();
-}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c92a104..8f542bb 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -27,7 +27,6 @@
 import android.os.Bundle;
 import android.os.IRemoteCallback;
 import android.view.IApplicationToken;
-import android.view.IMagnificationCallbacks;
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
 import android.view.IWindowSession;
@@ -197,7 +196,7 @@
     void thawRotation();
 
     /**
-     * Gets whether the rotation is frozen. 
+     * Gets whether the rotation is frozen.
      *
      * @return Whether the rotation is frozen.
      */
@@ -231,55 +230,7 @@
     void lockNow(in Bundle options);
 
     /**
-     * Gets the token for the focused window.
-     */
-    IBinder getFocusedWindowToken();
-
-    /**
-     * Sets an input filter for manipulating the input event stream.
-     */
-    void setInputFilter(in IInputFilter filter);
-
-    /**
-     * Gets the frame of a window given its token.
-     */
-    void getWindowFrame(IBinder token, out Rect outFrame);
-
-    /**
      * Device is in safe mode.
      */
     boolean isSafeModeEnabled();
-
-    /**
-     * Sets the display magnification callbacks. These callbacks notify
-     * the client for contextual changes related to display magnification.
-     *
-     * @param callbacks The magnification callbacks.
-     */
-    void setMagnificationCallbacks(IMagnificationCallbacks callbacks);
-
-    /**
-     * Sets the magnification spec to be applied to all windows that can be
-     * magnified.
-     *
-     * @param spec The current magnification spec.
-     */
-    void setMagnificationSpec(in MagnificationSpec spec);
-
-    /**
-     * Gets the magnification spec for a window given its token. If the
-     * window has a compatibility scale it is also folded in the returned
-     * magnification spec.
-     *
-     * @param windowToken The unique window token.
-     * @return The magnification spec if such or null.
-     */
-    MagnificationSpec getCompatibleMagnificationSpecForWindow(in IBinder windowToken);
-
-    /**
-     * Sets the current touch exploration state.
-     *
-     * @param enabled Whether touch exploration is enabled.
-     */
-    void setTouchExplorationEnabled(boolean enabled);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ecd73af..c5835da 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4210,7 +4210,7 @@
             out.append(" #");
             out.append(Integer.toHexString(id));
             final Resources r = mResources;
-            if (id != 0 && r != null) {
+            if (Resources.resourceHasPackage(id) && r != null) {
                 try {
                     String pkgname;
                     switch (id&0xff000000) {
@@ -5101,10 +5101,6 @@
      * @see AccessibilityDelegate
      */
     public void sendAccessibilityEvent(int eventType) {
-        // Excluded views do not send accessibility events.
-        if (!includeForAccessibility()) {
-            return;
-        }
         if (mAccessibilityDelegate != null) {
             mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);
         } else {
@@ -6901,7 +6897,7 @@
      * @attr ref android.R.styleable#View_filterTouchesWhenObscured
      */
     public void setFilterTouchesWhenObscured(boolean enabled) {
-        setFlags(enabled ? 0 : FILTER_TOUCHES_WHEN_OBSCURED,
+        setFlags(enabled ? FILTER_TOUCHES_WHEN_OBSCURED : 0,
                 FILTER_TOUCHES_WHEN_OBSCURED);
     }
 
@@ -9386,6 +9382,8 @@
                 mParent.invalidateChild(this, null);
             }
             dispatchVisibilityChanged(this, newVisibility);
+
+            notifySubtreeAccessibilityStateChangedIfNeeded();
         }
 
         if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f9b9401..22fbbd6 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -3711,7 +3711,7 @@
             childHasTransientStateChanged(child, true);
         }
 
-        if (child.isImportantForAccessibility() && child.getVisibility() != View.GONE) {
+        if (child.getVisibility() != View.GONE) {
             notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
@@ -3954,7 +3954,7 @@
 
         onViewRemoved(view);
 
-        if (view.isImportantForAccessibility() && view.getVisibility() != View.GONE) {
+        if (view.getVisibility() != View.GONE) {
             notifySubtreeAccessibilityStateChangedIfNeeded();
         }
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 185cb65..a94a671 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -659,14 +659,14 @@
         mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(MSG_FLUSH_LAYER_UPDATES));
     }
 
-    public void attachFunctor(int functor) {
+    public void attachFunctor(long functor) {
         //noinspection SimplifiableIfStatement
         if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
             mAttachInfo.mHardwareRenderer.attachFunctor(mAttachInfo, functor);
         }
     }
 
-    public void detachFunctor(int functor) {
+    public void detachFunctor(long functor) {
         mBlockResizeBuffer = true;
         if (mAttachInfo.mHardwareRenderer != null) {
             mAttachInfo.mHardwareRenderer.detachFunctor(functor);
@@ -6355,7 +6355,7 @@
         public void ensureConnection() {
             if (mAttachInfo != null) {
                 final boolean registered =
-                    mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
+                    mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
                 if (!registered) {
                     mAttachInfo.mAccessibilityWindowId =
                         mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
@@ -6366,9 +6366,9 @@
 
         public void ensureNoConnection() {
             final boolean registered =
-                mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED;
+                mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
             if (registered) {
-                mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED;
+                mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
                 mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
             }
         }
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 52f9c0b..b05225b 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -787,7 +787,7 @@
             return;
         }
 
-        mVibrator.vibrate(VIBRATE_DURATION);
+        mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
     }
 
     protected void onRemoteVolumeChanged(int streamType, int flags) {
diff --git a/core/java/android/view/WindowInfo.aidl b/core/java/android/view/WindowInfo.aidl
new file mode 100644
index 0000000..75b8fd2
--- /dev/null
+++ b/core/java/android/view/WindowInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, 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.view;
+
+parcelable WindowInfo;
diff --git a/core/java/android/view/WindowInfo.java b/core/java/android/view/WindowInfo.java
new file mode 100644
index 0000000..7f89044
--- /dev/null
+++ b/core/java/android/view/WindowInfo.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2014 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.view;
+
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Pools;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents information about a window from the
+ * window manager to another part of the system.
+ *
+ * @hide
+ */
+public class WindowInfo implements Parcelable {
+    private static final int MAX_POOL_SIZE = 10;
+
+    private static final Pools.SynchronizedPool<WindowInfo> sPool =
+            new Pools.SynchronizedPool<WindowInfo>(MAX_POOL_SIZE);
+
+    public int type;
+    public int layer;
+    public IBinder token;
+    public IBinder parentToken;
+    public boolean focused;
+    public final Rect boundsInScreen = new Rect();
+    public List<IBinder> childTokens;
+
+    private WindowInfo() {
+        /* do nothing - hide constructor */
+    }
+
+    public static WindowInfo obtain() {
+        WindowInfo window = sPool.acquire();
+        if (window == null) {
+            window = new WindowInfo();
+        }
+        return window;
+    }
+
+    public static WindowInfo obtain(WindowInfo other) {
+        WindowInfo window = obtain();
+        window.type = other.type;
+        window.layer = other.layer;
+        window.token = other.token;
+        window.parentToken = other.parentToken;
+        window.focused = other.focused;
+        window.boundsInScreen.set(other.boundsInScreen);
+
+        if (other.childTokens != null && !other.childTokens.isEmpty()) {
+            if (window.childTokens == null) {
+                window.childTokens = new ArrayList<IBinder>(other.childTokens);
+            } else {
+                window.childTokens.addAll(other.childTokens);
+            }
+        }
+
+        return window;
+    }
+
+    public void recycle() {
+        clear();
+        sPool.release(this);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(type);
+        parcel.writeInt(layer);
+        parcel.writeStrongBinder(token);
+        parcel.writeStrongBinder(parentToken);
+        parcel.writeInt(focused ? 1 : 0);
+        boundsInScreen.writeToParcel(parcel, flags);
+
+        if (childTokens != null && !childTokens.isEmpty()) {
+            parcel.writeInt(1);
+            parcel.writeBinderList(childTokens);
+        } else {
+            parcel.writeInt(0);
+        }
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("WindowInfo[");
+        builder.append("type=").append(type);
+        builder.append(", layer=").append(layer);
+        builder.append(", token=").append(token);
+        builder.append(", parent=").append(parentToken);
+        builder.append(", focused=").append(focused);
+        builder.append(", children=").append(childTokens);
+        builder.append(']');
+        return builder.toString();
+    }
+
+    private void initFromParcel(Parcel parcel) {
+        type = parcel.readInt();
+        layer = parcel.readInt();
+        token = parcel.readStrongBinder();
+        parentToken = parcel.readStrongBinder();
+        focused = (parcel.readInt() == 1);
+        boundsInScreen.readFromParcel(parcel);
+
+        final boolean hasChildren = (parcel.readInt() == 1);
+        if (hasChildren) {
+            if (childTokens == null) {
+                childTokens = new ArrayList<IBinder>();
+            }
+            parcel.readBinderList(childTokens);
+        }
+    }
+
+    private void clear() {
+        type = 0;
+        layer = 0;
+        token = null;
+        parentToken = null;
+        focused = false;
+        boundsInScreen.setEmpty();
+        if (childTokens != null) {
+            childTokens.clear();
+        }
+    }
+
+    public static final Parcelable.Creator<WindowInfo> CREATOR =
+            new Creator<WindowInfo>() {
+        @Override
+        public WindowInfo createFromParcel(Parcel parcel) {
+            WindowInfo window = obtain();
+            window.initFromParcel(parcel);
+            return window;
+        }
+
+        @Override
+        public WindowInfo[] newArray(int size) {
+            return new WindowInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index a1bd4bd..14dc356 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -16,7 +16,12 @@
 
 package android.view;
 
+import android.graphics.Rect;
+import android.graphics.Region;
 import android.hardware.display.DisplayManagerInternal;
+import android.os.IBinder;
+
+import java.util.List;
 
 /**
  * Window manager local system service interface.
@@ -24,10 +29,136 @@
  * @hide Only for use within the system server.
  */
 public abstract class WindowManagerInternal {
+
+    /**
+     * Interface to receive a callback when the windows reported for
+     * accessibility changed.
+     */
+    public interface WindowsForAccessibilityCallback {
+
+        /**
+         * Called when the windows for accessibility changed.
+         *
+         * @param windows The windows for accessibility.
+         */
+        public void onWindowsForAccessibilityChanged(List<WindowInfo> windows);
+    }
+
+    /**
+     * Callbacks for contextual changes that affect the screen magnification
+     * feature.
+     */
+    public interface MagnificationCallbacks {
+
+        /**
+         * Called when the bounds of the screen content that is magnified changed.
+         * Note that not the entire screen is magnified.
+         *
+         * @param bounds The bounds.
+         */
+        public void onMagnifedBoundsChanged(Region bounds);
+
+        /**
+         * Called when an application requests a rectangle on the screen to allow
+         * the client to apply the appropriate pan and scale.
+         *
+         * @param left The rectangle left.
+         * @param top The rectangle top.
+         * @param right The rectangle right.
+         * @param bottom The rectangle bottom.
+         */
+        public void onRectangleOnScreenRequested(int left, int top, int right, int bottom);
+
+        /**
+         * Notifies that the rotation changed.
+         *
+         * @param rotation The current rotation.
+         */
+        public void onRotationChanged(int rotation);
+
+        /**
+         * Notifies that the context of the user changed. For example, an application
+         * was started.
+         */
+        public void onUserContextChanged();
+    }
+
     /**
      * Request that the window manager call
      * {@link DisplayManagerInternal#performTraversalInTransactionFromWindowManager}
      * within a surface transaction at a later time.
      */
     public abstract void requestTraversalFromDisplayManager();
-}
\ No newline at end of file
+
+    /**
+     * Set by the accessibility layer to observe changes in the magnified region,
+     * rotation, and other window transformations related to display magnification
+     * as the window manager is responsible for doing the actual magnification
+     * and has access to the raw window data while the accessibility layer serves
+     * as a controller.
+     *
+     * @param callbacks The callbacks to invoke.
+     */
+    public abstract void setMagnificationCallbacks(MagnificationCallbacks callbacks);
+
+    /**
+     * Set by the accessibility layer to specify the magnification and panning to
+     * be applied to all windows that should be magnified.
+     *
+     * @param callbacks The callbacks to invoke.
+     *
+     * @see #setMagnificationCallbacks(MagnificationCallbacks)
+     */
+    public abstract void setMagnificationSpec(MagnificationSpec spec);
+
+    /**
+     * Gets the magnification and translation applied to a window given its token.
+     * Not all windows are magnified and the window manager policy determines which
+     * windows are magnified. The returned result also takes into account the compat
+     * scale if necessary.
+     *
+     * @param windowToken The window's token.
+     *
+     * @return The magnification spec for the window.
+     *
+     * @see #setMagnificationCallbacks(MagnificationCallbacks)
+     */
+    public abstract MagnificationSpec getCompatibleMagnificationSpecForWindow(
+            IBinder windowToken);
+
+    /**
+     * Sets a callback for observing which windows are touchable for the purposes
+     * of accessibility.
+     *
+     * @param callback The callback.
+     */
+    public abstract void setWindowsForAccessibilityCallback(
+            WindowsForAccessibilityCallback callback);
+
+    /**
+     * Sets a filter for manipulating the input event stream.
+     *
+     * @param filter The filter implementation.
+     */
+    public abstract void setInputFilter(IInputFilter filter);
+
+    /**
+     * Gets the token of the window that has input focus.
+     *
+     * @return The token.
+     */
+    public abstract IBinder getFocusedWindowToken();
+
+    /**
+     * @return Whether the keyguard is engaged.
+     */
+    public abstract boolean isKeyguardLocked();
+
+    /**
+     * Gets the frame of a window given its token.
+     *
+     * @param token The token.
+     * @param outBounds The frame to populate.
+     */
+    public abstract void getWindowFrame(IBinder token, Rect outBounds);
+}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 75656545..bd203c8 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1187,11 +1187,4 @@
      * @return True if the window is a top level one.
      */
     public boolean isTopLevelWindow(int windowType);
-
-    /**
-     * Sets the current touch exploration state.
-     *
-     * @param enabled Whether touch exploration is enabled.
-     */
-    public void setTouchExplorationEnabled(boolean enabled);
 }
diff --git a/core/java/android/view/accessibility/AccessibilityCache.java b/core/java/android/view/accessibility/AccessibilityCache.java
new file mode 100644
index 0000000..77d48e2
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityCache.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2012 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.view.accessibility;
+
+import android.os.Build;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.LongArray;
+import android.util.LongSparseArray;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Cache for AccessibilityWindowInfos and AccessibilityNodeInfos.
+ * It is updated when windows change or nodes change.
+ */
+final class AccessibilityCache {
+
+    private static final String LOG_TAG = "AccessibilityCache";
+
+    private static final boolean DEBUG = false;
+
+    private static final boolean CHECK_INTEGRITY = Build.IS_DEBUGGABLE;
+
+    private final Object mLock = new Object();
+
+    private final LongArray mTempLongArray = new LongArray();
+
+    private final SparseArray<AccessibilityWindowInfo> mWindowCache =
+            new SparseArray<AccessibilityWindowInfo>();
+
+    private final SparseArray<LongSparseArray<AccessibilityNodeInfo>> mNodeCache =
+            new SparseArray<LongSparseArray<AccessibilityNodeInfo>>();
+
+    private final SparseArray<AccessibilityWindowInfo> mTempWindowArray =
+            new SparseArray<AccessibilityWindowInfo>();
+
+    public void addWindow(AccessibilityWindowInfo window) {
+        synchronized (mLock) {
+            if (DEBUG) {
+                Log.i(LOG_TAG, "Caching window: " + window.getId());
+            }
+            mWindowCache.put(window.getId(), window);
+        }
+    }
+
+    public void removeWindows(int[] windowIds) {
+        synchronized (mLock) {
+            final int windowCount = windowIds.length;
+            for (int i = 0; i < windowCount; i++) {
+                final int windowId = windowIds[i];
+                AccessibilityWindowInfo window = mWindowCache.get(windowId);
+                if (window != null) {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "Removing window: " + windowId);
+                    }
+                    window.recycle();
+                    mWindowCache.remove(windowId);
+                }
+                clearNodesForWindowLocked(windowIds[i]);
+            }
+        }
+    }
+
+    /**
+     * Notifies the cache that the something in the UI changed. As a result
+     * the cache will either refresh some nodes or evict some nodes.
+     *
+     * @param event An event.
+     */
+    public void onAccessibilityEvent(AccessibilityEvent event) {
+        synchronized (mLock) {
+            final int eventType = event.getEventType();
+            switch (eventType) {
+                case AccessibilityEvent.TYPE_VIEW_FOCUSED:
+                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
+                case AccessibilityEvent.TYPE_VIEW_SELECTED:
+                case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
+                case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
+                    refreshCachedNodeLocked(event.getWindowId(), event.getSourceNodeId());
+                } break;
+
+                case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
+                    synchronized (mLock) {
+                        final int windowId = event.getWindowId();
+                        final long sourceId = event.getSourceNodeId();
+                        if ((event.getContentChangeTypes()
+                                & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
+                            clearSubTreeLocked(windowId, sourceId);
+                        } else {
+                            refreshCachedNodeLocked(windowId, sourceId);
+                        }
+                    }
+                } break;
+
+                case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
+                    clearSubTreeLocked(event.getWindowId(), event.getSourceNodeId());
+                } break;
+            }
+        }
+
+        if (CHECK_INTEGRITY) {
+            checkIntegrity();
+        }
+    }
+
+    private void refreshCachedNodeLocked(int windowId, long sourceId) {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "Refreshing cached node.");
+        }
+
+        LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+        if (nodes == null) {
+            return;
+        }
+        AccessibilityNodeInfo cachedInfo = nodes.get(sourceId);
+        // If the source is not in the cache - nothing to do.
+        if (cachedInfo == null) {
+            return;
+        }
+        // The node changed so we will just refresh it right now.
+        if (cachedInfo.refresh(true)) {
+            return;
+        }
+        // Weird, we could not refresh. Just evict the entire sub-tree.
+        clearSubTreeLocked(windowId, sourceId);
+    }
+
+    /**
+     * Gets a cached {@link AccessibilityNodeInfo} given the id of the hosting
+     * window and the accessibility id of the node.
+     *
+     * @param windowId The id of the window hosting the node.
+     * @param accessibilityNodeId The info accessibility node id.
+     * @return The cached {@link AccessibilityNodeInfo} or null if such not found.
+     */
+    public AccessibilityNodeInfo getNode(int windowId, long accessibilityNodeId) {
+        synchronized(mLock) {
+            LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+            if (nodes == null) {
+                return null;
+            }
+            AccessibilityNodeInfo info = nodes.get(accessibilityNodeId);
+            if (info != null) {
+                // Return a copy since the client calls to AccessibilityNodeInfo#recycle()
+                // will wipe the data of the cached info.
+                info = AccessibilityNodeInfo.obtain(info);
+            }
+            if (DEBUG) {
+                Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
+            }
+            return info;
+        }
+    }
+
+    public List<AccessibilityWindowInfo> getWindows() {
+        synchronized (mLock) {
+            final int windowCount = mWindowCache.size();
+            if (windowCount > 0) {
+                // Careful to return the windows in a decreasing layer order.
+                SparseArray<AccessibilityWindowInfo> sortedWindows = mTempWindowArray;
+                sortedWindows.clear();
+
+                for (int i = 0; i < windowCount; i++) {
+                    AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+                    sortedWindows.put(window.getLayer(), window);
+                }
+
+                List<AccessibilityWindowInfo> windows = new ArrayList<AccessibilityWindowInfo>();
+                for (int i = windowCount - 1; i >= 0; i--) {
+                    AccessibilityWindowInfo window = sortedWindows.valueAt(i);
+                    windows.add(AccessibilityWindowInfo.obtain(window));
+                }
+
+                sortedWindows.clear();
+
+                return windows;
+            }
+            return null;
+        }
+    }
+
+    public AccessibilityWindowInfo getWindow(int windowId) {
+        synchronized (mLock) {
+            AccessibilityWindowInfo window = mWindowCache.get(windowId);
+            if (window != null) {
+               return AccessibilityWindowInfo.obtain(window);
+            }
+            return null;
+        }
+    }
+
+    /**
+     * Caches an {@link AccessibilityNodeInfo}.
+     *
+     * @param info The node to cache.
+     */
+    public void add(AccessibilityNodeInfo info) {
+        synchronized(mLock) {
+            if (DEBUG) {
+                Log.i(LOG_TAG, "add(" + info + ")");
+            }
+
+            final int windowId = info.getWindowId();
+            LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+            if (nodes == null) {
+                nodes = new LongSparseArray<AccessibilityNodeInfo>();
+                mNodeCache.put(windowId, nodes);
+            }
+
+            final long sourceId = info.getSourceNodeId();
+            AccessibilityNodeInfo oldInfo = nodes.get(sourceId);
+            if (oldInfo != null) {
+                // If the added node is in the cache we have to be careful if
+                // the new one represents a source state where some of the
+                // children have been removed to remove the descendants that
+                // are no longer present.
+                final LongArray newChildrenIds = info.getChildNodeIds();
+                if (newChildrenIds != null) {
+                    // Cache the new ids as we will do some lookups.
+                    LongArray newChildNodeIds = mTempLongArray;
+                    final int newChildCount = newChildNodeIds.size();
+                    for (int i = 0; i < newChildCount; i++) {
+                        newChildNodeIds.add(newChildrenIds.get(i));
+                    }
+
+                    final int oldChildCount = oldInfo.getChildCount();
+                    for (int i = 0; i < oldChildCount; i++) {
+                        final long oldChildId = oldInfo.getChildId(i);
+                        if (newChildNodeIds.indexOf(oldChildId) < 0) {
+                            clearSubTreeLocked(windowId, oldChildId);
+                        }
+                    }
+
+                    newChildNodeIds.clear();
+                }
+
+                // Also be careful if the parent has changed since the new
+                // parent may be a predecessor of the old parent which will
+                // add cyclse to the cache.
+                final long oldParentId = oldInfo.getParentNodeId();
+                if (info.getParentNodeId() != oldParentId) {
+                    clearSubTreeLocked(windowId, oldParentId);
+                }
+           }
+
+            // Cache a copy since the client calls to AccessibilityNodeInfo#recycle()
+            // will wipe the data of the cached info.
+            AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
+            nodes.put(sourceId, clone);
+        }
+    }
+
+    /**
+     * Clears the cache.
+     */
+    public void clear() {
+        synchronized(mLock) {
+            if (DEBUG) {
+                Log.i(LOG_TAG, "clear()");
+            }
+            final int windowCount = mWindowCache.size();
+            for (int i = windowCount - 1; i >= 0; i--) {
+                AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+                window.recycle();
+                mWindowCache.removeAt(i);
+            }
+            final int nodesForWindowCount = mNodeCache.size();
+            for (int i = 0; i < nodesForWindowCount; i++) {
+                final int windowId = mNodeCache.keyAt(i);
+                clearNodesForWindowLocked(windowId);
+            }
+        }
+    }
+
+    private void clearNodesForWindowLocked(int windowId) {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "clearWindowLocked(" + windowId + ")");
+        }
+        LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+        if (nodes == null) {
+            return;
+        }
+        // Recycle the nodes before clearing the cache.
+        final int nodeCount = nodes.size();
+        for (int i = nodeCount - 1; i >= 0; i--) {
+            AccessibilityNodeInfo info = nodes.valueAt(i);
+            nodes.removeAt(i);
+            info.recycle();
+        }
+        mNodeCache.remove(windowId);
+    }
+
+    /**
+     * Clears a subtree rooted at the node with the given id that is
+     * hosted in a given window.
+     *
+     * @param windowId The id of the hosting window.
+     * @param rootNodeId The root id.
+     */
+    private void clearSubTreeLocked(int windowId, long rootNodeId) {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "Clearing cached subtree.");
+        }
+        LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.get(windowId);
+        if (nodes != null) {
+            clearSubTreeRecursiveLocked(nodes, rootNodeId);
+        }
+    }
+
+    /**
+     * Clears a subtree given a pointer to the root id and the nodes
+     * in the hosting window.
+     *
+     * @param nodes The nodes in the hosting window.
+     * @param rootNodeId The id of the root to evict.
+     */
+    private void clearSubTreeRecursiveLocked(LongSparseArray<AccessibilityNodeInfo> nodes,
+            long rootNodeId) {
+        AccessibilityNodeInfo current = nodes.get(rootNodeId);
+        if (current == null) {
+            return;
+        }
+        nodes.remove(rootNodeId);
+        final int childCount = current.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final long childNodeId = current.getChildId(i);
+            clearSubTreeRecursiveLocked(nodes, childNodeId);
+        }
+    }
+
+    /**
+     * Check the integrity of the cache which is nodes from different windows
+     * are not mixed, there is a single active window, there is a single focused
+     * window, for every window there are no duplicates nodes, all nodes for a
+     * window are connected, for every window there is a single input focused
+     * node, and for every window there is a single accessibility focused node.
+     */
+    public void checkIntegrity() {
+        synchronized (mLock) {
+            // Get the root.
+            if (mWindowCache.size() <= 0 && mNodeCache.size() == 0) {
+                return;
+            }
+
+            AccessibilityWindowInfo focusedWindow = null;
+            AccessibilityWindowInfo activeWindow = null;
+
+            final int windowCount = mWindowCache.size();
+            for (int i = 0; i < windowCount; i++) {
+                AccessibilityWindowInfo window = mWindowCache.valueAt(i);
+
+                // Check for one active window.
+                if (window.isActive()) {
+                    if (activeWindow != null) {
+                        Log.e(LOG_TAG, "Duplicate active window:" + window);
+                    } else {
+                        activeWindow = window;
+                    }
+                }
+
+                // Check for one focused window.
+                if (window.isFocused()) {
+                    if (focusedWindow != null) {
+                        Log.e(LOG_TAG, "Duplicate focused window:" + window);
+                    } else {
+                        focusedWindow = window;
+                    }
+                }
+            }
+
+            // Traverse the tree and do some checks.
+            AccessibilityNodeInfo accessFocus = null;
+            AccessibilityNodeInfo inputFocus = null;
+
+            final int nodesForWindowCount = mNodeCache.size();
+            for (int i = 0; i < nodesForWindowCount; i++) {
+                LongSparseArray<AccessibilityNodeInfo> nodes = mNodeCache.valueAt(i);
+                if (nodes.size() <= 0) {
+                    continue;
+                }
+
+                ArraySet<AccessibilityNodeInfo> seen = new ArraySet<AccessibilityNodeInfo>();
+                final int windowId = mNodeCache.keyAt(i);
+
+                final int nodeCount = nodes.size();
+                for (int j = 0; j < nodeCount; j++) {
+                    AccessibilityNodeInfo node = nodes.valueAt(j);
+
+                    // Check for duplicates
+                    if (!seen.add(node)) {
+                        Log.e(LOG_TAG, "Duplicate node: " + node
+                                + " in window:" + windowId);
+                    }
+
+                    // Check for one accessibility focus.
+                    if (node.isAccessibilityFocused()) {
+                        if (accessFocus != null) {
+                            Log.e(LOG_TAG, "Duplicate accessibility focus:" + node
+                                    + " in window:" + windowId);
+                        } else {
+                            accessFocus = node;
+                        }
+                    }
+
+                    // Check for one input focus.
+                    if (node.isFocused()) {
+                        if (inputFocus != null) {
+                            Log.e(LOG_TAG, "Duplicate input focus: " + node
+                                    + " in window:" + windowId);
+                        } else {
+                            inputFocus = node;
+                        }
+                    }
+
+                    // The node should be a child of its parent if we have the parent.
+                    AccessibilityNodeInfo nodeParent = nodes.get(node.getParentNodeId());
+                    if (nodeParent != null) {
+                        boolean childOfItsParent = false;
+                        final int childCount = nodeParent.getChildCount();
+                        for (int k = 0; k < childCount; k++) {
+                            AccessibilityNodeInfo child = nodes.get(nodeParent.getChildId(k));
+                            if (child == node) {
+                                childOfItsParent = true;
+                                break;
+                            }
+                        }
+                        if (!childOfItsParent) {
+                            Log.e(LOG_TAG, "Invalid parent-child ralation between parent: "
+                                    + nodeParent + " and child: " + node);
+                        }
+                    }
+
+                    // The node should be the parent of its child if we have the child.
+                    final int childCount = node.getChildCount();
+                    for (int k = 0; k < childCount; k++) {
+                        AccessibilityNodeInfo child = nodes.get(node.getChildId(k));
+                        if (child != null) {
+                            AccessibilityNodeInfo parent = nodes.get(child.getParentNodeId());
+                            if (parent != node) {
+                                Log.e(LOG_TAG, "Invalid child-parent ralation between child: "
+                                        + node + " and parent: " + nodeParent);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 8b91155..417e22c 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -343,6 +343,23 @@
  * view.</br>
  * </p>
  * <p>
+ * <b>Windows changed</b> - represents the event of changes in the windows shown on
+ * the screen such as a window appeared, a window disappeared, a window size changed,
+ * a window layer changed, etc.</br>
+ * <em>Type:</em> {@link #TYPE_WINDOWS_CHANGED}</br>
+ * <em>Properties:</em></br>
+ * <ul>
+ *   <li>{@link #getEventType()} - The type of the event.</li>
+ *   <li>{@link #getEventTime()} - The event time.</li>
+ * </ul>
+ * <em>Note:</em> You can retrieve the {@link AccessibilityWindowInfo} for the window
+ * source of the event via {@link AccessibilityEvent#getSource()} to get the source
+ * node on which then call {@link AccessibilityNodeInfo#getWindow()
+ * AccessibilityNodeInfo.getWindow()} to get the window. Also all windows on the screen can
+ * be retrieved by a call to {@link android.accessibilityservice.AccessibilityService#getWindows()
+ * android.accessibilityservice.AccessibilityService.getWindows()}.
+ * </p>
+ * <p>
  * <b>NOTIFICATION TYPES</b></br>
  * </p>
  * <p>
@@ -662,6 +679,11 @@
     public static final int TYPE_TOUCH_INTERACTION_END = 0x00200000;
 
     /**
+     * Represents the event change in the windows shown on the screen.
+     */
+    public static final int TYPE_WINDOWS_CHANGED = 0x00400000;
+
+    /**
      * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
      * The type of change is not defined.
      */
@@ -708,6 +730,7 @@
      * @see #TYPE_GESTURE_DETECTION_END
      * @see #TYPE_TOUCH_INTERACTION_START
      * @see #TYPE_TOUCH_INTERACTION_END
+     * @see #TYPE_WINDOWS_CHANGED
      */
     public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
 
@@ -1366,6 +1389,13 @@
                     builder.append("TYPE_TOUCH_INTERACTION_END");
                     eventTypeCount++;
                 } break;
+                case TYPE_WINDOWS_CHANGED: {
+                    if (eventTypeCount > 0) {
+                        builder.append(", ");
+                    }
+                    builder.append("TYPE_WINDOWS_CHANGED");
+                    eventTypeCount++;
+                } break;
             }
         }
         if (eventTypeCount > 1) {
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 5a55e34..4dd8dcb 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -100,14 +100,11 @@
 
     private Message mSameThreadMessage;
 
-    // The connection cache is shared between all interrogating threads.
     private static final SparseArray<IAccessibilityServiceConnection> sConnectionCache =
         new SparseArray<IAccessibilityServiceConnection>();
 
-    // The connection cache is shared between all interrogating threads since
-    // at any given time there is only one window allowing querying.
-    private static final AccessibilityNodeInfoCache sAccessibilityNodeInfoCache =
-        new AccessibilityNodeInfoCache();
+    private static final AccessibilityCache sAccessibilityCache =
+        new AccessibilityCache();
 
     /**
      * @return The client for the current thread.
@@ -166,6 +163,86 @@
     }
 
     /**
+     * Gets the info for a window.
+     *
+     * @param connectionId The id of a connection for interacting with the system.
+     * @param accessibilityWindowId A unique window id. Use
+     *     {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID}
+     *     to query the currently active window.
+     * @return The {@link AccessibilityWindowInfo}.
+     */
+    public AccessibilityWindowInfo getWindow(int connectionId, int accessibilityWindowId) {
+        try {
+            IAccessibilityServiceConnection connection = getConnection(connectionId);
+            if (connection != null) {
+                AccessibilityWindowInfo window = sAccessibilityCache.getWindow(
+                        accessibilityWindowId);
+                if (window != null) {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "Window cache hit");
+                    }
+                    return window;
+                }
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Window cache miss");
+                }
+                window = connection.getWindow(accessibilityWindowId);
+                if (window != null) {
+                    sAccessibilityCache.addWindow(window);
+                    return window;
+                }
+            } else {
+                if (DEBUG) {
+                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+                }
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while calling remote getWindow", re);
+        }
+        return null;
+    }
+
+    /**
+     * Gets the info for all windows.
+     *
+     * @param connectionId The id of a connection for interacting with the system.
+     * @return The {@link AccessibilityWindowInfo} list.
+     */
+    public List<AccessibilityWindowInfo> getWindows(int connectionId) {
+        try {
+            IAccessibilityServiceConnection connection = getConnection(connectionId);
+            if (connection != null) {
+                List<AccessibilityWindowInfo> windows = sAccessibilityCache.getWindows();
+                if (windows != null) {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "Window cache hit");
+                    }
+                    return windows;
+                }
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "Window cache miss");
+                }
+                windows = connection.getWindows();
+                if (windows != null) {
+                    final int windowCount = windows.size();
+                    for (int i = 0; i < windowCount; i++) {
+                        AccessibilityWindowInfo window = windows.get(i);
+                        sAccessibilityCache.addWindow(window);
+                    }
+                    return windows;
+                }
+            } else {
+                if (DEBUG) {
+                    Log.w(LOG_TAG, "No connection for connection id: " + connectionId);
+                }
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while calling remote getWindows", re);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
      * Finds an {@link AccessibilityNodeInfo} by accessibility id.
      *
      * @param connectionId The id of a connection for interacting with the system.
@@ -183,15 +260,26 @@
     public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
             int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache,
             int prefetchFlags) {
+        if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0
+                && (prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) == 0) {
+            throw new IllegalArgumentException("FLAG_PREFETCH_SIBLINGS"
+                + " requires FLAG_PREFETCH_PREDECESSORS");
+        }
         try {
             IAccessibilityServiceConnection connection = getConnection(connectionId);
             if (connection != null) {
                 if (!bypassCache) {
-                    AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(
-                            accessibilityNodeId);
+                    AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode(
+                            accessibilityWindowId, accessibilityNodeId);
                     if (cachedInfo != null) {
+                        if (DEBUG) {
+                            Log.i(LOG_TAG, "Node cache hit");
+                        }
                         return cachedInfo;
                     }
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "Node cache miss");
+                    }
                 }
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
                 final boolean success = connection.findAccessibilityNodeInfoByAccessibilityId(
@@ -212,10 +300,8 @@
                 }
             }
         } catch (RemoteException re) {
-            if (DEBUG) {
-                Log.w(LOG_TAG, "Error while calling remote"
-                        + " findAccessibilityNodeInfoByAccessibilityId", re);
-            }
+            Log.e(LOG_TAG, "Error while calling remote"
+                    + " findAccessibilityNodeInfoByAccessibilityId", re);
         }
         return null;
     }
@@ -259,10 +345,8 @@
                 }
             }
         } catch (RemoteException re) {
-            if (DEBUG) {
-                Log.w(LOG_TAG, "Error while calling remote"
-                        + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
-            }
+            Log.w(LOG_TAG, "Error while calling remote"
+                    + " findAccessibilityNodeInfoByViewIdInActiveWindow", re);
         }
         return Collections.emptyList();
     }
@@ -307,10 +391,8 @@
                 }
             }
         } catch (RemoteException re) {
-            if (DEBUG) {
-                Log.w(LOG_TAG, "Error while calling remote"
-                        + " findAccessibilityNodeInfosByViewText", re);
-            }
+            Log.w(LOG_TAG, "Error while calling remote"
+                    + " findAccessibilityNodeInfosByViewText", re);
         }
         return Collections.emptyList();
     }
@@ -352,9 +434,7 @@
                 }
             }
         } catch (RemoteException re) {
-            if (DEBUG) {
-                Log.w(LOG_TAG, "Error while calling remote findFocus", re);
-            }
+            Log.w(LOG_TAG, "Error while calling remote findFocus", re);
         }
         return null;
     }
@@ -396,9 +476,7 @@
                 }
             }
         } catch (RemoteException re) {
-            if (DEBUG) {
-                Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
-            }
+            Log.w(LOG_TAG, "Error while calling remote accessibilityFocusSearch", re);
         }
         return null;
     }
@@ -436,19 +514,21 @@
                 }
             }
         } catch (RemoteException re) {
-            if (DEBUG) {
-                Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
-            }
+            Log.w(LOG_TAG, "Error while calling remote performAccessibilityAction", re);
         }
         return false;
     }
 
     public void clearCache() {
-        sAccessibilityNodeInfoCache.clear();
+        sAccessibilityCache.clear();
     }
 
     public void onAccessibilityEvent(AccessibilityEvent event) {
-        sAccessibilityNodeInfoCache.onAccessibilityEvent(event);
+        sAccessibilityCache.onAccessibilityEvent(event);
+    }
+
+    public void removeWindows(int[] windowIds) {
+        sAccessibilityCache.removeWindows(windowIds);
     }
 
     /**
@@ -613,7 +693,7 @@
         if (info != null) {
             info.setConnectionId(connectionId);
             info.setSealed(true);
-            sAccessibilityNodeInfoCache.add(info);
+            sAccessibilityCache.add(info);
         }
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index a711f48..cbc38c6 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -115,9 +115,9 @@
 
     private static AccessibilityManager sInstance;
 
-    private static final int DO_SET_STATE = 10;
+    private final Object mLock = new Object();
 
-    final IAccessibilityManager mService;
+    private IAccessibilityManager mService;
 
     final int mUserId;
 
@@ -166,29 +166,14 @@
         public void onTouchExplorationStateChanged(boolean enabled);
     }
 
-    final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
+    private final IAccessibilityManagerClient.Stub mClient =
+            new IAccessibilityManagerClient.Stub() {
         public void setState(int state) {
-            mHandler.obtainMessage(DO_SET_STATE, state, 0).sendToTarget();
-        }
-    };
-
-    class MyHandler extends Handler {
-
-        MyHandler(Looper mainLooper) {
-            super(mainLooper);
-        }
-
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case DO_SET_STATE :
-                    setState(message.arg1);
-                    return;
-                default :
-                    Log.w(LOG_TAG, "Unknown message type: " + message.what);
+            synchronized (mLock) {
+                setStateLocked(state);
             }
         }
-    }
+    };
 
     /**
      * Get an AccessibilityManager instance (create one if necessary).
@@ -234,17 +219,16 @@
         mHandler = new MyHandler(context.getMainLooper());
         mService = service;
         mUserId = userId;
-        if (mService == null) {
-            mIsEnabled = false;
+        synchronized (mLock) {
+            tryConnectToServiceLocked();
         }
-        try {
-            if (mService != null) {
-                final int stateFlags = mService.addClient(mClient, userId);
-                setState(stateFlags);
-            }
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
-        }
+    }
+
+    /**
+     * @hide
+     */
+    public IAccessibilityManagerClient getClient() {
+        return mClient;
     }
 
     /**
@@ -253,7 +237,11 @@
      * @return True if accessibility is enabled, false otherwise.
      */
     public boolean isEnabled() {
-        synchronized (mHandler) {
+        synchronized (mLock) {
+            IAccessibilityManager service = getServiceLocked();
+            if (service == null) {
+                return false;
+            }
             return mIsEnabled;
         }
     }
@@ -264,24 +252,16 @@
      * @return True if touch exploration is enabled, false otherwise.
      */
     public boolean isTouchExplorationEnabled() {
-        synchronized (mHandler) {
+        synchronized (mLock) {
+            IAccessibilityManager service = getServiceLocked();
+            if (service == null) {
+                return false;
+            }
             return mIsTouchExplorationEnabled;
         }
     }
 
     /**
-     * Returns the client interface this instance registers in
-     * the centralized accessibility manager service.
-     *
-     * @return The client.
-     *
-     * @hide
-     */
-    public IAccessibilityManagerClient getClient() {
-       return (IAccessibilityManagerClient) mClient.asBinder();
-    }
-
-    /**
      * Sends an {@link AccessibilityEvent}.
      *
      * @param event The event to send.
@@ -295,8 +275,17 @@
      * their descendants.
      */
     public void sendAccessibilityEvent(AccessibilityEvent event) {
-        if (!mIsEnabled) {
-            throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+        final IAccessibilityManager service;
+        final int userId;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+            if (!mIsEnabled) {
+                throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+            }
+            userId = mUserId;
         }
         boolean doRecycle = false;
         try {
@@ -305,7 +294,7 @@
             // client using it is called through Binder from another process. Example: MMS
             // app adds a SMS notification and the NotificationManagerService calls this method
             long identityToken = Binder.clearCallingIdentity();
-            doRecycle = mService.sendAccessibilityEvent(event, mUserId);
+            doRecycle = service.sendAccessibilityEvent(event, userId);
             Binder.restoreCallingIdentity(identityToken);
             if (DEBUG) {
                 Log.i(LOG_TAG, event + " sent");
@@ -323,11 +312,20 @@
      * Requests feedback interruption from all accessibility services.
      */
     public void interrupt() {
-        if (!mIsEnabled) {
-            throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+        final IAccessibilityManager service;
+        final int userId;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
+            }
+            if (!mIsEnabled) {
+                throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+            }
+            userId = mUserId;
         }
         try {
-            mService.interrupt(mUserId);
+            service.interrupt(userId);
             if (DEBUG) {
                 Log.i(LOG_TAG, "Requested interrupt from all services");
             }
@@ -361,18 +359,30 @@
      * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
      */
     public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
+        final IAccessibilityManager service;
+        final int userId;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return Collections.emptyList();
+            }
+            userId = mUserId;
+        }
+
         List<AccessibilityServiceInfo> services = null;
         try {
-            if (mService != null) {
-                services = mService.getInstalledAccessibilityServiceList(mUserId);
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
-                }
+            services = service.getInstalledAccessibilityServiceList(userId);
+            if (DEBUG) {
+                Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
             }
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
         }
-        return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST;
+        if (services != null) {
+            return Collections.unmodifiableList(services);
+        } else {
+            return Collections.emptyList();
+        }
     }
 
     /**
@@ -390,18 +400,30 @@
      */
     public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
             int feedbackTypeFlags) {
+        final IAccessibilityManager service;
+        final int userId;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return Collections.emptyList();
+            }
+            userId = mUserId;
+        }
+
         List<AccessibilityServiceInfo> services = null;
         try {
-            if (mService != null) {
-                services = mService.getEnabledAccessibilityServiceList(feedbackTypeFlags, mUserId);
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
-                }
+            services = service.getEnabledAccessibilityServiceList(feedbackTypeFlags, userId);
+            if (DEBUG) {
+                Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
             }
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
         }
-        return services != null ? Collections.unmodifiableList(services) : Collections.EMPTY_LIST;
+        if (services != null) {
+            return Collections.unmodifiableList(services);
+        } else {
+            return Collections.emptyList();
+        }
     }
 
     /**
@@ -413,6 +435,7 @@
      */
     public boolean addAccessibilityStateChangeListener(
             AccessibilityStateChangeListener listener) {
+        // Final CopyOnArrayList - no lock needed.
         return mAccessibilityStateChangeListeners.add(listener);
     }
 
@@ -424,6 +447,7 @@
      */
     public boolean removeAccessibilityStateChangeListener(
             AccessibilityStateChangeListener listener) {
+        // Final CopyOnArrayList - no lock needed.
         return mAccessibilityStateChangeListeners.remove(listener);
     }
 
@@ -436,6 +460,7 @@
      */
     public boolean addTouchExplorationStateChangeListener(
             TouchExplorationStateChangeListener listener) {
+        // Final CopyOnArrayList - no lock needed.
         return mTouchExplorationStateChangeListeners.add(listener);
     }
 
@@ -447,6 +472,7 @@
      */
     public boolean removeTouchExplorationStateChangeListener(
             TouchExplorationStateChangeListener listener) {
+        // Final CopyOnArrayList - no lock needed.
         return mTouchExplorationStateChangeListeners.remove(listener);
     }
 
@@ -455,50 +481,24 @@
      *
      * @param stateFlags The state flags.
      */
-    private void setState(int stateFlags) {
+    private void setStateLocked(int stateFlags) {
         final boolean enabled = (stateFlags & STATE_FLAG_ACCESSIBILITY_ENABLED) != 0;
         final boolean touchExplorationEnabled =
                 (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0;
-        synchronized (mHandler) {
-            final boolean wasEnabled = mIsEnabled;
-            final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
 
-            // Ensure listeners get current state from isZzzEnabled() calls.
-            mIsEnabled = enabled;
-            mIsTouchExplorationEnabled = touchExplorationEnabled;
+        final boolean wasEnabled = mIsEnabled;
+        final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled;
 
-            if (wasEnabled != enabled) {
-                notifyAccessibilityStateChangedLh();
-            }
+        // Ensure listeners get current state from isZzzEnabled() calls.
+        mIsEnabled = enabled;
+        mIsTouchExplorationEnabled = touchExplorationEnabled;
 
-            if (wasTouchExplorationEnabled != touchExplorationEnabled) {
-                notifyTouchExplorationStateChangedLh();
-            }
+        if (wasEnabled != enabled) {
+            mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED);
         }
-    }
 
-    /**
-     * Notifies the registered {@link AccessibilityStateChangeListener}s.
-     * <p>
-     * The caller must be locked on {@link #mHandler}.
-     */
-    private void notifyAccessibilityStateChangedLh() {
-        final int listenerCount = mAccessibilityStateChangeListeners.size();
-        for (int i = 0; i < listenerCount; i++) {
-            mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled);
-        }
-    }
-
-    /**
-     * Notifies the registered {@link TouchExplorationStateChangeListener}s.
-     * <p>
-     * The caller must be locked on {@link #mHandler}.
-     */
-    private void notifyTouchExplorationStateChangedLh() {
-        final int listenerCount = mTouchExplorationStateChangeListeners.size();
-        for (int i = 0; i < listenerCount; i++) {
-            mTouchExplorationStateChangeListeners.get(i)
-                    .onTouchExplorationStateChanged(mIsTouchExplorationEnabled);
+        if (wasTouchExplorationEnabled != touchExplorationEnabled) {
+            mHandler.sendEmptyMessage(MyHandler.MSG_NOTIFY_EXPLORATION_STATE_CHANGED);
         }
     }
 
@@ -511,11 +511,17 @@
      */
     public int addAccessibilityInteractionConnection(IWindow windowToken,
             IAccessibilityInteractionConnection connection) {
-        if (mService == null) {
-            return View.NO_ID;
+        final IAccessibilityManager service;
+        final int userId;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return View.NO_ID;
+            }
+            userId = mUserId;
         }
         try {
-            return mService.addAccessibilityInteractionConnection(windowToken, connection, mUserId);
+            return service.addAccessibilityInteractionConnection(windowToken, connection, userId);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
         }
@@ -529,12 +535,90 @@
      * @hide
      */
     public void removeAccessibilityInteractionConnection(IWindow windowToken) {
-        try {
-            if (mService != null) {
-                mService.removeAccessibilityInteractionConnection(windowToken);
+        final IAccessibilityManager service;
+        synchronized (mLock) {
+            service = getServiceLocked();
+            if (service == null) {
+                return;
             }
+        }
+        try {
+            service.removeAccessibilityInteractionConnection(windowToken);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
         }
     }
+
+    private  IAccessibilityManager getServiceLocked() {
+        if (mService == null) {
+            tryConnectToServiceLocked();
+        }
+        return mService;
+    }
+
+    private void tryConnectToServiceLocked() {
+        IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+        if (iBinder == null) {
+            return;
+        }
+        IAccessibilityManager service = IAccessibilityManager.Stub.asInterface(iBinder);
+        try {
+            final int stateFlags = service.addClient(mClient, mUserId);
+            setStateLocked(stateFlags);
+            mService = service;
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+        }
+    }
+
+    /**
+     * Notifies the registered {@link AccessibilityStateChangeListener}s.
+     */
+    private void handleNotifyAccessibilityStateChanged() {
+        final boolean isEnabled;
+        synchronized (mLock) {
+            isEnabled = mIsEnabled;
+        }
+        final int listenerCount = mAccessibilityStateChangeListeners.size();
+        for (int i = 0; i < listenerCount; i++) {
+            mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(isEnabled);
+        }
+    }
+
+    /**
+     * Notifies the registered {@link TouchExplorationStateChangeListener}s.
+     */
+    private void handleNotifyTouchExplorationStateChanged() {
+        final boolean isTouchExplorationEnabled;
+        synchronized (mLock) {
+            isTouchExplorationEnabled = mIsTouchExplorationEnabled;
+        }
+        final int listenerCount = mTouchExplorationStateChangeListeners.size();
+        for (int i = 0; i < listenerCount; i++) {
+            mTouchExplorationStateChangeListeners.get(i)
+                    .onTouchExplorationStateChanged(isTouchExplorationEnabled);
+        }
+    }
+
+    private final class MyHandler extends Handler {
+        public static final int MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED = 1;
+        public static final int MSG_NOTIFY_EXPLORATION_STATE_CHANGED = 2;
+
+        public MyHandler(Looper looper) {
+            super(looper, null, false);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            switch (message.what) {
+                case MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED: {
+                    handleNotifyAccessibilityStateChanged();
+                } break;
+
+                case MSG_NOTIFY_EXPLORATION_STATE_CHANGED: {
+                    handleNotifyTouchExplorationStateChanged();
+                }
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 560d0c9..a6904f7 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -62,13 +62,19 @@
     private static final boolean DEBUG = false;
 
     /** @hide */
-    public static final int UNDEFINED = -1;
+    public static final int UNDEFINED_CONNECTION_ID = -1;
 
     /** @hide */
-    public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED);
+    public static final int UNDEFINED_SELECTION_INDEX = -1;
 
     /** @hide */
-    public static final int ACTIVE_WINDOW_ID = UNDEFINED;
+    public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE;
+
+    /** @hide */
+    public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID);
+
+    /** @hide */
+    public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID;
 
     /** @hide */
     public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
@@ -504,6 +510,13 @@
      * @hide
      */
     public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
+        // We changed the value for undefined node to positive due to wrong
+        // global id composition (two 32-bin ints into one 64-bit long) but
+        // the value used for the host node provider view has id -1 so we
+        // remap it here.
+        if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
+            virtualDescendantId = UNDEFINED_ITEM_ID;
+        }
         return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
     }
 
@@ -515,7 +528,7 @@
     private boolean mSealed;
 
     // Data.
-    private int mWindowId = UNDEFINED;
+    private int mWindowId = UNDEFINED_ITEM_ID;
     private long mSourceNodeId = ROOT_NODE_ID;
     private long mParentNodeId = ROOT_NODE_ID;
     private long mLabelForId = ROOT_NODE_ID;
@@ -536,14 +549,14 @@
 
     private int mMovementGranularities;
 
-    private int mTextSelectionStart = UNDEFINED;
-    private int mTextSelectionEnd = UNDEFINED;
+    private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
+    private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
     private int mInputType = InputType.TYPE_NULL;
     private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
 
     private Bundle mExtras;
 
-    private int mConnectionId = UNDEFINED;
+    private int mConnectionId = UNDEFINED_CONNECTION_ID;
 
     private RangeInfo mRangeInfo;
     private CollectionInfo mCollectionInfo;
@@ -567,7 +580,7 @@
      * @param source The info source.
      */
     public void setSource(View source) {
-        setSource(source, UNDEFINED);
+        setSource(source, UNDEFINED_ITEM_ID);
     }
 
     /**
@@ -591,9 +604,9 @@
      */
     public void setSource(View root, int virtualDescendantId) {
         enforceNotSealed();
-        mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
+        mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID;
         final int rootAccessibilityViewId =
-            (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+            (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
         mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
     }
 
@@ -766,7 +779,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void addChild(View child) {
-        addChildInternal(child, UNDEFINED, true);
+        addChildInternal(child, UNDEFINED_ITEM_ID, true);
     }
 
     /**
@@ -776,7 +789,7 @@
      * @hide
      */
     public void addChildUnchecked(View child) {
-        addChildInternal(child, UNDEFINED, false);
+        addChildInternal(child, UNDEFINED_ITEM_ID, false);
     }
 
     /**
@@ -794,7 +807,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public boolean removeChild(View child) {
-        return removeChild(child, UNDEFINED);
+        return removeChild(child, UNDEFINED_ITEM_ID);
     }
 
     /**
@@ -821,7 +834,7 @@
             mChildNodeIds = new LongArray();
         }
         final int rootAccessibilityViewId =
-            (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+            (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
         // If we're checking uniqueness and the ID already exists, abort.
         if (checked && mChildNodeIds.indexOf(childNodeId) >= 0) {
@@ -847,7 +860,7 @@
             return false;
         }
         final int rootAccessibilityViewId =
-                (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+                (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
         final int index = childIds.indexOf(childNodeId);
         if (index < 0) {
@@ -1043,6 +1056,22 @@
     }
 
     /**
+     * Gets the window to which this node belongs.
+     *
+     * @return The window.
+     *
+     * @see android.accessibilityservice.AccessibilityService#getWindows()
+     */
+    public AccessibilityWindowInfo getWindow() {
+        enforceSealed();
+        if (!canPerformRequestOverConnection(mSourceNodeId)) {
+            return null;
+        }
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.getWindow(mConnectionId, mWindowId);
+    }
+
+    /**
      * Gets the parent.
      * <p>
      *   <strong>Note:</strong> It is a client responsibility to recycle the
@@ -1059,7 +1088,8 @@
         }
         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
-                mWindowId, mParentNodeId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+                mWindowId, mParentNodeId, false, FLAG_PREFETCH_PREDECESSORS
+                        | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
     }
 
     /**
@@ -1084,7 +1114,7 @@
      * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setParent(View parent) {
-        setParent(parent, UNDEFINED);
+        setParent(parent, UNDEFINED_ITEM_ID);
     }
 
     /**
@@ -1109,7 +1139,7 @@
     public void setParent(View root, int virtualDescendantId) {
         enforceNotSealed();
         final int rootAccessibilityViewId =
-            (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
+            (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
         mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
     }
 
@@ -1811,7 +1841,7 @@
      * @param labeled The view for which this info serves as a label.
      */
     public void setLabelFor(View labeled) {
-        setLabelFor(labeled, UNDEFINED);
+        setLabelFor(labeled, UNDEFINED_ITEM_ID);
     }
 
     /**
@@ -1836,7 +1866,7 @@
     public void setLabelFor(View root, int virtualDescendantId) {
         enforceNotSealed();
         final int rootAccessibilityViewId = (root != null)
-                ? root.getAccessibilityViewId() : UNDEFINED;
+                ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
         mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
     }
 
@@ -1858,7 +1888,8 @@
         }
         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
-                mWindowId, mLabelForId, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+                mWindowId, mLabelForId, false, FLAG_PREFETCH_PREDECESSORS
+                        | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
     }
 
     /**
@@ -1868,7 +1899,7 @@
      * @param label The view that labels this node's source.
      */
     public void setLabeledBy(View label) {
-        setLabeledBy(label, UNDEFINED);
+        setLabeledBy(label, UNDEFINED_ITEM_ID);
     }
 
     /**
@@ -1893,7 +1924,7 @@
     public void setLabeledBy(View root, int virtualDescendantId) {
         enforceNotSealed();
         final int rootAccessibilityViewId = (root != null)
-                ? root.getAccessibilityViewId() : UNDEFINED;
+                ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
         mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
     }
 
@@ -1915,7 +1946,8 @@
         }
         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
-                mWindowId, mLabeledById, false, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
+                mWindowId, mLabeledById, false, FLAG_PREFETCH_PREDECESSORS
+                        | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
     }
 
     /**
@@ -2362,6 +2394,7 @@
             if (mChildNodeIds == null) {
                 mChildNodeIds = otherChildNodeIds.clone();
             } else {
+                mChildNodeIds.clear();
                 mChildNodeIds.addAll(otherChildNodeIds);
             }
         }
@@ -2474,8 +2507,8 @@
         mParentNodeId = ROOT_NODE_ID;
         mLabelForId = ROOT_NODE_ID;
         mLabeledById = ROOT_NODE_ID;
-        mWindowId = UNDEFINED;
-        mConnectionId = UNDEFINED;
+        mWindowId = UNDEFINED_ITEM_ID;
+        mConnectionId = UNDEFINED_CONNECTION_ID;
         mMovementGranularities = 0;
         if (mChildNodeIds != null) {
             mChildNodeIds.clear();
@@ -2489,8 +2522,8 @@
         mContentDescription = null;
         mViewIdResourceName = null;
         mActions = 0;
-        mTextSelectionStart = UNDEFINED;
-        mTextSelectionEnd = UNDEFINED;
+        mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
+        mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
         mInputType = InputType.TYPE_NULL;
         mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
         if (mExtras != null) {
@@ -2583,9 +2616,9 @@
     }
 
     private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
-        return (mWindowId != UNDEFINED
-                && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
-                && mConnectionId != UNDEFINED);
+        return (mWindowId != UNDEFINED_ITEM_ID
+                && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID
+                && mConnectionId != UNDEFINED_CONNECTION_ID);
     }
 
     @Override
@@ -2625,6 +2658,7 @@
         builder.append(super.toString());
 
         if (DEBUG) {
+            builder.append("; sourceNodeId: " + mSourceNodeId);
             builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
             builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
             builder.append("; mParentNodeId: " + mParentNodeId);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
deleted file mode 100644
index b4944be..0000000
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright (C) 2012 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.view.accessibility;
-
-import android.os.Build;
-import android.util.Log;
-import android.util.LongArray;
-import android.util.LongSparseArray;
-
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.Queue;
-
-/**
- * Simple cache for AccessibilityNodeInfos. The cache is mapping an
- * accessibility id to an info. The cache allows storing of
- * <code>null</code> values. It also tracks accessibility events
- * and invalidates accordingly.
- *
- * @hide
- */
-public class AccessibilityNodeInfoCache {
-
-    private static final String LOG_TAG = AccessibilityNodeInfoCache.class.getSimpleName();
-
-    private static final boolean ENABLED = true;
-
-    private static final boolean DEBUG = false;
-
-    private static final boolean CHECK_INTEGRITY_IF_DEBUGGABLE_BUILD = true;
-
-    private final Object mLock = new Object();
-
-    private final LongSparseArray<AccessibilityNodeInfo> mCacheImpl;
-
-    private int mWindowId;
-
-    public AccessibilityNodeInfoCache() {
-        if (ENABLED) {
-            mCacheImpl = new LongSparseArray<AccessibilityNodeInfo>();
-        } else {
-            mCacheImpl = null;
-        }
-    }
-
-    /**
-     * The cache keeps track of {@link AccessibilityEvent}s and invalidates
-     * cached nodes as appropriate.
-     *
-     * @param event An event.
-     */
-    public void onAccessibilityEvent(AccessibilityEvent event) {
-        if (ENABLED) {
-            final int eventType = event.getEventType();
-            switch (eventType) {
-                case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
-                case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
-                case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
-                case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
-                    // If the active window changes, clear the cache.
-                    final int windowId = event.getWindowId();
-                    if (mWindowId != windowId) {
-                        mWindowId = windowId;
-                        clear();
-                    }
-                } break;
-                case AccessibilityEvent.TYPE_VIEW_FOCUSED:
-                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
-                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
-                case AccessibilityEvent.TYPE_VIEW_SELECTED:
-                case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
-                case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
-                    refreshCachedNode(event.getSourceNodeId());
-                } break;
-                case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
-                    synchronized (mLock) {
-                        clearSubTreeLocked(event.getSourceNodeId());
-                    }
-                } break;
-                case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: {
-                    synchronized (mLock) {
-                        final long sourceId = event.getSourceNodeId();
-                        if ((event.getContentChangeTypes()
-                                & AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE) != 0) {
-                            clearSubTreeLocked(sourceId);
-                        } else {
-                            refreshCachedNode(sourceId);
-                        }
-                    }
-                } break;
-            }
-            if (CHECK_INTEGRITY_IF_DEBUGGABLE_BUILD && Build.IS_DEBUGGABLE) {
-                checkIntegrity();
-            }
-        }
-    }
-
-    private void refreshCachedNode(long sourceId) {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "Refreshing cached node.");
-        }
-        synchronized (mLock) {
-            AccessibilityNodeInfo cachedInfo = mCacheImpl.get(sourceId);
-            // If the source is not in the cache - nothing to do.
-            if (cachedInfo == null) {
-                return;
-            }
-            // The node changed so we will just refresh it right now.
-            if (cachedInfo.refresh(true)) {
-                return;
-            }
-            // Weird, we could not refresh. Just evict the entire sub-tree.
-            clearSubTreeLocked(sourceId);
-        }
-    }
-
-    /**
-     * Gets a cached {@link AccessibilityNodeInfo} given its accessibility node id.
-     *
-     * @param accessibilityNodeId The info accessibility node id.
-     * @return The cached {@link AccessibilityNodeInfo} or null if such not found.
-     */
-    public AccessibilityNodeInfo get(long accessibilityNodeId) {
-        if (ENABLED) {
-            synchronized(mLock) {
-                AccessibilityNodeInfo info = mCacheImpl.get(accessibilityNodeId);
-                if (info != null) {
-                    // Return a copy since the client calls to AccessibilityNodeInfo#recycle()
-                    // will wipe the data of the cached info.
-                    info = AccessibilityNodeInfo.obtain(info);
-                }
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
-                }
-                return info;
-            }
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * Caches an {@link AccessibilityNodeInfo} given its accessibility node id.
-     *
-     * @param info The {@link AccessibilityNodeInfo} to cache.
-     */
-    public void add(AccessibilityNodeInfo info) {
-        if (ENABLED) {
-            synchronized(mLock) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "add(" + info + ")");
-                }
-
-                final long sourceId = info.getSourceNodeId();
-                AccessibilityNodeInfo oldInfo = mCacheImpl.get(sourceId);
-                if (oldInfo != null) {
-                    // If the added node is in the cache we have to be careful if
-                    // the new one represents a source state where some of the
-                    // children have been removed to avoid having disconnected
-                    // subtrees in the cache.
-                    // TODO: Runs in O(n^2), could optimize to O(n + n log n)
-                    final LongArray newChildrenIds = info.getChildNodeIds();
-                    if (newChildrenIds != null) {
-                        final int oldChildCount = oldInfo.getChildCount();
-                        for (int i = 0; i < oldChildCount; i++) {
-                            final long oldChildId = oldInfo.getChildId(i);
-                            if (newChildrenIds.indexOf(oldChildId) < 0) {
-                                clearSubTreeLocked(oldChildId);
-                            }
-                        }
-                    }
-
-                    // Also be careful if the parent has changed since the new
-                    // parent may be a predecessor of the old parent which will
-                    // make the cached tree cyclic.
-                    final long oldParentId = oldInfo.getParentNodeId();
-                    if (info.getParentNodeId() != oldParentId) {
-                        clearSubTreeLocked(oldParentId);
-                    }
-                }
-
-                // Cache a copy since the client calls to AccessibilityNodeInfo#recycle()
-                // will wipe the data of the cached info.
-                AccessibilityNodeInfo clone = AccessibilityNodeInfo.obtain(info);
-                mCacheImpl.put(sourceId, clone);
-            }
-        }
-    }
-
-    /**
-     * Clears the cache.
-     */
-    public void clear() {
-        if (ENABLED) {
-            synchronized(mLock) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "clear()");
-                }
-                // Recycle the nodes before clearing the cache.
-                final int nodeCount = mCacheImpl.size();
-                for (int i = 0; i < nodeCount; i++) {
-                    AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
-                    info.recycle();
-                }
-                mCacheImpl.clear();
-            }
-        }
-    }
-
-    /**
-     * Clears a subtree rooted at the node with the given id.
-     *
-     * @param rootNodeId The root id.
-     */
-    private void clearSubTreeLocked(long rootNodeId) {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "Clearing cached subtree.");
-        }
-        clearSubTreeRecursiveLocked(rootNodeId);
-    }
-
-    private void clearSubTreeRecursiveLocked(long rootNodeId) {
-        AccessibilityNodeInfo current = mCacheImpl.get(rootNodeId);
-        if (current == null) {
-            return;
-        }
-        mCacheImpl.remove(rootNodeId);
-        final int childCount = current.getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final long childNodeId = current.getChildId(i);
-            clearSubTreeRecursiveLocked(childNodeId);
-        }
-    }
-
-    /**
-     * Check the integrity of the cache which is it does not have nodes
-     * from more than one window, there are no duplicates, all nodes are
-     * connected, there is a single input focused node, and there is a
-     * single accessibility focused node.
-     */
-    private void checkIntegrity() {
-        synchronized (mLock) {
-            // Get the root.
-            if (mCacheImpl.size() <= 0) {
-                return;
-            }
-
-            // If the cache is a tree it does not matter from
-            // which node we start to search for the root.
-            AccessibilityNodeInfo root = mCacheImpl.valueAt(0);
-            AccessibilityNodeInfo parent = root;
-            while (parent != null) {
-                root = parent;
-                parent = mCacheImpl.get(parent.getParentNodeId());
-            }
-
-            // Traverse the tree and do some checks.
-            final int windowId = root.getWindowId();
-            AccessibilityNodeInfo accessFocus = null;
-            AccessibilityNodeInfo inputFocus = null;
-            HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
-            Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
-            fringe.add(root);
-
-            while (!fringe.isEmpty()) {
-                AccessibilityNodeInfo current = fringe.poll();
-                // Check for duplicates
-                if (!seen.add(current)) {
-                    Log.e(LOG_TAG, "Duplicate node: " + current);
-                    return;
-                }
-
-                // Check for one accessibility focus.
-                if (current.isAccessibilityFocused()) {
-                    if (accessFocus != null) {
-                        Log.e(LOG_TAG, "Duplicate accessibility focus:" + current);
-                    } else {
-                        accessFocus = current;
-                    }
-                }
-
-                // Check for one input focus.
-                if (current.isFocused()) {
-                    if (inputFocus != null) {
-                        Log.e(LOG_TAG, "Duplicate input focus: " + current);
-                    } else {
-                        inputFocus = current;
-                    }
-                }
-
-                final int childCount = current.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    final long childId = current.getChildId(i);
-                    final AccessibilityNodeInfo child = mCacheImpl.get(childId);
-                    if (child != null) {
-                        fringe.add(child);
-                    }
-                }
-            }
-
-            int disconnectedNodeCount = 0;
-            // Check for disconnected nodes or ones from another window.
-            for (int i = 0; i < mCacheImpl.size(); i++) {
-                AccessibilityNodeInfo info = mCacheImpl.valueAt(i);
-                if (!seen.contains(info)) {
-                    if (info.getWindowId() == windowId) {
-                        if (DEBUG) {
-                            Log.e(LOG_TAG, "Disconnected node: " + info);
-                        }
-                        disconnectedNodeCount++;
-                    } else {
-                        Log.e(LOG_TAG, "Node from: " + info.getWindowId() + " not from:"
-                                + windowId + " " + info);
-                    }
-                }
-            }
-            if (disconnectedNodeCount > 0) {
-                Log.e(LOG_TAG, String.format("Found %d disconnected nodes", disconnectedNodeCount));
-            }
-        }
-    }
-}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index 718c32f..abcbb70 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -70,9 +70,14 @@
 public abstract class AccessibilityNodeProvider {
 
     /**
+     * The virtual id for the hosting View.
+     */
+    public static final int HOST_VIEW_ID = -1;
+
+    /**
      * Returns an {@link AccessibilityNodeInfo} representing a virtual view,
      * i.e. a descendant of the host View, with the given <code>virtualViewId</code>
-     * or the host View itself if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+     * or the host View itself if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
      * <p>
      * A virtual descendant is an imaginary View that is reported as a part of the view
      * hierarchy for accessibility purposes. This enables custom views that draw complex
@@ -99,7 +104,7 @@
     /**
      * Performs an accessibility action on a virtual view, i.e. a descendant of the
      * host View, with the given <code>virtualViewId</code> or the host View itself
-     * if <code>virtualViewId</code> equals to {@link View#NO_ID}.
+     * if <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
      *
      * @param virtualViewId A client defined virtual view id.
      * @param action The action to perform.
@@ -117,8 +122,8 @@
     /**
      * Finds {@link AccessibilityNodeInfo}s by text. The match is case insensitive
      * containment. The search is relative to the virtual view, i.e. a descendant of the
-     * host View, with the given <code>virtualViewId</code> or the host View itself 
-     * <code>virtualViewId</code> equals to {@link View#NO_ID}.
+     * host View, with the given <code>virtualViewId</code> or the host View itself
+     * <code>virtualViewId</code> equals to {@link #HOST_VIEW_ID}.
      *
      * @param virtualViewId A client defined virtual view id which defined
      *     the root of the tree in which to perform the search.
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 3fcd218..cc6a71d 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -78,7 +78,7 @@
     private boolean mIsInPool;
 
     boolean mSealed;
-    int mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
+    int mBooleanProperties = 0;
     int mCurrentItemIndex = UNDEFINED;
     int mItemCount = UNDEFINED;
     int mFromIndex = UNDEFINED;
@@ -791,7 +791,7 @@
      */
     void clear() {
         mSealed = false;
-        mBooleanProperties = PROPERTY_IMPORTANT_FOR_ACCESSIBILITY;
+        mBooleanProperties = 0;
         mCurrentItemIndex = UNDEFINED;
         mItemCount = UNDEFINED;
         mFromIndex = UNDEFINED;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
new file mode 100644
index 0000000..fdb25fb
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, 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.view.accessibility;
+
+parcelable AccessibilityWindowInfo;
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
new file mode 100644
index 0000000..80b5c50
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java
@@ -0,0 +1,574 @@
+/*
+ * Copyright (C) 2014 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.view.accessibility;
+
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.LongArray;
+import android.util.Pools.SynchronizedPool;
+
+/**
+ * This class represents a state snapshot of a window for accessibility
+ * purposes. The screen content contains one or more windows where some
+ * windows can be descendants of other windows, which is the windows are
+ * hierarchically ordered. Note that there is no root window. Hence, the
+ * screen content can be seen as a collection of window trees.
+ */
+public final class AccessibilityWindowInfo implements Parcelable {
+
+    private static final boolean DEBUG = false;
+
+    /**
+     * Window type: This is an application window. Such a window shows UI for
+     * interacting with an application.
+     */
+    public static final int TYPE_APPLICATION = 1;
+
+    /**
+     * Window type: This is an input method window. Such a window shows UI for
+     * inputting text such as keyboard, suggestions, etc.
+     */
+    public static final int TYPE_INPUT_METHOD = 2;
+
+    /**
+     * Window type: This is an system window. Such a window shows UI for
+     * interacting with the system.
+     */
+    public static final int TYPE_SYSTEM = 3;
+
+    private static final int UNDEFINED = -1;
+
+    private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0;
+    private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1;
+
+    // Housekeeping.
+    private static final int MAX_POOL_SIZE = 10;
+    private static final SynchronizedPool<AccessibilityWindowInfo> sPool =
+            new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE);
+
+    // Data.
+    private int mType = UNDEFINED;
+    private int mLayer = UNDEFINED;
+    private int mBooleanProperties;
+    private int mId = UNDEFINED;
+    private int mParentId = UNDEFINED;
+    private final Rect mBoundsInScreen = new Rect();
+    private LongArray mChildIds;
+
+    private int mConnectionId = UNDEFINED;
+
+    private AccessibilityWindowInfo() {
+        /* do nothing - hide constructor */
+    }
+
+    /**
+     * Gets the type of the window.
+     *
+     * @return The type.
+     *
+     * @see #TYPE_APPLICATION
+     * @see #TYPE_INPUT_METHOD
+     * @see #TYPE_SYSTEM
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Sets the type of the window.
+     *
+     * @param The type
+     *
+     * @hide
+     */
+    public void setType(int type) {
+        mType = type;
+    }
+
+    /**
+     * Gets the layer which determines the Z-order of the window. Windows
+     * with greater layer appear on top of windows with lesser layer.
+     *
+     * @return The window layer.
+     */
+    public int getLayer() {
+        return mLayer;
+    }
+
+    /**
+     * Sets the layer which determines the Z-order of the window. Windows
+     * with greater layer appear on top of windows with lesser layer.
+     *
+     * @param The window layer.
+     *
+     * @hide
+     */
+    public void setLayer(int layer) {
+        mLayer = layer;
+    }
+
+    /**
+     * Gets the root node in the window's hierarchy.
+     *
+     * @return The root node.
+     */
+    public AccessibilityNodeInfo getRoot() {
+        if (mConnectionId == UNDEFINED) {
+            return null;
+        }
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
+                mId, AccessibilityNodeInfo.ROOT_NODE_ID,
+                true, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS);
+    }
+
+    /**
+     * Gets the parent window if such.
+     *
+     * @return The parent window.
+     */
+    public AccessibilityWindowInfo getParent() {
+        if (mConnectionId == UNDEFINED || mParentId == UNDEFINED) {
+            return null;
+        }
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.getWindow(mConnectionId, mParentId);
+    }
+
+    /**
+     * Sets the parent window id.
+     *
+     * @param parentId The parent id.
+     *
+     * @hide
+     */
+    public void setParentId(int parentId) {
+        mParentId = parentId;
+    }
+
+    /**
+     * Gets the unique window id.
+     *
+     * @return windowId The window id.
+     */
+    public int getId() {
+        return mId;
+    }
+
+    /**
+     * Sets the unique window id.
+     *
+     * @param windowId The window id.
+     *
+     * @hide
+     */
+    public void setId(int id) {
+        mId = id;
+    }
+
+    /**
+     * Sets the unique id of the IAccessibilityServiceConnection over which
+     * this instance can send requests to the system.
+     *
+     * @param connectionId The connection id.
+     *
+     * @hide
+     */
+    public void setConnectionId(int connectionId) {
+        mConnectionId = connectionId;
+    }
+
+    /**
+     * Gets the bounds of this window in the screen.
+     *
+     * @param outBounds The out window bounds.
+     */
+    public void getBoundsInScreen(Rect outBounds) {
+        outBounds.set(mBoundsInScreen);
+    }
+
+    /**
+     * Sets the bounds of this window in the screen.
+     *
+     * @param bounds The out window bounds.
+     *
+     * @hide
+     */
+    public void setBoundsInScreen(Rect bounds) {
+        mBoundsInScreen.set(bounds);
+    }
+
+    /**
+     * Gets if this window is active. An active window is the one
+     * the user is currently touching or the window has input focus
+     * and the user is not touching any window.
+     *
+     * @return Whether this is the active window.
+     */
+    public boolean isActive() {
+        return getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE);
+    }
+
+    /**
+     * Sets if this window is active, which is this is the window
+     * the user is currently touching or the window has input focus
+     * and the user is not touching any window.
+     *
+     * @param Whether this is the active window.
+     *
+     * @hide
+     */
+    public void setActive(boolean active) {
+        setBooleanProperty(BOOLEAN_PROPERTY_ACTIVE, active);
+    }
+
+    /**
+     * Gets if this window has input focus.
+     *
+     * @return Whether has input focus.
+     */
+    public boolean isFocused() {
+        return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
+    }
+
+    /**
+     * Sets if this window has input focus.
+     *
+     * @param Whether has input focus.
+     *
+     * @hide
+     */
+    public void setFocused(boolean focused) {
+        setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
+    }
+
+    /**
+     * Gets the number of child windows.
+     *
+     * @return The child count.
+     */
+    public int getChildCount() {
+        return (mChildIds != null) ? mChildIds.size() : 0;
+    }
+
+    /**
+     * Gets the child window at a given index.
+     *
+     * @param index The index.
+     * @return The child.
+     */
+    public AccessibilityWindowInfo getChild(int index) {
+        if (mChildIds == null) {
+            throw new IndexOutOfBoundsException();
+        }
+        if (mConnectionId == UNDEFINED) {
+            return null;
+        }
+        final int childId = (int) mChildIds.get(index);
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.getWindow(mConnectionId, childId);
+    }
+
+    /**
+     * Adds a child window.
+     *
+     * @param childId The child window id.
+     *
+     * @hide
+     */
+    public void addChild(int childId) {
+        if (mChildIds == null) {
+            mChildIds = new LongArray();
+        }
+        mChildIds.add(childId);
+    }
+
+    /**
+     * Returns a cached instance if such is available or a new one is
+     * created.
+     *
+     * @return An instance.
+     */
+    public static AccessibilityWindowInfo obtain() {
+        AccessibilityWindowInfo info = sPool.acquire();
+        if (info == null) {
+            info = new AccessibilityWindowInfo();
+        }
+        return info;
+    }
+
+    /**
+     * Returns a cached instance if such is available or a new one is
+     * created. The returned instance is initialized from the given
+     * <code>info</code>.
+     *
+     * @param info The other info.
+     * @return An instance.
+     */
+    public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) {
+        AccessibilityWindowInfo infoClone = obtain();
+
+        infoClone.mType = info.mType;
+        infoClone.mLayer = info.mLayer;
+        infoClone.mBooleanProperties = info.mBooleanProperties;
+        infoClone.mId = info.mId;
+        infoClone.mParentId = info.mParentId;
+        infoClone.mBoundsInScreen.set(info.mBoundsInScreen);
+
+        if (info.mChildIds != null && info.mChildIds.size() > 0) {
+            if (infoClone.mChildIds == null) {
+                infoClone.mChildIds = info.mChildIds.clone();
+            } else {
+                infoClone.mChildIds.addAll(info.mChildIds);
+            }
+        }
+
+        infoClone.mConnectionId = info.mConnectionId;
+
+        return infoClone;
+    }
+
+    /**
+     * Return an instance back to be reused.
+     * <p>
+     * <strong>Note:</strong> You must not touch the object after calling this function.
+     * </p>
+     *
+     * @throws IllegalStateException If the info is already recycled.
+     */
+    public void recycle() {
+        clear();
+        sPool.release(this);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mType);
+        parcel.writeInt(mLayer);
+        parcel.writeInt(mBooleanProperties);
+        parcel.writeInt(mId);
+        parcel.writeInt(mParentId);
+        mBoundsInScreen.writeToParcel(parcel, flags);
+
+        final LongArray childIds = mChildIds;
+        if (childIds == null) {
+            parcel.writeInt(0);
+        } else {
+            final int childCount = childIds.size();
+            parcel.writeInt(childCount);
+            for (int i = 0; i < childCount; i++) {
+                parcel.writeInt((int) childIds.get(i));
+            }
+        }
+
+        parcel.writeInt(mConnectionId);
+    }
+
+    private void initFromParcel(Parcel parcel) {
+        mType = parcel.readInt();
+        mLayer = parcel.readInt();
+        mBooleanProperties = parcel.readInt();
+        mId = parcel.readInt();
+        mParentId = parcel.readInt();
+        mBoundsInScreen.readFromParcel(parcel);
+
+        final int childCount = parcel.readInt();
+        if (childCount > 0) {
+            if (mChildIds == null) {
+                mChildIds = new LongArray(childCount);
+            }
+            for (int i = 0; i < childCount; i++) {
+                final int childId = parcel.readInt();
+                mChildIds.add(childId);
+            }
+        }
+
+        mConnectionId = parcel.readInt();
+    }
+
+    @Override
+    public int hashCode() {
+        return mId;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        AccessibilityWindowInfo other = (AccessibilityWindowInfo) obj;
+        return (mId == other.mId);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("AccessibilityWindowInfo[");
+        builder.append("id=").append(mId);
+        builder.append(", type=").append(typeToString(mType));
+        builder.append(", layer=").append(mLayer);
+        builder.append(", bounds=").append(mBoundsInScreen);
+        builder.append(", focused=").append(isFocused());
+        builder.append(", active=").append(isActive());
+        if (DEBUG) {
+            builder.append(", parent=").append(mParentId);
+            builder.append(", children=[");
+            if (mChildIds != null) {
+                final int childCount = mChildIds.size();
+                for (int i = 0; i < childCount; i++) {
+                    builder.append(mChildIds.get(i));
+                    if (i < childCount - 1) {
+                        builder.append(',');
+                    }
+                }
+            } else {
+                builder.append("null");
+            }
+            builder.append(']');
+        } else {
+            builder.append(", hasParent=").append(mParentId != UNDEFINED);
+            builder.append(", hasChildren=").append(mChildIds != null
+                    && mChildIds.size() > 0);
+        }
+        builder.append(']');
+        return builder.toString();
+    }
+
+    /**
+     * Clears the internal state.
+     */
+    private void clear() {
+        mType = UNDEFINED;
+        mLayer = UNDEFINED;
+        mBooleanProperties = 0;
+        mId = UNDEFINED;
+        mParentId = UNDEFINED;
+        mBoundsInScreen.setEmpty();
+        if (mChildIds != null) {
+            mChildIds.clear();
+        }
+        mConnectionId = UNDEFINED;
+    }
+
+    /**
+     * Gets the value of a boolean property.
+     *
+     * @param property The property.
+     * @return The value.
+     */
+    private boolean getBooleanProperty(int property) {
+        return (mBooleanProperties & property) != 0;
+    }
+
+    /**
+     * Sets a boolean property.
+     *
+     * @param property The property.
+     * @param value The value.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    private void setBooleanProperty(int property, boolean value) {
+        if (value) {
+            mBooleanProperties |= property;
+        } else {
+            mBooleanProperties &= ~property;
+        }
+    }
+
+    private static String typeToString(int type) {
+        switch (type) {
+            case TYPE_APPLICATION: {
+                return "TYPE_APPLICATION";
+            }
+            case TYPE_INPUT_METHOD: {
+                return "TYPE_INPUT_METHOD";
+            }
+            case TYPE_SYSTEM: {
+                return "TYPE_SYSTEM";
+            }
+            default:
+                return "<UNKNOWN>";
+        }
+    }
+
+    /**
+     * Checks whether this window changed. The argument should be
+     * another state of the same window, which is have the same id
+     * and type as they never change.
+     *
+     * @param other The new state.
+     * @return Whether something changed.
+     *
+     * @hide
+     */
+    public boolean changed(AccessibilityWindowInfo other) {
+        if (other.mId != mId) {
+            throw new IllegalArgumentException("Not same window.");
+        }
+        if (other.mType != mType) {
+            throw new IllegalArgumentException("Not same type.");
+        }
+        if (!mBoundsInScreen.equals(mBoundsInScreen)) {
+            return true;
+        }
+        if (mLayer != other.mLayer) {
+            return true;
+        }
+        if (mBooleanProperties != other.mBooleanProperties) {
+            return true;
+        }
+        if (mParentId != other.mParentId) {
+            return true;
+        }
+        if (mChildIds == null) {
+            if (other.mChildIds != null) {
+                return true;
+            }
+        } else if (!mChildIds.equals(other.mChildIds)) {
+            return true;
+        }
+        return false;
+    }
+
+    public static final Parcelable.Creator<AccessibilityWindowInfo> CREATOR =
+            new Creator<AccessibilityWindowInfo>() {
+        @Override
+        public AccessibilityWindowInfo createFromParcel(Parcel parcel) {
+            AccessibilityWindowInfo info = obtain();
+            info.initFromParcel(parcel);
+            return info;
+        }
+
+        @Override
+        public AccessibilityWindowInfo[] newArray(int size) {
+            return new AccessibilityWindowInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/view/animation/ClipRectAnimation.java b/core/java/android/view/animation/ClipRectAnimation.java
new file mode 100644
index 0000000..2361501
--- /dev/null
+++ b/core/java/android/view/animation/ClipRectAnimation.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 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.view.animation;
+
+import android.graphics.Rect;
+
+/**
+ * An animation that controls the clip of an object. See the
+ * {@link android.view.animation full package} description for details and
+ * sample code.
+ *
+ * @hide
+ */
+public class ClipRectAnimation extends Animation {
+    private Rect mFromRect = new Rect();
+    private Rect mToRect = new Rect();
+
+    /**
+     * Constructor to use when building a ClipRectAnimation from code
+     *
+     * @param fromClip the clip rect to animate from
+     * @param toClip the clip rect to animate to
+     */
+    public ClipRectAnimation(Rect fromClip, Rect toClip) {
+        if (fromClip == null || toClip == null) {
+            throw new RuntimeException("Expected non-null animation clip rects");
+        }
+        mFromRect.set(fromClip);
+        mToRect.set(toClip);
+    }
+
+    @Override
+    protected void applyTransformation(float it, Transformation tr) {
+        int l = mFromRect.left + (int) ((mToRect.left - mFromRect.left) * it);
+        int t = mFromRect.top + (int) ((mToRect.top - mFromRect.top) * it);
+        int r = mFromRect.right + (int) ((mToRect.right - mFromRect.right) * it);
+        int b = mFromRect.bottom + (int) ((mToRect.bottom - mFromRect.bottom) * it);
+        tr.setClipRect(l, t, r, b);
+    }
+
+    @Override
+    public boolean willChangeTransformationMatrix() {
+        return false;
+    }
+}
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
index 890909b..2f4fe73 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -17,6 +17,7 @@
 package android.view.animation;
 
 import android.graphics.Matrix;
+import android.graphics.Rect;
 
 import java.io.PrintWriter;
 
@@ -47,6 +48,9 @@
     protected float mAlpha;
     protected int mTransformationType;
 
+    private boolean mHasClipRect;
+    private Rect mClipRect = new Rect();
+
     /**
      * Creates a new transformation with alpha = 1 and the identity matrix.
      */
@@ -65,6 +69,8 @@
         } else {
             mMatrix.reset();
         }
+        mClipRect.setEmpty();
+        mHasClipRect = false;
         mAlpha = 1.0f;
         mTransformationType = TYPE_BOTH;
     }
@@ -98,9 +104,15 @@
     public void set(Transformation t) {
         mAlpha = t.getAlpha();
         mMatrix.set(t.getMatrix());
+        if (t.mHasClipRect) {
+            setClipRect(t.getClipRect());
+        } else {
+            mHasClipRect = false;
+            mClipRect.setEmpty();
+        }
         mTransformationType = t.getTransformationType();
     }
-    
+
     /**
      * Apply this Transformation to an existing Transformation, e.g. apply
      * a scale effect to something that has already been rotated.
@@ -109,6 +121,9 @@
     public void compose(Transformation t) {
         mAlpha *= t.getAlpha();
         mMatrix.preConcat(t.getMatrix());
+        if (t.mHasClipRect) {
+            setClipRect(t.getClipRect());
+        }
     }
     
     /**
@@ -119,6 +134,9 @@
     public void postCompose(Transformation t) {
         mAlpha *= t.getAlpha();
         mMatrix.postConcat(t.getMatrix());
+        if (t.mHasClipRect) {
+            setClipRect(t.getClipRect());
+        }
     }
 
     /**
@@ -138,6 +156,39 @@
     }
 
     /**
+     * Sets the current Transform's clip rect
+     * @hide
+     */
+    public void setClipRect(Rect r) {
+        setClipRect(r.left, r.top, r.right, r.bottom);
+    }
+
+    /**
+     * Sets the current Transform's clip rect
+     * @hide
+     */
+    public void setClipRect(int l, int t, int r, int b) {
+        mClipRect.set(l, t, r, b);
+        mHasClipRect = true;
+    }
+
+    /**
+     * Returns the current Transform's clip rect
+     * @hide
+     */
+    public Rect getClipRect() {
+        return mClipRect;
+    }
+
+    /**
+     * Returns whether the current Transform's clip rect is set
+     * @hide
+     */
+    public boolean hasClipRect() {
+        return mHasClipRect;
+    }
+
+    /**
      * @return The degree of transparency
      */
     public float getAlpha() {
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 16c41f3..cd75010 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -36,4 +36,6 @@
     List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
     void setMode(int code, int uid, String packageName, int mode);
     void resetAllModes();
+    int checkAudioOperation(int code, int stream, int uid, String packageName);
+    void setAudioRestriction(int code, int stream, int uid, int mode, in String[] exceptionPackages);
 }
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 5ba5c57..32f700d 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -55,7 +55,7 @@
     void noteScreenOff();
     void noteInputEvent();
     void noteUserActivity(int uid, int event);
-    void noteDataConnectionActive(int type, boolean active);
+    void noteDataConnectionActive(int type, boolean active, long timestampNs);
     void notePhoneOn();
     void notePhoneOff();
     void notePhoneSignalStrength(in SignalStrength signalStrength);
diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
index 7ddd5d2..5214dd9 100644
--- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
+++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl
@@ -59,6 +59,5 @@
     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 userId);
-
 }
 
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index a604d84..1bfad05 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -34,6 +34,8 @@
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
 
 import libcore.io.ErrnoException;
 import libcore.io.Libcore;
@@ -55,20 +57,24 @@
     private static final String TRANSPORT_DESTINATION_STRING
             = "Backing up to debug-only private cache";
 
-    // The single hardcoded restore set always has the same (nonzero!) token
-    private static final long RESTORE_TOKEN = 1;
+    // The currently-active restore set always has the same (nonzero!) token
+    private static final long CURRENT_SET_TOKEN = 1;
 
     private Context mContext;
     private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
+    private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
+
     private PackageInfo[] mRestorePackages = null;
     private int mRestorePackage = -1;  // Index into mRestorePackages
+    private File mRestoreDataDir;
+    private long mRestoreToken;
 
 
     public LocalTransport(Context context) {
         mContext = context;
-        mDataDir.mkdirs();
-        if (!SELinux.restorecon(mDataDir)) {
-            Log.e(TAG, "SELinux restorecon failed for " + mDataDir);
+        mCurrentSetDir.mkdirs();
+        if (!SELinux.restorecon(mCurrentSetDir)) {
+            Log.e(TAG, "SELinux restorecon failed for " + mCurrentSetDir);
         }
     }
 
@@ -96,7 +102,7 @@
 
     public int initializeDevice() {
         if (DEBUG) Log.v(TAG, "wiping all data");
-        deleteContents(mDataDir);
+        deleteContents(mCurrentSetDir);
         return BackupConstants.TRANSPORT_OK;
     }
 
@@ -112,7 +118,7 @@
             }
         }
 
-        File packageDir = new File(mDataDir, packageInfo.packageName);
+        File packageDir = new File(mCurrentSetDir, packageInfo.packageName);
         packageDir.mkdirs();
 
         // Each 'record' in the restore set is kept in its own file, named by
@@ -193,7 +199,7 @@
     public int clearBackupData(PackageInfo packageInfo) {
         if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
 
-        File packageDir = new File(mDataDir, packageInfo.packageName);
+        File packageDir = new File(mCurrentSetDir, packageInfo.packageName);
         final File[] fileset = packageDir.listFiles();
         if (fileset != null) {
             for (File f : fileset) {
@@ -210,22 +216,38 @@
     }
 
     // Restore handling
+    static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 }; 
     public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException {
-        // one hardcoded restore set
-        RestoreSet set = new RestoreSet("Local disk image", "flash", RESTORE_TOKEN);
-        RestoreSet[] array = { set };
-        return array;
+        long[] existing = new long[POSSIBLE_SETS.length + 1];
+        int num = 0;
+
+        // see which possible non-current sets exist, then put the current set at the end
+        for (long token : POSSIBLE_SETS) {
+            if ((new File(mDataDir, Long.toString(token))).exists()) {
+                existing[num++] = token;
+            }
+        }
+        // and always the currently-active set last
+        existing[num++] = CURRENT_SET_TOKEN;
+
+        RestoreSet[] available = new RestoreSet[num];
+        for (int i = 0; i < available.length; i++) {
+            available[i] = new RestoreSet("Local disk image", "flash", existing[i]);
+        }
+        return available;
     }
 
     public long getCurrentRestoreSet() {
-        // The hardcoded restore set always has the same token
-        return RESTORE_TOKEN;
+        // The current restore set always has the same token
+        return CURRENT_SET_TOKEN;
     }
 
     public int startRestore(long token, PackageInfo[] packages) {
         if (DEBUG) Log.v(TAG, "start restore " + token);
         mRestorePackages = packages;
         mRestorePackage = -1;
+        mRestoreToken = token;
+        mRestoreDataDir = new File(mDataDir, Long.toString(token));
         return BackupConstants.TRANSPORT_OK;
     }
 
@@ -234,7 +256,7 @@
         while (++mRestorePackage < mRestorePackages.length) {
             String name = mRestorePackages[mRestorePackage].packageName;
             // skip packages where we have a data dir but no actual contents
-            String[] contents = (new File(mDataDir, name)).list();
+            String[] contents = (new File(mRestoreDataDir, name)).list();
             if (contents != null && contents.length > 0) {
                 if (DEBUG) Log.v(TAG, "  nextRestorePackage() = " + name);
                 return name;
@@ -248,29 +270,32 @@
     public int getRestoreData(ParcelFileDescriptor outFd) {
         if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
         if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
-        File packageDir = new File(mDataDir, mRestorePackages[mRestorePackage].packageName);
+        File packageDir = new File(mRestoreDataDir, mRestorePackages[mRestorePackage].packageName);
 
         // The restore set is the concatenation of the individual record blobs,
-        // each of which is a file in the package's directory
-        File[] blobs = packageDir.listFiles();
+        // each of which is a file in the package's directory.  We return the
+        // data in lexical order sorted by key, so that apps which use synthetic
+        // keys like BLOB_1, BLOB_2, etc will see the date in the most obvious
+        // order.
+        ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
         if (blobs == null) {  // nextRestorePackage() ensures the dir exists, so this is an error
-            Log.e(TAG, "Error listing directory: " + packageDir);
+            Log.e(TAG, "No keys for package: " + packageDir);
             return BackupConstants.TRANSPORT_ERROR;
         }
 
         // We expect at least some data if the directory exists in the first place
-        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.length + " key files");
+        if (DEBUG) Log.v(TAG, "  getRestoreData() found " + blobs.size() + " key files");
         BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor());
         try {
-            for (File f : blobs) {
+            for (DecodedFilename keyEntry : blobs) {
+                File f = keyEntry.file;
                 FileInputStream in = new FileInputStream(f);
                 try {
                     int size = (int) f.length();
                     byte[] buf = new byte[size];
                     in.read(buf);
-                    String key = new String(Base64.decode(f.getName()));
-                    if (DEBUG) Log.v(TAG, "    ... key=" + key + " size=" + size);
-                    out.writeEntityHeader(key, size);
+                    if (DEBUG) Log.v(TAG, "    ... key=" + keyEntry.key + " size=" + size);
+                    out.writeEntityHeader(keyEntry.key, size);
                     out.writeEntityData(buf, size);
                 } finally {
                     in.close();
@@ -283,6 +308,39 @@
         }
     }
 
+    static class DecodedFilename implements Comparable<DecodedFilename> {
+        public File file;
+        public String key;
+
+        public DecodedFilename(File f) {
+            file = f;
+            key = new String(Base64.decode(f.getName()));
+        }
+
+        @Override
+        public int compareTo(DecodedFilename other) {
+            // sorts into ascending lexical order by decoded key
+            return key.compareTo(other.key);
+        }
+    }
+
+    // Return a list of the files in the given directory, sorted lexically by
+    // the Base64-decoded file name, not by the on-disk filename
+    private ArrayList<DecodedFilename> contentsByKey(File dir) {
+        File[] allFiles = dir.listFiles();
+        if (allFiles == null || allFiles.length == 0) {
+            return null;
+        }
+
+        // Decode the filenames into keys then sort lexically by key
+        ArrayList<DecodedFilename> contents = new ArrayList<DecodedFilename>();
+        for (File f : allFiles) {
+            contents.add(new DecodedFilename(f));
+        }
+        Collections.sort(contents);
+        return contents;
+    }
+
     public void finishRestore() {
         if (DEBUG) Log.v(TAG, "finishRestore()");
     }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 26d7f5f..1d88533 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -87,7 +87,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 99 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 101 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -191,7 +191,7 @@
 
     long mHistoryBaseTime;
     boolean mHaveBatteryLevel = false;
-    boolean mRecordingHistory = true;
+    boolean mRecordingHistory = false;
     int mNumHistoryItems;
 
     static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB
@@ -200,6 +200,7 @@
     final HistoryItem mHistoryLastWritten = new HistoryItem();
     final HistoryItem mHistoryLastLastWritten = new HistoryItem();
     final HistoryItem mHistoryReadTmp = new HistoryItem();
+    final HistoryItem mHistoryAddTmp = new HistoryItem();
     final HashMap<HistoryTag, Integer> mHistoryTagPool = new HashMap<HistoryTag, Integer>();
     String[] mReadHistoryStrings;
     int[] mReadHistoryUids;
@@ -208,7 +209,9 @@
     int mNumHistoryTagChars = 0;
     int mHistoryBufferLastPos = -1;
     boolean mHistoryOverflow = false;
-    long mLastHistoryTime = 0;
+    long mLastHistoryElapsedRealtime = 0;
+    long mTrackRunningHistoryElapsedRealtime = 0;
+    long mTrackRunningHistoryUptime = 0;
 
     final HistoryItem mHistoryCur = new HistoryItem();
 
@@ -286,6 +289,7 @@
     boolean mMobileRadioActive;
     StopwatchTimer mMobileRadioActiveTimer;
     StopwatchTimer mMobileRadioActivePerAppTimer;
+    LongSamplingCounter mMobileRadioActiveAdjustedTime;
     LongSamplingCounter mMobileRadioActiveUnknownTime;
     LongSamplingCounter mMobileRadioActiveUnknownCount;
 
@@ -329,12 +333,21 @@
     private final HashMap<String, SamplingTimer> mKernelWakelockStats =
             new HashMap<String, SamplingTimer>();
 
-    public Map<String, ? extends SamplingTimer> getKernelWakelockStats() {
+    public Map<String, ? extends Timer> getKernelWakelockStats() {
         return mKernelWakelockStats;
     }
 
     private static int sKernelWakelockUpdateVersion = 0;
 
+    String mLastWakeupReason = null;
+    long mLastWakeupUptimeMs = 0;
+    private final HashMap<String, LongSamplingCounter> mWakeupReasonStats =
+            new HashMap<String, LongSamplingCounter>();
+
+    public Map<String, ? extends LongCounter> getWakeupReasonStats() {
+        return mWakeupReasonStats;
+    }
+
     private static final int[] PROC_WAKELOCKS_FORMAT = new int[] {
         Process.PROC_TAB_TERM|Process.PROC_OUT_STRING|                // 0: name
                               Process.PROC_QUOTES,
@@ -718,7 +731,7 @@
         }
     }
 
-    public static class LongSamplingCounter implements TimeBaseObs {
+    public static class LongSamplingCounter extends LongCounter implements TimeBaseObs {
         final TimeBase mTimeBase;
         long mCount;
         long mLoadedCount;
@@ -774,6 +787,14 @@
             return val;
         }
 
+        @Override
+        public void logState(Printer pw, String prefix) {
+            pw.println(prefix + "mCount=" + mCount
+                    + " mLoadedCount=" + mLoadedCount + " mLastCount=" + mLastCount
+                    + " mUnpluggedCount=" + mUnpluggedCount
+                    + " mPluggedCount=" + mPluggedCount);
+        }
+
         void addCountLocked(long count) {
             mCount += count;
         }
@@ -1408,6 +1429,10 @@
             return 0;
         }
 
+        long getLastUpdateTimeMs() {
+            return mUpdateTime;
+        }
+
         void stopRunningLocked(long elapsedRealtimeMs) {
             // Ignore attempt to stop a timer that isn't running
             if (mNesting == 0) {
@@ -1501,6 +1526,19 @@
         }
     }
 
+    /*
+     * Get the wakeup reason counter, and create a new one if one
+     * doesn't already exist.
+     */
+    public LongSamplingCounter getWakeupReasonCounterLocked(String name) {
+        LongSamplingCounter counter = mWakeupReasonStats.get(name);
+        if (counter == null) {
+            counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase);
+            mWakeupReasonStats.put(name, counter);
+        }
+        return counter;
+    }
+
     private final Map<String, KernelWakelockStats> readKernelWakelockStats() {
 
         FileInputStream is;
@@ -1758,6 +1796,10 @@
         if (stateIntChanged) {
             firstToken |= DELTA_STATE_FLAG;
         }
+        final boolean state2IntChanged = cur.states2 != last.states2;
+        if (state2IntChanged) {
+            firstToken |= DELTA_STATE2_FLAG;
+        }
         if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
             firstToken |= DELTA_WAKELOCK_FLAG;
         }
@@ -1794,6 +1836,11 @@
                     + " batteryPlugType=" + cur.batteryPlugType
                     + " states=0x" + Integer.toHexString(cur.states));
         }
+        if (state2IntChanged) {
+            dest.writeInt(cur.states2);
+            if (DEBUG) Slog.i(TAG, "WRITE DELTA: states2=0x"
+                    + Integer.toHexString(cur.states2));
+        }
         if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
             int wakeLockIndex;
             int wakeReasonIndex;
@@ -1916,6 +1963,12 @@
             cur.states = (firstToken&DELTA_STATE_MASK) | (cur.states&(~DELTA_STATE_MASK));
         }
 
+        if ((firstToken&DELTA_STATE2_FLAG) != 0) {
+            cur.states2 = src.readInt();
+            if (DEBUG) Slog.i(TAG, "READ DELTA: states2=0x"
+                    + Integer.toHexString(cur.states2));
+        }
+
         if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) {
             int indexes = src.readInt();
             int wakeLockIndex = indexes&0xffff;
@@ -1957,59 +2010,64 @@
         }
     }
 
-    void addHistoryBufferLocked(long curTime) {
+    void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
         }
 
-        final long timeDiff = (mHistoryBaseTime+curTime) - mHistoryLastWritten.time;
-        final int diffStates = mHistoryLastWritten.states^mHistoryCur.states;
+        final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
+        final int diffStates = mHistoryLastWritten.states^cur.states;
+        final int diffStates2 = mHistoryLastWritten.states2^cur.states2;
         final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
+        final int lastDiffStates2 = mHistoryLastWritten.states2^mHistoryLastLastWritten.states2;
         if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
                 + Integer.toHexString(diffStates) + " lastDiff="
-                + Integer.toHexString(lastDiffStates));
+                + Integer.toHexString(lastDiffStates) + " diff2="
+                + Integer.toHexString(diffStates2) + " lastDiff2="
+                + Integer.toHexString(lastDiffStates2));
         if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
                 && timeDiff < 1000 && (diffStates&lastDiffStates) == 0
-                && (mHistoryLastWritten.wakelockTag == null || mHistoryCur.wakelockTag == null)
-                && (mHistoryLastWritten.wakeReasonTag == null || mHistoryCur.wakeReasonTag == null)
+                && (diffStates2&lastDiffStates2) == 0
+                && (mHistoryLastWritten.wakelockTag == null || cur.wakelockTag == null)
+                && (mHistoryLastWritten.wakeReasonTag == null || cur.wakeReasonTag == null)
                 && (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
-                        || mHistoryCur.eventCode == HistoryItem.EVENT_NONE)
-                && mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel
-                && mHistoryLastWritten.batteryStatus == mHistoryCur.batteryStatus
-                && mHistoryLastWritten.batteryHealth == mHistoryCur.batteryHealth
-                && mHistoryLastWritten.batteryPlugType == mHistoryCur.batteryPlugType
-                && mHistoryLastWritten.batteryTemperature == mHistoryCur.batteryTemperature
-                && mHistoryLastWritten.batteryVoltage == mHistoryCur.batteryVoltage) {
+                        || cur.eventCode == HistoryItem.EVENT_NONE)
+                && mHistoryLastWritten.batteryLevel == cur.batteryLevel
+                && mHistoryLastWritten.batteryStatus == cur.batteryStatus
+                && mHistoryLastWritten.batteryHealth == cur.batteryHealth
+                && mHistoryLastWritten.batteryPlugType == cur.batteryPlugType
+                && mHistoryLastWritten.batteryTemperature == cur.batteryTemperature
+                && mHistoryLastWritten.batteryVoltage == cur.batteryVoltage) {
             // We can merge this new change in with the last one.  Merging is
-            // allows as long as only the states have changed, and within those states
+            // allowed as long as only the states have changed, and within those states
             // as long as no bit has changed both between now and the last entry, as
             // well as the last entry and the one before it (so we capture any toggles).
             if (DEBUG) Slog.i(TAG, "ADD: rewinding back to " + mHistoryBufferLastPos);
             mHistoryBuffer.setDataSize(mHistoryBufferLastPos);
             mHistoryBuffer.setDataPosition(mHistoryBufferLastPos);
             mHistoryBufferLastPos = -1;
-            curTime = mHistoryLastWritten.time - mHistoryBaseTime;
+            elapsedRealtimeMs = mHistoryLastWritten.time - mHistoryBaseTime;
             // If the last written history had a wakelock tag, we need to retain it.
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have a wakelock tag.
             if (mHistoryLastWritten.wakelockTag != null) {
-                mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
-                mHistoryCur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
+                cur.wakelockTag = cur.localWakelockTag;
+                cur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
             }
             // If the last written history had a wake reason tag, we need to retain it.
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have a wakelock tag.
             if (mHistoryLastWritten.wakeReasonTag != null) {
-                mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
-                mHistoryCur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
+                cur.wakeReasonTag = cur.localWakeReasonTag;
+                cur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
             }
             // If the last written history had an event, we need to retain it.
             // Note that the condition above made sure that we aren't in a case where
             // both it and the current history item have an event.
             if (mHistoryLastWritten.eventCode != HistoryItem.EVENT_NONE) {
-                mHistoryCur.eventCode = mHistoryLastWritten.eventCode;
-                mHistoryCur.eventTag = mHistoryCur.localEventTag;
-                mHistoryCur.eventTag.setTo(mHistoryLastWritten.eventTag);
+                cur.eventCode = mHistoryLastWritten.eventCode;
+                cur.eventTag = cur.localEventTag;
+                cur.eventTag.setTo(mHistoryLastWritten.eventTag);
             }
             mHistoryLastWritten.setTo(mHistoryLastLastWritten);
         }
@@ -2018,8 +2076,8 @@
         if (dataSize >= MAX_HISTORY_BUFFER) {
             if (!mHistoryOverflow) {
                 mHistoryOverflow = true;
-                addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
-                addHistoryBufferLocked(curTime, HistoryItem.CMD_OVERFLOW);
+                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
+                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW, cur);
                 return;
             }
 
@@ -2027,42 +2085,64 @@
             // record changes to the battery level and the most interesting states.
             // Once we've reached the maximum maximum number of items, we only
             // record changes to the battery level.
-            if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
+            if (mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
                     (dataSize >= MAX_MAX_HISTORY_BUFFER
-                            || ((mHistoryLastWritten.states^mHistoryCur.states)
+                            || ((mHistoryLastWritten.states^cur.states)
                                     & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                 return;
             }
 
-            addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+            addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
             return;
         }
 
-        addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE, cur);
     }
 
-    private void addHistoryBufferLocked(long curTime, byte cmd) {
+    private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd,
+            HistoryItem cur) {
         if (mIteratingHistory) {
             throw new IllegalStateException("Can't do this while iterating history!");
         }
         mHistoryBufferLastPos = mHistoryBuffer.dataPosition();
         mHistoryLastLastWritten.setTo(mHistoryLastWritten);
-        mHistoryLastWritten.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+        mHistoryLastWritten.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
         writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
-        mLastHistoryTime = curTime;
-        mHistoryCur.wakelockTag = null;
-        mHistoryCur.wakeReasonTag = null;
-        mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
-        mHistoryCur.eventTag = null;
+        mLastHistoryElapsedRealtime = elapsedRealtimeMs;
+        cur.wakelockTag = null;
+        cur.wakeReasonTag = null;
+        cur.eventCode = HistoryItem.EVENT_NONE;
+        cur.eventTag = null;
         if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
                 + " now " + mHistoryBuffer.dataPosition()
                 + " size is now " + mHistoryBuffer.dataSize());
     }
 
     int mChangedStates = 0;
+    int mChangedStates2 = 0;
 
-    void addHistoryRecordLocked(long curTime) {
-        addHistoryBufferLocked(curTime);
+    void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
+        if (mTrackRunningHistoryElapsedRealtime != 0) {
+            final long diffElapsed = elapsedRealtimeMs - mTrackRunningHistoryElapsedRealtime;
+            final long diffUptime = uptimeMs - mTrackRunningHistoryUptime;
+            if (diffUptime < (diffElapsed-20)) {
+                final long wakeElapsedTime = elapsedRealtimeMs - (diffElapsed - diffUptime);
+                mHistoryAddTmp.setTo(mHistoryLastWritten);
+                mHistoryAddTmp.wakelockTag = null;
+                mHistoryAddTmp.wakeReasonTag = null;
+                mHistoryAddTmp.eventCode = HistoryItem.EVENT_NONE;
+                mHistoryAddTmp.states &= ~HistoryItem.STATE_CPU_RUNNING_FLAG;
+                addHistoryRecordInnerLocked(wakeElapsedTime, uptimeMs, mHistoryAddTmp);
+            }
+        }
+        mHistoryCur.states |= HistoryItem.STATE_CPU_RUNNING_FLAG;
+        mTrackRunningHistoryElapsedRealtime = elapsedRealtimeMs;
+        mTrackRunningHistoryUptime = uptimeMs;
+        addHistoryRecordInnerLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
+    }
+
+    void addHistoryRecordInnerLocked(long elapsedRealtimeMs, long uptimeMs, HistoryItem cur) {
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, cur);
 
         if (!USE_OLD_HISTORY) {
             return;
@@ -2077,30 +2157,33 @@
         // are now resetting back to their original value, then just collapse
         // into one record.
         if (mHistoryEnd != null && mHistoryEnd.cmd == HistoryItem.CMD_UPDATE
-                && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+2000)
-                && ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 0) {
+                && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
+                && ((mHistoryEnd.states^cur.states)&mChangedStates) == 0
+                && ((mHistoryEnd.states2^cur.states2)&mChangedStates2) == 0) {
             // If the current is the same as the one before, then we no
             // longer need the entry.
             if (mHistoryLastEnd != null && mHistoryLastEnd.cmd == HistoryItem.CMD_UPDATE
-                    && (mHistoryBaseTime+curTime) < (mHistoryEnd.time+500)
-                    && mHistoryLastEnd.sameNonEvent(mHistoryCur)) {
+                    && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+500)
+                    && mHistoryLastEnd.sameNonEvent(cur)) {
                 mHistoryLastEnd.next = null;
                 mHistoryEnd.next = mHistoryCache;
                 mHistoryCache = mHistoryEnd;
                 mHistoryEnd = mHistoryLastEnd;
                 mHistoryLastEnd = null;
             } else {
-                mChangedStates |= mHistoryEnd.states^mHistoryCur.states;
-                mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, mHistoryCur);
+                mChangedStates |= mHistoryEnd.states^cur.states;
+                mChangedStates2 |= mHistoryEnd.states^cur.states2;
+                mHistoryEnd.setTo(mHistoryEnd.time, HistoryItem.CMD_UPDATE, cur);
             }
             return;
         }
 
         mChangedStates = 0;
+        mChangedStates2 = 0;
 
         if (mNumHistoryItems == MAX_HISTORY_ITEMS
                 || mNumHistoryItems == MAX_MAX_HISTORY_ITEMS) {
-            addHistoryRecordLocked(curTime, HistoryItem.CMD_OVERFLOW);
+            addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_OVERFLOW);
         }
 
         if (mNumHistoryItems >= MAX_HISTORY_ITEMS) {
@@ -2109,33 +2192,35 @@
             // Once we've reached the maximum maximum number of items, we only
             // record changes to the battery level.
             if (mHistoryEnd != null && mHistoryEnd.batteryLevel
-                    == mHistoryCur.batteryLevel &&
+                    == cur.batteryLevel &&
                     (mNumHistoryItems >= MAX_MAX_HISTORY_ITEMS
-                            || ((mHistoryEnd.states^mHistoryCur.states)
+                            || ((mHistoryEnd.states^cur.states)
                                     & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
                 return;
             }
         }
 
-        addHistoryRecordLocked(curTime, HistoryItem.CMD_UPDATE);
+        addHistoryRecordLocked(elapsedRealtimeMs, HistoryItem.CMD_UPDATE);
     }
 
-    void addHistoryEventLocked(long curTime, int code, String name, int uid) {
+    void addHistoryEventLocked(long elapsedRealtimeMs, long uptimeMs, int code,
+            String name, int uid) {
         mHistoryCur.eventCode = code;
         mHistoryCur.eventTag = mHistoryCur.localEventTag;
         mHistoryCur.eventTag.string = name;
         mHistoryCur.eventTag.uid = uid;
-        addHistoryBufferLocked(curTime);
+        // XXX should be calling addHistoryRecordLocked()?
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, mHistoryCur);
     }
 
-    void addHistoryRecordLocked(long curTime, byte cmd) {
+    void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd, HistoryItem cur) {
         HistoryItem rec = mHistoryCache;
         if (rec != null) {
             mHistoryCache = rec.next;
         } else {
             rec = new HistoryItem();
         }
-        rec.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+        rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, cur);
 
         addHistoryRecordLocked(rec);
     }
@@ -2164,7 +2249,9 @@
         }
 
         mHistoryBaseTime = 0;
-        mLastHistoryTime = 0;
+        mLastHistoryElapsedRealtime = 0;
+        mTrackRunningHistoryElapsedRealtime = 0;
+        mTrackRunningHistoryUptime = 0;
 
         mHistoryBuffer.setDataSize(0);
         mHistoryBuffer.setDataPosition(0);
@@ -2262,7 +2349,9 @@
                 active.remove(name);
             }
         }
-        addHistoryEventLocked(SystemClock.elapsedRealtime(), code, name, uid);
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
+        addHistoryEventLocked(elapsedRealtime, uptime, code, name, uid);
     }
 
     private void requestWakelockCpuUpdate() {
@@ -2273,11 +2362,12 @@
     }
 
     public void noteStartWakeLocked(int uid, int pid, String name, String historyName, int type,
-            boolean unimportantForLogging, long elapsedRealtime) {
+            boolean unimportantForLogging, long elapsedRealtime, long uptime) {
         uid = mapUid(uid);
         if (type == WAKE_TYPE_PARTIAL) {
             // Only care about partial wake locks, since full wake locks
             // will be canceled when the user puts the screen to sleep.
+            aggregateLastWakeupUptimeLocked(uptime);
             if (mWakeLockNesting == 0) {
                 mHistoryCur.states |= HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Start wake lock to: "
@@ -2286,7 +2376,7 @@
                 mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
                 mHistoryCur.wakelockTag.uid = uid;
                 mWakeLockImportant = !unimportantForLogging;
-                addHistoryRecordLocked(elapsedRealtime);
+                addHistoryRecordLocked(elapsedRealtime, uptime);
             } else if (!mWakeLockImportant && !unimportantForLogging) {
                 if (mHistoryLastWritten.wakelockTag != null) {
                     // We'll try to update the last tag.
@@ -2294,19 +2384,23 @@
                     mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
                     mHistoryCur.wakelockTag.string = historyName != null ? historyName : name;
                     mHistoryCur.wakelockTag.uid = uid;
-                    addHistoryRecordLocked(elapsedRealtime);
+                    addHistoryRecordLocked(elapsedRealtime, uptime);
                 }
                 mWakeLockImportant = true;
             }
             mWakeLockNesting++;
         }
         if (uid >= 0) {
+            //if (uid == 0) {
+            //    Slog.wtf(TAG, "Acquiring wake lock from root: " + name);
+            //}
             requestWakelockCpuUpdate();
             getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime);
         }
     }
 
-    public void noteStopWakeLocked(int uid, int pid, String name, int type, long elapsedRealtime) {
+    public void noteStopWakeLocked(int uid, int pid, String name, int type, long elapsedRealtime,
+            long uptime) {
         uid = mapUid(uid);
         if (type == WAKE_TYPE_PARTIAL) {
             mWakeLockNesting--;
@@ -2314,7 +2408,7 @@
                 mHistoryCur.states &= ~HistoryItem.STATE_WAKE_LOCK_FLAG;
                 if (DEBUG_HISTORY) Slog.v(TAG, "Stop wake lock to: "
                         + Integer.toHexString(mHistoryCur.states));
-                addHistoryRecordLocked(elapsedRealtime);
+                addHistoryRecordLocked(elapsedRealtime, uptime);
             }
         }
         if (uid >= 0) {
@@ -2326,10 +2420,11 @@
     public void noteStartWakeFromSourceLocked(WorkSource ws, int pid, String name,
             String historyName, int type, boolean unimportantForLogging) {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         final int N = ws.size();
         for (int i=0; i<N; i++) {
             noteStartWakeLocked(ws.get(i), pid, name, historyName, type, unimportantForLogging,
-                    elapsedRealtime);
+                    elapsedRealtime, uptime);
         }
     }
 
@@ -2337,36 +2432,51 @@
             WorkSource newWs, int newPid, String newName,
             String newHistoryName, int newType, boolean newUnimportantForLogging) {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         // For correct semantics, we start the need worksources first, so that we won't
         // make inappropriate history items as if all wake locks went away and new ones
         // appeared.  This is okay because tracking of wake locks allows nesting.
-        final int NN = ws.size();
+        final int NN = newWs.size();
         for (int i=0; i<NN; i++) {
             noteStartWakeLocked(newWs.get(i), newPid, newName, newHistoryName, newType,
-                    newUnimportantForLogging, elapsedRealtime);
+                    newUnimportantForLogging, elapsedRealtime, uptime);
         }
         final int NO = ws.size();
         for (int i=0; i<NO; i++) {
-            noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime);
+            noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime, uptime);
         }
     }
 
     public void noteStopWakeFromSourceLocked(WorkSource ws, int pid, String name, int type) {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         final int N = ws.size();
         for (int i=0; i<N; i++) {
-            noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime);
+            noteStopWakeLocked(ws.get(i), pid, name, type, elapsedRealtime, uptime);
         }
     }
 
-    public void noteWakeupReasonLocked(int irq, String reason) {
+    void aggregateLastWakeupUptimeLocked(long uptimeMs) {
+        if (mLastWakeupReason != null) {
+            long deltaUptime = uptimeMs - mLastWakeupUptimeMs;
+            LongSamplingCounter timer = getWakeupReasonCounterLocked(mLastWakeupReason);
+            timer.addCountLocked(deltaUptime);
+            mLastWakeupReason = null;
+        }
+    }
+
+    public void noteWakeupReasonLocked(String reason) {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
-        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
+        final long uptime = SystemClock.uptimeMillis();
+        if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason reason \"" + reason +"\": "
                 + Integer.toHexString(mHistoryCur.states));
+        aggregateLastWakeupUptimeLocked(uptime);
         mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
         mHistoryCur.wakeReasonTag.string = reason;
-        mHistoryCur.wakeReasonTag.uid = irq;
-        addHistoryRecordLocked(elapsedRealtime);
+        mHistoryCur.wakeReasonTag.uid = 0;
+        mLastWakeupReason = reason;
+        mLastWakeupUptimeMs = uptime;
+        addHistoryRecordLocked(elapsedRealtime, uptime);
     }
 
     public int startAddingCpuLocked() {
@@ -2511,11 +2621,12 @@
     public void noteStartSensorLocked(int uid, int sensor) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (mSensorNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_SENSOR_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Start sensor to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         mSensorNesting++;
         getUidStatsLocked(uid).noteStartSensor(sensor, elapsedRealtime);
@@ -2524,12 +2635,13 @@
     public void noteStopSensorLocked(int uid, int sensor) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         mSensorNesting--;
         if (mSensorNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_SENSOR_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Stop sensor to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         getUidStatsLocked(uid).noteStopSensor(sensor, elapsedRealtime);
     }
@@ -2539,11 +2651,12 @@
     public void noteStartGpsLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (mGpsNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         mGpsNesting++;
         getUidStatsLocked(uid).noteStartGps(elapsedRealtime);
@@ -2552,12 +2665,13 @@
     public void noteStopGpsLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         mGpsNesting--;
         if (mGpsNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Stop GPS to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         getUidStatsLocked(uid).noteStopGps(elapsedRealtime);
     }
@@ -2565,10 +2679,11 @@
     public void noteScreenOnLocked() {
         if (!mScreenOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states |= HistoryItem.STATE_SCREEN_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mScreenOn = true;
             mScreenOnTimer.startRunningLocked(elapsedRealtime);
             if (mScreenBrightnessBin >= 0) {
@@ -2580,7 +2695,8 @@
 
             // Fake a wake lock, so we consider the device waked as long
             // as the screen is on.
-            noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false, elapsedRealtime);
+            noteStartWakeLocked(-1, -1, "screen", null, WAKE_TYPE_PARTIAL, false,
+                    elapsedRealtime, uptime);
 
             // Update discharge amounts.
             if (mOnBatteryInternal) {
@@ -2592,17 +2708,19 @@
     public void noteScreenOffLocked() {
         if (mScreenOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mScreenOn = false;
             mScreenOnTimer.stopRunningLocked(elapsedRealtime);
             if (mScreenBrightnessBin >= 0) {
                 mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
             }
 
-            noteStopWakeLocked(-1, -1, "screen", WAKE_TYPE_PARTIAL, elapsedRealtime);
+            noteStopWakeLocked(-1, -1, "screen", WAKE_TYPE_PARTIAL,
+                    elapsedRealtime, uptime);
 
             updateTimeBasesLocked(mOnBatteryTimeBase.isRunning(), true,
                     SystemClock.uptimeMillis() * 1000, elapsedRealtime * 1000);
@@ -2621,11 +2739,12 @@
         else if (bin >= NUM_SCREEN_BRIGHTNESS_BINS) bin = NUM_SCREEN_BRIGHTNESS_BINS-1;
         if (mScreenBrightnessBin != bin) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_BRIGHTNESS_MASK)
                     | (bin << HistoryItem.STATE_BRIGHTNESS_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Screen brightness " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             if (mScreenOn) {
                 if (mScreenBrightnessBin >= 0) {
                     mScreenBrightnessTimer[mScreenBrightnessBin].stopRunningLocked(elapsedRealtime);
@@ -2647,23 +2766,39 @@
         }
     }
 
-    public void noteDataConnectionActive(int type, boolean active) {
+    public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
         if (ConnectivityManager.isNetworkTypeMobile(type)) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             if (mMobileRadioActive != active) {
-                if (active) mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
-                else mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                long realElapsedRealtimeMs;
+                if (active) {
+                    realElapsedRealtimeMs = elapsedRealtime;
+                    mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                } else {
+                    realElapsedRealtimeMs = timestampNs / (1000*1000);
+                    long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
+                    if (realElapsedRealtimeMs < lastUpdateTimeMs) {
+                        Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
+                                + " is before start time " + lastUpdateTimeMs);
+                        realElapsedRealtimeMs = elapsedRealtime;
+                    } else if (realElapsedRealtimeMs < elapsedRealtime) {
+                        mMobileRadioActiveAdjustedTime.addCountLocked(elapsedRealtime
+                                - realElapsedRealtimeMs);
+                    }
+                    mHistoryCur.states &= ~HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
+                }
                 if (DEBUG_HISTORY) Slog.v(TAG, "Mobile network active " + active + " to: "
                         + Integer.toHexString(mHistoryCur.states));
-                addHistoryRecordLocked(elapsedRealtime);
+                addHistoryRecordLocked(elapsedRealtime, uptime);
                 mMobileRadioActive = active;
                 if (active) {
                     mMobileRadioActiveTimer.startRunningLocked(elapsedRealtime);
                     mMobileRadioActivePerAppTimer.startRunningLocked(elapsedRealtime);
                 } else {
-                    updateNetworkActivityLocked(NET_UPDATE_MOBILE, elapsedRealtime);
-                    mMobileRadioActiveTimer.stopRunningLocked(elapsedRealtime);
-                    mMobileRadioActivePerAppTimer.stopRunningLocked(elapsedRealtime);
+                    mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
+                    mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
+                    updateNetworkActivityLocked(NET_UPDATE_MOBILE, realElapsedRealtimeMs);
                 }
             }
         }
@@ -2672,10 +2807,11 @@
     public void notePhoneOnLocked() {
         if (!mPhoneOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states |= HistoryItem.STATE_PHONE_IN_CALL_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mPhoneOn = true;
             mPhoneOnTimer.startRunningLocked(elapsedRealtime);
         }
@@ -2684,10 +2820,11 @@
     public void notePhoneOffLocked() {
         if (mPhoneOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states &= ~HistoryItem.STATE_PHONE_IN_CALL_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Phone off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mPhoneOn = false;
             mPhoneOnTimer.stopRunningLocked(elapsedRealtime);
         }
@@ -2727,6 +2864,7 @@
         mPhoneSignalStrengthBinRaw = strengthBin;
 
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
 
         if (simState == TelephonyManager.SIM_STATE_ABSENT) {
             // In this case we will always be STATE_OUT_OF_SERVICE, so need
@@ -2800,7 +2938,7 @@
         }
 
         if (newHistory) {
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
     }
 
@@ -2875,11 +3013,12 @@
         if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData);
         if (mPhoneDataConnectionType != bin) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_DATA_CONNECTION_MASK)
                     | (bin << HistoryItem.STATE_DATA_CONNECTION_SHIFT);
             if (DEBUG_HISTORY) Slog.v(TAG, "Data connection " + bin + " to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             if (mPhoneDataConnectionType >= 0) {
                 mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(
                         elapsedRealtime);
@@ -2892,10 +3031,11 @@
     public void noteWifiOnLocked() {
         if (!mWifiOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states |= HistoryItem.STATE_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mWifiOn = true;
             mWifiOnTimer.startRunningLocked(elapsedRealtime);
         }
@@ -2903,11 +3043,12 @@
 
     public void noteWifiOffLocked() {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (mWifiOn) {
             mHistoryCur.states &= ~HistoryItem.STATE_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mWifiOn = false;
             mWifiOnTimer.stopRunningLocked(elapsedRealtime);
         }
@@ -2916,11 +3057,12 @@
     public void noteAudioOnLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (!mAudioOn) {
             mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mAudioOn = true;
             mAudioOnTimer.startRunningLocked(elapsedRealtime);
         }
@@ -2930,11 +3072,12 @@
     public void noteAudioOffLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (mAudioOn) {
             mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mAudioOn = false;
             mAudioOnTimer.stopRunningLocked(elapsedRealtime);
         }
@@ -2944,11 +3087,12 @@
     public void noteVideoOnLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (!mVideoOn) {
-            mHistoryCur.states |= HistoryItem.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mVideoOn = true;
             mVideoOnTimer.startRunningLocked(elapsedRealtime);
         }
@@ -2958,11 +3102,12 @@
     public void noteVideoOffLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (mVideoOn) {
-            mHistoryCur.states &= ~HistoryItem.STATE_VIDEO_ON_FLAG;
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mVideoOn = false;
             mVideoOnTimer.stopRunningLocked(elapsedRealtime);
         }
@@ -2992,10 +3137,11 @@
     public void noteWifiRunningLocked(WorkSource ws) {
         if (!mGlobalWifiRunning) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(SystemClock.elapsedRealtime());
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mGlobalWifiRunning = true;
             mGlobalWifiRunningTimer.startRunningLocked(elapsedRealtime);
             int N = ws.size();
@@ -3029,10 +3175,11 @@
     public void noteWifiStoppedLocked(WorkSource ws) {
         if (mGlobalWifiRunning) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mGlobalWifiRunning = false;
             mGlobalWifiRunningTimer.stopRunningLocked(elapsedRealtime);
             int N = ws.size();
@@ -3060,10 +3207,11 @@
     public void noteBluetoothOnLocked() {
         if (!mBluetoothOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states |= HistoryItem.STATE_BLUETOOTH_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mBluetoothOn = true;
             mBluetoothOnTimer.startRunningLocked(elapsedRealtime);
         }
@@ -3072,10 +3220,11 @@
     public void noteBluetoothOffLocked() {
         if (mBluetoothOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             mHistoryCur.states &= ~HistoryItem.STATE_BLUETOOTH_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Bluetooth off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
             mBluetoothOn = false;
             mBluetoothOnTimer.stopRunningLocked(elapsedRealtime);
         }
@@ -3098,11 +3247,12 @@
     public void noteFullWifiLockAcquiredLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (mWifiFullLockNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         mWifiFullLockNesting++;
         getUidStatsLocked(uid).noteFullWifiLockAcquiredLocked(elapsedRealtime);
@@ -3111,12 +3261,13 @@
     public void noteFullWifiLockReleasedLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         mWifiFullLockNesting--;
         if (mWifiFullLockNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_WIFI_FULL_LOCK_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI full lock off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         getUidStatsLocked(uid).noteFullWifiLockReleasedLocked(elapsedRealtime);
     }
@@ -3126,11 +3277,12 @@
     public void noteWifiScanStartedLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (mWifiScanNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_SCAN_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan started for: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         mWifiScanNesting++;
         getUidStatsLocked(uid).noteWifiScanStartedLocked(elapsedRealtime);
@@ -3139,12 +3291,13 @@
     public void noteWifiScanStoppedLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         mWifiScanNesting--;
         if (mWifiScanNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_WIFI_SCAN_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI scan stopped for: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         getUidStatsLocked(uid).noteWifiScanStoppedLocked(elapsedRealtime);
     }
@@ -3166,11 +3319,12 @@
     public void noteWifiMulticastEnabledLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (mWifiMulticastNesting == 0) {
             mHistoryCur.states |= HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast on to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         mWifiMulticastNesting++;
         getUidStatsLocked(uid).noteWifiMulticastEnabledLocked(elapsedRealtime);
@@ -3179,12 +3333,13 @@
     public void noteWifiMulticastDisabledLocked(int uid) {
         uid = mapUid(uid);
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         mWifiMulticastNesting--;
         if (mWifiMulticastNesting == 0) {
             mHistoryCur.states &= ~HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI multicast off to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
         getUidStatsLocked(uid).noteWifiMulticastDisabledLocked(elapsedRealtime);
     }
@@ -3354,6 +3509,10 @@
         return mMobileRadioActiveTimer.getCountLocked(which);
     }
 
+    @Override public long getMobileRadioActiveAdjustedTime(int which) {
+        return mMobileRadioActiveAdjustedTime.getCountLocked(which);
+    }
+
     @Override public long getMobileRadioActiveUnknownTime(int which) {
         return mMobileRadioActiveUnknownTime.getCountLocked(which);
     }
@@ -5414,6 +5573,7 @@
         }
         mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase);
+        mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase);
         mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase);
         mWifiOnTimer = new StopwatchTimer(null, -3, null, mOnBatteryTimeBase);
@@ -5499,9 +5659,9 @@
                 PrintWriter pw = new FastPrintWriter(new LogWriter(android.util.Log.WARN, TAG));
                 pw.println("Histories differ!");
                 pw.println("Old history:");
-                (new HistoryPrinter()).printNextItem(pw, out, now, false);
+                (new HistoryPrinter()).printNextItem(pw, out, now, false, true);
                 pw.println("New history:");
-                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false);
+                (new HistoryPrinter()).printNextItem(pw, mHistoryReadTmp, now, false, true);
                 pw.flush();
             }
         }
@@ -5627,12 +5787,13 @@
 
     public void resetAllStatsCmdLocked() {
         resetAllStatsLocked();
-        long uptime = SystemClock.uptimeMillis() * 1000;
+        final long mSecUptime = SystemClock.uptimeMillis();
+        long uptime = mSecUptime * 1000;
         long mSecRealtime = SystemClock.elapsedRealtime();
         long realtime = mSecRealtime * 1000;
         mDischargeStartLevel = mHistoryCur.batteryLevel;
         pullPendingStateUpdatesLocked();
-        addHistoryRecordLocked(mSecRealtime);
+        addHistoryRecordLocked(mSecRealtime, mSecUptime);
         mDischargeCurrentLevel = mDischargeUnplugLevel = mHistoryCur.batteryLevel;
         mOnBatteryTimeBase.reset(uptime, realtime);
         mOnBatteryScreenOffTimeBase.reset(uptime, realtime);
@@ -5647,7 +5808,7 @@
             mDischargeAmountScreenOn = 0;
             mDischargeAmountScreenOff = 0;
         }
-        initActiveHistoryEventsLocked(mSecRealtime);
+        initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
     }
 
     private void resetAllStatsLocked() {
@@ -5674,6 +5835,7 @@
         }
         mMobileRadioActiveTimer.reset(false);
         mMobileRadioActivePerAppTimer.reset(false);
+        mMobileRadioActiveAdjustedTime.reset(false);
         mMobileRadioActiveUnknownTime.reset(false);
         mMobileRadioActiveUnknownCount.reset(false);
         mWifiOnTimer.reset(false);
@@ -5699,13 +5861,20 @@
             }
             mKernelWakelockStats.clear();
         }
-        
+
+        if (mWakeupReasonStats.size() > 0) {
+            for (LongSamplingCounter timer : mWakeupReasonStats.values()) {
+                mOnBatteryScreenOffTimeBase.remove(timer);
+            }
+            mWakeupReasonStats.clear();
+        }
+
         initDischarge();
 
         clearHistoryLocked();
     }
 
-    private void initActiveHistoryEventsLocked(long nowRealtime) {
+    private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) {
         for (int i=0; i<HistoryItem.EVENT_COUNT; i++) {
             HashMap<String, SparseBooleanArray> active = mActiveEvents[i];
             if (active == null) {
@@ -5715,7 +5884,8 @@
                 SparseBooleanArray uids = ent.getValue();
                 for (int j=0; j<uids.size(); j++) {
                     if (uids.valueAt(j)) {
-                        addHistoryEventLocked(nowRealtime, i, ent.getKey(), uids.keyAt(j));
+                        addHistoryEventLocked(elapsedRealtimeMs, uptimeMs, i, ent.getKey(),
+                                uids.keyAt(j));
                     }
                 }
             }
@@ -5753,16 +5923,16 @@
         }
     }
 
-    void setOnBatteryLocked(boolean onBattery, int oldStatus, int level) {
+    void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery,
+            final int oldStatus, final int level) {
         boolean doWrite = false;
         Message m = mHandler.obtainMessage(MSG_REPORT_POWER_CHANGE);
         m.arg1 = onBattery ? 1 : 0;
         mHandler.sendMessage(m);
         mOnBattery = mOnBatteryInternal = onBattery;
 
-        long uptime = SystemClock.uptimeMillis() * 1000;
-        long mSecRealtime = SystemClock.elapsedRealtime();
-        long realtime = mSecRealtime * 1000;
+        final long uptime = mSecUptime * 1000;
+        final long realtime = mSecRealtime * 1000;
         if (onBattery) {
             // We will reset our status if we are unplugging after the
             // battery was last full, or the level is at 100, or
@@ -5782,10 +5952,11 @@
             mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
                     + Integer.toHexString(mHistoryCur.states));
-            mHistoryCur.currentTime = System.currentTimeMillis();
-            addHistoryBufferLocked(mSecRealtime, HistoryItem.CMD_CURRENT_TIME);
-            mHistoryCur.currentTime = 0;
-            addHistoryRecordLocked(mSecRealtime);
+            if (reset) {
+                mRecordingHistory = true;
+                startRecordingHistory(mSecRealtime, mSecUptime, reset);
+            }
+            addHistoryRecordLocked(mSecRealtime, mSecUptime);
             mDischargeCurrentLevel = mDischargeUnplugLevel = level;
             if (mScreenOn) {
                 mDischargeScreenOnUnplugLevel = level;
@@ -5797,16 +5968,13 @@
             mDischargeAmountScreenOn = 0;
             mDischargeAmountScreenOff = 0;
             updateTimeBasesLocked(true, !mScreenOn, uptime, realtime);
-            if (reset) {
-                initActiveHistoryEventsLocked(mSecRealtime);
-            }
         } else {
             pullPendingStateUpdatesLocked();
             mHistoryCur.batteryLevel = (byte)level;
             mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery plugged to: "
                     + Integer.toHexString(mHistoryCur.states));
-            addHistoryRecordLocked(mSecRealtime);
+            addHistoryRecordLocked(mSecRealtime, mSecUptime);
             mDischargeCurrentLevel = level;
             if (level < mDischargeUnplugLevel) {
                 mLowDischargeAmountSinceCharge += mDischargeUnplugLevel-level-1;
@@ -5822,13 +5990,27 @@
         }
     }
 
+    private void startRecordingHistory(final long elapsedRealtimeMs, final long uptimeMs,
+            boolean reset) {
+        mRecordingHistory = true;
+        mHistoryCur.currentTime = System.currentTimeMillis();
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_CURRENT_TIME,
+                mHistoryCur);
+        mHistoryCur.currentTime = 0;
+        if (reset) {
+            initActiveHistoryEventsLocked(elapsedRealtimeMs, uptimeMs);
+        }
+    }
+
     // This should probably be exposed in the API, though it's not critical
     private static final int BATTERY_PLUGGED_NONE = 0;
 
     public void setBatteryState(int status, int health, int plugType, int level,
             int temp, int volt) {
         synchronized(this) {
-            boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+            final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+            final long uptime = SystemClock.uptimeMillis();
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
             int oldStatus = mHistoryCur.batteryStatus;
             if (!mHaveBatteryLevel) {
                 mHaveBatteryLevel = true;
@@ -5847,7 +6029,15 @@
             }
             if (onBattery) {
                 mDischargeCurrentLevel = level;
-                mRecordingHistory = true;
+                if (!mRecordingHistory) {
+                    mRecordingHistory = true;
+                    startRecordingHistory(elapsedRealtime, uptime, true);
+                }
+            } else if (level < 96) {
+                if (!mRecordingHistory) {
+                    mRecordingHistory = true;
+                    startRecordingHistory(elapsedRealtime, uptime, true);
+                }
             }
             if (onBattery != mOnBattery) {
                 mHistoryCur.batteryLevel = (byte)level;
@@ -5856,7 +6046,7 @@
                 mHistoryCur.batteryPlugType = (byte)plugType;
                 mHistoryCur.batteryTemperature = (short)temp;
                 mHistoryCur.batteryVoltage = (char)volt;
-                setOnBatteryLocked(onBattery, oldStatus, level);
+                setOnBatteryLocked(elapsedRealtime, uptime, onBattery, oldStatus, level);
             } else {
                 boolean changed = false;
                 if (mHistoryCur.batteryLevel != level) {
@@ -5886,7 +6076,7 @@
                     changed = true;
                 }
                 if (changed) {
-                    addHistoryRecordLocked(SystemClock.elapsedRealtime());
+                    addHistoryRecordLocked(elapsedRealtime, uptime);
                 }
             }
             if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
@@ -6161,7 +6351,18 @@
             return val;
         }
     }
-    
+
+    @Override
+    public int getDischargeAmount(int which) {
+        int dischargeAmount = which == STATS_SINCE_CHARGED
+                ? getHighDischargeAmountSinceCharge()
+                : (getDischargeStartLevel() - getDischargeCurrentLevel());
+        if (dischargeAmount < 0) {
+            dischargeAmount = 0;
+        }
+        return dischargeAmount;
+    }
+
     public int getDischargeAmountScreenOn() {
         synchronized(this) {
             int val = mDischargeAmountScreenOn;
@@ -6438,14 +6639,14 @@
         }
 
         if (mHistoryBuffer.dataPosition() > 0) {
-            long now = SystemClock.elapsedRealtime();
+            mRecordingHistory = true;
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
             if (USE_OLD_HISTORY) {
-                addHistoryRecordLocked(now, HistoryItem.CMD_START);
+                addHistoryRecordLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
             }
-            addHistoryBufferLocked(now, HistoryItem.CMD_START);
-            mHistoryCur.currentTime = System.currentTimeMillis();
-            addHistoryBufferLocked(now, HistoryItem.CMD_CURRENT_TIME);
-            mHistoryCur.currentTime = 0;
+            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START, mHistoryCur);
+            startRecordingHistory(elapsedRealtime, uptime, false);
         }
     }
 
@@ -6540,11 +6741,11 @@
             StringBuilder sb = new StringBuilder(128);
             sb.append("****************** WRITING mHistoryBaseTime: ");
             TimeUtils.formatDuration(mHistoryBaseTime, sb);
-            sb.append(" mLastHistoryTime: ");
-            TimeUtils.formatDuration(mLastHistoryTime, sb);
+            sb.append(" mLastHistoryElapsedRealtime: ");
+            TimeUtils.formatDuration(mLastHistoryElapsedRealtime, sb);
             Slog.i(TAG, sb.toString());
         }
-        out.writeLong(mHistoryBaseTime + mLastHistoryTime);
+        out.writeLong(mHistoryBaseTime + mLastHistoryElapsedRealtime);
         out.writeInt(mHistoryTagPool.size());
         for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) {
             HistoryTag tag = ent.getKey();
@@ -6621,6 +6822,7 @@
         mMobileRadioActive = false;
         mMobileRadioActiveTimer.readSummaryFromParcelLocked(in);
         mMobileRadioActivePerAppTimer.readSummaryFromParcelLocked(in);
+        mMobileRadioActiveAdjustedTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownTime.readSummaryFromParcelLocked(in);
         mMobileRadioActiveUnknownCount.readSummaryFromParcelLocked(in);
         mWifiOn = false;
@@ -6648,6 +6850,18 @@
             }
         }
 
+        int NWR = in.readInt();
+        if (NWR > 10000) {
+            Slog.w(TAG, "File corrupt: too many wakeup reasons " + NWR);
+            return;
+        }
+        for (int iwr = 0; iwr < NWR; iwr++) {
+            if (in.readInt() != 0) {
+                String reasonName = in.readString();
+                getWakeupReasonCounterLocked(reasonName).readSummaryFromParcelLocked(in);
+            }
+        }
+
         sNumSpeedSteps = in.readInt();
         if (sNumSpeedSteps < 0 || sNumSpeedSteps > 100) {
             throw new BadParcelableException("Bad speed steps in data: " + sNumSpeedSteps);
@@ -6855,6 +7069,7 @@
         }
         mMobileRadioActiveTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         mMobileRadioActivePerAppTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        mMobileRadioActiveAdjustedTime.writeSummaryFromParcelLocked(out);
         mMobileRadioActiveUnknownTime.writeSummaryFromParcelLocked(out);
         mMobileRadioActiveUnknownCount.writeSummaryFromParcelLocked(out);
         mWifiOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -6873,7 +7088,19 @@
             if (kwlt != null) {
                 out.writeInt(1);
                 out.writeString(ent.getKey());
-                ent.getValue().writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+                kwlt.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+            } else {
+                out.writeInt(0);
+            }
+        }
+
+        out.writeInt(mWakeupReasonStats.size());
+        for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+            LongSamplingCounter counter = ent.getValue();
+            if (counter != null) {
+                out.writeInt(1);
+                out.writeString(ent.getKey());
+                counter.writeSummaryFromParcelLocked(out);
             } else {
                 out.writeInt(0);
             }
@@ -7111,6 +7338,7 @@
         mMobileRadioActiveTimer = new StopwatchTimer(null, -400, null, mOnBatteryTimeBase, in);
         mMobileRadioActivePerAppTimer = new StopwatchTimer(null, -401, null, mOnBatteryTimeBase,
                 in);
+        mMobileRadioActiveAdjustedTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownTime = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mMobileRadioActiveUnknownCount = new LongSamplingCounter(mOnBatteryTimeBase, in);
         mWifiOn = false;
@@ -7145,12 +7373,22 @@
         for (int ikw = 0; ikw < NKW; ikw++) {
             if (in.readInt() != 0) {
                 String wakelockName = in.readString();
-                in.readInt(); // Extra 0/1 written by Timer.writeTimerToParcel
                 SamplingTimer kwlt = new SamplingTimer(mOnBatteryTimeBase, in);
                 mKernelWakelockStats.put(wakelockName, kwlt);
             }
         }
 
+        mWakeupReasonStats.clear();
+        int NWR = in.readInt();
+        for (int iwr = 0; iwr < NWR; iwr++) {
+            if (in.readInt() != 0) {
+                String reasonName = in.readString();
+                LongSamplingCounter counter = new LongSamplingCounter(mOnBatteryScreenOffTimeBase,
+                        in);
+                mWakeupReasonStats.put(reasonName, counter);
+            }
+        }
+
         mPartialTimers.clear();
         mFullTimers.clear();
         mWindowTimers.clear();
@@ -7223,6 +7461,7 @@
         }
         mMobileRadioActiveTimer.writeToParcel(out, uSecRealtime);
         mMobileRadioActivePerAppTimer.writeToParcel(out, uSecRealtime);
+        mMobileRadioActiveAdjustedTime.writeToParcel(out);
         mMobileRadioActiveUnknownTime.writeToParcel(out);
         mMobileRadioActiveUnknownCount.writeToParcel(out);
         mWifiOnTimer.writeToParcel(out, uSecRealtime);
@@ -7253,7 +7492,18 @@
                 if (kwlt != null) {
                     out.writeInt(1);
                     out.writeString(ent.getKey());
-                    Timer.writeTimerToParcel(out, kwlt, uSecRealtime);
+                    kwlt.writeToParcel(out, uSecRealtime);
+                } else {
+                    out.writeInt(0);
+                }
+            }
+            out.writeInt(mWakeupReasonStats.size());
+            for (Map.Entry<String, LongSamplingCounter> ent : mWakeupReasonStats.entrySet()) {
+                LongSamplingCounter counter = ent.getValue();
+                if (counter != null) {
+                    out.writeInt(1);
+                    out.writeString(ent.getKey());
+                    counter.writeToParcel(out);
                 } else {
                     out.writeInt(0);
                 }
@@ -7323,6 +7573,8 @@
             }
             pr.println("*** Mobile network active timer:");
             mMobileRadioActiveTimer.logState(pr, "  ");
+            pr.println("*** Mobile network active adjusted timer:");
+            mMobileRadioActiveAdjustedTime.logState(pr, "  ");
             pr.println("*** Wifi timer:");
             mWifiOnTimer.logState(pr, "  ");
             pr.println("*** WifiRunning timer:");
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 91056f1..9501f92 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -28,6 +28,7 @@
     boolean checkPattern(in String pattern, int userId);
     void setLockPassword(in String password, int userId);
     boolean checkPassword(in String password, int userId);
+    boolean checkVoldPassword(int userId);
     boolean havePattern(int userId);
     boolean havePassword(int userId);
     void removeUser(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 2d79491..e5aaf7e 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -313,6 +313,20 @@
     }
 
     /**
+     * Check to see if vold already has the password.
+     * Note that this also clears vold's copy of the password.
+     * @return Whether the vold password matches or not.
+     */
+    public boolean checkVoldPassword() {
+        final int userId = getCurrentOrCallingUserId();
+        try {
+            return getLockSettings().checkVoldPassword(userId);
+        } catch (RemoteException re) {
+            return false;
+        }
+    }
+
+    /**
      * Check to see if a password matches any of the passwords stored in the
      * password history.
      *
diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java
index f856027..64ce918 100644
--- a/core/java/com/android/internal/widget/RotarySelector.java
+++ b/core/java/com/android/internal/widget/RotarySelector.java
@@ -24,6 +24,7 @@
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.Matrix;
+import android.media.AudioManager;
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -34,7 +35,9 @@
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 import android.view.animation.DecelerateInterpolator;
+
 import static android.view.animation.AnimationUtils.currentAnimationTimeMillis;
+
 import com.android.internal.R;
 
 
@@ -676,7 +679,7 @@
                 mVibrator = (android.os.Vibrator) getContext()
                         .getSystemService(Context.VIBRATOR_SERVICE);
             }
-            mVibrator.vibrate(duration);
+            mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index aebc4f6..deb0fd7 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -21,6 +21,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -821,7 +822,7 @@
                 mVibrator = (android.os.Vibrator) getContext()
                         .getSystemService(Context.VIBRATOR_SERVICE);
             }
-            mVibrator.vibrate(duration);
+            mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java
index 0c5993b..86f14b3 100644
--- a/core/java/com/android/internal/widget/WaveView.java
+++ b/core/java/com/android/internal/widget/WaveView.java
@@ -25,6 +25,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.drawable.BitmapDrawable;
+import android.media.AudioManager;
 import android.os.UserHandle;
 import android.os.Vibrator;
 import android.provider.Settings;
@@ -582,7 +583,7 @@
                 mVibrator = (android.os.Vibrator) getContext()
                         .getSystemService(Context.VIBRATOR_SERVICE);
             }
-            mVibrator.vibrate(duration);
+            mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
index 93ea5b3..772dc5f 100644
--- a/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/GlowPadView.java
@@ -30,6 +30,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.Vibrator;
@@ -564,7 +565,7 @@
                 mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
                 UserHandle.USER_CURRENT) != 0;
         if (mVibrator != null && hapticEnabled) {
-            mVibrator.vibrate(mVibrationDuration);
+            mVibrator.vibrate(mVibrationDuration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index e22d1e8..4648f39 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -31,6 +31,7 @@
 import android.graphics.Canvas;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.Vibrator;
@@ -599,7 +600,7 @@
                 mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
                 UserHandle.USER_CURRENT) != 0;
         if (mVibrator != null && hapticEnabled) {
-            mVibrator.vibrate(mVibrationDuration);
+            mVibrator.vibrate(mVibrationDuration, AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/core/java/com/android/server/AppWidgetBackupBridge.java b/core/java/com/android/server/AppWidgetBackupBridge.java
new file mode 100644
index 0000000..2ea2f79
--- /dev/null
+++ b/core/java/com/android/server/AppWidgetBackupBridge.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 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 java.util.List;
+
+/**
+ * Runtime bridge between the Backup Manager Service and the App Widget Service,
+ * since those two modules are intentionally decoupled for modularity.
+ *
+ * @hide
+ */
+public class AppWidgetBackupBridge {
+    private static WidgetBackupProvider sAppWidgetService;
+
+    public static void register(WidgetBackupProvider instance) {
+        sAppWidgetService = instance;
+    }
+
+    public static List<String> getWidgetParticipants(int userId) {
+        return (sAppWidgetService != null)
+                ? sAppWidgetService.getWidgetParticipants(userId)
+                : null;
+    }
+
+    public static byte[] getWidgetState(String packageName, int userId) {
+        return (sAppWidgetService != null)
+                ? sAppWidgetService.getWidgetState(packageName, userId)
+                : null;
+    }
+
+    public static void restoreStarting(int userId) {
+        if (sAppWidgetService != null) {
+            sAppWidgetService.restoreStarting(userId);
+        }
+    }
+
+    public static void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+        if (sAppWidgetService != null) {
+            sAppWidgetService.restoreWidgetState(packageName, restoredState, userId);
+        }
+    }
+
+    public static void restoreFinished(int userId) {
+        if (sAppWidgetService != null) {
+            sAppWidgetService.restoreFinished(userId);
+        }
+    }
+}
diff --git a/core/java/com/android/server/WidgetBackupProvider.java b/core/java/com/android/server/WidgetBackupProvider.java
new file mode 100644
index 0000000..a2efbdd
--- /dev/null
+++ b/core/java/com/android/server/WidgetBackupProvider.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014 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 java.util.List;
+
+/**
+ * Shim to allow core/backup to communicate with the app widget service
+ * about various important events without needing to be able to see the
+ * implementation of the service.
+ *
+ * @hide
+ */
+public interface WidgetBackupProvider {
+    public List<String> getWidgetParticipants(int userId);
+    public byte[] getWidgetState(String packageName, int userId);
+    public void restoreStarting(int userId);
+    public void restoreWidgetState(String packageName, byte[] restoredState, int userId);
+    public void restoreFinished(int userId);
+}
diff --git a/core/jni/android_content_res_Configuration.cpp b/core/jni/android_content_res_Configuration.cpp
index 246e3bd..201ffe8 100644
--- a/core/jni/android_content_res_Configuration.cpp
+++ b/core/jni/android_content_res_Configuration.cpp
@@ -70,15 +70,6 @@
             gConfigurationClassInfo.smallestScreenWidthDp);
 }
 
-/*
- * JNI registration.
- */
-static JNINativeMethod gMethods[] = {
-    /* name, signature, funcPtr */
-    //{ "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
-    //        (void*) android_content_res_ObbScanner_getObbInfo },
-};
-
 #define FIND_CLASS(var, className) \
         var = env->FindClass(className); \
         LOG_FATAL_IF(! var, "Unable to find class " className);
@@ -123,8 +114,7 @@
     GET_FIELD_ID(gConfigurationClassInfo.smallestScreenWidthDp, clazz,
             "smallestScreenWidthDp", "I");
 
-    return AndroidRuntime::registerNativeMethods(env, "android/content/res/Configuration", gMethods,
-            NELEM(gMethods));
+    return 0;
 }
 
 }; // namespace android
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index a64d3ba..3a5b566 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -606,7 +606,7 @@
 
 // ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
-        jbyteArray javaBytes, jint byteOffset, jint offsetInBytes, jint sizeInBytes,
+        jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
         jint javaAudioFormat, jboolean isWriteBlocking) {
     //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
     //    offsetInBytes, sizeInBytes);
@@ -623,7 +623,7 @@
         return AUDIOTRACK_ERROR_BAD_VALUE;
     }
 
-    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get() + byteOffset, offsetInBytes,
+    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
             sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
 
     return written;
@@ -922,7 +922,7 @@
     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
     {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte},
     {"native_write_native_bytes",
-                             "(Ljava/lang/Object;IIIIZ)I",
+                             "(Ljava/lang/Object;IIIZ)I",
                                          (void *)android_media_AudioTrack_write_native_bytes},
     {"native_write_short",   "([SIII)I", (void *)android_media_AudioTrack_write_short},
     {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index aa6a035..e72aff9 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -42,13 +42,13 @@
 #include <SkTemplates.h>
 #include <SkXfermode.h>
 
-#include <DisplayList.h>
 #include <DisplayListRenderer.h>
 #include <LayerRenderer.h>
 #include <OpenGLRenderer.h>
 #include <SkiaShader.h>
 #include <Stencil.h>
 #include <Rect.h>
+#include <RenderNode.h>
 
 #include <TextLayout.h>
 #include <TextLayoutCache.h>
diff --git a/core/jni/android_view_GLRenderer.cpp b/core/jni/android_view_GLRenderer.cpp
index 228a92e..00d92fc 100644
--- a/core/jni/android_view_GLRenderer.cpp
+++ b/core/jni/android_view_GLRenderer.cpp
@@ -27,9 +27,9 @@
 #include <utils/Timers.h>
 
 #include <Caches.h>
-#include <DisplayList.h>
 #include <Extensions.h>
 #include <LayerRenderer.h>
+#include <RenderNode.h>
 
 #ifdef USE_OPENGL_RENDERER
     EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp
index ad2e9ff..4bf5f78 100644
--- a/core/jni/android_view_HardwareLayer.cpp
+++ b/core/jni/android_view_HardwareLayer.cpp
@@ -32,10 +32,10 @@
 #include <SkXfermode.h>
 
 #include <DeferredLayerUpdater.h>
-#include <DisplayList.h>
 #include <LayerRenderer.h>
 #include <SkiaShader.h>
 #include <Rect.h>
+#include <RenderNode.h>
 
 namespace android {
 
diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp
index 823f8d7..fa08a78 100644
--- a/core/jni/android_view_RenderNode.cpp
+++ b/core/jni/android_view_RenderNode.cpp
@@ -23,8 +23,8 @@
 #include <nativehelper/JNIHelp.h>
 #include <android_runtime/AndroidRuntime.h>
 
-#include <DisplayList.h>
 #include <DisplayListRenderer.h>
+#include <RenderNode.h>
 
 namespace android {
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c2159fb..2168bd1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -83,6 +83,8 @@
     <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DELETED" />
     <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DISABLED" />
     <protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_RESTORED" />
 
     <protected-broadcast android:name="android.backup.intent.RUN" />
     <protected-broadcast android:name="android.backup.intent.CLEAR" />
@@ -682,6 +684,12 @@
         android:label="@string/permlab_installLocationProvider"
         android:description="@string/permdesc_installLocationProvider" />
 
+    <!-- Allows HDMI-CEC service to access device and configuration files.
+         @hide This should only be used by HDMI-CEC service.
+    -->
+    <permission android:name="android.permission.HDMI_CEC"
+        android:protectionLevel="signatureOrSystem" />
+
     <!-- Allows an application to use location features in hardware,
          such as the geofencing api.
          <p>Not for use by third-party applications. -->
@@ -1913,24 +1921,12 @@
         android:description="@string/permdesc_filter_events"
         android:protectionLevel="signature" />
 
-    <!-- @hide Allows an application to retrieve info for a window from the window manager. -->
-    <permission android:name="android.permission.RETRIEVE_WINDOW_INFO"
-        android:label="@string/permlab_retrieve_window_info"
-        android:description="@string/permdesc_retrieve_window_info"
-        android:protectionLevel="signature" />
-
     <!-- @hide Allows an application to temporary enable accessibility on the device. -->
     <permission android:name="android.permission.TEMPORARY_ENABLE_ACCESSIBILITY"
         android:label="@string/permlab_temporary_enable_accessibility"
         android:description="@string/permdesc_temporary_enable_accessibility"
         android:protectionLevel="signature" />
 
-    <!-- @hide Allows an application to magnify the content of a display. -->
-    <permission android:name="android.permission.MAGNIFY_DISPLAY"
-        android:label="@string/permlab_magnify_display"
-        android:description="@string/permdesc_magnify_display"
-        android:protectionLevel="signature" />
-
     <!-- Allows an application to watch and control how activities are
          started globally in the system.  Only for is in debugging
          (usually the monkey command).
@@ -2400,6 +2396,12 @@
         android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
         android:protectionLevel="signature" />
 
+    <!-- Internal permission to allows an application to read indexable data.
+        @hide -->
+    <permission android:name="android.permission.READ_SEARCH_INDEXABLES"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature|system" />
+
     <!-- Allows applications to set a live wallpaper.
          @hide XXX Change to signature once the picker is moved to its
          own apk as Ghod Intended. -->
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
index 6b31579..7b6d48b 100644
--- a/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
index df0121b..bafe878 100644
--- a/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
+++ b/core/res/res/drawable-xhdpi/cab_background_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png
index 418f322..cbb4f4c 100644
--- a/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png
+++ b/core/res/res/drawable-xxhdpi/cab_background_top_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png b/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png
index a5a59d4..6d467f7 100644
--- a/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png
+++ b/core/res/res/drawable-xxhdpi/cab_background_top_holo_light.9.png
Binary files differ
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 682e0ea..0330b7b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -690,7 +690,7 @@
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"conèixer les observacions sobre les condicions de la xarxa"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Permet que una aplicació conegui les observacions sobre les condicions de la xarxa. No s\'ha de necessitar mai per a aplicacions normals."</string>
     <string name="permlab_setInputCalibration" msgid="4902620118878467615">"canviar el calibratge del dispositiu d\'entrada"</string>
-    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permet que l\'aplicació modifiqui els paràmetres de calibratge de la pantalla tàctil. S\'ha de procurar no fer servir mai aquesta opció per a les aplicacions normals."</string>
+    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permet que l\'aplicació modifiqui els paràmetres de calibratge de la pantalla tàctil. No ha de ser mai necessari per a aplicacions normals."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Definir les normes de contrasenya"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controla la longitud i els caràcters permesos a les contrasenyes de desbloqueig de pantalla."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Controlar intents de desbloqueig de pantalla"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index ffe25013..e144e9d 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1435,8 +1435,8 @@
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
     <string name="activitychooserview_choose_application" msgid="2125168057199941199">"Επιλέξτε κάποια εφαρμογή"</string>
     <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"Δεν ήταν δυνατή η εκκίνηση του <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινή χρήση με"</string>
-    <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινή χρήση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+    <string name="shareactionprovider_share_with" msgid="806688056141131819">"Κοινοποίηση με"</string>
+    <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"Κοινοποίηση με <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="content_description_sliding_handle" msgid="415975056159262248">"Στοιχείο χειρισμού με δυνατότητα ολίσθησης. Αγγίξτε και πατήστε παρατεταμένα."</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"Σύρετε για ξεκλείδωμα."</string>
     <string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"Συνδέστε ακουστικά για να ακούσετε τα πλήκτρα του κωδικού πρόσβασης να εκφωνούνται."</string>
@@ -1480,7 +1480,7 @@
     <string name="sha1_fingerprint" msgid="7930330235269404581">"Αποτύπωμα SHA-1"</string>
     <string name="activity_chooser_view_see_all" msgid="4292569383976636200">"Εμφάνιση όλων"</string>
     <string name="activity_chooser_view_dialog_title_default" msgid="4710013864974040615">"Επιλογή δραστηριότητας"</string>
-    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινή χρήση με"</string>
+    <string name="share_action_provider_share_with" msgid="5247684435979149216">"Κοινοποίηση με"</string>
     <string name="list_delimeter" msgid="3975117572185494152">", "</string>
     <string name="sending" msgid="3245653681008218030">"Γίνεται αποστολή…"</string>
     <string name="launchBrowserDefault" msgid="2057951947297614725">"Εκκίνηση προγράμματος περιήγησης;"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index ec4ed33..2c9599c 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -689,7 +689,7 @@
     <string name="permdesc_invokeCarrierSetup" msgid="4159549152529111920">"Permite al propietario ejecutar la aplicación de configuración proporcionada por el proveedor. Las aplicaciones normales no deberían necesitar este permiso."</string>
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"Detectar cambios en el estado de la red"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Permite que una aplicación detecte cambios en el estado de la red. Las aplicaciones normales no deberían necesitar este permiso."</string>
-    <string name="permlab_setInputCalibration" msgid="4902620118878467615">"Cambiar la calibración del dispositivo de entrada"</string>
+    <string name="permlab_setInputCalibration" msgid="4902620118878467615">"cambiar la calibración del dispositivo de entrada"</string>
     <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Permite que la aplicación modifique los parámetros de calibración de la pantalla táctil. Las aplicaciones normales no deberían necesitar este permiso."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Establecer reglas de contraseña"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas para desbloquear la pantalla"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 857a1ea..308ea59 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -690,7 +690,7 @@
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"گوش دادن برای بررسی شرایط شبکه"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"به برنامه امکان می‌دهد برای بررسی شرایط شبکه گوش دهد. این امکان هرگز نباید برای برنامه‌های معمولی مورد نیاز باشد."</string>
     <string name="permlab_setInputCalibration" msgid="4902620118878467615">"تغییر کالیبراسیون دستگاه ورودی"</string>
-    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"به برنامه امکان می‌دهد پارامترهای صفحه لمسی را کالیبره کند. هرگز نباید برای برنامه‌های عادی مورد نیاز باشد."</string>
+    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"به برنامه امکان می‌دهد پارامترهای کالیبراسیون صفحه لمسی را تغییر دهد. هرگز نباید برای برنامه‌های عادی مورد نیاز باشد."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"‏طول و نویسه‎های مجاز در گذرواژه‌های بازکردن قفل صفحه را کنترل کنید."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاش‌های قفل گشایی صفحه"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 1d6b16d..b40117b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -690,7 +690,7 @@
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"hálózati körülményekkel kapcsolatos észrevételek figyelemmel kísérése"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Lehetővé teszi egy alkalmazás számára, hogy figyelemmel kísérje a hálózati körülményekkel kapcsolatos észrevételeket. A normál alkalmazásoknak erre soha nincs szükségük."</string>
     <string name="permlab_setInputCalibration" msgid="4902620118878467615">"beviteli eszköz kalibrációjának módosítása"</string>
-    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lehetővé teszi, hogy alkalmazás módosítsa az érintőképernyő kalibrációs paramétereit. A normál alkalmazásoknál erre elvileg soha nincs szükség."</string>
+    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Lehetővé teszi, hogy az alkalmazás módosítsa az érintőképernyő kalibrációs paramétereit. A normál alkalmazásoknál erre elvileg soha nincs szükség."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Jelszavakkal kapcsolatos szabályok beállítása"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"A képernyőzár-feloldási jelszavakban engedélyezett karakterek és hosszúság vezérlése."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Képernyőzár-feloldási kísérletek figyelése"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index c8f0dd5..f330aaa 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1111,7 +1111,7 @@
     <string name="cut" msgid="3092569408438626261">"חתוך"</string>
     <string name="copy" msgid="2681946229533511987">"העתק"</string>
     <string name="paste" msgid="5629880836805036433">"הדבק"</string>
-    <string name="replace" msgid="5781686059063148930">"להחליף..."</string>
+    <string name="replace" msgid="5781686059063148930">"החלף..."</string>
     <string name="delete" msgid="6098684844021697789">"מחק"</string>
     <string name="copyUrl" msgid="2538211579596067402">"העתק כתובת אתר"</string>
     <string name="selectTextMode" msgid="1018691815143165326">"בחר טקסט"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 22fc1c4..4284032 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -690,7 +690,7 @@
     <string name="permlab_accessNetworkConditions" msgid="8206077447838909516">"vykdyti tinklo sąlygų stebėjimą"</string>
     <string name="permdesc_accessNetworkConditions" msgid="6899102075825272211">"Leidžiama programai vykdyti tinklo sąlygų stebėjimą. To niekada neturėtų prireikti naudojant įprastas programas."</string>
     <string name="permlab_setInputCalibration" msgid="4902620118878467615">"keisti įvesties įrenginio kalibravimą"</string>
-    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Leidžiama programai keisti jutiklinio ekrano kalibravimo parametrus. Neturėti prireikti naudojant įprastas programas."</string>
+    <string name="permdesc_setInputCalibration" msgid="4527511047549456929">"Leidžiama programai keisti jutiklinio ekrano kalibravimo parametrus. Neturėtų prireikti naudojant įprastas programas."</string>
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Nustatyti slaptažodžio taisykles"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Valdyti leidžiamą ekrano atrakinimo slaptažodžių ilgį ir leidžiamus naudoti simbolius."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Stebėti bandymus atrakinti ekraną"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index f8e9782..4df9785 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -38,7 +38,7 @@
     <string name="mmiFdnError" msgid="5224398216385316471">"Operácia je obmedzená len na režim čísla pevného vytáčania."</string>
     <string name="serviceEnabled" msgid="8147278346414714315">"Služba bola povolená."</string>
     <string name="serviceEnabledFor" msgid="6856228140453471041">"Služba bola povolená pre:"</string>
-    <string name="serviceDisabled" msgid="1937553226592516411">"Služba bola zakázaná."</string>
+    <string name="serviceDisabled" msgid="1937553226592516411">"Služba bola vypnutá."</string>
     <string name="serviceRegistered" msgid="6275019082598102493">"Registrácia prebehla úspešne."</string>
     <string name="serviceErased" msgid="1288584695297200972">"Vymazanie prebehlo úspešne."</string>
     <string name="passwordIncorrect" msgid="7612208839450128715">"Nesprávne heslo."</string>
@@ -528,14 +528,14 @@
     <string name="permdesc_mount_format_filesystems" msgid="8784268246779198627">"Umožňuje aplikácii formátovať vymeniteľný ukladací priestor."</string>
     <string name="permlab_asec_access" msgid="3411338632002193846">"získať informácie o internom ukladacom priestore"</string>
     <string name="permdesc_asec_access" msgid="3094563844593878548">"Umožňuje aplikácii získať informácie o internom ukladacom priestore."</string>
-    <string name="permlab_asec_create" msgid="6414757234789336327">"vytvoriť interný ukladací priestor"</string>
-    <string name="permdesc_asec_create" msgid="4558869273585856876">"Umožňuje aplikácii vytvoriť interný ukladací priestor."</string>
-    <string name="permlab_asec_destroy" msgid="526928328301618022">"zničiť interný ukladací priestor"</string>
-    <string name="permdesc_asec_destroy" msgid="7218749286145526537">"Umožňuje aplikácii zničiť interný ukladací priestor."</string>
-    <string name="permlab_asec_mount_unmount" msgid="8877998101944999386">"pripojiť alebo odpojiť interný ukladací priestor"</string>
-    <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"Umožňuje aplikácii pripojiť alebo odpojiť interný ukladací priestor."</string>
-    <string name="permlab_asec_rename" msgid="7496633954080472417">"premenovať interný ukladací priestor"</string>
-    <string name="permdesc_asec_rename" msgid="1794757588472127675">"Umožňuje aplikácii premenovať interný ukladací priestor."</string>
+    <string name="permlab_asec_create" msgid="6414757234789336327">"vytvoriť interné úložisko"</string>
+    <string name="permdesc_asec_create" msgid="4558869273585856876">"Umožňuje aplikácii vytvoriť interné úložisko."</string>
+    <string name="permlab_asec_destroy" msgid="526928328301618022">"zničiť interné úložisko"</string>
+    <string name="permdesc_asec_destroy" msgid="7218749286145526537">"Umožňuje aplikácii zničiť interné úložisko."</string>
+    <string name="permlab_asec_mount_unmount" msgid="8877998101944999386">"pripojiť alebo odpojiť interné úložisko"</string>
+    <string name="permdesc_asec_mount_unmount" msgid="3451360114902490929">"Umožňuje aplikácii pripojiť alebo odpojiť interné úložisko."</string>
+    <string name="permlab_asec_rename" msgid="7496633954080472417">"premenovať interné úložisko"</string>
+    <string name="permdesc_asec_rename" msgid="1794757588472127675">"Umožňuje aplikácii premenovať interné úložisko."</string>
     <string name="permlab_vibrate" msgid="7696427026057705834">"ovládať vibrovanie"</string>
     <string name="permdesc_vibrate" msgid="6284989245902300945">"Umožňuje aplikácii ovládať vibrácie."</string>
     <string name="permlab_flashlight" msgid="2155920810121984215">"ovládanie kontrolky"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index e07bd7d..305036d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1667,7 +1667,7 @@
     <item quantity="other" msgid="4730868920742952817">"ลองอีกใน <xliff:g id="COUNT">%d</xliff:g> วินาที"</item>
   </plurals>
     <string name="restr_pin_try_later" msgid="973144472490532377">"ลองอีกครั้งในภายหลัง"</string>
-    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"กวาดนิ้วจากบนลงล่างเพื่อออกจากโหมดเต็มหน้าจอ"</string>
+    <string name="immersive_mode_confirmation" msgid="7227416894979047467">"กวาดนิ้วบนลงล่างเพื่อออกจากโหมดเต็มหน้าจอ"</string>
     <string name="done_label" msgid="2093726099505892398">"เสร็จสิ้น"</string>
     <string name="hour_picker_description" msgid="6698199186859736512">"ตัวเลื่อนหมุนระบุชั่วโมง"</string>
     <string name="minute_picker_description" msgid="8606010966873791190">"ตัวเลื่อนหมุนระบุนาที"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 8482fdb..582ed1b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2641,6 +2641,8 @@
             <flag name="flagReportViewIds" value="0x00000010" />
             <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_FILTER_KEY_EVENTS} -->
             <flag name="flagRequestFilterKeyEvents" value="0x00000020" />
+            <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_RETRIEVE_INTERACTIVE_WINDOWS} -->
+            <flag name="flagRetrieveInteractiveWindows" value="0x00000040" />
         </attr>
         <!-- Component name of an activity that allows the user to modify
              the settings for this service. This setting cannot be changed at runtime. -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index f1bcf65..0699e8b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -830,13 +830,6 @@
          user consent.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_retrieve_window_info">retrieve window info</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_retrieve_window_info">Allows an application to retrieve
-         information about the the windows from the window manager. Malicious apps may
-         retrieve information that is intended for internal system usage.</string>
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_filter_events">filter events</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_filter_events">Allows an application to register an input filter
@@ -844,13 +837,6 @@
             may control the system UI whtout user intervention.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permlab_magnify_display">magnify display</string>
-    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
-    <string name="permdesc_magnify_display">Allows an application to magnify the content of a
-        display. Malicious apps may transform the display content in a way that renders the
-        device unusable.</string>
-
-    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_shutdown">partial shutdown</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_shutdown">Puts the activity manager into a shutdown
diff --git a/docs/downloads/training/BitmapFun.zip b/docs/downloads/training/BitmapFun.zip
deleted file mode 100644
index d3dd02a..0000000
--- a/docs/downloads/training/BitmapFun.zip
+++ /dev/null
Binary files differ
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 5f0779c..fc60e1f 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -313,6 +313,9 @@
 
 # -------------------- MISC ----------------------
 
+- from: /shareables/training/BitmapFun.zip
+  to: /downloads/samples/DisplayingBitmaps.zip
+
 - from: /shareables/...
   to: http://commondatastorage.googleapis.com/androiddevelopers/shareables/...
 
diff --git a/docs/html/guide/topics/graphics/hardware-accel.jd b/docs/html/guide/topics/graphics/hardware-accel.jd
index 54ef20c..7c957b8 100644
--- a/docs/html/guide/topics/graphics/hardware-accel.jd
+++ b/docs/html/guide/topics/graphics/hardware-accel.jd
@@ -263,256 +263,153 @@
   <p>The following table describes the support level of various operations across API levels:</p>
 
   <style type="text/css">
-    .tblGenFixed, .tblGeneric{font-size:15px}.tblGenFixed td {padding:0 3px;letter-spacing:0;word-spacing:0;background-color:#fff;z-index:1;border-top:0px none;border-left:0px none;border-bottom:1px solid #CCC;border-right:1px solid #CCC;} .dn {display:none} .tblGenFixed td.s0 {background-color:white;border-top:1px solid #CCC;border-left:1px solid #CCC;} .tblGenFixed td.s1 {background-color:#434343;color:#ffffff;text-align:center;border-top:1px solid #CCC;} .tblGenFixed td.s2 {background-color:#d9d9d9;color:#000000;text-align:center;} .tblGenFixed td.s3 {background-color:white;color:#000000;text-align:center;} .tblGenFixed td.s5 {background-color:#434343;color:#ffffff;text-align:left;border-left:1px solid #CCC;} .tblGenFixed td.s10 {background-color:white;font-family:courier new,monospace;color:#000000;text-align:right;border-left:1px solid #CCC;} .tblGenFixed td.g_pos {background-color:#d9d9d9;color:#6aa84f;text-align:center;} .tblGenFixed td.g_neg {background-color:#d9d9d9;color:#980000;text-align:center;} .tblGenFixed td.w_pos {background-color:white;color:#6aa84f;text-align:center;} .tblGenFixed td.w_neg {background-color:white;color:#980000;text-align:center;}
+    .tblGenFixed, .tblGeneric{font-size:15px}.tblGenFixed td {padding:0 3px;letter-spacing:0;word-spacing:0;background-color:#fff;z-index:1;border-top:0px none;border-left:0px none;border-bottom:1px solid #CCC;border-right:1px solid #CCC;} .dn {display:none} .tblGenFixed td.s0 {background-color:white;border-top:1px solid #CCC;border-left:1px solid #CCC;} .tblGenFixed td.s1 {background-color:#434343;color:#ffffff;text-align:center;border-top:1px solid #CCC;} .tblGenFixed td.s3 {background-color:white;color:#000000;text-align:center;} .tblGenFixed td.s5 {background-color:#434343;color:#ffffff;text-align:left;border-left:1px solid #CCC;} .tblGenFixed td.label_pos {background-color:white;font-family:courier new,monospace;color:#000000;text-align:right;border-left:1px solid #CCC;} .tblGenFixed td.label_neg {background-color:#ececec;font-family:courier new,monospace;color:#000000;text-align:right;border-left:1px solid #CCC;} .tblGenFixed td.value_pos {background-color:white;color:#000000;text-align:center;} .tblGenFixed td.value_neg {background-color:#ececec;color:#980000;text-align:center;}
   </style>
   <table border="0" cellpadding="0" cellspacing="0" class="tblGenFixed" id="tblMain">
     <tbody>
     <tr class="rShim">
         <td class="rShim" style="width:380px;"></td>
-        <td class="rShim" style="width:120px;"></td>
-        <td class="rShim" style="width:120px;"></td>
-        <td class="rShim" style="width:120px;"></td>
-        <td class="rShim" style="width:120px;"></td>
+        <td class="rShim" style="width:240px;"></td>
     </tr>
     <tr>
-        <td rowspan="2" class="s0"></td>
-        <td colspan="4" class="s1">API level</td>
-    </tr>
-    <tr>
-        <td style="display:none;"></td>
-        <td class="s2">&lt; 16</td>
-        <td class="s3">16</td>
-        <td class="s2">17</td>
-        <td class="s3">18</td>
+        <td class="s0"></td>
+        <td class="s1">First supported API level</td>
     </tr>
     <tr>
         <td colspan="5" class="s5">Canvas</td>
     </tr>
     <tr>
-        <td class="s10">drawBitmapMesh() (colors array)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">drawBitmapMesh() (colors array)</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
-        <td class="s10">drawPicture()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">drawPicture()</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">drawPosText()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
-        <td class="g_pos">&#10003;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">drawPosText()</td>
+        <td class="value_pos">16</td>
     </tr>
     <tr>
-        <td class="s10">drawTextOnPath()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
-        <td class="g_pos">&#10003;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">drawTextOnPath()</td>
+        <td class="value_pos">16</td>
     </tr>
     <tr>
-        <td class="s10">drawVertices()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">drawVertices()</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">setDrawFilter()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
-        <td class="g_pos">&#10003;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">setDrawFilter()</td>
+        <td class="value_pos">16</td>
     </tr>
     <tr>
-        <td class="s10">clipPath()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">clipPath()</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
-        <td class="s10">clipRegion()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">clipRegion()</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
-        <td class="s10">clipRect(Region.Op.XOR)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">clipRect(Region.Op.XOR)</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
-        <td class="s10">clipRect(Region.Op.Difference)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">clipRect(Region.Op.Difference)</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
-        <td class="s10">clipRect(Region.Op.ReverseDifference)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">clipRect(Region.Op.ReverseDifference)</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
-        <td class="s10">clipRect() with rotation/perspective</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">clipRect() with rotation/perspective</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
         <td colspan="5" class="s5">Paint</td>
     </tr>
     <tr>
-        <td class="s10">setAntiAlias() (for text)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">setAntiAlias() (for text)</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
-        <td class="s10">setAntiAlias() (for lines)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
-        <td class="g_pos">&#10003;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">setAntiAlias() (for lines)</td>
+        <td class="value_pos">16</td>
     </tr>
     <tr>
-        <td class="s10">setFilterBitmap()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_pos">&#10003;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">setFilterBitmap()</td>
+        <td class="value_pos">17</td>
     </tr>
     <tr>
-        <td class="s10">setLinearText()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">setLinearText()</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">setMaskFilter()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">setMaskFilter()</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">setPathEffect() (for lines)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">setPathEffect() (for lines)</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">setRasterizer()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">setRasterizer()</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">setShadowLayer() (other than text)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">setShadowLayer() (other than text)</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">setStrokeCap() (for lines)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">setStrokeCap() (for lines)</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
-        <td class="s10">setStrokeCap() (for points)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_pos">setStrokeCap() (for points)</td>
+        <td class="value_pos">19</td>
     </tr>
     <tr>
-        <td class="s10">setSubpixelText()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">setSubpixelText()</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
         <td colspan="5" class="s5">Xfermode</td>
     </tr>
     <tr>
-        <td class="s10">AvoidXfermode</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">AvoidXfermode</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">PixelXorXfermode</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">PixelXorXfermode</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">PorterDuff.Mode.DARKEN (framebuffer)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">PorterDuff.Mode.DARKEN (framebuffer)</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">PorterDuff.Mode.LIGHTEN (framebuffer)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">PorterDuff.Mode.LIGHTEN (framebuffer)</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">PorterDuff.Mode.OVERLAY (framebuffer)</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">PorterDuff.Mode.OVERLAY (framebuffer)</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
         <td colspan="5" class="s5">Shader</td>
     </tr>
     <tr>
-        <td class="s10">ComposeShader inside ComposeShader</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">ComposeShader inside ComposeShader</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">Same type shaders inside ComposeShader</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
+        <td class="label_neg">Same type shaders inside ComposeShader</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">Local matrix on ComposeShader</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
+        <td class="label_pos">Local matrix on ComposeShader</td>
+        <td class="value_pos">18</td>
     </tr>
     </tbody>
   </table>
@@ -530,64 +427,39 @@
     <tbody>
     <tr class="rShim">
         <td class="rShim" style="width:380px;"></td>
-        <td class="rShim" style="width:120px;"></td>
-        <td class="rShim" style="width:120px;"></td>
-        <td class="rShim" style="width:120px;"></td>
+        <td class="rShim" style="width:240px;"></td>
     </tr>
     <tr>
-        <td rowspan="2" class="s0"></td>
-        <td colspan="4" class="s1">API level</td>
+        <td class="s5">Drawing operation to be scaled</td>
+        <td class="s1">First supported API level</td>
     </tr>
     <tr>
-        <td style="display:none;"></td>
-        <td class="s2">&lt; 17</td>
-        <td class="s3">17</td>
-        <td class="s2">18</td>
+        <td class="label_pos">drawText()</td>
+        <td class="value_pos">18</td>
     </tr>
     <tr>
-        <td colspan="5" class="s5">Support for large scale factors</td>
+        <td class="label_neg">drawPosText()</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">drawText()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_pos">&#10003;</td>
+        <td class="label_neg">drawTextOnPath()</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">drawPosText()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
+        <td class="label_pos">Simple Shapes*</td>
+        <td class="value_pos">17</td>
     </tr>
     <tr>
-        <td class="s10">drawTextOnPath()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
+        <td class="label_neg">Complex Shapes*</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">Simple Shapes*</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_pos">&#10003;</td>
-        <td class="g_pos">&#10003;</td>
+        <td class="label_neg">drawPath()</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     <tr>
-        <td class="s10">Complex Shapes*</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-    </tr>
-    <tr>
-        <td class="s10">drawPath()</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
-    </tr>
-    <tr>
-        <td class="s10">Shadow layer</td>
-        <td class="g_neg">&#10007;</td>
-        <td class="w_neg">&#10007;</td>
-        <td class="g_neg">&#10007;</td>
+        <td class="label_neg">Shadow layer</td>
+        <td class="value_neg">&#10007;</td>
     </tr>
     </tbody>
   </table>
diff --git a/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html b/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html
index 94b5977..6375d9a 100644
--- a/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html
+++ b/docs/html/reference/android/preview/support/v4/app/NotificationManagerCompat.html
@@ -1030,7 +1030,17 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification. </p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>id</td>
+          <td>the ID of the notification
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1059,7 +1069,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification. </p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Cancel a previously shown notification.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>tag</td>
+          <td>the string identifier of the notification.</td>
+        </tr>
+        <tr>
+          <th>id</td>
+          <td>the ID of the notification
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1177,7 +1201,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc. </p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>id</td>
+          <td>the ID of the notification</td>
+        </tr>
+        <tr>
+          <th>notification</td>
+          <td>the notification to post to the system
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1206,7 +1244,25 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc. </p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Post a notification to be shown in the status bar, stream, etc.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>tag</td>
+          <td>the string identifier for a notification. Can be <code>null</code>.</td>
+        </tr>
+        <tr>
+          <th>id</td>
+          <td>the ID of the notification. The pair (tag, id) must be unique within your app.</td>
+        </tr>
+        <tr>
+          <th>notification</td>
+          <td>the notification to post to the system
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html
index 307fc2a..6fbf8b6 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html
@@ -886,12 +886,15 @@
       <table class="jd-tagtable">
         <tr>
           <th>returnKey</td>
-          <td>the extras key to be set with input collected from the user
-         when the intent is sent.
-</td>
+          <td>the intent extras key that refers to the input collected from the user</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -966,10 +969,24 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Specifies whether the user can provide arbitrary values.  The
- default is <code>true</code>.  If this is set to <code>false</code>, a
- non-null non-empty value should be passed to <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])">setChoices(String[])</a></code>.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Specifies whether the user can provide arbitrary values.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>allowFreeFormInput</td>
+          <td>The default is <code>true</code>.
+         If you specify <code>false</code>, you must
+         provide a non-null and non-empty array to <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])">setChoices(String[])</a></code> or
+         an <code><a href="http://developer.android.com/reference/java/lang/IllegalArgumentException.html">IllegalArgumentException</a></code> is thrown.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -998,8 +1015,23 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Specifies choices available to the user to satisfy this input.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Specifies choices available to the user to satisfy this input.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>choices</td>
+          <td>an array of pre-defined choices for users input.
+        You must provide a non-null and non-empty array if
+        you set <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#allowFreeFormInput">allowFreeFormInput</a></code> to <code>false</code>.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1028,8 +1060,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Set a label to be displayed to the user when collecting this input.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Set a label to be displayed to the user when collecting this input.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>label</td>
+          <td>The label to show to users when they input a response.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html
index e8aa651..0e1cebe 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/RemoteInput.html
@@ -467,22 +467,27 @@
 
 
 <h2>Class Overview</h2>
-<p itemprop="articleBody">A RemoteInput specifies a response to be collected from the user as part of an intent being
- sent. For example, when used with a notification Action, a response may be collected
- when the user triggers the action, and the results sent as data along with the action's
- PendingIntent. The result value is set in the extras of the triggered Intent with the key
- <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#returnKey">returnKey</a></code>.
+<p itemprop="articleBody">A <code>RemoteInput</code> object collects a response from users and sets the
+ response as an intent extra inside the <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code> that is sent.
+ Always use <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html">RemoteInput.Builder</a></code> to create instances of this class.
+ <p class="note"> See
+ <a href="/wear/notifications/remote-input.html">Receiving Voice Input from
+ a Notification</a> for more information on how to use this class.
 
- <p>Use the builder class <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html">RemoteInput.Builder</a></code> to create this object.
-
- <p>Example which adds a RemoteInput to an Action:
+ <p>The following example adds a <code>RemoteInput</code> to a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>,
+ sets the intent extra key as <code>quick_reply</code>, and sets the label as <code>Quick Reply</code>.
+ Users are prompted to input a response when they trigger the action. The results are sent as an
+ intent extra with the key of <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#returnKey">returnKey</a></code> in the action's
+ <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code>.
 
  <pre class="prettyprint">
+ public static final String EXTRA_QUICK_REPLY_TEXT = "quick_reply";
  WearableNotifications.Action action = new WearableNotifications.Action.Builder(
          R.drawable.reply, &quot;Reply&quot;, actionIntent)
-         .addRemoteInput(new RemoteInput.Builder(EXTRA_QUICK_REPLY_TEXT)
-                 .setLabel("Quick reply").build())
+         <b>.addRemoteInput(new RemoteInput.Builder(EXTRA_QUICK_REPLY_TEXT)
+                 .setLabel("Quick reply").build()</b>)
          .build();</pre>
+
 </p>
 
 
@@ -619,8 +624,8 @@
           final
           boolean</nobr></td>
           <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#allowFreeFormInput">allowFreeFormInput</a></td>
-          <td class="jd-descrcol" width="100%">Indicates whether or not the user may provide an arbitrary value for
- this input.</td>
+          <td class="jd-descrcol" width="100%">Indicates whether or not users can provide an arbitrary value for
+ input.</td>
       </tr>
       
     
@@ -631,7 +636,7 @@
           final
           <a href="http://developer.android.com/reference/java/lang/String.html">String[]</a></nobr></td>
           <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></td>
-          <td class="jd-descrcol" width="100%">The choices available to the user.</td>
+          <td class="jd-descrcol" width="100%">Possible input choices.</td>
       </tr>
       
     
@@ -642,7 +647,7 @@
           final
           <a href="http://developer.android.com/reference/java/lang/String.html">String</a></nobr></td>
           <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#label">label</a></td>
-          <td class="jd-descrcol" width="100%">The label to be displayed to the user when collecting this input.</td>
+          <td class="jd-descrcol" width="100%">The label to display to users when collecting this input.</td>
       </tr>
       
     
@@ -653,8 +658,7 @@
           final
           <a href="http://developer.android.com/reference/java/lang/String.html">String</a></nobr></td>
           <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#returnKey">returnKey</a></td>
-          <td class="jd-descrcol" width="100%">The extras key to be populated with input from the user when the
- intent is sent.</td>
+          <td class="jd-descrcol" width="100%">The lookup key for the intent extra that the response is set in.</td>
       </tr>
       
     
@@ -1062,10 +1066,10 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Indicates whether or not the user may provide an arbitrary value for
- this input.  If set to false, then the user should select one of the
- provided choices.  It is an error to set this to <code>false</code> and
- not provide <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></code>.
+  <div class="jd-tagdata jd-tagdescr"><p>Indicates whether or not users can provide an arbitrary value for
+ input. If you set this to <code>false</code>, users must select one of the
+ provided <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></code>. An <code><a href="http://developer.android.com/reference/java/lang/IllegalArgumentException.html">IllegalArgumentException</a></code> is thrown
+ if you set this to false and <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html#choices">choices</a></code> is <code>null</code> or empty.
 </p></div>
 
     
@@ -1094,8 +1098,8 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>The choices available to the user.  May be null if there are no choices
- to present to the user.
+  <div class="jd-tagdata jd-tagdescr"><p>Possible input choices. This can be <code>null</code>
+ if there are no choices to present.
 </p></div>
 
     
@@ -1124,7 +1128,7 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>The label to be displayed to the user when collecting this input.
+  <div class="jd-tagdata jd-tagdescr"><p>The label to display to users when collecting this input.
 </p></div>
 
     
@@ -1153,8 +1157,8 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>The extras key to be populated with input from the user when the
- intent is sent.
+  <div class="jd-tagdata jd-tagdescr"><p>The lookup key for the intent extra that the response is set in. This is populated
+ when the <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code> is sent.
 </p></div>
 
     
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html
index 884de4a..f27d406 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html
@@ -448,15 +448,6 @@
 
 <h2>Class Overview</h2>
 <p itemprop="articleBody">Builder class for <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> objects.
-
- <p>Example:
-
- <pre class="prettyprint">
- WearableNotifications.Builder builder = new WearableNotifications.Builder(mContext)
-         .addAction(new WearableNotifications.Action.Builder(
-                 R.drawable.navigate, &quot;Navigate&quot, pendingIntent)
-                 .build());
- Notification notif = builder.build();</pre>
 </p>
 
 
@@ -525,7 +516,7 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html#WearableNotifications.Action.Builder(int, java.lang.CharSequence, android.app.PendingIntent)">WearableNotifications.Action.Builder</a></span>(int icon, <a href="http://developer.android.com/reference/java/lang/CharSequence.html">CharSequence</a> title, <a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a> intent)</nobr>
         
-        <div class="jd-descrdiv">Construct a new builder for an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.</div>
+        <div class="jd-descrdiv">Construct a new builder for <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.</div>
   
   </td></tr>
 
@@ -872,8 +863,25 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Construct a new builder for an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Construct a new builder for <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> object.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>icon</td>
+          <td>icon to show for this action</td>
+        </tr>
+        <tr>
+          <th>title</td>
+          <td>the title of the action</td>
+        </tr>
+        <tr>
+          <th>intent</td>
+          <td>the <code><a href="http://developer.android.com/reference/android/app/PendingIntent.html">PendingIntent</a></code> to fire when users trigger this action
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -918,9 +926,22 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Add an input to be collected from the user when this action is sent.
- Response values are sent as extras to this Action's pending intent when
- sent.
-</p></div>
+ Response values are sent as extras to this action's pending intent when
+ sent.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>remoteInput</td>
+          <td>a <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> to add to the action</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -950,8 +971,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Combine all of the options that have been set and return a new <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>
- object.
-</p></div>
+ object.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the built action
+</li></ul>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html
index e073881..ff9c904 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html
@@ -474,14 +474,11 @@
 
 
 <h2>Class Overview</h2>
-<p itemprop="articleBody">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> which adds support for additional
- wearable extensions.
-
- <p>To create a new Action, use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html">WearableNotifications.Action.Builder</a></code> class and then call
+<p itemprop="articleBody">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> that adds additional
+ wearable extensions for actions.
+ <p>Always use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.Builder.html">WearableNotifications.Action.Builder</a></code> to build instances of this class and call
  <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#addAction(android.preview.support.wearable.notifications.WearableNotifications.Action)">addAction(WearableNotifications.Action)</a></code> to add the action to a notification.
 
- <p>Example:
-
  <pre class="prettyprint">
  WearableNotifications.Builder builder = new WearableNotifications.Builder(mContext)
          .addAction(new WearableNotifications.Action.Builder(
@@ -991,8 +988,12 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Get a list of inputs to be collected from the user when this action is sent.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Get a list of inputs to be collected from the user when this action is sent.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the array of <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects associated with this action
+</li></ul>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html
index 25e4520..d6ec260 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html
@@ -447,29 +447,11 @@
 
 
 <h2>Class Overview</h2>
-<p itemprop="articleBody">Builder object that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to provide
- methods for adding wearable extensions to a notification.
+<p itemprop="articleBody">Builder class that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to add
+ wearable extensions for a notification.
 
- <p>Methods on the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> and this object
- can be called in any order, but the final Notification must be built with
- the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> method of this class.
-
- <p>Note: Notifications created using this builder should be posted to the notification
- system using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Example:
-
- <pre class="prettyprint">
- NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
-         .setContentTitle(&quot;New mail from &quot; + sender.toString())
-         .setContentText(subject)
-         .setSmallIcon(R.drawable.new_mail);
- Notification notif = new WearableNotifications.Builder(builder)
-         .setLocalOnly(true)
-         .setMinPriority()
-         .build();
- NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
+ <p>You can chain the "set" methods for this builder in any order,
+ but you must call the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> method last to build the final notification.
 </p>
 
 
@@ -662,7 +644,7 @@
         
         <div class="jd-descrdiv">Combine all of the options that have been set by both this builder and
  the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> object and return a new
- <code><a href="/http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.</div>
+ <code><a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.</div>
   
   </td></tr>
 
@@ -826,7 +808,7 @@
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setMinPriority()">setMinPriority</a></span>()</nobr>
         
         <div class="jd-descrdiv">Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
   
   </td></tr>
 
@@ -1105,14 +1087,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Construct a builder to be used for adding wearable extensions to notifications. Both the
- wrapped builder (accessible via <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#getCompatBuilder()">getCompatBuilder()</a></code> and this builder can be used
+ wrapped builder (accessible via <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#getCompatBuilder()">getCompatBuilder()</a></code>) and this builder can be used
  simultaneously, but the build() method from this object must be called in the end.
 
- <p>Note: Notifications created using this builder should be posted to the notification
- system using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Example:
+ <p>Always post notifications to the notification
+ system with the <code>NotificationManagerCompat.notify(...)</code> methods
+ instead of the <code>NotificationManager.notify(...)</code> methods.
 
  <pre class="prettyprint">
  WearableNotifications.Builder builder = new WearableNotifications.Builder(mContext)
@@ -1156,12 +1136,9 @@
  a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code>. Both the wrapped builder and this
  builder can be used simultaneously, but the build() method from this object must be
  called in the end.
-
- <p>Note: Notifications created using this builder should be posted to the notification
- system using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Example:
+ <p>Always post notifications to the notification
+ system with the <code>NotificationManagerCompat.notify(...)</code> methods
+ instead of the <code>NotificationManager.notify(...)</code> methods.
 
  <pre class="prettyprint">
  NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
@@ -1221,6 +1198,19 @@
  accepts <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> extension wrappers. Actions added by this function
  are appended when <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> is called.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>action</td>
+          <td>the action to add to this notification</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code></li>
       </ul>
@@ -1258,6 +1248,19 @@
  subsequent pages. This field can be used to separate a notification into multiple
  sections.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>page</td>
+          <td>the notification to add as another page</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getPages(android.app.Notification)">getPages(Notification)</a></code></li>
       </ul>
@@ -1295,6 +1298,19 @@
  subsequent pages. This field can be used to separate a notification into multiple
  sections.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>pages</td>
+          <td>a collection of notifications</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getPages(android.app.Notification)">getPages(Notification)</a></code></li>
       </ul>
@@ -1328,8 +1344,21 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Adds a <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> for the content intent.  The collected
- data will be overlayed onto the content intent.
-</p></div>
+ data will be overlayed onto the content intent.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>input</td>
+          <td>a <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> object to obtain a user response</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1360,8 +1389,12 @@
       
   <div class="jd-tagdata jd-tagdescr"><p>Combine all of the options that have been set by both this builder and
  the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> object and return a new
- <code><a href="/http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.
-</p></div>
+ <code><a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a></code> object.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the notification
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1390,8 +1423,12 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Return the <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> being wrapped by this object.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Return the <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> being wrapped by this object.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the wrapped <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code>
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1423,8 +1460,12 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the current metadata Bundle used by this Builder, creating a new one
  as necessary.
 
- <p>The returned Bundle is shared with this Builder.
-</p></div>
+ <p>The returned Bundle is shared with this Builder.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the extras bundle
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1460,15 +1501,19 @@
       <table class="jd-tagtable">
         <tr>
           <th>icon</td>
-          <td>Icon to display for the content action.</td>
+          <td>icon to display for the content action.</td>
         </tr>
         <tr>
           <th>subtext</td>
-          <td>Optional subtext to display with the big action icon.
-</td>
+          <td>Optional subtext to display with the big action icon.</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1504,11 +1549,15 @@
       <table class="jd-tagtable">
         <tr>
           <th>icon</td>
-          <td>Icon to display for the content action.
-</td>
+          <td>icon to display for the content action.</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1551,11 +1600,15 @@
           <th>groupOrder</td>
           <td>The 0-indexed sort order within the group. Can also be set
          to the sentinel value <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></code> to mark this
-         notification as being the group summary.
-</td>
+         notification as being the group summary.</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1592,11 +1645,15 @@
       <table class="jd-tagtable">
         <tr>
           <th>groupKey</td>
-          <td>The group key of the group. Unique within a package.
-</td>
+          <td>The group key of the group. Unique within a package.</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1625,8 +1682,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>hintHideIcon</td>
+          <td><code>true</code> to hide the icon, <code>false</code> otherwise.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1660,6 +1730,20 @@
  <p>Some notifications can be bridged to other devices for remote display.
  This hint can be set to recommend this notification not be bridged.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>localOnly</td>
+          <td><code>true</code> to keep the notification on this device,
+ <code>false</code> otherwise. Default value is <code>false</code>.</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getLocalOnly(android.app.Notification)">getLocalOnly(Notification)</a></code></li>
       </ul>
@@ -1693,10 +1777,14 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
  minimum priority notifications will bypass the notification manager on platforms
- that do not support ambient level notifications.
-</p></div>
+ that do not support ambient level notifications.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>this object for method chaining
+</li></ul>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html
index 45b77c6..e03e16e 100644
--- a/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html
+++ b/docs/html/reference/android/preview/support/wearable/notifications/WearableNotifications.html
@@ -450,15 +450,39 @@
 
 
 <h2>Class Overview</h2>
-<p itemprop="articleBody">Helper providing extensions to android notifications for use with wearable devices.
+<p itemprop="articleBody">Helper class that contains wearable extensions for notifications.
+ Always use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code> to create instances of this class.
+ <p class="note"> See
+ <a href="/wear/notifications/creating.html">Creating Notifications
+ for Android Wear</a> for more information on how to use this class.
+ <p>
+ To create a notification with wearable extensions:
+ <ol>
+   <li>Create a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code>, setting any desired
+   properties.
+   <li>Create a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code>, passing in the
+   <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> as a starting point.
+   <li>Set wearable-specific properties using the
+   <code>add</code> and <code>set</code> methods of <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code>.
+   <li>Call <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#build()">build()</a></code> to create the notification.
+   <li>Post the notification to the notification
+   system with the <code>NotificationManagerCompat.notify(...)</code> methods
+   and not the <code>NotificationManager.notify(...)</code> methods.
+ </ol>
 
- <p>To build notifications with wearable extensions, use the <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></code> class.
- Notifications created using Builder should be posted to the notification system
- using the <code>NotificationManagerCompat.notify(...)</code> methods instead of
- <code>NotificationManager.notify(...)</code>.
-
- <p>Once a notification is built, a variety of methods are provide in this utility to access
- the value of various notification fields in a backwards compatible manner.
+ <pre class="prettyprint">
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext)
+         .setContentTitle(&quot;New mail from &quot; + sender.toString())
+         .setContentText(subject)
+         .setSmallIcon(R.drawable.new_mail);
+ Notification notif = new WearableNotifications.Builder(builder)
+         .setLocalOnly(true)
+         .setMinPriority()
+         .build();
+ NotificationManagerCompat.from(mContext).notify(0, notif);</pre>
+ <p>When you receive a notification object from the builder, the methods in
+ this class let you access the values of various notification fields in
+ a backward-compatible manner.
 </p>
 
 
@@ -502,8 +526,8 @@
         
         class</nobr></td>
       <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></td>
-      <td class="jd-descrcol" width="100%">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> which adds support for additional
- wearable extensions.&nbsp;</td>
+      <td class="jd-descrcol" width="100%">Subclass of <code><a href="/reference/android/support/v4/app/NotificationCompat.Action.html">NotificationCompat.Action</a></code> that adds additional
+ wearable extensions for actions.&nbsp;</td>
     </tr>
     
     
@@ -515,8 +539,8 @@
         
         class</nobr></td>
       <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html">WearableNotifications.Builder</a></td>
-      <td class="jd-descrcol" width="100%">Builder object that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to provide
- methods for adding wearable extensions to a notification.&nbsp;</td>
+      <td class="jd-descrcol" width="100%">Builder class that wraps a <code><a href="/reference/android/support/v4/app/NotificationCompat.Builder.html">NotificationCompat.Builder</a></code> to add
+ wearable extensions for a notification.&nbsp;</td>
     </tr>
     
     
@@ -548,8 +572,9 @@
     <tr class=" api apilevel-" >
         <td class="jd-typecol">int</td>
         <td class="jd-linkcol"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></td>
-        <td class="jd-descrcol" width="100%">Sentinel value provided to the groupOrder parameter <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String)">setGroup(Notification, String)</a></code> to indicate that
- this member of a notification group is the summary of the group.</td>
+        <td class="jd-descrcol" width="100%">Sentinel value provided to the <code>groupOrder</code> parameter of the
+ <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String, int)">setGroup(android.app.Notification, java.lang.String, int)</a></code>
+ method.</td>
     </tr>
     
     
@@ -589,8 +614,8 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getAction(android.app.Notification, int)">getAction</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif, int actionIndex)</nobr>
         
-        <div class="jd-descrdiv">Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at index <code>actionIndex</code>
- in the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.</div>
+        <div class="jd-descrdiv">Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at <code>actionIndex</code>
+ in the <code><a href="http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.</div>
   
   </td></tr>
 
@@ -662,7 +687,7 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getContentIntentRemoteInputs(android.app.Notification)">getContentIntentRemoteInputs</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif)</nobr>
         
-        <div class="jd-descrdiv">Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code>s associated with the content intent.</div>
+        <div class="jd-descrdiv">Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects associated with the content intent.</div>
   
   </td></tr>
 
@@ -680,8 +705,8 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#getExtras(android.app.Notification)">getExtras</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif)</nobr>
         
-        <div class="jd-descrdiv">Gets the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backwards
- compatible manner.</div>
+        <div class="jd-descrdiv">Gets the <code><a href="http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backward-compatible
+ manner.</div>
   
   </td></tr>
 
@@ -919,7 +944,7 @@
         <span class="sympad"><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setMinPriority(android.app.Notification)">setMinPriority</a></span>(<a href="http://developer.android.com/reference/android/app/Notification.html">Notification</a> notif)</nobr>
         
         <div class="jd-descrdiv">Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>).</div>
   
   </td></tr>
 
@@ -1246,8 +1271,10 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Sentinel value provided to the groupOrder parameter <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String)">setGroup(Notification, String)</a></code> to indicate that
- this member of a notification group is the summary of the group.
+  <div class="jd-tagdata jd-tagdescr"><p>Sentinel value provided to the <code>groupOrder</code> parameter of the
+ <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setGroup(android.app.Notification, java.lang.String, int)">setGroup(android.app.Notification, java.lang.String, int)</a></code>
+ method. This value indicates that this index of the
+ notification group is the summary of the group.
 </p></div>
 
     
@@ -1309,9 +1336,22 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at index <code>actionIndex</code>
- in the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Get a <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code> wrapper for the notification at <code>actionIndex</code>
+ in the <code><a href="http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code> array.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+        <tr>
+          <th>actionIndex</td>
+          <td>the index of the desired action
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1342,6 +1382,20 @@
       
   <div class="jd-tagdata jd-tagdescr"><p>Get the number of actions present on this notification.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the number of actions for this notification
+</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/http://developer.android.com/reference/android/app/Notification.html#actions">actions</a></code></li>
       </ul>
@@ -1377,6 +1431,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the big action icon to be displayed with this notification. Big actions show
  a hint to users about the action taken when the content intent is triggered.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the icon or <code>0</code> if it wasn't set</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setBigActionIcon(android.app.Notification, int)">setBigActionIcon(Notification, int)</a></code></li>
       </ul>
@@ -1411,6 +1478,19 @@
       
   <div class="jd-tagdata jd-tagdescr"><p>Get the big action icon subtext to be shown with a big action icon.</p></div>
   <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the subtext of the big action icon or <code>null</code> if it wasn't exist.</li></ul>
+  </div>
+  <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
       <ul class="nolist"><li><code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#setBigActionIcon(android.app.Notification, int)">setBigActionIcon(Notification, int)</a></code></li>
       </ul>
@@ -1443,8 +1523,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code>s associated with the content intent.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects associated with the content intent.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>array of RemoteInput objects or <code>null</code> if it doesn't exist
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1473,10 +1566,23 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="/http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backwards
- compatible manner. Extras field was supported from JellyBean (Api level 16)
- forwards. This function will return null on older api levels.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Gets the <code><a href="http://developer.android.com/reference/android/app/Notification.html#extras">extras</a></code> field from a notification in a backward-compatible
+ manner. Extras field was supported from JellyBean (API level 16)
+ forwards. This function will return null on older API levels.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the extras associated with this notification.
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1506,8 +1612,17 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Get the key used to group this notification into a cluster or stack
- with other notifications. This key is unique within a package.
-</p></div>
+ with other notifications. This key is unique within a package.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1539,8 +1654,21 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the sort order of this notification within a group of notifications
  with the same group key set. Group orders are 0-indexed integers that are used
  to sort notifications in ascending order. Can also be the sentinel value
- <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></code> if this is the summary notification for a group.
-</p></div>
+ <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY">GROUP_ORDER_SUMMARY</a></code> if this is the summary notification for a group.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the sort order of this notification within this group
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1569,8 +1697,22 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Get a hint that this notification's icon should not be displayed.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Get a hint that this notification's icon should not be displayed.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li><code>true</code> if this icon should not be displayed, false otherwise.
+ The default value is <code>false</code> if this was never set.
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1602,8 +1744,22 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get whether or not this notification is only relevant to the current device.
 
  <p>Some notifications can be bridged to other devices for remote display.
- If this hint is set, it is recommended that this notification not be bridged.
-</p></div>
+ If this hint is set, it is recommended that this notification not be bridged.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li><code>true</code> if this notification is local only, <code>false</code> otherwise.
+        Default value is <code>false</code> if not set.
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1635,8 +1791,21 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the array of additional pages of content for displaying this notification. The
  current notification forms the first page, and elements within this array form
  subsequent pages. This field can be used to separate a notification into multiple
- sections.
-</p></div>
+ sections.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to inspect</td>
+        </tr>
+      </table>
+  </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>the pages for this notification
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1671,8 +1840,12 @@
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
         <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
           <th>icon</td>
-          <td>Icon to display for the content action.
+          <td>icon to display for the content action.
 </td>
         </tr>
       </table>
@@ -1711,8 +1884,12 @@
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
         <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
           <th>icon</td>
-          <td>Icon to display for the content action.</td>
+          <td>icon to display for the content action.</td>
         </tr>
         <tr>
           <th>subtext</td>
@@ -1751,8 +1928,21 @@
       
   <div class="jd-tagdata jd-tagdescr"><p>Sets <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code>s to be collected when the user triggers the
  <code>contentIntent</code>.  These function just as if they were attached to
- an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>.
-</p></div>
+ an <code><a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Action.html">WearableNotifications.Action</a></code>.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
+          <th>inputs</td>
+          <td>array of <code><a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">RemoteInput</a></code> objects desired from the user.
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1788,6 +1978,10 @@
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
         <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
           <th>groupKey</td>
           <td>The group key of the group. Unique within a package.
 </td>
@@ -1829,6 +2023,10 @@
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
         <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
           <th>groupKey</td>
           <td>The group key of the group. Unique within a package.</td>
         </tr>
@@ -1869,8 +2067,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Set a hint that this notification's icon should not be displayed.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
+          <th>hintHideIcon</td>
+          <td><code>true</code> to hide this icon, <code>false</code> otherwise.
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1902,8 +2113,22 @@
   <div class="jd-tagdata jd-tagdescr"><p>Set whether or not this notification is only relevant to the current device.
 
  <p>Some notifications can be bridged to other devices for remote display.
- This hint can be set to recommend this notification not be bridged.
-</p></div>
+ This hint can be set to recommend this notification not be bridged.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
+          <th>localOnly</td>
+          <td>set to <code>true</code> to keep the notification on this device only,
+        <code>false</code> otherwise.
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1933,10 +2158,19 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Set the priority of this notification to be minimum priority level
- (<code><a href="/http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
+ (<code><a href="http://developer.android.com/reference/android/app/Notification.html#PRIORITY_MIN">PRIORITY_MIN</a></code>). When set via WearableNotifications, these
  minimum priority notifications will bypass the notification manager on platforms
- that do not support ambient level notifications.
-</p></div>
+ that do not support ambient level notifications.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
@@ -1968,8 +2202,22 @@
   <div class="jd-tagdata jd-tagdescr"><p>Set additional pages of content to display with this notification. The current
  notification forms the first page, and pages set using this function form
  subsequent pages. This field can be used to separate a notification into multiple
- sections.
-</p></div>
+ sections.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>notif</td>
+          <td>the notification to modify</td>
+        </tr>
+        <tr>
+          <th>pages</td>
+          <td>the pages to add to the current notification. Replaces any
+ existing pages with this value.
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/training/displaying-bitmaps/cache-bitmap.jd b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
index ff9c3a0..7b9216e 100644
--- a/docs/html/training/displaying-bitmaps/cache-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/cache-bitmap.jd
@@ -24,8 +24,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/display-bitmap.jd b/docs/html/training/displaying-bitmaps/display-bitmap.jd
index ed1836c..4147f83 100644
--- a/docs/html/training/displaying-bitmaps/display-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/display-bitmap.jd
@@ -24,8 +24,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/index.jd b/docs/html/training/displaying-bitmaps/index.jd
index 7003585..831c64d 100644
--- a/docs/html/training/displaying-bitmaps/index.jd
+++ b/docs/html/training/displaying-bitmaps/index.jd
@@ -18,8 +18,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/load-bitmap.jd b/docs/html/training/displaying-bitmaps/load-bitmap.jd
index 938901f..f963baa 100644
--- a/docs/html/training/displaying-bitmaps/load-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/load-bitmap.jd
@@ -18,8 +18,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/manage-memory.jd b/docs/html/training/displaying-bitmaps/manage-memory.jd
index 7f2b4c5..5a5c2cd 100644
--- a/docs/html/training/displaying-bitmaps/manage-memory.jd
+++ b/docs/html/training/displaying-bitmaps/manage-memory.jd
@@ -26,8 +26,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/training/displaying-bitmaps/process-bitmap.jd b/docs/html/training/displaying-bitmaps/process-bitmap.jd
index ed0b368..a557619 100644
--- a/docs/html/training/displaying-bitmaps/process-bitmap.jd
+++ b/docs/html/training/displaying-bitmaps/process-bitmap.jd
@@ -26,8 +26,8 @@
 <h2>Try it out</h2>
 
 <div class="download-box">
-  <a href="{@docRoot}shareables/training/BitmapFun.zip" class="button">Download the sample</a>
-  <p class="filename">BitmapFun.zip</p>
+  <a href="{@docRoot}downloads/samples/DisplayingBitmaps.zip" class="button">Download the sample</a>
+  <p class="filename">DisplayingBitmaps.zip</p>
 </div>
 
 </div>
diff --git a/docs/html/wear/css/wear.css b/docs/html/wear/css/wear.css
index 9d9d7a7..40afeaa 100644
--- a/docs/html/wear/css/wear.css
+++ b/docs/html/wear/css/wear.css
@@ -2,6 +2,7 @@
  * UTILITIES
  */
 
+
 .border-box {
   box-sizing: border-box;
 }
@@ -92,6 +93,10 @@
  * LAYOUT
  */
 
+#body-content,
+.fullpage,
+#jd-content,
+.jd-descr,
 .wear-body-content {
   height: 100%;
 }
@@ -134,13 +139,11 @@
 }
 
 .wear-hero-container {
-  height: 800px;
-  height: 100vh;
+  height: 100%;
 }
 
 .wear-hero {
-  height: 100%;
-  height: calc(100% - 72px);
+  height: calc(100% - 70px);
   min-height: 504px;
   margin-top: -4px;
   padding-top: 0;
@@ -184,6 +187,7 @@
 }
 
 .wear-button {
+  white-space: nowrap;
   display: inline-block;
   padding: 16px 32px;
   font-size: 18px;
@@ -233,6 +237,7 @@
 }
 
 .wear-video-link {
+  white-space: nowrap;
   display: inline-block;
   padding: 16px 32px 16px 82px;
   font-size: 18px;
@@ -436,7 +441,7 @@
 #icon-video-close {
 background-image: url("../images/close.png");
 background-position: 0 0;
-height: 48px;
-width: 48px;
+height: 36px;
+width: 36px;
 display:block;
-}
\ No newline at end of file
+}
diff --git a/docs/html/wear/design/index.html b/docs/html/wear/design/index.html
index 4bbbf29..9952490 100644
--- a/docs/html/wear/design/index.html
+++ b/docs/html/wear/design/index.html
@@ -379,11 +379,15 @@
 }
 </style>
 <p>
-Android Wear devices provide just the right information at just the right time, allowing you to be connected to the virtual world and present in the real world.</p>
+Android wearables provide just the right information at just the right time, allowing you to be connected to the virtual world and present in the real world.</p>
 
 <img src="/wear/images/05_images.png" height="200" width="169" style="float:right;clear:right;margin:0 0 60px 60px" />
 
-<p>Here you’ll find some guidelines for designing great user experiences on the Android Wear platform. Designing for Android Wear is substantially different than designing for phones or tablets, so we’ll start by describing how your content can work in tandem with the overall Android Wear vision.</p>
+<p>Here you’ll find some guidelines for designing great user experiences on the Android Wear
+platform. Designing for Android Wear is substantially different than designing for phones or
+tablets, so we’ll start by describing how your content can work in tandem with the overall
+Android Wear vision. To better understand the user experience on Android Wear, also be sure
+to read the <a href="/wear/design/user-interface.html">UI Overview</a>.</p>
 
 
 <img src="/wear/images/02_notifications.png" height="200" width="169" style="float:right;clear:right;margin:0 0 20px 60px" />
@@ -393,7 +397,7 @@
 <p>Android Wear experiences are:</p>
 
 <ul>
-  <li><strong>Contextually aware and smart.</strong> These devices bring a new level of awareness to computing. Rather than requiring attention and input from users, Android Wear devices are aware of their situation and state, and helpfully display the right information at the right time. <em>Timely, relevant, specific</em>.</li>
+  <li><strong>Contextually aware and smart.</strong> These devices bring a new level of awareness to computing. Rather than requiring attention and input from users, Android wearables are aware of their situation and state, and helpfully display the right information at the right time. <em>Timely, relevant, specific</em>.</li>
 
   <li><strong>Glanceable.</strong> Wearable devices are used all throughout the day, even when they sit in our peripheral vision. Effective apps provide the maximum payload of information with a minimum of fuss, optimized to provide tiny snippets of relevant information throughout the day. <em>Short, sharp, immediate.</em></li>
 
@@ -462,7 +466,8 @@
 
 <img src="/wear/images/07_appicons.png" height="200" style="float:right;margin:0 0 20px 60px" />
 
-<p>Your application’s launcher icon will be automatically placed on the card, identifying your notification. Do not use the notification title or background image to identify or brand your application. Instead, allow your icon to identify itself and focus on delivering a clear, succinct message in the card and image. You can choose not to display this icon.
+<p>Your application’s launcher icon will be automatically placed on the card, identifying your notification. Do not use the notification title or background image to identify or brand your application. Instead, allow your icon to identify itself and focus on delivering a clear, succinct message in the card and image. You can choose not to display this icon using
+ <a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setHintHideIcon(boolean)"><code>setHintHideIcon()</code></a>.
 </p>
 
 
diff --git a/docs/html/wear/design/user-interface.html b/docs/html/wear/design/user-interface.html
index f87b9da..c23d79c 100644
--- a/docs/html/wear/design/user-interface.html
+++ b/docs/html/wear/design/user-interface.html
@@ -404,9 +404,9 @@
 <p>Cards in the stream are more than simple notifications. They can be swiped horizontally to
 reveal additional pages. Further horizontal swiping may reveal tappable buttons, allowing the user
 to take action on the notification. Cards can also be dismissed by swiping left to right, removing
-them from the stream until the next time they have useful information to display. In the emulator,
-hovering the mouse over the screen illuminates a blue bar at the top of the device
-that takes you home when clicked.</p>
+them from the stream until the next time they have useful information to display.
+In the emulator, hovering the mouse over the top of the screen illuminates a blue bar at
+the top of the device that takes you home when clicked.</p>
 
 
 
diff --git a/docs/html/wear/images/close.png b/docs/html/wear/images/close.png
index bd473d2..7e45fb7 100644
--- a/docs/html/wear/images/close.png
+++ b/docs/html/wear/images/close.png
Binary files differ
diff --git a/docs/html/wear/index.html b/docs/html/wear/index.html
index 5ea793b..712e1af 100644
--- a/docs/html/wear/index.html
+++ b/docs/html/wear/index.html
@@ -280,7 +280,17 @@
 
 
     <div class="jd-descr" itemprop="articleBody">
-    <div id="video-container">
+    <style>
+.fullpage>#footer,
+#jd-content>.content-footer.wrap {
+  display:none;
+}
+</style>
+
+
+
+
+<div id="video-container">
   <div id="video-frame">
     <div class="video-close">
       <span id="icon-video-close">&nbsp;</span>
@@ -294,6 +304,7 @@
 </div>
 
 
+
 <div class="wear-body-content">
   <div class="wear-hero-container">
     <div class="wear-section wear-hero">
@@ -319,7 +330,7 @@
                 <a href="/wear/preview/start.html" class="wear-button wear-primary" style="margin-top: 40px;">
                   Get the Developer Preview
                 </a>
-                <a id="watchVideo" href="https://youtube.googleapis.com/v/i2uvYI6blEE">
+                <a id="watchVideo" href="https://youtube.googleapis.com/v/0xQ3y902DEQ">
                   <div class="wear-video-link">Watch the video</div>
                 </a>
 <script>
@@ -328,7 +339,7 @@
 
   var params = { allowScriptAccess: "always"};
   var atts = { id: "ytapiplayer" };
-  swfobject.embedSWF("//www.youtube.com/v/i2uvYI6blEE?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1;autoplay=1",
+  swfobject.embedSWF("//www.youtube.com/v/0xQ3y902DEQ?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1;autoplay=1",
     "ytapiplayer", "940", "526.4", "8", null, null, params, atts);
 
   e.preventDefault();
@@ -491,7 +502,7 @@
                 <img src="/wear/images/features/ts2.png" alt="">
                 <p>Send Data</p>
                 <p class="wear-small">
-                  Send data and actions between a phone and a wearable with a data replication APIs and RPCs.
+                  Send data and actions between a phone and a wearable with data replication APIs and RPCs.
                 </p>
               </div>
               <div class="col-4">
@@ -505,7 +516,7 @@
                 <img src="/wear/images/features/ts4.png" alt="">
                 <p>Voice Actions</p>
                 <p class="wear-small">
-                  Register your app to handle voice actions, like "Ok Google, take a note.""
+                  Register your app to handle voice actions, like "Ok Google, take a note."
                 </p>
               </div>
             </div>
@@ -590,30 +601,30 @@
           <div class="cols">
             <div class="wear-body">
               <div class="col-3-wide">
-                  <a href="/TODO">
+                  <a target="_blank" href="https://www.youtube.com/playlist?list=PLWz5rJ2EKKc-kIrPiq098QH9dOle-fLef">
                     <img class="wear-social-image" src="//www.google.com/images/icons/product/youtube-128.png" alt="">
                   </a>
                 <div class="wear-social-copy">
                   <p>DevBytes</p>
                   <p class="wear-small">
-                    Learn how to optimize your app notifications for wearable devices in this <a href="/TODO">DevBytes video</a> using the Android Wear Developer Preview.
+                    Learn how to optimize your app notifications for wearable devices in this <a target="_blank" href="https://www.youtube.com/playlist?list=PLWz5rJ2EKKc-kIrPiq098QH9dOle-fLef">DevBytes video</a> using the Android Wear Developer Preview.
                   </p>
                 </div>
               </div>
               <div class="col-3-wide">
-                <a href="http://android-developers.blogspot.com/">
+                <a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-wear-developer-preview.html">
                   <img class="wear-social-image" src="/wear/images/blogger.png" alt="">
                 </a>
                 <div class="wear-social-copy">
                   <p>Blog Post</p>
                   <p class="wear-small">
                     Read more about the Android Wear Developer Preview announcement
-                    at the <a href="http://android-developers.blogspot.com/">Android Developers Blog</a>.
+                    at the <a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-wear-developer-preview.html">Android Developers Blog</a>.
                   </p>
                 </div>
               </div>
               <div class="col-3-wide">
-                <a href="http://g.co/androidweardev">
+                <a target="_blank" href="http://g.co/androidweardev">
                   <img class="wear-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="+Android Wear Developers">
                 </a>
                 <div class="wear-social-copy">
@@ -622,7 +633,7 @@
                     Follow us on Google+ to stay up-to-date on Android Wear development and join the discussion!
                   </p>
                   <p class="wear-small">
-                    <a href="http://g.co/androidweardev">+Android Wear Developers</a>
+                    <a target="_blank" href="http://g.co/androidweardev">+Android Wear Developers</a>
                   </p>
                 </div>
               </div>
@@ -631,6 +642,25 @@
         </div> <!-- end .wrap -->
       </div> <!-- end .wear-section -->
     </div> <!-- end .wear-rest-of-page -->
+
+
+    <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
+      <div class="layout-content-col col-16" style="padding-top:4px">
+        <style>#___plusone_0 {float:right !important;}</style>
+        <div class="g-plusone" data-size="medium"></div>
+      </div>
+    </div>
+    <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px">
+      <div id="copyright">
+        Except as noted, this content is
+        licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+        Creative Commons Attribution 2.5</a>. For details and
+        restrictions, see the <a href="/license.html">Content
+        License</a>.
+      </div>
+    </div>
+
+
   </div> <!-- end wear-body-content -->
 
   <script>
@@ -641,6 +671,8 @@
     e.preventDefault();
   });
   </script>
+
+
     </div>
 
       <div class="content-footer wrap"
diff --git a/docs/html/wear/notifications/pages.html b/docs/html/wear/notifications/pages.html
index abff8fa..ce568eb 100644
--- a/docs/html/wear/notifications/pages.html
+++ b/docs/html/wear/notifications/pages.html
@@ -377,7 +377,7 @@
 <img src="/wear/images/08_pages.png" height="200" style="float:right;margin:0 0 20px 40px" />
 
 <p>When you'd like to provide more information without requiring users
-to open your app on their phones, you can
+to open your app on their handheld device, you can
 add one or more pages to the notification on Android Wear. The additional pages
 appear immediately to the right of the main notification card.
 For information about when to use and how to design
diff --git a/docs/html/wear/notifications/remote-input.html b/docs/html/wear/notifications/remote-input.html
index 6500233..c8f6621 100644
--- a/docs/html/wear/notifications/remote-input.html
+++ b/docs/html/wear/notifications/remote-input.html
@@ -373,20 +373,24 @@
 
 
     <div class="jd-descr" itemprop="articleBody">
-    <img src="/wear/images/13_voicereply.png" height="200" width="169" style="float:right;margin:0 0 20px 60px" />
+    <img src="/wear/images/13_voicereply.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
 
 <img src="/wear/images/03_actions.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
 
 <p>If your notification includes an action to respond with text,
     such as to reply to an email, it should normally launch an activity
-    on the handheld device. However, when your notification appears on an Android Wear device, you can
+    on the handheld device. However, when your notification appears on an Android wearable, you can
     allow users to dictate a reply with voice input. You can also provide pre-defined text
-    replies for the user to select.</p>
+    messages for the user to select.</p>
 
 <p>When the user replies with voice or selects one of the available
-responses, the system delivers your app on the handheld the
-message as a string extra in the <code><a href="/reference/android/content/Intent.html">Intent</a></code> you specified
-to be used for the action.</p>
+messages, the system sends the message to your app on the connected handheld device.
+The message is attached as an extra in the <code><a href="/reference/android/content/Intent.html">Intent</a></code> you specified
+to be used for the notification action.</p>
+
+<p class="note"><strong>Note:</strong> When developing with the Android emulator,
+you must type text replies into the voice input field, so be sure you have enabled
+<strong>Hardware keyboard present</strong> in the AVD settings.</p>
 
 
 <h2 id="RemoteInput">Define the Remote Input</h2>
@@ -397,10 +401,10 @@
   <a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html"><code>RemoteInput.Builder</code></a> APIs.
     The
   <a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html"><code>RemoteInput.Builder</code></a> constructor takes a string that the system
-    will use as a key for the <code><a href="/reference/android/content/Intent.html">Intent</a></code> extra that caries the reply message
+    will use as a key for the <code><a href="/reference/android/content/Intent.html">Intent</a></code> extra that carries the reply message
     to your app on the handheld.</p>
 
-<p>For example, here's a new
+<p>For example, here's how to create a new
   <a href="/reference/android/preview/support/wearable/notifications/RemoteInput.html">
 <code>RemoteInput</code></a> object that provides a custom
     label for the voice input prompt:</p>
@@ -422,7 +426,7 @@
 <img src="/wear/images/12_voicereply.png" height="200" style="float:right;margin:0 0 20px 40px" />
 
 <p>In addition to allowing voice input, you can
-    provide up to five text responses the user can select for quick replies. Call
+    provide up to five text responses that the user can select for quick replies. Call
   <a href="/reference/android/preview/support/wearable/notifications/RemoteInput.Builder.html#setChoices(java.lang.String[])"><code>setChoices()</code></a> and pass it a string array.</p>
 
 <p>For example, you may define some responses in a resource array:</p>
@@ -465,9 +469,9 @@
 
 <pre>
 // Create intent for reply action
-Intent replyIntent = new Intent(this, ReplyService.class);
+Intent replyIntent = new Intent(this, ReplyActivity.class);
 PendingIntent replyPendingIntent =
-        PendingIntent.getService(this, 0, replyIntent, 0);
+        PendingIntent.getActivity(this, 0, replyIntent, 0);
 
 // Build the notification
 NotificationCompat.Builder replyNotificationBuilder =
@@ -563,8 +567,8 @@
         .build();
 </pre>
 
-<p>Now, when the user selects "Reply" from an Android wearable,
-    the system prompts for voice input (and provides the list of pre-defined replies, if provided).
+<p>Now, when the user selects "Reply" from an Android wearable, the system prompts the user
+    for voice input (and shows the list of pre-defined replies, if provided).
     Once the user completes a response, the system invokes
     the <code><a href="/reference/android/content/Intent.html">Intent</a></code> attached to the action and adds the
 <code>EXTRA_VOICE_REPLY</code> extra (the string
diff --git a/docs/html/wear/notifications/stacks.html b/docs/html/wear/notifications/stacks.html
index 5d10165..e4f74a0 100644
--- a/docs/html/wear/notifications/stacks.html
+++ b/docs/html/wear/notifications/stacks.html
@@ -376,15 +376,20 @@
     <img src="/wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
 <img src="/wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" />
 
-<p>When your app creates more than one notification about the same type, you should traditionally
-update the existing notification with a summary of all the notifications instead of creating multiple notifications. For instance, instead
-of three notifications for each received email, you should create one with a summary such as "3 new
-messages." To view the contents of each message, the user must then touch the notification to open
-your app.</p>
+<p>When creating notifications for a handheld device, you should always aggregate similar
+notifications into a single summary notification. For example, if your app creates notifications
+for received messages, you should not show more than one notification
+on a handheld device&mdash;when more than one is message is received, use a single notification
+to provide a summary such as "2 new messages."</p>
 
-<p>However, when a user is viewing your notifications on a wearable device, you can create
-a stack that collects all the notifications for immediate access without creating multiple
-cards in the card stream.</p>
+<p>However, a summary notification is less useful on an Android wearable because users
+are not able to read details from each message on the wearable (they must open your app on the
+handheld to view more information). So for the wearable device, you should
+group all the notifications together in a stack. The stack of notifications appears as a single
+card, which users can expand to view the details from each notification separately. The new
+<a href="/reference/android/preview/support/wearable/notifications/WearableNotifications.Builder.html#setGroup(java.lang.String, int)">
+<code>setGroup()</code></a> method makes this possible while allowing you to still provide
+only one summary notification on the handheld device.</p>
 
 <p>For details about designing notification stacks, see the
 <a href="/wear/design/index.html#NotificationStacks">Design Principles of Android
@@ -420,12 +425,10 @@
 
 <h2 id="AddSummary">Add a Summary Notification</h2>
 
-<p>It's important that you still provide a summary notification for handheld devices. So in
-addition to adding each unique notification to the same stack group, also add the summary
-notification but set its order position to be <a
-href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.
-The notification in this position does not appear in the stack on the wearable but
-appears as the only notification on the handheld.</p>
+<p>It's important that you still provide a summary notification that appears on handheld devices.
+So in addition to adding each unique notification to the same stack group, also add a summary
+notification, but set its order position to be <a
+href="/reference/android/preview/support/wearable/notifications/WearableNotifications.html#GROUP_ORDER_SUMMARY"><code>GROUP_ORDER_SUMMARY</code></a>.</p>
 
 <pre>
 Notification summaryNotification = new WearableNotifications.Builder(builder)
@@ -433,6 +436,8 @@
          .build();
 </pre>
 
+<p>This notification will not appear in your stack of notifications on the wearable, but
+appears as the only notification on the handheld device.
 
 </body>
 </html>
diff --git a/docs/html/wear/preview/start.html b/docs/html/wear/preview/start.html
index ce51c66..4aec648 100644
--- a/docs/html/wear/preview/start.html
+++ b/docs/html/wear/preview/start.html
@@ -376,12 +376,11 @@
     <div class="cols">
 
   <div class="col-5">
-<p>The Android Wear Developer Preview provides tools and APIs that allow you to
+<p>The Android Wear Developer Preview includes tools and APIs that allow you to
 enhance your app notifications
-to provide an optimized user experience on Android Wear.</p>
+to provide an optimized user experience on Android wearables.</p>
 
-<p>With the Android Wear
-Developer Preview, you can:</p>
+<p>With the Android Wear Developer Preview, you can:</p>
 
 <ul>
   <li>Run the Android Wear platform in the Android emulator.</li>
@@ -438,12 +437,12 @@
 </ol>
 
 <p class="note"><strong>Note:</strong>
-If you're already using the ADT plugin for Eclipse, you must update to version 22.6.1 or higher.
-To check for updates, select <strong>Help &gt; Check for updates</strong> in the Eclipse toolbar. </p>
+If you're using the ADT plugin for Eclipse, you must update to version 22.6.1 or higher.
+If you're using Android Studio, you must update to version 0.5.1 or higher</p>
 
 
 
-<h2 id="Install">1. Install the Android Wear system image</h2>
+<h2 id="Install">1. Install the Android Wear System Image</h2>
 
 
 <ol>
@@ -464,7 +463,9 @@
     </ol>
   </li>
 
-  <li>Below Android 4.4.2, select <strong>Android Wear ARM EABI v7a System Image</strong>.</li>
+  <li>Below Android 4.4.2, select <strong>Android Wear ARM EABI v7a System Image</strong>.
+<p class="note"><strong>Note:</strong> Android Wear is designed to support multiple processor architectures.
+</p></li>
   <li>Below Extras, ensure that you have the latest version of the
 <a href="/tools/support-library/index.html">Android Support Library</a>.
     If an update is available, select <strong>Android Support Library</strong>. If you're using Android Studio, also select <strong>Android Support Repository</strong>.</li>
@@ -490,11 +491,13 @@
 <li>For the Device, select <strong>Android Wear Square</strong> or
 	<strong>Android Wear Round</strong>.</li>
 <li>For the Target, select <strong>Android 4.4.2 - API Level 19</strong> (or higher).</li>
-<li>For the CPU/ABI, select <strong>Android Wear ARM (armeabi-v7a)</strong>.</li>
+<li>For the CPU/ABI, select <strong>Android Wear ARM (armeabi-v7a)</strong>.
+<p class="note"><strong>Note:</strong> Android Wear is designed to support multiple processor architectures.
+</p></li>
 <li>For the Skin, select <strong>AndroidWearSquare</strong> or
 <strong>AndroidWearRound</strong>.</li>
 <li>Leave all other options set to their defaults and click <strong>OK</strong>.
-  <p>Although real Android Wear devices do not provide a keyboard as an input method,
+  <p>Although real Android wearables do not provide a keyboard as an input method,
     you should keep <strong>Hardware keyboard present</strong> selected so you can
     provide text input on screens where users will instead provide voice input.</p>
 </li>
@@ -526,8 +529,8 @@
 
 <h2 id="SetupApp">3. Set Up the Android Wear Preview App</h2>
 
-<p>The <em>Android Wear Preview</em> app is an app you must have installed on your Android
-device (a phone or tablet) in order to deliver app notifications to the Android Wear emulator.</p>
+<p>To view your app's notifications on the Android Wear emulator, you must have the
+<em>Android Wear Preview</em> app installed on your Android device (a phone or tablet).</p>
 
 <p>To receive the Android Wear Preview app, you must <a
 href="/wear/preview/signup.html">sign up for the Developer Preview</a> using the same
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp
index ce6cc38..589211f 100644
--- a/libs/androidfw/Asset.cpp
+++ b/libs/androidfw/Asset.cpp
@@ -72,7 +72,7 @@
         }
         cur = cur->mNext;
     }
-    
+
     return res;
 }
 
@@ -84,18 +84,18 @@
     mNext = mPrev = NULL;
     if (gTail == NULL) {
         gHead = gTail = this;
-  	} else {
-  	    mPrev = gTail;
-  	    gTail->mNext = this;
-  	    gTail = this;
-  	}
+    } else {
+        mPrev = gTail;
+        gTail->mNext = this;
+        gTail = this;
+    }
     //ALOGI("Creating Asset %p #%d\n", this, gCount);
 }
 
 Asset::~Asset(void)
 {
     AutoMutex _l(gAssetLock);
-	gCount--;
+    gCount--;
     if (gHead == this) {
         gHead = mNext;
     }
@@ -409,7 +409,7 @@
     }
 
     mFileName = fileName != NULL ? strdup(fileName) : NULL;
-    
+
     return NO_ERROR;
 }
 
@@ -538,7 +538,7 @@
         free(mFileName);
         mFileName = NULL;
     }
-    
+
     if (mFp != NULL) {
         // can only be NULL when called from destructor
         // (otherwise we would never return this object)
diff --git a/libs/androidfw/BackupData.cpp b/libs/androidfw/BackupData.cpp
index 1a5c55c..a5b9416 100644
--- a/libs/androidfw/BackupData.cpp
+++ b/libs/androidfw/BackupData.cpp
@@ -291,7 +291,7 @@
                     (int)(m_pos - sizeof(m_header)), (int)m_header.type);
             m_status = EINVAL;
     }
-    
+
     return m_status;
 }
 
diff --git a/libs/androidfw/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp
index 302fbf6..ab837ad 100644
--- a/libs/androidfw/BackupHelpers.cpp
+++ b/libs/androidfw/BackupHelpers.cpp
@@ -568,8 +568,8 @@
 
     // [ 108 :   8 ] uid -- ignored in Android format; uids are remapped at restore time
     // [ 116 :   8 ] gid -- ignored in Android format
-    snprintf(buf + 108, 8, "0%lo", s.st_uid);
-    snprintf(buf + 116, 8, "0%lo", s.st_gid);
+    snprintf(buf + 108, 8, "0%lo", (unsigned long)s.st_uid);
+    snprintf(buf + 116, 8, "0%lo", (unsigned long)s.st_gid);
 
     // [ 124 :  12 ] file size in bytes
     if (s.st_size > 077777777777LL) {
@@ -778,7 +778,7 @@
         ALOGW("Could not open file %s -- %s", filename.string(), strerror(errno));
         return errno;
     }
-    
+
     while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) {
         err = write(fd, buf, amt);
         if (err != amt) {
diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp
index 0f54edb..2b74a33 100644
--- a/libs/androidfw/CursorWindow.cpp
+++ b/libs/androidfw/CursorWindow.cpp
@@ -1,16 +1,16 @@
 /*
  * Copyright (C) 2006-2007 The Android Open Source Project
  *
- * Licensed under the Apache License, Version 2.0 (the "License"); 
- * you may not use this file except in compliance with the License. 
- * You may obtain a copy of the License at 
+ * 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 
+ *      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 
+ * 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.
  */
 
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 98849e3..652cd4a 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -1266,7 +1266,7 @@
         const ResXMLTree_node* next = (const ResXMLTree_node*)
             (((const uint8_t*)mCurNode) + dtohl(mCurNode->header.size));
         //ALOGW("Next node: prev=%p, next=%p\n", mCurNode, next);
-        
+
         if (((const uint8_t*)next) >= mTree.mDataEnd) {
             mCurNode = NULL;
             return (mEventCode=END_DOCUMENT);
@@ -1303,7 +1303,7 @@
                      (int)(((const uint8_t*)next)-((const uint8_t*)mTree.mHeader)));
                 continue;
         }
-        
+
         if ((totalSize-headerSize) < minExtSize) {
             ALOGW("Bad XML block: header type 0x%x in node at 0x%x has size %d, need %d\n",
                  (int)dtohs(next->header.type),
@@ -1311,10 +1311,10 @@
                  (int)(totalSize-headerSize), (int)minExtSize);
             return (mEventCode=BAD_DOCUMENT);
         }
-        
+
         //printf("CurNode=%p, CurExt=%p, headerSize=%d, minExtSize=%d\n",
         //       mCurNode, mCurExt, headerSize, minExtSize);
-        
+
         return eventCode;
     } while (true);
 }
@@ -2717,7 +2717,7 @@
             delete types[i];
         }
     }
-    
+
     ResTable* const                 owner;
     const Header* const             header;
     const ResTable_package* const   package;
@@ -2725,7 +2725,7 @@
 
     ResStringPool                   typeStrings;
     ResStringPool                   keyStrings;
-    
+
     const Type* getType(size_t idx) const {
         return idx < types.size() ? types[idx] : NULL;
     }
@@ -2775,18 +2775,18 @@
             bags = NULL;
         }
     }
-    
+
     ResTable* const                 owner;
     String16 const                  name;
     uint32_t const                  id;
     Vector<Package*>                packages;
-    
+
     // This is for finding typeStrings and other common package stuff.
     Package*                        basePackage;
 
     // For quick access.
     size_t                          typeCount;
-    
+
     // Computed attribute bags, first indexed by the type and second
     // by the entry in that type.
     bag_set***                      bags;
@@ -2935,7 +2935,7 @@
 
     //ALOGI("Applying style 0x%08x (force=%d)  theme %p...\n", resID, force, this);
     //dumpToLog();
-    
+
     return NO_ERROR;
 }
 
@@ -2944,7 +2944,7 @@
     //ALOGI("Setting theme %p from theme %p...\n", this, &other);
     //dumpToLog();
     //other.dumpToLog();
-    
+
     if (&mTable == &other.mTable) {
         for (size_t i=0; i<Res_MAXPACKAGE; i++) {
             if (mPackages[i] != NULL) {
@@ -2974,7 +2974,7 @@
 
     //ALOGI("Final theme:");
     //dumpToLog();
-    
+
     return NO_ERROR;
 }
 
@@ -2984,7 +2984,7 @@
     int cnt = 20;
 
     if (outTypeSpecFlags != NULL) *outTypeSpecFlags = 0;
-    
+
     do {
         const ssize_t p = mTable.getResourcePackageIndex(resID);
         const uint32_t t = Res_GETTYPE(resID);
@@ -3058,12 +3058,12 @@
     for (size_t i=0; i<Res_MAXPACKAGE; i++) {
         package_info* pi = mPackages[i];
         if (pi == NULL) continue;
-        
+
         ALOGI("  Package #0x%02x:\n", (int)(i+1));
         for (size_t j=0; j<pi->numTypes; j++) {
             type_info& ti = pi->types[j];
             if (ti.numEntries == 0) continue;
-            
+
             ALOGI("    Type #0x%02x:\n", (int)(j+1));
             for (size_t k=0; k<ti.numEntries; k++) {
                 theme_entry& te = ti.entries[k];
@@ -3125,11 +3125,11 @@
 status_t ResTable::add(ResTable* src)
 {
     mError = src->mError;
-    
+
     for (size_t i=0; i<src->mHeaders.size(); i++) {
         mHeaders.add(src->mHeaders[i]);
     }
-    
+
     for (size_t i=0; i<src->mPackageGroups.size(); i++) {
         PackageGroup* srcPg = src->mPackageGroups[i];
         PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
@@ -3140,14 +3140,14 @@
         pg->typeCount = srcPg->typeCount;
         mPackageGroups.add(pg);
     }
-    
+
     memcpy(mPackageMap, src->mPackageMap, sizeof(mPackageMap));
-    
+
     return mError;
 }
 
 status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie,
-                       Asset* asset, bool copyData, const Asset* idmap)
+                       Asset* /*asset*/, bool copyData, const Asset* idmap)
 {
     if (!data) return NO_ERROR;
     Header* header = new Header(this);
@@ -3171,7 +3171,7 @@
     LOAD_TABLE_NOISY(
         ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d "
              "idmap=%p\n", data, size, cookie, asset, copyData, idmap));
-    
+
     if (copyData || notDeviceEndian) {
         header->ownedData = malloc(size);
         if (header->ownedData == NULL) {
@@ -3503,7 +3503,7 @@
             // are identical (diff == 0), or overlay packages will not take effect.
             continue;
         }
-        
+
         bestItem = thisConfig;
         bestValue = item;
         bestPackage = package;
@@ -3573,7 +3573,7 @@
 
 const char16_t* ResTable::valueToString(
     const Res_value* value, size_t stringBlock,
-    char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen)
+    char16_t /*tmpBuffer*/ [TMP_BUFFER_SIZE], size_t* outLen)
 {
     if (!value) {
         return NULL;
@@ -3596,7 +3596,7 @@
     return err;
 }
 
-void ResTable::unlockBag(const bag_entry* bag) const
+void ResTable::unlockBag(const bag_entry* /*bag*/) const
 {
     //printf("<<< unlockBag %p\n", this);
     mLock.unlock();
@@ -3697,7 +3697,7 @@
     bag_set* set = NULL;
 
     TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
-    
+
     ResTable_config bestConfig;
     memset(&bestConfig, 0, sizeof(bestConfig));
 
@@ -3763,7 +3763,7 @@
             ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0;
         const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
             ? dtohl(((const ResTable_map_entry*)entry)->count) : 0;
-        
+
         size_t N = count;
 
         TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
@@ -3807,7 +3807,7 @@
         } else {
             set->typeSpecFlags = -1;
         }
-        
+
         // Now merge in the new attributes...
         ssize_t curOff = offset;
         const ResTable_map* map;
@@ -4070,7 +4070,7 @@
             TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n",
                                String8(group->name).string(), ti));
         }
-        
+
         size_t NTC = typeConfigs->configs.size();
         for (size_t tci=0; tci<NTC; tci++) {
             const ResTable_type* const ty = typeConfigs->configs[tci];
@@ -4086,9 +4086,9 @@
                 if (offset == ResTable_type::NO_ENTRY) {
                     continue;
                 }
-                
+
                 offset += typeOffset;
-                
+
                 if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) {
                     ALOGW("ResTable_entry at %d is beyond type chunk data %d",
                          offset, dtohl(ty->header.size));
@@ -4102,7 +4102,7 @@
                          String8(name, nameLen).string());
                     return 0;
                 }
-                
+
                 const ResTable_entry* const entry = (const ResTable_entry*)
                     (((const uint8_t*)ty) + offset);
                 if (dtohs(entry->size) < sizeof(*entry)) {
@@ -4259,7 +4259,7 @@
     if (*realEnd != 0) {
         return false;
     }
-    
+
     const unit_entry* cur = unitNames;
     while (cur->name) {
         if (len == cur->len && strncmp(cur->name, str, len) == 0) {
@@ -4410,7 +4410,7 @@
             if (neg) {
                 mantissa = (-mantissa) & Res_value::COMPLEX_MANTISSA_MASK;
             }
-            outValue->data |= 
+            outValue->data |=
                 (radix<<Res_value::COMPLEX_RADIX_SHIFT)
                 | (mantissa<<Res_value::COMPLEX_MANTISSA_SHIFT);
             //printf("Input value: %f 0x%016Lx, mult: %f, radix: %d, shift: %d, final: 0x%08x\n",
@@ -4523,7 +4523,7 @@
         // Note: we don't check attrType here because the reference can
         // be to any other type; we just need to count on the client making
         // sure the referenced type is correct.
-        
+
         //printf("Looking up ref: %s\n", String8(s, len).string());
 
         // It's a reference!
@@ -4610,7 +4610,7 @@
             }
         }
     }
-    
+
     if (*s == '#') {
         // It's a color!  Convert to an integer of the form 0xaarrggbb.
         uint32_t color = 0;
@@ -4710,7 +4710,7 @@
         //       String8(package).string(), String8(type).string(),
         //       String8(name).string());
         uint32_t specFlags = 0;
-        uint32_t rid = 
+        uint32_t rid =
             identifierForName(name.string(), name.size(),
                               type.string(), type.size(),
                               package.string(), package.size(), &specFlags);
@@ -4875,7 +4875,7 @@
                             return true;
                         }
                     }
-    
+
                 }
                 bag++;
                 cnt--;
@@ -5240,43 +5240,43 @@
             entryIndex, (int)allTypes->entryCount);
         return BAD_TYPE;
     }
-        
+
     const ResTable_type* type = NULL;
     uint32_t offset = ResTable_type::NO_ENTRY;
     ResTable_config bestConfig;
     memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up
-    
+
     const size_t NT = allTypes->configs.size();
     for (size_t i=0; i<NT; i++) {
         const ResTable_type* const thisType = allTypes->configs[i];
         if (thisType == NULL) continue;
-        
+
         ResTable_config thisConfig;
         thisConfig.copyFromDtoH(thisType->config);
 
         TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n",
                            entryIndex, typeIndex+1, dtohl(thisType->config.size),
                            thisConfig.toString().string()));
-        
+
         // Check to make sure this one is valid for the current parameters.
         if (config && !thisConfig.match(*config)) {
             TABLE_GETENTRY(ALOGI("Does not match config!\n"));
             continue;
         }
-        
+
         // Check if there is the desired entry in this type.
-        
+
         const uint8_t* const end = ((const uint8_t*)thisType)
             + dtohl(thisType->header.size);
         const uint32_t* const eindex = (const uint32_t*)
             (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize));
-        
+
         uint32_t thisOffset = dtohl(eindex[entryIndex]);
         if (thisOffset == ResTable_type::NO_ENTRY) {
             TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n"));
             continue;
         }
-        
+
         if (type != NULL) {
             // Check if this one is less specific than the last found.  If so,
             // we will skip it.  We check starting with things we most care
@@ -5286,19 +5286,19 @@
                 continue;
             }
         }
-        
+
         type = thisType;
         offset = thisOffset;
         bestConfig = thisConfig;
         TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
         if (!config) break;
     }
-    
+
     if (type == NULL) {
         TABLE_GETENTRY(ALOGI("No value found for requested entry!\n"));
         return BAD_INDEX;
     }
-    
+
     offset += dtohl(type->entriesStart);
     TABLE_NOISY(aout << "Looking in resource table " << package->header->header
           << ", typeOff="
@@ -5363,7 +5363,7 @@
              dtohl(pkg->keyStrings));
         return (mError=BAD_TYPE);
     }
-    
+
     Package* package = NULL;
     PackageGroup* group = NULL;
     uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id);
@@ -5372,12 +5372,12 @@
     // always loaded alongside their idmaps, but during idmap creation
     // the package is temporarily loaded by itself.
     if (id < 256) {
-    
+
         package = new Package(this, header, pkg);
         if (package == NULL) {
             return (mError=NO_MEMORY);
         }
-        
+
         size_t idx = mPackageMap[id];
         if (idx == 0) {
             idx = mPackageGroups.size()+1;
@@ -5411,7 +5411,7 @@
                 return (mError=err);
             }
             group->basePackage = package;
-            
+
             mPackageMap[id] = (uint8_t)idx;
         } else {
             group = mPackageGroups.itemAt(idx-1);
@@ -5428,10 +5428,10 @@
         return NO_ERROR;
     }
 
-    
+
     // Iterate through all chunks.
     size_t curPackage = 0;
-    
+
     const ResChunk_header* chunk =
         (const ResChunk_header*)(((const uint8_t*)pkg)
                                  + dtohs(pkg->header.headerSize));
@@ -5450,9 +5450,9 @@
             if (err != NO_ERROR) {
                 return (mError=err);
             }
-            
+
             const size_t typeSpecSize = dtohl(typeSpec->header.size);
-            
+
             LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
                                     (void*)(base-(const uint8_t*)chunk),
                                     dtohs(typeSpec->header.type),
@@ -5468,12 +5468,12 @@
                      (void*)typeSpecSize);
                 return (mError=BAD_TYPE);
             }
-            
+
             if (typeSpec->id == 0) {
                 ALOGW("ResTable_type has an id of 0.");
                 return (mError=BAD_TYPE);
             }
-            
+
             while (package->types.size() < typeSpec->id) {
                 package->types.add(NULL);
             }
@@ -5489,7 +5489,7 @@
             t->typeSpecFlags = (const uint32_t*)(
                     ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
             t->typeSpec = typeSpec;
-            
+
         } else if (ctype == RES_TABLE_TYPE_TYPE) {
             const ResTable_type* type = (const ResTable_type*)(chunk);
             err = validate_chunk(&type->header, sizeof(*type)-sizeof(ResTable_config)+4,
@@ -5497,9 +5497,9 @@
             if (err != NO_ERROR) {
                 return (mError=err);
             }
-            
+
             const uint32_t typeSize = dtohl(type->header.size);
-            
+
             LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
                                     (void*)(base-(const uint8_t*)chunk),
                                     dtohs(type->header.type),
@@ -5523,7 +5523,7 @@
                 ALOGW("ResTable_type has an id of 0.");
                 return (mError=BAD_TYPE);
             }
-            
+
             while (package->types.size() < type->id) {
                 package->types.add(NULL);
             }
@@ -5536,7 +5536,7 @@
                     (int)dtohl(type->entryCount), (int)t->entryCount);
                 return (mError=BAD_TYPE);
             }
-            
+
             TABLE_GETENTRY(
                 ResTable_config thisConfig;
                 thisConfig.copyFromDtoH(type->config);
@@ -5557,7 +5557,7 @@
     if (group->typeCount == 0) {
         group->typeCount = package->types.size();
     }
-    
+
     return NO_ERROR;
 }
 
@@ -5757,7 +5757,7 @@
             * RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT)
                             & Res_value::COMPLEX_RADIX_MASK];
     printf("%f", value);
-    
+
     if (!isFraction) {
         switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) {
             case Res_value::COMPLEX_UNIT_PX: printf("px"); break;
@@ -5832,7 +5832,7 @@
             } else {
                 printf("(string) null\n");
             }
-        } 
+        }
     } else if (value.dataType == Res_value::TYPE_FLOAT) {
         printf("(float) %g\n", *(const float*)&value.data);
     } else if (value.dataType == Res_value::TYPE_DIMENSION) {
@@ -5875,7 +5875,7 @@
         printf("Package Group %d id=%d packageCount=%d name=%s\n",
                 (int)pgIndex, pg->id, (int)pg->packages.size(),
                 String8(pg->name).string());
-        
+
         size_t pkgCount = pg->packages.size();
         for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
             const Package* pkg = pg->packages[pkgIndex];
@@ -5942,17 +5942,17 @@
                         continue;
                     }
                     for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
-                        
+
                         const uint8_t* const end = ((const uint8_t*)type)
                             + dtohl(type->header.size);
                         const uint32_t* const eindex = (const uint32_t*)
                             (((const uint8_t*)type) + dtohs(type->header.headerSize));
-                        
+
                         uint32_t thisOffset = dtohl(eindex[entryIndex]);
                         if (thisOffset == ResTable_type::NO_ENTRY) {
                             continue;
                         }
-                        
+
                         uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
                                     | (0x00ff0000 & ((typeIndex+1)<<16))
                                     | (0x0000ffff & (entryIndex));
@@ -5985,7 +5985,7 @@
                                    entriesStart, thisOffset, typeSize);
                             continue;
                         }
-                        
+
                         const ResTable_entry* ent = (const ResTable_entry*)
                             (((const uint8_t*)type) + entriesStart + thisOffset);
                         if (((entriesStart + thisOffset)&0x3) != 0) {
@@ -5993,7 +5993,7 @@
                                  (entriesStart + thisOffset));
                             continue;
                         }
-                        
+
                         uintptr_t esize = dtohs(ent->size);
                         if ((esize&0x3) != 0) {
                             printf("NON-INTEGER ResTable_entry SIZE: 0x%x\n", esize);
@@ -6004,7 +6004,7 @@
                                    entriesStart, thisOffset, esize, typeSize);
                             continue;
                         }
-                            
+
                         const Res_value* valuePtr = NULL;
                         const ResTable_map_entry* bagPtr = NULL;
                         Res_value value;
@@ -6019,12 +6019,12 @@
                                    (int)value.dataType, (int)value.data,
                                    (int)value.size, (int)value.res0);
                         }
-                        
+
                         if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
                             printf(" (PUBLIC)");
                         }
                         printf("\n");
-                        
+
                         if (inclValues) {
                             if (valuePtr != NULL) {
                                 printf("          ");
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index e9ac2fe..6fa0f14 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -127,7 +127,7 @@
             goto z_bail;
         }
 
-		/* output buffer holds all, so no need to write the output */
+        /* output buffer holds all, so no need to write the output */
     } while (zerr == Z_OK);
 
     assert(zerr == Z_STREAM_END);       /* other errors should've been caught */
@@ -197,7 +197,7 @@
     {
     }
 
-    long read(unsigned char** nextBuffer, long readSize) {
+    long read(unsigned char** nextBuffer, long /*readSize*/) {
         if (!mBufferReturned) {
             mBufferReturned = true;
             *nextBuffer = mInput;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index eeff4c0..4de755d 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -38,6 +38,7 @@
 		Program.cpp \
 		ProgramCache.cpp \
 		RenderBufferCache.cpp \
+		RenderNode.cpp \
 		RenderProperties.cpp \
 		ResourceCache.cpp \
 		ShadowTessellator.cpp \
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index d124cde..cf745ee 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -22,10 +22,10 @@
 #include <SkMatrix.h>
 #include <utils/StrongPointer.h>
 
-#include "DisplayList.h"
 #include "Layer.h"
 #include "OpenGLRenderer.h"
 #include "Rect.h"
+#include "RenderNode.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index f4de8ec..9e6a96d 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -29,543 +29,6 @@
 namespace android {
 namespace uirenderer {
 
-void RenderNode::outputLogBuffer(int fd) {
-    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
-    if (logBuffer.isEmpty()) {
-        return;
-    }
-
-    FILE *file = fdopen(fd, "a");
-
-    fprintf(file, "\nRecent DisplayList operations\n");
-    logBuffer.outputCommands(file);
-
-    String8 cachesLog;
-    Caches::getInstance().dumpMemoryUsage(cachesLog);
-    fprintf(file, "\nCaches:\n%s", cachesLog.string());
-    fprintf(file, "\n");
-
-    fflush(file);
-}
-
-RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) {
-}
-
-RenderNode::~RenderNode() {
-    LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
-
-    mDestroyed = true;
-    delete mDisplayListData;
-}
-
-void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
-    if (displayList) {
-        DISPLAY_LIST_LOGD("Deferring display list destruction");
-        Caches::getInstance().deleteDisplayListDeferred(displayList);
-    }
-}
-
-void RenderNode::setData(DisplayListData* data) {
-    delete mDisplayListData;
-    mDisplayListData = data;
-    if (mDisplayListData) {
-        Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
-    }
-}
-
-/**
- * This function is a simplified version of replay(), where we simply retrieve and log the
- * display list. This function should remain in sync with the replay() function.
- */
-void RenderNode::output(uint32_t level) {
-    ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
-            mName.string(), isRenderable());
-    ALOGD("%*s%s %d", level * 2, "", "Save",
-            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-
-    outputViewProperties(level);
-    int flags = DisplayListOp::kOpLogFlag_Recurse;
-    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
-        mDisplayListData->displayListOps[i]->output(level, flags);
-    }
-
-    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
-}
-
-void RenderNode::outputViewProperties(const int level) {
-    properties().updateMatrix();
-    if (properties().mLeft != 0 || properties().mTop != 0) {
-        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop);
-    }
-    if (properties().mStaticMatrix) {
-        ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
-                level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix));
-    }
-    if (properties().mAnimationMatrix) {
-        ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
-                level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix));
-    }
-    if (properties().mMatrixFlags != 0) {
-        if (properties().mMatrixFlags == TRANSLATION) {
-            ALOGD("%*sTranslate %.2f, %.2f, %.2f",
-                    level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ);
-        } else {
-            ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
-                    level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix));
-        }
-    }
-
-    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
-    if (properties().mAlpha < 1) {
-        if (properties().mCaching) {
-            ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha);
-        } else if (!properties().mHasOverlappingRendering) {
-            ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha);
-        } else {
-            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
-            if (clipToBoundsNeeded) {
-                flags |= SkCanvas::kClipToLayer_SaveFlag;
-                clipToBoundsNeeded = false; // clipping done by save layer
-            }
-            ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
-                    (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
-                    (int)(properties().mAlpha * 255), flags);
-        }
-    }
-    if (clipToBoundsNeeded) {
-        ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
-                (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
-    }
-}
-
-/*
- * For property operations, we pass a savecount of 0, since the operations aren't part of the
- * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
- * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount)
- */
-#define PROPERTY_SAVECOUNT 0
-
-template <class T>
-void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
-        const int level) {
-#if DEBUG_DISPLAY_LIST
-    outputViewProperties(level);
-#endif
-    properties().updateMatrix();
-    if (properties().mLeft != 0 || properties().mTop != 0) {
-        renderer.translate(properties().mLeft, properties().mTop);
-    }
-    if (properties().mStaticMatrix) {
-        renderer.concatMatrix(properties().mStaticMatrix);
-    } else if (properties().mAnimationMatrix) {
-        renderer.concatMatrix(properties().mAnimationMatrix);
-    }
-    if (properties().mMatrixFlags != 0) {
-        if (properties().mMatrixFlags == TRANSLATION) {
-            renderer.translate(properties().mTranslationX, properties().mTranslationY);
-        } else {
-            renderer.concatMatrix(*properties().mTransformMatrix);
-        }
-    }
-    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
-    if (properties().mAlpha < 1) {
-        if (properties().mCaching) {
-            renderer.setOverrideLayerAlpha(properties().mAlpha);
-        } else if (!properties().mHasOverlappingRendering) {
-            renderer.scaleAlpha(properties().mAlpha);
-        } else {
-            // TODO: should be able to store the size of a DL at record time and not
-            // have to pass it into this call. In fact, this information might be in the
-            // location/size info that we store with the new native transform data.
-            int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
-            if (clipToBoundsNeeded) {
-                saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
-                clipToBoundsNeeded = false; // clipping done by saveLayer
-            }
-
-            SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
-                    0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
-            handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-        }
-    }
-    if (clipToBoundsNeeded) {
-        ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
-                properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
-        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-    }
-    if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
-        ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
-        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-    }
-}
-
-/**
- * Apply property-based transformations to input matrix
- *
- * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
- * matrix computation instead of the Skia 3x3 matrix + camera hackery.
- */
-void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
-    if (properties().mLeft != 0 || properties().mTop != 0) {
-        matrix.translate(properties().mLeft, properties().mTop);
-    }
-    if (properties().mStaticMatrix) {
-        mat4 stat(*properties().mStaticMatrix);
-        matrix.multiply(stat);
-    } else if (properties().mAnimationMatrix) {
-        mat4 anim(*properties().mAnimationMatrix);
-        matrix.multiply(anim);
-    }
-    if (properties().mMatrixFlags != 0) {
-        properties().updateMatrix();
-        if (properties().mMatrixFlags == TRANSLATION) {
-            matrix.translate(properties().mTranslationX, properties().mTranslationY,
-                    true3dTransform ? properties().mTranslationZ : 0.0f);
-        } else {
-            if (!true3dTransform) {
-                matrix.multiply(*properties().mTransformMatrix);
-            } else {
-                mat4 true3dMat;
-                true3dMat.loadTranslate(
-                        properties().mPivotX + properties().mTranslationX,
-                        properties().mPivotY + properties().mTranslationY,
-                        properties().mTranslationZ);
-                true3dMat.rotate(properties().mRotationX, 1, 0, 0);
-                true3dMat.rotate(properties().mRotationY, 0, 1, 0);
-                true3dMat.rotate(properties().mRotation, 0, 0, 1);
-                true3dMat.scale(properties().mScaleX, properties().mScaleY, 1);
-                true3dMat.translate(-properties().mPivotX, -properties().mPivotY);
-
-                matrix.multiply(true3dMat);
-            }
-        }
-    }
-}
-
-/**
- * Organizes the DisplayList hierarchy to prepare for background projection reordering.
- *
- * This should be called before a call to defer() or drawDisplayList()
- *
- * Each DisplayList that serves as a 3d root builds its list of composited children,
- * which are flagged to not draw in the standard draw loop.
- */
-void RenderNode::computeOrdering() {
-    ATRACE_CALL();
-    mProjectedNodes.clear();
-
-    // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
-    // transform properties are applied correctly to top level children
-    if (mDisplayListData == NULL) return;
-    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
-        DrawDisplayListOp* childOp = mDisplayListData->children[i];
-        childOp->mDisplayList->computeOrderingImpl(childOp,
-                &mProjectedNodes, &mat4::identity());
-    }
-}
-
-void RenderNode::computeOrderingImpl(
-        DrawDisplayListOp* opState,
-        Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
-        const mat4* transformFromProjectionSurface) {
-    mProjectedNodes.clear();
-    if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
-
-    // TODO: should avoid this calculation in most cases
-    // TODO: just calculate single matrix, down to all leaf composited elements
-    Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
-    localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
-
-    if (properties().mProjectBackwards) {
-        // composited projectee, flag for out of order draw, save matrix, and store in proj surface
-        opState->mSkipInOrderDraw = true;
-        opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
-        compositedChildrenOfProjectionSurface->add(opState);
-    } else {
-        // standard in order draw
-        opState->mSkipInOrderDraw = false;
-    }
-
-    if (mDisplayListData->children.size() > 0) {
-        const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
-        bool haveAppliedPropertiesToProjection = false;
-        for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
-            DrawDisplayListOp* childOp = mDisplayListData->children[i];
-            RenderNode* child = childOp->mDisplayList;
-
-            Vector<DrawDisplayListOp*>* projectionChildren = NULL;
-            const mat4* projectionTransform = NULL;
-            if (isProjectionReceiver && !child->properties().mProjectBackwards) {
-                // if receiving projections, collect projecting descendent
-
-                // Note that if a direct descendent is projecting backwards, we pass it's
-                // grandparent projection collection, since it shouldn't project onto it's
-                // parent, where it will already be drawing.
-                projectionChildren = &mProjectedNodes;
-                projectionTransform = &mat4::identity();
-            } else {
-                if (!haveAppliedPropertiesToProjection) {
-                    applyViewPropertyTransforms(localTransformFromProjectionSurface);
-                    haveAppliedPropertiesToProjection = true;
-                }
-                projectionChildren = compositedChildrenOfProjectionSurface;
-                projectionTransform = &localTransformFromProjectionSurface;
-            }
-            child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
-        }
-    }
-
-}
-
-class DeferOperationHandler {
-public:
-    DeferOperationHandler(DeferStateStruct& deferStruct, int level)
-        : mDeferStruct(deferStruct), mLevel(level) {}
-    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
-        operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
-    }
-    inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
-
-private:
-    DeferStateStruct& mDeferStruct;
-    const int mLevel;
-};
-
-void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
-    DeferOperationHandler handler(deferStruct, level);
-    iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
-}
-
-class ReplayOperationHandler {
-public:
-    ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
-        : mReplayStruct(replayStruct), mLevel(level) {}
-    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
-#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
-        properties().mReplayStruct.mRenderer.eventMark(operation->name());
-#endif
-        operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
-    }
-    inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
-
-private:
-    ReplayStateStruct& mReplayStruct;
-    const int mLevel;
-};
-
-void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
-    ReplayOperationHandler handler(replayStruct, level);
-
-    replayStruct.mRenderer.startMark(mName.string());
-    iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
-    replayStruct.mRenderer.endMark();
-
-    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
-            replayStruct.mDrawGlStatus);
-}
-
-void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
-    if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
-
-    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
-        DrawDisplayListOp* childOp = mDisplayListData->children[i];
-        RenderNode* child = childOp->mDisplayList;
-        float childZ = child->properties().mTranslationZ;
-
-        if (childZ != 0.0f) {
-            zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
-            childOp->mSkipInOrderDraw = true;
-        } else if (!child->properties().mProjectBackwards) {
-            // regular, in order drawing DisplayList
-            childOp->mSkipInOrderDraw = false;
-        }
-    }
-
-    // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
-    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
-}
-
-#define SHADOW_DELTA 0.1f
-
-template <class T>
-void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
-        ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
-    const int size = zTranslatedNodes.size();
-    if (size == 0
-            || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
-            || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
-        // no 3d children to draw
-        return;
-    }
-
-    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-    LinearAllocator& alloc = handler.allocator();
-    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
-            SkRegion::kIntersect_Op); // clip to 3d root bounds
-    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
-    /**
-     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
-     * with very similar Z heights to draw together.
-     *
-     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
-     * underneath both, and neither's shadow is drawn on top of the other.
-     */
-    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
-    size_t drawIndex, shadowIndex, endIndex;
-    if (mode == kNegativeZChildren) {
-        drawIndex = 0;
-        endIndex = nonNegativeIndex;
-        shadowIndex = endIndex; // draw no shadows
-    } else {
-        drawIndex = nonNegativeIndex;
-        endIndex = size;
-        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
-    }
-    float lastCasterZ = 0.0f;
-    while (shadowIndex < endIndex || drawIndex < endIndex) {
-        if (shadowIndex < endIndex) {
-            DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
-            RenderNode* caster = casterOp->mDisplayList;
-            const float casterZ = zTranslatedNodes[shadowIndex].key;
-            // attempt to render the shadow if the caster about to be drawn is its caster,
-            // OR if its caster's Z value is similar to the previous potential caster
-            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
-
-                if (caster->properties().mAlpha > 0.0f) {
-                    mat4 shadowMatrixXY(casterOp->mTransformFromParent);
-                    caster->applyViewPropertyTransforms(shadowMatrixXY);
-
-                    // Z matrix needs actual 3d transformation, so mapped z values will be correct
-                    mat4 shadowMatrixZ(casterOp->mTransformFromParent);
-                    caster->applyViewPropertyTransforms(shadowMatrixZ, true);
-
-                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(
-                            shadowMatrixXY, shadowMatrixZ,
-                            caster->properties().mAlpha, &(caster->properties().mOutline),
-                            caster->properties().mWidth, caster->properties().mHeight);
-                    handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-                }
-
-                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
-                shadowIndex++;
-                continue;
-            }
-        }
-
-        // only the actual child DL draw needs to be in save/restore,
-        // since it modifies the renderer's matrix
-        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
-
-        DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
-        RenderNode* child = childOp->mDisplayList;
-
-        renderer.concatMatrix(childOp->mTransformFromParent);
-        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
-        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
-        childOp->mSkipInOrderDraw = true;
-
-        renderer.restoreToCount(restoreTo);
-        drawIndex++;
-    }
-    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
-}
-
-template <class T>
-void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
-    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
-    LinearAllocator& alloc = handler.allocator();
-    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
-            SkRegion::kReplace_Op); // clip to projection surface root bounds
-    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
-    for (size_t i = 0; i < mProjectedNodes.size(); i++) {
-        DrawDisplayListOp* childOp = mProjectedNodes[i];
-
-        // matrix save, concat, and restore can be done safely without allocating operations
-        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
-        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
-        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
-        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
-        childOp->mSkipInOrderDraw = true;
-        renderer.restoreToCount(restoreTo);
-    }
-    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
-}
-
-/**
- * This function serves both defer and replay modes, and will organize the displayList's component
- * operations for a single frame:
- *
- * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
- * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
- * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
- * defer vs replay logic, per operation
- */
-template <class T>
-void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
-    if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
-        ALOGW("Error: %s is drawing after destruction", mName.string());
-        CRASH();
-    }
-    if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) {
-        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
-        return;
-    }
-
-#if DEBUG_DISPLAY_LIST
-    Rect* clipRect = renderer.getClipRect();
-    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
-            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
-            clipRect->right, clipRect->bottom);
-#endif
-
-    LinearAllocator& alloc = handler.allocator();
-    int restoreTo = renderer.getSaveCount();
-    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
-            PROPERTY_SAVECOUNT, properties().mClipToBounds);
-
-    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
-            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
-
-    setViewProperties<T>(renderer, handler, level + 1);
-
-    bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight);
-    if (!quickRejected) {
-        Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
-        buildZSortedChildList(zTranslatedNodes);
-
-        // for 3d root, draw children with negative z values
-        iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
-
-        DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
-        const int saveCountOffset = renderer.getSaveCount() - 1;
-        const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
-        for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
-            DisplayListOp *op = mDisplayListData->displayListOps[i];
-
-#if DEBUG_DISPLAY_LIST
-            op->output(level + 1);
-#endif
-
-            logBuffer.writeCommand(level, op->name());
-            handler(op, saveCountOffset, properties().mClipToBounds);
-
-            if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
-                iterateProjectedChildren(renderer, handler, level);
-            }
-        }
-
-        // for 3d root, draw children with positive z values
-        iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
-    }
-
-    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
-    handler(new (alloc) RestoreToCountOp(restoreTo),
-            PROPERTY_SAVECOUNT, properties().mClipToBounds);
-    renderer.setOverrideLayerAlpha(1.0f);
-}
-
 void DisplayListData::cleanupResources() {
     Caches& caches = Caches::getInstance();
     caches.unregisterFunctors(functorCount);
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index b80c118..df5cba6 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -143,136 +143,6 @@
     void cleanupResources();
 };
 
-/**
- * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
- *
- * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
- * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
- * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
- * a renderer).
- *
- * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
- * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
- * attached.
- */
-class RenderNode {
-public:
-    ANDROID_API RenderNode();
-    ANDROID_API ~RenderNode();
-
-    // See flags defined in DisplayList.java
-    enum ReplayFlag {
-        kReplayFlag_ClipChildren = 0x1
-    };
-
-    ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList);
-    ANDROID_API static void outputLogBuffer(int fd);
-
-    ANDROID_API void setData(DisplayListData* newData);
-
-    void computeOrdering();
-    void defer(DeferStateStruct& deferStruct, const int level);
-    void replay(ReplayStateStruct& replayStruct, const int level);
-
-    ANDROID_API void output(uint32_t level = 1);
-
-    bool isRenderable() const {
-        return mDisplayListData && mDisplayListData->hasDrawOps;
-    }
-
-    void setName(const char* name) {
-        if (name) {
-            char* lastPeriod = strrchr(name, '.');
-            if (lastPeriod) {
-                mName.setTo(lastPeriod + 1);
-            } else {
-                mName.setTo(name);
-            }
-        }
-    }
-
-    RenderProperties& properties() {
-        return mProperties;
-    }
-
-    bool isProjectionReceiver() {
-        return properties().isProjectionReceiver();
-    }
-
-    int getWidth() {
-        return properties().getWidth();
-    }
-
-    int getHeight() {
-        return properties().getHeight();
-    }
-
-private:
-    typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
-
-    static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
-        for (size_t i = 0; i < nodes.size(); i++) {
-            if (nodes[i].key >= 0.0f) return i;
-        }
-        return nodes.size();
-    }
-
-    enum ChildrenSelectMode {
-        kNegativeZChildren,
-        kPositiveZChildren
-    };
-
-    void outputViewProperties(const int level);
-
-    void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
-
-    void computeOrderingImpl(DrawDisplayListOp* opState,
-            Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
-            const mat4* transformFromProjectionSurface);
-
-    template <class T>
-    inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
-
-    void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
-
-    template <class T>
-    inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
-            ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
-
-    template <class T>
-    inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
-
-    template <class T>
-    inline void iterate(OpenGLRenderer& renderer, T& handler, const int level);
-
-    class TextContainer {
-    public:
-        size_t length() const {
-            return mByteLength;
-        }
-
-        const char* text() const {
-            return (const char*) mText;
-        }
-
-        size_t mByteLength;
-        const char* mText;
-    };
-
-    String8 mName;
-    bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
-
-    RenderProperties mProperties;
-    DisplayListData* mDisplayListData;
-
-    /**
-     * Draw time state - these properties are only set and used during rendering
-     */
-
-    // for projection surfaces, contains a list of all children items
-    Vector<DrawDisplayListOp*> mProjectedNodes;
-}; // class DisplayList
-
 }; // namespace uirenderer
 }; // namespace android
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index e69e08e..78c97e1 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -21,12 +21,12 @@
 
 #include <private/hwui/DrawGlInfo.h>
 
-#include "DisplayList.h"
+#include "Caches.h"
 #include "DeferredDisplayList.h"
 #include "DisplayListLogBuffer.h"
 #include "DisplayListOp.h"
 #include "DisplayListRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 65498a5..04c5a73 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -22,9 +22,9 @@
 #include <SkPath.h>
 #include <cutils/compiler.h>
 
-#include "DisplayList.h"
 #include "DisplayListLogBuffer.h"
 #include "OpenGLRenderer.h"
+#include "RenderNode.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 52176d4..bd9bfe9 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,12 +18,12 @@
 
 #include <utils/Log.h>
 
-#include "DisplayList.h"
+#include "Caches.h"
 #include "DeferredDisplayList.h"
 #include "Layer.h"
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
-#include "Caches.h"
+#include "RenderNode.h"
 
 namespace android {
 namespace uirenderer {
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index cb8155b..cdd789d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -3226,11 +3226,13 @@
     const int casterVertexCount = casterVertices2d.size();
     Vector3 casterPolygon[casterVertexCount];
     float minZ = FLT_MAX;
+    float maxZ = -FLT_MAX;
     for (int i = 0; i < casterVertexCount; i++) {
         const Vertex& point2d = casterVertices2d[i];
         casterPolygon[i] = Vector3(point2d.x, point2d.y, 0);
         mapPointFakeZ(casterPolygon[i], casterTransformXY, casterTransformZ);
         minZ = fmin(minZ, casterPolygon[i].z);
+        maxZ = fmax(maxZ, casterPolygon[i].z);
     }
 
     // map the centroid of the caster into 3d
@@ -3248,6 +3250,15 @@
         }
         centroid3d.z += casterLift;
     }
+
+    // Check whether we want to draw the shadow at all by checking the caster's
+    // bounds against clip.
+    // We only have ortho projection, so we can just ignore the Z in caster for
+    // simple rejection calculation.
+    Rect localClip = mSnapshot->getLocalClip();
+    Rect casterBounds(casterOutline->getBounds());
+    casterTransformXY.mapRect(casterBounds);
+
     bool isCasterOpaque = (casterAlpha == 1.0f);
     // draw caster's shadows
     if (mCaches.propertyAmbientShadowStrength > 0) {
@@ -3255,7 +3266,7 @@
         VertexBuffer ambientShadowVertexBuffer;
         VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateAmbientShadow(
                 isCasterOpaque, casterPolygon, casterVertexCount, centroid3d,
-                ambientShadowVertexBuffer);
+                casterBounds, localClip, maxZ, ambientShadowVertexBuffer);
         drawVertexBuffer(vertexBufferMode, ambientShadowVertexBuffer, &paint);
     }
 
@@ -3266,7 +3277,8 @@
                 mCaches.propertyLightPosYScale, mCaches.propertyLightPosZScale);
         VertexBufferMode vertexBufferMode = ShadowTessellator::tessellateSpotShadow(
                 isCasterOpaque, casterPolygon, casterVertexCount, lightPosScale,
-                *currentTransform(), getWidth(), getHeight(), spotShadowVertexBuffer);
+                *currentTransform(), getWidth(), getHeight(), casterBounds, localClip,
+                spotShadowVertexBuffer);
         drawVertexBuffer(vertexBufferMode, spotShadowVertexBuffer, &paint);
     }
 
diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h
index c230149..0083b77 100644
--- a/libs/hwui/Rect.h
+++ b/libs/hwui/Rect.h
@@ -18,6 +18,7 @@
 #define ANDROID_HWUI_RECT_H
 
 #include <cmath>
+#include <SkRect.h>
 
 #include <utils/Log.h>
 
@@ -68,6 +69,13 @@
             bottom(height) {
     }
 
+    inline Rect(const SkRect& rect):
+            left(rect.fLeft),
+            top(rect.fTop),
+            right(rect.fRight),
+            bottom(rect.fBottom) {
+    }
+
     friend int operator==(const Rect& a, const Rect& b) {
         return !memcmp(&a, &b, sizeof(a));
     }
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
new file mode 100644
index 0000000..e371590
--- /dev/null
+++ b/libs/hwui/RenderNode.cpp
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define ATRACE_TAG ATRACE_TAG_VIEW
+
+#include "RenderNode.h"
+
+#include <SkCanvas.h>
+#include <algorithm>
+
+#include <utils/Trace.h>
+
+#include "Debug.h"
+#include "DisplayListOp.h"
+#include "DisplayListLogBuffer.h"
+
+namespace android {
+namespace uirenderer {
+
+void RenderNode::outputLogBuffer(int fd) {
+    DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+    if (logBuffer.isEmpty()) {
+        return;
+    }
+
+    FILE *file = fdopen(fd, "a");
+
+    fprintf(file, "\nRecent DisplayList operations\n");
+    logBuffer.outputCommands(file);
+
+    String8 cachesLog;
+    Caches::getInstance().dumpMemoryUsage(cachesLog);
+    fprintf(file, "\nCaches:\n%s", cachesLog.string());
+    fprintf(file, "\n");
+
+    fflush(file);
+}
+
+RenderNode::RenderNode() : mDestroyed(false), mDisplayListData(0) {
+}
+
+RenderNode::~RenderNode() {
+    LOG_ALWAYS_FATAL_IF(mDestroyed, "Double destroyed DisplayList %p", this);
+
+    mDestroyed = true;
+    delete mDisplayListData;
+}
+
+void RenderNode::destroyDisplayListDeferred(RenderNode* displayList) {
+    if (displayList) {
+        DISPLAY_LIST_LOGD("Deferring display list destruction");
+        Caches::getInstance().deleteDisplayListDeferred(displayList);
+    }
+}
+
+void RenderNode::setData(DisplayListData* data) {
+    delete mDisplayListData;
+    mDisplayListData = data;
+    if (mDisplayListData) {
+        Caches::getInstance().registerFunctors(mDisplayListData->functorCount);
+    }
+}
+
+/**
+ * This function is a simplified version of replay(), where we simply retrieve and log the
+ * display list. This function should remain in sync with the replay() function.
+ */
+void RenderNode::output(uint32_t level) {
+    ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this,
+            mName.string(), isRenderable());
+    ALOGD("%*s%s %d", level * 2, "", "Save",
+            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+
+    outputViewProperties(level);
+    int flags = DisplayListOp::kOpLogFlag_Recurse;
+    for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+        mDisplayListData->displayListOps[i]->output(level, flags);
+    }
+
+    ALOGD("%*sDone (%p, %s)", (level - 1) * 2, "", this, mName.string());
+}
+
+void RenderNode::outputViewProperties(const int level) {
+    properties().updateMatrix();
+    if (properties().mLeft != 0 || properties().mTop != 0) {
+        ALOGD("%*sTranslate (left, top) %d, %d", level * 2, "", properties().mLeft, properties().mTop);
+    }
+    if (properties().mStaticMatrix) {
+        ALOGD("%*sConcatMatrix (static) %p: " SK_MATRIX_STRING,
+                level * 2, "", properties().mStaticMatrix, SK_MATRIX_ARGS(properties().mStaticMatrix));
+    }
+    if (properties().mAnimationMatrix) {
+        ALOGD("%*sConcatMatrix (animation) %p: " SK_MATRIX_STRING,
+                level * 2, "", properties().mAnimationMatrix, SK_MATRIX_ARGS(properties().mAnimationMatrix));
+    }
+    if (properties().mMatrixFlags != 0) {
+        if (properties().mMatrixFlags == TRANSLATION) {
+            ALOGD("%*sTranslate %.2f, %.2f, %.2f",
+                    level * 2, "", properties().mTranslationX, properties().mTranslationY, properties().mTranslationZ);
+        } else {
+            ALOGD("%*sConcatMatrix %p: " MATRIX_4_STRING,
+                    level * 2, "", properties().mTransformMatrix, MATRIX_4_ARGS(properties().mTransformMatrix));
+        }
+    }
+
+    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
+    if (properties().mAlpha < 1) {
+        if (properties().mCaching) {
+            ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", properties().mAlpha);
+        } else if (!properties().mHasOverlappingRendering) {
+            ALOGD("%*sScaleAlpha %.2f", level * 2, "", properties().mAlpha);
+        } else {
+            int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
+            if (clipToBoundsNeeded) {
+                flags |= SkCanvas::kClipToLayer_SaveFlag;
+                clipToBoundsNeeded = false; // clipping done by save layer
+            }
+            ALOGD("%*sSaveLayerAlpha %.2f, %.2f, %.2f, %.2f, %d, 0x%x", level * 2, "",
+                    (float) 0, (float) 0, (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop,
+                    (int)(properties().mAlpha * 255), flags);
+        }
+    }
+    if (clipToBoundsNeeded) {
+        ALOGD("%*sClipRect %.2f, %.2f, %.2f, %.2f", level * 2, "", 0.0f, 0.0f,
+                (float) properties().mRight - properties().mLeft, (float) properties().mBottom - properties().mTop);
+    }
+}
+
+/*
+ * For property operations, we pass a savecount of 0, since the operations aren't part of the
+ * displaylist, and thus don't have to compensate for the record-time/playback-time discrepancy in
+ * base saveCount (i.e., how RestoreToCount uses saveCount + properties().mCount)
+ */
+#define PROPERTY_SAVECOUNT 0
+
+template <class T>
+void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler,
+        const int level) {
+#if DEBUG_DISPLAY_LIST
+    outputViewProperties(level);
+#endif
+    properties().updateMatrix();
+    if (properties().mLeft != 0 || properties().mTop != 0) {
+        renderer.translate(properties().mLeft, properties().mTop);
+    }
+    if (properties().mStaticMatrix) {
+        renderer.concatMatrix(properties().mStaticMatrix);
+    } else if (properties().mAnimationMatrix) {
+        renderer.concatMatrix(properties().mAnimationMatrix);
+    }
+    if (properties().mMatrixFlags != 0) {
+        if (properties().mMatrixFlags == TRANSLATION) {
+            renderer.translate(properties().mTranslationX, properties().mTranslationY);
+        } else {
+            renderer.concatMatrix(*properties().mTransformMatrix);
+        }
+    }
+    bool clipToBoundsNeeded = properties().mCaching ? false : properties().mClipToBounds;
+    if (properties().mAlpha < 1) {
+        if (properties().mCaching) {
+            renderer.setOverrideLayerAlpha(properties().mAlpha);
+        } else if (!properties().mHasOverlappingRendering) {
+            renderer.scaleAlpha(properties().mAlpha);
+        } else {
+            // TODO: should be able to store the size of a DL at record time and not
+            // have to pass it into this call. In fact, this information might be in the
+            // location/size info that we store with the new native transform data.
+            int saveFlags = SkCanvas::kHasAlphaLayer_SaveFlag;
+            if (clipToBoundsNeeded) {
+                saveFlags |= SkCanvas::kClipToLayer_SaveFlag;
+                clipToBoundsNeeded = false; // clipping done by saveLayer
+            }
+
+            SaveLayerOp* op = new (handler.allocator()) SaveLayerOp(
+                    0, 0, properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, properties().mAlpha * 255, saveFlags);
+            handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+        }
+    }
+    if (clipToBoundsNeeded) {
+        ClipRectOp* op = new (handler.allocator()) ClipRectOp(0, 0,
+                properties().mRight - properties().mLeft, properties().mBottom - properties().mTop, SkRegion::kIntersect_Op);
+        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+    }
+    if (CC_UNLIKELY(properties().mClipToOutline && !properties().mOutline.isEmpty())) {
+        ClipPathOp* op = new (handler.allocator()) ClipPathOp(&properties().mOutline, SkRegion::kIntersect_Op);
+        handler(op, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+    }
+}
+
+/**
+ * Apply property-based transformations to input matrix
+ *
+ * If true3dTransform is set to true, the transform applied to the input matrix will use true 4x4
+ * matrix computation instead of the Skia 3x3 matrix + camera hackery.
+ */
+void RenderNode::applyViewPropertyTransforms(mat4& matrix, bool true3dTransform) {
+    if (properties().mLeft != 0 || properties().mTop != 0) {
+        matrix.translate(properties().mLeft, properties().mTop);
+    }
+    if (properties().mStaticMatrix) {
+        mat4 stat(*properties().mStaticMatrix);
+        matrix.multiply(stat);
+    } else if (properties().mAnimationMatrix) {
+        mat4 anim(*properties().mAnimationMatrix);
+        matrix.multiply(anim);
+    }
+    if (properties().mMatrixFlags != 0) {
+        properties().updateMatrix();
+        if (properties().mMatrixFlags == TRANSLATION) {
+            matrix.translate(properties().mTranslationX, properties().mTranslationY,
+                    true3dTransform ? properties().mTranslationZ : 0.0f);
+        } else {
+            if (!true3dTransform) {
+                matrix.multiply(*properties().mTransformMatrix);
+            } else {
+                mat4 true3dMat;
+                true3dMat.loadTranslate(
+                        properties().mPivotX + properties().mTranslationX,
+                        properties().mPivotY + properties().mTranslationY,
+                        properties().mTranslationZ);
+                true3dMat.rotate(properties().mRotationX, 1, 0, 0);
+                true3dMat.rotate(properties().mRotationY, 0, 1, 0);
+                true3dMat.rotate(properties().mRotation, 0, 0, 1);
+                true3dMat.scale(properties().mScaleX, properties().mScaleY, 1);
+                true3dMat.translate(-properties().mPivotX, -properties().mPivotY);
+
+                matrix.multiply(true3dMat);
+            }
+        }
+    }
+}
+
+/**
+ * Organizes the DisplayList hierarchy to prepare for background projection reordering.
+ *
+ * This should be called before a call to defer() or drawDisplayList()
+ *
+ * Each DisplayList that serves as a 3d root builds its list of composited children,
+ * which are flagged to not draw in the standard draw loop.
+ */
+void RenderNode::computeOrdering() {
+    ATRACE_CALL();
+    mProjectedNodes.clear();
+
+    // TODO: create temporary DDLOp and call computeOrderingImpl on top DisplayList so that
+    // transform properties are applied correctly to top level children
+    if (mDisplayListData == NULL) return;
+    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+        DrawDisplayListOp* childOp = mDisplayListData->children[i];
+        childOp->mDisplayList->computeOrderingImpl(childOp,
+                &mProjectedNodes, &mat4::identity());
+    }
+}
+
+void RenderNode::computeOrderingImpl(
+        DrawDisplayListOp* opState,
+        Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+        const mat4* transformFromProjectionSurface) {
+    mProjectedNodes.clear();
+    if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
+
+    // TODO: should avoid this calculation in most cases
+    // TODO: just calculate single matrix, down to all leaf composited elements
+    Matrix4 localTransformFromProjectionSurface(*transformFromProjectionSurface);
+    localTransformFromProjectionSurface.multiply(opState->mTransformFromParent);
+
+    if (properties().mProjectBackwards) {
+        // composited projectee, flag for out of order draw, save matrix, and store in proj surface
+        opState->mSkipInOrderDraw = true;
+        opState->mTransformFromCompositingAncestor.load(localTransformFromProjectionSurface);
+        compositedChildrenOfProjectionSurface->add(opState);
+    } else {
+        // standard in order draw
+        opState->mSkipInOrderDraw = false;
+    }
+
+    if (mDisplayListData->children.size() > 0) {
+        const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
+        bool haveAppliedPropertiesToProjection = false;
+        for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+            DrawDisplayListOp* childOp = mDisplayListData->children[i];
+            RenderNode* child = childOp->mDisplayList;
+
+            Vector<DrawDisplayListOp*>* projectionChildren = NULL;
+            const mat4* projectionTransform = NULL;
+            if (isProjectionReceiver && !child->properties().mProjectBackwards) {
+                // if receiving projections, collect projecting descendent
+
+                // Note that if a direct descendent is projecting backwards, we pass it's
+                // grandparent projection collection, since it shouldn't project onto it's
+                // parent, where it will already be drawing.
+                projectionChildren = &mProjectedNodes;
+                projectionTransform = &mat4::identity();
+            } else {
+                if (!haveAppliedPropertiesToProjection) {
+                    applyViewPropertyTransforms(localTransformFromProjectionSurface);
+                    haveAppliedPropertiesToProjection = true;
+                }
+                projectionChildren = compositedChildrenOfProjectionSurface;
+                projectionTransform = &localTransformFromProjectionSurface;
+            }
+            child->computeOrderingImpl(childOp, projectionChildren, projectionTransform);
+        }
+    }
+
+}
+
+class DeferOperationHandler {
+public:
+    DeferOperationHandler(DeferStateStruct& deferStruct, int level)
+        : mDeferStruct(deferStruct), mLevel(level) {}
+    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+        operation->defer(mDeferStruct, saveCount, mLevel, clipToBounds);
+    }
+    inline LinearAllocator& allocator() { return *(mDeferStruct.mAllocator); }
+
+private:
+    DeferStateStruct& mDeferStruct;
+    const int mLevel;
+};
+
+void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
+    DeferOperationHandler handler(deferStruct, level);
+    iterate<DeferOperationHandler>(deferStruct.mRenderer, handler, level);
+}
+
+class ReplayOperationHandler {
+public:
+    ReplayOperationHandler(ReplayStateStruct& replayStruct, int level)
+        : mReplayStruct(replayStruct), mLevel(level) {}
+    inline void operator()(DisplayListOp* operation, int saveCount, bool clipToBounds) {
+#if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
+        properties().mReplayStruct.mRenderer.eventMark(operation->name());
+#endif
+        operation->replay(mReplayStruct, saveCount, mLevel, clipToBounds);
+    }
+    inline LinearAllocator& allocator() { return *(mReplayStruct.mAllocator); }
+
+private:
+    ReplayStateStruct& mReplayStruct;
+    const int mLevel;
+};
+
+void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
+    ReplayOperationHandler handler(replayStruct, level);
+
+    replayStruct.mRenderer.startMark(mName.string());
+    iterate<ReplayOperationHandler>(replayStruct.mRenderer, handler, level);
+    replayStruct.mRenderer.endMark();
+
+    DISPLAY_LIST_LOGD("%*sDone (%p, %s), returning %d", level * 2, "", this, mName.string(),
+            replayStruct.mDrawGlStatus);
+}
+
+void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
+    if (mDisplayListData == NULL || mDisplayListData->children.size() == 0) return;
+
+    for (unsigned int i = 0; i < mDisplayListData->children.size(); i++) {
+        DrawDisplayListOp* childOp = mDisplayListData->children[i];
+        RenderNode* child = childOp->mDisplayList;
+        float childZ = child->properties().mTranslationZ;
+
+        if (childZ != 0.0f) {
+            zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
+            childOp->mSkipInOrderDraw = true;
+        } else if (!child->properties().mProjectBackwards) {
+            // regular, in order drawing DisplayList
+            childOp->mSkipInOrderDraw = false;
+        }
+    }
+
+    // Z sort 3d children (stable-ness makes z compare fall back to standard drawing order)
+    std::stable_sort(zTranslatedNodes.begin(), zTranslatedNodes.end());
+}
+
+#define SHADOW_DELTA 0.1f
+
+template <class T>
+void RenderNode::iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+        ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
+    const int size = zTranslatedNodes.size();
+    if (size == 0
+            || (mode == kNegativeZChildren && zTranslatedNodes[0].key > 0.0f)
+            || (mode == kPositiveZChildren && zTranslatedNodes[size - 1].key < 0.0f)) {
+        // no 3d children to draw
+        return;
+    }
+
+    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    LinearAllocator& alloc = handler.allocator();
+    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
+            SkRegion::kIntersect_Op); // clip to 3d root bounds
+    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+    /**
+     * Draw shadows and (potential) casters mostly in order, but allow the shadows of casters
+     * with very similar Z heights to draw together.
+     *
+     * This way, if Views A & B have the same Z height and are both casting shadows, the shadows are
+     * underneath both, and neither's shadow is drawn on top of the other.
+     */
+    const size_t nonNegativeIndex = findNonNegativeIndex(zTranslatedNodes);
+    size_t drawIndex, shadowIndex, endIndex;
+    if (mode == kNegativeZChildren) {
+        drawIndex = 0;
+        endIndex = nonNegativeIndex;
+        shadowIndex = endIndex; // draw no shadows
+    } else {
+        drawIndex = nonNegativeIndex;
+        endIndex = size;
+        shadowIndex = drawIndex; // potentially draw shadow for each pos Z child
+    }
+    float lastCasterZ = 0.0f;
+    while (shadowIndex < endIndex || drawIndex < endIndex) {
+        if (shadowIndex < endIndex) {
+            DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
+            RenderNode* caster = casterOp->mDisplayList;
+            const float casterZ = zTranslatedNodes[shadowIndex].key;
+            // attempt to render the shadow if the caster about to be drawn is its caster,
+            // OR if its caster's Z value is similar to the previous potential caster
+            if (shadowIndex == drawIndex || casterZ - lastCasterZ < SHADOW_DELTA) {
+
+                if (caster->properties().mAlpha > 0.0f) {
+                    mat4 shadowMatrixXY(casterOp->mTransformFromParent);
+                    caster->applyViewPropertyTransforms(shadowMatrixXY);
+
+                    // Z matrix needs actual 3d transformation, so mapped z values will be correct
+                    mat4 shadowMatrixZ(casterOp->mTransformFromParent);
+                    caster->applyViewPropertyTransforms(shadowMatrixZ, true);
+
+                    DisplayListOp* shadowOp  = new (alloc) DrawShadowOp(
+                            shadowMatrixXY, shadowMatrixZ,
+                            caster->properties().mAlpha, &(caster->properties().mOutline),
+                            caster->properties().mWidth, caster->properties().mHeight);
+                    handler(shadowOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+                }
+
+                lastCasterZ = casterZ; // must do this even if current caster not casting a shadow
+                shadowIndex++;
+                continue;
+            }
+        }
+
+        // only the actual child DL draw needs to be in save/restore,
+        // since it modifies the renderer's matrix
+        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+
+        DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
+        RenderNode* child = childOp->mDisplayList;
+
+        renderer.concatMatrix(childOp->mTransformFromParent);
+        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
+        childOp->mSkipInOrderDraw = true;
+
+        renderer.restoreToCount(restoreTo);
+        drawIndex++;
+    }
+    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
+}
+
+template <class T>
+void RenderNode::iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level) {
+    int rootRestoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+    LinearAllocator& alloc = handler.allocator();
+    ClipRectOp* clipOp = new (alloc) ClipRectOp(0, 0, properties().mWidth, properties().mHeight,
+            SkRegion::kReplace_Op); // clip to projection surface root bounds
+    handler(clipOp, PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+    for (size_t i = 0; i < mProjectedNodes.size(); i++) {
+        DrawDisplayListOp* childOp = mProjectedNodes[i];
+
+        // matrix save, concat, and restore can be done safely without allocating operations
+        int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
+        renderer.concatMatrix(childOp->mTransformFromCompositingAncestor);
+        childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
+        handler(childOp, renderer.getSaveCount() - 1, properties().mClipToBounds);
+        childOp->mSkipInOrderDraw = true;
+        renderer.restoreToCount(restoreTo);
+    }
+    handler(new (alloc) RestoreToCountOp(rootRestoreTo), PROPERTY_SAVECOUNT, properties().mClipToBounds);
+}
+
+/**
+ * This function serves both defer and replay modes, and will organize the displayList's component
+ * operations for a single frame:
+ *
+ * Every 'simple' state operation that affects just the matrix and alpha (or other factors of
+ * DeferredDisplayState) may be issued directly to the renderer, but complex operations (with custom
+ * defer logic) and operations in displayListOps are issued through the 'handler' which handles the
+ * defer vs replay logic, per operation
+ */
+template <class T>
+void RenderNode::iterate(OpenGLRenderer& renderer, T& handler, const int level) {
+    if (CC_UNLIKELY(mDestroyed)) { // temporary debug logging
+        ALOGW("Error: %s is drawing after destruction", mName.string());
+        CRASH();
+    }
+    if (mDisplayListData->isEmpty() || properties().mAlpha <= 0) {
+        DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, mName.string());
+        return;
+    }
+
+#if DEBUG_DISPLAY_LIST
+    Rect* clipRect = renderer.getClipRect();
+    DISPLAY_LIST_LOGD("%*sStart display list (%p, %s), clipRect: %.0f, %.0f, %.0f, %.0f",
+            level * 2, "", this, mName.string(), clipRect->left, clipRect->top,
+            clipRect->right, clipRect->bottom);
+#endif
+
+    LinearAllocator& alloc = handler.allocator();
+    int restoreTo = renderer.getSaveCount();
+    handler(new (alloc) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+            PROPERTY_SAVECOUNT, properties().mClipToBounds);
+
+    DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "",
+            SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo);
+
+    setViewProperties<T>(renderer, handler, level + 1);
+
+    bool quickRejected = properties().mClipToBounds && renderer.quickRejectConservative(0, 0, properties().mWidth, properties().mHeight);
+    if (!quickRejected) {
+        Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+        buildZSortedChildList(zTranslatedNodes);
+
+        // for 3d root, draw children with negative z values
+        iterate3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+
+        DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
+        const int saveCountOffset = renderer.getSaveCount() - 1;
+        const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex;
+        for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
+            DisplayListOp *op = mDisplayListData->displayListOps[i];
+
+#if DEBUG_DISPLAY_LIST
+            op->output(level + 1);
+#endif
+
+            logBuffer.writeCommand(level, op->name());
+            handler(op, saveCountOffset, properties().mClipToBounds);
+
+            if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) {
+                iterateProjectedChildren(renderer, handler, level);
+            }
+        }
+
+        // for 3d root, draw children with positive z values
+        iterate3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+    }
+
+    DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
+    handler(new (alloc) RestoreToCountOp(restoreTo),
+            PROPERTY_SAVECOUNT, properties().mClipToBounds);
+    renderer.setOverrideLayerAlpha(1.0f);
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
new file mode 100644
index 0000000..177f33e
--- /dev/null
+++ b/libs/hwui/RenderNode.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef RENDERNODE_H
+#define RENDERNODE_H
+
+#ifndef LOG_TAG
+    #define LOG_TAG "OpenGLRenderer"
+#endif
+
+#include <SkCamera.h>
+#include <SkMatrix.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/LinearAllocator.h>
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/String8.h>
+#include <utils/Vector.h>
+
+#include <cutils/compiler.h>
+
+#include <androidfw/ResourceTypes.h>
+
+#include "Debug.h"
+#include "Matrix.h"
+#include "DeferredDisplayList.h"
+#include "DisplayList.h"
+#include "RenderProperties.h"
+
+class SkBitmap;
+class SkPaint;
+class SkPath;
+class SkRegion;
+
+namespace android {
+namespace uirenderer {
+
+class DeferredDisplayList;
+class DisplayListOp;
+class DisplayListRenderer;
+class OpenGLRenderer;
+class Rect;
+class Layer;
+class SkiaShader;
+
+class ClipRectOp;
+class SaveLayerOp;
+class SaveOp;
+class RestoreToCountOp;
+class DrawDisplayListOp;
+
+/**
+ * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
+ *
+ * Recording of canvas commands is somewhat similar to SkPicture, except the canvas-recording
+ * functionality is split between DisplayListRenderer (which manages the recording), DisplayListData
+ * (which holds the actual data), and DisplayList (which holds properties and performs playback onto
+ * a renderer).
+ *
+ * Note that DisplayListData is swapped out from beneath an individual DisplayList when a view's
+ * recorded stream of canvas operations is refreshed. The DisplayList (and its properties) stay
+ * attached.
+ */
+class RenderNode {
+public:
+    ANDROID_API RenderNode();
+    ANDROID_API ~RenderNode();
+
+    // See flags defined in DisplayList.java
+    enum ReplayFlag {
+        kReplayFlag_ClipChildren = 0x1
+    };
+
+    ANDROID_API static void destroyDisplayListDeferred(RenderNode* displayList);
+    ANDROID_API static void outputLogBuffer(int fd);
+
+    ANDROID_API void setData(DisplayListData* newData);
+
+    void computeOrdering();
+    void defer(DeferStateStruct& deferStruct, const int level);
+    void replay(ReplayStateStruct& replayStruct, const int level);
+
+    ANDROID_API void output(uint32_t level = 1);
+
+    bool isRenderable() const {
+        return mDisplayListData && mDisplayListData->hasDrawOps;
+    }
+
+    void setName(const char* name) {
+        if (name) {
+            char* lastPeriod = strrchr(name, '.');
+            if (lastPeriod) {
+                mName.setTo(lastPeriod + 1);
+            } else {
+                mName.setTo(name);
+            }
+        }
+    }
+
+    RenderProperties& properties() {
+        return mProperties;
+    }
+
+    bool isProjectionReceiver() {
+        return properties().isProjectionReceiver();
+    }
+
+    int getWidth() {
+        return properties().getWidth();
+    }
+
+    int getHeight() {
+        return properties().getHeight();
+    }
+
+private:
+    typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
+
+    static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
+        for (size_t i = 0; i < nodes.size(); i++) {
+            if (nodes[i].key >= 0.0f) return i;
+        }
+        return nodes.size();
+    }
+
+    enum ChildrenSelectMode {
+        kNegativeZChildren,
+        kPositiveZChildren
+    };
+
+    void outputViewProperties(const int level);
+
+    void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
+
+    void computeOrderingImpl(DrawDisplayListOp* opState,
+            Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+            const mat4* transformFromProjectionSurface);
+
+    template <class T>
+    inline void setViewProperties(OpenGLRenderer& renderer, T& handler, const int level);
+
+    void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
+
+    template <class T>
+    inline void iterate3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+            ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
+
+    template <class T>
+    inline void iterateProjectedChildren(OpenGLRenderer& renderer, T& handler, const int level);
+
+    template <class T>
+    inline void iterate(OpenGLRenderer& renderer, T& handler, const int level);
+
+    class TextContainer {
+    public:
+        size_t length() const {
+            return mByteLength;
+        }
+
+        const char* text() const {
+            return (const char*) mText;
+        }
+
+        size_t mByteLength;
+        const char* mText;
+    };
+
+    String8 mName;
+    bool mDestroyed; // used for debugging crash, TODO: remove once invalid state crash fixed
+
+    RenderProperties mProperties;
+    DisplayListData* mDisplayListData;
+
+    /**
+     * Draw time state - these properties are only set and used during rendering
+     */
+
+    // for projection surfaces, contains a list of all children items
+    Vector<DrawDisplayListOp*> mProjectedNodes;
+}; // class RenderNode
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERNODE_H */
diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp
index 771904a..4d0edfb 100644
--- a/libs/hwui/ShadowTessellator.cpp
+++ b/libs/hwui/ShadowTessellator.cpp
@@ -35,7 +35,8 @@
 
 VertexBufferMode ShadowTessellator::tessellateAmbientShadow(bool isCasterOpaque,
         const Vector3* casterPolygon, int casterVertexCount,
-        const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer) {
+        const Vector3& centroid3d, const Rect& casterBounds,
+        const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer) {
     ATRACE_CALL();
 
     // A bunch of parameters to tweak the shadow.
@@ -43,6 +44,16 @@
     const float heightFactor = 1.0f / 128;
     const float geomFactor = 64;
 
+    Rect ambientShadowBounds(casterBounds);
+    ambientShadowBounds.outset(maxZ * geomFactor * heightFactor);
+
+    if (!localClip.intersects(ambientShadowBounds)) {
+#if DEBUG_SHADOW
+        ALOGD("Ambient shadow is out of clip rect!");
+#endif
+        return kVertexBufferMode_OnePolyRingShadow;
+    }
+
     return AmbientShadow::createAmbientShadow(isCasterOpaque, casterPolygon,
             casterVertexCount, centroid3d, heightFactor, geomFactor,
             shadowVertexBuffer);
@@ -52,7 +63,8 @@
 VertexBufferMode ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque,
         const Vector3* casterPolygon, int casterVertexCount,
         const Vector3& lightPosScale, const mat4& receiverTransform,
-        int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer) {
+        int screenWidth, int screenHeight, const Rect& casterBounds,
+        const Rect& localClip, VertexBuffer& shadowVertexBuffer) {
     ATRACE_CALL();
 
     // A bunch of parameters to tweak the shadow.
@@ -73,6 +85,18 @@
     const float lightSize = maximal / 4;
     const int lightVertexCount = 8;
 
+    // Now light and caster are both in local space, we will check whether
+    // the shadow is within the clip area.
+    Rect lightRect = Rect(lightCenter.x - lightSize, lightCenter.y - lightSize,
+            lightCenter.x + lightSize, lightCenter.y + lightSize);
+    lightRect.unionWith(localClip);
+    if (!lightRect.intersects(casterBounds)) {
+#if DEBUG_SHADOW
+        ALOGD("Spot shadow is out of clip rect!");
+#endif
+        return kVertexBufferMode_OnePolyRingShadow;
+    }
+
     VertexBufferMode mode = SpotShadow::createSpotShadow(isCasterOpaque,
             casterPolygon, casterVertexCount, lightCenter, lightSize,
             lightVertexCount, shadowVertexBuffer);
diff --git a/libs/hwui/ShadowTessellator.h b/libs/hwui/ShadowTessellator.h
index ab039fa..ff3de74 100644
--- a/libs/hwui/ShadowTessellator.h
+++ b/libs/hwui/ShadowTessellator.h
@@ -66,12 +66,14 @@
 public:
     static VertexBufferMode tessellateAmbientShadow(bool isCasterOpaque,
             const Vector3* casterPolygon, int casterVertexCount,
-            const Vector3& centroid3d, VertexBuffer& shadowVertexBuffer);
+            const Vector3& centroid3d,  const Rect& casterBounds,
+            const Rect& localClip, float maxZ, VertexBuffer& shadowVertexBuffer);
 
     static VertexBufferMode tessellateSpotShadow(bool isCasterOpaque,
             const Vector3* casterPolygon, int casterVertexCount,
             const Vector3& lightPosScale, const mat4& receiverTransform,
-            int screenWidth, int screenHeight, VertexBuffer& shadowVertexBuffer);
+            int screenWidth, int screenHeight, const Rect& casterBounds,
+            const Rect& localClip, VertexBuffer& shadowVertexBuffer);
 
     static void generateShadowIndices(uint16_t*  shadowIndices);
 
diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp
index f81cd12..5fa0ba5 100644
--- a/libs/hwui/SpotShadow.cpp
+++ b/libs/hwui/SpotShadow.cpp
@@ -573,10 +573,8 @@
     for (int j = 0; j < lightPolyLength; j++) {
         int m = 0;
         for (int i = 0; i < polyLength; i++) {
+            // After validating the input, deltaZ is guaranteed to be positive.
             float deltaZ = lightPoly[j].z - poly[i].z;
-            if (deltaZ == 0) {
-                return;
-            }
             float ratioZ = lightPoly[j].z / deltaZ;
             float x = lightPoly[j].x - ratioZ * (lightPoly[j].x - poly[i].x);
             float y = lightPoly[j].y - ratioZ * (lightPoly[j].y - poly[i].y);
@@ -615,9 +613,6 @@
         // If there is no real umbra, make a fake one.
         for (int i = 0; i < polyLength; i++) {
             float deltaZ = lightCenter.z - poly[i].z;
-            if (deltaZ == 0) {
-                return;
-            }
             float ratioZ = lightCenter.z / deltaZ;
             float x = lightCenter.x - ratioZ * (lightCenter.x - poly[i].x);
             float y = lightCenter.y - ratioZ * (lightCenter.y - poly[i].y);
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 46b74da..fe510f6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -258,7 +258,7 @@
     private final boolean mUseFixedVolume;
 
     // stream names used by dumpStreamStates()
-    private final String[] STREAM_NAMES = new String[] {
+    private static final String[] STREAM_NAMES = new String[] {
             "STREAM_VOICE_CALL",
             "STREAM_SYSTEM",
             "STREAM_RING",
@@ -614,6 +614,12 @@
         pw.println(Integer.toHexString(mMuteAffectedStreams));
     }
 
+    /** @hide */
+    public static String streamToString(int stream) {
+        if (stream >= 0 && stream < STREAM_NAMES.length) return STREAM_NAMES[stream];
+        if (stream == AudioManager.USE_DEFAULT_STREAM_TYPE) return "USE_DEFAULT_STREAM_TYPE";
+        return "UNKNOWN_STREAM_" + stream;
+    }
 
     private void updateStreamVolumeAlias(boolean updateVolumes) {
         int dtmfStreamAlias;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 5611efb..40c6797 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -23,11 +23,20 @@
 import java.nio.NioUtils;
 
 import android.annotation.IntDef;
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
+import android.content.Context;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.Log;
 
+import com.android.internal.app.IAppOpsService;
+
 
 /**
  * The AudioTrack class manages and plays a single audio resource for Java applications.
@@ -239,7 +248,10 @@
      * Audio session ID
      */
     private int mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE;
-
+    /**
+     * Reference to the app-ops service.
+     */
+    private final IAppOpsService mAppOps;
 
     //--------------------------------
     // Used exclusively by native code
@@ -343,6 +355,9 @@
 
         audioBuffSizeCheck(bufferSizeInBytes);
 
+        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+        mAppOps = IAppOpsService.Stub.asInterface(b);
+
         if (sessionId < 0) {
             throw new IllegalArgumentException("Invalid audio session ID: "+sessionId);
         }
@@ -841,6 +856,9 @@
      *    {@link #ERROR_INVALID_OPERATION}
      */
     public int setStereoVolume(float leftVolume, float rightVolume) {
+        if (isRestricted()) {
+            return SUCCESS;
+        }
         if (mState == STATE_UNINITIALIZED) {
             return ERROR_INVALID_OPERATION;
         }
@@ -1014,13 +1032,25 @@
         if (mState != STATE_INITIALIZED) {
             throw new IllegalStateException("play() called on uninitialized AudioTrack.");
         }
-
+        if (isRestricted()) {
+            setVolume(0);
+        }
         synchronized(mPlayStateLock) {
             native_start();
             mPlayState = PLAYSTATE_PLAYING;
         }
     }
 
+    private boolean isRestricted() {
+        try {
+            final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, mStreamType,
+                    Process.myUid(), ActivityThread.currentPackageName());
+            return mode != AppOpsManager.MODE_ALLOWED;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     /**
      * Stops playing the audio data.
      * When used on an instance created in {@link #MODE_STREAM} mode, audio will stop playing
@@ -1177,14 +1207,11 @@
      * In streaming mode, the blocking behavior will depend on the write mode.
      * @param audioData the buffer that holds the data to play, starting at the position reported
      *     by <code>audioData.position()</code>.
-     *     <BR>Note that this method will not update the position in this buffer, therefore when
-     *     writing a loop to write all the data in the buffer, you should increment the
-     *     <code>offsetInBytes</code> parameter at each pass by the amount that was previously
-     *     written for this buffer.
-     * @param offsetInBytes offset to read from in bytes (note this differs from
-     *     <code>audioData.position()</code>).
-     * @param sizeInBytes number of bytes to read (note this differs from
-     *     <code>audioData.remaining()</code>).
+     *     <BR>Note that upon return, the buffer position (<code>audioData.position()</code>) will
+     *     have been advanced to reflect the amount of data that was successfully written to
+     *     the AudioTrack.
+     * @param sizeInBytes number of bytes to write.
+     *     <BR>Note this may differ from <code>audioData.remaining()</code>, but cannot exceed it.
      * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
      *     effect in static mode.
      *     <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
@@ -1194,7 +1221,7 @@
      * @return 0 or a positive number of bytes that were written, or
      *     {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
      */
-    public int write(ByteBuffer audioData, int offsetInBytes, int sizeInBytes,
+    public int write(ByteBuffer audioData, int sizeInBytes,
             @WriteMode int writeMode) {
 
         if (mState == STATE_UNINITIALIZED) {
@@ -1207,22 +1234,19 @@
             return ERROR_BAD_VALUE;
         }
 
-        if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
-                || (offsetInBytes + sizeInBytes < 0)    // detect integer overflow
-                || (offsetInBytes + sizeInBytes > audioData.remaining())) {
-            Log.e(TAG, "AudioTrack.write() called with invalid size/offset values");
+        if ( (audioData == null) || (sizeInBytes < 0) || (sizeInBytes > audioData.remaining())) {
+            Log.e(TAG, "AudioTrack.write() called with invalid size (" + sizeInBytes + ") value");
             return ERROR_BAD_VALUE;
         }
 
         int ret = 0;
         if (audioData.isDirect()) {
             ret = native_write_native_bytes(audioData,
-                    audioData.position(),
-                    offsetInBytes, sizeInBytes, mAudioFormat,
+                    audioData.position(), sizeInBytes, mAudioFormat,
                     writeMode == WRITE_BLOCKING);
         } else {
             ret = native_write_byte(NioUtils.unsafeArray(audioData),
-                    NioUtils.unsafeArrayOffset(audioData) + audioData.position() + offsetInBytes,
+                    NioUtils.unsafeArrayOffset(audioData) + audioData.position(),
                     sizeInBytes, mAudioFormat,
                     writeMode == WRITE_BLOCKING);
         }
@@ -1234,6 +1258,10 @@
             mState = STATE_INITIALIZED;
         }
 
+        if (ret > 0) {
+            audioData.position(audioData.position() + ret);
+        }
+
         return ret;
     }
 
@@ -1296,6 +1324,9 @@
      *    {@link #ERROR_INVALID_OPERATION}
      */
     public int setAuxEffectSendLevel(float level) {
+        if (isRestricted()) {
+            return SUCCESS;
+        }
         if (mState == STATE_UNINITIALIZED) {
             return ERROR_INVALID_OPERATION;
         }
@@ -1443,7 +1474,7 @@
                                                 int offsetInShorts, int sizeInShorts, int format);
 
     private native final int native_write_native_bytes(Object audioData,
-            int positionInBytes, int offsetInBytes, int sizeInBytes, int format, boolean blocking);
+            int positionInBytes, int sizeInBytes, int format, boolean blocking);
 
     private native final int native_reload_static();
 
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index 67680a8..eb91668 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -269,6 +269,6 @@
         native_init();
     }
 
-    private int mNativeContext;
+    private long mNativeContext;
 
 }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e20a4af..1b92410 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -16,6 +16,8 @@
 
 package android.media;
 
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -32,6 +34,8 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceHolder;
@@ -42,6 +46,8 @@
 import android.media.SubtitleController;
 import android.media.SubtitleData;
 
+import com.android.internal.app.IAppOpsService;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -576,6 +582,8 @@
     private PowerManager.WakeLock mWakeLock = null;
     private boolean mScreenOnWhilePlaying;
     private boolean mStayAwake;
+    private final IAppOpsService mAppOps;
+    private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
 
     /**
      * Default constructor. Consider using one of the create() methods for
@@ -599,6 +607,8 @@
         mOutOfBandSubtitleTracks = new Vector<SubtitleTrack>();
         mOpenSubtitleSources = new Vector<InputStream>();
         mInbandSubtitleTracks = new SubtitleTrack[0];
+        IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+        mAppOps = IAppOpsService.Stub.asInterface(b);
 
         /* Native setup requires a weak reference to our object.
          * It's easier to create it here than in C++.
@@ -1055,13 +1065,35 @@
      *
      * @throws IllegalStateException if it is called in an invalid state
      */
-    public  void start() throws IllegalStateException {
+    public void start() throws IllegalStateException {
+        if (isRestricted()) {
+            _setVolume(0, 0);
+        }
         stayAwake(true);
         _start();
     }
 
     private native void _start() throws IllegalStateException;
 
+    private boolean isRestricted() {
+        try {
+            final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+                    getAudioStreamType(), Process.myUid(), ActivityThread.currentPackageName());
+            return mode != AppOpsManager.MODE_ALLOWED;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    private int getAudioStreamType() {
+        if (mStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
+            mStreamType = _getAudioStreamType();
+        }
+        return mStreamType;
+    }
+
+    private native int _getAudioStreamType() throws IllegalStateException;
+
     /**
      * Stops playback after playback has been stopped or paused.
      *
@@ -1402,7 +1434,12 @@
      * @param streamtype the audio stream type
      * @see android.media.AudioManager
      */
-    public native void setAudioStreamType(int streamtype);
+    public void setAudioStreamType(int streamtype) {
+        _setAudioStreamType(streamtype);
+        mStreamType = streamtype;
+    }
+
+    private native void _setAudioStreamType(int streamtype);
 
     /**
      * Sets the player to be looping or non-looping.
@@ -1435,7 +1472,14 @@
      * The single parameter form below is preferred if the channel volumes don't need
      * to be set independently.
      */
-    public native void setVolume(float leftVolume, float rightVolume);
+    public void setVolume(float leftVolume, float rightVolume) {
+        if (isRestricted()) {
+            return;
+        }
+        _setVolume(leftVolume, rightVolume);
+    }
+
+    private native void _setVolume(float leftVolume, float rightVolume);
 
     /**
      * Similar, excepts sets volume of all channels to same value.
@@ -1500,7 +1544,14 @@
      * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
      * @param level send level scalar
      */
-    public native void setAuxEffectSendLevel(float level);
+    public void setAuxEffectSendLevel(float level) {
+        if (isRestricted()) {
+            return;
+        }
+        _setAuxEffectSendLevel(level);
+    }
+
+    private native void _setAuxEffectSendLevel(float level);
 
     /*
      * @param request Parcel destinated to the media player. The
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index f1b256e..14f0c69 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -20,16 +20,24 @@
 import java.io.FileDescriptor;
 import java.lang.ref.WeakReference;
 
+import android.app.ActivityThread;
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
 import android.os.Handler;
+import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.SystemProperties;
 import android.util.AndroidRuntimeException;
 import android.util.Log;
 
+import com.android.internal.app.IAppOpsService;
+
 
 /**
  * The SoundPool class manages and plays audio resources for applications.
@@ -449,6 +457,8 @@
         private SoundPool mProxy;
 
         private final Object mLock;
+        private final int mStreamType;
+        private final IAppOpsService mAppOps;
 
         // SoundPool messages
         //
@@ -463,6 +473,9 @@
             }
             mLock = new Object();
             mProxy = proxy;
+            mStreamType = streamType;
+            IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE);
+            mAppOps = IAppOpsService.Stub.asInterface(b);
         }
 
         public int load(String path, int priority)
@@ -522,9 +535,27 @@
 
         public native final boolean unload(int soundID);
 
-        public native final int play(int soundID, float leftVolume, float rightVolume,
+        public final int play(int soundID, float leftVolume, float rightVolume,
+                int priority, int loop, float rate) {
+            if (isRestricted()) {
+                leftVolume = rightVolume = 0;
+            }
+            return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
+        }
+
+        public native final int _play(int soundID, float leftVolume, float rightVolume,
                 int priority, int loop, float rate);
 
+        private boolean isRestricted() {
+            try {
+                final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO,
+                        mStreamType, Process.myUid(), ActivityThread.currentPackageName());
+                return mode != AppOpsManager.MODE_ALLOWED;
+            } catch (RemoteException e) {
+                return false;
+            }
+        }
+
         public native final void pause(int streamID);
 
         public native final void resume(int streamID);
@@ -535,8 +566,14 @@
 
         public native final void stop(int streamID);
 
-        public native final void setVolume(int streamID,
-                float leftVolume, float rightVolume);
+        public final void setVolume(int streamID, float leftVolume, float rightVolume) {
+            if (isRestricted()) {
+                return;
+            }
+            _setVolume(streamID, leftVolume, rightVolume);
+        }
+
+        private native final void _setVolume(int streamID, float leftVolume, float rightVolume);
 
         public void setVolume(int streamID, float volume) {
             setVolume(streamID, volume, volume);
diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp
index c48af11..0e7d83e 100644
--- a/media/jni/android_media_MediaHTTPConnection.cpp
+++ b/media/jni/android_media_MediaHTTPConnection.cpp
@@ -84,7 +84,7 @@
 static sp<JMediaHTTPConnection> setObject(
         JNIEnv *env, jobject thiz, const sp<JMediaHTTPConnection> &conn) {
     sp<JMediaHTTPConnection> old =
-        (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context);
+        (JMediaHTTPConnection *)env->GetLongField(thiz, gFields.context);
 
     if (conn != NULL) {
         conn->incStrong(thiz);
@@ -92,13 +92,13 @@
     if (old != NULL) {
         old->decStrong(thiz);
     }
-    env->SetIntField(thiz, gFields.context, (int)conn.get());
+    env->SetLongField(thiz, gFields.context, (jlong)conn.get());
 
     return old;
 }
 
 static sp<JMediaHTTPConnection> getObject(JNIEnv *env, jobject thiz) {
-    return (JMediaHTTPConnection *)env->GetIntField(thiz, gFields.context);
+    return (JMediaHTTPConnection *)env->GetLongField(thiz, gFields.context);
 }
 
 static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) {
@@ -106,7 +106,7 @@
             env, env->FindClass("android/media/MediaHTTPConnection"));
     CHECK(clazz.get() != NULL);
 
-    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "I");
+    gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J");
     CHECK(gFields.context != NULL);
 
     gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I");
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index dc3ae5b..abebd48 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -500,6 +500,20 @@
     process_media_player_call( env, thiz, mp->setAudioStreamType((audio_stream_type_t) streamtype) , NULL, NULL );
 }
 
+static jint
+android_media_MediaPlayer_getAudioStreamType(JNIEnv *env, jobject thiz)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL ) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return 0;
+    }
+    audio_stream_type_t streamtype;
+    process_media_player_call( env, thiz, mp->getAudioStreamType(&streamtype), NULL, NULL );
+    ALOGV("getAudioStreamType: %d (streamtype)", streamtype);
+    return (jint) streamtype;
+}
+
 static void
 android_media_MediaPlayer_setLooping(JNIEnv *env, jobject thiz, jboolean looping)
 {
@@ -841,10 +855,11 @@
     {"getDuration",         "()I",                              (void *)android_media_MediaPlayer_getDuration},
     {"_release",            "()V",                              (void *)android_media_MediaPlayer_release},
     {"_reset",              "()V",                              (void *)android_media_MediaPlayer_reset},
-    {"setAudioStreamType",  "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},
+    {"_setAudioStreamType", "(I)V",                             (void *)android_media_MediaPlayer_setAudioStreamType},
+    {"_getAudioStreamType", "()I",                              (void *)android_media_MediaPlayer_getAudioStreamType},
     {"setLooping",          "(Z)V",                             (void *)android_media_MediaPlayer_setLooping},
     {"isLooping",           "()Z",                              (void *)android_media_MediaPlayer_isLooping},
-    {"setVolume",           "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
+    {"_setVolume",          "(FF)V",                            (void *)android_media_MediaPlayer_setVolume},
     {"native_invoke",       "(Landroid/os/Parcel;Landroid/os/Parcel;)I",(void *)android_media_MediaPlayer_invoke},
     {"native_setMetadataFilter", "(Landroid/os/Parcel;)I",      (void *)android_media_MediaPlayer_setMetadataFilter},
     {"native_getMetadata", "(ZZLandroid/os/Parcel;)Z",          (void *)android_media_MediaPlayer_getMetadata},
@@ -853,7 +868,7 @@
     {"native_finalize",     "()V",                              (void *)android_media_MediaPlayer_native_finalize},
     {"getAudioSessionId",   "()I",                              (void *)android_media_MediaPlayer_get_audio_session_id},
     {"setAudioSessionId",   "(I)V",                             (void *)android_media_MediaPlayer_set_audio_session_id},
-    {"setAuxEffectSendLevel", "(F)V",                           (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
+    {"_setAuxEffectSendLevel", "(F)V",                          (void *)android_media_MediaPlayer_setAuxEffectSendLevel},
     {"attachAuxEffect",     "(I)V",                             (void *)android_media_MediaPlayer_attachAuxEffect},
     {"native_pullBatteryData", "(Landroid/os/Parcel;)I",        (void *)android_media_MediaPlayer_pullBatteryData},
     {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I",  (void *)android_media_MediaPlayer_setRetransmitEndpoint},
diff --git a/media/jni/mediaeditor/Android.mk b/media/jni/mediaeditor/Android.mk
index 76e8346..312c366 100644
--- a/media/jni/mediaeditor/Android.mk
+++ b/media/jni/mediaeditor/Android.mk
@@ -47,7 +47,6 @@
 
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
-    libaudioflinger \
     libaudioutils \
     libbinder \
     libcutils \
diff --git a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
index 9cc55ab..bda3b6b 100644
--- a/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
+++ b/media/jni/soundpool/android_media_SoundPool_SoundPoolImpl.cpp
@@ -229,7 +229,7 @@
         "(I)Z",
         (void *)android_media_SoundPool_SoundPoolImpl_unload
     },
-    {   "play",
+    {   "_play",
         "(IFFIIF)I",
         (void *)android_media_SoundPool_SoundPoolImpl_play
     },
@@ -253,7 +253,7 @@
         "(I)V",
         (void *)android_media_SoundPool_SoundPoolImpl_stop
     },
-    {   "setVolume",
+    {   "_setVolume",
         "(IFF)V",
         (void *)android_media_SoundPool_SoundPoolImpl_setVolume
     },
diff --git a/packages/DocumentsUI/res/values-de/strings.xml b/packages/DocumentsUI/res/values-de/strings.xml
index 3b448d9..e43a1e2 100644
--- a/packages/DocumentsUI/res/values-de/strings.xml
+++ b/packages/DocumentsUI/res/values-de/strings.xml
@@ -47,7 +47,7 @@
     <string name="pref_advanced_devices" msgid="903257239609301276">"Erweiterte Geräte anzeigen"</string>
     <string name="pref_file_size" msgid="2826879315743961459">"Dateigröße anzeigen"</string>
     <string name="pref_device_size" msgid="3542106883278997222">"Geräteabmessungen anzeigen"</string>
-    <string name="empty" msgid="7858882803708117596">"Keine Elemente"</string>
+    <string name="empty" msgid="7858882803708117596">"Keine Dokumente"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"Datei kann nicht geöffnet werden."</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"Einige Dokumente konnten nicht gelöscht werden."</string>
     <string name="share_via" msgid="8966594246261344259">"Teilen über"</string>
diff --git a/packages/DocumentsUI/res/values-el/strings.xml b/packages/DocumentsUI/res/values-el/strings.xml
index aec3318..5a91484 100644
--- a/packages/DocumentsUI/res/values-el/strings.xml
+++ b/packages/DocumentsUI/res/values-el/strings.xml
@@ -27,7 +27,7 @@
     <string name="menu_settings" msgid="6008033148948428823">"Ρυθμίσεις"</string>
     <string name="menu_open" msgid="432922957274920903">"Άνοιγμα"</string>
     <string name="menu_save" msgid="2394743337684426338">"Αποθήκευση"</string>
-    <string name="menu_share" msgid="3075149983979628146">"Κοινή χρήση"</string>
+    <string name="menu_share" msgid="3075149983979628146">"Κοινοποίηση"</string>
     <string name="menu_delete" msgid="8138799623850614177">"Διαγραφή"</string>
     <string name="mode_selected_count" msgid="459111894725594625">"Επιλέχθηκαν <xliff:g id="COUNT">%1$d</xliff:g>"</string>
     <string name="sort_name" msgid="9183560467917256779">"Κατά όνομα"</string>
@@ -50,5 +50,5 @@
     <string name="empty" msgid="7858882803708117596">"Δεν υπάρχουν στοιχεία"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"Δεν είναι δυνατό το άνοιγμα του αρχείου"</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"Δεν είναι δυνατή η διαγραφή ορισμένων εγγράφων"</string>
-    <string name="share_via" msgid="8966594246261344259">"Κοινή χρήση μέσω"</string>
+    <string name="share_via" msgid="8966594246261344259">"Κοινοποίηση μέσω"</string>
 </resources>
diff --git a/packages/DocumentsUI/res/values-in/strings.xml b/packages/DocumentsUI/res/values-in/strings.xml
index 519b936..d9f4475 100644
--- a/packages/DocumentsUI/res/values-in/strings.xml
+++ b/packages/DocumentsUI/res/values-in/strings.xml
@@ -22,7 +22,7 @@
     <string name="menu_create_dir" msgid="5947289605844398389">"Buat folder"</string>
     <string name="menu_grid" msgid="6878021334497835259">"Tampilan kisi"</string>
     <string name="menu_list" msgid="7279285939892417279">"Tampilan daftar"</string>
-    <string name="menu_sort" msgid="7677740407158414452">"Sortir menurut"</string>
+    <string name="menu_sort" msgid="7677740407158414452">"Urutkan menurut"</string>
     <string name="menu_search" msgid="3816712084502856974">"Telusuri"</string>
     <string name="menu_settings" msgid="6008033148948428823">"Setelan"</string>
     <string name="menu_open" msgid="432922957274920903">"Buka"</string>
diff --git a/packages/DocumentsUI/res/values-sk/strings.xml b/packages/DocumentsUI/res/values-sk/strings.xml
index 2a96b1a..4b5ebcd 100644
--- a/packages/DocumentsUI/res/values-sk/strings.xml
+++ b/packages/DocumentsUI/res/values-sk/strings.xml
@@ -44,7 +44,7 @@
     <string name="root_type_shortcut" msgid="3318760609471618093">"Skratky"</string>
     <string name="root_type_device" msgid="7121342474653483538">"Zariadenia"</string>
     <string name="root_type_apps" msgid="8838065367985945189">"Ďalšie aplikácie"</string>
-    <string name="pref_advanced_devices" msgid="903257239609301276">"Zobraziť rozšírené zariadenia"</string>
+    <string name="pref_advanced_devices" msgid="903257239609301276">"Zobraziť pokročilé zariadenia"</string>
     <string name="pref_file_size" msgid="2826879315743961459">"Zobraziť veľkosť súboru"</string>
     <string name="pref_device_size" msgid="3542106883278997222">"Zobraziť veľkosť zariadenia"</string>
     <string name="empty" msgid="7858882803708117596">"Žiadne položky"</string>
diff --git a/packages/ExternalStorageProvider/res/values-sk/strings.xml b/packages/ExternalStorageProvider/res/values-sk/strings.xml
index fd424c8..9be7b79 100644
--- a/packages/ExternalStorageProvider/res/values-sk/strings.xml
+++ b/packages/ExternalStorageProvider/res/values-sk/strings.xml
@@ -17,6 +17,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="7123375275748530234">"Externý ukladací priestor"</string>
-    <string name="root_internal_storage" msgid="827844243068584127">"Interný ukladací priestor"</string>
+    <string name="root_internal_storage" msgid="827844243068584127">"Interné úložisko"</string>
     <string name="root_documents" msgid="4051252304075469250">"Dokumenty"</string>
 </resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 1e79ee4..2e76f19 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -1120,6 +1120,7 @@
             KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i);
             frame.removeAllViews();
         }
+        getSecurityContainer().onPause(); // clean up any actions in progress
     }
 
     public void goToWidget(int appWidgetId) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
index 8738288..5b35ba88 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewManager.java
@@ -400,6 +400,7 @@
         } else {
             mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
         }
+        mWindowLayoutParams.format = show ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
 
         mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);
     }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
index 31e806c..3fc562c 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewMediator.java
@@ -979,6 +979,13 @@
             return;
         }
 
+        if (mLockPatternUtils.checkVoldPassword()) {
+            if (DEBUG) Log.d(TAG, "Not showing lock screen since just decrypted");
+            // Without this, settings is not enabled until the lock screen first appears
+            hideLocked();
+            return;
+        }
+
         if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
         showLocked(options);
     }
diff --git a/packages/PrintSpooler/res/values-fr/strings.xml b/packages/PrintSpooler/res/values-fr/strings.xml
index cfb557e..17fabdc 100644
--- a/packages/PrintSpooler/res/values-fr/strings.xml
+++ b/packages/PrintSpooler/res/values-fr/strings.xml
@@ -33,7 +33,7 @@
     <string name="page_count_unknown" msgid="6058852665954511124">"Pages"</string>
     <string name="generating_print_job" msgid="3119608742651698916">"Génération tâche impression…"</string>
     <string name="save_as_pdf" msgid="5718454119847596853">"Enregistrer au format .PDF"</string>
-    <string name="all_printers" msgid="5018829726861876202">"Toutes les imprimantes…"</string>
+    <string name="all_printers" msgid="5018829726861876202">"Toutes les imprim."</string>
     <string name="print_dialog" msgid="32628687461331979">"Boîte de dialogue d\'impression"</string>
     <string name="search" msgid="5421724265322228497">"Rechercher"</string>
     <string name="all_printers_label" msgid="3178848870161526399">"Toutes les imprimantes"</string>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 55d7def..58086c4 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -69,7 +69,7 @@
     // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion'
     // is properly propagated through your change.  Not doing so will result in a loss of user
     // settings.
-    private static final int DATABASE_VERSION = 100;
+    private static final int DATABASE_VERSION = 101;
 
     private Context mContext;
     private int mUserHandle;
@@ -1591,6 +1591,28 @@
             }
             upgradeVersion = 100;
         }
+        if (upgradeVersion == 100) {
+           // Catch devices that were initialized to version 100 and missed these in onCreate()
+            if (mUserHandle == UserHandle.USER_OWNER) {
+                db.beginTransaction();
+                SQLiteStatement stmt = null;
+                try {
+                    stmt = db.compileStatement("INSERT OR IGNORE INTO global(name,value)"
+                            + " VALUES(?,?);");
+                    loadIntegerSetting(stmt, Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                            R.integer.def_lock_screen_show_notifications);
+
+                    loadIntegerSetting(stmt, Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+                            R.integer.def_heads_up_enabled);
+
+                    db.setTransactionSuccessful();
+                } finally {
+                    db.endTransaction();
+                    if (stmt != null) stmt.close();
+                }
+            }
+            upgradeVersion = 101;
+        }
 
         // *** Remember to update DATABASE_VERSION above!
 
@@ -2314,6 +2336,12 @@
             loadIntegerSetting(stmt, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
                     R.integer.def_wifi_scan_always_available);
 
+            loadIntegerSetting(stmt, Settings.Global.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                    R.integer.def_lock_screen_show_notifications);
+
+            loadIntegerSetting(stmt, Global.HEADS_UP_NOTIFICATIONS_ENABLED,
+                    R.integer.def_heads_up_enabled);
+
             // --- New global settings start here
         } finally {
             if (stmt != null) stmt.close();
diff --git a/packages/Shell/res/values-el/strings.xml b/packages/Shell/res/values-el/strings.xml
index 3669f78..9b1eb7b 100644
--- a/packages/Shell/res/values-el/strings.xml
+++ b/packages/Shell/res/values-el/strings.xml
@@ -18,7 +18,7 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="3701846017049540910">"Κέλυφος"</string>
     <string name="bugreport_finished_title" msgid="2293711546892863898">"Η λήψη της αναφοράς ήταν επιτυχής"</string>
-    <string name="bugreport_finished_text" msgid="3559904746859400732">"Αγγίξτε για κοινή χρήση της αναφοράς σας σφαλμάτων"</string>
+    <string name="bugreport_finished_text" msgid="3559904746859400732">"Αγγίξτε για να μοιραστείτε τη αναφορά σφαλμάτων"</string>
     <string name="bugreport_confirm" msgid="5130698467795669780">"Οι αναφορές σφαλμάτων περιέχουν δεδομένα από τα διάφορα αρχεία καταγραφής του συστήματος, συμπεριλαμβανομένων προσωπικών και ιδιωτικών πληροφοριών. Να μοιράζεστε αναφορές σφαλμάτων μόνο με εφαρμογές και άτομα που εμπιστεύεστε."</string>
     <string name="bugreport_confirm_repeat" msgid="4926842460688645058">"Εμφάνιση αυτού του μηνύματος την επόμενη φορά"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 71a644d..7671ef1 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -206,7 +206,7 @@
     <string name="quick_settings_inversion_label" msgid="1666358784283020762">"Modalità inversione colori"</string>
     <string name="quick_settings_contrast_label" msgid="3319507551689108692">"Modalità di contrasto avanzata"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"Modalità di correzione del colore"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"ELEMENTI RECENTI"</string>
+    <string name="recents_empty_message" msgid="2269156590813544104">"MESSAGGI RECENTI"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"La rete potrebbe\nessere monitorata"</string>
     <string name="description_target_search" msgid="3091587249776033139">"Ricerca"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"Su per <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 6e09131..032fa34 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -204,7 +204,7 @@
     <string name="quick_settings_inversion_label" msgid="1666358784283020762">"Modus voor kleurinversie"</string>
     <string name="quick_settings_contrast_label" msgid="3319507551689108692">"Modus voor verbeterd contrast"</string>
     <string name="quick_settings_color_space_label" msgid="853443689745584770">"Modus voor kleurcorrectie"</string>
-    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTEN"</string>
+    <string name="recents_empty_message" msgid="2269156590813544104">"RECENTE"</string>
     <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"Netwerk kan\nworden gecontroleerd"</string>
     <string name="description_target_search" msgid="3091587249776033139">"Zoeken"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"Veeg omhoog voor <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g>."</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 0d9be4c..0188563 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -151,7 +151,7 @@
     <string name="accessibility_quick_settings_wifi" msgid="6099781031669728709">"<xliff:g id="SIGNAL">%1$s</xliff:g> <xliff:g id="NETWORK">%2$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_mobile" msgid="4876806564086241341">"มือถือ <xliff:g id="SIGNAL">%1$s</xliff:g> <xliff:g id="TYPE">%2$s</xliff:g> <xliff:g id="NETWORK">%3$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_battery" msgid="1480931583381408972">"แบตเตอรี่ <xliff:g id="STATE">%s</xliff:g>"</string>
-    <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"โหมดใช้งานบนเครื่องบิน <xliff:g id="STATE">%s</xliff:g>"</string>
+    <string name="accessibility_quick_settings_airplane" msgid="4196876722090224753">"โหมดใช้บนเครื่องบิน <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_bluetooth" msgid="5749054971341882340">"บลูทูธ <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_location" msgid="4577282329866813100">"สถานที่ <xliff:g id="STATE">%s</xliff:g>"</string>
     <string name="accessibility_quick_settings_alarm" msgid="3959908972897295660">"ตั้งเวลาปลุกไว้ที่ <xliff:g id="TIME">%s</xliff:g>"</string>
@@ -174,7 +174,7 @@
     <string name="dessert_case" msgid="1295161776223959221">"ชั้นแสดงของหวาน"</string>
     <string name="start_dreams" msgid="7219575858348719790">"Daydream"</string>
     <string name="ethernet_label" msgid="7967563676324087464">"อีเทอร์เน็ต"</string>
-    <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้งานบนเครื่องบิน"</string>
+    <string name="quick_settings_airplane_mode_label" msgid="5510520633448831350">"โหมดใช้บนเครื่องบิน"</string>
     <string name="quick_settings_battery_charging_label" msgid="490074774465309209">"กำลังชาร์จ, <xliff:g id="NUMBER">%d</xliff:g><xliff:g id="PERCENT">%%</xliff:g>"</string>
     <string name="quick_settings_battery_charged_label" msgid="8865413079414246081">"ชาร์จแล้ว"</string>
     <string name="quick_settings_bluetooth_label" msgid="6304190285170721401">"บลูทูธ"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 55399c2..163ef2a 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -199,7 +199,7 @@
     <string name="quick_settings_wifi_label" msgid="9135344704899546041">"Wi-Fi"</string>
     <string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"未連線"</string>
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"沒有網路"</string>
-    <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi:關閉"</string>
+    <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi 已關閉"</string>
     <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"投放螢幕"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"亮度"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"自動"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index e1a4bb2..48a6522 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -22,6 +22,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.content.Context;
+import android.media.AudioManager;
 import android.os.Vibrator;
 import android.util.Log;
 import android.view.Gravity;
@@ -605,7 +606,7 @@
             mVibrator = (android.os.Vibrator)
                     mContext.getSystemService(Context.VIBRATOR_SERVICE);
         }
-        mVibrator.vibrate(duration);
+        mVibrator.vibrate(duration, AudioManager.STREAM_SYSTEM);
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
index b096ead..09aa60f 100644
--- a/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/SearchPanelView.java
@@ -25,6 +25,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.media.AudioManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
@@ -207,7 +208,8 @@
                 Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, UserHandle.USER_CURRENT) != 0) {
             Resources res = context.getResources();
             Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
-            vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration));
+            vibrator.vibrate(res.getInteger(R.integer.config_search_panel_view_vibration_duration),
+                    AudioManager.STREAM_SYSTEM);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index ed981ed..3d47cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
@@ -31,6 +32,7 @@
     DisplayMetrics mDisplayMetrics;
 
     public Rect systemInsets = new Rect();
+    public Rect displayRect = new Rect();
 
     /** Private constructor */
     private RecentsConfiguration() {}
@@ -51,10 +53,11 @@
 
     /** Updates the state, given the specified context */
     void update(Context context) {
-        mDisplayMetrics = context.getResources().getDisplayMetrics();
+        Resources res = context.getResources();
+        DisplayMetrics dm = res.getDisplayMetrics();
+        mDisplayMetrics = dm;
 
-        boolean isPortrait = context.getResources().getConfiguration().orientation ==
-                Configuration.ORIENTATION_PORTRAIT;
+        displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
     }
 
     public void updateSystemInsets(Rect insets) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index c5e325e..cdbee82 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -29,45 +29,73 @@
 import android.os.HandlerThread;
 import android.os.UserHandle;
 import android.util.LruCache;
+import android.util.Pair;
 import com.android.systemui.recents.model.SpaceNode;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
 
 
 /** A bitmap load queue */
 class TaskResourceLoadQueue {
     ConcurrentLinkedQueue<Task> mQueue = new ConcurrentLinkedQueue<Task>();
+    ConcurrentHashMap<Task.TaskKey, Boolean> mForceLoadSet =
+            new ConcurrentHashMap<Task.TaskKey, Boolean>();
 
-    Task nextTask() {
-        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
-        return mQueue.poll();
-    }
+    static final Boolean sFalse = new Boolean(false);
 
-    void addTask(Task t) {
+    /** Adds a new task to the load queue */
+    void addTask(Task t, boolean forceLoad) {
         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|addTask]");
         if (!mQueue.contains(t)) {
             mQueue.add(t);
         }
+        if (forceLoad) {
+            mForceLoadSet.put(t.key, new Boolean(true));
+        }
         synchronized(this) {
             notifyAll();
         }
     }
 
+    /**
+     * Retrieves the next task from the load queue, as well as whether we want that task to be
+     * force reloaded.
+     */
+    Pair<Task, Boolean> nextTask() {
+        Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|nextTask]");
+        Task task = mQueue.poll();
+        Boolean forceLoadTask = null;
+        if (task != null) {
+            forceLoadTask = mForceLoadSet.remove(task.key);
+        }
+        if (forceLoadTask == null) {
+            forceLoadTask = sFalse;
+        }
+        return new Pair<Task, Boolean>(task, forceLoadTask);
+    }
+
+    /** Removes a task from the load queue */
     void removeTask(Task t) {
         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|removeTask]");
         mQueue.remove(t);
+        mForceLoadSet.remove(t.key);
     }
 
+    /** Clears all the tasks from the load queue */
     void clearTasks() {
         Console.log(Constants.DebugFlags.App.TaskDataLoader, "  [TaskResourceLoadQueue|clearTasks]");
         mQueue.clear();
+        mForceLoadSet.clear();
     }
 
+    /** Returns whether the load queue is empty */
     boolean isEmpty() {
         return mQueue.isEmpty();
     }
@@ -147,16 +175,19 @@
                 }
             } else {
                 // Load the next item from the queue
-                final Task t = mLoadQueue.nextTask();
+                Pair<Task, Boolean> nextTaskData = mLoadQueue.nextTask();
+                final Task t = nextTaskData.first;
+                final boolean forceLoadTask = nextTaskData.second;
                 if (t != null) {
                     try {
                         Drawable loadIcon = mIconCache.get(t.key);
                         Bitmap loadThumbnail = mThumbnailCache.get(t.key);
                         Console.log(Constants.DebugFlags.App.TaskDataLoader,
                                 "  [TaskResourceLoader|load]",
-                                t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail);
+                                t + " icon: " + loadIcon + " thumbnail: " + loadThumbnail +
+                                        " forceLoad: " + forceLoadTask);
                         // Load the icon
-                        if (loadIcon == null) {
+                        if (loadIcon == null || forceLoadTask) {
                             PackageManager pm = mContext.getPackageManager();
                             ActivityInfo info = pm.getActivityInfo(t.key.intent.getComponent(),
                                     PackageManager.GET_META_DATA);
@@ -172,7 +203,7 @@
                             }
                         }
                         // Load the thumbnail
-                        if (loadThumbnail == null) {
+                        if (loadThumbnail == null || forceLoadTask) {
                             ActivityManager am = (ActivityManager)
                                     mContext.getSystemService(Context.ACTIVITY_SERVICE);
                             Bitmap thumbnail = am.getTaskTopThumbnail(t.key.id);
@@ -197,7 +228,7 @@
                             mMainThreadHandler.post(new Runnable() {
                                 @Override
                                 public void run() {
-                                    t.notifyTaskDataLoaded(newThumbnail, newIcon);
+                                    t.notifyTaskDataLoaded(newThumbnail, newIcon, forceLoadTask);
                                 }
                             });
                         }
@@ -329,9 +360,11 @@
     /** Reload the set of recent tasks */
     SpaceNode reload(Context context, int preloadCount) {
         Console.log(Constants.DebugFlags.App.TaskDataLoader, "[RecentsTaskLoader|reload]");
+        ArrayList<Task> tasksToForceLoad = new ArrayList<Task>();
         TaskStack stack = new TaskStack(context);
         SpaceNode root = new SpaceNode(context);
         root.setStack(stack);
+
         try {
             long t1 = System.currentTimeMillis();
 
@@ -387,6 +420,12 @@
                     // Load the icon (if possible and not the foremost task, from the cache)
                     if (!isForemostTask) {
                         task.icon = mIconCache.get(task.key);
+
+                        if (task.icon != null) {
+                            // Even though we get things from the cache, we should update them if
+                            // they've changed in the bg
+                            tasksToForceLoad.add(task);
+                        }
                     }
                     if (task.icon == null) {
                         task.icon = info.loadIcon(pm);
@@ -400,6 +439,12 @@
                     // Load the thumbnail (if possible and not the foremost task, from the cache)
                     if (!isForemostTask) {
                         task.thumbnail = mThumbnailCache.get(task.key);
+
+                        if (task.thumbnail != null) {
+                            // Even though we get things from the cache, we should update them if
+                            // they've changed in the bg
+                            tasksToForceLoad.add(task);
+                        }
                     }
                     if (task.thumbnail == null) {
                         Console.log(Constants.DebugFlags.App.TaskDataLoader,
@@ -451,6 +496,11 @@
         // Start the task loader
         mLoader.start(context);
 
+        // Add all the tasks that we are force/re-loading
+        for (Task t : tasksToForceLoad) {
+            mLoadQueue.addTask(t, true);
+        }
+
         return root;
     }
 
@@ -473,9 +523,9 @@
             requiresLoad = true;
         }
         if (requiresLoad) {
-            mLoadQueue.addTask(t);
+            mLoadQueue.addTask(t, false);
         }
-        t.notifyTaskDataLoaded(thumbnail, icon);
+        t.notifyTaskDataLoaded(thumbnail, icon, false);
     }
 
     /** Releases the task resource data back into the pool. */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 0c3c528..edcf9b2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -29,7 +29,7 @@
     /* Task callbacks */
     public interface TaskCallbacks {
         /* Notifies when a task has been bound */
-        public void onTaskDataLoaded();
+        public void onTaskDataLoaded(boolean reloadingTaskData);
         /* Notifies when a task has been unbound */
         public void onTaskDataUnloaded();
     }
@@ -84,11 +84,11 @@
     }
 
     /** Notifies the callback listeners that this task has been loaded */
-    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable icon) {
+    public void notifyTaskDataLoaded(Bitmap thumbnail, Drawable icon, boolean reloadingTaskData) {
         this.icon = icon;
         this.thumbnail = thumbnail;
         if (mCb != null) {
-            mCb.onTaskDataLoaded();
+            mCb.onTaskDataLoaded(reloadingTaskData);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 77b78f3..d997222 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -17,6 +17,7 @@
 package com.android.systemui.recents.views;
 
 import android.app.ActivityOptions;
+import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
@@ -210,11 +211,14 @@
                 int offsetX = 0;
                 int offsetY = 0;
                 if (tv == null) {
-                    // Launch the activity
+                    // If there is no actual task view, then use the stack view as the source view
+                    // and then offset to the expected transform rect, but bound this to just
+                    // outside the display rect (to ensure we don't animate from too far away)
+                    RecentsConfiguration config = RecentsConfiguration.getInstance();
                     sourceView = stackView;
                     transform = stackView.getStackTransform(stack.indexOfTask(task));
                     offsetX = transform.rect.left;
-                    offsetY = transform.rect.top;
+                    offsetY = Math.min(transform.rect.top, config.displayRect.height());
                 } else {
                     transform = stackView.getStackTransform(stack.indexOfTask(task));
                 }
@@ -242,10 +246,14 @@
                 i.setFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
                         | Intent.FLAG_ACTIVITY_TASK_ON_HOME
                         | Intent.FLAG_ACTIVITY_NEW_TASK);
-                if (opts != null) {
-                    getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT);
-                } else {
-                    getContext().startActivityAsUser(i, UserHandle.CURRENT);
+                try {
+                    if (opts != null) {
+                        getContext().startActivityAsUser(i, opts.toBundle(), UserHandle.CURRENT);
+                    } else {
+                        getContext().startActivityAsUser(i, UserHandle.CURRENT);
+                    }
+                } catch (ActivityNotFoundException anfe) {
+                    Console.logError(getContext(), "Could not start Activity");
                 }
 
                 Console.logTraceTime(Constants.DebugFlags.App.TimeRecentsLaunchTask,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 62cf394..2b27a06 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -881,6 +881,13 @@
                 }
                 break;
             }
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
+                mActivePointerId = ev.getPointerId(index);
+                mLastMotionX = (int) ev.getX(index);
+                mLastMotionY = (int) ev.getY(index);
+                break;
+            }
             case MotionEvent.ACTION_MOVE: {
                 if (mActivePointerId == INACTIVE_POINTER_ID) break;
 
@@ -957,6 +964,19 @@
                 recycleVelocityTracker();
                 break;
             }
+            case MotionEvent.ACTION_POINTER_UP: {
+                int pointerIndex = ev.getActionIndex();
+                int pointerId = ev.getPointerId(pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    // Select a new active pointer id and reset the motion state
+                    final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
+                    mActivePointerId = ev.getPointerId(newPointerIndex);
+                    mLastMotionX = (int) ev.getX(newPointerIndex);
+                    mLastMotionY = (int) ev.getY(newPointerIndex);
+                    mVelocityTracker.clear();
+                }
+                break;
+            }
             case MotionEvent.ACTION_CANCEL: {
                 if (mIsScrolling) {
                     // Disable HW layers
@@ -1006,10 +1026,6 @@
         Task task = tv.getTask();
         Activity activity = (Activity) mSv.getContext();
 
-        // We have to disable the listener to ensure that we
-        // don't hit this again
-        tv.animate().setListener(null);
-
         // Remove the task from the view
         mSv.mStack.removeTask(task);
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index f411717..e6dce37 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -65,10 +65,7 @@
 
             setImageBitmap(t.thumbnail);
             if (animate) {
-                setAlpha(0f);
-                animate().alpha(1f)
-                        .setDuration(Constants.Values.TaskView.Animation.TaskDataUpdatedFadeDuration)
-                        .start();
+                // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
             }
         }
     }
@@ -143,10 +140,7 @@
         if (t.icon != null) {
             setImageDrawable(t.icon);
             if (animate) {
-                setAlpha(0f);
-                animate().alpha(1f)
-                        .setDuration(Constants.Values.TaskView.Animation.TaskDataUpdatedFadeDuration)
-                        .start();
+                // XXX: Investigate how expensive it will be to create a second bitmap and crossfade
             }
         }
     }
@@ -311,6 +305,7 @@
                     .translationY(0)
                     .setStartDelay(235)
                     .setDuration(Constants.Values.TaskView.Animation.TaskIconOnEnterDuration)
+                    .withLayer()
                     .start();
         }
     }
@@ -333,13 +328,8 @@
                 .setStartDelay(0)
                 .setDuration(Constants.Values.TaskView.Animation.TaskIconOnLeavingDuration)
                 .setInterpolator(new DecelerateInterpolator())
-                .setListener(
-                    new AnimatorListenerAdapter() {
-                        @Override
-                        public void onAnimationEnd(Animator animation) {
-                            r.run();
-                        }
-                    })
+                .withLayer()
+                .withEndAction(r)
                 .start();
         }
     }
@@ -379,10 +369,10 @@
     }
 
     @Override
-    public void onTaskDataLoaded() {
+    public void onTaskDataLoaded(boolean reloadingTaskData) {
         // Bind each of the views to the new task data
-        mThumbnailView.rebindToTask(mTask, false);
-        mIconView.rebindToTask(mTask, false);
+        mThumbnailView.rebindToTask(mTask, reloadingTaskData);
+        mIconView.rebindToTask(mTask, reloadingTaskData);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index bd36128..68c8364 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -111,7 +111,7 @@
 
     protected static final boolean ENABLE_HEADS_UP = true;
     // scores above this threshold should be displayed in heads up mode.
-    protected static final int INTERRUPTION_THRESHOLD = 11;
+    protected static final int INTERRUPTION_THRESHOLD = 10;
     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
     // Should match the value in PhoneWindowManager
@@ -757,6 +757,7 @@
 
     protected void toggleRecentsActivity() {
         if (mRecents != null) {
+            sendCloseSystemWindows(mContext, SYSTEM_DIALOG_REASON_RECENT_APPS);
             mRecents.toggleRecents(mDisplay, mLayoutDirection, getStatusBarView());
         }
     }
@@ -1521,17 +1522,8 @@
     // A: Almost none! Only things coming from the system (package is "android") that also
     // have special "kind" tags marking them as relevant for setup (see below).
     protected boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
-        if ("android".equals(sbn.getPackageName())) {
-            if (sbn.getNotification().kind != null) {
-                for (String aKind : sbn.getNotification().kind) {
-                    // IME switcher, created by InputMethodManagerService
-                    if ("android.system.imeswitcher".equals(aKind)) return true;
-                    // OTA availability & errors, created by SystemUpdateService
-                    if ("android.system.update".equals(aKind)) return true;
-                }
-            }
-        }
-        return false;
+        return "android".equals(sbn.getPackageName())
+                && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
     }
 
     public boolean inKeyguardRestrictedInputMode() {
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 9540bd4..91325ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -49,6 +49,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.inputmethodservice.InputMethodService;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -2713,7 +2714,7 @@
     void vibrate() {
         android.os.Vibrator vib = (android.os.Vibrator)mContext.getSystemService(
                 Context.VIBRATOR_SERVICE);
-        vib.vibrate(250);
+        vib.vibrate(250, AudioManager.STREAM_SYSTEM);
     }
 
     Runnable mStartTracing = new Runnable() {
diff --git a/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml b/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml
index 1622742..2a0188a 100644
--- a/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml
+++ b/packages/WallpaperCropper/res/layout/actionbar_set_wallpaper.xml
@@ -20,6 +20,7 @@
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     style="?android:actionButtonStyle"
+    android:id="@+id/set_wallpaper_button"
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
     <TextView style="?android:actionBarTabTextStyle"
diff --git a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
index cdc5cdc..764156d 100644
--- a/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
+++ b/packages/WallpaperCropper/src/com/android/photos/BitmapRegionTileSource.java
@@ -245,6 +245,9 @@
             try {
                 ei.readExif(mPath);
                 return true;
+            } catch (NullPointerException e) {
+                Log.w("BitmapRegionTileSource", "reading exif failed", e);
+                return false;
             } catch (IOException e) {
                 Log.w("BitmapRegionTileSource", "getting decoder failed", e);
                 return false;
@@ -311,6 +314,9 @@
             } catch (IOException e) {
                 Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
                 return false;
+            } catch (NullPointerException e) {
+                Log.e("BitmapRegionTileSource", "Failed to read EXIF for URI " + mUri, e);
+                return false;
             } finally {
                 Utils.closeSilently(is);
             }
diff --git a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
index e45b98c..d6c0c99 100644
--- a/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
+++ b/packages/WallpaperCropper/src/com/android/wallpapercropper/WallpaperCropActivity.java
@@ -75,6 +75,7 @@
 
     protected CropView mCropView;
     protected Uri mUri;
+    private View mSetWallpaperButton;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -111,10 +112,12 @@
                         cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
                     }
                 });
+        mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
 
         // Load image in background
         final BitmapRegionTileSource.UriBitmapSource bitmapSource =
                 new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
+        mSetWallpaperButton.setVisibility(View.INVISIBLE);
         Runnable onLoad = new Runnable() {
             public void run() {
                 if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
@@ -122,6 +125,8 @@
                             getString(R.string.wallpaper_load_fail),
                             Toast.LENGTH_LONG).show();
                     finish();
+                } else {
+                    mSetWallpaperButton.setVisibility(View.VISIBLE);
                 }
             }
         };
@@ -136,7 +141,21 @@
         final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
             protected Void doInBackground(Void...args) {
                 if (!isCancelled()) {
-                    bitmapSource.loadInBackground();
+                    try {
+                        bitmapSource.loadInBackground();
+                    } catch (SecurityException securityException) {
+                        if (isDestroyed()) {
+                            // Temporarily granted permissions are revoked when the activity
+                            // finishes, potentially resulting in a SecurityException here.
+                            // Even though {@link #isDestroyed} might also return true in different
+                            // situations where the configuration changes, we are fine with
+                            // catching these cases here as well.
+                            cancel(false);
+                        } else {
+                            // otherwise it had a different cause and we throw it further
+                            throw securityException;
+                        }
+                    }
                 }
                 return null;
             }
@@ -271,6 +290,9 @@
             }
         } catch (IOException e) {
             Log.w(LOGTAG, "Getting exif data failed", e);
+        } catch (NullPointerException e) {
+            // Sometimes the ExifInterface has an internal NPE if Exif data isn't valid
+            Log.w(LOGTAG, "Getting exif data failed", e);
         } finally {
             Utils.closeSilently(bis);
             Utils.closeSilently(is);
diff --git a/packages/services/PacProcessor/jni/Android.mk b/packages/services/PacProcessor/jni/Android.mk
index f16c90b..8a60927 100644
--- a/packages/services/PacProcessor/jni/Android.mk
+++ b/packages/services/PacProcessor/jni/Android.mk
@@ -35,6 +35,7 @@
 
 LOCAL_MODULE := libjni_pacprocessor
 LOCAL_MODULE_TAGS := optional
+LOCAL_32_BIT_ONLY := true
 
 include external/stlport/libstlport.mk
 
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 1cca164..3c23c6e 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -91,6 +91,7 @@
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 
@@ -212,6 +213,7 @@
     final Object mServiceAquireLock = new Object();
     Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
     SearchManager mSearchManager;
+    AccessibilityManager mAccessibilityManager;
 
     // Vibrator pattern for haptic feedback of a long press.
     long[] mLongPressVibePattern;
@@ -299,7 +301,6 @@
     boolean mOrientationSensorEnabled = false;
     int mCurrentAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     boolean mHasSoftInput = false;
-    boolean mTouchExplorationEnabled = false;
     boolean mTranslucentDecorEnabled = true;
 
     int mPointerLocationMode = 0; // guarded by mLock
@@ -905,6 +906,9 @@
                 com.android.internal.R.bool.config_enableTranslucentDecor);
         readConfigurationDependentBehaviors();
 
+        mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
+
         // register for dock events
         IntentFilter filter = new IntentFilter();
         filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
@@ -1105,7 +1109,8 @@
      *         navigation bar and touch exploration is not enabled
      */
     private boolean canHideNavigationBar() {
-        return mHasNavigationBar && !mTouchExplorationEnabled;
+        return mHasNavigationBar
+                && !mAccessibilityManager.isTouchExplorationEnabled();
     }
 
     @Override
@@ -5083,10 +5088,10 @@
         }
         if (pattern.length == 1) {
             // One-shot vibration
-            mVibrator.vibrate(owningUid, owningPackage, pattern[0]);
+            mVibrator.vibrate(owningUid, owningPackage, pattern[0], AudioManager.STREAM_SYSTEM);
         } else {
             // Pattern vibration
-            mVibrator.vibrate(owningUid, owningPackage, pattern, -1);
+            mVibrator.vibrate(owningUid, owningPackage, pattern, -1, AudioManager.STREAM_SYSTEM);
         }
         return true;
     }
@@ -5246,7 +5251,8 @@
      * R.boolean.config_enableTranslucentDecor is false.
      */
     private boolean areTranslucentBarsAllowed() {
-        return mTranslucentDecorEnabled && !mTouchExplorationEnabled;
+        return mTranslucentDecorEnabled
+                && !mAccessibilityManager.isTouchExplorationEnabled();
     }
 
     // Use this instead of checking config_showNavigationBar so that it can be consistently
@@ -5297,11 +5303,6 @@
     }
 
     @Override
-    public void setTouchExplorationEnabled(boolean enabled) {
-        mTouchExplorationEnabled = enabled;
-    }
-
-    @Override
     public boolean isTopLevelWindow(int windowType) {
         if (windowType >= WindowManager.LayoutParams.FIRST_SUB_WINDOW
                 && windowType <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index d4fa5a7..dce4f581 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -235,8 +235,8 @@
         rsnContextSendMessage(mContext, id, data);
     }
 
-    native void rsnContextBindRootScript(long con, int script);
-    synchronized void nContextBindRootScript(int script) {
+    native void rsnContextBindRootScript(long con, long script);
+    synchronized void nContextBindRootScript(long script) {
         validate();
         rsnContextBindRootScript(mContext, script);
     }
@@ -245,23 +245,23 @@
         validate();
         rsnContextBindSampler(mContext, sampler, slot);
     }
-    native void rsnContextBindProgramStore(long con, int pfs);
-    synchronized void nContextBindProgramStore(int pfs) {
+    native void rsnContextBindProgramStore(long con, long pfs);
+    synchronized void nContextBindProgramStore(long pfs) {
         validate();
         rsnContextBindProgramStore(mContext, pfs);
     }
-    native void rsnContextBindProgramFragment(long con, int pf);
-    synchronized void nContextBindProgramFragment(int pf) {
+    native void rsnContextBindProgramFragment(long con, long pf);
+    synchronized void nContextBindProgramFragment(long pf) {
         validate();
         rsnContextBindProgramFragment(mContext, pf);
     }
-    native void rsnContextBindProgramVertex(long con, int pv);
-    synchronized void nContextBindProgramVertex(int pv) {
+    native void rsnContextBindProgramVertex(long con, long pv);
+    synchronized void nContextBindProgramVertex(long pv) {
         validate();
         rsnContextBindProgramVertex(mContext, pv);
     }
-    native void rsnContextBindProgramRaster(long con, int pr);
-    synchronized void nContextBindProgramRaster(int pr) {
+    native void rsnContextBindProgramRaster(long con, long pr);
+    synchronized void nContextBindProgramRaster(long pr) {
         validate();
         rsnContextBindProgramRaster(mContext, pr);
     }
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index b547706..671b43d 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -452,7 +452,7 @@
     // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
     assert(dataSize == 5);
 
-    uint32_t elementData[5];
+    uintptr_t elementData[5];
     rsaElementGetNativeData((RsContext)con, (RsElement)id, elementData, dataSize);
 
     for(jint i = 0; i < dataSize; i ++) {
@@ -473,7 +473,7 @@
 
     uintptr_t *ids = (uintptr_t*)malloc(dataSize * sizeof(uintptr_t));
     const char **names = (const char **)malloc(dataSize * sizeof(const char *));
-    size_t *arraySizes = (size_t *)malloc(dataSize * sizeof(size_t));
+    uint32_t *arraySizes = (uint32_t *)malloc(dataSize * sizeof(uint32_t));
 
     rsaElementGetSubElements((RsContext)con, (RsElement)id, ids, names, arraySizes, (uint32_t)dataSize);
 
@@ -1404,35 +1404,35 @@
 // ---------------------------------------------------------------------------
 
 static void
-nContextBindRootScript(JNIEnv *_env, jobject _this, jlong con, jint script)
+nContextBindRootScript(JNIEnv *_env, jobject _this, jlong con, jlong script)
 {
     LOG_API("nContextBindRootScript, con(%p), script(%p)", (RsContext)con, (RsScript)script);
     rsContextBindRootScript((RsContext)con, (RsScript)script);
 }
 
 static void
-nContextBindProgramStore(JNIEnv *_env, jobject _this, jlong con, jint pfs)
+nContextBindProgramStore(JNIEnv *_env, jobject _this, jlong con, jlong pfs)
 {
     LOG_API("nContextBindProgramStore, con(%p), pfs(%p)", (RsContext)con, (RsProgramStore)pfs);
     rsContextBindProgramStore((RsContext)con, (RsProgramStore)pfs);
 }
 
 static void
-nContextBindProgramFragment(JNIEnv *_env, jobject _this, jlong con, jint pf)
+nContextBindProgramFragment(JNIEnv *_env, jobject _this, jlong con, jlong pf)
 {
     LOG_API("nContextBindProgramFragment, con(%p), pf(%p)", (RsContext)con, (RsProgramFragment)pf);
     rsContextBindProgramFragment((RsContext)con, (RsProgramFragment)pf);
 }
 
 static void
-nContextBindProgramVertex(JNIEnv *_env, jobject _this, jlong con, jint pf)
+nContextBindProgramVertex(JNIEnv *_env, jobject _this, jlong con, jlong pf)
 {
     LOG_API("nContextBindProgramVertex, con(%p), pf(%p)", (RsContext)con, (RsProgramVertex)pf);
     rsContextBindProgramVertex((RsContext)con, (RsProgramVertex)pf);
 }
 
 static void
-nContextBindProgramRaster(JNIEnv *_env, jobject _this, jlong con, jint pf)
+nContextBindProgramRaster(JNIEnv *_env, jobject _this, jlong con, jlong pf)
 {
     LOG_API("nContextBindProgramRaster, con(%p), pf(%p)", (RsContext)con, (RsProgramRaster)pf);
     rsContextBindProgramRaster((RsContext)con, (RsProgramRaster)pf);
@@ -1676,11 +1676,11 @@
 {"rsnProgramRasterCreate",           "(JZI)J",                                (void*)nProgramRasterCreate },
 {"rsnProgramVertexCreate",           "(JLjava/lang/String;[Ljava/lang/String;[J)J",              (void*)nProgramVertexCreate },
 
-{"rsnContextBindRootScript",         "(JI)V",                                 (void*)nContextBindRootScript },
-{"rsnContextBindProgramStore",       "(JI)V",                                 (void*)nContextBindProgramStore },
-{"rsnContextBindProgramFragment",    "(JI)V",                                 (void*)nContextBindProgramFragment },
-{"rsnContextBindProgramVertex",      "(JI)V",                                 (void*)nContextBindProgramVertex },
-{"rsnContextBindProgramRaster",      "(JI)V",                                 (void*)nContextBindProgramRaster },
+{"rsnContextBindRootScript",         "(JJ)V",                                 (void*)nContextBindRootScript },
+{"rsnContextBindProgramStore",       "(JJ)V",                                 (void*)nContextBindProgramStore },
+{"rsnContextBindProgramFragment",    "(JJ)V",                                 (void*)nContextBindProgramFragment },
+{"rsnContextBindProgramVertex",      "(JJ)V",                                 (void*)nContextBindProgramVertex },
+{"rsnContextBindProgramRaster",      "(JJ)V",                                 (void*)nContextBindProgramRaster },
 
 {"rsnSamplerCreate",                 "(JIIIIIF)J",                            (void*)nSamplerCreate },
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 959d4a9..0edce11 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -44,7 +44,6 @@
 import android.hardware.display.DisplayManager;
 import android.hardware.input.InputManager;
 import android.net.Uri;
-import android.opengl.Matrix;
 import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
@@ -52,7 +51,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
-import android.os.Parcel;
 import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
@@ -63,24 +61,27 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.TextUtils.SimpleStringSplitter;
+import android.util.LongArray;
 import android.util.Pools.Pool;
 import android.util.Pools.SimplePool;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.IWindow;
-import android.view.IWindowManager;
 import android.view.InputDevice;
 import android.view.InputEventConsistencyVerifier;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MagnificationSpec;
+import android.view.WindowInfo;
 import android.view.WindowManager;
+import android.view.WindowManagerInternal;
 import android.view.WindowManagerPolicy;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
 import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.accessibility.IAccessibilityManager;
@@ -89,6 +90,7 @@
 import com.android.internal.R;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.statusbar.IStatusBarService;
+import com.android.server.LocalServices;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -166,7 +168,7 @@
 
     private final PackageManager mPackageManager;
 
-    private final IWindowManager mWindowManagerService;
+    private final WindowManagerInternal mWindowManagerService;
 
     private final SecurityPolicy mSecurityPolicy;
 
@@ -197,9 +199,13 @@
 
     private int mCurrentUserId = UserHandle.USER_OWNER;
 
+    private final LongArray mTempLongArray = new LongArray();
+
     //TODO: Remove this hack
     private boolean mInitialized;
 
+    private WindowsForAccessibilityCallback mWindowsForAccessibilityCallback;
+
     private UserState getCurrentUserStateLocked() {
         return getUserStateLocked(mCurrentUserId);
     }
@@ -221,7 +227,7 @@
     public AccessibilityManagerService(Context context) {
         mContext = context;
         mPackageManager = mContext.getPackageManager();
-        mWindowManagerService = (IWindowManager) ServiceManager.getService(Context.WINDOW_SERVICE);
+        mWindowManagerService = LocalServices.getService(WindowManagerInternal.class);
         mSecurityPolicy = new SecurityPolicy();
         mMainHandler = new MainHandler(mContext.getMainLooper());
         //TODO: (multi-display) We need to support multiple displays.
@@ -390,7 +396,7 @@
             if (resolvedUserId != mCurrentUserId) {
                 return true; // yes, recycle the event
             }
-            if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
+            if (mSecurityPolicy.canDispatchAccessibilityEventLocked(event)) {
                 mSecurityPolicy.updateEventSourceLocked(event);
                 mMainHandler.obtainMessage(MainHandler.MSG_UPDATE_ACTIVE_WINDOW,
                         event.getWindowId(), event.getEventType()).sendToTarget();
@@ -632,11 +638,7 @@
         mSecurityPolicy.enforceCallingPermission(
                 Manifest.permission.TEMPORARY_ENABLE_ACCESSIBILITY,
                 TEMPORARY_ENABLE_ACCESSIBILITY_UNTIL_KEYGUARD_REMOVED);
-        try {
-            if (!mWindowManagerService.isKeyguardLocked()) {
-                return;
-            }
-        } catch (RemoteException re) {
+        if (!mWindowManagerService.isKeyguardLocked()) {
             return;
         }
         synchronized (mLock) {
@@ -739,6 +741,8 @@
      * @param outBounds The output to which to write the bounds.
      */
     boolean getActiveWindowBounds(Rect outBounds) {
+        // TODO: This should be refactored to work with accessibility
+        // focus in multiple windows.
         IBinder token;
         synchronized (mLock) {
             final int windowId = mSecurityPolicy.mActiveWindowId;
@@ -747,13 +751,9 @@
                 token = getCurrentUserStateLocked().mWindowTokens.get(windowId);
             }
         }
-        try {
-            mWindowManagerService.getWindowFrame(token, outBounds);
-            if (!outBounds.isEmpty()) {
-                return true;
-            }
-        } catch (RemoteException re) {
-            /* ignore */
+        mWindowManagerService.getWindowFrame(token, outBounds);
+        if (!outBounds.isEmpty()) {
+            return true;
         }
         return false;
     }
@@ -771,7 +771,7 @@
     }
 
     void onMagnificationStateChanged() {
-        notifyClearAccessibilityNodeInfoCacheLocked();
+        notifyClearAccessibilityCacheLocked();
     }
 
     private void switchUser(int userId) {
@@ -879,7 +879,7 @@
         return false;
     }
 
-    private void notifyClearAccessibilityNodeInfoCacheLocked() {
+    private void notifyClearAccessibilityCacheLocked() {
         UserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             Service service = state.mBoundServices.get(i);
@@ -887,6 +887,16 @@
         }
     }
 
+    private void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) {
+        UserState state = getCurrentUserStateLocked();
+        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+            Service service = state.mBoundServices.get(i);
+            if (mSecurityPolicy.canRetrieveWindowsLocked(service)) {
+                service.notifyWindowsChangedLocked(windows);
+            }
+        }
+    }
+
     /**
      * Removes an AccessibilityInteractionConnection.
      *
@@ -994,7 +1004,8 @@
                 Service service = state.mBoundServices.get(i);
 
                 if (service.mIsDefault == isDefault) {
-                    if (canDispathEventLocked(service, event, state.mHandledFeedbackTypes)) {
+                    if (canDispatchEventToServiceLocked(service, event,
+                            state.mHandledFeedbackTypes)) {
                         state.mHandledFeedbackTypes |= service.mFeedbackType;
                         service.notifyAccessibilityEvent(event);
                     }
@@ -1043,7 +1054,7 @@
      * @param handledFeedbackTypes The feedback types for which services have been notified.
      * @return True if the listener should be notified, false otherwise.
      */
-    private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
+    private boolean canDispatchEventToServiceLocked(Service service, AccessibilityEvent event,
             int handledFeedbackTypes) {
 
         if (!service.canReceiveEventsLocked()) {
@@ -1232,11 +1243,7 @@
             }
         }
         if (setInputFilter) {
-            try {
-                mWindowManagerService.setInputFilter(inputFilter);
-            } catch (RemoteException re) {
-                /* ignore */
-            }
+            mWindowManagerService.setInputFilter(inputFilter);
         }
     }
 
@@ -1296,6 +1303,7 @@
         mInitialized = true;
         updateLegacyCapabilities(userState);
         updateServicesLocked(userState);
+        updateWindowsForAccessibilityCallback(userState);
         updateFilterKeyEventsLocked(userState);
         updateTouchExplorationLocked(userState);
         updateEnhancedWebAccessibilityLocked(userState);
@@ -1304,6 +1312,43 @@
         scheduleUpdateClientsIfNeededLocked(userState);
     }
 
+    private void updateWindowsForAccessibilityCallback(UserState userState) {
+        if (userState.mIsAccessibilityEnabled) {
+            // We observe windows for accessibility only if there is at least
+            // one bound service that can retrieve window content that specified
+            // it is interested in accessing such windows. For services that are
+            // binding we do an update pass after each bind event, so we run this
+            // code and register the callback if needed.
+            boolean boundServiceCanRetrieveInteractiveWindows = false;
+
+            List<Service> boundServices = userState.mBoundServices;
+            final int boundServiceCount = boundServices.size();
+            for (int i = 0; i < boundServiceCount; i++) {
+                Service boundService = boundServices.get(i);
+                if (mSecurityPolicy.canRetrieveWindowContentLocked(boundService)
+                        && boundService.mRetrieveInteractiveWindows) {
+                    boundServiceCanRetrieveInteractiveWindows = true;
+                    break;
+                }
+            }
+
+            if (boundServiceCanRetrieveInteractiveWindows) {
+                if (mWindowsForAccessibilityCallback == null) {
+                    mWindowsForAccessibilityCallback = new WindowsForAccessibilityCallback();
+                    mWindowManagerService.setWindowsForAccessibilityCallback(
+                            mWindowsForAccessibilityCallback);
+                }
+                return;
+            }
+        }
+
+        if (mWindowsForAccessibilityCallback != null) {
+            mWindowsForAccessibilityCallback = null;
+            mWindowManagerService.setWindowsForAccessibilityCallback(
+                    mWindowsForAccessibilityCallback);
+        }
+    }
+
     private void updateLegacyCapabilities(UserState userState) {
         // Up to JB-MR1 we had a white list with services that can enable touch
         // exploration. When a service is first started we show a dialog to the
@@ -1435,11 +1480,6 @@
                     Settings.Secure.TOUCH_EXPLORATION_ENABLED, enabled ? 1 : 0,
                     userState.mUserId);
         }
-        try {
-            mWindowManagerService.setTouchExplorationEnabled(enabled);
-        } catch (RemoteException e) {
-            e.printStackTrace();
-        }
     }
 
     private boolean canRequestAndRequestsTouchExplorationLocked(Service service) {
@@ -1605,6 +1645,7 @@
                     }
                     event.recycle();
                 } break;
+
                 case MSG_SEND_KEY_EVENT_TO_INPUT_FILTER: {
                     KeyEvent event = (KeyEvent) msg.obj;
                     final int policyFlags = msg.arg1;
@@ -1615,28 +1656,34 @@
                     }
                     event.recycle();
                 } break;
+
                 case MSG_SEND_STATE_TO_CLIENTS: {
                     final int clientState = msg.arg1;
                     final int userId = msg.arg2;
                     sendStateToClients(clientState, mGlobalClients);
                     sendStateToClientsForUser(clientState, userId);
                 } break;
+
                 case MSG_SEND_CLEARED_STATE_TO_CLIENTS_FOR_USER: {
                     final int userId = msg.arg1;
                     sendStateToClientsForUser(0, userId);
                 } break;
+
                 case MSG_UPDATE_ACTIVE_WINDOW: {
                     final int windowId = msg.arg1;
                     final int eventType = msg.arg2;
                     mSecurityPolicy.updateActiveWindow(windowId, eventType);
                 } break;
+
                 case MSG_ANNOUNCE_NEW_USER_IF_NEEDED: {
                     announceNewUserIfNeeded();
                 } break;
+
                 case MSG_UPDATE_INPUT_FILTER: {
                     UserState userState = (UserState) msg.obj;
                     updateInputFilter(userState);
                 } break;
+
                 case MSG_SHOW_ENABLED_TOUCH_EXPLORATION_DIALOG: {
                     Service service = (Service) msg.obj;
                     showEnableTouchExplorationDialog(service);
@@ -1655,7 +1702,6 @@
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_ANNOUNCEMENT);
                     event.getText().add(message);
-                    event.setWindowId(mSecurityPolicy.getRetrievalAllowingWindowLocked());
                     sendAccessibilityEvent(event, mCurrentUserId);
                 }
             }
@@ -1703,6 +1749,19 @@
         mPendingEventPool.release(pendingEvent);
     }
 
+    private int findWindowIdLocked(IBinder token) {
+        final int globalIndex = mGlobalWindowTokens.indexOfValue(token);
+        if (globalIndex >= 0) {
+            return mGlobalWindowTokens.keyAt(globalIndex);
+        }
+        UserState userState = getCurrentUserStateLocked();
+        final int userIndex = userState.mWindowTokens.indexOfValue(token);
+        if (userIndex >= 0) {
+            return userState.mWindowTokens.keyAt(userIndex);
+        }
+        return -1;
+    }
+
     /**
      * This class represents an accessibility service. It stores all per service
      * data required for the service management, provides API for starting/stopping the
@@ -1738,6 +1797,8 @@
 
         boolean mRequestFilterKeyEvents;
 
+        boolean mRetrieveInteractiveWindows;
+
         int mFetchFlags;
 
         long mNotificationTimeout;
@@ -1748,7 +1809,9 @@
 
         boolean mIsAutomation;
 
-        final Rect mTempBounds = new Rect();
+        final Rect mTempBounds1 = new Rect();
+
+        final Rect mTempBounds2 = new Rect();
 
         final ResolveInfo mResolveInfo;
 
@@ -1758,6 +1821,9 @@
 
         final KeyEventDispatcher mKeyEventDispatcher = new KeyEventDispatcher();
 
+        final SparseArray<AccessibilityWindowInfo> mIntrospectedWindows =
+                new SparseArray<AccessibilityWindowInfo>();
+
         boolean mWasConnectedAndDied;
 
         // Handler only for dispatching accessibility events since we use event
@@ -1822,7 +1888,13 @@
             mRequestEnhancedWebAccessibility = (info.flags
                     & AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0;
             mRequestFilterKeyEvents = (info.flags
-                    & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS)  != 0;
+                    & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0;
+            mRetrieveInteractiveWindows = (info.flags
+                    & AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS) != 0;
+
+            if (!mRetrieveInteractiveWindows) {
+                clearIntrospectedWindows();
+            }
         }
 
         /**
@@ -1932,6 +2004,59 @@
         }
 
         @Override
+        public List<AccessibilityWindowInfo> getWindows() {
+            synchronized (mLock) {
+                final int resolvedUserId = mSecurityPolicy
+                        .resolveCallingUserIdEnforcingPermissionsLocked(
+                                UserHandle.getCallingUserId());
+                if (resolvedUserId != mCurrentUserId) {
+                    return null;
+                }
+                final boolean permissionGranted =
+                        mSecurityPolicy.canRetrieveWindowsLocked(this);
+                if (!permissionGranted) {
+                    return null;
+                }
+                List<AccessibilityWindowInfo> windows = new ArrayList<AccessibilityWindowInfo>();
+                final int windowCount = mSecurityPolicy.mWindows.size();
+                for (int i = 0; i < windowCount; i++) {
+                    AccessibilityWindowInfo window = mSecurityPolicy.mWindows.get(i);
+                    AccessibilityWindowInfo windowClone =
+                            AccessibilityWindowInfo.obtain(window);
+                    windowClone.setConnectionId(mId);
+                    mIntrospectedWindows.put(window.getId(), windowClone);
+                    windows.add(windowClone);
+                }
+                return windows;
+            }
+        }
+
+        @Override
+        public AccessibilityWindowInfo getWindow(int windowId) {
+            synchronized (mLock) {
+                final int resolvedUserId = mSecurityPolicy
+                        .resolveCallingUserIdEnforcingPermissionsLocked(
+                                UserHandle.getCallingUserId());
+                if (resolvedUserId != mCurrentUserId) {
+                    return null;
+                }
+                final boolean permissionGranted =
+                        mSecurityPolicy.canRetrieveWindowsLocked(this);
+                if (!permissionGranted) {
+                    return null;
+                }
+                AccessibilityWindowInfo window = mSecurityPolicy.findWindowById(windowId);
+                if (window != null) {
+                    AccessibilityWindowInfo windowClone = AccessibilityWindowInfo.obtain(window);
+                    windowClone.setConnectionId(mId);
+                    mIntrospectedWindows.put(windowId, windowClone);
+                    return windowClone;
+                }
+                return null;
+            }
+        }
+
+        @Override
         public boolean findAccessibilityNodeInfosByViewId(int accessibilityWindowId,
                 long accessibilityNodeId, String viewIdResName, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
@@ -1945,12 +2070,12 @@
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
-                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
-                final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+                resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
+                final boolean permissionGranted =
+                        mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
                 if (!permissionGranted) {
                     return false;
                 } else {
-                    resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
                     connection = getConnectionLocked(resolvedWindowId);
                     if (connection == null) {
                         return false;
@@ -1989,7 +2114,6 @@
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
-                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
                 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
                 final boolean permissionGranted =
                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2034,7 +2158,6 @@
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
-                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
                 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
                 final boolean permissionGranted =
                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2079,7 +2202,6 @@
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
-                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
                 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
                 final boolean permissionGranted =
                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2123,7 +2245,6 @@
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
-                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
                 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
                 final boolean permissionGranted =
                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, resolvedWindowId);
@@ -2167,7 +2288,6 @@
                 if (resolvedUserId != mCurrentUserId) {
                     return false;
                 }
-                mSecurityPolicy.enforceCanRetrieveWindowContent(this);
                 resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
                 final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
                         resolvedWindowId, action, arguments);
@@ -2365,7 +2485,7 @@
                 }
 
                 mPendingEvents.remove(eventType);
-                if (mSecurityPolicy.canRetrieveWindowContent(this)) {
+                if (mSecurityPolicy.canRetrieveWindowContentLocked(this)) {
                     event.setConnectionId(mId);
                 } else {
                     event.setSource(null);
@@ -2396,12 +2516,69 @@
         }
 
         public void notifyClearAccessibilityNodeInfoCache() {
+            clearIntrospectedWindows();
             mInvocationHandler.sendEmptyMessage(
-                    InvocationHandler.MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE);
+                    InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
+        }
+
+        private void clearIntrospectedWindows() {
+            final int windowCount = mIntrospectedWindows.size();
+            for (int i = windowCount - 1; i >= 0; i--) {
+                mIntrospectedWindows.valueAt(i).recycle();
+                mIntrospectedWindows.removeAt(i);
+            }
+        }
+
+        public void notifyWindowsChangedLocked(List<AccessibilityWindowInfo> windows) {
+            LongArray changedWindows = mTempLongArray;
+            changedWindows.clear();
+
+            // Figure out which windows the service cares about changed.
+            final int windowCount = windows.size();
+            for (int i = 0; i < windowCount; i++) {
+                AccessibilityWindowInfo newState = windows.get(i);
+                final int windowId = newState.getId();
+                AccessibilityWindowInfo oldState = mIntrospectedWindows.get(windowId);
+                if (oldState != null && oldState.changed(newState)) {
+                    oldState.recycle();
+                    mIntrospectedWindows.put(newState.getId(),
+                            AccessibilityWindowInfo.obtain(newState));
+                    changedWindows.add(windowId);
+                }
+            }
+
+            // Figure out which windows the service cares about went away.
+            final int introspectedWindowCount = mIntrospectedWindows.size();
+            for (int i = introspectedWindowCount - 1; i >= 0; i--) {
+                AccessibilityWindowInfo window = mIntrospectedWindows.valueAt(i);
+                if (changedWindows.indexOf(window.getId()) < 0 && !windows.contains(window)) {
+                    changedWindows.add(window.getId());
+                    mIntrospectedWindows.removeAt(i);
+                    window.recycle();
+                }
+            }
+
+            int[] windowIds = null;
+
+            final int changedWindowCount = changedWindows.size();
+            if (changedWindowCount > 0) {
+                windowIds = new int[changedWindowCount];
+                for (int i = 0; i < changedWindowCount; i++) {
+                    // Cast is fine as we use long array to cache ints.
+                    windowIds[i] = (int) changedWindows.get(i);
+                }
+                changedWindows.clear();
+            }
+
+            mInvocationHandler.obtainMessage(InvocationHandler.MSG_ON_WINDOWS_CHANGED,
+                    windowIds).sendToTarget();
         }
 
         private void notifyGestureInternal(int gestureId) {
-            IAccessibilityServiceClient listener = mServiceInterface;
+            final IAccessibilityServiceClient listener;
+            synchronized (mLock) {
+                listener = mServiceInterface;
+            }
             if (listener != null) {
                 try {
                     listener.onGesture(gestureId);
@@ -2416,11 +2593,14 @@
             mKeyEventDispatcher.notifyKeyEvent(event, policyFlags);
         }
 
-        private void notifyClearAccessibilityNodeInfoCacheInternal() {
-            IAccessibilityServiceClient listener = mServiceInterface;
+        private void notifyClearAccessibilityCacheInternal() {
+            final IAccessibilityServiceClient listener;
+            synchronized (mLock) {
+                listener = mServiceInterface;
+            }
             if (listener != null) {
                 try {
-                    listener.clearAccessibilityNodeInfoCache();
+                    listener.clearAccessibilityCache();
                 } catch (RemoteException re) {
                     Slog.e(LOG_TAG, "Error during requesting accessibility info cache"
                             + " to be cleared.", re);
@@ -2428,6 +2608,20 @@
             }
         }
 
+        private void notifyWindowsChangedInternal(int[] windowIds) {
+            final IAccessibilityServiceClient listener;
+            synchronized (mLock) {
+                listener = mServiceInterface;
+            }
+            if (listener != null) {
+                try {
+                    listener.onWindowsChanged(windowIds);
+                } catch (RemoteException re) {
+                    Slog.e(LOG_TAG, "Error during sending windows to: " + mService, re);
+                }
+            }
+        }
+
         private void sendDownAndUpKeyEvents(int keyCode) {
             final long token = Binder.clearCallingIdentity();
 
@@ -2511,27 +2705,23 @@
         }
 
         private MagnificationSpec getCompatibleMagnificationSpec(int windowId) {
-            try {
-                IBinder windowToken = mGlobalWindowTokens.get(windowId);
-                if (windowToken == null) {
-                    windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
-                }                    
-                if (windowToken != null) {
-                    return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
-                            windowToken);
-                }
-            } catch (RemoteException re) {
-                /* ignore */
+            IBinder windowToken = mGlobalWindowTokens.get(windowId);
+            if (windowToken == null) {
+                windowToken = getCurrentUserStateLocked().mWindowTokens.get(windowId);
+            }
+            if (windowToken != null) {
+                return mWindowManagerService.getCompatibleMagnificationSpecForWindow(
+                        windowToken);
             }
             return null;
         }
 
         private final class InvocationHandler extends Handler {
-
             public static final int MSG_ON_GESTURE = 1;
             public static final int MSG_ON_KEY_EVENT = 2;
-            public static final int MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE = 3;
+            public static final int MSG_CLEAR_ACCESSIBILITY_CACHE = 3;
             public static final int MSG_ON_KEY_EVENT_TIMEOUT = 4;
+            public static final int MSG_ON_WINDOWS_CHANGED = 5;
 
             public InvocationHandler(Looper looper) {
                 super(looper, null, true);
@@ -2545,18 +2735,27 @@
                         final int gestureId = message.arg1;
                         notifyGestureInternal(gestureId);
                     } break;
+
                     case MSG_ON_KEY_EVENT: {
                         KeyEvent event = (KeyEvent) message.obj;
                         final int policyFlags = message.arg1;
                         notifyKeyEventInternal(event, policyFlags);
                     } break;
-                    case MSG_CLEAR_ACCESSIBILITY_NODE_INFO_CACHE: {
-                        notifyClearAccessibilityNodeInfoCacheInternal();
+
+                    case MSG_CLEAR_ACCESSIBILITY_CACHE: {
+                        notifyClearAccessibilityCacheInternal();
                     } break;
+
                     case MSG_ON_KEY_EVENT_TIMEOUT: {
                         PendingEvent eventState = (PendingEvent) message.obj;
                         setOnKeyEventResult(false, eventState.sequence);
                     } break;
+
+                    case MSG_ON_WINDOWS_CHANGED: {
+                        final int[] windowIds = (int[]) message.obj;
+                        notifyWindowsChangedInternal(windowIds);
+                    } break;
+
                     default: {
                         throw new IllegalArgumentException("Unknown message: " + type);
                     }
@@ -2700,6 +2899,113 @@
         }
     }
 
+    final class WindowsForAccessibilityCallback implements
+            WindowManagerInternal.WindowsForAccessibilityCallback {
+
+        @Override
+        public void onWindowsForAccessibilityChanged(List<WindowInfo> windows) {
+            synchronized (mLock) {
+                List<WindowInfo> receivedWindows = (List<WindowInfo>) windows;
+
+                // Populate the windows to report.
+                List<AccessibilityWindowInfo> reportedWindows =
+                        new ArrayList<AccessibilityWindowInfo>();
+                final int receivedWindowCount = receivedWindows.size();
+                for (int i = 0; i < receivedWindowCount; i++) {
+                    WindowInfo receivedWindow = receivedWindows.get(i);
+                    AccessibilityWindowInfo reportedWindow = populateReportedWindow(
+                            receivedWindow);
+                    if (reportedWindow != null) {
+                        reportedWindows.add(reportedWindow);
+                    }
+                }
+
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, "Windows changed: " + reportedWindows);
+                }
+
+                // Let the policy update the focused and active windows.
+                mSecurityPolicy.updateWindowsLocked(reportedWindows);
+            }
+        }
+
+        private AccessibilityWindowInfo populateReportedWindow(WindowInfo window) {
+            final int windowId = findWindowIdLocked(window.token);
+            if (windowId < 0) {
+                return null;
+            }
+
+            AccessibilityWindowInfo reportedWindow = AccessibilityWindowInfo.obtain();
+
+            reportedWindow.setId(windowId);
+            reportedWindow.setType(getTypeForWindowManagerWindowType(window.type));
+            reportedWindow.setLayer(window.layer);
+            reportedWindow.setFocused(window.focused);
+            reportedWindow.setBoundsInScreen(window.boundsInScreen);
+
+            final int parentId = findWindowIdLocked(window.parentToken);
+            if (parentId >= 0) {
+                reportedWindow.setParentId(parentId);
+            }
+
+            if (window.childTokens != null) {
+                final int childCount = window.childTokens.size();
+                for (int i = 0; i < childCount; i++) {
+                    IBinder childToken = window.childTokens.get(i);
+                    final int childId = findWindowIdLocked(childToken);
+                    if (childId >= 0) {
+                        reportedWindow.addChild(childId);
+                    }
+                }
+            }
+
+            return reportedWindow;
+        }
+
+        private int getTypeForWindowManagerWindowType(int windowType) {
+            switch (windowType) {
+                case WindowManager.LayoutParams.TYPE_APPLICATION:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_STARTING:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+                case WindowManager.LayoutParams.TYPE_BASE_APPLICATION:
+                case WindowManager.LayoutParams.TYPE_PHONE:
+                case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+                case WindowManager.LayoutParams.TYPE_TOAST:
+                case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: {
+                    return AccessibilityWindowInfo.TYPE_APPLICATION;
+                }
+
+                case WindowManager.LayoutParams.TYPE_INPUT_METHOD:
+                case WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG: {
+                    return AccessibilityWindowInfo.TYPE_INPUT_METHOD;
+                }
+
+                case WindowManager.LayoutParams.TYPE_KEYGUARD:
+                case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+                case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR:
+                case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
+                case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+                case WindowManager.LayoutParams.TYPE_STATUS_BAR:
+                case WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL:
+                case WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL:
+                case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY:
+                case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+                case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+                case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+                case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+                case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: {
+                    return AccessibilityWindowInfo.TYPE_SYSTEM;
+                }
+
+                default: {
+                    return -1;
+                }
+            }
+        }
+    }
+
     final class SecurityPolicy {
         private static final int VALID_ACTIONS =
             AccessibilityNodeInfo.ACTION_CLICK
@@ -2738,17 +3044,37 @@
             | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
             | AccessibilityEvent.TYPE_VIEW_SCROLLED
             | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED
-            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
+            | AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED
+            | AccessibilityEvent.TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY;
 
-        private int mActiveWindowId;
+        public final List<AccessibilityWindowInfo> mWindows =
+                new ArrayList<AccessibilityWindowInfo>();
+
+        public int mActiveWindowId;
+        public int mFocusedWindowId;
+        public AccessibilityEvent mShowingFocusedWindowEvent;
+
         private boolean mTouchInteractionInProgress;
 
-        private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
+        private boolean canDispatchAccessibilityEventLocked(AccessibilityEvent event) {
             final int eventType = event.getEventType();
             switch (eventType) {
                 // All events that are for changes in a global window
                 // state should *always* be dispatched.
                 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED:
+                    if (mWindowsForAccessibilityCallback != null) {
+                        // OK, this is fun. Sometimes the focused window is notified
+                        // it has focus before being shown. Historically this event
+                        // means that the window is focused and can be introspected.
+                        // But we still have not gotten the window state from the
+                        // window manager, so delay the notification until then.
+                        AccessibilityWindowInfo window = findWindowById(event.getWindowId());
+                        if (window == null || !window.isFocused()) {
+                            mShowingFocusedWindowEvent = AccessibilityEvent.obtain(event);
+                            return false;
+                        }
+                    }
+                // $fall-through$
                 case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED:
                 // All events generated by the user touching the
                 // screen should *always* be dispatched.
@@ -2758,15 +3084,81 @@
                 case AccessibilityEvent.TYPE_GESTURE_DETECTION_END:
                 case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START:
                 case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
-                // These will change the active window, so dispatch.
                 case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER:
                 case AccessibilityEvent.TYPE_VIEW_HOVER_EXIT: {
                     return true;
                 }
                 // All events for changes in window content should be
-                // dispatched *only* if this window is the active one.
-                default:
-                    return event.getWindowId() == mActiveWindowId;
+                // dispatched *only* if this window is one of the windows
+                // the accessibility layer reports which are windows
+                // that a sighted user can touch.
+                default: {
+                    return isRetrievalAllowingWindow(event.getWindowId());
+                }
+            }
+        }
+
+        public void updateWindowsLocked(List<AccessibilityWindowInfo> windows) {
+            final int oldWindowCount = mWindows.size();
+            for (int i = oldWindowCount - 1; i >= 0; i--) {
+                mWindows.remove(i).recycle();
+            }
+
+            mFocusedWindowId = -1;
+            if (!mTouchInteractionInProgress) {
+                mActiveWindowId = -1;
+            }
+
+            // If the active window goes away while the user is touch exploring we
+            // reset the active window id and wait for the next hover event from
+            // under the user's finger to determine which one is the new one. It
+            // is possible that the finger is not moving and the input system
+            // filters out such events.
+            boolean activeWindowGone = true;
+
+            final int windowCount = windows.size();
+            if (windowCount > 0) {
+                for (int i = 0; i < windowCount; i++) {
+                    AccessibilityWindowInfo window = windows.get(i);
+                    final int windowId = window.getId();
+                    if (window.isFocused()) {
+                        mFocusedWindowId = windowId;
+                        if (!mTouchInteractionInProgress) {
+                            mActiveWindowId = windowId;
+                            window.setActive(true);
+                        } else if (windowId == mActiveWindowId) {
+                            activeWindowGone = false;
+                        }
+                    }
+                    mWindows.add(window);
+                }
+
+                if (mTouchInteractionInProgress && activeWindowGone) {
+                    mActiveWindowId = -1;
+                }
+
+                // Focused window may change the active one, so set the
+                // active window once we decided which it is.
+                for (int i = 0; i < windowCount; i++) {
+                    AccessibilityWindowInfo window = mWindows.get(i);
+                    if (window.getId() == mActiveWindowId) {
+                       window.setActive(true);
+                    }
+                }
+            }
+
+            notifyWindowsChangedLocked(mWindows);
+
+            // If we are delaying a window state change event as the window
+            // source was showing when it was fired, now is the time to send.
+            if (mShowingFocusedWindowEvent != null) {
+                final int windowId = mShowingFocusedWindowEvent.getWindowId();
+                AccessibilityWindowInfo window = findWindowById(windowId);
+                if (window != null && window.isFocused()) {
+                    // Sending does the recycle.
+                    sendAccessibilityEvent(mShowingFocusedWindowEvent, mCurrentUserId);
+                }
+                mShowingFocusedWindowEvent = null;
             }
         }
 
@@ -2781,67 +3173,96 @@
             // the window that the user is currently touching. If the user is
             // touching a window that does not have input focus as soon as the
             // the user stops touching that window the focused window becomes
-            // the active one.
+            // the active one. Here we detect the touched window and make it
+            // active. In updateWindowsLocked() we update the focused window
+            // and if the user is not touching the screen, we make the focused
+            // window the active one.
             switch (eventType) {
                 case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: {
-                    if (getFocusedWindowId() == windowId) {
-                        mActiveWindowId = windowId;
+                    // If no service has the capability to introspect screen,
+                    // we do not register callback in the window manager for
+                    // window changes, so we have to ask the window manager
+                    // what the focused window is to update the active one.
+                    // The active window also determined events from which
+                    // windows are delivered.
+                    boolean focusedWindowActive = false;
+                    synchronized (mLock) {
+                        if (mWindowsForAccessibilityCallback == null) {
+                            focusedWindowActive = true;
+                        }
+                    }
+                    if (focusedWindowActive) {
+                        if (windowId == getFocusedWindowId()) {
+                            synchronized (mLock) {
+                                mActiveWindowId = windowId;
+                            }
+                        }
                     }
                 } break;
+
                 case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: {
                     // Do not allow delayed hover events to confuse us
                     // which the active window is.
-                    if (mTouchInteractionInProgress) {
-                        mActiveWindowId = windowId;
+                    synchronized (mLock) {
+                        if (mTouchInteractionInProgress && mActiveWindowId != windowId) {
+                            setActiveWindowLocked(windowId);
+                        }
                     }
                 } break;
             }
         }
 
         public void onTouchInteractionStart() {
-            mTouchInteractionInProgress = true;
+            synchronized (mLock) {
+                mTouchInteractionInProgress = true;
+            }
         }
 
         public void onTouchInteractionEnd() {
-            mTouchInteractionInProgress = false;
-            // We want to set the active window to be current immediately
-            // after the user has stopped touching the screen since if the
-            // user types with the IME he should get a feedback for the
-            // letter typed in the text view which is in the input focused
-            // window. Note that we always deliver hover accessibility events
-            // (they are a result of user touching the screen) so change of
-            // the active window before all hover accessibility events from
-            // the touched window are delivered is fine.
-            mActiveWindowId = getFocusedWindowId();
+            synchronized (mLock) {
+                mTouchInteractionInProgress = false;
+                // We want to set the active window to be current immediately
+                // after the user has stopped touching the screen since if the
+                // user types with the IME he should get a feedback for the
+                // letter typed in the text view which is in the input focused
+                // window. Note that we always deliver hover accessibility events
+                // (they are a result of user touching the screen) so change of
+                // the active window before all hover accessibility events from
+                // the touched window are delivered is fine.
+                setActiveWindowLocked(mFocusedWindowId);
+            }
         }
 
-        public int getRetrievalAllowingWindowLocked() {
-            return mActiveWindowId;
+        private void setActiveWindowLocked(int windowId) {
+            if (mActiveWindowId != windowId) {
+                mActiveWindowId = windowId;
+                final int windowCount = mWindows.size();
+                for (int i = 0; i < windowCount; i++) {
+                    AccessibilityWindowInfo window = mWindows.get(i);
+                    window.setActive(window.getId() == windowId);
+                }
+                notifyWindowsChangedLocked(mWindows);
+            }
         }
 
         public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
-            return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
+            return canRetrieveWindowContentLocked(service) && isRetrievalAllowingWindow(windowId);
         }
 
         public boolean canPerformActionLocked(Service service, int windowId, int action,
                 Bundle arguments) {
-            return canRetrieveWindowContent(service)
+            return canRetrieveWindowContentLocked(service)
                 && isRetrievalAllowingWindow(windowId)
                 && isActionPermitted(action);
         }
 
-        public boolean canRetrieveWindowContent(Service service) {
-            return (service.mAccessibilityServiceInfo.getCapabilities()
-                    & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
+        public boolean canRetrieveWindowsLocked(Service service) {
+            return canRetrieveWindowContentLocked(service) && service.mRetrieveInteractiveWindows;
         }
 
-        public void enforceCanRetrieveWindowContent(Service service) throws RemoteException {
-            // This happens due to incorrect registration so make it apparent.
-            if (!canRetrieveWindowContent(service)) {
-                Slog.e(LOG_TAG, "Accessibility serivce " + service.mComponentName + " does not " +
-                        "declare android:canRetrieveWindowContent.");
-                throw new RemoteException();
-            }
+        public boolean canRetrieveWindowContentLocked(Service service) {
+            return (service.mAccessibilityServiceInfo.getCapabilities()
+                    & AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0;
         }
 
         public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) {
@@ -2878,7 +3299,21 @@
         }
 
         private boolean isRetrievalAllowingWindow(int windowId) {
-            return (mActiveWindowId == windowId);
+            if (windowId == mActiveWindowId) {
+                return true;
+            }
+            return findWindowById(windowId) != null;
+        }
+
+        private AccessibilityWindowInfo findWindowById(int windowId) {
+            final int windowCount = mWindows.size();
+            for (int i = 0; i < windowCount; i++) {
+                AccessibilityWindowInfo window = mWindows.get(i);
+                if (window.getId() == windowId) {
+                    return window;
+                }
+            }
+            return null;
         }
 
         private boolean isActionPermitted(int action) {
@@ -2901,35 +3336,8 @@
         }
 
         private int getFocusedWindowId() {
-            try {
-                // We call this only on window focus change or after touch
-                // exploration gesture end and the shown windows are not that
-                // many, so the linear look up is just fine.
-                IBinder token = mWindowManagerService.getFocusedWindowToken();
-                if (token != null) {
-                    synchronized (mLock) {
-                        int windowId = getFocusedWindowIdLocked(token, mGlobalWindowTokens);
-                        if (windowId < 0) {
-                            windowId = getFocusedWindowIdLocked(token,
-                                    getCurrentUserStateLocked().mWindowTokens);
-                        }
-                        return windowId;
-                    }
-                }
-            } catch (RemoteException re) {
-                /* ignore */
-            }
-            return -1;
-        }
-
-        private int getFocusedWindowIdLocked(IBinder token, SparseArray<IBinder> windows) {
-            final int windowCount = windows.size();
-            for (int i = 0; i < windowCount; i++) {
-                if (windows.valueAt(i) == token) {
-                    return windows.keyAt(i);
-                }
-            }
-            return -1;
+            IBinder token = mWindowManagerService.getFocusedWindowToken();
+            return findWindowIdLocked(token);
         }
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
index 5f12cf4..c8b080e 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -29,8 +29,6 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
@@ -38,8 +36,6 @@
 import android.util.Slog;
 import android.view.GestureDetector;
 import android.view.GestureDetector.SimpleOnGestureListener;
-import android.view.IMagnificationCallbacks;
-import android.view.IWindowManager;
 import android.view.MagnificationSpec;
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
@@ -48,10 +44,12 @@
 import android.view.ScaleGestureDetector.OnScaleGestureListener;
 import android.view.View;
 import android.view.ViewConfiguration;
+import android.view.WindowManagerInternal;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.animation.DecelerateInterpolator;
 
 import com.android.internal.os.SomeArgs;
+import com.android.server.LocalServices;
 
 import java.util.Locale;
 
@@ -94,8 +92,8 @@
  *
  * 6. The magnification scale will be persisted in settings and in the cloud.
  */
-public final class ScreenMagnifier extends IMagnificationCallbacks.Stub
-        implements EventStreamTransformation {
+public final class ScreenMagnifier implements WindowManagerInternal.MagnificationCallbacks,
+        EventStreamTransformation {
 
     private static final String LOG_TAG = ScreenMagnifier.class.getSimpleName();
 
@@ -127,7 +125,7 @@
     private final Rect mTempRect1 = new Rect();
 
     private final Context mContext;
-    private final IWindowManager mWindowManager;
+    private final WindowManagerInternal mWindowManager;
     private final MagnificationController mMagnificationController;
     private final ScreenStateObserver mScreenStateObserver;
 
@@ -191,8 +189,7 @@
 
     public ScreenMagnifier(Context context, int displayId, AccessibilityManagerService service) {
         mContext = context;
-        mWindowManager = IWindowManager.Stub.asInterface(
-                ServiceManager.getService("window"));
+        mWindowManager = LocalServices.getService(WindowManagerInternal.class);
         mAms = service;
 
         mLongAnimationDuration = context.getResources().getInteger(
@@ -208,11 +205,7 @@
         mMagnificationController = new MagnificationController(mLongAnimationDuration);
         mScreenStateObserver = new ScreenStateObserver(context, mMagnificationController);
 
-        try {
-            mWindowManager.setMagnificationCallbacks(this);
-        } catch (RemoteException re) {
-            /* ignore */
-        }
+        mWindowManager.setMagnificationCallbacks(this);
 
         transitionToState(STATE_DETECTING);
     }
@@ -378,11 +371,7 @@
     @Override
     public void onDestroy() {
         mScreenStateObserver.destroy();
-        try {
-            mWindowManager.setMagnificationCallbacks(null);
-        } catch (RemoteException re) {
-            /* ignore */
-        }
+        mWindowManager.setMagnificationCallbacks(null);
     }
 
     private void handleMotionEventStateDelegating(MotionEvent event,
@@ -462,7 +451,7 @@
         }
         return mTempPointerProperties;
     }
-    
+
     private void transitionToState(int state) {
         if (DEBUG_STATE_TRANSITIONS) {
             switch (state) {
@@ -1120,15 +1109,10 @@
             if (DEBUG_SET_MAGNIFICATION_SPEC) {
                 Slog.i(LOG_TAG, "Sending: " + spec);
             }
-            try {
-                mSentMagnificationSpec.scale = spec.scale;
-                mSentMagnificationSpec.offsetX = spec.offsetX;
-                mSentMagnificationSpec.offsetY = spec.offsetY;
-                mWindowManager.setMagnificationSpec(
-                        MagnificationSpec.obtain(spec));
-            } catch (RemoteException re) {
-                /* ignore */
-            }
+            mSentMagnificationSpec.scale = spec.scale;
+            mSentMagnificationSpec.offsetX = spec.offsetX;
+            mSentMagnificationSpec.offsetY = spec.offsetY;
+            mWindowManager.setMagnificationSpec(MagnificationSpec.obtain(spec));
         }
     }
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
index e208677..77d5076 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetService.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -38,18 +37,19 @@
 import com.android.internal.appwidget.IAppWidgetService;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.AppWidgetBackupBridge;
+import com.android.server.WidgetBackupProvider;
 import com.android.server.SystemService;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.List;
-import java.util.Locale;
 
 
 /**
  * SystemService that publishes an IAppWidgetService.
  */
-public class AppWidgetService extends SystemService {
+public class AppWidgetService extends SystemService implements WidgetBackupProvider {
 
     static final String TAG = "AppWidgetService";
 
@@ -72,6 +72,7 @@
     @Override
     public void onStart() {
         publishBinderService(Context.APPWIDGET_SERVICE, mServiceImpl);
+        AppWidgetBackupBridge.register(this);
     }
 
     @Override
@@ -81,13 +82,40 @@
         }
     }
 
+
+    // backup <-> app widget service bridge surface
+    @Override
+    public List<String> getWidgetParticipants(int userId) {
+        return mServiceImpl.getWidgetParticipants(userId);
+    }
+
+    @Override
+    public byte[] getWidgetState(String packageName, int userId) {
+        return mServiceImpl.getWidgetState(packageName, userId);
+    }
+
+    @Override
+    public void restoreStarting(int userId) {
+        mServiceImpl.restoreStarting(userId);
+    }
+
+    @Override
+    public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+        mServiceImpl.restoreWidgetState(packageName, restoredState, userId);
+    }
+
+    @Override
+    public void restoreFinished(int userId) {
+        mServiceImpl.restoreFinished(userId);
+    }
+
+
+    // implementation entry point and binder service
     private final AppWidgetServiceStub mServiceImpl = new AppWidgetServiceStub();
 
-    private class AppWidgetServiceStub extends IAppWidgetService.Stub {
+    class AppWidgetServiceStub extends IAppWidgetService.Stub {
 
         private boolean mSafeMode;
-        private Locale mLocale;
-        private PackageManager mPackageManager;
 
         public void systemRunning(boolean safeMode) {
             mSafeMode = safeMode;
@@ -227,6 +255,29 @@
             }
         }
 
+
+        // support of the widget/backup bridge
+        public List<String> getWidgetParticipants(int userId) {
+            return getImplForUser(userId).getWidgetParticipants();
+        }
+
+        public byte[] getWidgetState(String packageName, int userId) {
+            return getImplForUser(userId).getWidgetState(packageName);
+        }
+
+        public void restoreStarting(int userId) {
+            getImplForUser(userId).restoreStarting();
+        }
+
+        public void restoreWidgetState(String packageName, byte[] restoredState, int userId) {
+            getImplForUser(userId).restoreWidgetState(packageName, restoredState);
+        }
+
+        public void restoreFinished(int userId) {
+            getImplForUser(userId).restoreFinished();
+        }
+
+
         private void checkPermission(int userId) {
             int realUserId = ActivityManager.handleIncomingUser(
                     Binder.getCallingPid(),
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index b6391b6..b84df79 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -31,6 +31,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
@@ -51,6 +52,7 @@
 import android.util.AttributeSet;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TypedValue;
 import android.util.Xml;
 import android.view.Display;
@@ -66,6 +68,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -79,8 +83,11 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map.Entry;
 import java.util.Set;
 
+import libcore.util.MutableInt;
+
 class AppWidgetServiceImpl {
 
     private static final String KEYGUARD_HOST_PACKAGE = "com.android.keyguard";
@@ -89,8 +96,10 @@
     private static final String SETTINGS_FILENAME = "appwidgets.xml";
     private static final int MIN_UPDATE_PERIOD = 30 * 60 * 1000; // 30 minutes
     private static final int CURRENT_VERSION = 1; // Bump if the stored widgets need to be upgraded.
+    private static final int WIDGET_STATE_VERSION = 1;  // version of backed-up widget state
 
-    private static boolean DBG = false;
+    private static boolean DBG = true;
+    private static boolean DEBUG_BACKUP = DBG || true;
 
     /*
      * When identifying a Host or Provider based on the calling process, use the uid field. When
@@ -105,6 +114,25 @@
         boolean zombie; // if we're in safe mode, don't prune this just because nobody references it
 
         int tag; // for use while saving state (the index)
+
+        // is there an instance of this provider hosted by the given app?
+        public boolean isHostedBy(String packageName) {
+            final int N = instances.size();
+            for (int i = 0; i < N; i++) {
+                AppWidgetId id = instances.get(i);
+                if (packageName.equals(id.host.packageName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "Provider{" + ((info == null) ? "null" : info.provider)
+                    + (zombie ? " Z" : "")
+                    + '}';
+        }
     }
 
     static class Host {
@@ -125,14 +153,62 @@
                 return this.uid == callingUid;
             }
         }
+
+        boolean hostsPackage(String pkg) {
+            final int N = instances.size();
+            for (int i = 0; i < N; i++) {
+                Provider p = instances.get(i).provider;
+                if (p.info != null && pkg.equals(p.info.provider.getPackageName())) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "Host{" + packageName + ":" + hostId + '}';
+        }
     }
 
     static class AppWidgetId {
         int appWidgetId;
+        int restoredId;  // tracking & remapping any restored state
         Provider provider;
         RemoteViews views;
         Bundle options;
         Host host;
+
+        @Override
+        public String toString() {
+            return "AppWidgetId{" + appWidgetId + ':' + host + ':' + provider + '}';
+        }
+    }
+
+    AppWidgetId findRestoredWidgetLocked(int restoredId, Host host, Provider p) {
+        if (DEBUG_BACKUP) {
+            Slog.i(TAG, "Find restored widget: id=" + restoredId
+                    + " host=" + host + " provider=" + p);
+        }
+
+        if (p == null || host == null) {
+            return null;
+        }
+
+        final int N = mAppWidgetIds.size();
+        for (int i = 0; i < N; i++) {
+            AppWidgetId widget = mAppWidgetIds.get(i);
+            if (widget.restoredId == restoredId
+                    && widget.host.hostId == host.hostId
+                    && widget.host.packageName.equals(host.packageName)
+                    && widget.provider.info.provider.equals(p.info.provider)) {
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "   Found at " + i + " : " + widget);
+                }
+                return widget;
+            }
+        }
+        return null;
     }
 
     /**
@@ -194,6 +270,22 @@
     boolean mStateLoaded;
     int mMaxWidgetBitmapMemory;
 
+    // Map old (restored) widget IDs to new AppWidgetId instances.  This object is used
+    // as the lock around manipulation of the overall restored-widget state, just as
+    // mAppWidgetIds is used as the lock object around all "live" widget state
+    // manipulations.  Methods that must be called with this lock held are decorated
+    // with the suffix "Lr".
+    //
+    // In cases when both of those locks must be held concurrently, mRestoredWidgetIds
+    // must be acquired *first.*
+    private final SparseArray<AppWidgetId> mRestoredWidgetIds = new SparseArray<AppWidgetId>();
+
+    // We need to make sure to wipe the pre-restore widget state only once for
+    // a given package.  Keep track of what we've done so far here; the list is
+    // cleared at the start of every system restore pass, but preserved through
+    // any install-time restore operations.
+    HashSet<String> mPrunedApps = new HashSet<String>();
+
     private final Handler mSaveStateHandler;
 
     // These are for debugging only -- widgets are going missing in some rare instances
@@ -300,10 +392,19 @@
                         providersModified |= updateProvidersForPackageLocked(pkgName, null);
                     }
                 } else {
-                    // The package was just added
+                    // The package was just added.  Fix up the providers...
                     for (String pkgName : pkgList) {
                         providersModified |= addProvidersForPackageLocked(pkgName);
                     }
+                    // ...and see if these are hosts we've been awaiting
+                    for (String pkg : pkgList) {
+                        try {
+                            int uid = getUidForPackage(pkg);
+                            resolveHostUidLocked(pkg, uid);
+                        } catch (NameNotFoundException e) {
+                            // shouldn't happen; we just installed it!
+                        }
+                    }
                 }
                 saveStateAsync();
             }
@@ -331,6 +432,19 @@
         }
     }
 
+    void resolveHostUidLocked(String pkg, int uid) {
+        final int N = mHosts.size();
+        for (int i = 0; i < N; i++) {
+            Host h = mHosts.get(i);
+            if (h.uid == -1 && pkg.equals(h.packageName)) {
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "host " + pkg + ":" + h.hostId + " resolved to uid " + uid);
+                }
+                h.uid = uid;
+            }
+        }
+    }
+
     private void dumpProvider(Provider p, int index, PrintWriter pw) {
         AppWidgetProviderInfo info = p.info;
         pw.print("  ["); pw.print(index); pw.print("] provider ");
@@ -433,7 +547,7 @@
             if (!mHasFeature) {
                 return;
             }
-            loadAppWidgetListLocked();
+            loadWidgetProviderListLocked();
             loadStateLocked();
             mStateLoaded = true;
         }
@@ -516,6 +630,7 @@
     }
 
     void deleteHostLocked(Host host) {
+        if (DBG) log("Deleting host " + host);
         final int N = host.instances.size();
         for (int i = N - 1; i >= 0; i--) {
             AppWidgetId id = host.instances.get(i);
@@ -719,7 +834,7 @@
             }
             final ComponentName componentName = intent.getComponent();
             try {
-                final ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(componentName,
+                final ServiceInfo si = mPm.getServiceInfo(componentName,
                         PackageManager.GET_PERMISSIONS, mUserId);
                 if (!android.Manifest.permission.BIND_REMOTEVIEWS.equals(si.permission)) {
                     throw new SecurityException("Selected service does not require "
@@ -981,7 +1096,6 @@
             if (!mHasFeature) {
                 return;
             }
-            options = cloneIfLocalBinder(options);
             ensureStateLoadedLocked();
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
 
@@ -991,7 +1105,7 @@
 
             Provider p = id.provider;
             // Merge the options
-            id.options.putAll(options);
+            id.options.putAll(cloneIfLocalBinder(options));
 
             // send the broacast saying that this appWidgetId has been deleted
             Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED);
@@ -1278,9 +1392,13 @@
     }
 
     Provider lookupProviderLocked(ComponentName provider) {
-        final int N = mInstalledProviders.size();
+        return lookupProviderLocked(provider, mInstalledProviders);
+    }
+
+    Provider lookupProviderLocked(ComponentName provider, ArrayList<Provider> installedProviders) {
+        final int N = installedProviders.size();
         for (int i = 0; i < N; i++) {
-            Provider p = mInstalledProviders.get(i);
+            Provider p = installedProviders.get(i);
             if (p.info.provider.equals(provider)) {
                 return p;
             }
@@ -1317,11 +1435,12 @@
 
     void pruneHostLocked(Host host) {
         if (host.instances.size() == 0 && host.callbacks == null) {
+            if (DBG) log("Pruning host " + host);
             mHosts.remove(host);
         }
     }
 
-    void loadAppWidgetListLocked() {
+    void loadWidgetProviderListLocked() {
         Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
         try {
             List<ResolveInfo> broadcastReceivers = mPm.queryIntentReceivers(intent,
@@ -1348,7 +1467,23 @@
         Provider p = parseProviderInfoXml(new ComponentName(ri.activityInfo.packageName,
                 ri.activityInfo.name), ri);
         if (p != null) {
-            mInstalledProviders.add(p);
+            // we might have an inactive entry for this provider already due to
+            // a preceding restore operation.  if so, fix it up in place; otherwise
+            // just add this new one.
+            Provider existing = lookupProviderLocked(p.info.provider);
+            if (existing != null) {
+                if (existing.zombie && !mSafeMode) {
+                    // it's a placeholder that was set up during an app restore
+                    existing.zombie = false;
+                    existing.info = p.info; // the real one filled out from the ResolveInfo
+                    existing.uid = p.uid;
+                    if (DEBUG_BACKUP) {
+                        Slog.i(TAG, "Provider placeholder now reified: " + existing);
+                    }
+                }
+            } else {
+                mInstalledProviders.add(p);
+            }
             return true;
         } else {
             return false;
@@ -1463,6 +1598,554 @@
         }
     }
 
+    public List<String> getWidgetParticipants() {
+        HashSet<String> packages = new HashSet<String>();
+        synchronized (mAppWidgetIds) {
+            final int N = mAppWidgetIds.size();
+            for (int i = 0; i < N; i++) {
+                final AppWidgetId id = mAppWidgetIds.get(i);
+                packages.add(id.host.packageName);
+                packages.add(id.provider.info.provider.getPackageName());
+            }
+        }
+        return new ArrayList<String>(packages);
+    }
+
+    private void serializeProvider(XmlSerializer out, Provider p) throws IOException {
+        out.startTag(null, "p");
+        out.attribute(null, "pkg", p.info.provider.getPackageName());
+        out.attribute(null, "cl", p.info.provider.getClassName());
+        out.endTag(null, "p");
+    }
+
+    private void serializeHost(XmlSerializer out, Host host) throws IOException {
+        out.startTag(null, "h");
+        out.attribute(null, "pkg", host.packageName);
+        out.attribute(null, "id", Integer.toHexString(host.hostId));
+        out.endTag(null, "h");
+    }
+
+    private void serializeAppWidgetId(XmlSerializer out, AppWidgetId id) throws IOException {
+        out.startTag(null, "g");
+        out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
+        out.attribute(null, "rid", Integer.toHexString(id.restoredId));
+        out.attribute(null, "h", Integer.toHexString(id.host.tag));
+        if (id.provider != null) {
+            out.attribute(null, "p", Integer.toHexString(id.provider.tag));
+        }
+        if (id.options != null) {
+            out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
+            out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
+            out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
+            out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
+            out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
+                    AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
+        }
+        out.endTag(null, "g");
+    }
+
+    private Bundle parseWidgetIdOptions(XmlPullParser parser) {
+        Bundle options = new Bundle();
+        String minWidthString = parser.getAttributeValue(null, "min_width");
+        if (minWidthString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
+                    Integer.parseInt(minWidthString, 16));
+        }
+        String minHeightString = parser.getAttributeValue(null, "min_height");
+        if (minHeightString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
+                    Integer.parseInt(minHeightString, 16));
+        }
+        String maxWidthString = parser.getAttributeValue(null, "max_width");
+        if (maxWidthString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
+                    Integer.parseInt(maxWidthString, 16));
+        }
+        String maxHeightString = parser.getAttributeValue(null, "max_height");
+        if (maxHeightString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
+                    Integer.parseInt(maxHeightString, 16));
+        }
+        String categoryString = parser.getAttributeValue(null, "host_category");
+        if (categoryString != null) {
+            options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
+                    Integer.parseInt(categoryString, 16));
+        }
+        return options;
+    }
+
+    // Does this package either host or provide any active widgets?
+    private boolean packageNeedsWidgetBackupLocked(String packageName) {
+        int N = mAppWidgetIds.size();
+        for (int i = 0; i < N; i++) {
+            AppWidgetId id = mAppWidgetIds.get(i);
+            if (packageName.equals(id.host.packageName)) {
+                // this package is hosting widgets, so it knows widget IDs
+                return true;
+            }
+            Provider p = id.provider;
+            if (p != null && packageName.equals(p.info.provider.getPackageName())) {
+                // someone is hosting this app's widgets, so it knows widget IDs
+                return true;
+            }
+        }
+        return false;
+    }
+
+    // build the widget-state blob that we save for the app during backup.
+    public byte[] getWidgetState(String backupTarget) {
+        if (!mHasFeature) {
+            return null;
+        }
+
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        synchronized (mAppWidgetIds) {
+            // Preflight: if this app neither hosts nor provides any live widgets
+            // we have no work to do.
+            if (!packageNeedsWidgetBackupLocked(backupTarget)) {
+                return null;
+            }
+
+            try {
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(stream, "utf-8");
+                out.startDocument(null, true);
+                out.startTag(null, "ws");      // widget state
+                out.attribute(null, "version", String.valueOf(WIDGET_STATE_VERSION));
+                out.attribute(null, "pkg", backupTarget);
+
+                // Remember all the providers that are currently hosted or published
+                // by this package: that is, all of the entities related to this app
+                // which will need to be told about id remapping.
+                int N = mInstalledProviders.size();
+                int index = 0;
+                for (int i = 0; i < N; i++) {
+                    Provider p = mInstalledProviders.get(i);
+                    if (p.instances.size() > 0) {
+                        if (backupTarget.equals(p.info.provider.getPackageName())
+                                || p.isHostedBy(backupTarget)) {
+                            serializeProvider(out, p);
+                            p.tag = index++;
+                        }
+                    }
+                }
+
+                N = mHosts.size();
+                index = 0;
+                for (int i = 0; i < N; i++) {
+                    Host host = mHosts.get(i);
+                    if (backupTarget.equals(host.packageName)
+                            || host.hostsPackage(backupTarget)) {
+                        serializeHost(out, host);
+                        host.tag = index++;
+                    }
+                }
+
+                // All widget instances involving this package,
+                // either as host or as provider
+                N = mAppWidgetIds.size();
+                for (int i = 0; i < N; i++) {
+                    AppWidgetId id = mAppWidgetIds.get(i);
+                    if (backupTarget.equals(id.host.packageName)
+                            || backupTarget.equals(id.provider.info.provider.getPackageName())) {
+                        serializeAppWidgetId(out, id);
+                    }
+                }
+
+                out.endTag(null, "ws");
+                out.endDocument();
+            } catch (IOException e) {
+                Slog.w(TAG, "Unable to save widget state for " + backupTarget);
+                return null;
+            }
+
+        }
+        return stream.toByteArray();
+    }
+
+    public void restoreStarting() {
+        if (DEBUG_BACKUP) {
+            Slog.i(TAG, "restore starting for user " + mUserId);
+        }
+        synchronized (mRestoredWidgetIds) {
+            // We're starting a new "system" restore operation, so any widget restore
+            // state that we see from here on is intended to replace the current
+            // widget configuration of any/all of the affected apps.
+            mPrunedApps.clear();
+            mUpdatesByProvider.clear();
+            mUpdatesByHost.clear();
+        }
+    }
+
+    // We're restoring widget state for 'pkg', so we start by wiping (a) all widget
+    // instances that are hosted by that app, and (b) all instances in other hosts
+    // for which 'pkg' is the provider.  We assume that we'll be restoring all of
+    // these hosts & providers, so will be reconstructing a correct live state.
+    private void pruneWidgetStateLr(String pkg) {
+        if (!mPrunedApps.contains(pkg)) {
+            if (DEBUG_BACKUP) {
+                Slog.i(TAG, "pruning widget state for restoring package " + pkg);
+            }
+            for (int i = mAppWidgetIds.size() - 1; i >= 0; i--) {
+                AppWidgetId id = mAppWidgetIds.get(i);
+                Provider p = id.provider;
+                if (id.host.packageName.equals(pkg)
+                        || p.info.provider.getPackageName().equals(pkg)) {
+                    // 'pkg' is either the host or the provider for this instances,
+                    // so we tear it down in anticipation of it (possibly) being
+                    // reconstructed due to the restore
+                    p.instances.remove(id);
+
+                    unbindAppWidgetRemoteViewsServicesLocked(id);
+                    mAppWidgetIds.remove(i);
+                }
+            }
+            mPrunedApps.add(pkg);
+        } else {
+            if (DEBUG_BACKUP) {
+                Slog.i(TAG, "already pruned " + pkg + ", continuing normally");
+            }
+        }
+    }
+
+    // Accumulate a list of updates that affect the given provider for a final
+    // coalesced notification broadcast once restore is over.
+    class RestoreUpdateRecord {
+        public int oldId;
+        public int newId;
+        public boolean notified;
+
+        public RestoreUpdateRecord(int theOldId, int theNewId) {
+            oldId = theOldId;
+            newId = theNewId;
+            notified = false;
+        }
+    }
+
+    HashMap<Provider, ArrayList<RestoreUpdateRecord>> mUpdatesByProvider
+            = new HashMap<Provider, ArrayList<RestoreUpdateRecord>>();
+    HashMap<Host, ArrayList<RestoreUpdateRecord>> mUpdatesByHost
+            = new HashMap<Host, ArrayList<RestoreUpdateRecord>>();
+
+    private boolean alreadyStashed(ArrayList<RestoreUpdateRecord> stash,
+            final int oldId, final int newId) {
+        final int N = stash.size();
+        for (int i = 0; i < N; i++) {
+            RestoreUpdateRecord r = stash.get(i);
+            if (r.oldId == oldId && r.newId == newId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private void stashProviderRestoreUpdateLr(Provider provider, int oldId, int newId) {
+        ArrayList<RestoreUpdateRecord> r = mUpdatesByProvider.get(provider);
+        if (r == null) {
+            r = new ArrayList<RestoreUpdateRecord>();
+            mUpdatesByProvider.put(provider, r);
+        } else {
+            // don't duplicate
+            if (alreadyStashed(r, oldId, newId)) {
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "ID remap " + oldId + " -> " + newId
+                            + " already stashed for " + provider);
+                }
+                return;
+            }
+        }
+        r.add(new RestoreUpdateRecord(oldId, newId));
+    }
+
+    private void stashHostRestoreUpdateLr(Host host, int oldId, int newId) {
+        ArrayList<RestoreUpdateRecord> r = mUpdatesByHost.get(host);
+        if (r == null) {
+            r = new ArrayList<RestoreUpdateRecord>();
+            mUpdatesByHost.put(host, r);
+        } else {
+            if (alreadyStashed(r, oldId, newId)) {
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "ID remap " + oldId + " -> " + newId
+                            + " already stashed for " + host);
+                }
+                return;
+            }
+        }
+        r.add(new RestoreUpdateRecord(oldId, newId));
+    }
+
+    public void restoreWidgetState(String packageName, byte[] restoredState) {
+        if (!mHasFeature) {
+            return;
+        }
+
+        if (DEBUG_BACKUP) {
+            Slog.i(TAG, "Restoring widget state for " + packageName);
+        }
+
+        ByteArrayInputStream stream = new ByteArrayInputStream(restoredState);
+        try {
+            // Providers mentioned in the widget dataset by ordinal
+            ArrayList<Provider> restoredProviders = new ArrayList<Provider>();
+
+            // Hosts mentioned in the widget dataset by ordinal
+            ArrayList<Host> restoredHosts = new ArrayList<Host>();
+
+            //HashSet<String> toNotify = new HashSet<String>();
+
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(stream, null);
+
+            synchronized (mAppWidgetIds) {
+                synchronized (mRestoredWidgetIds) {
+                    int type;
+                    do {
+                        type = parser.next();
+                        if (type == XmlPullParser.START_TAG) {
+                            final String tag = parser.getName();
+                            if ("ws".equals(tag)) {
+                                String v = parser.getAttributeValue(null, "version");
+                                String pkg = parser.getAttributeValue(null, "pkg");
+
+                                // TODO: fix up w.r.t. canonical vs current package names
+                                if (!packageName.equals(pkg)) {
+                                    Slog.w(TAG, "Package mismatch in ws");
+                                    return;
+                                }
+
+                                int version = Integer.parseInt(v);
+                                if (version > WIDGET_STATE_VERSION) {
+                                    Slog.w(TAG, "Unable to process state version " + version);
+                                    return;
+                                }
+                            } else if ("p".equals(tag)) {
+                                String pkg = parser.getAttributeValue(null, "pkg");
+                                String cl = parser.getAttributeValue(null, "cl");
+
+                                // hostedProviders index will match 'p' attribute in widget's
+                                // entry in the xml file being restored
+                                // If there's no live entry for this provider, add an inactive one
+                                // so that widget IDs referring to them can be properly allocated
+                                final ComponentName cn = new ComponentName(pkg, cl);
+                                Provider p = lookupProviderLocked(cn, mInstalledProviders);
+                                if (p == null) {
+                                    p = new Provider();
+                                    p.info = new AppWidgetProviderInfo();
+                                    p.info.provider = cn;
+                                    p.zombie = true;
+                                    mInstalledProviders.add(p);
+                                }
+                                if (DEBUG_BACKUP) {
+                                    Slog.i(TAG, "   provider " + cn);
+                                }
+                                restoredProviders.add(p);
+                            } else if ("h".equals(tag)) {
+                                // The host app may not yet exist on the device.  If it's here we
+                                // just use the existing Host entry, otherwise we create a
+                                // placeholder whose uid will be fixed up at PACKAGE_ADDED time.
+                                String pkg = parser.getAttributeValue(null, "pkg");
+                                int uid;
+                                try {
+                                    uid = getUidForPackage(pkg);
+                                } catch (NameNotFoundException e) {
+                                    uid = -1;
+                                }
+                                int hostId = Integer.parseInt(
+                                        parser.getAttributeValue(null, "id"), 16);
+                                Host h = lookupOrAddHostLocked(uid, pkg, hostId);
+                                if (DEBUG_BACKUP) {
+                                    Slog.i(TAG, "   host[" + restoredHosts.size()
+                                            + "]: {" + h.packageName + ":" + h.hostId + "}");
+                                }
+                                restoredHosts.add(h);
+                            } else if ("g".equals(tag)) {
+                                int restoredId = Integer.parseInt(
+                                        parser.getAttributeValue(null, "id"), 16);
+                                int hostIndex = Integer.parseInt(
+                                        parser.getAttributeValue(null, "h"), 16);
+                                Host host = restoredHosts.get(hostIndex);
+                                Provider p = null;
+                                String prov = parser.getAttributeValue(null, "p");
+                                if (prov != null) {
+                                    // could have been null if the app had allocated an id
+                                    // but not yet established a binding under that id
+                                    int which = Integer.parseInt(prov, 16);
+                                    p = restoredProviders.get(which);
+                                }
+
+                                // We'll be restoring widget state for both the host and
+                                // provider sides of this widget ID, so make sure we are
+                                // beginning from a clean slate on both fronts.
+                                pruneWidgetStateLr(host.packageName);
+                                if (p != null) {
+                                    pruneWidgetStateLr(p.info.provider.getPackageName());
+                                }
+
+                                // Have we heard about this ancestral widget instance before?
+                                AppWidgetId id = findRestoredWidgetLocked(restoredId, host, p);
+                                if (id == null) {
+                                    id = new AppWidgetId();
+                                    id.appWidgetId = mNextAppWidgetId++;
+                                    id.restoredId = restoredId;
+                                    id.options = parseWidgetIdOptions(parser);
+                                    id.host = host;
+                                    id.host.instances.add(id);
+                                    id.provider = p;
+                                    if (id.provider != null) {
+                                        id.provider.instances.add(id);
+                                    }
+                                    if (DEBUG_BACKUP) {
+                                        Slog.i(TAG, "New restored id " + restoredId
+                                                + " now " + id);
+                                    }
+                                    mAppWidgetIds.add(id);
+                                }
+                                if (id.provider.info != null) {
+                                    stashProviderRestoreUpdateLr(id.provider,
+                                            restoredId, id.appWidgetId);
+                                } else {
+                                    Slog.w(TAG, "Missing provider for restored widget " + id);
+                                }
+                                stashHostRestoreUpdateLr(id.host, restoredId, id.appWidgetId);
+
+                                if (DEBUG_BACKUP) {
+                                    Slog.i(TAG, "   instance: " + restoredId
+                                            + " -> " + id.appWidgetId
+                                            + " :: p=" + id.provider);
+                                }
+                            }
+                        }
+                    } while (type != XmlPullParser.END_DOCUMENT);
+
+                    // We've updated our own bookkeeping.  We'll need to notify the hosts and
+                    // providers about the changes, but we can't do that yet because the restore
+                    // target is not necessarily fully live at this moment.  Set aside the
+                    // information for now; the backup manager will call us once more at the
+                    // end of the process when all of the targets are in a known state, and we
+                    // will update at that point.
+                }
+            }
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Unable to restore widget state for " + packageName);
+        } catch (IOException e) {
+            Slog.w(TAG, "Unable to restore widget state for " + packageName);
+        } finally {
+            saveStateAsync();
+        }
+    }
+
+    // Called once following the conclusion of a restore operation.  This is when we
+    // send out updates to apps involved in widget-state restore telling them about
+    // the new widget ID space.
+    public void restoreFinished() {
+        if (DEBUG_BACKUP) {
+            Slog.i(TAG, "restoreFinished for " + mUserId);
+        }
+
+        final UserHandle userHandle = new UserHandle(mUserId);
+        synchronized (mRestoredWidgetIds) {
+            // Build the providers' broadcasts and send them off
+            Set<Entry<Provider, ArrayList<RestoreUpdateRecord>>> providerEntries
+                    = mUpdatesByProvider.entrySet();
+            for (Entry<Provider, ArrayList<RestoreUpdateRecord>> e : providerEntries) {
+                // For each provider there's a list of affected IDs
+                Provider provider = e.getKey();
+                ArrayList<RestoreUpdateRecord> updates = e.getValue();
+                final int pending = countPendingUpdates(updates);
+                if (DEBUG_BACKUP) {
+                    Slog.i(TAG, "Provider " + provider + " pending: " + pending);
+                }
+                if (pending > 0) {
+                    int[] oldIds = new int[pending];
+                    int[] newIds = new int[pending];
+                    final int N = updates.size();
+                    int nextPending = 0;
+                    for (int i = 0; i < N; i++) {
+                        RestoreUpdateRecord r = updates.get(i);
+                        if (!r.notified) {
+                            r.notified = true;
+                            oldIds[nextPending] = r.oldId;
+                            newIds[nextPending] = r.newId;
+                            nextPending++;
+                            if (DEBUG_BACKUP) {
+                                Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
+                            }
+                        }
+                    }
+                    sendWidgetRestoreBroadcast(AppWidgetManager.ACTION_APPWIDGET_RESTORED,
+                            provider, null, oldIds, newIds, userHandle);
+                }
+            }
+
+            // same thing per host
+            Set<Entry<Host, ArrayList<RestoreUpdateRecord>>> hostEntries
+                    = mUpdatesByHost.entrySet();
+            for (Entry<Host, ArrayList<RestoreUpdateRecord>> e : hostEntries) {
+                Host host = e.getKey();
+                if (host.uid > 0) {
+                    ArrayList<RestoreUpdateRecord> updates = e.getValue();
+                    final int pending = countPendingUpdates(updates);
+                    if (DEBUG_BACKUP) {
+                        Slog.i(TAG, "Host " + host + " pending: " + pending);
+                    }
+                    if (pending > 0) {
+                        int[] oldIds = new int[pending];
+                        int[] newIds = new int[pending];
+                        final int N = updates.size();
+                        int nextPending = 0;
+                        for (int i = 0; i < N; i++) {
+                            RestoreUpdateRecord r = updates.get(i);
+                            if (!r.notified) {
+                                r.notified = true;
+                                oldIds[nextPending] = r.oldId;
+                                newIds[nextPending] = r.newId;
+                                nextPending++;
+                                if (DEBUG_BACKUP) {
+                                    Slog.i(TAG, "   " + r.oldId + " => " + r.newId);
+                                }
+                            }
+                        }
+                        sendWidgetRestoreBroadcast(AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED,
+                                null, host, oldIds, newIds, userHandle);
+                    }
+                }
+            }
+        }
+    }
+
+    private int countPendingUpdates(ArrayList<RestoreUpdateRecord> updates) {
+        int pending = 0;
+        final int N = updates.size();
+        for (int i = 0; i < N; i++) {
+            RestoreUpdateRecord r = updates.get(i);
+            if (!r.notified) {
+                pending++;
+            }
+        }
+        return pending;
+    }
+
+    void sendWidgetRestoreBroadcast(String action, Provider provider, Host host,
+            int[] oldIds, int[] newIds, UserHandle userHandle) {
+        Intent intent = new Intent(action);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS, oldIds);
+        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, newIds);
+        if (provider != null) {
+            intent.setComponent(provider.info.provider);
+            mContext.sendBroadcastAsUser(intent, userHandle);
+        }
+        if (host != null) {
+            intent.setComponent(null);
+            intent.setPackage(host.packageName);
+            intent.putExtra(AppWidgetManager.EXTRA_HOST_ID, host.hostId);
+            mContext.sendBroadcastAsUser(intent, userHandle);
+        }
+    }
+
     private Provider parseProviderInfoXml(ComponentName component, ResolveInfo ri) {
         Provider p = null;
 
@@ -1660,10 +2343,7 @@
             for (int i = 0; i < N; i++) {
                 Provider p = mInstalledProviders.get(i);
                 if (p.instances.size() > 0) {
-                    out.startTag(null, "p");
-                    out.attribute(null, "pkg", p.info.provider.getPackageName());
-                    out.attribute(null, "cl", p.info.provider.getClassName());
-                    out.endTag(null, "p");
+                    serializeProvider(out, p);
                     p.tag = providerIndex;
                     providerIndex++;
                 }
@@ -1672,35 +2352,14 @@
             N = mHosts.size();
             for (int i = 0; i < N; i++) {
                 Host host = mHosts.get(i);
-                out.startTag(null, "h");
-                out.attribute(null, "pkg", host.packageName);
-                out.attribute(null, "id", Integer.toHexString(host.hostId));
-                out.endTag(null, "h");
+                serializeHost(out, host);
                 host.tag = i;
             }
 
             N = mAppWidgetIds.size();
             for (int i = 0; i < N; i++) {
                 AppWidgetId id = mAppWidgetIds.get(i);
-                out.startTag(null, "g");
-                out.attribute(null, "id", Integer.toHexString(id.appWidgetId));
-                out.attribute(null, "h", Integer.toHexString(id.host.tag));
-                if (id.provider != null) {
-                    out.attribute(null, "p", Integer.toHexString(id.provider.tag));
-                }
-                if (id.options != null) {
-                    out.attribute(null, "min_width", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)));
-                    out.attribute(null, "min_height", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)));
-                    out.attribute(null, "max_width", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)));
-                    out.attribute(null, "max_height", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)));
-                    out.attribute(null, "host_category", Integer.toHexString(id.options.getInt(
-                            AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)));
-                }
-                out.endTag(null, "g");
+                serializeAppWidgetId(out, id);
             }
 
             Iterator<String> it = mPackagesWithBindWidgetPermission.iterator();
@@ -1801,6 +2460,11 @@
                             mNextAppWidgetId = id.appWidgetId + 1;
                         }
 
+                        // restored ID is allowed to be absent
+                        String restoredIdString = parser.getAttributeValue(null, "rid");
+                        id.restoredId = (restoredIdString == null) ? 0
+                                : Integer.parseInt(restoredIdString, 16);
+
                         Bundle options = new Bundle();
                         String minWidthString = parser.getAttributeValue(null, "min_width");
                         if (minWidthString != null) {
@@ -1975,8 +2639,7 @@
                 continue;
             }
             if (pkgName.equals(ai.packageName)) {
-                addProviderLocked(ri);
-                providersAdded = true;
+                providersAdded = addProviderLocked(ri);
             }
         }
 
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 8eaefef..1a1512f 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -24,6 +24,7 @@
 import android.app.IBackupAgent;
 import android.app.PendingIntent;
 import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
 import android.app.backup.FullBackup;
 import android.app.backup.RestoreSet;
@@ -82,12 +83,14 @@
 import com.android.internal.backup.BackupConstants;
 import com.android.internal.backup.IBackupTransport;
 import com.android.internal.backup.IObbBackupService;
+import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
 import com.android.server.SystemService;
 import com.android.server.backup.PackageManagerBackupAgent.Metadata;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
@@ -115,10 +118,13 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Random;
 import java.util.Set;
+import java.util.TreeMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.zip.Deflater;
 import java.util.zip.DeflaterOutputStream;
@@ -136,22 +142,43 @@
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.SecretKeySpec;
 
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
 public class BackupManagerService extends IBackupManager.Stub {
 
     private static final String TAG = "BackupManagerService";
     private static final boolean DEBUG = true;
     private static final boolean MORE_DEBUG = false;
 
+    // System-private key used for backing up an app's widget state.  Must
+    // begin with U+FFxx by convention (we reserve all keys starting
+    // with U+FF00 or higher for system use).
+    static final String KEY_WIDGET_STATE = "\uffed\uffedwidget";
+
     // Historical and current algorithm names
     static final String PBKDF_CURRENT = "PBKDF2WithHmacSHA1";
     static final String PBKDF_FALLBACK = "PBKDF2WithHmacSHA1And8bit";
 
     // Name and current contents version of the full-backup manifest file
+    //
+    // Manifest version history:
+    //
+    // 1 : initial release
     static final String BACKUP_MANIFEST_FILENAME = "_manifest";
     static final int BACKUP_MANIFEST_VERSION = 1;
+
+    // External archive format version history:
+    //
+    // 1 : initial release
+    // 2 : no format change per se; version bump to facilitate PBKDF2 version skew detection
+    // 3 : introduced "_meta" metadata file; no other format change per se
+    static final int BACKUP_FILE_VERSION = 3;
     static final String BACKUP_FILE_HEADER_MAGIC = "ANDROID BACKUP\n";
-    static final int BACKUP_FILE_VERSION = 2;
     static final int BACKUP_PW_FILE_VERSION = 2;
+    static final String BACKUP_METADATA_FILENAME = "_meta";
+    static final int BACKUP_METADATA_VERSION = 1;
+    static final int BACKUP_WIDGET_METADATA_TOKEN = 0x01FFED01;
     static final boolean COMPRESS_FULL_BACKUPS = true; // should be true in production
 
     static final String SHARED_BACKUP_AGENT_PACKAGE = "com.android.sharedstoragebackup";
@@ -186,6 +213,7 @@
     private static final int MSG_RUN_FULL_RESTORE = 10;
     private static final int MSG_RETRY_INIT = 11;
     private static final int MSG_RETRY_CLEAR = 12;
+    private static final int MSG_WIDGET_BROADCAST = 13;
 
     // backup task state machine tick
     static final int MSG_BACKUP_RESTORE_STEP = 20;
@@ -337,42 +365,47 @@
         public long token;
         public PackageInfo pkgInfo;
         public int pmToken; // in post-install restore, the PM's token for this transaction
-        public boolean needFullBackup;
+        public boolean isSystemRestore;
         public String[] filterSet;
 
+        // Restore a single package
         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-                long _token, PackageInfo _pkg, int _pmToken, boolean _needFullBackup) {
+                long _token, PackageInfo _pkg, int _pmToken) {
             transport = _transport;
             dirName = _dirName;
             observer = _obs;
             token = _token;
             pkgInfo = _pkg;
             pmToken = _pmToken;
-            needFullBackup = _needFullBackup;
+            isSystemRestore = false;
             filterSet = null;
         }
 
+        // Restore everything possible.  This is the form that Setup Wizard or similar
+        // restore UXes use.
         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-                long _token, boolean _needFullBackup) {
+                long _token) {
             transport = _transport;
             dirName = _dirName;
             observer = _obs;
             token = _token;
             pkgInfo = null;
             pmToken = 0;
-            needFullBackup = _needFullBackup;
+            isSystemRestore = true;
             filterSet = null;
         }
 
+        // Restore some set of packages.  Leave this one up to the caller to specify
+        // whether it's to be considered a system-level restore.
         RestoreParams(IBackupTransport _transport, String _dirName, IRestoreObserver _obs,
-                long _token, String[] _filterSet, boolean _needFullBackup) {
+                long _token, String[] _filterSet, boolean _isSystemRestore) {
             transport = _transport;
             dirName = _dirName;
             observer = _obs;
             token = _token;
             pkgInfo = null;
             pmToken = 0;
-            needFullBackup = _needFullBackup;
+            isSystemRestore = _isSystemRestore;
             filterSet = _filterSet;
         }
     }
@@ -413,16 +446,19 @@
         public boolean includeApks;
         public boolean includeObbs;
         public boolean includeShared;
+        public boolean doWidgets;
         public boolean allApps;
         public boolean includeSystem;
         public String[] packages;
 
         FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveObbs,
-                boolean saveShared, boolean doAllApps, boolean doSystem, String[] pkgList) {
+                boolean saveShared, boolean alsoWidgets, boolean doAllApps, boolean doSystem,
+                String[] pkgList) {
             fd = output;
             includeApks = saveApks;
             includeObbs = saveObbs;
             includeShared = saveShared;
+            doWidgets = alsoWidgets;
             allApps = doAllApps;
             includeSystem = doSystem;
             packages = pkgList;
@@ -618,7 +654,8 @@
                 FullBackupParams params = (FullBackupParams)msg.obj;
                 PerformFullBackupTask task = new PerformFullBackupTask(params.fd,
                         params.observer, params.includeApks, params.includeObbs,
-                        params.includeShared, params.curPassword, params.encryptPassword,
+                        params.includeShared, params.doWidgets,
+                        params.curPassword, params.encryptPassword,
                         params.allApps, params.includeSystem, params.packages, params.latch);
                 (new Thread(task)).start();
                 break;
@@ -631,7 +668,7 @@
                 PerformRestoreTask task = new PerformRestoreTask(
                         params.transport, params.dirName, params.observer,
                         params.token, params.pkgInfo, params.pmToken,
-                        params.needFullBackup, params.filterSet);
+                        params.isSystemRestore, params.filterSet);
                 Message restoreMsg = obtainMessage(MSG_BACKUP_RESTORE_STEP, task);
                 sendMessage(restoreMsg);
                 break;
@@ -770,6 +807,13 @@
                 }
                 break;
             }
+
+            case MSG_WIDGET_BROADCAST:
+            {
+                final Intent intent = (Intent) msg.obj;
+                mContext.sendBroadcastAsUser(intent, UserHandle.OWNER);
+                break;
+            }
             }
         }
     }
@@ -2360,10 +2404,40 @@
 
         @Override
         public void operationComplete() {
-            // Okay, the agent successfully reported back to us.  Spin the data off to the
+            // Okay, the agent successfully reported back to us.  The next thing we do is
+            // push the app widget state for the app, if any.
+            final String pkgName = mCurrentPackage.packageName;
+            final long filepos = mBackupDataName.length();
+            FileDescriptor fd = mBackupData.getFileDescriptor();
+            try {
+                BackupDataOutput out = new BackupDataOutput(fd);
+                byte[] widgetState = AppWidgetBackupBridge.getWidgetState(pkgName,
+                        UserHandle.USER_OWNER);
+                if (widgetState != null) {
+                    out.writeEntityHeader(KEY_WIDGET_STATE, widgetState.length);
+                    out.writeEntityData(widgetState, widgetState.length);
+                } else {
+                    // No widget state for this app, but push a 'delete' operation for it
+                    // in case they're trying to play games with the payload.
+                    out.writeEntityHeader(KEY_WIDGET_STATE, -1);
+                }
+            } catch (IOException e) {
+                // Hard disk error; recovery/failure policy TBD.  For now roll back,
+                // but we may want to consider this a transport-level failure (i.e.
+                // we're in such a bad state that we can't contemplate doing backup
+                // operations any more during this pass).
+                Slog.w(TAG, "Unable to save widget state for " + pkgName);
+                try {
+                    Libcore.os.ftruncate(fd, filepos);
+                } catch (ErrnoException ee) {
+                    Slog.w(TAG, "Unable to roll back!");
+                }
+            }
+
+            // Spin the data off to the
             // transport and proceed with the next stage.
             if (MORE_DEBUG) Slog.v(TAG, "operationComplete(): sending data to transport for "
-                    + mCurrentPackage.packageName);
+                    + pkgName);
             mBackupHandler.removeMessages(MSG_TIMEOUT);
             clearAgentState();
             addBackupTrace("operation complete");
@@ -2402,17 +2476,14 @@
                 if (mStatus == BackupConstants.TRANSPORT_OK) {
                     mBackupDataName.delete();
                     mNewStateName.renameTo(mSavedStateName);
-                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE,
-                            mCurrentPackage.packageName, size);
-                    logBackupComplete(mCurrentPackage.packageName);
+                    EventLog.writeEvent(EventLogTags.BACKUP_PACKAGE, pkgName, size);
+                    logBackupComplete(pkgName);
                 } else {
-                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
-                            mCurrentPackage.packageName);
+                    EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
                 }
             } catch (Exception e) {
-                Slog.e(TAG, "Transport error backing up " + mCurrentPackage.packageName, e);
-                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE,
-                        mCurrentPackage.packageName);
+                Slog.e(TAG, "Transport error backing up " + pkgName, e);
+                EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, pkgName);
                 mStatus = BackupConstants.TRANSPORT_ERROR;
             } finally {
                 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
@@ -2631,18 +2702,21 @@
         boolean mIncludeApks;
         boolean mIncludeObbs;
         boolean mIncludeShared;
+        boolean mDoWidgets;
         boolean mAllApps;
         final boolean mIncludeSystem;
-        String[] mPackages;
+        ArrayList<String> mPackages;
         String mCurrentPassword;
         String mEncryptPassword;
         AtomicBoolean mLatchObject;
         File mFilesDir;
         File mManifestFile;
+        File mMetadataFile;
         
 
         class FullBackupRunner implements Runnable {
             PackageInfo mPackage;
+            byte[] mWidgetData;
             IBackupAgent mAgent;
             ParcelFileDescriptor mPipe;
             int mToken;
@@ -2650,8 +2724,10 @@
             boolean mWriteManifest;
 
             FullBackupRunner(PackageInfo pack, IBackupAgent agent, ParcelFileDescriptor pipe,
-                    int token, boolean sendApk, boolean writeManifest)  throws IOException {
+                    int token, boolean sendApk, boolean writeManifest, byte[] widgetData)
+                            throws IOException {
                 mPackage = pack;
+                mWidgetData = widgetData;
                 mAgent = agent;
                 mPipe = ParcelFileDescriptor.dup(pipe.getFileDescriptor());
                 mToken = token;
@@ -2666,12 +2742,24 @@
                             mPipe.getFileDescriptor());
 
                     if (mWriteManifest) {
+                        final boolean writeWidgetData = mWidgetData != null;
                         if (MORE_DEBUG) Slog.d(TAG, "Writing manifest for " + mPackage.packageName);
-                        writeAppManifest(mPackage, mManifestFile, mSendApk);
+                        writeAppManifest(mPackage, mManifestFile, mSendApk, writeWidgetData);
                         FullBackup.backupToTar(mPackage.packageName, null, null,
                                 mFilesDir.getAbsolutePath(),
                                 mManifestFile.getAbsolutePath(),
                                 output);
+                        mManifestFile.delete();
+
+                        // We only need to write a metadata file if we have widget data to stash
+                        if (writeWidgetData) {
+                            writeMetadata(mPackage, mMetadataFile, mWidgetData);
+                            FullBackup.backupToTar(mPackage.packageName, null, null,
+                                    mFilesDir.getAbsolutePath(),
+                                    mMetadataFile.getAbsolutePath(),
+                                    output);
+                            mMetadataFile.delete();
+                        }
                     }
 
                     if (mSendApk) {
@@ -2696,16 +2784,19 @@
 
         PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer, 
                 boolean includeApks, boolean includeObbs, boolean includeShared,
-                String curPassword, String encryptPassword, boolean doAllApps,
+                boolean doWidgets, String curPassword, String encryptPassword, boolean doAllApps,
                 boolean doSystem, String[] packages, AtomicBoolean latch) {
             mOutputFile = fd;
             mObserver = observer;
             mIncludeApks = includeApks;
             mIncludeObbs = includeObbs;
             mIncludeShared = includeShared;
+            mDoWidgets = doWidgets;
             mAllApps = doAllApps;
             mIncludeSystem = doSystem;
-            mPackages = packages;
+            mPackages = (packages == null)
+                    ? new ArrayList<String>()
+                    : new ArrayList<String>(Arrays.asList(packages));
             mCurrentPassword = curPassword;
             // when backing up, if there is a current backup password, we require that
             // the user use a nonempty encryption password as well.  if one is supplied
@@ -2720,13 +2811,28 @@
 
             mFilesDir = new File("/data/system");
             mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
+            mMetadataFile = new File(mFilesDir, BACKUP_METADATA_FILENAME);
+        }
+
+        void addPackagesToSet(TreeMap<String, PackageInfo> set, List<String> pkgNames) {
+            for (String pkgName : pkgNames) {
+                if (!set.containsKey(pkgName)) {
+                    try {
+                        PackageInfo info = mPackageManager.getPackageInfo(pkgName,
+                                PackageManager.GET_SIGNATURES);
+                        set.put(pkgName, info);
+                    } catch (NameNotFoundException e) {
+                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
+                    }
+                }
+            }
         }
 
         @Override
         public void run() {
             Slog.i(TAG, "--- Performing full-dataset backup ---");
 
-            List<PackageInfo> packagesToBackup = new ArrayList<PackageInfo>();
+            TreeMap<String, PackageInfo> packagesToBackup = new TreeMap<String, PackageInfo>();
             FullBackupObbConnection obbConnection = new FullBackupObbConnection();
             obbConnection.establish();  // we'll want this later
 
@@ -2734,63 +2840,70 @@
 
             // doAllApps supersedes the package set if any
             if (mAllApps) {
-                packagesToBackup = mPackageManager.getInstalledPackages(
+                List<PackageInfo> allPackages = mPackageManager.getInstalledPackages(
                         PackageManager.GET_SIGNATURES);
-                // Exclude system apps if we've been asked to do so
-                if (mIncludeSystem == false) {
-                    for (int i = 0; i < packagesToBackup.size(); ) {
-                        PackageInfo pkg = packagesToBackup.get(i);
-                        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-                            packagesToBackup.remove(i);
-                        } else {
-                            i++;
-                        }
+                for (int i = 0; i < allPackages.size(); i++) {
+                    PackageInfo pkg = allPackages.get(i);
+                    // Exclude system apps if we've been asked to do so
+                    if (mIncludeSystem == true
+                            || ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0)) {
+                        packagesToBackup.put(pkg.packageName, pkg);
                     }
                 }
             }
 
+            // If we're doing widget state as well, ensure that we have all the involved
+            // host & provider packages in the set
+            if (mDoWidgets) {
+                List<String> pkgs =
+                        AppWidgetBackupBridge.getWidgetParticipants(UserHandle.USER_OWNER);
+                if (pkgs != null) {
+                    if (MORE_DEBUG) {
+                        Slog.i(TAG, "Adding widget participants to backup set:");
+                        StringBuilder sb = new StringBuilder(128);
+                        sb.append("   ");
+                        for (String s : pkgs) {
+                            sb.append(' ');
+                            sb.append(s);
+                        }
+                        Slog.i(TAG, sb.toString());
+                    }
+                    addPackagesToSet(packagesToBackup, pkgs);
+                }
+            }
+
             // Now process the command line argument packages, if any. Note that explicitly-
             // named system-partition packages will be included even if includeSystem was
             // set to false.
             if (mPackages != null) {
-                for (String pkgName : mPackages) {
-                    try {
-                        packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
-                                PackageManager.GET_SIGNATURES));
-                    } catch (NameNotFoundException e) {
-                        Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
-                    }
-                }
+                addPackagesToSet(packagesToBackup, mPackages);
             }
 
-            // Cull any packages that have indicated that backups are not permitted, as well
-            // as any explicit mention of the 'special' shared-storage agent package (we
-            // handle that one at the end).
-            for (int i = 0; i < packagesToBackup.size(); ) {
-                PackageInfo pkg = packagesToBackup.get(i);
+            // Now we cull any inapplicable / inappropriate packages from the set
+            Iterator<Entry<String, PackageInfo>> iter = packagesToBackup.entrySet().iterator();
+            while (iter.hasNext()) {
+                PackageInfo pkg = iter.next().getValue();
                 if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0
                         || pkg.packageName.equals(SHARED_BACKUP_AGENT_PACKAGE)) {
-                    packagesToBackup.remove(i);
-                } else {
-                    i++;
-                }
-            }
-
-            // Cull any packages that run as system-domain uids but do not define their
-            // own backup agents
-            for (int i = 0; i < packagesToBackup.size(); ) {
-                PackageInfo pkg = packagesToBackup.get(i);
-                if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
+                    // Cull any packages that have indicated that backups are not permitted, as well
+                    // as any explicit mention of the 'special' shared-storage agent package (we
+                    // handle that one at the end).
+                    iter.remove();
+                } else if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
                         && (pkg.applicationInfo.backupAgentName == null)) {
+                    // Cull any packages that run as system-domain uids but do not define their
+                    // own backup agents
                     if (MORE_DEBUG) {
                         Slog.i(TAG, "... ignoring non-agent system package " + pkg.packageName);
                     }
-                    packagesToBackup.remove(i);
-                } else {
-                    i++;
+                    iter.remove();
                 }
             }
 
+            // flatten the set of packages now so we can explicitly control the ordering
+            ArrayList<PackageInfo> backupQueue =
+                    new ArrayList<PackageInfo>(packagesToBackup.values());
+
             FileOutputStream ofstream = new FileOutputStream(mOutputFile.getFileDescriptor());
             OutputStream out = null;
 
@@ -2866,16 +2979,16 @@
                 if (mIncludeShared) {
                     try {
                         pkg = mPackageManager.getPackageInfo(SHARED_BACKUP_AGENT_PACKAGE, 0);
-                        packagesToBackup.add(pkg);
+                        backupQueue.add(pkg);
                     } catch (NameNotFoundException e) {
                         Slog.e(TAG, "Unable to find shared-storage backup handler");
                     }
                 }
 
                 // Now back up the app data via the agent mechanism
-                int N = packagesToBackup.size();
+                int N = backupQueue.size();
                 for (int i = 0; i < N; i++) {
-                    pkg = packagesToBackup.get(i);
+                    pkg = backupQueue.get(i);
                     backupOnePackage(pkg, out);
 
                     // after the app's agent runs to handle its private filesystem
@@ -3006,11 +3119,13 @@
                             && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
                                 (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
 
+                    byte[] widgetBlob = AppWidgetBackupBridge.getWidgetState(pkg.packageName,
+                            UserHandle.USER_OWNER);
                     sendOnBackupPackage(isSharedStorage ? "Shared storage" : pkg.packageName);
 
                     final int token = generateToken();
                     FullBackupRunner runner = new FullBackupRunner(pkg, agent, pipes[1],
-                            token, sendApk, !isSharedStorage);
+                            token, sendApk, !isSharedStorage, widgetBlob);
                     pipes[1].close();   // the runner has dup'd it
                     pipes[1] = null;
                     Thread t = new Thread(runner);
@@ -3086,8 +3201,8 @@
             }
         }
 
-        private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
-                throws IOException {
+        private void writeAppManifest(PackageInfo pkg, File manifestFile,
+                boolean withApk, boolean withWidgets) throws IOException {
             // Manifest format. All data are strings ending in LF:
             //     BACKUP_MANIFEST_VERSION, currently 1
             //
@@ -3125,6 +3240,43 @@
             outstream.close();
         }
 
+        // Widget metadata format. All header entries are strings ending in LF:
+        //
+        // Version 1 header:
+        //     BACKUP_METADATA_VERSION, currently "1"
+        //     package name
+        //
+        // File data (all integers are binary in network byte order)
+        // *N: 4 : integer token identifying which metadata blob
+        //     4 : integer size of this blob = N
+        //     N : raw bytes of this metadata blob
+        //
+        // Currently understood blobs (always in network byte order):
+        //
+        //     widgets : metadata token = 0x01FFED01 (BACKUP_WIDGET_METADATA_TOKEN)
+        //
+        // Unrecognized blobs are *ignored*, not errors.
+        private void writeMetadata(PackageInfo pkg, File destination, byte[] widgetData)
+                throws IOException {
+            StringBuilder b = new StringBuilder(512);
+            StringBuilderPrinter printer = new StringBuilderPrinter(b);
+            printer.println(Integer.toString(BACKUP_METADATA_VERSION));
+            printer.println(pkg.packageName);
+
+            FileOutputStream fout = new FileOutputStream(destination);
+            BufferedOutputStream bout = new BufferedOutputStream(fout);
+            DataOutputStream out = new DataOutputStream(bout);
+            bout.write(b.toString().getBytes());    // bypassing DataOutputStream
+
+            if (widgetData != null && widgetData.length > 0) {
+                out.writeInt(BACKUP_WIDGET_METADATA_TOKEN);
+                out.writeInt(widgetData.length);
+                out.write(widgetData);
+            }
+            bout.flush();
+            out.close();
+        }
+
         private void tearDown(PackageInfo pkg) {
             if (pkg != null) {
                 final ApplicationInfo app = pkg.applicationInfo;
@@ -3228,6 +3380,7 @@
         ApplicationInfo mTargetApp;
         FullBackupObbConnection mObbConnection = null;
         ParcelFileDescriptor[] mPipes = null;
+        byte[] mWidgetData = null;
 
         long mBytes;
 
@@ -3524,7 +3677,8 @@
                         // Clean up the previous agent relationship if necessary,
                         // and let the observer know we're considering a new app.
                         if (mAgent != null) {
-                            if (DEBUG) Slog.d(TAG, "Saw new package; tearing down old one");
+                            if (DEBUG) Slog.d(TAG, "Saw new package; finalizing old one");
+                            // Now we're really done
                             tearDownPipes();
                             tearDownAgent(mTargetApp);
                             mTargetApp = null;
@@ -3540,6 +3694,9 @@
                         // input file
                         skipTarPadding(info.size, instream);
                         sendOnRestorePackage(pkg);
+                    } else if (info.path.equals(BACKUP_METADATA_FILENAME)) {
+                        // Metadata blobs!
+                        readMetadata(info, instream);
                     } else {
                         // Non-manifest, so it's actual file data.  Is this a package
                         // we're ignoring?
@@ -3996,6 +4153,71 @@
             }
         }
 
+        // Read a widget metadata file, returning the restored blob
+        void readMetadata(FileMetadata info, InputStream instream) throws IOException {
+            byte[] data = null;
+
+            // Fail on suspiciously large widget dump files
+            if (info.size > 64 * 1024) {
+                throw new IOException("Metadata too big; corrupt? size=" + info.size);
+            }
+
+            byte[] buffer = new byte[(int) info.size];
+            if (readExactly(instream, buffer, 0, (int)info.size) == info.size) {
+                mBytes += info.size;
+            } else throw new IOException("Unexpected EOF in widget data");
+
+            String[] str = new String[1];
+            int offset = extractLine(buffer, 0, str);
+            int version = Integer.parseInt(str[0]);
+            if (version == BACKUP_MANIFEST_VERSION) {
+                offset = extractLine(buffer, offset, str);
+                final String pkg = str[0];
+                if (info.packageName.equals(pkg)) {
+                    // Data checks out -- the rest of the buffer is a concatenation of
+                    // binary blobs as described in the comment at writeAppWidgetData()
+                    ByteArrayInputStream bin = new ByteArrayInputStream(buffer,
+                            offset, buffer.length - offset);
+                    DataInputStream in = new DataInputStream(bin);
+                    while (bin.available() > 0) {
+                        int token = in.readInt();
+                        int size = in.readInt();
+                        if (size > 64 * 1024) {
+                            throw new IOException("Datum "
+                                    + Integer.toHexString(token)
+                                    + " too big; corrupt? size=" + info.size);
+                        }
+                        switch (token) {
+                            case BACKUP_WIDGET_METADATA_TOKEN:
+                            {
+                                if (MORE_DEBUG) {
+                                    Slog.i(TAG, "Got widget metadata for " + info.packageName);
+                                }
+                                mWidgetData = new byte[size];
+                                in.read(mWidgetData);
+                                break;
+                            }
+                            default:
+                            {
+                                if (DEBUG) {
+                                    Slog.i(TAG, "Ignoring metadata blob "
+                                            + Integer.toHexString(token)
+                                            + " for " + info.packageName);
+                                }
+                                in.skipBytes(size);
+                                break;
+                            }
+                        }
+                    }
+                } else {
+                    Slog.w(TAG, "Metadata mismatch: package " + info.packageName
+                            + " but widget data for " + pkg);
+                }
+            } else {
+                Slog.w(TAG, "Unsupported metadata version " + version);
+            }
+        }
+
         // Returns a policy constant; takes a buffer arg to reduce memory churn
         RestorePolicy readAppManifest(FileMetadata info, InputStream instream)
                 throws IOException {
@@ -4486,7 +4708,7 @@
         private PackageInfo mTargetPackage;
         private File mStateDir;
         private int mPmToken;
-        private boolean mNeedFullBackup;
+        private boolean mIsSystemRestore;
         private HashSet<String> mFilterSet;
         private long mStartRealtime;
         private PackageManagerBackupAgent mPmAgent;
@@ -4497,11 +4719,13 @@
         private boolean mFinished;
         private int mStatus;
         private File mBackupDataName;
+        private File mStageName;
         private File mNewStateName;
         private File mSavedStateName;
         private ParcelFileDescriptor mBackupData;
         private ParcelFileDescriptor mNewState;
         private PackageInfo mCurrentPackage;
+        private byte[] mWidgetData;
 
 
         class RestoreRequest {
@@ -4516,7 +4740,7 @@
 
         PerformRestoreTask(IBackupTransport transport, String dirName, IRestoreObserver observer,
                 long restoreSetToken, PackageInfo targetPackage, int pmToken,
-                boolean needFullBackup, String[] filterSet) {
+                boolean isSystemRestore, String[] filterSet) {
             mCurrentState = RestoreState.INITIAL;
             mFinished = false;
             mPmAgent = null;
@@ -4526,7 +4750,7 @@
             mToken = restoreSetToken;
             mTargetPackage = targetPackage;
             mPmToken = pmToken;
-            mNeedFullBackup = needFullBackup;
+            mIsSystemRestore = isSystemRestore;
 
             if (filterSet != null) {
                 mFilterSet = new HashSet<String>();
@@ -4696,8 +4920,7 @@
                 omPackage.packageName = PACKAGE_MANAGER_SENTINEL;
                 mPmAgent = new PackageManagerBackupAgent(
                         mPackageManager, mAgentPackages);
-                initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()),
-                        mNeedFullBackup);
+                initiateOneRestore(omPackage, 0, IBackupAgent.Stub.asInterface(mPmAgent.onBind()));
                 // The PM agent called operationComplete() already, because our invocation
                 // of it is process-local and therefore synchronous.  That means that a
                 // RUNNING_QUEUE message is already enqueued.  Only if we're unable to
@@ -4727,6 +4950,12 @@
 
             // Metadata is intact, so we can now run the restore queue.  If we get here,
             // we have already enqueued the necessary next-step message on the looper.
+            // We've deferred telling the App Widget service that we might be replacing
+            // the widget environment with something else, but now we know we've got
+            // data coming, so we do it here.
+            if (mIsSystemRestore) {
+                AppWidgetBackupBridge.restoreStarting(UserHandle.USER_OWNER);
+            }
         }
 
         void restoreNextAgent() {
@@ -4835,7 +5064,7 @@
 
                 // And then finally start the restore on this agent
                 try {
-                    initiateOneRestore(packageInfo, metaInfo.versionCode, agent, mNeedFullBackup);
+                    initiateOneRestore(packageInfo, metaInfo.versionCode, agent);
                     ++mCount;
                 } catch (Exception e) {
                     Slog.e(TAG, "Error when attempting restore: " + e.toString());
@@ -4889,6 +5118,9 @@
             mBackupHandler.sendEmptyMessageDelayed(MSG_RESTORE_TIMEOUT,
                     TIMEOUT_RESTORE_INTERVAL);
 
+            // Kick off any work that may be needed regarding app widget restores
+            AppWidgetBackupBridge.restoreFinished(UserHandle.USER_OWNER);
+
             // done; we can finally release the wakelock
             Slog.i(TAG, "Restore complete.");
             mWakelock.release();
@@ -4896,43 +5128,92 @@
 
         // Call asynchronously into the app, passing it the restore data.  The next step
         // after this is always a callback, either operationComplete() or handleTimeout().
-        void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent,
-                boolean needFullBackup) {
+        void initiateOneRestore(PackageInfo app, int appVersionCode, IBackupAgent agent) {
             mCurrentPackage = app;
+            mWidgetData = null;
             final String packageName = app.packageName;
 
             if (DEBUG) Slog.d(TAG, "initiateOneRestore packageName=" + packageName);
 
             // !!! TODO: get the dirs from the transport
             mBackupDataName = new File(mDataDir, packageName + ".restore");
+            mStageName = new File(mDataDir, packageName + ".stage");
             mNewStateName = new File(mStateDir, packageName + ".new");
             mSavedStateName = new File(mStateDir, packageName);
 
+            // don't stage the 'android' package where the wallpaper data lives.  this is
+            // an optimization: we know there's no widget data hosted/published by that
+            // package, and this way we avoid doing a spurious copy of MB-sized wallpaper
+            // data following the download.
+            boolean staging = !packageName.equals("android");
+            ParcelFileDescriptor stage;
+            File downloadFile = (staging) ? mStageName : mBackupDataName;
+
             final int token = generateToken();
             try {
                 // Run the transport's restore pass
-                mBackupData = ParcelFileDescriptor.open(mBackupDataName,
+                stage = ParcelFileDescriptor.open(downloadFile,
                             ParcelFileDescriptor.MODE_READ_WRITE |
                             ParcelFileDescriptor.MODE_CREATE |
                             ParcelFileDescriptor.MODE_TRUNCATE);
 
                 if (!SELinux.restorecon(mBackupDataName)) {
-                    Slog.e(TAG, "SElinux restorecon failed for " + mBackupDataName);
+                    Slog.e(TAG, "SElinux restorecon failed for " + downloadFile);
                 }
 
-                if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) {
+                if (mTransport.getRestoreData(stage) != BackupConstants.TRANSPORT_OK) {
                     // Transport-level failure, so we wind everything up and
                     // terminate the restore operation.
                     Slog.e(TAG, "Error getting restore data for " + packageName);
                     EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
-                    mBackupData.close();
-                    mBackupDataName.delete();
+                    stage.close();
+                    downloadFile.delete();
                     executeNextState(RestoreState.FINAL);
                     return;
                 }
 
+                // We have the data from the transport. Now we extract and strip
+                // any per-package metadata (typically widget-related information)
+                // if appropriate
+                if (staging) {
+                    stage.close();
+                    stage = ParcelFileDescriptor.open(downloadFile,
+                            ParcelFileDescriptor.MODE_READ_ONLY);
+
+                    mBackupData = ParcelFileDescriptor.open(mBackupDataName,
+                            ParcelFileDescriptor.MODE_READ_WRITE |
+                            ParcelFileDescriptor.MODE_CREATE |
+                            ParcelFileDescriptor.MODE_TRUNCATE);
+
+                    BackupDataInput in = new BackupDataInput(stage.getFileDescriptor());
+                    BackupDataOutput out = new BackupDataOutput(mBackupData.getFileDescriptor());
+                    byte[] buffer = new byte[8192]; // will grow when needed
+                    while (in.readNextHeader()) {
+                        final String key = in.getKey();
+                        final int size = in.getDataSize();
+
+                        // is this a special key?
+                        if (key.equals(KEY_WIDGET_STATE)) {
+                            if (DEBUG) {
+                                Slog.i(TAG, "Restoring widget state for " + packageName);
+                            }
+                            mWidgetData = new byte[size];
+                            in.readEntityData(mWidgetData, 0, size);
+                        } else {
+                            if (size > buffer.length) {
+                                buffer = new byte[size];
+                            }
+                            in.readEntityData(buffer, 0, size);
+                            out.writeEntityHeader(key, size);
+                            out.writeEntityData(buffer, size);
+                        }
+                    }
+
+                    mBackupData.close();
+                }
+
                 // Okay, we have the data.  Now have the agent do the restore.
-                mBackupData.close();
+                stage.close();
                 mBackupData = ParcelFileDescriptor.open(mBackupDataName,
                             ParcelFileDescriptor.MODE_READ_ONLY);
 
@@ -4943,7 +5224,8 @@
 
                 // Kick off the restore, checking for hung agents
                 prepareOperationTimeout(token, TIMEOUT_RESTORE_INTERVAL, this);
-                agent.doRestore(mBackupData, appVersionCode, mNewState, token, mBackupManagerBinder);
+                agent.doRestore(mBackupData, appVersionCode, mNewState,
+                        token, mBackupManagerBinder);
             } catch (Exception e) {
                 Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
                 EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName, e.toString());
@@ -4966,6 +5248,7 @@
 
         void agentCleanup() {
             mBackupDataName.delete();
+            mStageName.delete();
             try { if (mBackupData != null) mBackupData.close(); } catch (IOException e) {}
             try { if (mNewState != null) mNewState.close(); } catch (IOException e) {}
             mBackupData = mNewState = null;
@@ -5024,9 +5307,17 @@
         public void operationComplete() {
             int size = (int) mBackupDataName.length();
             EventLog.writeEvent(EventLogTags.RESTORE_PACKAGE, mCurrentPackage.packageName, size);
+
             // Just go back to running the restore queue
             agentCleanup();
 
+            // If there was widget state associated with this app, get the OS to
+            // incorporate it into current bookeeping and then pass that along to
+            // the app as part of the restore operation.
+            if (mWidgetData != null) {
+                restoreWidgetData(mCurrentPackage.packageName, mWidgetData);
+            }
+
             executeNextState(RestoreState.RUNNING_QUEUE);
         }
 
@@ -5050,6 +5341,12 @@
         }
     }
 
+    // Used by both incremental and full restore
+    void restoreWidgetData(String packageName, byte[] widgetData) {
+        // Apply the restored widget state and generate the ID update for the app
+        AppWidgetBackupBridge.restoreWidgetState(packageName, widgetData, UserHandle.USER_OWNER);
+    }
+
     class PerformClearTask implements Runnable {
         IBackupTransport mTransport;
         PackageInfo mPackage;
@@ -5345,8 +5642,9 @@
     // Run a *full* backup pass for the given package, writing the resulting data stream
     // to the supplied file descriptor.  This method is synchronous and does not return
     // to the caller until the backup has been completed.
+    @Override
     public void fullBackup(ParcelFileDescriptor fd, boolean includeApks,
-            boolean includeObbs, boolean includeShared,
+            boolean includeObbs, boolean includeShared, boolean doWidgets,
             boolean doAllApps, boolean includeSystem, String[] pkgList) {
         mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
 
@@ -5382,7 +5680,7 @@
             Slog.i(TAG, "Beginning full backup...");
 
             FullBackupParams params = new FullBackupParams(fd, includeApks, includeObbs,
-                    includeShared, doAllApps, includeSystem, pkgList);
+                    includeShared, doWidgets, doAllApps, includeSystem, pkgList);
             final int token = generateToken();
             synchronized (mFullConfirmations) {
                 mFullConfirmations.put(token, params);
@@ -5839,7 +6137,7 @@
                 mWakelock.acquire();
                 Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                 msg.obj = new RestoreParams(transport, dirName, null,
-                        restoreSet, pkg, token, true);
+                        restoreSet, pkg, token);
                 mBackupHandler.sendMessage(msg);
             } catch (RemoteException e) {
                 // Binding to the transport broke; back off and proceed with the installation.
@@ -6020,7 +6318,7 @@
                         mWakelock.acquire();
                         Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                         msg.obj = new RestoreParams(mRestoreTransport, dirName,
-                                observer, token, true);
+                                observer, token);
                         mBackupHandler.sendMessage(msg);
                         Binder.restoreCallingIdentity(oldId);
                         return 0;
@@ -6032,6 +6330,7 @@
             return -1;
         }
 
+        // Restores of more than a single package are treated as 'system' restores
         public synchronized int restoreSome(long token, IRestoreObserver observer,
                 String[] packages) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -6090,7 +6389,7 @@
                         mWakelock.acquire();
                         Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
                         msg.obj = new RestoreParams(mRestoreTransport, dirName, observer, token,
-                                packages, true);
+                                packages, packages.length > 1);
                         mBackupHandler.sendMessage(msg);
                         Binder.restoreCallingIdentity(oldId);
                         return 0;
@@ -6169,7 +6468,7 @@
             mWakelock.acquire();
             Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
             msg.obj = new RestoreParams(mRestoreTransport, dirName,
-                    observer, token, app, 0, false);
+                    observer, token, app, 0);
             mBackupHandler.sendMessage(msg);
             Binder.restoreCallingIdentity(oldId);
             return 0;
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 9de3efe..41ffdc1 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -103,6 +103,7 @@
     private long mNextNonWakeup;
     int mBroadcastRefCount = 0;
     PowerManager.WakeLock mWakeLock;
+    boolean mLastWakeLockUnimportantForLogging;
     ArrayList<InFlight> mInFlight = new ArrayList<InFlight>();
     final AlarmHandler mHandler = new AlarmHandler();
     ClockReceiver mClockReceiver;
@@ -1345,17 +1346,21 @@
      */
     void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, boolean first) {
         try {
-            mWakeLock.setUnimportantForLogging(pi == mTimeTickSender);
+            final boolean unimportant = pi == mTimeTickSender;
+            mWakeLock.setUnimportantForLogging(unimportant);
             if (ws != null) {
-                if (first) {
+                if (first || mLastWakeLockUnimportantForLogging) {
                     mWakeLock.setHistoryTag(pi.getTag(
                             type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
                                     ? "*walarm*:" : "*alarm*:"));
                 } else {
                     mWakeLock.setHistoryTag(null);
                 }
+                mLastWakeLockUnimportantForLogging = unimportant;
                 mWakeLock.setWorkSource(ws);
                 return;
+            } else {
+                mLastWakeLockUnimportantForLogging = false;
             }
 
             final int uid = ActivityManagerNative.getDefault()
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index e5615c0..e26747c 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -33,6 +33,7 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.media.AudioService;
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Handler;
@@ -42,6 +43,7 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Pair;
@@ -123,6 +125,8 @@
             = new ArrayMap<String, ArrayList<Callback>>();
     final ArrayMap<IBinder, Callback> mModeWatchers
             = new ArrayMap<IBinder, Callback>();
+    final SparseArray<SparseArray<Restriction>> mAudioRestrictions
+            = new SparseArray<SparseArray<Restriction>>();
 
     public final class Callback implements DeathRecipient {
         final IAppOpsCallback mCallback;
@@ -553,6 +557,58 @@
     }
 
     @Override
+    public int checkAudioOperation(int code, int stream, int uid, String packageName) {
+        synchronized (this) {
+            final int mode = checkRestrictionLocked(code, stream, uid, packageName);
+            if (mode != AppOpsManager.MODE_ALLOWED) {
+                return mode;
+            }
+        }
+        return checkOperation(code, uid, packageName);
+    }
+
+    private int checkRestrictionLocked(int code, int stream, int uid, String packageName) {
+        final SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code);
+        if (streamRestrictions != null) {
+            final Restriction r = streamRestrictions.get(stream);
+            if (r != null && !r.exceptionPackages.contains(packageName)) {
+                return r.mode;
+            }
+        }
+        return AppOpsManager.MODE_ALLOWED;
+    }
+
+    @Override
+    public void setAudioRestriction(int code, int stream, int uid, int mode,
+            String[] exceptionPackages) {
+        verifyIncomingUid(uid);
+        verifyIncomingOp(code);
+        synchronized (this) {
+            SparseArray<Restriction> streamRestrictions = mAudioRestrictions.get(code);
+            if (streamRestrictions == null) {
+                streamRestrictions = new SparseArray<Restriction>();
+                mAudioRestrictions.put(code, streamRestrictions);
+            }
+            streamRestrictions.remove(stream);
+            if (mode != AppOpsManager.MODE_ALLOWED) {
+                final Restriction r = new Restriction();
+                r.mode = mode;
+                if (exceptionPackages != null) {
+                    final int N = exceptionPackages.length;
+                    r.exceptionPackages = new ArraySet<String>(N);
+                    for (int i = 0; i < N; i++) {
+                        final String pkg = exceptionPackages[i];
+                        if (pkg != null) {
+                            r.exceptionPackages.add(pkg.trim());
+                        }
+                    }
+                }
+                streamRestrictions.put(stream, r);
+            }
+        }
+    }
+
+    @Override
     public int checkPackage(int uid, String packageName) {
         synchronized (this) {
             if (getOpsLocked(uid, packageName, true) != null) {
@@ -1048,6 +1104,31 @@
                     }
                 }
             }
+            if (mAudioRestrictions.size() > 0) {
+                boolean printedHeader = false;
+                for (int o=0; o<mAudioRestrictions.size(); o++) {
+                    final String op = AppOpsManager.opToName(mAudioRestrictions.keyAt(o));
+                    final SparseArray<Restriction> restrictions = mAudioRestrictions.valueAt(o);
+                    for (int i=0; i<restrictions.size(); i++) {
+                        if (!printedHeader){
+                            pw.println("  Audio Restrictions:");
+                            printedHeader = true;
+                            needSep = true;
+                        }
+                        final int stream = restrictions.keyAt(i);
+                        pw.print("    "); pw.print(op);
+                        pw.print(" stream="); pw.print(AudioService.streamToString(stream));
+                        Restriction r = restrictions.valueAt(i);
+                        pw.print(": mode="); pw.println(r.mode);
+                        if (!r.exceptionPackages.isEmpty()) {
+                            pw.println("      Exceptions:");
+                            for (int j=0; j<r.exceptionPackages.size(); j++) {
+                                pw.print("        "); pw.println(r.exceptionPackages.valueAt(j));
+                            }
+                        }
+                    }
+                }
+            }
             if (needSep) {
                 pw.println();
             }
@@ -1080,4 +1161,10 @@
             }
         }
     }
+
+    private static final class Restriction {
+        private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>();
+        int mode;
+        ArraySet<String> exceptionPackages = NO_EXCEPTIONS;
+    }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 68b779c..d2513d3 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2434,7 +2434,9 @@
         if (timeout > 0 && iface != null) {
             try {
                 mNetd.addIdleTimer(iface, timeout, type);
-            } catch (RemoteException e) {
+            } catch (Exception e) {
+                // You shall not crash!
+                loge("Exception in setupDataActivityTracking " + e);
             }
         }
     }
@@ -2451,7 +2453,8 @@
             try {
                 // the call fails silently if no idletimer setup for this interface
                 mNetd.removeIdleTimer(iface);
-            } catch (RemoteException e) {
+            } catch (Exception e) {
+                loge("Exception in removeDataActivityTracking " + e);
             }
         }
     }
@@ -4041,6 +4044,14 @@
      */
     private static final int CMP_RESULT_CODE_PROVISIONING_NETWORK = 5;
 
+    /**
+     * The mobile network is provisioning
+     */
+    private static final int CMP_RESULT_CODE_IS_PROVISIONING = 6;
+
+    private AtomicBoolean mIsProvisioningNetwork = new AtomicBoolean(false);
+    private AtomicBoolean mIsStartingProvisioning = new AtomicBoolean(false);
+
     private AtomicBoolean mIsCheckingMobileProvisioning = new AtomicBoolean(false);
 
     @Override
@@ -4111,11 +4122,25 @@
                                 setProvNotificationVisible(true,
                                         ConnectivityManager.TYPE_MOBILE_HIPRI, ni.getExtraInfo(),
                                         url);
+                                // Mark that we've got a provisioning network and
+                                // Disable Mobile Data until user actually starts provisioning.
+                                mIsProvisioningNetwork.set(true);
+                                MobileDataStateTracker mdst = (MobileDataStateTracker)
+                                        mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+                                mdst.setInternalDataEnable(false);
                             } else {
                                 if (DBG) log("CheckMp.onComplete: warm (no dns/tcp), no url");
                             }
                             break;
                         }
+                        case CMP_RESULT_CODE_IS_PROVISIONING: {
+                            // FIXME: Need to know when provisioning is done. Probably we can
+                            // check the completion status if successful we're done if we
+                            // "timedout" or still connected to provisioning APN turn off data?
+                            if (DBG) log("CheckMp.onComplete: provisioning started");
+                            mIsStartingProvisioning.set(false);
+                            break;
+                        }
                         default: {
                             loge("CheckMp.onComplete: ignore unexpected result=" + result);
                             break;
@@ -4265,6 +4290,12 @@
                 return result;
             }
 
+            if (mCs.mIsStartingProvisioning.get()) {
+                result = CMP_RESULT_CODE_IS_PROVISIONING;
+                log("isMobileOk: X is provisioning result=" + result);
+                return result;
+            }
+
             // See if we've already determined we've got a provisioning connection,
             // if so we don't need to do anything active.
             MobileDataStateTracker mdstDefault = (MobileDataStateTracker)
@@ -4602,19 +4633,20 @@
     };
 
     private void handleMobileProvisioningAction(String url) {
-        // Notication mark notification as not visible
+        // Mark notification as not visible
         setProvNotificationVisible(false, ConnectivityManager.TYPE_MOBILE_HIPRI, null, null);
 
         // If provisioning network handle as a special case,
         // otherwise launch browser with the intent directly.
-        NetworkInfo ni = getProvisioningNetworkInfo();
-        if ((ni != null) && ni.isConnectedToProvisioningNetwork()) {
-            if (DBG) log("handleMobileProvisioningAction: on provisioning network");
+        if (mIsProvisioningNetwork.get()) {
+            if (DBG) log("handleMobileProvisioningAction: on prov network enable then launch");
+            mIsStartingProvisioning.set(true);
             MobileDataStateTracker mdst = (MobileDataStateTracker)
                     mNetTrackers[ConnectivityManager.TYPE_MOBILE];
+            mdst.setEnableFailFastMobileData(DctConstants.ENABLED);
             mdst.enableMobileProvisioning(url);
         } else {
-            if (DBG) log("handleMobileProvisioningAction: on default network");
+            if (DBG) log("handleMobileProvisioningAction: not prov network");
             // Check for  apps that can handle provisioning first
             Intent provisioningIntent = new Intent(TelephonyIntents.ACTION_CARRIER_SETUP);
             provisioningIntent.addCategory(TelephonyIntents.CATEGORY_MCCMNC_PREFIX
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index af8a6e7..31822e7 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -637,7 +637,8 @@
         mImeSwitcherNotification.vibrate = null;
 
         // Tag this notification specially so SystemUI knows it's important
-        mImeSwitcherNotification.kind = new String[] { "android.system.imeswitcher" };
+        mImeSwitcherNotification.extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);
+        mImeSwitcherNotification.category = Notification.CATEGORY_SYSTEM;
 
         Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
         mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index fe814fc..19e8083 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -30,7 +30,11 @@
 import android.database.sqlite.SQLiteStatement;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.storage.IMountService;
+import android.os.ServiceManager;
+import android.os.storage.StorageManager;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -79,6 +83,7 @@
 
     private final Context mContext;
     private LockPatternUtils mLockPatternUtils;
+    private boolean mFirstCallToVold;
 
     public LockSettingsService(Context context) {
         mContext = context;
@@ -86,6 +91,7 @@
         mOpenHelper = new DatabaseHelper(mContext);
 
         mLockPatternUtils = new LockPatternUtils(context);
+        mFirstCallToVold = true;
     }
 
     public void systemReady() {
@@ -347,6 +353,33 @@
     }
 
     @Override
+    public boolean checkVoldPassword(int userId) throws RemoteException {
+        if (!mFirstCallToVold) {
+            return false;
+        }
+        mFirstCallToVold = false;
+
+        checkPasswordReadPermission(userId);
+
+        // There's no guarantee that this will safely connect, but if it fails
+        // we will simply show the lock screen when we shouldn't, so relatively
+        // benign. There is an outside chance something nasty would happen if
+        // this service restarted before vold stales out the password in this
+        // case. The nastiness is limited to not showing the lock screen when
+        // we should, within the first minute of decrypting the phone if this
+        // service can't connect to vold, it restarts, and then the new instance
+        // does successfully connect.
+        final IMountService service = getMountService();
+        String password = service.getPassword();
+        service.clearPassword();
+        if (service.getPasswordType() == StorageManager.CRYPT_TYPE_PATTERN) {
+            return checkPattern(password, userId);
+        } else {
+            return checkPassword(password, userId);
+        }
+    }
+
+    @Override
     public void removeUser(int userId) {
         checkWritePermission(userId);
 
@@ -524,4 +557,12 @@
         Secure.LOCK_SCREEN_OWNER_INFO_ENABLED,
         Secure.LOCK_SCREEN_OWNER_INFO
     };
+
+    private IMountService getMountService() {
+        final IBinder service = ServiceManager.getService("mount");
+        if (service != null) {
+            return IMountService.Stub.asInterface(service);
+        }
+        return null;
+    }
 }
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index cd74fed..b6e0d5f 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -74,6 +74,7 @@
 import com.google.android.collect.Maps;
 
 import org.apache.commons.codec.binary.Hex;
+import org.apache.commons.codec.DecoderException;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
@@ -573,6 +574,14 @@
         }
     }
 
+    private boolean isReady() {
+        try {
+            return mConnectedSignal.await(0, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            return false;
+        }
+    }
+
     private void handleSystemReady() {
         // Snapshot current volume states since it's not safe to call into vold
         // while holding locks.
@@ -2081,6 +2090,19 @@
         return new String(Hex.encodeHex(bytes));
     }
 
+    private String fromHex(String hexPassword) {
+        if (hexPassword == null) {
+            return null;
+        }
+
+        try {
+            byte[] bytes = Hex.decodeHex(hexPassword.toCharArray());
+            return new String(bytes, StandardCharsets.UTF_8);
+        } catch (DecoderException e) {
+            return null;
+        }
+    }
+
     @Override
     public int decryptStorage(String password) {
         if (TextUtils.isEmpty(password)) {
@@ -2230,6 +2252,35 @@
     }
 
     @Override
+    public String getPassword() throws RemoteException {
+        if (!isReady()) {
+            return new String();
+        }
+
+        final NativeDaemonEvent event;
+        try {
+            event = mConnector.execute("cryptfs", "getpw");
+            return fromHex(event.getMessage());
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public void clearPassword() throws RemoteException {
+        if (!isReady()) {
+            return;
+        }
+
+        final NativeDaemonEvent event;
+        try {
+            event = mConnector.execute("cryptfs", "clearpw");
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
     public int mkdirs(String callingPkg, String appPath) {
         final int userId = UserHandle.getUserId(Binder.getCallingUid());
         final UserEnvironment userEnv = new UserEnvironment(userId);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index a09d605..5273cec 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -348,7 +348,7 @@
      */
     private void notifyInterfaceClassActivity(int type, boolean active, long tsNanos) {
         try {
-            getBatteryStats().noteDataConnectionActive(type, active);
+            getBatteryStats().noteDataConnectionActive(type, active, tsNanos);
         } catch (RemoteException e) {
         }
 
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 28eb948..aa756a1 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -84,24 +84,27 @@
         private final long    mStartTime;
         private final long[]  mPattern;
         private final int     mRepeat;
+        private final int     mStreamHint;
         private final int     mUid;
         private final String  mPackageName;
 
-        Vibration(IBinder token, long millis, int uid, String packageName) {
-            this(token, millis, null, 0, uid, packageName);
+        Vibration(IBinder token, long millis, int streamHint, int uid, String packageName) {
+            this(token, millis, null, 0, streamHint, uid, packageName);
         }
 
-        Vibration(IBinder token, long[] pattern, int repeat, int uid, String packageName) {
-            this(token, 0, pattern, repeat, uid, packageName);
+        Vibration(IBinder token, long[] pattern, int repeat, int streamHint, int uid,
+                String packageName) {
+            this(token, 0, pattern, repeat, streamHint, uid, packageName);
         }
 
         private Vibration(IBinder token, long millis, long[] pattern,
-                int repeat, int uid, String packageName) {
+                int repeat, int streamHint, int uid, String packageName) {
             mToken = token;
             mTimeout = millis;
             mStartTime = SystemClock.uptimeMillis();
             mPattern = pattern;
             mRepeat = repeat;
+            mStreamHint = streamHint;
             mUid = uid;
             mPackageName = packageName;
         }
@@ -191,7 +194,8 @@
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
     }
 
-    public void vibrate(int uid, String packageName, long milliseconds, IBinder token) {
+    public void vibrate(int uid, String packageName, long milliseconds, int streamHint,
+            IBinder token) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
@@ -207,7 +211,7 @@
             return;
         }
 
-        Vibration vib = new Vibration(token, milliseconds, uid, packageName);
+        Vibration vib = new Vibration(token, milliseconds, streamHint, uid, packageName);
 
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -233,7 +237,7 @@
     }
 
     public void vibratePattern(int uid, String packageName, long[] pattern, int repeat,
-            IBinder token) {
+            int streamHint, IBinder token) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires VIBRATE permission");
@@ -258,7 +262,7 @@
                 return;
             }
 
-            Vibration vib = new Vibration(token, pattern, repeat, uid, packageName);
+            Vibration vib = new Vibration(token, pattern, repeat, streamHint, uid, packageName);
             try {
                 token.linkToDeath(vib, 0);
             } catch (RemoteException e) {
@@ -342,8 +346,12 @@
     // Lock held on mVibrations
     private void startVibrationLocked(final Vibration vib) {
         try {
-            int mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
+            int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
+                    vib.mStreamHint, vib.mUid, vib.mPackageName);
+            if (mode == AppOpsManager.MODE_ALLOWED) {
+                mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
                     AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
+            }
             if (mode != AppOpsManager.MODE_ALLOWED) {
                 if (mode == AppOpsManager.MODE_ERRORED) {
                     Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
@@ -354,7 +362,7 @@
         } catch (RemoteException e) {
         }
         if (vib.mTimeout != 0) {
-            doVibratorOn(vib.mTimeout, vib.mUid);
+            doVibratorOn(vib.mTimeout, vib.mUid, vib.mStreamHint);
             mH.postDelayed(mVibrationRunnable, vib.mTimeout);
         } else {
             // mThread better be null here. doCancelVibrate should always be
@@ -473,7 +481,7 @@
         return vibratorExists();
     }
 
-    private void doVibratorOn(long millis, int uid) {
+    private void doVibratorOn(long millis, int uid, int streamHint) {
         synchronized (mInputDeviceVibrators) {
             try {
                 mBatteryStatsService.noteVibratorOn(uid, millis);
@@ -483,7 +491,7 @@
             final int vibratorCount = mInputDeviceVibrators.size();
             if (vibratorCount != 0) {
                 for (int i = 0; i < vibratorCount; i++) {
-                    mInputDeviceVibrators.get(i).vibrate(millis);
+                    mInputDeviceVibrators.get(i).vibrate(millis, streamHint);
                 }
             } else {
                 vibratorOn(millis);
@@ -546,6 +554,7 @@
                 final int len = pattern.length;
                 final int repeat = mVibration.mRepeat;
                 final int uid = mVibration.mUid;
+                final int streamHint = mVibration.mStreamHint;
                 int index = 0;
                 long duration = 0;
 
@@ -566,7 +575,7 @@
                         // duration is saved for delay() at top of loop
                         duration = pattern[index++];
                         if (duration > 0) {
-                            VibratorService.this.doVibratorOn(duration, uid);
+                            VibratorService.this.doVibratorOn(duration, uid, streamHint);
                         }
                     } else {
                         if (repeat < 0) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 1345cfd..033b967 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2087,10 +2087,12 @@
 
             // Sanity check: if the service listed for the app is not one
             // we actually are maintaining, just let it drop.
-            if (smap.mServicesByName.get(sr.name) != sr) {
-                ServiceRecord cur = smap.mServicesByName.get(sr.name);
-                Slog.wtf(TAG, "Service " + sr + " in process " + app
-                        + " not same as in map: " + cur);
+            final ServiceRecord curRec = smap.mServicesByName.get(sr.name);
+            if (curRec != sr) {
+                if (curRec != null) {
+                    Slog.wtf(TAG, "Service " + sr + " in process " + app
+                            + " not same as in map: " + curRec);
+                }
                 continue;
             }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5500b9d..239e423 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6820,7 +6820,7 @@
             }
         }
 
-        if (pending == null && receiver != null) {
+        if (pending.pendingRecords.isEmpty() && receiver != null) {
             // In this case all thumbnails were available and the client
             // is being asked to be told when the remaining ones come in...
             // which is unusually, since the top-most currently running
@@ -6895,6 +6895,21 @@
                     rti.stackId = tr.stack.mStackId;
                     rti.userId = tr.userId;
 
+                    final ArrayList<ActivityRecord> activities = tr.mActivities;
+                    int numSet = 0;
+                    for (int activityNdx = activities.size() - 1; activityNdx >= 0 && numSet < 2;
+                            --activityNdx) {
+                        final ActivityRecord r = activities.get(activityNdx);
+                        if (rti.activityLabel == null && r.recentsLabel != null) {
+                            rti.activityLabel = r.recentsLabel;
+                            ++numSet;
+                        }
+                        if (rti.activityIcon == null && r.recentsIcon != null) {
+                            rti.activityIcon = r.recentsIcon;
+                            ++numSet;
+                        }
+                    }
+
                     if ((flags&ActivityManager.RECENT_IGNORE_UNAVAILABLE) != 0) {
                         // Check whether this activity is currently available.
                         try {
@@ -6960,6 +6975,26 @@
     }
 
     @Override
+    public void setRecentsLabel(IBinder token, CharSequence recentsLabel) {
+        synchronized (this) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.recentsLabel = recentsLabel.toString();
+            }
+        }
+    }
+
+    @Override
+    public void setRecentsIcon(IBinder token, Bitmap recentsIcon) {
+        synchronized (this) {
+            ActivityRecord r = ActivityRecord.isInStackLocked(token);
+            if (r != null) {
+                r.recentsIcon = recentsIcon;
+            }
+        }
+    }
+
+    @Override
     public boolean removeSubTask(int taskId, int subTaskIndex) {
         synchronized (this) {
             enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 329be5d..77f5c59 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -148,6 +148,9 @@
     boolean mStartingWindowShown = false;
     ActivityContainer mInitialActivityContainer;
 
+    String recentsLabel;
+    Bitmap recentsIcon;
+
     void dump(PrintWriter pw, String prefix) {
         final long now = SystemClock.uptimeMillis();
         pw.print(prefix); pw.print("packageName="); pw.print(packageName);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 07f76bd..47b4d0a 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1778,6 +1778,7 @@
                 return ActivityManager.START_RETURN_LOCK_TASK_MODE_VIOLATION;
             }
             targetStack = sourceTask.stack;
+            targetStack.moveToFront();
             mWindowManager.moveTaskToTop(sourceTask.taskId);
             if (!addingToTask &&
                     (launchFlags&Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
@@ -1829,6 +1830,7 @@
             // of a new task...  just put it in the top task, though these days
             // this case should never happen.
             targetStack = adjustStackFocus(r);
+            targetStack.moveToFront();
             ActivityRecord prev = targetStack.topActivity();
             r.setTask(prev != null ? prev.task
                     : targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 3414daf..0ddb827 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -135,14 +135,15 @@
         enforceCallingPermission();
         synchronized (mStats) {
             mStats.noteStartWakeLocked(uid, pid, name, historyName, type, unimportantForLogging,
-                    SystemClock.elapsedRealtime());
+                    SystemClock.elapsedRealtime(), SystemClock.uptimeMillis());
         }
     }
 
     public void noteStopWakelock(int uid, int pid, String name, int type) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteStopWakeLocked(uid, pid, name, type, SystemClock.elapsedRealtime());
+            mStats.noteStopWakeLocked(uid, pid, name, type, SystemClock.elapsedRealtime(),
+                    SystemClock.uptimeMillis());
         }
     }
 
@@ -247,10 +248,10 @@
         }
     }
 
-    public void noteDataConnectionActive(int type, boolean active) {
+    public void noteDataConnectionActive(int type, boolean active, long timestampNs) {
         enforceCallingPermission();
         synchronized (mStats) {
-            mStats.noteDataConnectionActive(type, active);
+            mStats.noteDataConnectionActive(type, active, timestampNs);
         }
     }
 
@@ -569,9 +570,12 @@
                 int num;
                 while ((num=nativeWaitWakeup(mIrqs, mReasons)) >= 0) {
                     synchronized (mStats) {
-                        for (int i=0; i<num; i++) {
-                            //Slog.i(TAG, "Wakeup: irq #" + mIrqs[i] + " reason=" + mReasons[i]);
-                            mStats.noteWakeupReasonLocked(mIrqs[i], mReasons[i]);
+                        if (num > 0) {
+                            for (int i=0; i<num; i++) {
+                                mStats.noteWakeupReasonLocked(mReasons[i]);
+                            }
+                        } else {
+                            mStats.noteWakeupReasonLocked("unknown");
                         }
                     }
                 }
@@ -652,7 +656,7 @@
                     dumpHelp(pw);
                     return;
                 } else if ("-a".equals(arg)) {
-                    // fall through
+                    flags |= BatteryStats.DUMP_VERBOSE;
                 } else if (arg.length() > 0 && arg.charAt(0) == '-'){
                     pw.println("Unknown option: " + arg);
                     dumpHelp(pw);
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index e43dea9..026fd296 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2450,7 +2450,7 @@
                             Log.v(TAG, "canceling and rescheduling sync since an initialization "
                                     + "takes higher priority, " + conflict);
                         }
-                    } else if (candidate.expedited && !conflict.mSyncOperation.expedited
+                    } else if (candidate.isExpedited() && !conflict.mSyncOperation.isExpedited()
                             && (candidateIsInitialization
                                 == conflict.mSyncOperation.isInitialization())) {
                         toReschedule = conflict;
diff --git a/services/core/java/com/android/server/content/SyncOperation.java b/services/core/java/com/android/server/content/SyncOperation.java
index 5233014..9a4abce 100644
--- a/services/core/java/com/android/server/content/SyncOperation.java
+++ b/services/core/java/com/android/server/content/SyncOperation.java
@@ -65,17 +65,18 @@
     /** Why this sync was kicked off. {@link #REASON_NAMES} */
     public final int reason;
     /** Where this sync was initiated. */
-    public int syncSource;
+    public final int syncSource;
     public final boolean allowParallelSyncs;
-    public Bundle extras;
     public final String key;
-    public boolean expedited;
+    /** Internal boolean to avoid reading a bundle everytime we want to compare operations. */
+    private final boolean expedited;
+    public Bundle extras;
     /** Bare-bones version of this operation that is persisted across reboots. */
     public SyncStorageEngine.PendingOperation pendingOperation;
     /** Elapsed real time in millis at which to run this sync. */
     public long latestRunTime;
     /** Set by the SyncManager in order to delay retries. */
-    public Long backoff;
+    public long backoff;
     /** Specified by the adapter to delay subsequent sync operations. */
     public long delayUntil;
     /**
@@ -92,61 +93,58 @@
     public SyncOperation(Account account, int userId, int reason, int source, String provider,
             Bundle extras, long runTimeFromNow, long flexTime, long backoff,
             long delayUntil, boolean allowParallelSyncs) {
-        this.target = new SyncStorageEngine.EndPoint(account, provider, userId);
-        this.reason = reason;
-        this.allowParallelSyncs = allowParallelSyncs;
-        this.key = initialiseOperation(this.target, source, extras, runTimeFromNow, flexTime,
-                backoff, delayUntil);
+        this(new SyncStorageEngine.EndPoint(account, provider, userId),
+                reason, source, extras, runTimeFromNow, flexTime, backoff, delayUntil,
+                allowParallelSyncs);
     }
 
     public SyncOperation(ComponentName service, int userId, int reason, int source,
             Bundle extras, long runTimeFromNow, long flexTime, long backoff,
             long delayUntil) {
-        this.target = new SyncStorageEngine.EndPoint(service, userId);
-        // Default to true for sync service. The service itself decides how to handle this.
-        this.allowParallelSyncs = true;
+        this(new SyncStorageEngine.EndPoint(service, userId), reason, source, extras,
+                runTimeFromNow, flexTime, backoff, delayUntil, true /* allowParallelSyncs */);
+    }
+
+    private SyncOperation(SyncStorageEngine.EndPoint info, int reason, int source, Bundle extras,
+            long runTimeFromNow, long flexTime, long backoff, long delayUntil,
+            boolean allowParallelSyncs) {
+        this.target = info;
         this.reason = reason;
-        this.key =
-                initialiseOperation(this.target,
-                        source, extras, runTimeFromNow, flexTime, backoff, delayUntil);
-    }
-
-    /** Used to reschedule a sync at a new point in time. */
-    SyncOperation(SyncOperation other, long newRunTimeFromNow) {
-        this.target = other.target;
-        this.reason = other.reason;
-        this.expedited = other.expedited;
-        this.allowParallelSyncs = other.allowParallelSyncs;
-        // re-use old flex, but only 
-        long newFlexTime = Math.min(other.flexTime, newRunTimeFromNow);
-        this.key =
-                initialiseOperation(this.target,
-                    other.syncSource, other.extras,
-                    newRunTimeFromNow /* runTimeFromNow*/,
-                    newFlexTime /* flexTime */,
-                    other.backoff,
-                    0L /* delayUntil */);
-    }
-
-    private String initialiseOperation(SyncStorageEngine.EndPoint info, int source, Bundle extras,
-            long runTimeFromNow, long flexTime, long backoff, long delayUntil) {
         this.syncSource = source;
         this.extras = new Bundle(extras);
         cleanBundle(this.extras);
         this.delayUntil = delayUntil;
         this.backoff = backoff;
+        this.allowParallelSyncs = allowParallelSyncs;
         final long now = SystemClock.elapsedRealtime();
-        if (runTimeFromNow < 0 || isExpedited()) {
+        // Set expedited based on runTimeFromNow. The SyncManager specifies whether the op is
+        // expedited (Not done solely based on bundle).
+        if (runTimeFromNow < 0) {
             this.expedited = true;
+            // Sanity check: Will always be true.
+            if (!this.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false)) {
+                this.extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+            }
             this.latestRunTime = now;
             this.flexTime = 0;
         } else {
             this.expedited = false;
+            this.extras.remove(ContentResolver.SYNC_EXTRAS_EXPEDITED);
             this.latestRunTime = now + runTimeFromNow;
             this.flexTime = flexTime;
         }
         updateEffectiveRunTime();
-        return toKey(info, this.extras);
+        this.key = toKey(info, this.extras);
+    }
+
+    /** Used to reschedule a sync at a new point in time. */
+    public SyncOperation(SyncOperation other, long newRunTimeFromNow) {
+        this(other.target, other.reason, other.syncSource, new Bundle(other.extras),
+                newRunTimeFromNow,
+                0L /* In back-off so no flex */,
+                other.backoff,
+                other.delayUntil,
+                other.allowParallelSyncs);
     }
 
     public boolean matchesAuthority(SyncOperation other) {
@@ -264,7 +262,7 @@
     }
 
     public boolean isExpedited() {
-        return extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false) || expedited;
+        return expedited;
     }
 
     public boolean ignoreBackoff() {
diff --git a/services/core/java/com/android/server/content/SyncQueue.java b/services/core/java/com/android/server/content/SyncQueue.java
index 5d93882..587de1c 100644
--- a/services/core/java/com/android/server/content/SyncQueue.java
+++ b/services/core/java/com/android/server/content/SyncQueue.java
@@ -76,12 +76,11 @@
                 operationToAdd = new SyncOperation(
                         info.account, info.userId, op.reason, op.syncSource, info.provider,
                         op.extras,
-                        0 /* delay */,
+                        op.expedited ? -1 : 0 /* delay */,
                         0 /* flex */,
-                        backoff != null ? backoff.first : 0,
+                        backoff != null ? backoff.first : 0L,
                         mSyncStorageEngine.getDelayUntilTime(info),
                         syncAdapterInfo.type.allowParallelSyncs());
-                operationToAdd.expedited = op.expedited;
                 operationToAdd.pendingOperation = op;
                 add(operationToAdd, op);
             } else if (info.target_service) {
@@ -96,11 +95,10 @@
                 operationToAdd = new SyncOperation(
                         info.service, info.userId, op.reason, op.syncSource,
                         op.extras,
-                        0 /* delay */,
+                        op.expedited ? -1 : 0 /* delay */,
                         0 /* flex */,
                         backoff != null ? backoff.first : 0,
                         mSyncStorageEngine.getDelayUntilTime(info));
-                operationToAdd.expedited = op.expedited;
                 operationToAdd.pendingOperation = op;
                 add(operationToAdd, op);
             }
@@ -129,7 +127,6 @@
         if (existingOperation != null) {
             boolean changed = false;
             if (operation.compareTo(existingOperation) <= 0 ) {
-                existingOperation.expedited = operation.expedited;
                 long newRunTime =
                         Math.min(existingOperation.latestRunTime, operation.latestRunTime);
                 // Take smaller runtime.
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 5df7fc6..3a2e4db 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -1088,7 +1088,7 @@
             }
 
             pop = new PendingOperation(authority, op.reason, op.syncSource, op.extras,
-                    op.expedited);
+                    op.isExpedited());
             mPendingOperations.add(pop);
             appendPendingOperationLocked(pop);
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
new file mode 100644
index 0000000..9916435
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecDevice.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014 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.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiCecListener;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CecDevice class represents a CEC logical device characterized
+ * by its device type. A physical device can contain the functions of
+ * more than one logical device, in which case it should create
+ * as many logical devices as necessary.
+ *
+ * <p>Note that if a physical device has multiple instances of a particular
+ * functionality, it should advertize only one instance. For instance, if
+ * a device has multiple tuners, it should only expose one for control
+ * via CEC. In this case, it is up to the device itself to manage multiple tuners.
+ *
+ * <p>The version of HDMI-CEC protocol supported in this class is 1.3a.
+ *
+ * <p>Declared as package-private, accessed by HdmiCecService only.
+ */
+final class HdmiCecDevice {
+    private static final String TAG = "HdmiCecDevice";
+
+    private final int mType;
+
+    // List of listeners to the message/event coming to the device.
+    private final List<IHdmiCecListener> mListeners = new ArrayList<IHdmiCecListener>();
+    private final Binder mBinder = new Binder();
+
+    private String mName;
+    private boolean mIsActiveSource;
+
+    /**
+     * Constructor.
+     */
+    public HdmiCecDevice(int type) {
+        mType = type;
+        mIsActiveSource = false;
+    }
+
+    /**
+     * Return the binder token that identifies this instance.
+     */
+    public Binder getToken() {
+        return mBinder;
+    }
+
+    /**
+     * Return the type of this device.
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Set the name of the device. The name will be transferred via the message
+     * &lt;Set OSD Name&gt; to other HDMI-CEC devices connected through HDMI
+     * cables and shown on TV screen to identify the devicie.
+     *
+     * @param name name of the device
+     */
+    public void setName(String name) {
+        mName = name;
+    }
+
+    /**
+     * Return the name of this device.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Register a listener to be invoked when events occur.
+     *
+     * @param listener the listern that will run
+     */
+    public void addListener(IHdmiCecListener listener) {
+        mListeners.add(listener);
+    }
+
+    /**
+     * Remove the listener that was previously registered.
+     *
+     * @param listener IHdmiCecListener instance to be removed
+     */
+    public void removeListener(IHdmiCecListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * Indicate if the device has listeners.
+     *
+     * @return true if there are listener instances for this device
+     */
+    public boolean hasListener() {
+        return !mListeners.isEmpty();
+    }
+
+    /**
+     * Handle HDMI-CEC message coming to the device by invoking the registered
+     * listeners.
+     */
+    public void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
+        if (opcode == HdmiCec.MESSAGE_ACTIVE_SOURCE) {
+            mIsActiveSource = false;
+        }
+        if (mListeners.size() == 0) {
+            return;
+        }
+        HdmiCecMessage message = new HdmiCecMessage(srcAddress, dstAddress, opcode, params);
+        for (IHdmiCecListener listener : mListeners) {
+            try {
+                listener.onMessageReceived(message);
+            } catch (RemoteException e) {
+                Log.e(TAG, "listener.onMessageReceived failed.");
+            }
+        }
+    }
+
+    public void handleHotplug(boolean connected) {
+        for (IHdmiCecListener listener : mListeners) {
+            try {
+                listener.onCableStatusChanged(connected);
+            } catch (RemoteException e) {
+                Log.e(TAG, "listener.onCableStatusChanged failed.");
+            }
+        }
+    }
+
+    /**
+     * Return the active status of the device.
+     *
+     * @return true if the device is the active source among the connected
+     *         HDMI-CEC-enabled devices; otherwise false.
+     */
+    public boolean isActiveSource() {
+        return mIsActiveSource;
+    }
+
+    /**
+     * Update the active source state of the device.
+     */
+    public void setIsActiveSource(boolean state) {
+        mIsActiveSource = state;
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecService.java b/services/core/java/com/android/server/hdmi/HdmiCecService.java
new file mode 100644
index 0000000..01f2ec3
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiCecService.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2014 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.hdmi;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiCecListener;
+import android.hardware.hdmi.IHdmiCecService;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * Provides a service for sending and processing HDMI-CEC messages, and providing
+ * the information on HDMI settings in general.
+ */
+public final class HdmiCecService extends SystemService {
+    private static final String TAG = "HdmiCecService";
+
+    // Maintains the allocated logical devices. Device type, not logical address,
+    // is used for key as logical address is likely to change over time while
+    // device type is permanent. Type-address mapping is maintained only at
+    // native level.
+    private final SparseArray<HdmiCecDevice> mLogicalDevices = new SparseArray<HdmiCecDevice>();
+
+    // List of IBinder.DeathRecipient instances to handle dead IHdmiCecListener
+    // objects.
+    private final ArrayList<ListenerRecord> mListenerRecords = new ArrayList<ListenerRecord>();
+
+    // Used to synchronize the access to the service.
+    private final Object mLock = new Object();
+
+    // Stores the pointer to the native implementation of the service that
+    // interacts with HAL.
+    private long mNativePtr;
+
+    private static final String PERMISSION = "android.permission.HDMI_CEC";
+
+    // Service name under which it is registered to service manager.
+    // TODO: Move this to Context once HdmiCecManager is introduced.
+    private static final String HDMI_CEC_SERVICE = "hdmi_cec";
+
+    public HdmiCecService(Context context) {
+        super(context);
+    }
+
+    private static native long nativeInit(HdmiCecService service);
+
+    @Override
+    public void onStart() {
+        mNativePtr = nativeInit(this);
+        publishBinderService(HDMI_CEC_SERVICE, new BinderService());
+    }
+
+    /**
+     * Called by native when an HDMI-CEC message arrived. Invokes the registered
+     * listeners to handle the message.
+     */
+    private void handleMessage(int srcAddress, int dstAddress, int opcode, byte[] params) {
+        // TODO: Messages like <Standby> may not need be passed to listener
+        //       but better be handled in service by turning off the screen
+        //       or putting the device into suspend mode. List up such messages
+        //       and handle them here.
+        int type = HdmiCec.getTypeFromAddress(dstAddress);
+        synchronized (mLock) {
+            if (dstAddress == HdmiCec.ADDR_BROADCAST) {
+                for (int i = 0; i < mLogicalDevices.size(); ++i) {
+                    mLogicalDevices.valueAt(i).handleMessage(srcAddress, dstAddress, opcode,
+                            params);
+                }
+            } else {
+                HdmiCecDevice device = mLogicalDevices.get(type);
+                if (device == null) {
+                    Log.w(TAG, "logical device not found. type: " + type);
+                    return;
+                }
+                device.handleMessage(srcAddress, dstAddress, opcode, params);
+            }
+        }
+    }
+
+    /**
+     * Called by native when internal HDMI hotplug event occurs. Invokes the registered
+     * listeners to handle the event.
+     */
+    private void handleHotplug(boolean connected) {
+        synchronized(mLock) {
+            for (int i = 0; i < mLogicalDevices.size(); ++i) {
+                mLogicalDevices.valueAt(i).handleHotplug(connected);
+            }
+        }
+    }
+
+    /**
+     * Called by native when it needs to know whether we have an active source.
+     * The native part uses the return value to respond to &lt;Request Active
+     * Source &gt;.
+     *
+     * @return type of the device which is active; DEVICE_INACTIVE if there is
+     *        no active logical device in the system.
+     */
+    private int getActiveSource() {
+        synchronized(mLock) {
+            for (int i = 0; i < mLogicalDevices.size(); ++i) {
+                if (mLogicalDevices.valueAt(i).isActiveSource()) {
+                    return mLogicalDevices.keyAt(i);
+                }
+            }
+        }
+        return HdmiCec.DEVICE_INACTIVE;
+    }
+
+    /**
+     * Called by native when a request for the device OSD name was received.
+     * The native part uses the return value to generate the message
+     * &lt;Set Osd Name&gt; in response.
+     */
+    private byte[] getOsdName(int type) {
+        synchronized (mLock) {
+            HdmiCecDevice device = mLogicalDevices.get(type);
+            if (device != null) {
+                return device.getName().getBytes(Charset.forName("US-ASCII"));
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Called by native when a request for the menu language of the device was
+     * received. The native part uses the return value to generate the message
+     * &lt;Set Menu Language&gt; in response. The language should be of
+     * the 3-letter format as defined in ISO/FDIS 639-2. We use system default
+     * locale.
+     */
+    private String getLanguage(int type) {
+        return Locale.getDefault().getISO3Language();
+    }
+
+    private void enforceAccessPermission() {
+        getContext().enforceCallingOrSelfPermission(PERMISSION, "HdmiCecService");
+    }
+
+    private void dumpInternal(PrintWriter pw) {
+        pw.println("HdmiCecService (dumpsys hdmi_cec)");
+        pw.println("");
+        synchronized (mLock) {
+            for (int i = 0; i < mLogicalDevices.size(); ++i) {
+                HdmiCecDevice device = mLogicalDevices.valueAt(i);
+                pw.println("Device: name=" + device.getName() +
+                           ", type=" + device.getType() +
+                           ", active=" + device.isActiveSource());
+            }
+        }
+    }
+
+    // Remove logical device of a given type.
+    private void removeLogicalDeviceLocked(int type) {
+        ensureValidType(type);
+        mLogicalDevices.remove(type);
+        nativeRemoveLogicalAddress(mNativePtr, type);
+    }
+
+    private static void ensureValidType(int type) {
+        if (!HdmiCec.isValidType(type)) {
+            throw new IllegalArgumentException("invalid type: " + type);
+        }
+    }
+
+    // Return the logical device identified by the given binder token.
+    private HdmiCecDevice getLogicalDeviceLocked(IBinder b) {
+        for (int i = 0; i < mLogicalDevices.size(); ++i) {
+            HdmiCecDevice device = mLogicalDevices.valueAt(i);
+            if (device.getToken() == b) {
+                return device;
+            }
+        }
+        throw new IllegalArgumentException("Device not found");
+    }
+
+    private final class ListenerRecord implements IBinder.DeathRecipient {
+        private final IHdmiCecListener mListener;
+        private final int mType;
+
+        public ListenerRecord(IHdmiCecListener listener, int type) {
+            mListener = listener;
+            mType = type;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mListenerRecords.remove(this);
+                HdmiCecDevice device = mLogicalDevices.get(mType);
+                if (device != null) {
+                    device.removeListener(mListener);
+                    if (!device.hasListener()) {
+                        removeLogicalDeviceLocked(mType);
+                    }
+                }
+            }
+        }
+    }
+
+    private final class BinderService extends IHdmiCecService.Stub {
+
+        @Override
+        public IBinder allocateLogicalDevice(int type, IHdmiCecListener listener) {
+            enforceAccessPermission();
+            ensureValidType(type);
+            if (listener == null) {
+                throw new IllegalArgumentException("listener must not be null");
+            }
+            synchronized (mLock) {
+                HdmiCecDevice device = mLogicalDevices.get(type);
+                if (device != null) {
+                    Log.v(TAG, "Logical address already allocated. Adding listener only.");
+                } else {
+                    int address = nativeAllocateLogicalAddress(mNativePtr, type);
+                    if (!HdmiCec.isValidAddress(address)) {
+                        Log.e(TAG, "Logical address was not allocated");
+                        return null;
+                    } else {
+                        device = new HdmiCecDevice(type);
+                        device.setName(HdmiCec.getDefaultDeviceName(address));
+                        mLogicalDevices.put(type, device);
+                    }
+                }
+
+                // Adds the listener and its monitor
+                ListenerRecord record = new ListenerRecord(listener, type);
+                try {
+                    listener.asBinder().linkToDeath(record, 0);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Listener already died");
+                    if (!device.hasListener()) {
+                         removeLogicalDeviceLocked(type);
+                    }
+                    return null;
+                }
+                mListenerRecords.add(record);
+                device.addListener(listener);
+                return device.getToken();
+            }
+        }
+
+        @Override
+        public void setOsdName(IBinder b, String name) {
+            enforceAccessPermission();
+            if (TextUtils.isEmpty(name)) {
+                throw new IllegalArgumentException("name must not be null");
+            }
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                device.setName(name);
+            }
+        }
+
+        @Override
+        public void sendActiveSource(IBinder b) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                device.setIsActiveSource(true);
+                nativeSendActiveSource(mNativePtr, device.getType());
+            }
+        }
+
+        @Override
+        public void sendInactiveSource(IBinder b) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                device.setIsActiveSource(false);
+                nativeSendInactiveSource(mNativePtr, device.getType());
+            }
+        }
+
+        @Override
+        public void sendImageViewOn(IBinder b) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                nativeSendImageViewOn(mNativePtr, device.getType());
+            }
+        }
+
+        @Override
+        public void sendTextViewOn(IBinder b) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                nativeSendTextViewOn(mNativePtr, device.getType());
+            }
+        }
+
+        @Override
+        public void sendGiveDevicePowerStatus(IBinder b, int address) {
+            enforceAccessPermission();
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                nativeSendGiveDevicePowerStatus(mNativePtr, device.getType(), address);
+            }
+        }
+
+        @Override
+        public void removeServiceListener(IBinder b, IHdmiCecListener listener) {
+            enforceAccessPermission();
+            if (listener == null) {
+                throw new IllegalArgumentException("listener must not be null");
+            }
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                for (ListenerRecord record : mListenerRecords) {
+                    if (record.mType == device.getType()
+                            && record.mListener.asBinder() == listener.asBinder()) {
+                        mListenerRecords.remove(record);
+                        device.removeListener(record.mListener);
+                        if (!device.hasListener()) {
+                            removeLogicalDeviceLocked(record.mType);
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void sendMessage(IBinder b, HdmiCecMessage message) {
+            enforceAccessPermission();
+            if (message == null) {
+                throw new IllegalArgumentException("message must not be null");
+            }
+            synchronized (mLock) {
+                HdmiCecDevice device = getLogicalDeviceLocked(b);
+                nativeSendMessage(mNativePtr, device.getType(), message.getDestination(),
+                        message.getOpcode(), message.getParams());
+            }
+        }
+
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+                pw.println("Permission denial: can't dump HdmiCecService from pid="
+                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                dumpInternal(pw);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+    }
+
+    private static native int nativeAllocateLogicalAddress(long handler, int deviceType);
+    private static native void nativeRemoveLogicalAddress(long handler, int deviceType);
+    private static native void nativeSendMessage(long handler, int deviceType, int destination,
+            int opcode, byte[] params);
+    private static native void nativeSendActiveSource(long handler, int deviceType);
+    private static native void nativeSendInactiveSource(long handler, int deviceType);
+    private static native void nativeSendImageViewOn(long handler, int deviceType);
+    private static native void nativeSendTextViewOn(long handler, int deviceType);
+    private static native void nativeSendGiveDevicePowerStatus(long handler, int deviceType,
+            int address);
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index e2226aa..7c2de8b 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -206,10 +206,6 @@
     final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>();
 
     private int mZenMode;
-    private int mPreZenAlarmVolume = -1;
-    private int mPreZenRingerMode = -1;
-    private boolean mZenMutingAlarm;
-    private boolean mZenMutingRinger;
     // temporary, until we update apps to provide metadata
     private static final Set<String> CALL_PACKAGES = new HashSet<String>(Arrays.asList(
             "com.google.android.dialer",
@@ -1122,9 +1118,6 @@
         private final Uri ZEN_MODE
                 = Settings.Global.getUriFor(Settings.Global.ZEN_MODE);
 
-        private final Uri MODE_RINGER
-                = Settings.Global.getUriFor(Settings.Global.MODE_RINGER);
-
         SettingsObserver(Handler handler) {
             super(handler);
         }
@@ -1137,8 +1130,6 @@
                     false, this, UserHandle.USER_ALL);
             resolver.registerContentObserver(ZEN_MODE,
                     false, this);
-            resolver.registerContentObserver(MODE_RINGER,
-                    false, this);
             update(null);
         }
 
@@ -1162,9 +1153,6 @@
             if (ZEN_MODE.equals(uri)) {
                 updateZenMode();
             }
-            if (MODE_RINGER.equals(uri)) {
-                updateRingerMode();
-            }
         }
     }
 
@@ -1230,7 +1218,6 @@
                     Settings.Global.DEVICE_PROVISIONED, 0)) {
             mDisableNotificationAlerts = true;
         }
-        updateRingerMode();
         updateZenMode();
 
         // register for various Intents
@@ -1694,10 +1681,6 @@
             pw.println("  mVibrateNotification=" + mVibrateNotification);
             pw.println("  mDisableNotificationAlerts=" + mDisableNotificationAlerts);
             pw.println("  mZenMode=" + Settings.Global.zenModeToString(mZenMode));
-            pw.println("  mPreZenAlarmVolume=" + mPreZenAlarmVolume);
-            pw.println("  mPreZenRingerMode=" + mPreZenRingerMode);
-            pw.println("  mZenMutingAlarm=" + mZenMutingAlarm);
-            pw.println("  mZenMutingRinger=" + mZenMutingRinger);
             pw.println("  mSystemReady=" + mSystemReady);
             pw.println("  mArchive=" + mArchive.toString());
             Iterator<StatusBarNotification> iter = mArchive.descendingIterator();
@@ -2023,7 +2006,7 @@
                                         useDefaultVibrate ? mDefaultVibrationPattern
                                             : mFallbackVibrationPattern,
                                         ((notification.flags & Notification.FLAG_INSISTENT) != 0)
-                                                ? 0: -1);
+                                                ? 0: -1, notification.audioStreamType);
                                 } finally {
                                     Binder.restoreCallingIdentity(identity);
                                 }
@@ -2033,7 +2016,7 @@
                                 mVibrator.vibrate(r.sbn.getUid(), r.sbn.getBasePkg(),
                                         notification.vibrate,
                                     ((notification.flags & Notification.FLAG_INSISTENT) != 0)
-                                            ? 0: -1);
+                                            ? 0: -1, notification.audioStreamType);
                             }
                         }
                     }
@@ -2552,17 +2535,6 @@
         }
     }
 
-    private void updateRingerMode() {
-        final int ringerMode = Settings.Global.getInt(getContext().getContentResolver(),
-                Settings.Global.MODE_RINGER, -1);
-        final boolean nonSilentRingerMode = ringerMode == AudioManager.RINGER_MODE_NORMAL
-                || ringerMode == AudioManager.RINGER_MODE_VIBRATE;
-        if (mZenMode != Settings.Global.ZEN_MODE_OFF && nonSilentRingerMode) {
-            Settings.Global.putInt(getContext().getContentResolver(),
-                    Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
-        }
-    }
-
     private void updateZenMode() {
         final int mode = Settings.Global.getInt(getContext().getContentResolver(),
                 Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
@@ -2572,62 +2544,31 @@
                     Settings.Global.zenModeToString(mode)));
         }
         mZenMode = mode;
-        if (mAudioManager != null) {
-            // call audio
-            final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
-            if (muteCalls) {
-                if (!mZenMutingRinger) {
-                    if (DBG) Slog.d(TAG, "Muting STREAM_RING");
-                    mAudioManager.setStreamMute(AudioManager.STREAM_RING, true);
-                    mZenMutingRinger = true;
-                }
-                // calls vibrate if ringer mode = vibrate, so set the ringer mode as well
-                final int ringerMode = mAudioManager.getRingerMode();
-                if (ringerMode != AudioManager.RINGER_MODE_SILENT) {
-                    if (DBG) Slog.d(TAG, "Saving ringer mode of " + ringerMode);
-                    mPreZenRingerMode = ringerMode;
-                    mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
-                }
-            } else {
-                if (mZenMutingRinger) {
-                    if (DBG) Slog.d(TAG, "Unmuting STREAM_RING");
-                    mAudioManager.setStreamMute(AudioManager.STREAM_RING, false);
-                    mZenMutingRinger = false;
-                }
-                if (mPreZenRingerMode != -1) {
-                    if (DBG) Slog.d(TAG, "Restoring ringer mode to " + mPreZenRingerMode);
-                    mAudioManager.setRingerMode(mPreZenRingerMode);
-                    mPreZenRingerMode = -1;
-                }
-            }
-            // alarm audio
-            final boolean muteAlarms = mZenMode == Settings.Global.ZEN_MODE_FULL;
-            if (muteAlarms) {
-                if (!mZenMutingAlarm) {
-                    if (DBG) Slog.d(TAG, "Muting STREAM_ALARM");
-                    mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, true);
-                    mZenMutingAlarm = true;
-                }
-                // alarms don't simply respect mute, so set the volume as well
-                final int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM);
-                if (volume != 0) {
-                    if (DBG) Slog.d(TAG, "Saving STREAM_ALARM volume of " + volume);
-                    mPreZenAlarmVolume = volume;
-                    mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, 0, 0);
-                }
-            } else {
-                if (mZenMutingAlarm) {
-                    if (DBG) Slog.d(TAG, "Unmuting STREAM_ALARM");
-                    mAudioManager.setStreamMute(AudioManager.STREAM_ALARM, false);
-                    mZenMutingAlarm = false;
-                }
-                if (mPreZenAlarmVolume != -1) {
-                    if (DBG) Slog.d(TAG, "Restoring STREAM_ALARM volume to " + mPreZenAlarmVolume);
-                    mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mPreZenAlarmVolume, 0);
-                    mPreZenAlarmVolume = -1;
-                }
-            }
-        }
+
+        final String[] exceptionPackages = null; // none (for now)
+
+        // call restrictions
+        final boolean muteCalls = mZenMode != Settings.Global.ZEN_MODE_OFF;
+        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_RING,
+                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+                exceptionPackages);
+        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_RING,
+                muteCalls ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+                exceptionPackages);
+
+        // alarm restrictions
+        final boolean muteAlarms = mZenMode == Settings.Global.ZEN_MODE_FULL;
+        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.STREAM_ALARM,
+                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+                exceptionPackages);
+        mAppOps.setRestriction(AppOpsManager.OP_PLAY_AUDIO, AudioManager.STREAM_ALARM,
+                muteAlarms ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+                exceptionPackages);
+
+        // restrict vibrations with no hints
+        mAppOps.setRestriction(AppOpsManager.OP_VIBRATE, AudioManager.USE_DEFAULT_STREAM_TYPE,
+                (muteAlarms || muteCalls) ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED,
+                exceptionPackages);
     }
 
     private void updateRelatedUserCache(Context context) {
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index b7e367b..ae1cfab 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -406,4 +406,8 @@
 
         return execute(builder.toString());
     }
+
+    public boolean restoreconData() {
+        return (execute("restorecondata") == 0);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cbcf408..a01c586 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -200,6 +200,9 @@
 
     private static final boolean GET_CERTIFICATES = true;
 
+    // Cap the size of permission trees that 3rd party apps can define
+    private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;     // characters of text
+
     private static final int REMOVE_EVENTS =
         FileObserver.CLOSE_WRITE | FileObserver.DELETE | FileObserver.MOVED_FROM;
     private static final int ADD_EVENTS =
@@ -302,12 +305,15 @@
     // This is the object monitoring the privileged system app dir.
     final FileObserver mPrivilegedInstallObserver;
 
-    // This is the object monitoring the system app dir.
+    // This is the object monitoring the vendor app dir.
     final FileObserver mVendorInstallObserver;
 
     // This is the object monitoring the vendor overlay package dir.
     final FileObserver mVendorOverlayInstallObserver;
 
+    // This is the object monitoring the OEM app dir.
+    final FileObserver mOemInstallObserver;
+
     // This is the object monitoring mAppInstallDir.
     final FileObserver mAppInstallObserver;
 
@@ -1157,7 +1163,12 @@
             sUserManager = new UserManagerService(context, this,
                     mInstallLock, mPackages);
 
-            readPermissions();
+            // Read permissions and features from system
+            readPermissions(Environment.buildPath(
+                    Environment.getRootDirectory(), "etc", "permissions"), false);
+            // Only read features from OEM
+            readPermissions(Environment.buildPath(
+                    Environment.getOemDirectory(), "etc", "permissions"), true);
 
             mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
 
@@ -1343,6 +1354,14 @@
             scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
 
+            // Collect all OEM packages.
+            File oemAppDir = new File(Environment.getOemDirectory(), "app");
+            mOemInstallObserver = new AppDirObserver(
+                    oemAppDir.getPath(), OBSERVER_EVENTS, true, false);
+            mOemInstallObserver.startWatching();
+            scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
+                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
+
             if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
             mInstaller.moveFiles();
 
@@ -1493,6 +1512,13 @@
             // can downgrade to reader
             mSettings.writeLPr();
 
+            if (SELinuxMMAC.shouldRestorecon()) {
+                Slog.i(TAG, "Relabeling of /data/data and /data/user issued.");
+                if (mInstaller.restoreconData()) {
+                    SELinuxMMAC.setRestoreconDone();
+                }
+            }
+
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                     SystemClock.uptimeMillis());
 
@@ -1581,9 +1607,8 @@
         mSettings.removePackageLPw(ps.name);
     }
 
-    void readPermissions() {
+    void readPermissions(File libraryDir, boolean onlyFeatures) {
         // Read permissions from .../etc/permission directory.
-        File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");
         if (!libraryDir.exists() || !libraryDir.isDirectory()) {
             Slog.w(TAG, "No directory " + libraryDir + ", skipping");
             return;
@@ -1609,16 +1634,16 @@
                 continue;
             }
 
-            readPermissionsFromXml(f);
+            readPermissionsFromXml(f, onlyFeatures);
         }
 
         // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
         final File permFile = new File(Environment.getRootDirectory(),
                 "etc/permissions/platform.xml");
-        readPermissionsFromXml(permFile);
+        readPermissionsFromXml(permFile, onlyFeatures);
     }
 
-    private void readPermissionsFromXml(File permFile) {
+    private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
         FileReader permReader = null;
         try {
             permReader = new FileReader(permFile);
@@ -1640,7 +1665,7 @@
                 }
 
                 String name = parser.getName();
-                if ("group".equals(name)) {
+                if ("group".equals(name) && !onlyFeatures) {
                     String gidStr = parser.getAttributeValue(null, "gid");
                     if (gidStr != null) {
                         int gid = Process.getGidForName(gidStr);
@@ -1652,7 +1677,7 @@
 
                     XmlUtils.skipCurrentTag(parser);
                     continue;
-                } else if ("permission".equals(name)) {
+                } else if ("permission".equals(name) && !onlyFeatures) {
                     String perm = parser.getAttributeValue(null, "name");
                     if (perm == null) {
                         Slog.w(TAG, "<permission> without name at "
@@ -1663,7 +1688,7 @@
                     perm = perm.intern();
                     readPermission(parser, perm);
 
-                } else if ("assign-permission".equals(name)) {
+                } else if ("assign-permission".equals(name) && !onlyFeatures) {
                     String perm = parser.getAttributeValue(null, "name");
                     if (perm == null) {
                         Slog.w(TAG, "<assign-permission> without name at "
@@ -1695,7 +1720,7 @@
                     perms.add(perm);
                     XmlUtils.skipCurrentTag(parser);
 
-                } else if ("library".equals(name)) {
+                } else if ("library".equals(name) && !onlyFeatures) {
                     String lname = parser.getAttributeValue(null, "name");
                     String lfile = parser.getAttributeValue(null, "file");
                     if (lname == null) {
@@ -2335,7 +2360,35 @@
         //if (pi1.descriptionRes != pi2.descriptionRes) return false;
         return true;
     }
-    
+
+    int permissionInfoFootprint(PermissionInfo info) {
+        int size = info.name.length();
+        if (info.nonLocalizedLabel != null) size += info.nonLocalizedLabel.length();
+        if (info.nonLocalizedDescription != null) size += info.nonLocalizedDescription.length();
+        return size;
+    }
+
+    int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
+        int size = 0;
+        for (BasePermission perm : mSettings.mPermissions.values()) {
+            if (perm.uid == tree.uid) {
+                size += perm.name.length() + permissionInfoFootprint(perm.perm.info);
+            }
+        }
+        return size;
+    }
+
+    void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
+        // We calculate the max size of permissions defined by this uid and throw
+        // if that plus the size of 'info' would exceed our stated maximum.
+        if (tree.uid != Process.SYSTEM_UID) {
+            final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
+            if (curTreeSize + permissionInfoFootprint(info) > MAX_PERMISSION_TREE_FOOTPRINT) {
+                throw new SecurityException("Permission tree size cap exceeded");
+            }
+        }
+    }
+
     boolean addPermissionLocked(PermissionInfo info, boolean async) {
         if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
             throw new SecurityException("Label must be specified in permission");
@@ -2346,6 +2399,7 @@
         boolean changed = true;
         int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
         if (added) {
+            enforcePermissionCapLocked(info, tree);
             bp = new BasePermission(info.name, tree.sourcePackage,
                     BasePermission.TYPE_DYNAMIC);
         } else if (bp.type != BasePermission.TYPE_DYNAMIC) {
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 6e12e41..d70c725 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -25,10 +25,15 @@
 
 import com.android.internal.util.XmlUtils;
 
+import libcore.io.IoUtils;
+
 import java.io.File;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
 
 import java.util.HashMap;
 
@@ -59,6 +64,13 @@
         new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
         null};
 
+    // Location of seapp_contexts policy file.
+    private static final String SEAPP_CONTEXTS_FILE = "/seapp_contexts";
+
+    // Stores the hash of the last used seapp_contexts file.
+    private static final String SEAPP_HASH_FILE =
+            Environment.getDataDirectory().toString() + "/system/seapp_hash";
+
     // Signature policy stanzas
     static class Policy {
         private String seinfo;
@@ -102,7 +114,6 @@
 
     /**
      * Parses an MMAC install policy from a predefined list of locations.
-     * @param none
      * @return boolean indicating whether an install policy was correctly parsed.
      */
     public static boolean readInstallPolicy() {
@@ -112,7 +123,7 @@
 
     /**
      * Parses an MMAC install policy given as an argument.
-     * @param File object representing the path of the policy.
+     * @param policyFile object representing the path of the policy.
      * @return boolean indicating whether the install policy was correctly parsed.
      */
     public static boolean readInstallPolicy(File policyFile) {
@@ -344,8 +355,7 @@
     /**
      * Labels a package based on an seinfo tag from install policy.
      * The label is attached to the ApplicationInfo instance of the package.
-     * @param PackageParser.Package object representing the package
-     *         to labeled.
+     * @param pkg object representing the package to be labeled.
      * @return boolean which determines whether a non null seinfo label
      *         was assigned to the package. A null value simply meaning that
      *         no policy matched.
@@ -390,4 +400,89 @@
 
         return (sDefaultSeinfo != null);
     }
+
+    /**
+     * Determines if a recursive restorecon on /data/data and /data/user is needed.
+     * It does this by comparing the SHA-1 of the seapp_contexts file against the
+     * stored hash at /data/system/seapp_hash.
+     *
+     * @return Returns true if the restorecon should occur or false otherwise.
+     */
+    public static boolean shouldRestorecon() {
+        // Any error with the seapp_contexts file should be fatal
+        byte[] currentHash = null;
+        try {
+            currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+        } catch (IOException ioe) {
+            Slog.e(TAG, "Error with hashing seapp_contexts.", ioe);
+            return false;
+        }
+
+        // Push past any error with the stored hash file
+        byte[] storedHash = null;
+        try {
+            storedHash = IoUtils.readFileAsByteArray(SEAPP_HASH_FILE);
+        } catch (IOException ioe) {
+            Slog.w(TAG, "Error opening " + SEAPP_HASH_FILE + ". Assuming first boot.");
+        }
+
+        return (storedHash == null || !MessageDigest.isEqual(storedHash, currentHash));
+    }
+
+    /**
+     * Stores the SHA-1 of the seapp_contexts to /data/system/seapp_hash.
+     */
+    public static void setRestoreconDone() {
+        try {
+            final byte[] currentHash = returnHash(SEAPP_CONTEXTS_FILE);
+            dumpHash(new File(SEAPP_HASH_FILE), currentHash);
+        } catch (IOException ioe) {
+            Slog.e(TAG, "Error with saving hash to " + SEAPP_HASH_FILE, ioe);
+        }
+    }
+
+    /**
+     * Dump the contents of a byte array to a specified file.
+     *
+     * @param file The file that receives the byte array content.
+     * @param content A byte array that will be written to the specified file.
+     * @throws IOException if any failed I/O operation occured.
+     *         Included is the failure to atomically rename the tmp
+     *         file used in the process.
+     */
+    private static void dumpHash(File file, byte[] content) throws IOException {
+        FileOutputStream fos = null;
+        File tmp = null;
+        try {
+            tmp = File.createTempFile("seapp_hash", ".journal", file.getParentFile());
+            tmp.setReadable(true);
+            fos = new FileOutputStream(tmp);
+            fos.write(content);
+            fos.getFD().sync();
+            if (!tmp.renameTo(file)) {
+                throw new IOException("Failure renaming " + file.getCanonicalPath());
+            }
+        } finally {
+            if (tmp != null) {
+                tmp.delete();
+            }
+            IoUtils.closeQuietly(fos);
+        }
+    }
+
+    /**
+     * Return the SHA-1 of a file.
+     *
+     * @param file The path to the file given as a string.
+     * @return Returns the SHA-1 of the file as a byte array.
+     * @throws IOException if any failed I/O operations occured.
+     */
+    private static byte[] returnHash(String file) throws IOException {
+        try {
+            final byte[] contents = IoUtils.readFileAsByteArray(file);
+            return MessageDigest.getInstance("SHA-1").digest(contents);
+        } catch (NoSuchAlgorithmException nsae) {
+            throw new RuntimeException(nsae);  // impossible
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/power/AutomaticBrightnessController.java b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
index 3ca628a..807262a 100644
--- a/services/core/java/com/android/server/power/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/power/AutomaticBrightnessController.java
@@ -200,8 +200,7 @@
 
     public void updatePowerState(DisplayPowerRequest request) {
         if (setScreenAutoBrightnessAdjustment(request.screenAutoBrightnessAdjustment)
-                || setLightSensorEnabled(request.useAutoBrightness
-                    && request.wantScreenOnNormal())) {
+                || setLightSensorEnabled(request.wantLightSensorEnabled())) {
             updateAutoBrightness(false /*sendUpdate*/);
         }
     }
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 88a27f5..8fed79f 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -24,6 +24,7 @@
 import android.app.ProgressDialog;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.IBluetoothManager;
+import android.media.AudioManager;
 import android.nfc.NfcAdapter;
 import android.nfc.INfcAdapter;
 import android.content.BroadcastReceiver;
@@ -498,7 +499,7 @@
             // vibrate before shutting down
             Vibrator vibrator = new SystemVibrator();
             try {
-                vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
+                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, AudioManager.STREAM_SYSTEM);
             } catch (Exception e) {
                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                 Log.w(TAG, "Failed to vibrate during shutdown.", e);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
new file mode 100644
index 0000000..35b7f99
--- /dev/null
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -0,0 +1,1173 @@
+/*
+ * Copyright (C) 2014 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.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.app.Service;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TypedValue;
+import android.view.MagnificationSpec;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.WindowInfo;
+import android.view.WindowManager;
+import android.view.WindowManagerInternal.MagnificationCallbacks;
+import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
+import android.view.WindowManagerPolicy;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+
+import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * This class contains the accessibility related logic of the window manger.
+ */
+final class AccessibilityController {
+
+    private final WindowManagerService mWindowManagerService;
+
+    private static final float[] sTempFloats = new float[9];
+
+    public AccessibilityController(WindowManagerService service) {
+        mWindowManagerService = service;
+    }
+
+    private DisplayMagnifier mDisplayMagnifier;
+
+    private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
+
+    public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
+        if (callbacks != null) {
+            if (mDisplayMagnifier != null) {
+                throw new IllegalStateException("Magnification callbacks already set!");
+            }
+            mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks);
+        } else {
+            if  (mDisplayMagnifier == null) {
+                throw new IllegalStateException("Magnification callbacks already cleared!");
+            }
+            mDisplayMagnifier.destroyLocked();
+            mDisplayMagnifier = null;
+        }
+    }
+
+    public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
+        if (callback != null) {
+            if (mWindowsForAccessibilityObserver != null) {
+                throw new IllegalStateException(
+                        "Windows for accessibility callback already set!");
+            }
+            mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
+                    mWindowManagerService, callback);
+        } else {
+            if (mWindowsForAccessibilityObserver == null) {
+                throw new IllegalStateException(
+                        "Windows for accessibility callback already cleared!");
+            }
+            mWindowsForAccessibilityObserver = null;
+        }
+    }
+
+    public void setMagnificationSpecLocked(MagnificationSpec spec) {
+        if (mDisplayMagnifier != null) {
+            mDisplayMagnifier.setMagnificationSpecLocked(spec);
+        }
+        if (mWindowsForAccessibilityObserver != null) {
+            mWindowsForAccessibilityObserver.computeChangedWindows();
+        }
+    }
+
+    public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
+        if (mDisplayMagnifier != null) {
+            mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate);
+        }
+        // Not relevant for the window observer.
+    }
+
+    public void onWindowLayersChangedLocked() {
+        if (mDisplayMagnifier != null) {
+            mDisplayMagnifier.onWindowLayersChangedLocked();
+        }
+        if (mWindowsForAccessibilityObserver != null) {
+            mWindowsForAccessibilityObserver.computeChangedWindows();
+        }
+    }
+
+    public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
+        if (mDisplayMagnifier != null) {
+            mDisplayMagnifier.onRotationChangedLocked(displayContent, rotation);
+        }
+        if (mWindowsForAccessibilityObserver != null) {
+            mWindowsForAccessibilityObserver.computeChangedWindows();
+        }
+    }
+
+    public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
+        if (mDisplayMagnifier != null) {
+            mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
+        }
+        // Not relevant for the window observer.
+    }
+
+    public void onWindowTransitionLocked(WindowState windowState, int transition) {
+        if (mDisplayMagnifier != null) {
+            mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
+        }
+        if (mWindowsForAccessibilityObserver != null) {
+            mWindowsForAccessibilityObserver.computeChangedWindows();
+        }
+    }
+
+    public void onWindowFocusChangedLocked() {
+        // Not relevant for the display magnifier.
+
+        if (mWindowsForAccessibilityObserver != null) {
+            mWindowsForAccessibilityObserver.computeChangedWindows();
+        }
+    }
+
+    /** NOTE: This has to be called within a surface transaction. */
+    public void drawMagnifiedRegionBorderIfNeededLocked() {
+        if (mDisplayMagnifier != null) {
+            mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+        }
+        // Not relevant for the window observer.
+    }
+
+    public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+        if (mDisplayMagnifier != null) {
+            return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
+        }
+        return null;
+    }
+
+    public boolean hasCallbacksLocked() {
+        return (mDisplayMagnifier != null
+                || mWindowsForAccessibilityObserver != null);
+    }
+
+    private static void populateTransformationMatrixLocked(WindowState windowState,
+            Matrix outMatrix) {
+        sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
+        sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
+        sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
+        sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
+        sTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
+        sTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
+        sTempFloats[Matrix.MPERSP_0] = 0;
+        sTempFloats[Matrix.MPERSP_1] = 0;
+        sTempFloats[Matrix.MPERSP_2] = 1;
+        outMatrix.setValues(sTempFloats);
+    }
+
+    /**
+     * This class encapsulates the functionality related to display magnification.
+     */
+    private static final class DisplayMagnifier {
+
+        private static final String LOG_TAG = "DisplayMagnifier";
+
+        private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
+        private static final boolean DEBUG_ROTATION = false;
+        private static final boolean DEBUG_LAYERS = false;
+        private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
+        private static final boolean DEBUG_VIEWPORT_WINDOW = false;
+
+        private final Rect mTempRect1 = new Rect();
+        private final Rect mTempRect2 = new Rect();
+
+        private final Region mTempRegion1 = new Region();
+        private final Region mTempRegion2 = new Region();
+        private final Region mTempRegion3 = new Region();
+        private final Region mTempRegion4 = new Region();
+
+        private final Context mContext;
+        private final WindowManagerService mWindowManagerService;
+        private final MagnifiedViewport mMagnifedViewport;
+        private final Handler mHandler;
+
+        private final MagnificationCallbacks mCallbacks;
+
+        private final long mLongAnimationDuration;
+
+        public DisplayMagnifier(WindowManagerService windowManagerService,
+                MagnificationCallbacks callbacks) {
+            mContext = windowManagerService.mContext;
+            mWindowManagerService = windowManagerService;
+            mCallbacks = callbacks;
+            mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
+            mMagnifedViewport = new MagnifiedViewport();
+            mLongAnimationDuration = mContext.getResources().getInteger(
+                    com.android.internal.R.integer.config_longAnimTime);
+        }
+
+        public void setMagnificationSpecLocked(MagnificationSpec spec) {
+            mMagnifedViewport.updateMagnificationSpecLocked(spec);
+            mMagnifedViewport.recomputeBoundsLocked();
+            mWindowManagerService.scheduleAnimationLocked();
+        }
+
+        public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
+            if (DEBUG_RECTANGLE_REQUESTED) {
+                Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
+            }
+            if (!mMagnifedViewport.isMagnifyingLocked()) {
+                return;
+            }
+            Rect magnifiedRegionBounds = mTempRect2;
+            mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
+            if (magnifiedRegionBounds.contains(rectangle)) {
+                return;
+            }
+            SomeArgs args = SomeArgs.obtain();
+            args.argi1 = rectangle.left;
+            args.argi2 = rectangle.top;
+            args.argi3 = rectangle.right;
+            args.argi4 = rectangle.bottom;
+            mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
+                    args).sendToTarget();
+        }
+
+        public void onWindowLayersChangedLocked() {
+            if (DEBUG_LAYERS) {
+                Slog.i(LOG_TAG, "Layers changed.");
+            }
+            mMagnifedViewport.recomputeBoundsLocked();
+            mWindowManagerService.scheduleAnimationLocked();
+        }
+
+        public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
+            if (DEBUG_ROTATION) {
+                Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
+                        + " displayId: " + displayContent.getDisplayId());
+            }
+            mMagnifedViewport.onRotationChangedLocked();
+            mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
+        }
+
+        public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
+            if (DEBUG_WINDOW_TRANSITIONS) {
+                Slog.i(LOG_TAG, "Window transition: "
+                        + AppTransition.appTransitionToString(transition)
+                        + " displayId: " + windowState.getDisplayId());
+            }
+            final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+            if (magnifying) {
+                switch (transition) {
+                    case AppTransition.TRANSIT_ACTIVITY_OPEN:
+                    case AppTransition.TRANSIT_TASK_OPEN:
+                    case AppTransition.TRANSIT_TASK_TO_FRONT:
+                    case AppTransition.TRANSIT_WALLPAPER_OPEN:
+                    case AppTransition.TRANSIT_WALLPAPER_CLOSE:
+                    case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
+                        mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
+                    }
+                }
+            }
+        }
+
+        public void onWindowTransitionLocked(WindowState windowState, int transition) {
+            if (DEBUG_WINDOW_TRANSITIONS) {
+                Slog.i(LOG_TAG, "Window transition: "
+                        + AppTransition.appTransitionToString(transition)
+                        + " displayId: " + windowState.getDisplayId());
+            }
+            final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
+            final int type = windowState.mAttrs.type;
+            switch (transition) {
+                case WindowManagerPolicy.TRANSIT_ENTER:
+                case WindowManagerPolicy.TRANSIT_SHOW: {
+                    if (!magnifying) {
+                        break;
+                    }
+                    switch (type) {
+                        case WindowManager.LayoutParams.TYPE_APPLICATION:
+                        case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
+                        case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
+                        case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
+                        case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
+                        case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
+                        case WindowManager.LayoutParams.TYPE_PHONE:
+                        case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
+                        case WindowManager.LayoutParams.TYPE_TOAST:
+                        case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
+                        case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
+                        case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
+                        case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
+                        case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
+                        case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
+                        case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
+                        case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
+                            Rect magnifiedRegionBounds = mTempRect2;
+                            mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
+                                    magnifiedRegionBounds);
+                            Rect touchableRegionBounds = mTempRect1;
+                            windowState.getTouchableRegion(mTempRegion1);
+                            mTempRegion1.getBounds(touchableRegionBounds);
+                            if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
+                                mCallbacks.onRectangleOnScreenRequested(
+                                        touchableRegionBounds.left,
+                                        touchableRegionBounds.top,
+                                        touchableRegionBounds.right,
+                                        touchableRegionBounds.bottom);
+                            }
+                        } break;
+                    } break;
+                }
+            }
+        }
+
+        public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
+            MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
+            if (spec != null && !spec.isNop()) {
+                WindowManagerPolicy policy = mWindowManagerService.mPolicy;
+                final int windowType = windowState.mAttrs.type;
+                if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
+                        && !policy.canMagnifyWindow(windowType)) {
+                    return null;
+                }
+                if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
+                    return null;
+                }
+            }
+            return spec;
+        }
+
+        public void destroyLocked() {
+            mMagnifedViewport.destroyWindow();
+        }
+
+        /** NOTE: This has to be called within a surface transaction. */
+        public void drawMagnifiedRegionBorderIfNeededLocked() {
+            mMagnifedViewport.drawWindowIfNeededLocked();
+        }
+
+        private final class MagnifiedViewport {
+
+            private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
+
+            private final SparseArray<WindowState> mTempWindowStates =
+                    new SparseArray<WindowState>();
+
+            private final RectF mTempRectF = new RectF();
+
+            private final Point mTempPoint = new Point();
+
+            private final Matrix mTempMatrix = new Matrix();
+
+            private final Region mMagnifiedBounds = new Region();
+            private final Region mOldMagnifiedBounds = new Region();
+
+            private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
+
+            private final WindowManager mWindowManager;
+
+            private final int mBorderWidth;
+            private final int mHalfBorderWidth;
+
+            private final ViewportWindow mWindow;
+
+            private boolean mFullRedrawNeeded;
+
+            public MagnifiedViewport() {
+                mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
+                mBorderWidth = (int) TypedValue.applyDimension(
+                        TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
+                                mContext.getResources().getDisplayMetrics());
+                mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
+                mWindow = new ViewportWindow(mContext);
+                recomputeBoundsLocked();
+            }
+
+            public void updateMagnificationSpecLocked(MagnificationSpec spec) {
+                if (spec != null) {
+                    mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
+                } else {
+                    mMagnificationSpec.clear();
+                }
+                // If this message is pending we are in a rotation animation and do not want
+                // to show the border. We will do so when the pending message is handled.
+                if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
+                    setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
+                }
+            }
+
+            public void recomputeBoundsLocked() {
+                mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+                final int screenWidth = mTempPoint.x;
+                final int screenHeight = mTempPoint.y;
+
+                Region magnifiedBounds = mMagnifiedBounds;
+                magnifiedBounds.set(0, 0, 0, 0);
+
+                Region availableBounds = mTempRegion1;
+                availableBounds.set(0, 0, screenWidth, screenHeight);
+
+                Region nonMagnifiedBounds = mTempRegion4;
+                nonMagnifiedBounds.set(0,  0,  0,  0);
+
+                SparseArray<WindowState> visibleWindows = mTempWindowStates;
+                visibleWindows.clear();
+                populateWindowsOnScreenLocked(visibleWindows);
+
+                final int visibleWindowCount = visibleWindows.size();
+                for (int i = visibleWindowCount - 1; i >= 0; i--) {
+                    WindowState windowState = visibleWindows.valueAt(i);
+                    if (windowState.mAttrs.type == WindowManager
+                            .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
+                        continue;
+                    }
+
+                    Region windowBounds = mTempRegion2;
+                    Matrix matrix = mTempMatrix;
+                    populateTransformationMatrixLocked(windowState, matrix);
+                    RectF windowFrame = mTempRectF;
+
+                    if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
+                        windowFrame.set(windowState.mFrame);
+                        windowFrame.offset(-windowFrame.left, -windowFrame.top);
+                        matrix.mapRect(windowFrame);
+                        windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+                                (int) windowFrame.right, (int) windowFrame.bottom);
+                        magnifiedBounds.op(windowBounds, Region.Op.UNION);
+                        magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
+                    } else {
+                        Region touchableRegion = mTempRegion3;
+                        windowState.getTouchableRegion(touchableRegion);
+                        Rect touchableFrame = mTempRect1;
+                        touchableRegion.getBounds(touchableFrame);
+                        windowFrame.set(touchableFrame);
+                        windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+                        matrix.mapRect(windowFrame);
+                        windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
+                                (int) windowFrame.right, (int) windowFrame.bottom);
+                        nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
+                        windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
+                        availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
+                    }
+
+                    Region accountedBounds = mTempRegion2;
+                    accountedBounds.set(magnifiedBounds);
+                    accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
+                    accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
+
+                    if (accountedBounds.isRect()) {
+                        Rect accountedFrame = mTempRect1;
+                        accountedBounds.getBounds(accountedFrame);
+                        if (accountedFrame.width() == screenWidth
+                                && accountedFrame.height() == screenHeight) {
+                            break;
+                        }
+                    }
+                }
+
+                visibleWindows.clear();
+
+                magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
+                        screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
+                        Region.Op.INTERSECT);
+
+                if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
+                    Region bounds = Region.obtain();
+                    bounds.set(magnifiedBounds);
+                    mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
+                            bounds).sendToTarget();
+
+                    mWindow.setBounds(magnifiedBounds);
+                    Rect dirtyRect = mTempRect1;
+                    if (mFullRedrawNeeded) {
+                        mFullRedrawNeeded = false;
+                        dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
+                                screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
+                        mWindow.invalidate(dirtyRect);
+                    } else {
+                        Region dirtyRegion = mTempRegion3;
+                        dirtyRegion.set(magnifiedBounds);
+                        dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
+                        dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
+                        dirtyRegion.getBounds(dirtyRect);
+                        mWindow.invalidate(dirtyRect);
+                    }
+
+                    mOldMagnifiedBounds.set(magnifiedBounds);
+                }
+            }
+
+            public void onRotationChangedLocked() {
+                // If we are magnifying, hide the magnified border window immediately so
+                // the user does not see strange artifacts during rotation. The screenshot
+                // used for rotation has already the border. After the rotation is complete
+                // we will show the border.
+                if (isMagnifyingLocked()) {
+                    setMagnifiedRegionBorderShownLocked(false, false);
+                    final long delay = (long) (mLongAnimationDuration
+                            * mWindowManagerService.mWindowAnimationScale);
+                    Message message = mHandler.obtainMessage(
+                            MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
+                    mHandler.sendMessageDelayed(message, delay);
+                }
+                recomputeBoundsLocked();
+                mWindow.updateSize();
+            }
+
+            public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
+                if (shown) {
+                    mFullRedrawNeeded = true;
+                    mOldMagnifiedBounds.set(0,  0,  0,  0);
+                }
+                mWindow.setShown(shown, animate);
+            }
+
+            public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
+                MagnificationSpec spec = mMagnificationSpec;
+                mMagnifiedBounds.getBounds(rect);
+                rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
+                rect.scale(1.0f / spec.scale);
+            }
+
+            public boolean isMagnifyingLocked() {
+                return mMagnificationSpec.scale > 1.0f;
+            }
+
+            public MagnificationSpec getMagnificationSpecLocked() {
+                return mMagnificationSpec;
+            }
+
+            /** NOTE: This has to be called within a surface transaction. */
+            public void drawWindowIfNeededLocked() {
+                recomputeBoundsLocked();
+                mWindow.drawIfNeeded();
+            }
+
+            public void destroyWindow() {
+                mWindow.releaseSurface();
+            }
+
+            private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+                DisplayContent displayContent = mWindowManagerService
+                        .getDefaultDisplayContentLocked();
+                WindowList windowList = displayContent.getWindowList();
+                final int windowCount = windowList.size();
+                for (int i = 0; i < windowCount; i++) {
+                    WindowState windowState = windowList.get(i);
+                    if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
+                            .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
+                            && !windowState.mWinAnimator.mEnterAnimationPending) {
+                        outWindows.put(windowState.mLayer, windowState);
+                    }
+                }
+            }
+
+            private final class ViewportWindow {
+                private static final String SURFACE_TITLE = "Magnification Overlay";
+
+                private static final String PROPERTY_NAME_ALPHA = "alpha";
+
+                private static final int MIN_ALPHA = 0;
+                private static final int MAX_ALPHA = 255;
+
+                private final Region mBounds = new Region();
+                private final Rect mDirtyRect = new Rect();
+                private final Paint mPaint = new Paint();
+
+                private final ValueAnimator mShowHideFrameAnimator;
+                private final SurfaceControl mSurfaceControl;
+                private final Surface mSurface = new Surface();
+
+                private boolean mShown;
+                private int mAlpha;
+
+                private boolean mInvalidated;
+
+                public ViewportWindow(Context context) {
+                    SurfaceControl surfaceControl = null;
+                    try {
+                        mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+                        surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession,
+                                SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT,
+                                SurfaceControl.HIDDEN);
+                    } catch (OutOfResourcesException oore) {
+                        /* ignore */
+                    }
+                    mSurfaceControl = surfaceControl;
+                    mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
+                            .getLayerStack());
+                    mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
+                            WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
+                            * WindowManagerService.TYPE_LAYER_MULTIPLIER);
+                    mSurfaceControl.setPosition(0, 0);
+                    mSurface.copyFrom(mSurfaceControl);
+
+                    TypedValue typedValue = new TypedValue();
+                    context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
+                            typedValue, true);
+                    final int borderColor = context.getResources().getColor(typedValue.resourceId);
+
+                    mPaint.setStyle(Paint.Style.STROKE);
+                    mPaint.setStrokeWidth(mBorderWidth);
+                    mPaint.setColor(borderColor);
+
+                    Interpolator interpolator = new DecelerateInterpolator(2.5f);
+                    final long longAnimationDuration = context.getResources().getInteger(
+                            com.android.internal.R.integer.config_longAnimTime);
+
+                    mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
+                            MIN_ALPHA, MAX_ALPHA);
+                    mShowHideFrameAnimator.setInterpolator(interpolator);
+                    mShowHideFrameAnimator.setDuration(longAnimationDuration);
+                    mInvalidated = true;
+                }
+
+                public void setShown(boolean shown, boolean animate) {
+                    synchronized (mWindowManagerService.mWindowMap) {
+                        if (mShown == shown) {
+                            return;
+                        }
+                        mShown = shown;
+                        if (animate) {
+                            if (mShowHideFrameAnimator.isRunning()) {
+                                mShowHideFrameAnimator.reverse();
+                            } else {
+                                if (shown) {
+                                    mShowHideFrameAnimator.start();
+                                } else {
+                                    mShowHideFrameAnimator.reverse();
+                                }
+                            }
+                        } else {
+                            mShowHideFrameAnimator.cancel();
+                            if (shown) {
+                                setAlpha(MAX_ALPHA);
+                            } else {
+                                setAlpha(MIN_ALPHA);
+                            }
+                        }
+                        if (DEBUG_VIEWPORT_WINDOW) {
+                            Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
+                        }
+                    }
+                }
+
+                @SuppressWarnings("unused")
+                // Called reflectively from an animator.
+                public int getAlpha() {
+                    synchronized (mWindowManagerService.mWindowMap) {
+                        return mAlpha;
+                    }
+                }
+
+                public void setAlpha(int alpha) {
+                    synchronized (mWindowManagerService.mWindowMap) {
+                        if (mAlpha == alpha) {
+                            return;
+                        }
+                        mAlpha = alpha;
+                        invalidate(null);
+                        if (DEBUG_VIEWPORT_WINDOW) {
+                            Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
+                        }
+                    }
+                }
+
+                public void setBounds(Region bounds) {
+                    synchronized (mWindowManagerService.mWindowMap) {
+                        if (mBounds.equals(bounds)) {
+                            return;
+                        }
+                        mBounds.set(bounds);
+                        invalidate(mDirtyRect);
+                        if (DEBUG_VIEWPORT_WINDOW) {
+                            Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
+                        }
+                    }
+                }
+
+                public void updateSize() {
+                    synchronized (mWindowManagerService.mWindowMap) {
+                        mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
+                        mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
+                        invalidate(mDirtyRect);
+                    }
+                }
+
+                public void invalidate(Rect dirtyRect) {
+                    if (dirtyRect != null) {
+                        mDirtyRect.set(dirtyRect);
+                    } else {
+                        mDirtyRect.setEmpty();
+                    }
+                    mInvalidated = true;
+                    mWindowManagerService.scheduleAnimationLocked();
+                }
+
+                /** NOTE: This has to be called within a surface transaction. */
+                public void drawIfNeeded() {
+                    synchronized (mWindowManagerService.mWindowMap) {
+                        if (!mInvalidated) {
+                            return;
+                        }
+                        mInvalidated = false;
+                        Canvas canvas = null;
+                        try {
+                            // Empty dirty rectangle means unspecified.
+                            if (mDirtyRect.isEmpty()) {
+                                mBounds.getBounds(mDirtyRect);
+                            }
+                            mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
+                            canvas = mSurface.lockCanvas(mDirtyRect);
+                            if (DEBUG_VIEWPORT_WINDOW) {
+                                Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
+                            }
+                        } catch (IllegalArgumentException iae) {
+                            /* ignore */
+                        } catch (Surface.OutOfResourcesException oore) {
+                            /* ignore */
+                        }
+                        if (canvas == null) {
+                            return;
+                        }
+                        if (DEBUG_VIEWPORT_WINDOW) {
+                            Slog.i(LOG_TAG, "Bounds: " + mBounds);
+                        }
+                        canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+                        mPaint.setAlpha(mAlpha);
+                        Path path = mBounds.getBoundaryPath();
+                        canvas.drawPath(path, mPaint);
+
+                        mSurface.unlockCanvasAndPost(canvas);
+
+                        if (mAlpha > 0) {
+                            mSurfaceControl.show();
+                        } else {
+                            mSurfaceControl.hide();
+                        }
+                    }
+                }
+
+                public void releaseSurface() {
+                    mSurfaceControl.release();
+                    mSurface.release();
+                }
+            }
+        }
+
+        private class MyHandler extends Handler {
+            public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
+            public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
+            public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
+            public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
+            public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
+
+            public MyHandler(Looper looper) {
+                super(looper);
+            }
+
+            @Override
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
+                        Region bounds = (Region) message.obj;
+                        mCallbacks.onMagnifedBoundsChanged(bounds);
+                        bounds.recycle();
+                    } break;
+
+                    case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
+                        SomeArgs args = (SomeArgs) message.obj;
+                        final int left = args.argi1;
+                        final int top = args.argi2;
+                        final int right = args.argi3;
+                        final int bottom = args.argi4;
+                        mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
+                        args.recycle();
+                    } break;
+
+                    case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
+                        mCallbacks.onUserContextChanged();
+                    } break;
+
+                    case MESSAGE_NOTIFY_ROTATION_CHANGED: {
+                        final int rotation = message.arg1;
+                        mCallbacks.onRotationChanged(rotation);
+                    } break;
+
+                    case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
+                        synchronized (mWindowManagerService.mWindowMap) {
+                            if (mMagnifedViewport.isMagnifyingLocked()) {
+                                mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
+                                mWindowManagerService.scheduleAnimationLocked();
+                            }
+                        }
+                    } break;
+                }
+            }
+        }
+    }
+
+    /**
+     * This class encapsulates the functionality related to computing the windows
+     * reported for accessibility purposes. These windows are all windows a sighted
+     * user can see on the screen.
+     */
+    private static final class WindowsForAccessibilityObserver {
+        private static final String LOG_TAG = "WindowsForAccessibilityObserver";
+
+        private static final boolean DEBUG = false;
+
+        private final SparseArray<WindowState> mTempWindowStates =
+                new SparseArray<WindowState>();
+
+        private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
+
+        private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
+
+        private final RectF mTempRectF = new RectF();
+
+        private final Matrix mTempMatrix = new Matrix();
+
+        private final Point mTempPoint = new Point();
+
+        private final Rect mTempRect = new Rect();
+
+        private final Region mTempRegion = new Region();
+
+        private final Region mTempRegion1 = new Region();
+
+        private final Context mContext;
+
+        private final WindowManagerService mWindowManagerService;
+
+        private final Handler mHandler;
+
+        private final WindowsForAccessibilityCallback mCallback;
+
+        public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
+                WindowsForAccessibilityCallback callback) {
+            mContext = windowManagerService.mContext;
+            mWindowManagerService = windowManagerService;
+            mCallback = callback;
+            mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
+            computeChangedWindows();
+        }
+
+        public void computeChangedWindows() {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "computeChangedWindows()");
+            }
+
+            synchronized (mWindowManagerService.mWindowMap) {
+                WindowManager windowManager = (WindowManager)
+                        mContext.getSystemService(Context.WINDOW_SERVICE);
+                windowManager.getDefaultDisplay().getRealSize(mTempPoint);
+                final int screenWidth = mTempPoint.x;
+                final int screenHeight = mTempPoint.y;
+
+                Region unaccountedSpace = mTempRegion;
+                unaccountedSpace.set(0, 0, screenWidth, screenHeight);
+
+                SparseArray<WindowState> visibleWindows = mTempWindowStates;
+                populateVisibleWindowsOnScreenLocked(visibleWindows);
+
+                List<WindowInfo> windows = new ArrayList<WindowInfo>();
+
+                Set<IBinder> addedWindows = mTempBinderSet;
+                addedWindows.clear();
+
+                final int visibleWindowCount = visibleWindows.size();
+                for (int i = visibleWindowCount - 1; i >= 0; i--) {
+                    WindowState windowState = visibleWindows.valueAt(i);
+                    // Compute the window touchable frame as shown on the screen.
+
+                    // Get the touchable frame.
+                    Region touchableRegion = mTempRegion1;
+                    windowState.getTouchableRegion(touchableRegion);
+                    Rect touchableFrame = mTempRect;
+                    touchableRegion.getBounds(touchableFrame);
+
+                    // Move to origin as all transforms are captured by the matrix.
+                    RectF windowFrame = mTempRectF;
+                    windowFrame.set(touchableFrame);
+                    windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
+
+                    // Map the frame to get what appears on the screen.
+                    Matrix matrix = mTempMatrix;
+                    populateTransformationMatrixLocked(windowState, matrix);
+                    matrix.mapRect(windowFrame);
+
+                    // Got the bounds.
+                    Rect boundsInScreen = mTempRect;
+                    boundsInScreen.set((int) windowFrame.left, (int) windowFrame.top,
+                            (int) windowFrame.right, (int) windowFrame.bottom);
+
+                    final int flags = windowState.mAttrs.flags;
+
+                    // If the window is not touchable, do not report it but take into account
+                    // the space it takes since the content behind it cannot be touched.
+                    if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) == 1) {
+                        unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+                                Region.Op.DIFFERENCE);
+                        continue;
+                    }
+
+                    // If the window is completely covered by other windows - ignore.
+                    if (unaccountedSpace.quickReject(boundsInScreen)) {
+                        continue;
+                    }
+
+                    // Add windows of certain types not covered by modal windows.
+                    if (isReportedWindowType(windowState.mAttrs.type)) {
+                        // Add the window to the ones to be reported.
+                        WindowInfo window = WindowInfo.obtain();
+                        window.type = windowState.mAttrs.type;
+                        window.layer = windowState.mLayer;
+                        window.token = windowState.mClient.asBinder();
+
+                        addedWindows.add(window.token);
+
+                        WindowState attachedWindow = windowState.mAttachedWindow;
+                        if (attachedWindow != null) {
+                            window.parentToken = attachedWindow.mClient.asBinder();
+                        }
+
+                        window.focused = windowState.isFocused();
+                        window.boundsInScreen.set(boundsInScreen);
+
+                        final int childCount = windowState.mChildWindows.size();
+                        if (childCount > 0) {
+                            if (window.childTokens == null) {
+                                window.childTokens = new ArrayList<IBinder>();
+                            }
+                            for (int j = 0; j < childCount; j++) {
+                                WindowState child = windowState.mChildWindows.get(j);
+                                window.childTokens.add(child.mClient.asBinder());
+                            }
+                        }
+
+                        windows.add(window);
+                    }
+
+                    // Account for the space this window takes.
+                    unaccountedSpace.op(boundsInScreen, unaccountedSpace,
+                            Region.Op.REVERSE_DIFFERENCE);
+
+                    // We figured out what is touchable for the entire screen - done.
+                    if (unaccountedSpace.isEmpty()) {
+                        break;
+                    }
+
+                    // If a window is modal, no other below can be touched - done.
+                    if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
+                        break;
+                    }
+                }
+
+                // Remove child/parent references to windows that were not added.
+                final int windowCount = windows.size();
+                for (int i = 0; i < windowCount; i++) {
+                    WindowInfo window = windows.get(i);
+                    if (!addedWindows.contains(window.parentToken)) {
+                        window.parentToken = null;
+                    }
+                    if (window.childTokens != null) {
+                        final int childTokenCount = window.childTokens.size();
+                        for (int j = childTokenCount - 1; j >= 0; j--) {
+                            if (!addedWindows.contains(window.childTokens.get(j))) {
+                                window.childTokens.remove(j);
+                            }
+                        }
+                        // Leave the child token list if empty.
+                    }
+                }
+
+                visibleWindows.clear();
+                addedWindows.clear();
+
+                // We computed the windows and if they changed notify the client.
+                boolean windowsChanged = false;
+                if (mOldWindows.size() != windows.size()) {
+                    // Different size means something changed.
+                    windowsChanged = true;
+                } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
+                    // Since we always traverse windows from high to low layer
+                    // the old and new windows at the same index should be the
+                    // same, otherwise something changed.
+                    for (int i = 0; i < windowCount; i++) {
+                        WindowInfo oldWindow = mOldWindows.get(i);
+                        WindowInfo newWindow = windows.get(i);
+                        // We do not care for layer changes given the window
+                        // order does not change. This brings no new information
+                        // to the clients.
+                        if (windowChangedNoLayer(oldWindow, newWindow)) {
+                            windowsChanged = true;
+                            break;
+                        }
+                    }
+                }
+
+                if (windowsChanged) {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "Windows changed:" + windows);
+                    }
+                    // Remember the old windows to detect changes.
+                    cacheWindows(windows);
+                    // Announce the change.
+                    mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_WINDOWS_CHANGED,
+                            windows).sendToTarget();
+                } else {
+                    if (DEBUG) {
+                        Log.i(LOG_TAG, "No windows changed.");
+                    }
+                    // Recycle the nodes as we do not need them.
+                    clearAndRecycleWindows(windows);
+                }
+            }
+        }
+
+        private void cacheWindows(List<WindowInfo> windows) {
+            final int oldWindowCount = mOldWindows.size();
+            for (int i = oldWindowCount - 1; i >= 0; i--) {
+                mOldWindows.remove(i).recycle();
+            }
+            final int newWindowCount = windows.size();
+            for (int i = 0; i < newWindowCount; i++) {
+                WindowInfo newWindow = windows.get(i);
+                mOldWindows.add(WindowInfo.obtain(newWindow));
+            }
+        }
+
+        private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
+            if (oldWindow == newWindow) {
+                return false;
+            }
+            if (oldWindow == null && newWindow != null) {
+                return true;
+            }
+            if (oldWindow != null && newWindow == null) {
+                return true;
+            }
+            if (oldWindow.type != newWindow.type) {
+                return true;
+            }
+            if (oldWindow.focused != newWindow.focused) {
+                return true;
+            }
+            if (oldWindow.token == null) {
+                if (newWindow.token != null) {
+                    return true;
+                }
+            } else if (!oldWindow.token.equals(newWindow.token)) {
+                return true;
+            }
+            if (oldWindow.parentToken == null) {
+                if (newWindow.parentToken != null) {
+                    return true;
+                }
+            } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
+                return true;
+            }
+            if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
+                return true;
+            }
+            if (oldWindow.childTokens != null && newWindow.childTokens != null
+                    && !oldWindow.childTokens.equals(newWindow.childTokens)) {
+                return true;
+            }
+            return false;
+        }
+
+        private void clearAndRecycleWindows(List<WindowInfo> windows) {
+            final int windowCount = windows.size();
+            for (int i = windowCount - 1; i >= 0; i--) {
+                windows.remove(i).recycle();
+            }
+        }
+
+        private static boolean isReportedWindowType(int windowType) {
+            return (windowType != WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM
+                    && windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
+                    && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
+                    && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
+                    && windowType != WindowManager.LayoutParams.TYPE_DRAG
+                    && windowType != WindowManager.LayoutParams.TYPE_HIDDEN_NAV_CONSUMER
+                    && windowType != WindowManager.LayoutParams.TYPE_POINTER
+                    && windowType != WindowManager.LayoutParams.TYPE_UNIVERSE_BACKGROUND
+                    && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
+                    && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
+                    && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
+                    && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
+        }
+
+        private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
+            DisplayContent displayContent = mWindowManagerService
+                    .getDefaultDisplayContentLocked();
+            WindowList windowList = displayContent.getWindowList();
+            final int windowCount = windowList.size();
+            for (int i = 0; i < windowCount; i++) {
+                WindowState windowState = windowList.get(i);
+                if (windowState.isVisibleLw()) {
+                    outWindows.put(windowState.mLayer, windowState);
+                }
+            }
+        }
+
+        private class MyHandler extends Handler {
+            public static final int MESSAGE_NOTIFY_WINDOWS_CHANGED = 1;
+
+            public MyHandler(Looper looper) {
+                super(looper, null, false);
+            }
+
+            @Override
+            @SuppressWarnings("unchecked")
+            public void handleMessage(Message message) {
+                switch (message.what) {
+                    case MESSAGE_NOTIFY_WINDOWS_CHANGED: {
+                        List<WindowInfo> windows = (List<WindowInfo>) message.obj;
+                        mCallback.onWindowsForAccessibilityChanged(windows);
+                        clearAndRecycleWindows(windows);
+                    } break;
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 756e06a..7d8b5af 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -19,18 +19,22 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Point;
+import android.graphics.Rect;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IRemoteCallback;
+import android.os.SystemProperties;
 import android.util.Slog;
 import android.view.WindowManager;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationSet;
 import android.view.animation.AnimationUtils;
+import android.view.animation.ClipRectAnimation;
 import android.view.animation.Interpolator;
 import android.view.animation.ScaleAnimation;
 
+import android.view.animation.TranslateAnimation;
 import com.android.internal.util.DumpUtils.Dump;
 import com.android.server.AttributeCache;
 import com.android.server.wm.WindowManagerService.H;
@@ -125,6 +129,12 @@
     private static final int NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN = 4;
     private int mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
 
+    // These are the possible states for the enter/exit activities during a thumbnail transition
+    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_UP = 0;
+    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_UP = 1;
+    private static final int THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN = 2;
+    private static final int THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN = 3;
+
     private String mNextAppTransitionPackage;
     private Bitmap mNextAppTransitionThumbnail;
     // Used for thumbnail transitions. True if we're scaling up, false if scaling down
@@ -148,10 +158,13 @@
     private final Interpolator mThumbnailFadeoutInterpolator;
 
     private int mCurrentUserId = 0;
+    private boolean mUseAlternateThumbnailAnimation;
 
     AppTransition(Context context, Handler h) {
         mContext = context;
         mH = h;
+        mUseAlternateThumbnailAnimation =
+                SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
         mConfigShortAnimTime = context.getResources().getInteger(
                 com.android.internal.R.integer.config_shortAnimTime);
         mDecelerateInterpolator = AnimationUtils.loadInterpolator(context,
@@ -384,80 +397,10 @@
         return a;
     }
 
-    Animation createThumbnailAnimationLocked(int transit, boolean enter, boolean thumb,
-                                    int appWidth, int appHeight) {
-        Animation a;
-        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
-        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
-        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
-        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
-        if (thumb) {
-            // Animation for zooming thumbnail from its initial size to
-            // filling the screen.
-            if (mNextAppTransitionScaleUp) {
-                float scaleW = appWidth / thumbWidth;
-                float scaleH = appHeight / thumbHeight;
-                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
-                        computePivot(mNextAppTransitionStartX, 1 / scaleW),
-                        computePivot(mNextAppTransitionStartY, 1 / scaleH));
-                scale.setInterpolator(mDecelerateInterpolator);
-
-                Animation alpha = new AlphaAnimation(1, 0);
-                alpha.setInterpolator(mThumbnailFadeoutInterpolator);
-
-                // This AnimationSet uses the Interpolators assigned above.
-                AnimationSet set = new AnimationSet(false);
-                set.addAnimation(scale);
-                set.addAnimation(alpha);
-                a = set;
-            } else {
-                float scaleW = appWidth / thumbWidth;
-                float scaleH = appHeight / thumbHeight;
-                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
-                        computePivot(mNextAppTransitionStartX, 1 / scaleW),
-                        computePivot(mNextAppTransitionStartY, 1 / scaleH));
-            }
-        } else if (enter) {
-            // Entering app zooms out from the center of the thumbnail.
-            if (mNextAppTransitionScaleUp) {
-                float scaleW = thumbWidth / appWidth;
-                float scaleH = thumbHeight / appHeight;
-                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
-                        computePivot(mNextAppTransitionStartX, scaleW),
-                        computePivot(mNextAppTransitionStartY, scaleH));
-            } else {
-                // noop animation
-                a = new AlphaAnimation(1, 1);
-            }
-        } else {
-            // Exiting app
-            if (mNextAppTransitionScaleUp) {
-                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
-                    // Fade out while bringing up selected activity. This keeps the
-                    // current activity from showing through a launching wallpaper
-                    // activity.
-                    a = new AlphaAnimation(1, 0);
-                } else {
-                    // noop animation
-                    a = new AlphaAnimation(1, 1);
-                }
-            } else {
-                float scaleW = thumbWidth / appWidth;
-                float scaleH = thumbHeight / appHeight;
-                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
-                        computePivot(mNextAppTransitionStartX, scaleW),
-                        computePivot(mNextAppTransitionStartY, scaleH));
-
-                Animation alpha = new AlphaAnimation(1, 0);
-
-                AnimationSet set = new AnimationSet(true);
-                set.addAnimation(scale);
-                set.addAnimation(alpha);
-                set.setZAdjustment(Animation.ZORDER_TOP);
-                a = set;
-            }
-        }
-
+    /**
+     * Prepares the specified animation with a standard duration, interpolator, etc.
+     */
+    Animation prepareThumbnailAnimation(Animation a, int appWidth, int appHeight, int transit) {
         // Pick the desired duration.  If this is an inter-activity transition,
         // it  is the standard duration for that.  Otherwise we use the longer
         // task transition duration.
@@ -478,9 +421,223 @@
         return a;
     }
 
+    /**
+     * Return the current thumbnail transition state.
+     */
+    int getThumbnailTransitionState(boolean enter) {
+        if (enter) {
+            if (mNextAppTransitionScaleUp) {
+                return THUMBNAIL_TRANSITION_ENTER_SCALE_UP;
+            } else {
+                return THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN;
+            }
+        } else {
+            if (mNextAppTransitionScaleUp) {
+                return THUMBNAIL_TRANSITION_EXIT_SCALE_UP;
+            } else {
+                return THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN;
+            }
+        }
+    }
+
+    /**
+     * This animation runs for the thumbnail that gets cross faded with the enter/exit activity
+     * when a thumbnail is specified with the activity options.
+     */
+    Animation createThumbnailScaleAnimationLocked(int appWidth, int appHeight, int transit) {
+        Animation a;
+        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+        if (mNextAppTransitionScaleUp) {
+            // Animation for the thumbnail zooming from its initial size to the full screen
+            float scaleW = appWidth / thumbWidth;
+            float scaleH = appHeight / thumbHeight;
+            Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
+                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
+            scale.setInterpolator(mDecelerateInterpolator);
+
+            Animation alpha = new AlphaAnimation(1, 0);
+            alpha.setInterpolator(mThumbnailFadeoutInterpolator);
+
+            // This AnimationSet uses the Interpolators assigned above.
+            AnimationSet set = new AnimationSet(false);
+            set.addAnimation(scale);
+            set.addAnimation(alpha);
+            a = set;
+        } else {
+            // Animation for the thumbnail zooming down from the full screen to its final size
+            float scaleW = appWidth / thumbWidth;
+            float scaleH = appHeight / thumbHeight;
+            a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+                    computePivot(mNextAppTransitionStartX, 1 / scaleW),
+                    computePivot(mNextAppTransitionStartY, 1 / scaleH));
+        }
+
+        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+    }
+
+    /**
+     * This alternate animation is created when we are doing a thumbnail transition, for the
+     * activity that is leaving, and the activity that is entering.
+     */
+    Animation createAlternateThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
+                                                    int appHeight, int transit,
+                                                    Rect containingFrame, Rect contentInsets) {
+        Animation a;
+        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+        switch (thumbTransitState) {
+            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
+                // Entering app scales up with the thumbnail
+                float scale = thumbWidth / appWidth;
+                int unscaledThumbHeight = (int) (thumbHeight / scale);
+                int scaledTopDecor = (int) (scale * contentInsets.top);
+                Rect fromClipRect = new Rect(containingFrame);
+                fromClipRect.bottom = (fromClipRect.top + unscaledThumbHeight);
+                Rect toClipRect = new Rect(containingFrame);
+
+                Animation scaleAnim = new ScaleAnimation(scale, 1, scale, 1,
+                        computePivot(mNextAppTransitionStartX, scale),
+                        computePivot(mNextAppTransitionStartY, scale));
+                Animation alphaAnim = new AlphaAnimation(1, 1);
+                Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect);
+                Animation translateAnim = new TranslateAnimation(0, 0, -scaledTopDecor, 0);
+
+                AnimationSet set = new AnimationSet(true);
+                set.addAnimation(alphaAnim);
+                set.addAnimation(clipAnim);
+                set.addAnimation(scaleAnim);
+                set.addAnimation(translateAnim);
+                a = set;
+                break;
+            }
+            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+                // Exiting app while the thumbnail is scaling up should fade
+                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+                    // Fade out while bringing up selected activity. This keeps the
+                    // current activity from showing through a launching wallpaper
+                    // activity.
+                    a = new AlphaAnimation(1, 0);
+                } else {
+                    // noop animation
+                    a = new AlphaAnimation(1, 1);
+                }
+                break;
+            }
+            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+                // Entering the other app, it should just be visible while we scale the thumbnail
+                // down above it
+                a = new AlphaAnimation(1, 1);
+                break;
+            }
+            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+                // Exiting the current app, the app should scale down with the thumbnail
+                float scale = thumbWidth / appWidth;
+                int unscaledThumbHeight = (int) (thumbHeight / scale);
+                int scaledTopDecor = (int) (scale * contentInsets.top);
+                Rect fromClipRect = new Rect(containingFrame);
+                Rect toClipRect = new Rect(containingFrame);
+                toClipRect.bottom = (toClipRect.top + unscaledThumbHeight);
+
+                Animation scaleAnim = new ScaleAnimation(1, scale, 1, scale,
+                        computePivot(mNextAppTransitionStartX, scale),
+                        computePivot(mNextAppTransitionStartY, scale));
+                Animation alphaAnim = new AlphaAnimation(1, 1);
+                Animation clipAnim = new ClipRectAnimation(fromClipRect, toClipRect);
+                Animation translateAnim = new TranslateAnimation(0, 0, 0, -scaledTopDecor);
+
+                AnimationSet set = new AnimationSet(true);
+                set.addAnimation(alphaAnim);
+                set.addAnimation(clipAnim);
+                set.addAnimation(scaleAnim);
+                set.addAnimation(translateAnim);
+
+                a = set;
+                a.setZAdjustment(Animation.ZORDER_TOP);
+                break;
+            }
+            default:
+                throw new RuntimeException("Invalid thumbnail transition state");
+        }
+
+        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+    }
+
+    /**
+     * This animation is created when we are doing a thumbnail transition, for the activity that is
+     * leaving, and the activity that is entering.
+     */
+    Animation createThumbnailEnterExitAnimationLocked(int thumbTransitState, int appWidth,
+                                                    int appHeight, int transit) {
+        Animation a;
+        final int thumbWidthI = mNextAppTransitionThumbnail.getWidth();
+        final float thumbWidth = thumbWidthI > 0 ? thumbWidthI : 1;
+        final int thumbHeightI = mNextAppTransitionThumbnail.getHeight();
+        final float thumbHeight = thumbHeightI > 0 ? thumbHeightI : 1;
+
+        switch (thumbTransitState) {
+            case THUMBNAIL_TRANSITION_ENTER_SCALE_UP: {
+                // Entering app scales up with the thumbnail
+                float scaleW = thumbWidth / appWidth;
+                float scaleH = thumbHeight / appHeight;
+                a = new ScaleAnimation(scaleW, 1, scaleH, 1,
+                        computePivot(mNextAppTransitionStartX, scaleW),
+                        computePivot(mNextAppTransitionStartY, scaleH));
+                break;
+            }
+            case THUMBNAIL_TRANSITION_EXIT_SCALE_UP: {
+                // Exiting app while the thumbnail is scaling up should fade or stay in place
+                if (transit == TRANSIT_WALLPAPER_INTRA_OPEN) {
+                    // Fade out while bringing up selected activity. This keeps the
+                    // current activity from showing through a launching wallpaper
+                    // activity.
+                    a = new AlphaAnimation(1, 0);
+                } else {
+                    // noop animation
+                    a = new AlphaAnimation(1, 1);
+                }
+                break;
+            }
+            case THUMBNAIL_TRANSITION_ENTER_SCALE_DOWN: {
+                // Entering the other app, it should just be visible while we scale the thumbnail
+                // down above it
+                a = new AlphaAnimation(1, 1);
+                break;
+            }
+            case THUMBNAIL_TRANSITION_EXIT_SCALE_DOWN: {
+                // Exiting the current app, the app should scale down with the thumbnail
+                float scaleW = thumbWidth / appWidth;
+                float scaleH = thumbHeight / appHeight;
+                Animation scale = new ScaleAnimation(1, scaleW, 1, scaleH,
+                        computePivot(mNextAppTransitionStartX, scaleW),
+                        computePivot(mNextAppTransitionStartY, scaleH));
+
+                Animation alpha = new AlphaAnimation(1, 0);
+
+                AnimationSet set = new AnimationSet(true);
+                set.addAnimation(scale);
+                set.addAnimation(alpha);
+                set.setZAdjustment(Animation.ZORDER_TOP);
+                a = set;
+                break;
+            }
+            default:
+                throw new RuntimeException("Invalid thumbnail transition state");
+        }
+
+        return prepareThumbnailAnimation(a, appWidth, appHeight, transit);
+    }
+
 
     Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
-                            int appWidth, int appHeight) {
+                            int appWidth, int appHeight, Rect containingFrame, Rect contentInsets) {
         Animation a;
         if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
             a = loadAnimation(mNextAppTransitionPackage, enter ?
@@ -501,7 +658,14 @@
                 mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_DOWN) {
             mNextAppTransitionScaleUp =
                     (mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_SCALE_UP);
-            a = createThumbnailAnimationLocked(transit, enter, false, appWidth, appHeight);
+            if (mUseAlternateThumbnailAnimation) {
+                a = createAlternateThumbnailEnterExitAnimationLocked(
+                        getThumbnailTransitionState(enter), appWidth, appHeight, transit,
+                        containingFrame, contentInsets);
+            } else {
+                a = createThumbnailEnterExitAnimationLocked(getThumbnailTransitionState(enter),
+                        appWidth, appHeight, transit);
+            }
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {
                 String animName = mNextAppTransitionScaleUp ?
                         "ANIM_THUMBNAIL_SCALE_UP" : "ANIM_THUMBNAIL_SCALE_DOWN";
diff --git a/services/core/java/com/android/server/wm/DisplayMagnifier.java b/services/core/java/com/android/server/wm/DisplayMagnifier.java
deleted file mode 100644
index 382d7b4..0000000
--- a/services/core/java/com/android/server/wm/DisplayMagnifier.java
+++ /dev/null
@@ -1,756 +0,0 @@
-/*
- * Copyright (C) 2012 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.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.app.Service;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Paint;
-import android.graphics.Path;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.Region;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Pools.SimplePool;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.TypedValue;
-import android.view.IMagnificationCallbacks;
-import android.view.MagnificationSpec;
-import android.view.Surface;
-import android.view.Surface.OutOfResourcesException;
-import android.view.SurfaceControl;
-import android.view.WindowManager;
-import android.view.WindowManagerPolicy;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.Interpolator;
-
-import com.android.internal.R;
-import com.android.internal.os.SomeArgs;
-
-/**
- * This class is a part of the window manager and encapsulates the
- * functionality related to display magnification.
- */
-final class DisplayMagnifier {
-    private static final String LOG_TAG = DisplayMagnifier.class.getSimpleName();
-
-    private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
-    private static final boolean DEBUG_ROTATION = false;
-    private static final boolean DEBUG_LAYERS = false;
-    private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
-    private static final boolean DEBUG_VIEWPORT_WINDOW = false;
-
-    private final Rect mTempRect1 = new Rect();
-    private final Rect mTempRect2 = new Rect();
-
-    private final Region mTempRegion1 = new Region();
-    private final Region mTempRegion2 = new Region();
-    private final Region mTempRegion3 = new Region();
-    private final Region mTempRegion4 = new Region();
-
-    private final Context mContext;
-    private final WindowManagerService mWindowManagerService;
-    private final MagnifiedViewport mMagnifedViewport;
-    private final Handler mHandler;
-
-    private final IMagnificationCallbacks mCallbacks;
-
-    private final long mLongAnimationDuration;
-
-    public DisplayMagnifier(WindowManagerService windowManagerService,
-            IMagnificationCallbacks callbacks) {
-        mContext = windowManagerService.mContext;
-        mWindowManagerService = windowManagerService;
-        mCallbacks = callbacks;
-        mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
-        mMagnifedViewport = new MagnifiedViewport();
-        mLongAnimationDuration = mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_longAnimTime);
-    }
-
-    public void setMagnificationSpecLocked(MagnificationSpec spec) {
-        mMagnifedViewport.updateMagnificationSpecLocked(spec);
-        mMagnifedViewport.recomputeBoundsLocked();
-        mWindowManagerService.scheduleAnimationLocked();
-    }
-
-    public void onRectangleOnScreenRequestedLocked(Rect rectangle, boolean immediate) {
-        if (DEBUG_RECTANGLE_REQUESTED) {
-            Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
-        }
-        if (!mMagnifedViewport.isMagnifyingLocked()) {
-            return;
-        }
-        Rect magnifiedRegionBounds = mTempRect2;
-        mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
-        if (magnifiedRegionBounds.contains(rectangle)) {
-            return;
-        }
-        SomeArgs args = SomeArgs.obtain();
-        args.argi1 = rectangle.left;
-        args.argi2 = rectangle.top;
-        args.argi3 = rectangle.right;
-        args.argi4 = rectangle.bottom;
-        mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
-                args).sendToTarget();
-    }
-
-    public void onWindowLayersChangedLocked() {
-        if (DEBUG_LAYERS) {
-            Slog.i(LOG_TAG, "Layers changed.");
-        }
-        mMagnifedViewport.recomputeBoundsLocked();
-        mWindowManagerService.scheduleAnimationLocked();
-    }
-
-    public void onRotationChangedLocked(DisplayContent displayContent, int rotation) {
-        if (DEBUG_ROTATION) {
-            Slog.i(LOG_TAG, "Rotaton: " + Surface.rotationToString(rotation)
-                    + " displayId: " + displayContent.getDisplayId());
-        }
-        mMagnifedViewport.onRotationChangedLocked();
-        mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
-    }
-
-    public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
-        if (DEBUG_WINDOW_TRANSITIONS) {
-            Slog.i(LOG_TAG, "Window transition: "
-                    + AppTransition.appTransitionToString(transition)
-                    + " displayId: " + windowState.getDisplayId());
-        }
-        final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
-        if (magnifying) {
-            switch (transition) {
-                case AppTransition.TRANSIT_ACTIVITY_OPEN:
-                case AppTransition.TRANSIT_TASK_OPEN:
-                case AppTransition.TRANSIT_TASK_TO_FRONT:
-                case AppTransition.TRANSIT_WALLPAPER_OPEN:
-                case AppTransition.TRANSIT_WALLPAPER_CLOSE:
-                case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
-                    mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
-                }
-            }
-        }
-    }
-
-    public void onWindowTransitionLocked(WindowState windowState, int transition) {
-        if (DEBUG_WINDOW_TRANSITIONS) {
-            Slog.i(LOG_TAG, "Window transition: "
-                    + AppTransition.appTransitionToString(transition)
-                    + " displayId: " + windowState.getDisplayId());
-        }
-        final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
-        final int type = windowState.mAttrs.type;
-        switch (transition) {
-            case WindowManagerPolicy.TRANSIT_ENTER:
-            case WindowManagerPolicy.TRANSIT_SHOW: {
-                if (!magnifying) {
-                    break;
-                }
-                switch (type) {
-                    case WindowManager.LayoutParams.TYPE_APPLICATION:
-                    case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
-                    case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
-                    case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
-                    case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
-                    case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
-                    case WindowManager.LayoutParams.TYPE_PHONE:
-                    case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
-                    case WindowManager.LayoutParams.TYPE_TOAST:
-                    case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
-                    case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
-                    case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
-                    case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
-                    case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
-                    case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
-                    case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL:
-                    case WindowManager.LayoutParams.TYPE_RECENTS_OVERLAY: {
-                        Rect magnifiedRegionBounds = mTempRect2;
-                        mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
-                                magnifiedRegionBounds);
-                        Rect touchableRegionBounds = mTempRect1;
-                        windowState.getTouchableRegion(mTempRegion1);
-                        mTempRegion1.getBounds(touchableRegionBounds);
-                        if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
-                            try {
-                                mCallbacks.onRectangleOnScreenRequested(
-                                        touchableRegionBounds.left,
-                                        touchableRegionBounds.top,
-                                        touchableRegionBounds.right,
-                                        touchableRegionBounds.bottom);
-                            } catch (RemoteException re) {
-                                /* ignore */
-                            }
-                        }
-                    } break;
-                } break;
-            }
-        }
-    }
-
-    public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
-        MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
-        if (spec != null && !spec.isNop()) {
-            WindowManagerPolicy policy = mWindowManagerService.mPolicy;
-            final int windowType = windowState.mAttrs.type;
-            if (!policy.isTopLevelWindow(windowType) && windowState.mAttachedWindow != null
-                    && !policy.canMagnifyWindow(windowType)) {
-                return null;
-            }
-            if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
-                return null;
-            }
-        }
-        return spec;
-    }
-
-    public void destroyLocked() {
-        mMagnifedViewport.destroyWindow();
-    }
-
-    /** NOTE: This has to be called within a surface transaction. */
-    public void drawMagnifiedRegionBorderIfNeededLocked() {
-        mMagnifedViewport.drawWindowIfNeededLocked();
-    }
-
-    private final class MagnifiedViewport {
-
-        private static final int DEFAUTLT_BORDER_WIDTH_DIP = 5;
-
-        private final SparseArray<WindowStateInfo> mTempWindowStateInfos =
-                new SparseArray<WindowStateInfo>();
-
-        private final float[] mTempFloats = new float[9];
-
-        private final RectF mTempRectF = new RectF();
-
-        private final Point mTempPoint = new Point();
-
-        private final Matrix mTempMatrix = new Matrix();
-
-        private final Region mMagnifiedBounds = new Region();
-        private final Region mOldMagnifiedBounds = new Region();
-
-        private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
-
-        private final WindowManager mWindowManager;
-
-        private final int mBorderWidth;
-        private final int mHalfBorderWidth;
-
-        private final ViewportWindow mWindow;
-
-        private boolean mFullRedrawNeeded;
-
-        public MagnifiedViewport() {
-            mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
-            mBorderWidth = (int) TypedValue.applyDimension(
-                    TypedValue.COMPLEX_UNIT_DIP, DEFAUTLT_BORDER_WIDTH_DIP,
-                            mContext.getResources().getDisplayMetrics());
-            mHalfBorderWidth = (int) (mBorderWidth + 0.5) / 2;
-            mWindow = new ViewportWindow(mContext);
-            recomputeBoundsLocked();
-        }
-
-        public void updateMagnificationSpecLocked(MagnificationSpec spec) {
-            if (spec != null) {
-                mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
-            } else {
-                mMagnificationSpec.clear();
-            }
-            // If this message is pending we are in a rotation animation and do not want
-            // to show the border. We will do so when the pending message is handled.
-            if (!mHandler.hasMessages(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
-                setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
-            }
-        }
-
-        public void recomputeBoundsLocked() {
-            mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-            final int screenWidth = mTempPoint.x;
-            final int screenHeight = mTempPoint.y;
-
-            Region magnifiedBounds = mMagnifiedBounds;
-            magnifiedBounds.set(0, 0, 0, 0);
-
-            Region availableBounds = mTempRegion1;
-            availableBounds.set(0, 0, screenWidth, screenHeight);
-
-            Region nonMagnifiedBounds = mTempRegion4;
-            nonMagnifiedBounds.set(0,  0,  0,  0);
-
-            SparseArray<WindowStateInfo> visibleWindows = mTempWindowStateInfos;
-            visibleWindows.clear();
-            getWindowsOnScreenLocked(visibleWindows);
-
-            final int visibleWindowCount = visibleWindows.size();
-            for (int i = visibleWindowCount - 1; i >= 0; i--) {
-                WindowStateInfo info = visibleWindows.valueAt(i);
-                if (info.mWindowState.mAttrs.type == WindowManager
-                        .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
-                    continue;
-                }
-
-                Region windowBounds = mTempRegion2;
-                Matrix matrix = mTempMatrix;
-                populateTransformationMatrix(info.mWindowState, matrix);
-                RectF windowFrame = mTempRectF;
-
-                if (mWindowManagerService.mPolicy.canMagnifyWindow(info.mWindowState.mAttrs.type)) {
-                    windowFrame.set(info.mWindowState.mFrame);
-                    windowFrame.offset(-windowFrame.left, -windowFrame.top);
-                    matrix.mapRect(windowFrame);
-                    windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
-                            (int) windowFrame.right, (int) windowFrame.bottom);
-                    magnifiedBounds.op(windowBounds, Region.Op.UNION);
-                    magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
-                } else {
-                    windowFrame.set(info.mTouchableRegion);
-                    windowFrame.offset(-info.mWindowState.mFrame.left,
-                            -info.mWindowState.mFrame.top);
-                    matrix.mapRect(windowFrame);
-                    windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
-                            (int) windowFrame.right, (int) windowFrame.bottom);
-                    nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
-                    windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
-                    availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
-                }
-
-                Region accountedBounds = mTempRegion2;
-                accountedBounds.set(magnifiedBounds);
-                accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
-                accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
-
-                if (accountedBounds.isRect()) {
-                    Rect accountedFrame = mTempRect1;
-                    accountedBounds.getBounds(accountedFrame);
-                    if (accountedFrame.width() == screenWidth
-                            && accountedFrame.height() == screenHeight) {
-                        break;
-                    }
-                }
-            }
-
-            for (int i = visibleWindowCount - 1; i >= 0; i--) {
-                WindowStateInfo info = visibleWindows.valueAt(i);
-                info.recycle();
-                visibleWindows.removeAt(i);
-            }
-
-            magnifiedBounds.op(mHalfBorderWidth, mHalfBorderWidth,
-                    screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth,
-                    Region.Op.INTERSECT);
-
-            if (!mOldMagnifiedBounds.equals(magnifiedBounds)) {
-                Region bounds = Region.obtain();
-                bounds.set(magnifiedBounds);
-                mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED,
-                        bounds).sendToTarget();
-
-                mWindow.setBounds(magnifiedBounds);
-                Rect dirtyRect = mTempRect1;
-                if (mFullRedrawNeeded) {
-                    mFullRedrawNeeded = false;
-                    dirtyRect.set(mHalfBorderWidth, mHalfBorderWidth,
-                            screenWidth - mHalfBorderWidth, screenHeight - mHalfBorderWidth);
-                    mWindow.invalidate(dirtyRect);
-                } else {
-                    Region dirtyRegion = mTempRegion3;
-                    dirtyRegion.set(magnifiedBounds);
-                    dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
-                    dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
-                    dirtyRegion.getBounds(dirtyRect);
-                    mWindow.invalidate(dirtyRect);
-                }
-
-                mOldMagnifiedBounds.set(magnifiedBounds);
-            }
-        }
-
-        private void populateTransformationMatrix(WindowState windowState, Matrix outMatrix) {
-            mTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
-            mTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
-            mTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDsDy;
-            mTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDtDy;
-            mTempFloats[Matrix.MTRANS_X] = windowState.mShownFrame.left;
-            mTempFloats[Matrix.MTRANS_Y] = windowState.mShownFrame.top;
-            mTempFloats[Matrix.MPERSP_0] = 0;
-            mTempFloats[Matrix.MPERSP_1] = 0;
-            mTempFloats[Matrix.MPERSP_2] = 1;
-            outMatrix.setValues(mTempFloats);
-        }
-
-        private void getWindowsOnScreenLocked(SparseArray<WindowStateInfo> outWindowStates) {
-            DisplayContent displayContent = mWindowManagerService.getDefaultDisplayContentLocked();
-            WindowList windowList = displayContent.getWindowList();
-            final int windowCount = windowList.size();
-            for (int i = 0; i < windowCount; i++) {
-                WindowState windowState = windowList.get(i);
-                if ((windowState.isOnScreen() || windowState.mAttrs.type == WindowManager
-                        .LayoutParams.TYPE_UNIVERSE_BACKGROUND)
-                        && !windowState.mWinAnimator.mEnterAnimationPending) {
-                    outWindowStates.put(windowState.mLayer, WindowStateInfo.obtain(windowState));
-                }
-            }
-        }
-
-        public void onRotationChangedLocked() {
-            // If we are magnifying, hide the magnified border window immediately so
-            // the user does not see strange artifacts during rotation. The screenshot
-            // used for rotation has already the border. After the rotation is complete
-            // we will show the border.
-            if (isMagnifyingLocked()) {
-                setMagnifiedRegionBorderShownLocked(false, false);
-                final long delay = (long) (mLongAnimationDuration
-                        * mWindowManagerService.mWindowAnimationScale);
-                Message message = mHandler.obtainMessage(
-                        MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
-                mHandler.sendMessageDelayed(message, delay);
-            }
-            recomputeBoundsLocked();
-            mWindow.updateSize();
-        }
-
-        public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
-            if (shown) {
-                mFullRedrawNeeded = true;
-                mOldMagnifiedBounds.set(0,  0,  0,  0);
-            }
-            mWindow.setShown(shown, animate);
-        }
-
-        public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
-            MagnificationSpec spec = mMagnificationSpec;
-            mMagnifiedBounds.getBounds(rect);
-            rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
-            rect.scale(1.0f / spec.scale);
-        }
-
-        public boolean isMagnifyingLocked() {
-            return mMagnificationSpec.scale > 1.0f;
-        }
-
-        public MagnificationSpec getMagnificationSpecLocked() {
-            return mMagnificationSpec;
-        }
-
-        /** NOTE: This has to be called within a surface transaction. */
-        public void drawWindowIfNeededLocked() {
-            recomputeBoundsLocked();
-            mWindow.drawIfNeeded();
-        }
-
-        public void destroyWindow() {
-            mWindow.releaseSurface();
-        }
-
-        private final class ViewportWindow {
-            private static final String SURFACE_TITLE = "Magnification Overlay";
-
-            private static final String PROPERTY_NAME_ALPHA = "alpha";
-
-            private static final int MIN_ALPHA = 0;
-            private static final int MAX_ALPHA = 255;
-
-            private final Region mBounds = new Region();
-            private final Rect mDirtyRect = new Rect();
-            private final Paint mPaint = new Paint();
-
-            private final ValueAnimator mShowHideFrameAnimator;
-            private final SurfaceControl mSurfaceControl;
-            private final Surface mSurface = new Surface();
-
-            private boolean mShown;
-            private int mAlpha;
-
-            private boolean mInvalidated;
-
-            public ViewportWindow(Context context) {
-                SurfaceControl surfaceControl = null;
-                try {
-                    mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                    surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession, SURFACE_TITLE,
-                            mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT, SurfaceControl.HIDDEN);
-                } catch (OutOfResourcesException oore) {
-                    /* ignore */
-                }
-                mSurfaceControl = surfaceControl;
-                mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay().getLayerStack());
-                mSurfaceControl.setLayer(mWindowManagerService.mPolicy.windowTypeToLayerLw(
-                        WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
-                        * WindowManagerService.TYPE_LAYER_MULTIPLIER);
-                mSurfaceControl.setPosition(0, 0);
-                mSurface.copyFrom(mSurfaceControl);
-
-                TypedValue typedValue = new TypedValue();
-                context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
-                        typedValue, true);
-                final int borderColor = context.getResources().getColor(typedValue.resourceId);
-
-                mPaint.setStyle(Paint.Style.STROKE);
-                mPaint.setStrokeWidth(mBorderWidth);
-                mPaint.setColor(borderColor);
-
-                Interpolator interpolator = new DecelerateInterpolator(2.5f);
-                final long longAnimationDuration = context.getResources().getInteger(
-                        com.android.internal.R.integer.config_longAnimTime);
-
-                mShowHideFrameAnimator = ObjectAnimator.ofInt(this, PROPERTY_NAME_ALPHA,
-                        MIN_ALPHA, MAX_ALPHA);
-                mShowHideFrameAnimator.setInterpolator(interpolator);
-                mShowHideFrameAnimator.setDuration(longAnimationDuration);
-                mInvalidated = true;
-            }
-
-            public void setShown(boolean shown, boolean animate) {
-                synchronized (mWindowManagerService.mWindowMap) {
-                    if (mShown == shown) {
-                        return;
-                    }
-                    mShown = shown;
-                    if (animate) {
-                        if (mShowHideFrameAnimator.isRunning()) {
-                            mShowHideFrameAnimator.reverse();
-                        } else {
-                            if (shown) {
-                                mShowHideFrameAnimator.start();
-                            } else {
-                                mShowHideFrameAnimator.reverse();
-                            }
-                        }
-                    } else {
-                        mShowHideFrameAnimator.cancel();
-                        if (shown) {
-                            setAlpha(MAX_ALPHA);
-                        } else {
-                            setAlpha(MIN_ALPHA);
-                        }
-                    }
-                    if (DEBUG_VIEWPORT_WINDOW) {
-                        Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
-                    }
-                }
-            }
-
-            @SuppressWarnings("unused")
-            // Called reflectively from an animator.
-            public int getAlpha() {
-                synchronized (mWindowManagerService.mWindowMap) {
-                    return mAlpha;
-                }
-            }
-
-            public void setAlpha(int alpha) {
-                synchronized (mWindowManagerService.mWindowMap) {
-                    if (mAlpha == alpha) {
-                        return;
-                    }
-                    mAlpha = alpha;
-                    invalidate(null);
-                    if (DEBUG_VIEWPORT_WINDOW) {
-                        Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
-                    }
-                }
-            }
-
-            public void setBounds(Region bounds) {
-                synchronized (mWindowManagerService.mWindowMap) {
-                    if (mBounds.equals(bounds)) {
-                        return;
-                    }
-                    mBounds.set(bounds);
-                    invalidate(mDirtyRect);
-                    if (DEBUG_VIEWPORT_WINDOW) {
-                        Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
-                    }
-                }
-            }
-
-            public void updateSize() {
-                synchronized (mWindowManagerService.mWindowMap) {
-                    mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
-                    mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
-                    invalidate(mDirtyRect);
-                }
-            }
-
-            public void invalidate(Rect dirtyRect) {
-                if (dirtyRect != null) {
-                    mDirtyRect.set(dirtyRect);
-                } else {
-                    mDirtyRect.setEmpty();
-                }
-                mInvalidated = true;
-                mWindowManagerService.scheduleAnimationLocked();
-            }
-
-            /** NOTE: This has to be called within a surface transaction. */
-            public void drawIfNeeded() {
-                synchronized (mWindowManagerService.mWindowMap) {
-                    if (!mInvalidated) {
-                        return;
-                    }
-                    mInvalidated = false;
-                    Canvas canvas = null;
-                    try {
-                        // Empty dirty rectangle means unspecified.
-                        if (mDirtyRect.isEmpty()) {
-                            mBounds.getBounds(mDirtyRect);
-                        }
-                        mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
-                        canvas = mSurface.lockCanvas(mDirtyRect);
-                        if (DEBUG_VIEWPORT_WINDOW) {
-                            Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
-                        }
-                    } catch (IllegalArgumentException iae) {
-                        /* ignore */
-                    } catch (Surface.OutOfResourcesException oore) {
-                        /* ignore */
-                    }
-                    if (canvas == null) {
-                        return;
-                    }
-                    if (DEBUG_VIEWPORT_WINDOW) {
-                        Slog.i(LOG_TAG, "Bounds: " + mBounds);
-                    }
-                    canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
-                    mPaint.setAlpha(mAlpha);
-                    Path path = mBounds.getBoundaryPath();
-                    canvas.drawPath(path, mPaint);
-
-                    mSurface.unlockCanvasAndPost(canvas);
-
-                    if (mAlpha > 0) {
-                        mSurfaceControl.show();
-                    } else {
-                        mSurfaceControl.hide();
-                    }
-                }
-            }
-
-            public void releaseSurface() {
-                mSurfaceControl.release();
-                mSurface.release();
-            }
-        }
-    }
-
-    private static final class WindowStateInfo {
-        private static final int MAX_POOL_SIZE = 30;
-
-        private static final SimplePool<WindowStateInfo> sPool =
-                new SimplePool<WindowStateInfo>(MAX_POOL_SIZE);
-
-        private static final Region mTempRegion = new Region();
-
-        public WindowState mWindowState;
-        public final Rect mTouchableRegion = new Rect();
-
-        public static WindowStateInfo obtain(WindowState windowState) {
-            WindowStateInfo info = sPool.acquire();
-            if (info == null) {
-                info = new WindowStateInfo();
-            }
-            info.mWindowState = windowState;
-            windowState.getTouchableRegion(mTempRegion);
-            mTempRegion.getBounds(info.mTouchableRegion);
-            return info;
-        }
-
-        public void recycle() {
-            mWindowState = null;
-            mTouchableRegion.setEmpty();
-            sPool.release(this);
-        }
-    }
-
-    private class MyHandler extends Handler {
-        public static final int MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED = 1;
-        public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
-        public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
-        public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
-        public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
-
-        public MyHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message message) {
-            switch (message.what) {
-                case MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED: {
-                    Region bounds = (Region) message.obj;
-                    try {
-                        mCallbacks.onMagnifedBoundsChanged(bounds);
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    } finally {
-                        bounds.recycle();
-                    }
-                } break;
-                case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
-                    SomeArgs args = (SomeArgs) message.obj;
-                    final int left = args.argi1;
-                    final int top = args.argi2;
-                    final int right = args.argi3;
-                    final int bottom = args.argi4;
-                    try {
-                        mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    } finally {
-                        args.recycle();
-                    }
-                } break;
-                case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
-                    try {
-                        mCallbacks.onUserContextChanged();
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    }
-                } break;
-                case MESSAGE_NOTIFY_ROTATION_CHANGED: {
-                    final int rotation = message.arg1;
-                    try {
-                        mCallbacks.onRotationChanged(rotation);
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    }
-                } break;
-                case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
-                    synchronized (mWindowManagerService.mWindowMap) {
-                        if (mMagnifedViewport.isMagnifyingLocked()) {
-                            mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
-                            mWindowManagerService.scheduleAnimationLocked();
-                        }
-                    }
-                } break;
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 0c68258..266527d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -529,8 +529,9 @@
                 mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers();
 
                 //TODO (multidisplay): Magnification is supported only for the default display.
-                if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
-                    mService.mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
+                if (mService.mAccessibilityController != null
+                        && displayId == Display.DEFAULT_DISPLAY) {
+                    mService.mAccessibilityController.drawMagnifiedRegionBorderIfNeededLocked();
                 }
             }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a4f960e..4f80f1f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -103,7 +103,6 @@
 import android.view.Gravity;
 import android.view.IApplicationToken;
 import android.view.IInputFilter;
-import android.view.IMagnificationCallbacks;
 import android.view.IOnKeyguardExitResult;
 import android.view.IRotationWatcher;
 import android.view.IWindow;
@@ -418,7 +417,7 @@
 
     IInputMethodManager mInputMethodManager;
 
-    DisplayMagnifier mDisplayMagnifier;
+    AccessibilityController mAccessibilityController;
 
     final SurfaceSession mFxSession;
     Watermark mWatermark;
@@ -2439,9 +2438,9 @@
                     win.mExiting = true;
                 }
                 //TODO (multidisplay): Magnification is supported only for the default display.
-                if (mDisplayMagnifier != null
+                if (mAccessibilityController != null
                         && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                    mDisplayMagnifier.onWindowTransitionLocked(win, transit);
+                    mAccessibilityController.onWindowTransitionLocked(win, transit);
                 }
             }
             if (win.mExiting || win.mWinAnimator.isAnimating()) {
@@ -2759,11 +2758,12 @@
 
     public void onRectangleOnScreenRequested(IBinder token, Rect rectangle, boolean immediate) {
         synchronized (mWindowMap) {
-            if (mDisplayMagnifier != null) {
+            if (mAccessibilityController != null) {
                 WindowState window = mWindowMap.get(token);
                 //TODO (multidisplay): Magnification is supported only for the default display.
                 if (window != null && window.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                    mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle, immediate);
+                    mAccessibilityController.onRectangleOnScreenRequestedLocked(rectangle,
+                            immediate);
                 }
             }
         }
@@ -2998,9 +2998,9 @@
                             winAnimator.destroySurfaceLocked();
                         }
                         //TODO (multidisplay): Magnification is supported only for the default
-                        if (mDisplayMagnifier != null
+                        if (mAccessibilityController != null
                                 && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                            mDisplayMagnifier.onWindowTransitionLocked(win, transit);
+                            mAccessibilityController.onWindowTransitionLocked(win, transit);
                         }
                     }
                 }
@@ -3143,86 +3143,6 @@
         }
     }
 
-    @Override
-    public void getWindowFrame(IBinder token, Rect outBounds) {
-        if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
-                "getWindowInfo()")) {
-            throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
-        }
-        synchronized (mWindowMap) {
-            WindowState windowState = mWindowMap.get(token);
-            if (windowState != null) {
-                outBounds.set(windowState.mFrame);
-            } else {
-                outBounds.setEmpty();
-            }
-        }
-    }
-
-    @Override
-    public void setMagnificationSpec(MagnificationSpec spec) {
-        if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
-                "setMagnificationSpec()")) {
-            throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
-        }
-        synchronized (mWindowMap) {
-            if (mDisplayMagnifier != null) {
-                mDisplayMagnifier.setMagnificationSpecLocked(spec);
-            } else {
-                throw new IllegalStateException("Magnification callbacks not set!");
-            }
-        }
-        if (Binder.getCallingPid() != android.os.Process.myPid()) {
-            spec.recycle();
-        }
-    }
-
-    @Override
-    public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
-        if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
-                "getCompatibleMagnificationSpecForWindow()")) {
-            throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
-        }
-        synchronized (mWindowMap) {
-            WindowState windowState = mWindowMap.get(windowToken);
-            if (windowState == null) {
-                return null;
-            }
-            MagnificationSpec spec = null;
-            if (mDisplayMagnifier != null) {
-                spec = mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
-            }
-            if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
-                return null;
-            }
-            spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec);
-            spec.scale *= windowState.mGlobalScale;
-            return spec;
-        }
-    }
-
-    @Override
-    public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) {
-        if (!checkCallingPermission(android.Manifest.permission.MAGNIFY_DISPLAY,
-                "setMagnificationCallbacks()")) {
-            throw new SecurityException("Requires MAGNIFY_DISPLAY permission.");
-        }
-        synchronized (mWindowMap) {
-            if (mDisplayMagnifier == null) {
-                mDisplayMagnifier = new DisplayMagnifier(this, callbacks);
-            } else {
-                if (callbacks == null) {
-                    if (mDisplayMagnifier != null) {
-                        mDisplayMagnifier.destroyLocked();
-                        mDisplayMagnifier = null;
-                    }
-                } else {
-                    throw new IllegalStateException("Magnification callbacks already set!");
-                }
-            }
-        }
-    }
-
     private boolean applyAnimationLocked(AppWindowToken atoken,
             WindowManager.LayoutParams lp, int transit, boolean enter) {
         // Only apply an animation if the display isn't frozen.  If it is
@@ -3235,7 +3155,22 @@
             final int height = displayInfo.appHeight;
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG, "applyAnimation: atoken="
                     + atoken);
-            Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height);
+
+            // Determine the visible rect to calculate the thumbnail clip
+            WindowState win = atoken.findMainWindow();
+            Rect containingFrame = new Rect(0, 0, width, height);
+            Rect contentInsets = new Rect();
+            if (win != null) {
+                if (win.mContainingFrame != null) {
+                    containingFrame.set(win.mContainingFrame);
+                }
+                if (win.mContentInsets != null) {
+                    contentInsets.set(win.mContentInsets);
+                }
+            }
+
+            Animation a = mAppTransition.loadAnimation(lp, transit, enter, width, height,
+                    containingFrame, contentInsets);
             if (a != null) {
                 if (DEBUG_ANIM) {
                     RuntimeException e = null;
@@ -3402,8 +3337,8 @@
                             win.mWinAnimator.applyAnimationLocked(WindowManagerPolicy.TRANSIT_EXIT,
                                     false);
                             //TODO (multidisplay): Magnification is supported only for the default
-                            if (mDisplayMagnifier != null && win.isDefaultDisplay()) {
-                                mDisplayMagnifier.onWindowTransitionLocked(win,
+                            if (mAccessibilityController != null && win.isDefaultDisplay()) {
+                                mAccessibilityController.onWindowTransitionLocked(win,
                                         WindowManagerPolicy.TRANSIT_EXIT);
                             }
                             changed = true;
@@ -4259,9 +4194,9 @@
                 }
                 WindowState window = wtoken.findMainWindow();
                 //TODO (multidisplay): Magnification is supported only for the default display.
-                if (window != null && mDisplayMagnifier != null
+                if (window != null && mAccessibilityController != null
                         && window.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                    mDisplayMagnifier.onAppWindowTransitionLocked(window, transit);
+                    mAccessibilityController.onAppWindowTransitionLocked(window, transit);
                 }
                 changed = true;
             }
@@ -4281,9 +4216,9 @@
                             win.mWinAnimator.applyAnimationLocked(
                                     WindowManagerPolicy.TRANSIT_ENTER, true);
                             //TODO (multidisplay): Magnification is supported only for the default
-                            if (mDisplayMagnifier != null
+                            if (mAccessibilityController != null
                                     && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                                mDisplayMagnifier.onWindowTransitionLocked(win,
+                                mAccessibilityController.onWindowTransitionLocked(win,
                                         WindowManagerPolicy.TRANSIT_ENTER);
                             }
                         }
@@ -4298,9 +4233,9 @@
                         win.mWinAnimator.applyAnimationLocked(
                                 WindowManagerPolicy.TRANSIT_EXIT, false);
                         //TODO (multidisplay): Magnification is supported only for the default
-                        if (mDisplayMagnifier != null
+                        if (mAccessibilityController != null
                                 && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
-                            mDisplayMagnifier.onWindowTransitionLocked(win,
+                            mAccessibilityController.onWindowTransitionLocked(win,
                                     WindowManagerPolicy.TRANSIT_EXIT);
                         }
                     }
@@ -5278,19 +5213,6 @@
         ShutdownThread.rebootSafeMode(mContext, confirm);
     }
 
-    @Override
-    public void setInputFilter(IInputFilter filter) {
-        if (!checkCallingPermission(android.Manifest.permission.FILTER_EVENTS, "setInputFilter()")) {
-            throw new SecurityException("Requires FILTER_EVENTS permission");
-        }
-        mInputManager.setInputFilter(filter);
-    }
-
-    @Override
-    public void setTouchExplorationEnabled(boolean enabled) {
-        mPolicy.setTouchExplorationEnabled(enabled);
-    }
-
     public void updateRelatedUserIds(final int[] relatedUserIds) {
         synchronized (mWindowMap) {
             mRelatedUserIds = relatedUserIds;
@@ -6106,9 +6028,9 @@
         }
 
         //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mDisplayMagnifier != null
+        if (mAccessibilityController != null
                 && displayContent.getDisplayId() == Display.DEFAULT_DISPLAY) {
-            mDisplayMagnifier.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation);
+            mAccessibilityController.onRotationChangedLocked(getDefaultDisplayContentLocked(), rotation);
         }
 
         return true;
@@ -7015,21 +6937,6 @@
         }
     }
 
-    @Override
-    public IBinder getFocusedWindowToken() {
-        if (!checkCallingPermission(android.Manifest.permission.RETRIEVE_WINDOW_INFO,
-                "getFocusedWindowToken()")) {
-            throw new SecurityException("Requires RETRIEVE_WINDOW_INFO permission.");
-        }
-        synchronized (mWindowMap) {
-            WindowState windowState = getFocusedWindowLocked();
-            if (windowState != null) {
-                return windowState.mClient.asBinder();
-            }
-            return null;
-        }
-    }
-
     private WindowState getFocusedWindow() {
         synchronized (mWindowMap) {
             return getFocusedWindowLocked();
@@ -8209,9 +8116,9 @@
         }
 
         //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mDisplayMagnifier != null && anyLayerChanged
+        if (mAccessibilityController != null && anyLayerChanged
                 && windows.get(windows.size() - 1).getDisplayId() == Display.DEFAULT_DISPLAY) {
-            mDisplayMagnifier.onWindowLayersChangedLocked();
+            mAccessibilityController.onWindowLayersChangedLocked();
         }
     }
 
@@ -8716,11 +8623,13 @@
                 wtoken.deferClearAllDrawn = false;
             }
 
+            boolean useAlternateThumbnailAnimation =
+                            SystemProperties.getBoolean("persist.anim.use_alt_thumbnail", false);
             AppWindowAnimator appAnimator =
                     topOpeningApp == null ? null : topOpeningApp.mAppAnimator;
             Bitmap nextAppTransitionThumbnail = mAppTransition.getNextAppTransitionThumbnail();
-            if (nextAppTransitionThumbnail != null && appAnimator != null
-                    && appAnimator.animation != null) {
+            if (!useAlternateThumbnailAnimation && nextAppTransitionThumbnail != null
+                    && appAnimator != null && appAnimator.animation != null) {
                 // This thumbnail animation is very special, we need to have
                 // an extra surface with the thumbnail included with the animation.
                 Rect dirty = new Rect(0, 0, nextAppTransitionThumbnail.getWidth(),
@@ -8744,8 +8653,8 @@
                     drawSurface.release();
                     appAnimator.thumbnailLayer = topOpeningLayer;
                     DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
-                    Animation anim = mAppTransition.createThumbnailAnimationLocked(
-                            transit, true, true, displayInfo.appWidth, displayInfo.appHeight);
+                    Animation anim = mAppTransition.createThumbnailScaleAnimationLocked(
+                            displayInfo.appWidth, displayInfo.appHeight, transit);
                     appAnimator.thumbnailAnimation = anim;
                     anim.restrictDuration(MAX_ANIMATION_DURATION);
                     anim.scaleCurrentDuration(mTransitionAnimationScale);
@@ -9844,6 +9753,11 @@
             final WindowState oldFocus = mCurrentFocus;
             mCurrentFocus = newFocus;
             mLosingFocus.remove(newFocus);
+
+            if (mAccessibilityController != null) {
+                mAccessibilityController.onWindowFocusChangedLocked();
+            }
+
             int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
 
             if (imWindowChanged && oldFocus != mInputMethodWindow) {
@@ -10957,5 +10871,100 @@
         public void requestTraversalFromDisplayManager() {
             requestTraversal();
         }
+
+        @Override
+        public void setMagnificationSpec(MagnificationSpec spec) {
+            synchronized (mWindowMap) {
+                if (mAccessibilityController != null) {
+                    mAccessibilityController.setMagnificationSpecLocked(spec);
+                } else {
+                    throw new IllegalStateException("Magnification callbacks not set!");
+                }
+            }
+            if (Binder.getCallingPid() != android.os.Process.myPid()) {
+                spec.recycle();
+            }
+        }
+
+        @Override
+        public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
+            synchronized (mWindowMap) {
+                WindowState windowState = mWindowMap.get(windowToken);
+                if (windowState == null) {
+                    return null;
+                }
+                MagnificationSpec spec = null;
+                if (mAccessibilityController != null) {
+                    spec = mAccessibilityController.getMagnificationSpecForWindowLocked(windowState);
+                }
+                if ((spec == null || spec.isNop()) && windowState.mGlobalScale == 1.0f) {
+                    return null;
+                }
+                spec = (spec == null) ? MagnificationSpec.obtain() : MagnificationSpec.obtain(spec);
+                spec.scale *= windowState.mGlobalScale;
+                return spec;
+            }
+        }
+
+        @Override
+        public void setMagnificationCallbacks(MagnificationCallbacks callbacks) {
+            synchronized (mWindowMap) {
+                if (mAccessibilityController == null) {
+                    mAccessibilityController = new AccessibilityController(
+                            WindowManagerService.this);
+                }
+                mAccessibilityController.setMagnificationCallbacksLocked(callbacks);
+                if (!mAccessibilityController.hasCallbacksLocked()) {
+                     mAccessibilityController = null;
+                }
+            }
+        }
+
+        @Override
+        public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
+            synchronized (mWindowMap) {
+                if (mAccessibilityController == null) {
+                    mAccessibilityController = new AccessibilityController(
+                            WindowManagerService.this);
+                }
+                mAccessibilityController.setWindowsForAccessibilityCallback(callback);
+                if (!mAccessibilityController.hasCallbacksLocked()) {
+                     mAccessibilityController = null;
+                }
+            }
+        }
+
+        @Override
+        public void setInputFilter(IInputFilter filter) {
+            mInputManager.setInputFilter(filter);
+        }
+
+        @Override
+        public IBinder getFocusedWindowToken() {
+            synchronized (mWindowMap) {
+                WindowState windowState = getFocusedWindowLocked();
+                if (windowState != null) {
+                    return windowState.mClient.asBinder();
+                }
+                return null;
+            }
+        }
+
+        @Override
+        public boolean isKeyguardLocked() {
+            return isKeyguardLocked();
+        }
+
+        @Override
+        public void getWindowFrame(IBinder token, Rect outBounds) {
+            synchronized (mWindowMap) {
+                WindowState windowState = mWindowMap.get(token);
+                if (windowState != null) {
+                    outBounds.set(windowState.mFrame);
+                } else {
+                    outBounds.setEmpty();
+                }
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 93f6d22..6b3c368 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -113,6 +113,11 @@
     float mAlpha = 0;
     float mLastAlpha = 0;
 
+    boolean mHasClipRect;
+    Rect mClipRect = new Rect();
+    Rect mTmpClipRect = new Rect();
+    Rect mLastClipRect = new Rect();
+
     // Used to save animation distances between the time they are calculated and when they are
     // used.
     int mAnimDw;
@@ -951,9 +956,10 @@
             if (screenAnimation) {
                 tmpMatrix.postConcat(screenRotationAnimation.getEnterTransformation().getMatrix());
             }
+
             //TODO (multidisplay): Magnification is supported only for the default display.
-            if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
-                MagnificationSpec spec = mService.mDisplayMagnifier
+            if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+                MagnificationSpec spec = mService.mAccessibilityController
                         .getMagnificationSpecForWindowLocked(mWin);
                 if (spec != null && !spec.isNop()) {
                     tmpMatrix.postScale(spec.scale, spec.scale);
@@ -985,6 +991,7 @@
             // transforming since it is more important to have that
             // animation be smooth.
             mShownAlpha = mAlpha;
+            mHasClipRect = false;
             if (!mService.mLimitedAlphaCompositing
                     || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format)
                     || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy)
@@ -998,6 +1005,10 @@
                 }
                 if (appTransformation != null) {
                     mShownAlpha *= appTransformation.getAlpha();
+                    if (appTransformation.hasClipRect()) {
+                        mClipRect.set(appTransformation.getClipRect());
+                        mHasClipRect = true;
+                    }
                 }
                 if (mAnimator.mUniverseBackground != null) {
                     mShownAlpha *= mAnimator.mUniverseBackground.mUniverseTransform.getAlpha();
@@ -1032,8 +1043,8 @@
                 && mWin.mBaseLayer < mAnimator.mAboveUniverseLayer);
         MagnificationSpec spec = null;
         //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mDisplayMagnifier != null && displayId == Display.DEFAULT_DISPLAY) {
-            spec = mService.mDisplayMagnifier.getMagnificationSpecForWindowLocked(mWin);
+        if (mService.mAccessibilityController != null && displayId == Display.DEFAULT_DISPLAY) {
+            spec = mService.mAccessibilityController.getMagnificationSpecForWindowLocked(mWin);
         }
         if (applyUniverseTransformation || spec != null) {
             final Rect frame = mWin.mFrame;
@@ -1149,15 +1160,32 @@
             applyDecorRect(w.mDecorFrame);
         }
 
-        if (!w.mSystemDecorRect.equals(w.mLastSystemDecorRect)) {
-            w.mLastSystemDecorRect.set(w.mSystemDecorRect);
+        // By default, the clip rect is the system decor rect
+        Rect clipRect = w.mSystemDecorRect;
+        if (mHasClipRect) {
+
+            // If we have an animated clip rect, intersect it with the system decor rect
+            // NOTE: We are adding a temporary workaround due to the status bar not always reporting
+            // the correct system decor rect.  In such cases, we take into account the specified
+            // content insets as well.
+            int offsetTop = Math.max(w.mSystemDecorRect.top, w.mContentInsets.top);
+            mTmpClipRect.set(w.mSystemDecorRect);
+            mTmpClipRect.offset(0, -offsetTop);
+            mTmpClipRect.intersect(mClipRect);
+            mTmpClipRect.offset(0, offsetTop);
+            clipRect = mTmpClipRect;
+
+        }
+
+        if (!clipRect.equals(mLastClipRect)) {
+            mLastClipRect.set(clipRect);
             try {
                 if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
-                        "CROP " + w.mSystemDecorRect.toShortString(), null);
-                mSurfaceControl.setWindowCrop(w.mSystemDecorRect);
+                        "CROP " + clipRect.toShortString(), null);
+                mSurfaceControl.setWindowCrop(clipRect);
             } catch (RuntimeException e) {
                 Slog.w(TAG, "Error setting crop surface of " + w
-                        + " crop=" + w.mSystemDecorRect.toShortString(), e);
+                        + " crop=" + clipRect.toShortString(), e);
                 if (!recoveringMemory) {
                     mService.reclaimSomeSurfaceMemoryLocked(this, "crop", true);
                 }
@@ -1302,8 +1330,8 @@
                     mSurfaceLayer = mAnimLayer;
                     mSurfaceControl.setLayer(mAnimLayer);
                     mSurfaceControl.setMatrix(
-                        mDsDx*w.mHScale, mDtDx*w.mVScale,
-                        mDsDy*w.mHScale, mDtDy*w.mVScale);
+                            mDsDx * w.mHScale, mDtDx * w.mVScale,
+                            mDsDy * w.mHScale, mDtDy * w.mVScale);
 
                     if (mLastHidden && mDrawState == HAS_DRAWN) {
                         if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
@@ -1565,9 +1593,9 @@
         }
         applyAnimationLocked(transit, true);
         //TODO (multidisplay): Magnification is supported only for the default display.
-        if (mService.mDisplayMagnifier != null
+        if (mService.mAccessibilityController != null
                 && mWin.getDisplayId() == Display.DEFAULT_DISPLAY) {
-            mService.mDisplayMagnifier.onWindowTransitionLocked(mWin, transit);
+            mService.mAccessibilityController.onWindowTransitionLocked(mWin, transit);
         }
     }
 
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 0607925..1b3887c 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -8,23 +8,26 @@
     $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
     $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_hdmi_HdmiCecService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputApplicationHandle.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_input_InputWindowHandle.cpp \
     $(LOCAL_REL_DIR)/com_android_server_lights_LightsService.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \
+    $(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
     $(LOCAL_REL_DIR)/com_android_server_power_PowerManagerService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_SerialService.cpp \
     $(LOCAL_REL_DIR)/com_android_server_SystemServer.cpp \
     $(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
     $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_location_GpsLocationProvider.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_location_FlpHardwareProvider.cpp \
-    $(LOCAL_REL_DIR)/com_android_server_connectivity_Vpn.cpp \
     $(LOCAL_REL_DIR)/onload.cpp
 
+include external/stlport/libstlport.mk
+
 LOCAL_C_INCLUDES += \
     $(JNI_H_INCLUDE) \
     frameworks/base/services \
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 22cc519..da4cc48 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -139,16 +139,21 @@
             }
             endpos++;
         }
-        if (i == 0) {
+        // For now we are not separating out the first irq.
+        // This is because in practice there are always multiple
+        // lines of wakeup reasons, so it is better to just treat
+        // them all together as a single string.
+        if (false && i == 0) {
             firstirq = irq;
         } else {
-            int len = snprintf(mergedreasonpos, remainreasonlen, ":%d", irq);
+            int len = snprintf(mergedreasonpos, remainreasonlen,
+                    i == 0 ? "%d" : ":%d", irq);
             if (len >= 0 && len < remainreasonlen) {
                 mergedreasonpos += len;
                 remainreasonlen -= len;
             }
         }
-        int len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%s" : ":%s", pos);
+        int len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
         if (len >= 0 && len < remainreasonlen) {
             mergedreasonpos += len;
             remainreasonlen -= len;
diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
new file mode 100644
index 0000000..6170c09
--- /dev/null
+++ b/services/core/jni/com_android_server_hdmi_HdmiCecService.cpp
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define LOG_TAG "HdmiCecJni"
+
+#define LOG_NDEBUG 1
+
+#include "ScopedPrimitiveArray.h"
+
+#include <cstring>
+#include <deque>
+#include <map>
+
+#include <android_runtime/AndroidRuntime.h>
+#include <android_runtime/Log.h>
+#include <hardware/hdmi_cec.h>
+
+namespace android {
+
+static struct {
+    jmethodID handleMessage;
+    jmethodID handleHotplug;
+    jmethodID getActiveSource;
+    jmethodID getOsdName;
+    jmethodID getLanguage;
+} gHdmiCecServiceClassInfo;
+
+#ifndef min
+#define min(a, b) ((a) > (b) ? (b) : (a))
+#endif
+
+class HdmiCecHandler {
+public:
+    enum HdmiCecError {
+        SUCCESS = 0,
+        FAILED = -1
+    };
+
+    // Data type to hold a CEC message or internal event data.
+    typedef union {
+        cec_message_t cec;
+        hotplug_event_t hotplug;
+    } queue_item_t;
+
+    // Entry used for message queue.
+    typedef std::pair<int, const queue_item_t> MessageEntry;
+
+    HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj);
+
+    void initialize();
+
+    // initialize individual logical device.
+    int initLogicalDevice(int type);
+    void releaseLogicalDevice(int type);
+
+    cec_logical_address_t getLogicalAddress(int deviceType);
+    int getDeviceType(cec_logical_address_t addr);
+    void queueMessage(const MessageEntry& message);
+    void queueOutgoingMessage(const cec_message_t& message);
+    void sendReportPhysicalAddress();
+    void sendActiveSource(cec_logical_address_t srcAddr);
+    void sendInactiveSource(cec_logical_address_t srcAddr);
+    void sendImageViewOn(cec_logical_address_t srcAddr);
+    void sendTextViewOn(cec_logical_address_t srcAddr);
+    void sendGiveDevicePowerStatus(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+    void sendFeatureAbort(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+            int opcode, int reason);
+    void sendCecVersion(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+            int version);
+    void sendDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+    void sendGiveDeviceVendorID(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+    void sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+            const char* name, size_t len);
+    void sendSetMenuLanguage(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr);
+
+    void sendCecMessage(const cec_message_t& message);
+
+private:
+    enum {
+        EVENT_TYPE_RX,
+        EVENT_TYPE_TX,
+        EVENT_TYPE_HOTPLUG,
+        EVENT_TYPE_STANDBY
+    };
+
+    static const unsigned int MAX_BUFFER_SIZE = 256;
+    static const uint16_t INVALID_PHYSICAL_ADDRESS = 0xFFFF;
+    static const int INACTIVE_DEVICE_TYPE = -1;
+
+    static void onReceived(const hdmi_event_t* event, void* arg);
+    static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName);
+
+    void updatePhysicalAddress();
+    void dispatchMessage(const MessageEntry& message);
+    void processIncomingMessage(const cec_message_t& msg);
+
+    // Check the message before we pass it up to framework. If true, we proceed.
+    // otherwise do not propagate it.
+    bool precheckMessage(const cec_message_t& msg);
+
+    // Propagate the message up to Java layer.
+    void propagateMessage(const cec_message_t& msg);
+    void propagateHotplug(bool connected);
+
+    // Handles incoming <Request Active Source> message. If one of logical
+    // devices is active, it should reply with <Active Source> message.
+    void handleRequestActiveSource();
+    void handleGetOsdName(const cec_message_t& msg);
+    void handleGiveDeviceVendorID(const cec_message_t& msg);
+    void handleGetCECVersion(const cec_message_t& msg);
+    void handleGetMenuLanguage(const cec_message_t& msg);
+
+    // Internal thread for message queue handler
+    class HdmiThread : public Thread {
+    public:
+        HdmiThread(HdmiCecHandler* hdmiCecHandler, bool canCallJava) :
+            Thread(canCallJava),
+            mHdmiCecHandler(hdmiCecHandler) {
+        }
+    private:
+        virtual bool threadLoop() {
+            ALOGV("HdmiThread started");
+            AutoMutex _l(mHdmiCecHandler->mMessageQueueLock);
+            mHdmiCecHandler->mMessageQueueCondition.wait(mHdmiCecHandler->mMessageQueueLock);
+            /* Process all messages in the queue */
+            while (mHdmiCecHandler->mMessageQueue.size() > 0) {
+                MessageEntry entry = mHdmiCecHandler->mMessageQueue.front();
+                mHdmiCecHandler->dispatchMessage(entry);
+            }
+            return true;
+        }
+
+        HdmiCecHandler* mHdmiCecHandler;
+    };
+
+    // device type -> logical address mapping
+    std::map<int, cec_logical_address_t> mLogicalDevices;
+
+    hdmi_cec_device_t* mDevice;
+    jobject mCallbacksObj;
+    Mutex mLock;
+    Mutex mMessageQueueLock;
+    Condition mMessageQueueCondition;
+    sp<HdmiThread> mMessageQueueHandler;
+
+    std::deque<MessageEntry> mMessageQueue;
+    uint16_t mPhysicalAddress;
+};
+
+
+HdmiCecHandler::HdmiCecHandler(hdmi_cec_device_t* device, jobject callbacksObj) :
+    mDevice(device),
+    mCallbacksObj(callbacksObj) {
+}
+
+void HdmiCecHandler::initialize() {
+    mDevice->register_event_callback(mDevice, HdmiCecHandler::onReceived, this);
+    mMessageQueueHandler = new HdmiThread(this, true /* canCallJava */);
+    mMessageQueueHandler->run("MessageHandler");
+    updatePhysicalAddress();
+}
+
+void HdmiCecHandler::updatePhysicalAddress() {
+    uint16_t addr;
+    if (!mDevice->get_physical_address(mDevice, &addr)) {
+        mPhysicalAddress = addr;
+    } else {
+        mPhysicalAddress = INVALID_PHYSICAL_ADDRESS;
+    }
+}
+
+int HdmiCecHandler::initLogicalDevice(int type) {
+    cec_logical_address_t addr;
+    int res = mDevice->allocate_logical_address(mDevice, type, &addr);
+
+    if (res != 0) {
+        ALOGE("Logical Address Allocation failed: %d", res);
+    } else {
+        ALOGV("Logical Address Allocation success: %d", addr);
+        mLogicalDevices.insert(std::pair<int, cec_logical_address_t>(type, addr));
+    }
+    return addr;
+}
+
+void HdmiCecHandler::releaseLogicalDevice(int type) {
+    std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.find(type);
+    if (it != mLogicalDevices.end()) {
+        mLogicalDevices.erase(it);
+    }
+    // TODO: remove the address monitored in HAL as well.
+}
+
+cec_logical_address_t HdmiCecHandler::getLogicalAddress(int mDevicetype) {
+    std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.find(mDevicetype);
+    if (it != mLogicalDevices.end()) {
+        return it->second;
+    }
+    return CEC_ADDR_UNREGISTERED;
+}
+
+int HdmiCecHandler::getDeviceType(cec_logical_address_t addr) {
+    std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
+    for (; it != mLogicalDevices.end(); ++it) {
+        if (it->second == addr) {
+            return it->first;
+        }
+    }
+    return INACTIVE_DEVICE_TYPE;
+}
+
+void HdmiCecHandler::queueMessage(const MessageEntry& entry) {
+    AutoMutex _l(mMessageQueueLock);
+    if (mMessageQueue.size() <=  MAX_BUFFER_SIZE) {
+        mMessageQueue.push_back(entry);
+        mMessageQueueCondition.signal();
+    } else {
+        ALOGW("Queue is full! Message dropped.");
+    }
+}
+
+void HdmiCecHandler::queueOutgoingMessage(const cec_message_t& message) {
+    queue_item_t item;
+    item.cec = message;
+    MessageEntry entry = std::make_pair(EVENT_TYPE_TX, item);
+    queueMessage(entry);
+}
+
+void HdmiCecHandler::sendReportPhysicalAddress() {
+    if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+        ALOGE("Invalid physical address.");
+        return;
+    }
+
+    // Report physical address for each logical one hosted in it.
+    std::map<int, cec_logical_address_t>::iterator it = mLogicalDevices.begin();
+    while (it != mLogicalDevices.end()) {
+        cec_message_t msg;
+        msg.initiator = it->second;  // logical address
+        msg.destination = CEC_ADDR_BROADCAST;
+        msg.length = 4;
+        msg.body[0] = CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS;
+        std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
+        msg.body[3] = it->first;  // device type
+        queueOutgoingMessage(msg);
+        ++it;
+    }
+}
+
+void HdmiCecHandler::sendActiveSource(cec_logical_address_t srcAddr) {
+    if (mPhysicalAddress == INVALID_PHYSICAL_ADDRESS) {
+        ALOGE("Error getting physical address.");
+        return;
+    }
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = CEC_ADDR_BROADCAST;
+    msg.length = 3;
+    msg.body[0] = CEC_MESSAGE_ACTIVE_SOURCE;
+    std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendInactiveSource(cec_logical_address_t srcAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = CEC_ADDR_TV;
+    msg.length = 3;
+    msg.body[0] = CEC_MESSAGE_INACTIVE_SOURCE;
+    if (mPhysicalAddress != INVALID_PHYSICAL_ADDRESS) {
+        std::memcpy(msg.body + 1, &mPhysicalAddress, 2);
+        queueOutgoingMessage(msg);
+    }
+}
+
+void HdmiCecHandler::sendImageViewOn(cec_logical_address_t srcAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = CEC_ADDR_TV;
+    msg.length = 1;
+    msg.body[0] = CEC_MESSAGE_IMAGE_VIEW_ON;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendTextViewOn(cec_logical_address_t srcAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = CEC_ADDR_TV;
+    msg.length = 1;
+    msg.body[0] = CEC_MESSAGE_TEXT_VIEW_ON;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendGiveDevicePowerStatus(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 1;
+    msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendFeatureAbort(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr, int opcode, int reason) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 3;
+    msg.body[0] = CEC_MESSAGE_FEATURE_ABORT;
+    msg.body[1] = opcode;
+    msg.body[2] = reason;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendCecVersion(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr, int version) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 2;
+    msg.body[0] = CEC_MESSAGE_CEC_VERSION;
+    msg.body[1] = version;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendGiveDeviceVendorID(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 1;
+    msg.body[0] = CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID;
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendDeviceVendorID(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 4;
+    msg.body[0] = CEC_MESSAGE_DEVICE_VENDOR_ID;
+    uint32_t vendor_id;
+    mDevice->get_vendor_id(mDevice, &vendor_id);
+    std::memcpy(msg.body + 1, &vendor_id, 3);
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendSetOsdName(cec_logical_address_t srcAddr, cec_logical_address_t dstAddr,
+        const char* name, size_t len) {
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.body[0] = CEC_MESSAGE_SET_OSD_NAME;
+    msg.length = min(len + 1, CEC_MESSAGE_BODY_MAX_LENGTH);
+    std::memcpy(msg.body + 1, name, msg.length - 1);
+    queueOutgoingMessage(msg);
+}
+
+void HdmiCecHandler::sendSetMenuLanguage(cec_logical_address_t srcAddr,
+        cec_logical_address_t dstAddr) {
+    char lang[4];   // buffer for 3-letter language code
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jstring res = (jstring) env->CallObjectMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.getLanguage,
+            getDeviceType(srcAddr));
+    const char *clang = env->GetStringUTFChars(res, NULL);
+    strlcpy(lang, clang, sizeof(lang));
+    env->ReleaseStringUTFChars(res, clang);
+
+    cec_message_t msg;
+    msg.initiator = srcAddr;
+    msg.destination = dstAddr;
+    msg.length = 4;  // opcode (1) + language code (3)
+    msg.body[0] = CEC_MESSAGE_SET_MENU_LANGUAGE;
+    std::memcpy(msg.body + 1, lang, 3);
+    queueOutgoingMessage(msg);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::sendCecMessage(const cec_message_t& message) {
+    AutoMutex _l(mLock);
+    ALOGV("sendCecMessage");
+    mDevice->send_message(mDevice, &message);
+}
+
+// static
+void HdmiCecHandler::onReceived(const hdmi_event_t* event, void* arg) {
+    HdmiCecHandler* handler = static_cast<HdmiCecHandler*>(arg);
+    if (handler == NULL) {
+        return;
+    }
+    queue_item_t item;
+    if (event->type == HDMI_EVENT_CEC_MESSAGE) {
+        item.cec = event->cec;
+        MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_RX, item);
+        handler->queueMessage(entry);
+    } else if (event->type == HDMI_EVENT_HOT_PLUG) {
+        item.hotplug = event->hotplug;
+        MessageEntry entry = std::make_pair<int, const queue_item_t>(EVENT_TYPE_HOTPLUG, item);
+        handler->queueMessage(entry);
+    }
+}
+
+// static
+void HdmiCecHandler::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        ALOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
+void HdmiCecHandler::dispatchMessage(const MessageEntry& entry) {
+    int type = entry.first;
+    mMessageQueueLock.unlock();
+    if (type == EVENT_TYPE_RX) {
+        mMessageQueue.pop_front();
+        processIncomingMessage(entry.second.cec);
+    } else if (type == EVENT_TYPE_TX) {
+        sendCecMessage(entry.second.cec);
+        mMessageQueue.pop_front();
+    } else if (type == EVENT_TYPE_HOTPLUG) {
+        mMessageQueue.pop_front();
+        bool connected = entry.second.hotplug.connected;
+        if (connected) {
+            // TODO: Update logical addresses as well, since they also could have
+            // changed while the cable was disconnected.
+            updatePhysicalAddress();
+        }
+        propagateHotplug(connected);
+    }
+    mMessageQueueLock.lock();
+}
+
+void HdmiCecHandler::processIncomingMessage(const cec_message_t& msg) {
+    int opcode = msg.body[0];
+    if (opcode == CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS) {
+        sendReportPhysicalAddress();
+    } else if (opcode == CEC_MESSAGE_REQUEST_ACTIVE_SOURCE) {
+        handleRequestActiveSource();
+    } else if (opcode == CEC_MESSAGE_GET_OSD_NAME) {
+        handleGetOsdName(msg);
+    } else if (opcode == CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID) {
+        handleGiveDeviceVendorID(msg);
+    } else if (opcode == CEC_MESSAGE_GET_CEC_VERSION) {
+        handleGetCECVersion(msg);
+    } else if (opcode == CEC_MESSAGE_GET_MENU_LANGUAGE) {
+        handleGetMenuLanguage(msg);
+    } else {
+        if (precheckMessage(msg)) {
+            propagateMessage(msg);
+        }
+    }
+}
+
+bool HdmiCecHandler::precheckMessage(const cec_message_t& msg) {
+    // Check if this is the broadcast message coming to itself, which need not be passed
+    // back to framework. This happens because CEC spec specifies that a physical device
+    // may host multiple logical devices. A broadcast message sent by one of them therefore
+    // should be able to reach the others by the loopback mechanism.
+    //
+    // Currently we don't deal with multiple logical devices, so this is not necessary.
+    // It should be revisited once we support hosting multiple logical devices.
+    int opcode = msg.body[0];
+    if (msg.destination == CEC_ADDR_BROADCAST &&
+            (opcode == CEC_MESSAGE_ACTIVE_SOURCE ||
+             opcode == CEC_MESSAGE_SET_STREAM_PATH ||
+             opcode == CEC_MESSAGE_INACTIVE_SOURCE)) {
+        uint16_t senderAddr;
+        std::memcpy(&senderAddr, &msg.body[1], 2);
+        if (senderAddr == mPhysicalAddress) {
+            return false;
+        }
+    }
+    return true;
+}
+
+void HdmiCecHandler::propagateMessage(const cec_message_t& msg) {
+    int paramLen = msg.length - 1;
+    jint srcAddr = msg.initiator;
+    jint dstAddr = msg.destination;
+    jint opcode = msg.body[0];
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jbyteArray params = env->NewByteArray(paramLen);
+    const jbyte* body = reinterpret_cast<const jbyte *>(msg.body + 1);
+    if (paramLen > 0) {
+        env->SetByteArrayRegion(params, 0, paramLen, body);
+    }
+    env->CallVoidMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.handleMessage,
+            srcAddr, dstAddr, opcode, params);
+    env->DeleteLocalRef(params);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::propagateHotplug(bool connected) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->CallVoidMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.handleHotplug,
+            connected);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+
+void HdmiCecHandler::handleRequestActiveSource() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jint activeDeviceType = env->CallIntMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.getActiveSource);
+    if (activeDeviceType != INACTIVE_DEVICE_TYPE) {
+        sendActiveSource(getLogicalAddress(activeDeviceType));
+    }
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::handleGetOsdName(const cec_message_t& msg) {
+    cec_logical_address_t addr = msg.destination;
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    jbyteArray res = (jbyteArray) env->CallObjectMethod(mCallbacksObj,
+            gHdmiCecServiceClassInfo.getOsdName,
+            getDeviceType(addr));
+    jbyte *name = env->GetByteArrayElements(res, NULL);
+    if (name != NULL) {
+        sendSetOsdName(addr, msg.initiator, reinterpret_cast<const char *>(name),
+                env->GetArrayLength(res));
+        env->ReleaseByteArrayElements(res, name, JNI_ABORT);
+    }
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+void HdmiCecHandler::handleGiveDeviceVendorID(const cec_message_t& msg) {
+    sendDeviceVendorID(msg.destination, msg.initiator);
+}
+
+void HdmiCecHandler::handleGetCECVersion(const cec_message_t& msg) {
+    int version;
+    mDevice->get_version(mDevice, &version);
+    sendCecVersion(msg.destination, msg.initiator, version);
+}
+
+void HdmiCecHandler::handleGetMenuLanguage(const cec_message_t& msg) {
+    sendSetMenuLanguage(msg.destination, msg.initiator);
+}
+
+//------------------------------------------------------------------------------
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject callbacksObj) {
+    int err;
+    hw_module_t* module;
+    err = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, const_cast<const hw_module_t **>(&module));
+    if (err != 0) {
+        ALOGE("Error acquiring hardware module: %d", err);
+        return 0;
+    }
+    hw_device_t* device;
+    err = module->methods->open(module, HDMI_CEC_HARDWARE_INTERFACE, &device);
+    if (err != 0) {
+        ALOGE("Error opening hardware module: %d", err);
+        return 0;
+    }
+    HdmiCecHandler *handler = new HdmiCecHandler(reinterpret_cast<hdmi_cec_device *>(device),
+            env->NewGlobalRef(callbacksObj));
+    handler->initialize();
+
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.handleMessage, clazz,
+            "handleMessage", "(III[B)V");
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.handleHotplug, clazz,
+            "handleHotplug", "(Z)V");
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.getActiveSource, clazz,
+            "getActiveSource", "()I");
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.getOsdName, clazz,
+            "getOsdName", "(I)[B");
+    GET_METHOD_ID(gHdmiCecServiceClassInfo.getLanguage, clazz,
+            "getLanguage", "(I)Ljava/lang/String;");
+
+    return reinterpret_cast<jlong>(handler);
+}
+
+static void nativeSendMessage(JNIEnv* env, jclass clazz, jlong handlerPtr, jint deviceType,
+        jint dstAddr, jint opcode, jbyteArray params) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    jsize len = env->GetArrayLength(params);
+    ScopedByteArrayRO paramsPtr(env, params);
+    cec_message_t message;
+    message.initiator = srcAddr;
+    message.destination = static_cast<cec_logical_address_t>(dstAddr);
+    message.length = len + 1;
+    message.body[0] = opcode;
+    std::memcpy(message.body + 1, paramsPtr.get(), len);
+    handler->sendCecMessage(message);
+}
+
+static int nativeAllocateLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    return handler->initLogicalDevice(deviceType);
+}
+
+static void nativeRemoveLogicalAddress(JNIEnv* env, jclass clazz, jlong handlerPtr,
+       jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    return handler->releaseLogicalDevice(deviceType);
+}
+
+static void nativeSendActiveSource(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    handler->sendActiveSource(srcAddr);
+}
+
+static void nativeSendInactiveSource(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    handler->sendInactiveSource(srcAddr);
+}
+
+static void nativeSendImageViewOn(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    handler->sendImageViewOn(srcAddr);
+}
+
+static void nativeSendTextViewOn(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    handler->sendTextViewOn(srcAddr);
+}
+
+static void nativeSendGiveDevicePowerStatus(JNIEnv* env, jclass clazz, jlong handlerPtr,
+        jint deviceType, jint destination) {
+    HdmiCecHandler *handler = reinterpret_cast<HdmiCecHandler *>(handlerPtr);
+    cec_logical_address_t srcAddr = handler->getLogicalAddress(deviceType);
+    cec_logical_address_t dstAddr = static_cast<cec_logical_address_t>(destination);
+    handler->sendGiveDevicePowerStatus(srcAddr, dstAddr);
+}
+
+static JNINativeMethod sMethods[] = {
+    /* name, signature, funcPtr */
+    { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecService;)J",
+            (void *)nativeInit },
+    { "nativeSendMessage", "(JIII[B)V",
+            (void *)nativeSendMessage },
+    { "nativeAllocateLogicalAddress", "(JI)I",
+            (void *)nativeAllocateLogicalAddress },
+    { "nativeRemoveLogicalAddress", "(JI)V",
+            (void *)nativeRemoveLogicalAddress },
+    { "nativeSendActiveSource", "(JI)V",
+            (void *)nativeSendActiveSource },
+    { "nativeSendInactiveSource", "(JI)V",
+            (void *)nativeSendInactiveSource },
+    { "nativeSendImageViewOn", "(JI)V",
+            (void *)nativeSendImageViewOn },
+    { "nativeSendTextViewOn", "(JI)V",
+            (void *)nativeSendTextViewOn },
+    { "nativeSendGiveDevicePowerStatus", "(JII)V",
+            (void *)nativeSendGiveDevicePowerStatus }
+};
+
+#define CLASS_PATH "com/android/server/hdmi/HdmiCecService"
+
+int register_android_server_hdmi_HdmiCecService(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, CLASS_PATH, sMethods, NELEM(sMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+    return 0;
+}
+
+}  /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index ac6585b..bf9f7f4 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -21,6 +21,7 @@
 
 namespace android {
 int register_android_server_AlarmManagerService(JNIEnv* env);
+int register_android_server_AssetAtlasService(JNIEnv* env);
 int register_android_server_BatteryStatsService(JNIEnv* env);
 int register_android_server_ConsumerIrService(JNIEnv *env);
 int register_android_server_InputApplicationHandle(JNIEnv* env);
@@ -29,15 +30,15 @@
 int register_android_server_LightsService(JNIEnv* env);
 int register_android_server_PowerManagerService(JNIEnv* env);
 int register_android_server_SerialService(JNIEnv* env);
+int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_UsbDeviceManager(JNIEnv* env);
 int register_android_server_UsbHostManager(JNIEnv* env);
 int register_android_server_VibratorService(JNIEnv* env);
-int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_location_GpsLocationProvider(JNIEnv* env);
 int register_android_server_location_FlpHardwareProvider(JNIEnv* env);
 int register_android_server_connectivity_Vpn(JNIEnv* env);
-int register_android_server_AssetAtlasService(JNIEnv* env);
 int register_android_server_dreams_McuHal(JNIEnv* env);
+int register_android_server_hdmi_HdmiCecService(JNIEnv* env);
 };
 
 using namespace android;
@@ -71,6 +72,7 @@
     register_android_server_ConsumerIrService(env);
     register_android_server_dreams_McuHal(env);
     register_android_server_BatteryStatsService(env);
+    register_android_server_hdmi_HdmiCecService(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b4c099d..c925669 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -118,6 +118,8 @@
             "com.android.server.wifi.WifiService";
     private static final String WIFI_P2P_SERVICE_CLASS =
             "com.android.server.wifi.p2p.WifiP2pService";
+    private static final String HDMI_CEC_SERVICE_CLASS =
+            "com.android.server.hdmi.HdmiCecService";
 
     private final int mFactoryTestMode;
     private Timer mProfilerSnapshotTimer;
@@ -889,6 +891,12 @@
                 reportWtf("starting MediaSessionService", e);
             }
 
+            try {
+                mSystemServiceManager.startService(HDMI_CEC_SERVICE_CLASS);
+            } catch (Throwable e) {
+                reportWtf("starting HdmiCec Service", e);
+            }
+
             if (!disableNonCoreServices) {
                 try {
                     Slog.i(TAG, "Media Router Service");
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
index 37176d6..b0296a0 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncOperationTest.java
@@ -18,12 +18,13 @@
 
 import android.accounts.Account;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.os.Bundle;
+import android.os.SystemClock;
+import android.provider.Settings;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.server.content.SyncOperation;
-
 /**
  * You can run those tests with:
  *
@@ -35,6 +36,21 @@
 
 public class SyncOperationTest extends AndroidTestCase {
 
+    Account mDummy;
+    /** Indicate an unimportant long that we're not testing. */
+    long mUnimportantLong = 0L;
+    /** Empty bundle. */
+    Bundle mEmpty;
+    /** Silly authority. */
+    String mAuthority;
+
+    @Override
+    public void setUp() {
+        mDummy = new Account("account1", "type1");
+        mEmpty = new Bundle();
+        mAuthority = "authority1";
+    }
+
     @SmallTest
     public void testToKey() {
         Account account1 = new Account("account1", "type1");
@@ -111,35 +127,64 @@
 
     @SmallTest
     public void testCompareTo() {
-        Account dummy = new Account("account1", "type1");
-        Bundle b1 = new Bundle();
-        final long unimportant = 0L;
         long soon = 1000;
         long soonFlex = 50;
         long after = 1500;
         long afterFlex = 100;
-        SyncOperation op1 = new SyncOperation(dummy, 0, 0, SyncOperation.REASON_PERIODIC,
-                "authority1", b1, soon, soonFlex, unimportant, unimportant, true);
+        SyncOperation op1 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+                "authority1", mEmpty, soon, soonFlex, mUnimportantLong, mUnimportantLong, true);
 
         // Interval disjoint from and after op1.
-        SyncOperation op2 = new SyncOperation(dummy, 0, 0, SyncOperation.REASON_PERIODIC,
-                "authority1", b1, after, afterFlex, unimportant, unimportant, true);
+        SyncOperation op2 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+                "authority1", mEmpty, after, afterFlex, mUnimportantLong, mUnimportantLong, true);
 
         // Interval equivalent to op1, but expedited.
         Bundle b2 = new Bundle();
         b2.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
-        SyncOperation op3 = new SyncOperation(dummy, 0, 0, 0,
-                "authority1", b2, soon, soonFlex, unimportant, unimportant, true);
+        SyncOperation op3 = new SyncOperation(mDummy, 0, 0, 0,
+                "authority1", b2, -1, soonFlex, mUnimportantLong, mUnimportantLong, true);
 
         // Interval overlaps but not equivalent to op1.
-        SyncOperation op4 = new SyncOperation(dummy, 0, 0, SyncOperation.REASON_PERIODIC,
-                "authority1", b1, soon + 100, soonFlex + 100, unimportant, unimportant, true);
+        SyncOperation op4 = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_PERIODIC,
+                "authority1", mEmpty, soon + 100, soonFlex + 100, mUnimportantLong, mUnimportantLong, true);
 
         assertTrue(op1.compareTo(op2) == -1);
         assertTrue("less than not transitive.", op2.compareTo(op1) == 1);
-        assertTrue(op1.compareTo(op3) == 1);
+        assertTrue("Expedited sync not smaller than non-expedited.", op1.compareTo(op3) == 1);
         assertTrue("greater than not transitive. ", op3.compareTo(op1) == -1);
-        assertTrue("overlapping intervals not the same.", op1.compareTo(op4) == 0);
-        assertTrue("equality not transitive.", op4.compareTo(op1) == 0);
+        assertTrue("overlapping intervals not correctly compared.", op1.compareTo(op4) == -1);
+        assertTrue("equality not transitive.", op4.compareTo(op1) == 1);
+    }
+
+    @SmallTest
+    public void testCopyConstructor() {
+        long fiveSecondsFromNow = 5 * 1000L;
+        long twoSecondsFlex = 2 * 1000L;
+        long eightSeconds = 8 * 1000L;
+        long fourSeconds = 4 * 1000L;
+
+        Bundle withExpedited = new Bundle();
+        withExpedited.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        SyncOperation op = new SyncOperation(mDummy, 0, 0, SyncOperation.REASON_USER_START,
+                mAuthority, withExpedited, fiveSecondsFromNow, twoSecondsFlex,
+                eightSeconds /* backoff */, fourSeconds /* delayUntil */, true);
+        // Create another sync op to be rerun in 5 minutes.
+        long now = SystemClock.elapsedRealtime();
+        SyncOperation copy = new SyncOperation(op, fiveSecondsFromNow * 60);
+        // Copying an expedited sync to be re-run should not keep expedited property.
+        assertFalse("A rescheduled sync based off an expedited should not be expedited!",
+                copy.isExpedited());
+        assertFalse("A rescheduled sync based off an expedited should not have expedited=true in"
+                + "its bundle.",
+                copy.extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false));
+        assertTrue("Copied sync is not respecting new provided run-time.",
+                copy.latestRunTime == (now + fiveSecondsFromNow * 60));
+        assertTrue("A rescheduled sync should not have any flex.",
+                copy.flexTime == 0L);
+        assertTrue("A rescheduled op should honour the old op's backoff.",
+                copy.backoff == eightSeconds);
+        assertTrue("A rescheduled op should honour the old op's delayUntil param.",
+                copy.delayUntil == fourSeconds);
+
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
index 70fd810..ae1967e 100644
--- a/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
+++ b/services/tests/servicestests/src/com/android/server/content/SyncStorageEngineTest.java
@@ -131,7 +131,7 @@
         assertEquals(sop.target.userId, popRetrieved.target.userId);
         assertEquals(sop.reason, popRetrieved.reason);
         assertEquals(sop.syncSource, popRetrieved.syncSource);
-        assertEquals(sop.expedited, popRetrieved.expedited);
+        assertEquals(sop.isExpedited(), popRetrieved.expedited);
         assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
     }
 
@@ -169,7 +169,7 @@
         assertEquals(deleted.target.userId, popDeleted.target.userId);
         assertEquals(deleted.reason, popDeleted.reason);
         assertEquals(deleted.syncSource, popDeleted.syncSource);
-        assertEquals(deleted.expedited, popDeleted.expedited);
+        assertEquals(deleted.isExpedited(), popDeleted.expedited);
         assert(android.content.PeriodicSync.syncExtrasEquals(deleted.extras, popDeleted.extras));
         // Delete one to force write-all
         engine.deleteFromPending(popDeleted);
@@ -192,7 +192,7 @@
         assertEquals(sop.target.userId, popRetrieved.target.userId);
         assertEquals(sop.reason, popRetrieved.reason);
         assertEquals(sop.syncSource, popRetrieved.syncSource);
-        assertEquals(sop.expedited, popRetrieved.expedited);
+        assertEquals(sop.isExpedited(), popRetrieved.expedited);
         assert(android.content.PeriodicSync.syncExtrasEquals(sop.extras, popRetrieved.extras));
 
         popRetrieved = pops.get(1);
@@ -202,7 +202,7 @@
         assertEquals(sop1.target.userId, popRetrieved.target.userId);
         assertEquals(sop1.reason, popRetrieved.reason);
         assertEquals(sop1.syncSource, popRetrieved.syncSource);
-        assertEquals(sop1.expedited, popRetrieved.expedited);
+        assertEquals(sop1.isExpedited(), popRetrieved.expedited);
         assert(android.content.PeriodicSync.syncExtrasEquals(sop1.extras, popRetrieved.extras));
     }
 
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index efbfb33..502ee18 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -90,7 +90,6 @@
     private static final int MSG_SYSTEM_READY = 3;
     private static final int MSG_BOOT_COMPLETED = 4;
     private static final int MSG_USER_SWITCHED = 5;
-    private static final int MSG_START_ACCESSORY_MODE = 6;
 
     private static final int AUDIO_MODE_NONE = 0;
     private static final int AUDIO_MODE_SOURCE = 1;
@@ -153,7 +152,7 @@
                 mHandler.updateState(state);
             } else if ("START".equals(accessory)) {
                 if (DEBUG) Slog.d(TAG, "got accessory start");
-                 mHandler.sendEmptyMessage(MSG_START_ACCESSORY_MODE);
+                startAccessoryMode();
             }
         }
     };
@@ -171,7 +170,7 @@
 
         if (nativeIsStartRequested()) {
             if (DEBUG) Slog.d(TAG, "accessory attached at boot");
-             mHandler.sendEmptyMessage(MSG_START_ACCESSORY_MODE);
+            startAccessoryMode();
         }
 
         boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
@@ -233,8 +232,6 @@
             functions = UsbManager.USB_FUNCTION_AUDIO_SOURCE;
         }
 
-        if (DEBUG) Slog.d(TAG, "startAccessoryMode: " + functions);
-
         if (functions != null) {
             mAccessoryModeRequestTime = SystemClock.elapsedRealtime();
             setCurrentFunctions(functions, false);
@@ -309,7 +306,6 @@
         // current USB state
         private boolean mConnected;
         private boolean mConfigured;
-        private boolean mAccessoryStartPending;
         private String mCurrentFunctions;
         private String mDefaultFunctions;
         private UsbAccessory mCurrentAccessory;
@@ -538,7 +534,6 @@
 
             if (mConfigured && enteringAccessoryMode) {
                 // successfully entered accessory mode
-                mAccessoryModeRequestTime = 0;
 
                 if (mAccessoryStrings != null) {
                     mCurrentAccessory = new UsbAccessory(mAccessoryStrings);
@@ -616,11 +611,6 @@
                 case MSG_UPDATE_STATE:
                     mConnected = (msg.arg1 == 1);
                     mConfigured = (msg.arg2 == 1);
-
-                    if (!mConnected) {
-                        mAccessoryStartPending = false;
-                    }
-
                     updateUsbNotification();
                     updateAdbNotification();
                     if (containsFunction(mCurrentFunctions,
@@ -634,10 +624,6 @@
                         updateUsbState();
                         updateAudioSourceFunction();
                     }
-                    if (mConnected && mConfigured && mAccessoryStartPending) {
-                        startAccessoryMode();
-                        mAccessoryStartPending = false;
-                    }
                     break;
                 case MSG_ENABLE_ADB:
                     setAdbEnabled(msg.arg1 == 1);
@@ -674,16 +660,6 @@
                     mCurrentUser = msg.arg1;
                     break;
                 }
-                case MSG_START_ACCESSORY_MODE:
-                    if (mConnected && mConfigured) {
-                        startAccessoryMode();
-                    } else {
-                        // we sometimes receive the kernel "accessory start" uevent
-                        // before the "configured" uevent. In this case we need to defer
-                        // handling this event until after we received the configured event
-                        mAccessoryStartPending = true;
-                    }
-                    break;
             }
         }
 
diff --git a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
index 90b6abc..b12ed94 100644
--- a/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
+++ b/tests/permission/src/com/android/framework/permission/tests/VibratorServicePermissionTest.java
@@ -18,6 +18,7 @@
 
 import junit.framework.TestCase;
 
+import android.media.AudioManager;
 import android.os.Binder;
 import android.os.IVibratorService;
 import android.os.Process;
@@ -47,7 +48,8 @@
      */
     public void testVibrate() throws RemoteException {
         try {
-            mVibratorService.vibrate(Process.myUid(), null, 2000, new Binder());
+            mVibratorService.vibrate(Process.myUid(), null, 2000, AudioManager.STREAM_ALARM,
+                    new Binder());
             fail("vibrate did not throw SecurityException as expected");
         } catch (SecurityException e) {
             // expected
@@ -63,7 +65,8 @@
      */
     public void testVibratePattern() throws RemoteException {
         try {
-            mVibratorService.vibratePattern(Process.myUid(), null, new long[] {0}, 0, new Binder());
+            mVibratorService.vibratePattern(Process.myUid(), null, new long[] {0}, 0,
+                    AudioManager.STREAM_ALARM, new Binder());
             fail("vibratePattern did not throw SecurityException as expected");
         } catch (SecurityException e) {
             // expected
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index aff0088..652998e 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -1317,9 +1317,9 @@
                         curIsFormatted = false;
                         // Untranslatable strings must only exist in the default [empty] locale
                         if (locale.size() > 0) {
-                            fprintf(stderr, "aapt: warning: string '%s' in %s marked untranslatable but exists"
-                                    " in locale '%s'\n", String8(name).string(),
-                                    bundle->getResourceSourceDirs()[0],
+                            SourcePos(in->getPrintableSource(), block.getLineNumber()).warning(
+                                    "string '%s' marked untranslatable but exists in locale '%s'\n",
+                                    String8(name).string(),
                                     locale.string());
                             // hasErrors = localHasErrors = true;
                         } else {
@@ -1330,7 +1330,10 @@
                             // having no default translation.
                         }
                     } else {
-                        outTable->addLocalization(name, locale);
+                        outTable->addLocalization(
+                                name,
+                                locale,
+                                SourcePos(in->getPrintableSource(), block.getLineNumber()));
                     }
 
                     if (formatted == false16) {
@@ -2570,9 +2573,9 @@
 
 
 void
-ResourceTable::addLocalization(const String16& name, const String8& locale)
+ResourceTable::addLocalization(const String16& name, const String8& locale, const SourcePos& src)
 {
-    mLocalizations[name].insert(locale);
+    mLocalizations[name][locale] = src;
 }
 
 
@@ -2592,21 +2595,22 @@
     const String8 defaultLocale;
 
     // For all strings...
-    for (map<String16, set<String8> >::iterator nameIter = mLocalizations.begin();
+    for (map<String16, map<String8, SourcePos> >::iterator nameIter = mLocalizations.begin();
          nameIter != mLocalizations.end();
          nameIter++) {
-        const set<String8>& configSet = nameIter->second;   // naming convenience
+        const map<String8, SourcePos>& configSrcMap = nameIter->second;
 
         // Look for strings with no default localization
-        if (configSet.count(defaultLocale) == 0) {
-            fprintf(stdout, "aapt: warning: string '%s' has no default translation in %s; found:",
-                    String8(nameIter->first).string(), mBundle->getResourceSourceDirs()[0]);
-            for (set<String8>::const_iterator locales = configSet.begin();
-                 locales != configSet.end();
-                 locales++) {
-                fprintf(stdout, " %s", (*locales).string());
+        if (configSrcMap.count(defaultLocale) == 0) {
+            SourcePos().warning("string '%s' has no default translation.",
+                    String8(nameIter->first).string());
+            if (mBundle->getVerbose()) {
+                for (map<String8, SourcePos>::const_iterator locales = configSrcMap.begin();
+                    locales != configSrcMap.end();
+                    locales++) {
+                    locales->second.printf("locale %s found", locales->first.string());
+                }
             }
-            fprintf(stdout, "\n");
             // !!! TODO: throw an error here in some circumstances
         }
 
@@ -2616,6 +2620,8 @@
             const char* start = allConfigs;
             const char* comma;
             
+            set<String8> missingConfigs;
+            AaptLocaleValue locale;
             do {
                 String8 config;
                 comma = strchr(start, ',');
@@ -2626,27 +2632,38 @@
                     config.setTo(start);
                 }
 
+                if (!locale.initFromFilterString(config)) {
+                    continue;
+                }
+
                 // don't bother with the pseudolocale "zz_ZZ"
                 if (config != "zz_ZZ") {
-                    if (configSet.find(config) == configSet.end()) {
+                    if (configSrcMap.find(config) == configSrcMap.end()) {
                         // okay, no specific localization found.  it's possible that we are
                         // requiring a specific regional localization [e.g. de_DE] but there is an
                         // available string in the generic language localization [e.g. de];
                         // consider that string to have fulfilled the localization requirement.
                         String8 region(config.string(), 2);
-                        if (configSet.find(region) == configSet.end()) {
-                            if (configSet.count(defaultLocale) == 0) {
-                                fprintf(stdout, "aapt: warning: "
-                                        "**** string '%s' has no default or required localization "
-                                        "for '%s' in %s\n",
-                                        String8(nameIter->first).string(),
-                                        config.string(),
-                                        mBundle->getResourceSourceDirs()[0]);
-                            }
+                        if (configSrcMap.find(region) == configSrcMap.end() &&
+                                configSrcMap.count(defaultLocale) == 0) {
+                            missingConfigs.insert(config);
                         }
                     }
                 }
-           } while (comma != NULL);
+            } while (comma != NULL);
+
+            if (!missingConfigs.empty()) {
+                String8 configStr;
+                for (set<String8>::iterator iter = missingConfigs.begin();
+                     iter != missingConfigs.end();
+                     iter++) {
+                    configStr.appendFormat(" %s", iter->string());
+                }
+                SourcePos().warning("string '%s' is missing %u required localizations:%s",
+                        String8(nameIter->first).string(),
+                        (unsigned int)missingConfigs.size(),
+                        configStr.string());
+            }
         }
     }
 
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index a3e0666..75005cd 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -220,7 +220,7 @@
 
     status_t assignResourceIds();
     status_t addSymbols(const sp<AaptSymbols>& outSymbols = NULL);
-    void addLocalization(const String16& name, const String8& locale);
+    void addLocalization(const String16& name, const String8& locale, const SourcePos& src);
     status_t validateLocalizations(void);
 
     status_t flatten(Bundle*, const sp<AaptFile>& dest);
@@ -551,7 +551,7 @@
     Bundle* mBundle;
     
     // key = string resource name, value = set of locales in which that name is defined
-    map<String16, set<String8> > mLocalizations;
+    map<String16, map<String8, SourcePos> > mLocalizations;
 };
 
 #endif
diff --git a/tools/aapt/SourcePos.cpp b/tools/aapt/SourcePos.cpp
index e2a921c..ae25047 100644
--- a/tools/aapt/SourcePos.cpp
+++ b/tools/aapt/SourcePos.cpp
@@ -10,17 +10,20 @@
 // =============================================================================
 struct ErrorPos
 {
+    enum Level {
+        NOTE,
+        WARNING,
+        ERROR
+    };
+
     String8 file;
     int line;
     String8 error;
-    bool fatal;
+    Level level;
 
     ErrorPos();
     ErrorPos(const ErrorPos& that);
-    ErrorPos(const String8& file, int line, const String8& error, bool fatal);
-    ~ErrorPos();
-    bool operator<(const ErrorPos& rhs) const;
-    bool operator==(const ErrorPos& rhs) const;
+    ErrorPos(const String8& file, int line, const String8& error, Level level);
     ErrorPos& operator=(const ErrorPos& rhs);
 
     void print(FILE* to) const;
@@ -29,7 +32,7 @@
 static vector<ErrorPos> g_errors;
 
 ErrorPos::ErrorPos()
-    :line(-1), fatal(false)
+    :line(-1), level(NOTE)
 {
 }
 
@@ -37,61 +40,52 @@
     :file(that.file),
      line(that.line),
      error(that.error),
-     fatal(that.fatal)
+     level(that.level)
 {
 }
 
-ErrorPos::ErrorPos(const String8& f, int l, const String8& e, bool fat)
+ErrorPos::ErrorPos(const String8& f, int l, const String8& e, Level lev)
     :file(f),
      line(l),
      error(e),
-     fatal(fat)
+     level(lev)
 {
 }
 
-ErrorPos::~ErrorPos()
-{
-}
-
-bool
-ErrorPos::operator<(const ErrorPos& rhs) const
-{
-    if (this->file < rhs.file) return true;
-    if (this->file == rhs.file) {
-        if (this->line < rhs.line) return true;
-        if (this->line == rhs.line) {
-            if (this->error < rhs.error) return true;
-        }
-    }
-    return false;
-}
-
-bool
-ErrorPos::operator==(const ErrorPos& rhs) const
-{
-    return this->file == rhs.file
-            && this->line == rhs.line
-            && this->error == rhs.error;
-}
-
 ErrorPos&
 ErrorPos::operator=(const ErrorPos& rhs)
 {
     this->file = rhs.file;
     this->line = rhs.line;
     this->error = rhs.error;
+    this->level = rhs.level;
     return *this;
 }
 
 void
 ErrorPos::print(FILE* to) const
 {
-    const char* type = fatal ? "error:" : "warning:";
+    const char* type = "";
+    switch (level) {
+    case NOTE:
+        type = "note: ";
+        break;
+    case WARNING:
+        type = "warning: ";
+        break;
+    case ERROR:
+        type = "error: ";
+        break;
+    }
     
-    if (this->line >= 0) {
-        fprintf(to, "%s:%d: %s %s\n", this->file.string(), this->line, type, this->error.string());
+    if (!this->file.isEmpty()) {
+        if (this->line >= 0) {
+            fprintf(to, "%s:%d: %s%s\n", this->file.string(), this->line, type, this->error.string());
+        } else {
+            fprintf(to, "%s: %s%s\n", this->file.string(), type, this->error.string());
+        }
     } else {
-        fprintf(to, "%s: %s %s\n", this->file.string(), type, this->error.string());
+        fprintf(to, "%s%s\n", type, this->error.string());
     }
 }
 
@@ -116,40 +110,34 @@
 {
 }
 
-int
+void
 SourcePos::error(const char* fmt, ...) const
 {
-    int retval=0;
-    char buf[1024];
     va_list ap;
     va_start(ap, fmt);
-    retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+    String8 msg = String8::formatV(fmt, ap);
     va_end(ap);
-    char* p = buf + retval - 1;
-    while (p > buf && *p == '\n') {
-        *p = '\0';
-        p--;
-    }
-    g_errors.push_back(ErrorPos(this->file, this->line, String8(buf), true));
-    return retval;
+    g_errors.push_back(ErrorPos(this->file, this->line, msg, ErrorPos::ERROR));
 }
 
-int
+void
 SourcePos::warning(const char* fmt, ...) const
 {
-    int retval=0;
-    char buf[1024];
     va_list ap;
     va_start(ap, fmt);
-    retval = vsnprintf(buf, sizeof(buf), fmt, ap);
+    String8 msg = String8::formatV(fmt, ap);
     va_end(ap);
-    char* p = buf + retval - 1;
-    while (p > buf && *p == '\n') {
-        *p = '\0';
-        p--;
-    }
-    ErrorPos(this->file, this->line, String8(buf), false).print(stderr);
-    return retval;
+    ErrorPos(this->file, this->line, msg, ErrorPos::WARNING).print(stderr);
+}
+
+void
+SourcePos::printf(const char* fmt, ...) const
+{
+    va_list ap;
+    va_start(ap, fmt);
+    String8 msg = String8::formatV(fmt, ap);
+    va_end(ap);
+    ErrorPos(this->file, this->line, msg, ErrorPos::NOTE).print(stderr);
 }
 
 bool
diff --git a/tools/aapt/SourcePos.h b/tools/aapt/SourcePos.h
index 33f72a9..4ce817f 100644
--- a/tools/aapt/SourcePos.h
+++ b/tools/aapt/SourcePos.h
@@ -17,8 +17,9 @@
     SourcePos();
     ~SourcePos();
 
-    int error(const char* fmt, ...) const;
-    int warning(const char* fmt, ...) const;
+    void error(const char* fmt, ...) const;
+    void warning(const char* fmt, ...) const;
+    void printf(const char* fmt, ...) const;
 
     static bool hasErrors();
     static void printErrors(FILE* to);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index dd2cbc1..743a26c 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -458,44 +458,8 @@
     }
 
     @Override
-    public IBinder getFocusedWindowToken() {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
-    public void setInputFilter(IInputFilter filter) throws RemoteException {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void getWindowFrame(IBinder token, Rect outFrame) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public void setMagnificationSpec(MagnificationSpec spec) {
-        // TODO Auto-generated method stub
-    }
-
-    @Override
-    public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-    @Override
     public boolean isRotationFrozen() throws RemoteException {
         // TODO Auto-generated method stub
         return false;
     }
-
-    @Override
-    public void setTouchExplorationEnabled(boolean enabled) {
-    }
 }