Merge "Final polish of the interrogation feature."
diff --git a/api/14.txt b/api/14.txt
index 5c4732e..050540b 100644
--- a/api/14.txt
+++ b/api/14.txt
@@ -22239,7 +22239,6 @@
   }
 
   public class AccessibilityRecord {
-    ctor protected AccessibilityRecord();
     method public int getAddedCount();
     method public java.lang.CharSequence getBeforeText();
     method public java.lang.CharSequence getClassName();
@@ -22269,17 +22268,6 @@
     method public void setParcelableData(android.os.Parcelable);
     method public void setPassword(boolean);
     method public void setRemovedCount(int);
-    field protected int mAddedCount;
-    field protected java.lang.CharSequence mBeforeText;
-    field protected int mBooleanProperties;
-    field protected java.lang.CharSequence mClassName;
-    field protected java.lang.CharSequence mContentDescription;
-    field protected int mCurrentItemIndex;
-    field protected int mFromIndex;
-    field protected int mItemCount;
-    field protected android.os.Parcelable mParcelableData;
-    field protected int mRemovedCount;
-    field protected final java.util.List mText;
   }
 }
 
diff --git a/api/current.txt b/api/current.txt
index 89654b8..f641c96 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22721,13 +22721,11 @@
     method public void appendRecord(android.view.accessibility.AccessibilityRecord);
     method public int describeContents();
     method public static java.lang.String eventTypeToString(int);
-    method public int getAccessibilityWindowId();
     method public long getEventTime();
     method public int getEventType();
     method public java.lang.CharSequence getPackageName();
     method public android.view.accessibility.AccessibilityRecord getRecord(int);
     method public int getRecordCount();
-    method public android.view.accessibility.AccessibilityNodeInfo getSource();
     method public void initFromParcel(android.os.Parcel);
     method public static android.view.accessibility.AccessibilityEvent obtain(int);
     method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
@@ -22735,7 +22733,6 @@
     method public void setEventTime(long);
     method public void setEventType(int);
     method public void setPackageName(java.lang.CharSequence);
-    method public void setSource(android.view.View);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int INVALID_POSITION = -1; // 0xffffffff
@@ -22751,6 +22748,7 @@
     field public static final int TYPE_VIEW_LONG_CLICKED = 2; // 0x2
     field public static final int TYPE_VIEW_SELECTED = 4; // 0x4
     field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10
+    field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
     field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20
   }
 
@@ -22778,9 +22776,10 @@
     method public void addAction(int);
     method public void addChild(android.view.View);
     method public int describeContents();
-    method public int getAccessibilityWindowId();
+    method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(java.lang.String);
     method public int getActions();
-    method public void getBounds(android.graphics.Rect);
+    method public void getBoundsInParent(android.graphics.Rect);
+    method public void getBoundsInScreen(android.graphics.Rect);
     method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
     method public int getChildCount();
     method public java.lang.CharSequence getClassName();
@@ -22788,6 +22787,7 @@
     method public java.lang.CharSequence getPackageName();
     method public android.view.accessibility.AccessibilityNodeInfo getParent();
     method public java.lang.CharSequence getText();
+    method public int getWindowId();
     method public boolean isCheckable();
     method public boolean isChecked();
     method public boolean isClickable();
@@ -22801,7 +22801,8 @@
     method public static android.view.accessibility.AccessibilityNodeInfo obtain();
     method public boolean performAction(int);
     method public void recycle();
-    method public void setBounds(android.graphics.Rect);
+    method public void setBoundsInParent(android.graphics.Rect);
+    method public void setBoundsInScreen(android.graphics.Rect);
     method public void setCheckable(boolean);
     method public void setChecked(boolean);
     method public void setClassName(java.lang.CharSequence);
@@ -22826,7 +22827,6 @@
   }
 
   public class AccessibilityRecord {
-    ctor protected AccessibilityRecord();
     method public int getAddedCount();
     method public java.lang.CharSequence getBeforeText();
     method public java.lang.CharSequence getClassName();
@@ -22836,7 +22836,9 @@
     method public int getItemCount();
     method public android.os.Parcelable getParcelableData();
     method public int getRemovedCount();
+    method public android.view.accessibility.AccessibilityNodeInfo getSource();
     method public java.util.List<java.lang.CharSequence> getText();
+    method public int getWindowId();
     method public boolean isChecked();
     method public boolean isEnabled();
     method public boolean isFullScreen();
@@ -22857,17 +22859,7 @@
     method public void setParcelableData(android.os.Parcelable);
     method public void setPassword(boolean);
     method public void setRemovedCount(int);
-    field protected int mAddedCount;
-    field protected java.lang.CharSequence mBeforeText;
-    field protected int mBooleanProperties;
-    field protected java.lang.CharSequence mClassName;
-    field protected java.lang.CharSequence mContentDescription;
-    field protected int mCurrentItemIndex;
-    field protected int mFromIndex;
-    field protected int mItemCount;
-    field protected android.os.Parcelable mParcelableData;
-    field protected int mRemovedCount;
-    field protected final java.util.List mText;
+    method public void setSource(android.view.View);
   }
 
 }
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 8bb305d..164acbc 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -190,9 +190,9 @@
      *            <li>
      *              Register for all event types with no notification timeout and keep track
      *              for the active window by calling
