Merge "Import translations. DO NOT MERGE"
diff --git a/Android.mk b/Android.mk
index a691987..d279704 100644
--- a/Android.mk
+++ b/Android.mk
@@ -196,7 +196,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 +398,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..e41b3fe 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;
@@ -4600,6 +4602,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);
@@ -19546,8 +19549,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 +30403,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 +30467,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 +30614,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 +30666,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/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/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/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/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/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..b0d94d5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -570,13 +570,15 @@
         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 int states2;
+
         // The wake lock that was acquired at this point.
         public HistoryTag wakelockTag;
 
@@ -1216,6 +1218,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.
      */
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/provider/SearchIndexableData.java b/core/java/android/provider/SearchIndexableData.java
new file mode 100644
index 0000000..6381884
--- /dev/null
+++ b/core/java/android/provider/SearchIndexableData.java
@@ -0,0 +1,117 @@
+/*
+ * 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 to retrieve some resources.
+     *
+     * @see Context
+     */
+    public Context context;
+
+    /**
+     * The locale for the data
+     */
+    public Locale locale;
+
+    /**
+     * 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() {
+    }
+
+    /**
+     * Constructor with a {@link Context}.
+     *
+     * @param ctx the Context
+     */
+    public SearchIndexableData(Context ctx) {
+        context = ctx;
+        locale = Locale.getDefault();
+    }
+}
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/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..a8ccd49 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -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..1d6e998 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -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/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..116c173 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,16 +219,8 @@
         mHandler = new MyHandler(context.getMainLooper());
         mService = service;
         mUserId = userId;
-        if (mService == null) {
-            mIsEnabled = false;
-        }
-        try {
-            if (mService != null) {
-                final int stateFlags = mService.addClient(mClient, userId);
-                setState(stateFlags);
-            }
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+        synchronized (mLock) {
+            tryConnectToServiceLocked();
         }
     }
 
@@ -253,7 +230,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 +245,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 +268,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 +287,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 +305,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 +352,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 +393,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 +428,7 @@
      */
     public boolean addAccessibilityStateChangeListener(
             AccessibilityStateChangeListener listener) {
+        // Final CopyOnArrayList - no lock needed.
         return mAccessibilityStateChangeListeners.add(listener);
     }
 
@@ -424,6 +440,7 @@
      */
     public boolean removeAccessibilityStateChangeListener(
             AccessibilityStateChangeListener listener) {
+        // Final CopyOnArrayList - no lock needed.
         return mAccessibilityStateChangeListeners.remove(listener);
     }
 
@@ -436,6 +453,7 @@
      */
     public boolean addTouchExplorationStateChangeListener(
             TouchExplorationStateChangeListener listener) {
+        // Final CopyOnArrayList - no lock needed.
         return mTouchExplorationStateChangeListeners.add(listener);
     }
 
@@ -447,6 +465,7 @@
      */
     public boolean removeTouchExplorationStateChangeListener(
             TouchExplorationStateChangeListener listener) {
+        // Final CopyOnArrayList - no lock needed.
         return mTouchExplorationStateChangeListeners.remove(listener);
     }
 
@@ -455,50 +474,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 +504,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 +528,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/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/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 26d7f5f..082f1a5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -208,7 +208,8 @@
     int mNumHistoryTagChars = 0;
     int mHistoryBufferLastPos = -1;
     boolean mHistoryOverflow = false;
-    long mLastHistoryTime = 0;
+    long mLastHistoryElapsedRealtime = 0;
+    long mLastHistoryUptime = 0;
 
     final HistoryItem mHistoryCur = new HistoryItem();
 
@@ -1957,12 +1958,12 @@
         }
     }
 
-    void addHistoryBufferLocked(long curTime) {
+    void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs) {
         if (!mHaveBatteryLevel || !mRecordingHistory) {
             return;
         }
 
-        final long timeDiff = (mHistoryBaseTime+curTime) - mHistoryLastWritten.time;
+        final long timeDiff = (mHistoryBaseTime+elapsedRealtimeMs) - mHistoryLastWritten.time;
         final int diffStates = mHistoryLastWritten.states^mHistoryCur.states;
         final int lastDiffStates = mHistoryLastWritten.states^mHistoryLastLastWritten.states;
         if (DEBUG) Slog.i(TAG, "ADD: tdelta=" + timeDiff + " diff="
@@ -1981,14 +1982,14 @@
                 && mHistoryLastWritten.batteryTemperature == mHistoryCur.batteryTemperature
                 && mHistoryLastWritten.batteryVoltage == mHistoryCur.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.
@@ -2018,8 +2019,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);
+                addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_OVERFLOW);
                 return;
             }
 