-     *              {@link AccessibilityEvent#getAccessibilityWindowId()} of the last received
+     *              {@link AccessibilityEvent#getWindowId()} of the last received
      *              event and compare this with the
-     *              {@link AccessibilityNodeInfo#getAccessibilityWindowId()} before calling
+     *              {@link AccessibilityNodeInfo#getWindowId()} before calling
      *              retrieval methods on the latter.
      *            </li>
      *            <li>
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 19f0bf0..8b4e7aee 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -47,7 +47,9 @@
 
     /**
      * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
-     * insensitive containment.
+     * insensitive containment. The search is performed in the window whose
+     * id is specified and starts from the View whose accessibility id is
+     * specified.
      * <p>
      *   <strong>
      *     It is a client responsibility to recycle the received infos by
@@ -57,12 +59,35 @@
      * </p>
      *
      * @param text The searched text.
+     * @param accessibilityId The id of the view from which to start searching.
+     *        Use {@link android.view.View#NO_ID} to start from the root.
      * @return A list of node info.
      */
-    List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text);
+    List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text,
+        int accessibilityWindowId, int accessibilityViewId);
 
     /**
-     * Finds an {@link AccessibilityNodeInfo} by View id.
+     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
+     * insensitive containment. The search is performed in the currently
+     * active window and start from the root View in the window.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received infos by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     *
+     * @param text The searched text.
+     * @param accessibilityId The id of the view from which to start searching.
+     *        Use {@link android.view.View#NO_ID} to start from the root.
+     * @return A list of node info.
+     */
+    List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(String text);
+
+    /**
+     * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed
+     * in the currently active window and start from the root View in the window.
      * <p>
      *   <strong>
      *     It is a client responsibility to recycle the received info by
@@ -74,7 +99,7 @@
      * @param id The id of the node.
      * @return The node info.
      */
-    AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId);
+    AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId);
 
     /**
      * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 441cdc1..ee18722 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3704,7 +3704,8 @@
      * The base implementation sets:
      * <ul>
      *   <li>{@link AccessibilityNodeInfo#setParent(View)},</li>
-     *   <li>{@link AccessibilityNodeInfo#setBounds(Rect)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setBoundsInParent(Rect)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setBoundsInScreen(Rect)},</li>
      *   <li>{@link AccessibilityNodeInfo#setPackageName(CharSequence)},</li>
      *   <li>{@link AccessibilityNodeInfo#setClassName(CharSequence)},</li>
      *   <li>{@link AccessibilityNodeInfo#setContentDescription(CharSequence)},</li>
@@ -3724,7 +3725,12 @@
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         Rect bounds = mAttachInfo.mTmpInvalRect;
         getDrawingRect(bounds);
-        info.setBounds(bounds);
+        info.setBoundsInParent(bounds);
+
+        int[] locationOnScreen = mAttachInfo.mInvalidateChildLocation;
+        getLocationOnScreen(locationOnScreen);
+        bounds.offset(locationOnScreen[0], locationOnScreen[1]);
+        info.setBoundsInScreen(bounds);
 
         ViewParent parent = getParent();
         if (parent instanceof View) {
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index 17d7454..914973e 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -136,6 +136,13 @@
     static final ArrayList<ComponentCallbacks> sConfigCallbacks
             = new ArrayList<ComponentCallbacks>();
 
+    /**
+     * Delay before dispatching an accessibility event that the window
+     * content has changed. The window content is considered changed
+     * after a layout pass.
+     */
+    private static final long SEND_WINDOW_CONTENT_CHANGED_DELAY_MILLIS = 500;
+
     long mLastTrackballTime = 0;
     final TrackballAxis mTrackballAxisX = new TrackballAxis();
     final TrackballAxis mTrackballAxisY = new TrackballAxis();
@@ -277,6 +284,8 @@
 
     AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager;
 
+    SendWindowContentChanged mSendWindowContentChanged;
+
     private final int mDensity;
 
     /**
@@ -1427,6 +1436,10 @@
         if (triggerGlobalLayoutListener) {
             attachInfo.mRecomputeGlobalAttributes = false;
             attachInfo.mTreeObserver.dispatchOnGlobalLayout();
+
+            if (AccessibilityManager.getInstance(host.mContext).isEnabled()) {
+                postSendWindowContentChangedCallback();
+            }
         }
 
         if (computesInternalInsets) {
@@ -2086,6 +2099,7 @@
         mAccessibilityInteractionConnectionManager.ensureNoConnection();
         mAccessibilityManager.removeAccessibilityStateChangeListener(
                 mAccessibilityInteractionConnectionManager);
+        removeSendWindowContentChangedCallback();
 
         mView = null;
         mAttachInfo.mRootView = null;
@@ -3671,6 +3685,29 @@
         }
     }
 
+    /**
+     * Post a callback to send a
+     * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
+     */
+    private void postSendWindowContentChangedCallback() {
+        if (mSendWindowContentChanged == null) {
+            mSendWindowContentChanged = new SendWindowContentChanged();
+        } else {
+            removeCallbacks(mSendWindowContentChanged);
+        }
+        postDelayed(mSendWindowContentChanged, SEND_WINDOW_CONTENT_CHANGED_DELAY_MILLIS);
+    }
+
+    /**
+     * Remove a posted callback to send a
+     * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
+     */
+    private void removeSendWindowContentChangedCallback() {
+        if (mSendWindowContentChanged != null) {
+            removeCallbacks(mSendWindowContentChanged);
+        }
+    }
+
     public boolean showContextMenuForChild(View originalView) {
         return false;
     }
@@ -4268,12 +4305,12 @@
             }
         }
 
-        public void findAccessibilityNodeInfosByViewText(String text, int interactionId,
-                IAccessibilityInteractionConnectionCallback callback) {
+        public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
             if (mViewAncestor.get() != null) {
                 getAccessibilityInteractionController()
-                    .findAccessibilityNodeInfosByViewTextClientThread(text, interactionId,
-                            callback);
+                    .findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId,
+                            interactionId, callback);
             }
         }
     }
@@ -4414,13 +4451,15 @@
             }
         }
 
-        public void findAccessibilityNodeInfosByViewTextClientThread(String text, int interactionId,
+        public void findAccessibilityNodeInfosByViewTextClientThread(String text,
+                int accessibilityViewId, int interactionId,
                 IAccessibilityInteractionConnectionCallback callback) {
             Message message = Message.obtain();
             message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT;
             SomeArgs args = mPool.acquire();
             args.arg1 = text;
-            args.argi1 = interactionId;
+            args.argi1 = accessibilityViewId;
+            args.argi2 = interactionId;
             args.arg2 = callback;
             message.obj = args;
             sendMessage(message);
@@ -4429,18 +4468,28 @@
         public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
             SomeArgs args = (SomeArgs) message.obj;
             final String text = (String) args.arg1;
-            final int interactionId = args.argi1;
+            final int accessibilityViewId = args.argi1;
+            final int interactionId = args.argi2;
             final IAccessibilityInteractionConnectionCallback callback =
                 (IAccessibilityInteractionConnectionCallback) args.arg2;
             mPool.release(args);
 
             List<AccessibilityNodeInfo> infos = null;
             try {
-                View root = ViewAncestor.this.mView;
-
                 ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
                 foundViews.clear();
 
+                View root = null;
+                if (accessibilityViewId != View.NO_ID) {
+                    root = findViewByAccessibilityId(accessibilityViewId);
+                } else {
+                    root = ViewAncestor.this.mView;
+                }
+
+                if (root == null) {
+                    return;
+                }
+
                 root.findViewsWithText(foundViews, text);
                 if (foundViews.isEmpty()) {
                     return;
@@ -4577,4 +4626,12 @@
             }
         }
     }
+
+    private class SendWindowContentChanged implements Runnable {
+        public void run() {
+            if (mView != null) {
+                mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 06e4827..5ef7763 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -19,11 +19,10 @@
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
 import android.text.TextUtils;
-import android.view.View;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * This class represents accessibility events that are sent by the system when
@@ -130,7 +129,7 @@
  * <p>
  * <b>TRANSITION TYPES</b> <br>
  * <p>
- * <b>Window state changed</b> - represents the event of opening/closing a
+ * <b>Window state changed</b> - represents the event of opening a
  * {@link android.widget.PopupWindow}, {@link android.view.Menu},
  * {@link android.app.Dialog}, etc. <br>
  * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br>
@@ -140,6 +139,16 @@
  * {@link #getEventTime()},
  * {@link #getText()}
  * <p>
+ * <b>Window content changed</b> - represents the event of change in the
+ * content of a window. This change can be adding/removing view, changing
+ * a view size, etc.<br>
+ * Type: {@link #TYPE_WINDOW_CONTENT_CHANGED} <br>
+ * Properties:
+ * {@link #getClassName()},
+ * {@link #getPackageName()},
+ * {@link #getEventTime()},
+ * {@link #getText()}
+ * <p>
  * <b>NOTIFICATION TYPES</b> <br>
  * <p>
  * <b>Notification state changed</b> - represents the event showing/hiding
@@ -244,6 +253,11 @@
     public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400;
 
     /**
+     * Represents the event of changing the content of a window.
+     */
+    public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
+
+    /**
      * Mask for {@link AccessibilityEvent} all types.
      *
      * @see #TYPE_VIEW_CLICKED
@@ -264,15 +278,11 @@
     private boolean mIsInPool;
 
     private int mEventType;
-    private int mSourceAccessibilityViewId = View.NO_ID;
-    private int mSourceAccessibilityWindowId = View.NO_ID;
     private CharSequence mPackageName;
     private long mEventTime;
 
     private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
 
-    private IAccessibilityServiceConnection mConnection;
-
     /*
      * Hide constructor from clients.
      */
@@ -288,10 +298,43 @@
         super.init(event);
         mEventType = event.mEventType;
         mEventTime = event.mEventTime;
-        mSourceAccessibilityWindowId = event.mSourceAccessibilityWindowId;
-        mSourceAccessibilityViewId = event.mSourceAccessibilityViewId;
         mPackageName = event.mPackageName;
-        mConnection = event.mConnection;
+    }
+
+    /**
+     * Sets the connection for interacting with the AccessibilityManagerService.
+     *
+     * @param connection The connection.
+     *
+     * @hide
+     */
+    @Override
+    public void setConnection(IAccessibilityServiceConnection connection) {
+        super.setConnection(connection);
+        List<AccessibilityRecord> records = mRecords;
+        final int recordCount = records.size();
+        for (int i = 0; i < recordCount; i++) {
+            AccessibilityRecord record = records.get(i);
+            record.setConnection(connection);
+        }
+    }
+
+    /**
+     * Sets if this instance is sealed.
+     *
+     * @param sealed Whether is sealed.
+     *
+     * @hide
+     */
+    @Override
+    public void setSealed(boolean sealed) {
+        super.setSealed(sealed);
+        List<AccessibilityRecord> records = mRecords;
+        final int recordCount = records.size();
+        for (int i = 0; i < recordCount; i++) {
+            AccessibilityRecord record = records.get(i);
+            record.setSealed(sealed);
+        }
     }
 
     /**
@@ -335,81 +378,6 @@
     }
 
     /**
-     * Sets the event source.
-     *
-     * @param source The source.
-     *
-     * @throws IllegalStateException If called from an AccessibilityService.
-     */
-    public void setSource(View source) {
-        enforceNotSealed();
-        if (source != null) {
-            mSourceAccessibilityWindowId = source.getAccessibilityWindowId();
-            mSourceAccessibilityViewId = source.getAccessibilityViewId();
-        } else {
-            mSourceAccessibilityWindowId = View.NO_ID;
-            mSourceAccessibilityViewId = View.NO_ID;
-        }
-    }
-
-    /**
-     * Gets the {@link AccessibilityNodeInfo} of the event source.
-     * <p>
-     *   <strong>
-     *     It is a client responsibility to recycle the received info by
-     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
-     *     of multiple instances.
-     *   </strong>
-     * </p>
-     * @return The info.
-     */
-    public AccessibilityNodeInfo getSource() {
-        enforceSealed();
-        if (mSourceAccessibilityWindowId == View.NO_ID
-                || mSourceAccessibilityViewId == View.NO_ID) {
-            return null;
-        }
-        try {
-            return mConnection.findAccessibilityNodeInfoByAccessibilityId(
-                    mSourceAccessibilityWindowId, mSourceAccessibilityViewId);
-        } catch (RemoteException e) {
-            return null;
-        }
-    }
-
-    /**
-     * Gets the id of the window from which the event comes from.
-     *
-     * @return The window id.
-     */
-    public int getAccessibilityWindowId() {
-        return mSourceAccessibilityWindowId;
-    }
-
-    /**
-     * Sets the client token for the accessibility service that
-     * provided this node info.
-     *
-     * @param connection The connection.
-     *
-     * @hide
-     */
-    public final void setConnection(IAccessibilityServiceConnection connection) {
-        mConnection = connection;
-    }
-
-    /**
-     * Gets the accessibility window id of the source window.
-     *
-     * @return The id.
-     *
-     * @hide
-     */
-    public int getSourceAccessibilityWindowId() {
-        return mSourceAccessibilityWindowId;
-    }
-
-    /**
      * Sets the event type.
      *
      * @param eventType The event type.
@@ -548,10 +516,7 @@
     @Override
     protected void clear() {
         super.clear();
-        mConnection = null;
         mEventType = 0;
-        mSourceAccessibilityViewId = View.NO_ID;
-        mSourceAccessibilityWindowId = View.NO_ID;
         mPackageName = null;
         mEventTime = 0;
         while (!mRecords.isEmpty()) {
@@ -572,8 +537,6 @@
         }
         setSealed(parcel.readInt() == 1);
         mEventType = parcel.readInt();
-        mSourceAccessibilityWindowId = parcel.readInt();
-        mSourceAccessibilityViewId = parcel.readInt();
         mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
         mEventTime = parcel.readLong();
         readAccessibilityRecordFromParcel(this, parcel);
@@ -583,6 +546,8 @@
         for (int i = 0; i < recordCount; i++) {
             AccessibilityRecord record = AccessibilityRecord.obtain();
             readAccessibilityRecordFromParcel(record, parcel);
+            // Do this to write the connection only once.
+            record.setConnection(mConnection);
             mRecords.add(record);
         }
     }
@@ -606,6 +571,9 @@
         record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
         record.mParcelableData = parcel.readParcelable(null);
         parcel.readList(record.mText, null);
+        record.mSourceWindowId = parcel.readInt();
+        record.mSourceViewId = parcel.readInt();
+        record.mSealed = (parcel.readInt() == 1);
     }
 
     /**
@@ -620,8 +588,6 @@
         }
         parcel.writeInt(isSealed() ? 1 : 0);
         parcel.writeInt(mEventType);
-        parcel.writeInt(mSourceAccessibilityWindowId);
-        parcel.writeInt(mSourceAccessibilityViewId);
         TextUtils.writeToParcel(mPackageName, parcel, 0);
         parcel.writeLong(mEventTime);
         writeAccessibilityRecordToParcel(this, parcel, flags);
@@ -654,6 +620,9 @@
         TextUtils.writeToParcel(record.mBeforeText, parcel, flags);
         parcel.writeParcelable(record.mParcelableData, flags);
         parcel.writeList(record.mText);
+        parcel.writeInt(record.mSourceWindowId);
+        parcel.writeInt(record.mSourceViewId);
+        parcel.writeInt(record.mSealed ? 1 : 0);
     }
 
     /**
@@ -672,8 +641,8 @@
         builder.append(super.toString());
         if (DEBUG) {
             builder.append("\n");
-            builder.append("; sourceAccessibilityWindowId: ").append(mSourceAccessibilityWindowId);
-            builder.append("; sourceAccessibilityViewId: ").append(mSourceAccessibilityViewId);
+            builder.append("; sourceWindowId: ").append(mSourceWindowId);
+            builder.append("; sourceViewId: ").append(mSourceViewId);
             for (int i = 0; i < mRecords.size(); i++) {
                 AccessibilityRecord record = mRecords.get(i);
                 builder.append("  Record ");
@@ -733,6 +702,8 @@
                 return "TYPE_TOUCH_EXPLORATION_GESTURE_START";
             case TYPE_TOUCH_EXPLORATION_GESTURE_END:
                 return "TYPE_TOUCH_EXPLORATION_GESTURE_END";
+            case TYPE_WINDOW_CONTENT_CHANGED:
+                return "TYPE_WINDOW_CONTENT_CHANGED";
             default:
                 return null;
         }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 5fa65b4..18ef38a 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -26,6 +26,9 @@
 import android.util.SparseIntArray;
 import android.view.View;
 
+import java.util.Collections;
+import java.util.List;
+
 /**
  * This class represents a node of the screen content. From the point of
  * view of an accessibility service the screen content is presented as tree
@@ -97,7 +100,8 @@
     private int mAccessibilityWindowId = View.NO_ID;
     private int mParentAccessibilityViewId = View.NO_ID;
     private int mBooleanProperties;
-    private final Rect mBounds = new Rect();
+    private final Rect mBoundsInParent = new Rect();
+    private final Rect mBoundsInScreen = new Rect();
 
     private CharSequence mPackageName;
     private CharSequence mClassName;
@@ -132,7 +136,7 @@
      *
      * @return The window id.
      */
-    public int getAccessibilityWindowId() {
+    public int getWindowId() {
         return mAccessibilityWindowId;
     }
 
@@ -163,12 +167,16 @@
     public AccessibilityNodeInfo getChild(int index) {
         enforceSealed();
         final int childAccessibilityViewId = mChildAccessibilityIds.get(index);
+        if (!canPerformRequestOverConnection(childAccessibilityViewId)) {
+            return null;
+        }
         try {
             return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
                     childAccessibilityViewId);
-        } catch (RemoteException e) {
-             return null;
+        } catch (RemoteException re) {
+             /* ignore*/
         }
+        return null;
     }
 
     /**
@@ -230,12 +238,37 @@
      */
     public boolean performAction(int action) {
         enforceSealed();
+        if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
+            return false;
+        }
         try {
             return mConnection.performAccessibilityAction(mAccessibilityWindowId,
                     mAccessibilityViewId, action);
         } catch (RemoteException e) {
-            return false;
+            /* ignore */
         }
+        return false;
+    }
+
+    /**
+     * Finds {@link AccessibilityNodeInfo}s by text. The match is case
+     * insensitive containment.
+     *
+     * @param text The searched text.
+     * @return A list of node info.
+     */
+    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
+        enforceSealed();
+        if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
+            return null;
+        }
+        try {
+            return mConnection.findAccessibilityNodeInfosByViewText(text, mAccessibilityWindowId,
+                    mAccessibilityViewId);
+        } catch (RemoteException e) {
+            /* ignore */
+        }
+        return Collections.emptyList();
     }
 
     /**
@@ -251,12 +284,16 @@
      */
     public AccessibilityNodeInfo getParent() {
         enforceSealed();
-        try {
-            return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
-                    mParentAccessibilityViewId);
-        } catch (RemoteException e) {
+        if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
             return null;
         }
+        try {
+            return mConnection.findAccessibilityNodeInfoByAccessibilityId(
+                    mAccessibilityWindowId, mParentAccessibilityViewId);
+        } catch (RemoteException e) {
+            /* ignore */
+        }
+        return null;
     }
 
     /**
@@ -279,8 +316,9 @@
      *
      * @param outBounds The output node bounds.
      */
-    public void getBounds(Rect outBounds) {
-        outBounds.set(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
+    public void getBoundsInParent(Rect outBounds) {
+        outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
+                mBoundsInParent.right, mBoundsInParent.bottom);
     }
 
     /**
@@ -293,9 +331,34 @@
      *
      * @throws IllegalStateException If called from an AccessibilityService.
      */
-    public void setBounds(Rect bounds) {
+    public void setBoundsInParent(Rect bounds) {
         enforceNotSealed();
-        mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+    }
+
+    /**
+     * Gets the node bounds in screen coordinates.
+     *
+     * @param outBounds The output node bounds.
+     */
+    public void getBoundsInScreen(Rect outBounds) {
+        outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
+                mBoundsInScreen.right, mBoundsInScreen.bottom);
+    }
+
+    /**
+     * Sets the node bounds in screen coordinates.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param bounds The node bounds.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setBoundsInScreen(Rect bounds) {
+        enforceNotSealed();
+        mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
     }
 
     /**
@@ -636,6 +699,7 @@
      * @hide
      */
     public final void setConnection(IAccessibilityServiceConnection connection) {
+        enforceNotSealed();
         mConnection = connection;
     }
 
@@ -777,10 +841,15 @@
             parcel.writeInt(childIds.valueAt(i));
         }
 
-        parcel.writeInt(mBounds.top);
-        parcel.writeInt(mBounds.bottom);
-        parcel.writeInt(mBounds.left);
-        parcel.writeInt(mBounds.right);
+        parcel.writeInt(mBoundsInParent.top);
+        parcel.writeInt(mBoundsInParent.bottom);
+        parcel.writeInt(mBoundsInParent.left);
+        parcel.writeInt(mBoundsInParent.right);
+
+        parcel.writeInt(mBoundsInScreen.top);
+        parcel.writeInt(mBoundsInScreen.bottom);
+        parcel.writeInt(mBoundsInScreen.left);
+        parcel.writeInt(mBoundsInScreen.right);
 
         parcel.writeInt(mActions);
 
@@ -818,10 +887,15 @@
             childIds.put(i, childId);
         }
 
-        mBounds.top = parcel.readInt();
-        mBounds.bottom = parcel.readInt();
-        mBounds.left = parcel.readInt();
-        mBounds.right = parcel.readInt();
+        mBoundsInParent.top = parcel.readInt();
+        mBoundsInParent.bottom = parcel.readInt();
+        mBoundsInParent.left = parcel.readInt();
+        mBoundsInParent.right = parcel.readInt();
+
+        mBoundsInScreen.top = parcel.readInt();
+        mBoundsInScreen.bottom = parcel.readInt();
+        mBoundsInScreen.left = parcel.readInt();
+        mBoundsInScreen.right = parcel.readInt();
 
         mActions = parcel.readInt();
 
@@ -842,7 +916,8 @@
         mAccessibilityViewId = View.NO_ID;
         mParentAccessibilityViewId = View.NO_ID;
         mChildAccessibilityIds.clear();
-        mBounds.set(0, 0, 0, 0);
+        mBoundsInParent.set(0, 0, 0, 0);
+        mBoundsInScreen.set(0, 0, 0, 0);
         mBooleanProperties = 0;
         mPackageName = null;
         mClassName = null;
@@ -869,6 +944,12 @@
         return actionSymbolicNames.get(action);
     }
 
+    private boolean canPerformRequestOverConnection(int accessibilityViewId) {
+        return (mAccessibilityWindowId != View.NO_ID
+                && accessibilityViewId != View.NO_ID
+                && mConnection != null);
+    }
+
     @Override
     public boolean equals(Object object) {
         if (this == object) {
@@ -918,7 +999,8 @@
            builder.append("]");
         }
 
-        builder.append("; bounds: " + mBounds);
+        builder.append("; boundsInParent: " + mBoundsInParent);
+        builder.append("; boundsInScreen: " + mBoundsInScreen);
 
         builder.append("; packageName: ").append(mPackageName);
         builder.append("; className: ").append(mClassName);
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 4bf03a7..9c495e21 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -16,7 +16,10 @@
 
 package android.view.accessibility;
 
+import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.os.Parcelable;
+import android.os.RemoteException;
+import android.view.View;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -45,26 +48,29 @@
     private static int sPoolSize;
     private AccessibilityRecord mNext;
     private boolean mIsInPool;
-    private boolean mSealed;
 
-    protected int mBooleanProperties;
-    protected int mCurrentItemIndex;
-    protected int mItemCount;
-    protected int mFromIndex;
-    protected int mAddedCount;
-    protected int mRemovedCount;
+    boolean mSealed;
+    int mBooleanProperties;
+    int mCurrentItemIndex;
+    int mItemCount;
+    int mFromIndex;
+    int mAddedCount;
+    int mRemovedCount;
+    int mSourceViewId = View.NO_ID;
+    int mSourceWindowId = View.NO_ID;
 
-    protected CharSequence mClassName;
-    protected CharSequence mContentDescription;
-    protected CharSequence mBeforeText;
-    protected Parcelable mParcelableData;
+    CharSequence mClassName;
+    CharSequence mContentDescription;
+    CharSequence mBeforeText;
+    Parcelable mParcelableData;
 
-    protected final List<CharSequence> mText = new ArrayList<CharSequence>();
+    final List<CharSequence> mText = new ArrayList<CharSequence>();
+    IAccessibilityServiceConnection mConnection;
 
     /*
      * Hide constructor.
      */
-    protected AccessibilityRecord() {
+    AccessibilityRecord() {
 
     }
 
@@ -74,7 +80,7 @@
      * @param record The to initialize from.
      */
     void init(AccessibilityRecord record) {
-        mSealed = record.isSealed();
+        mSealed = record.mSealed;
         mBooleanProperties = record.mBooleanProperties;
         mCurrentItemIndex = record.mCurrentItemIndex;
         mItemCount = record.mItemCount;
@@ -86,6 +92,73 @@
         mBeforeText = record.mBeforeText;
         mParcelableData = record.mParcelableData;
         mText.addAll(record.mText);
+        mSourceWindowId = record.mSourceWindowId;
+        mSourceViewId = record.mSourceViewId;
+        mConnection = record.mConnection;
+    }
+
+    /**
+     * Sets the event source.
+     *
+     * @param source The source.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setSource(View source) {
+        enforceNotSealed();
+        if (source != null) {
+            mSourceWindowId = source.getAccessibilityWindowId();
+            mSourceViewId = source.getAccessibilityViewId();
+        } else {
+            mSourceWindowId = View.NO_ID;
+            mSourceViewId = View.NO_ID;
+        }
+    }
+
+    /**
+     * Gets the {@link AccessibilityNodeInfo} of the event source.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     * @return The info.
+     */
+    public AccessibilityNodeInfo getSource() {
+        enforceSealed();
+        if (mSourceWindowId == View.NO_ID || mSourceViewId == View.NO_ID || mConnection == null) {
+            return null;
+        }
+        try {
+            return mConnection.findAccessibilityNodeInfoByAccessibilityId(mSourceWindowId,
+                    mSourceViewId);
+        } catch (RemoteException e) {
+           /* ignore */
+        }
+        return null;
+    }
+
+    /**
+     * Sets the connection for interacting with the AccessibilityManagerService.
+     *
+     * @param connection The connection.
+     *
+     * @hide
+     */
+    public void setConnection(IAccessibilityServiceConnection connection) {
+        enforceNotSealed();
+        mConnection = connection;
+    }
+
+    /**
+     * Gets the id of the window from which the event comes from.
+     *
+     * @return The window id.
+     */
+    public int getWindowId() {
+        return mSourceWindowId;
     }
 
     /**
@@ -386,10 +459,8 @@
      * Gets if this instance is sealed.
      *
      * @return Whether is sealed.
-     *
-     * @hide
      */
-    public boolean isSealed() {
+    boolean isSealed() {
         return mSealed;
     }
 
@@ -397,10 +468,8 @@
      * Enforces that this instance is sealed.
      *
      * @throws IllegalStateException If this instance is not sealed.
-     *
-     * @hide
      */
-    protected void enforceSealed() {
+    void enforceSealed() {
         if (!isSealed()) {
             throw new IllegalStateException("Cannot perform this "
                     + "action on a not sealed instance.");
@@ -411,10 +480,8 @@
      * Enforces that this instance is not sealed.
      *
      * @throws IllegalStateException If this instance is sealed.
-     *
-     * @hide
      */
-    protected void enforceNotSealed() {
+    void enforceNotSealed() {
         if (isSealed()) {
             throw new IllegalStateException("Cannot perform this "
                     + "action on an sealed instance.");
@@ -502,10 +569,8 @@
 
     /**
      * Clears the state of this instance.
-     *
-     * @hide
      */
-    protected void clear() {
+    void clear() {
         mSealed = false;
         mBooleanProperties = 0;
         mCurrentItemIndex = INVALID_POSITION;
@@ -518,6 +583,8 @@
         mBeforeText = null;
         mParcelableData = null;
         mText.clear();
+        mSourceViewId = View.NO_ID;
+        mSourceWindowId = View.NO_ID;
     }
 
     @Override
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index 77dcd07..d35186b 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -33,8 +33,8 @@
     void findAccessibilityNodeInfoByViewId(int id, int interactionId,
         IAccessibilityInteractionConnectionCallback callback);
 
-    void findAccessibilityNodeInfosByViewText(String text, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback);
+    void findAccessibilityNodeInfosByViewText(String text, int accessibilityViewId,
+        int interactionId, IAccessibilityInteractionConnectionCallback callback);
 
     void performAccessibilityAction(int accessibilityId, int action, int interactionId,
         IAccessibilityInteractionConnectionCallback callback);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 82dd5db..3fe8149 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -55,7 +55,6 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
-import android.view.accessibility.AccessibilityEvent;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index c4d05e9..755d4e0 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -904,8 +904,10 @@
     public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) {
         // Add a record for ourselves as well.
         AccessibilityEvent record = AccessibilityEvent.obtain();
+        record.setSource(this);
         // Set the class since it is not populated in #dispatchPopulateAccessibilityEvent
         record.setClassName(getClass().getName());
+        child.onInitializeAccessibilityEvent(record);
         child.dispatchPopulateAccessibilityEvent(record);
         event.appendRecord(record);
         return true;
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
index 3260616..99d534c 100644
--- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
@@ -29,6 +29,7 @@
 import android.provider.Settings;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -49,6 +50,9 @@
  */
 public class InterrogationActivityTest
         extends ActivityInstrumentationTestCase2<InterrogationActivity> {
+    private static final boolean DEBUG = true;
+
+    private static String LOG_TAG = "InterrogationActivityTest";
 
     // Timeout before give up wait for the system to process an accessibility setting change.
     private static final int TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING = 2000;
@@ -62,7 +66,7 @@
     private static IAccessibilityServiceConnection sConnection;
 
     // The last received accessibility event
-    private static volatile AccessibilityEvent sLastAccessibilityEvent;
+    private static volatile AccessibilityEvent sLastFocusAccessibilityEvent;
 
     public InterrogationActivityTest() {
         super(InterrogationActivity.class);
@@ -72,18 +76,19 @@
     @LargeTest
     public void testFindAccessibilityNodeInfoByViewId() throws Exception {
         beforeClassIfNeeded();
+        final long startTimeMillis = SystemClock.uptimeMillis();
         try {
             // bring up the activity
             getActivity();
 
-            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
-                    R.id.button5);
+            AccessibilityNodeInfo button =
+                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertNotNull(button);
             assertEquals(0, button.getChildCount());
 
             // bounds
             Rect bounds = new Rect();
-            button.getBounds(bounds);
+            button.getBoundsInParent(bounds);
             assertEquals(0, bounds.left);
             assertEquals(0, bounds.top);
             assertEquals(73, bounds.right);
@@ -111,28 +116,40 @@
                 button.getActions());
         } finally {
             afterClassIfNeeded();
+            if (DEBUG) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewId: "
+                        + elapsedTimeMillis + "ms");
+            }
         }
     }
 
     @LargeTest
     public void testFindAccessibilityNodeInfoByViewText() throws Exception {
         beforeClassIfNeeded();
+        final long startTimeMillis = SystemClock.uptimeMillis();
         try {
             // bring up the activity
             getActivity();
 
             // find a view by text
             List<AccessibilityNodeInfo> buttons =
-                getConnection().findAccessibilityNodeInfosByViewText("butto");
+                getConnection().findAccessibilityNodeInfosByViewTextInActiveWindow("butto");
             assertEquals(9, buttons.size());
         } finally {
             afterClassIfNeeded();
+            if (DEBUG) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                Log.i(LOG_TAG, "testFindAccessibilityNodeInfoByViewText: "
+                        + elapsedTimeMillis + "ms");
+            }
         }
     }
 
     @LargeTest
     public void testTraverseAllViews() throws Exception {
         beforeClassIfNeeded();
+        final long startTimeMillis = SystemClock.uptimeMillis();
         try {
             // bring up the activity
             getActivity();
@@ -153,8 +170,8 @@
             classNameAndTextList.add("android.widget.ButtonButton8");
             classNameAndTextList.add("android.widget.ButtonButton9");
 
-            AccessibilityNodeInfo root =  getConnection().findAccessibilityNodeInfoByViewId(
-                    R.id.root);
+            AccessibilityNodeInfo root =
+                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.root);
             assertNotNull("We must find the existing root.", root);
 
             Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
@@ -181,125 +198,152 @@
             }
         } finally {
             afterClassIfNeeded();
+            if (DEBUG) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                Log.i(LOG_TAG, "testTraverseAllViews: " + elapsedTimeMillis + "ms");
+            }
         }
     }
 
     @LargeTest
     public void testPerformAccessibilityActionFocus() throws Exception {
         beforeClassIfNeeded();
+        final long startTimeMillis = SystemClock.uptimeMillis();
         try {
             // bring up the activity
             getActivity();
 
             // find a view and make sure it is not focused
-            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
-                    R.id.button5);
+            AccessibilityNodeInfo button =
+                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertFalse(button.isFocused());
 
             // focus the view
             assertTrue(button.performAction(ACTION_FOCUS));
 
             // find the view again and make sure it is focused
-            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertTrue(button.isFocused());
         } finally {
             afterClassIfNeeded();
+            if (DEBUG) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                Log.i(LOG_TAG, "testPerformAccessibilityActionFocus: " + elapsedTimeMillis + "ms");
+            }
         }
     }
 
     @LargeTest
     public void testPerformAccessibilityActionClearFocus() throws Exception {
         beforeClassIfNeeded();
+        final long startTimeMillis = SystemClock.uptimeMillis();
         try {
             // bring up the activity
             getActivity();
 
             // find a view and make sure it is not focused
-            AccessibilityNodeInfo button =  getConnection().findAccessibilityNodeInfoByViewId(
-                    R.id.button5);
+            AccessibilityNodeInfo button =
+                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertFalse(button.isFocused());
 
             // focus the view
             assertTrue(button.performAction(ACTION_FOCUS));
 
             // find the view again and make sure it is focused
-            button =  getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            button =  getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertTrue(button.isFocused());
 
             // unfocus the view
             assertTrue(button.performAction(ACTION_CLEAR_FOCUS));
 
             // find the view again and make sure it is not focused
-            button =  getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            button =  getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertFalse(button.isFocused());
         } finally {
             afterClassIfNeeded();
+            if (DEBUG) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                Log.i(LOG_TAG, "testPerformAccessibilityActionClearFocus: "
+                        + elapsedTimeMillis + "ms");
+            }
         }
     }
 
     @LargeTest
     public void testPerformAccessibilityActionSelect() throws Exception {
         beforeClassIfNeeded();
+        final long startTimeMillis = SystemClock.uptimeMillis();
         try {
             // bring up the activity
             getActivity();
 
             // find a view and make sure it is not selected
-            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
-                    R.id.button5);
+            AccessibilityNodeInfo button =
+                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertFalse(button.isSelected());
 
             // select the view
             assertTrue(button.performAction(ACTION_SELECT));
 
             // find the view again and make sure it is selected
-            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertTrue(button.isSelected());
         } finally {
             afterClassIfNeeded();
+            if (DEBUG) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                Log.i(LOG_TAG, "testPerformAccessibilityActionSelect: " + elapsedTimeMillis + "ms");
+            }
         }
     }
 
     @LargeTest
     public void testPerformAccessibilityActionClearSelection() throws Exception {
         beforeClassIfNeeded();
+        final long startTimeMillis = SystemClock.uptimeMillis();
         try {
             // bring up the activity
             getActivity();
 
             // find a view and make sure it is not selected
-            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
-                    R.id.button5);
+            AccessibilityNodeInfo button =
+                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertFalse(button.isSelected());
 
             // select the view
             assertTrue(button.performAction(ACTION_SELECT));
 
             // find the view again and make sure it is selected
-            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertTrue(button.isSelected());
 
             // unselect the view
             assertTrue(button.performAction(ACTION_CLEAR_SELECTION));
 
             // find the view again and make sure it is not selected
-            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            button = getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertFalse(button.isSelected());
         } finally {
             afterClassIfNeeded();
+            if (DEBUG) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                Log.i(LOG_TAG, "testPerformAccessibilityActionClearSelection: "
+                        + elapsedTimeMillis + "ms");
+            }
         }
     }
 
     @LargeTest
     public void testAccessibilityEventGetSource() throws Exception {
         beforeClassIfNeeded();
+        final long startTimeMillis = SystemClock.uptimeMillis();
         try {
             // bring up the activity
             getActivity();
 
             // find a view and make sure it is not focused
-            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
-                    R.id.button5);
+            AccessibilityNodeInfo button =
+                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
             assertFalse(button.isSelected());
 
             // focus the view
@@ -314,14 +358,14 @@
             }
 
             // check that last event source
-            AccessibilityNodeInfo source = sLastAccessibilityEvent.getSource();
+            AccessibilityNodeInfo source = sLastFocusAccessibilityEvent.getSource();
             assertNotNull(source);
 
             // bounds
             Rect buttonBounds = new Rect();
-            button.getBounds(buttonBounds);
+            button.getBoundsInParent(buttonBounds);
             Rect sourceBounds = new Rect();
-            source.getBounds(sourceBounds);
+            source.getBoundsInParent(sourceBounds);
 
             assertEquals(buttonBounds.left, sourceBounds.left);
             assertEquals(buttonBounds.right, sourceBounds.right);
@@ -346,6 +390,42 @@
             assertSame(button.isChecked(), source.isChecked());
         } finally {
             afterClassIfNeeded();
+            if (DEBUG) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                Log.i(LOG_TAG, "testAccessibilityEventGetSource: " + elapsedTimeMillis + "ms");
+            }
+        }
+    }
+
+    @LargeTest
+    public void testObjectContract() throws Exception {
+        beforeClassIfNeeded();
+        final long startTimeMillis = SystemClock.uptimeMillis();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not focused
+            AccessibilityNodeInfo button =
+                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            AccessibilityNodeInfo parent = button.getParent();
+            final int childCount = parent.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                AccessibilityNodeInfo child = parent.getChild(i);
+                assertNotNull(child);
+                if (child.equals(button)) {
+                    assertEquals("Equal objects must have same hasCode.", button.hashCode(),
+                            child.hashCode());
+                    return;
+                }
+            }
+            fail("Parent's children do not have the info whose parent is the parent.");
+        } finally {
+            afterClassIfNeeded();
+            if (DEBUG) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                Log.i(LOG_TAG, "testObjectContract: " + elapsedTimeMillis + "ms");
+            }
         }
     }
 