@@ -2034,22 +2035,23 @@
                 return;
             }
 
-            addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+            addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
             return;
         }
 
-        addHistoryBufferLocked(curTime, HistoryItem.CMD_UPDATE);
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_UPDATE);
     }
 
-    private void addHistoryBufferLocked(long curTime, byte cmd) {
+    private void addHistoryBufferLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
         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, mHistoryCur);
         writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
-        mLastHistoryTime = curTime;
+        mLastHistoryElapsedRealtime = elapsedRealtimeMs;
+        mLastHistoryUptime = uptimeMs;
         mHistoryCur.wakelockTag = null;
         mHistoryCur.wakeReasonTag = null;
         mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
@@ -2061,8 +2063,8 @@
 
     int mChangedStates = 0;
 
-    void addHistoryRecordLocked(long curTime) {
-        addHistoryBufferLocked(curTime);
+    void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs) {
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
 
         if (!USE_OLD_HISTORY) {
             return;
@@ -2077,12 +2079,12 @@
         // 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)
+                && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+1000)
                 && ((mHistoryEnd.states^mHistoryCur.states)&mChangedStates) == 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)
+                    && (mHistoryBaseTime+elapsedRealtimeMs) < (mHistoryEnd.time+500)
                     && mHistoryLastEnd.sameNonEvent(mHistoryCur)) {
                 mHistoryLastEnd.next = null;
                 mHistoryEnd.next = mHistoryCache;
@@ -2100,7 +2102,7 @@
 
         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) {
@@ -2117,25 +2119,26 @@
             }
         }
 
-        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);
+        addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs);
     }
 
-    void addHistoryRecordLocked(long curTime, byte cmd) {
+    void addHistoryRecordLocked(long elapsedRealtimeMs, long uptimeMs, byte cmd) {
         HistoryItem rec = mHistoryCache;
         if (rec != null) {
             mHistoryCache = rec.next;
         } else {
             rec = new HistoryItem();
         }
-        rec.setTo(mHistoryBaseTime + curTime, cmd, mHistoryCur);
+        rec.setTo(mHistoryBaseTime + elapsedRealtimeMs, cmd, mHistoryCur);
 
         addHistoryRecordLocked(rec);
     }
@@ -2164,7 +2167,8 @@
         }
 
         mHistoryBaseTime = 0;
-        mLastHistoryTime = 0;
+        mLastHistoryElapsedRealtime = 0;
+        mLastHistoryUptime = 0;
 
         mHistoryBuffer.setDataSize(0);
         mHistoryBuffer.setDataPosition(0);
@@ -2262,7 +2266,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,7 +2279,7 @@
     }
 
     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
@@ -2286,7 +2292,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 +2300,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 +2324,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 +2336,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 +2348,39 @@
             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) {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
+        final long uptime = SystemClock.uptimeMillis();
         if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
                 + Integer.toHexString(mHistoryCur.states));
         mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
         mHistoryCur.wakeReasonTag.string = reason;
         mHistoryCur.wakeReasonTag.uid = irq;
-        addHistoryRecordLocked(elapsedRealtime);
+        addHistoryRecordLocked(elapsedRealtime, uptime);
     }
 
     public int startAddingCpuLocked() {
@@ -2511,11 +2525,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 +2539,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 +2555,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 +2569,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 +2583,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 +2599,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 +2612,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 +2643,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);
@@ -2650,12 +2673,13 @@
     public void noteDataConnectionActive(int type, boolean active) {
         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;
                 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);
@@ -2672,10 +2696,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 +2709,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 +2753,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 +2827,7 @@
         }
 
         if (newHistory) {
-            addHistoryRecordLocked(elapsedRealtime);
+            addHistoryRecordLocked(elapsedRealtime, uptime);
         }
     }
 