@@ -442,7 +522,9 @@
                 public void onInterrupt() {}
 
                 public void onAccessibilityEvent(AccessibilityEvent event) {
-                    sLastAccessibilityEvent = AccessibilityEvent.obtain(event);
+                    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) {
+                        sLastFocusAccessibilityEvent = AccessibilityEvent.obtain(event);
+                    }
                     synchronized (sConnection) {
                         sConnection.notifyAll();
                     }
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 86671d6..ec59da6 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -46,6 +46,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.IWindow;
+import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityInteractionConnection;
@@ -149,9 +150,6 @@
 
             synchronized (mLock) {
                 notifyEventListenerLocked(service, eventType);
-                AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
-                service.mPendingEvents.remove(eventType);
-                tryRecycleLocked(oldEvent);
             }
         }
     };
@@ -319,17 +317,14 @@
 
     public boolean sendAccessibilityEvent(AccessibilityEvent event) {
         synchronized (mLock) {
-            mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event);
-            notifyAccessibilityServicesDelayedLocked(event, false);
-            notifyAccessibilityServicesDelayedLocked(event, true);
+            if (mSecurityPolicy.canDispatchAccessibilityEvent(event)) {
+                mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event);
+                notifyAccessibilityServicesDelayedLocked(event, false);
+                notifyAccessibilityServicesDelayedLocked(event, true);
+            }
         }
-        // event not scheduled for dispatch => recycle
-        if (mHandledFeedbackTypes == 0) {
-            event.recycle();
-        } else {
-            mHandledFeedbackTypes = 0;
-        }
-
+        event.recycle();
+        mHandledFeedbackTypes = 0;
         return (OWN_PROCESS_ID != Binder.getCallingPid());
     }
 
@@ -517,46 +512,27 @@
     private void notifyAccessibilityServiceDelayedLocked(Service service,
             AccessibilityEvent event) {
         synchronized (mLock) {
-            int eventType = event.getEventType();
+            final int eventType = event.getEventType();
+            // Make a copy since during dispatch it is possible the event to
+            // be modified to remove its source if the receiving service does
+            // not have permission to access the window content.
+            AccessibilityEvent newEvent = AccessibilityEvent.obtain(event);
             AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
-            service.mPendingEvents.put(eventType, event);
+            service.mPendingEvents.put(eventType, newEvent);
 
-            int what = eventType | (service.mId << 16);
+            final int what = eventType | (service.mId << 16);
             if (oldEvent != null) {
                 mHandler.removeMessages(what);
-                tryRecycleLocked(oldEvent);
+                oldEvent.recycle();
             }
 
             Message message = mHandler.obtainMessage(what, service);
-            message.arg1 = event.getEventType();
+            message.arg1 = eventType;
             mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
         }
     }
 
     /**
-     * Recycles an event if it can be safely recycled. The condition is that no
-     * not notified service is interested in the event.
-     *
-     * @param event The event.
-     */
-    private void tryRecycleLocked(AccessibilityEvent event) {
-        if (event == null) {
-            return;
-        }
-        int eventType = event.getEventType();
-        List<Service> services = mServices;
-
-        // linear in the number of service which is not large
-        for (int i = 0, count = services.size(); i < count; i++) {
-            Service service = services.get(i);
-            if (service.mPendingEvents.get(eventType) == event) {
-                return;
-            }
-        }
-        event.recycle();
-    }
-
-    /**
      * Notifies a service for a scheduled event given the event type.
      *
      * @param service The service.
@@ -565,7 +541,7 @@
     private void notifyEventListenerLocked(Service service, int eventType) {
         IEventListener listener = service.mServiceInterface;
         AccessibilityEvent event = service.mPendingEvents.get(eventType);
-
+        service.mPendingEvents.remove(eventType);
         try {
             if (mSecurityPolicy.canRetrieveWindowContent(service)) {
                 event.setConnection(service);
@@ -574,6 +550,7 @@
             }
             event.setSealed(true);
             listener.onAccessibilityEvent(event);
+            event.recycle();
             if (DEBUG) {
                 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
             }
@@ -926,7 +903,7 @@
             }
         }
 
-        public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId) {
+        public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId) {
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
                 final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
@@ -961,10 +938,18 @@
             return null;
         }
 
-        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text) {
+        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(
+                String text) {
+            return findAccessibilityNodeInfosByViewText(text,
+                    mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID);
+        }
+
+        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text,
+                int accessibilityWindowId, int accessibilityViewId) {
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
-                final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+                final boolean permissionGranted =
+                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
                 if (permissionGranted) {
                     connection = getConnectionToRetrievalAllowingWindowLocked();
                 }
@@ -978,7 +963,8 @@
             final long identityToken = Binder.clearCallingIdentity();
             try {
                 final int interactionId = mInteractionIdCounter.getAndIncrement();
-                connection.findAccessibilityNodeInfosByViewText(text, interactionId, mCallback);
+                connection.findAccessibilityNodeInfosByViewText(text, accessibilityViewId,
+                        interactionId, mCallback);
                 List<AccessibilityNodeInfo> infos =
                     mCallback.getFindAccessibilityNodeInfosResultAndClear(interactionId);
                 if (infos != null) {
@@ -1112,16 +1098,23 @@
             | AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED
             | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
             | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
-            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED
+            | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
 
         private int mRetrievalAlowingWindowId;
 
+        private boolean canDispatchAccessibilityEvent(AccessibilityEvent event) {
+            // Send window changed event only for the retrieval allowing window.
+            return (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+                    || event.getWindowId() == mRetrievalAlowingWindowId);
+        }
+
         public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) {
-            final int windowId = event.getSourceAccessibilityWindowId();
+            final int windowId = event.getWindowId();
             final int eventType = event.getEventType();
             if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) != 0) {
                 mRetrievalAlowingWindowId = windowId;
-            } else {
+            } else { 
                 event.setSource(null);
             }
         }