@@ -2875,11 +2902,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 +2920,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 +2932,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 +2946,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 +2961,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 +2976,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;
             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 +2991,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;
             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 +3026,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 +3064,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 +3096,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 +3109,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 +3136,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 +3150,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 +3166,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 +3180,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 +3208,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 +3222,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);
     }
@@ -5627,12 +5671,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 +5692,7 @@
             mDischargeAmountScreenOn = 0;
             mDischargeAmountScreenOff = 0;
         }
-        initActiveHistoryEventsLocked(mSecRealtime);
+        initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
     }
 
     private void resetAllStatsLocked() {
@@ -5705,7 +5750,7 @@
         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 +5760,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 +5799,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
@@ -5783,9 +5829,9 @@
             if (DEBUG_HISTORY) Slog.v(TAG, "Battery unplugged to: "
                     + Integer.toHexString(mHistoryCur.states));
             mHistoryCur.currentTime = System.currentTimeMillis();
-            addHistoryBufferLocked(mSecRealtime, HistoryItem.CMD_CURRENT_TIME);
+            addHistoryBufferLocked(mSecRealtime, mSecUptime, HistoryItem.CMD_CURRENT_TIME);
             mHistoryCur.currentTime = 0;
-            addHistoryRecordLocked(mSecRealtime);
+            addHistoryRecordLocked(mSecRealtime, mSecUptime);
             mDischargeCurrentLevel = mDischargeUnplugLevel = level;
             if (mScreenOn) {
                 mDischargeScreenOnUnplugLevel = level;
@@ -5798,7 +5844,7 @@
             mDischargeAmountScreenOff = 0;
             updateTimeBasesLocked(true, !mScreenOn, uptime, realtime);
             if (reset) {
-                initActiveHistoryEventsLocked(mSecRealtime);
+                initActiveHistoryEventsLocked(mSecRealtime, mSecUptime);
             }
         } else {
             pullPendingStateUpdatesLocked();
@@ -5806,7 +5852,7 @@
             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;
@@ -5828,7 +5874,9 @@
     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;
@@ -5856,7 +5904,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 +5934,7 @@
                     changed = true;
                 }
                 if (changed) {
-                    addHistoryRecordLocked(SystemClock.elapsedRealtime());
+                    addHistoryRecordLocked(elapsedRealtime, uptime);
                 }
             }
             if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
@@ -6161,7 +6209,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,13 +6497,14 @@
         }
 
         if (mHistoryBuffer.dataPosition() > 0) {
-            long now = SystemClock.elapsedRealtime();
+            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);
             }
-            addHistoryBufferLocked(now, HistoryItem.CMD_START);
+            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_START);
             mHistoryCur.currentTime = System.currentTimeMillis();
-            addHistoryBufferLocked(now, HistoryItem.CMD_CURRENT_TIME);
+            addHistoryBufferLocked(elapsedRealtime, uptime, HistoryItem.CMD_CURRENT_TIME);
             mHistoryCur.currentTime = 0;
         }
     }
@@ -6540,11 +6600,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();
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_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..37ef539b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1913,24 +1913,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 +2388,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/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/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/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/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/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..dee8705 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
@@ -1296,6 +1326,9 @@
      *    {@link #ERROR_INVALID_OPERATION}
      */
     public int setAuxEffectSendLevel(float level) {
+        if (isRestricted()) {
+            return SUCCESS;
+        }
         if (mState == STATE_UNINITIALIZED) {
             return ERROR_INVALID_OPERATION;
         }
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/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/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/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..671b43de 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/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/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 28eb948..52f9aa9 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);
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/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 09ec4f6..9783a66 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -1776,6 +1776,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) {
@@ -1827,6 +1828,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..fa4a9d1 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());
         }
     }
 
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..cb4cf98 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -302,12 +302,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 +1160,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 +1351,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 +1509,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 +1604,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 +1631,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 +1662,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 +1674,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 +1685,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 +1717,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) {
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/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/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 a4f960e7..adfb2bd 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
@@ -3402,8 +3322,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 +4179,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 +4201,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 +4218,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 +5198,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 +6013,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 +6922,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 +8101,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();
         }
     }
 
@@ -9844,6 +9736,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 +10854,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..2cd6000 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -952,8 +952,8 @@
                 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);
@@ -1032,8 +1032,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;
@@ -1565,9 +1565,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/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