Merge "Intra-process view hierarchy interrogation does not work."
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 8b4e7aee..7c41082 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -18,6 +18,7 @@
 
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 
 /**
  * Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
@@ -30,84 +31,79 @@
 
     /**
      * Finds an {@link AccessibilityNodeInfo} by accessibility id.
-     * <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>
      *
      * @param accessibilityWindowId A unique window id.
      * @param accessibilityViewId A unique View accessibility id.
-     * @return The node info.
+     * @param interactionId The id of the interaction for matching with the callback result.
+     * @param callback Callback which to receive the result.
+     * @param threadId The id of the calling thread.
+     * @return The current window scale, where zero means a failure.
      */
-    AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
-        int accessibilityViewId);
+    float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+        int accessibilityViewId, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback, long threadId);
 
     /**
      * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
      * 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
-     *     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.
+     * @param accessibilityWindowId A unique window id.
+     * @param accessibilityViewId A unique View accessibility id from where to start the search.
      *        Use {@link android.view.View#NO_ID} to start from the root.
-     * @return A list of node info.
+     * @param interactionId The id of the interaction for matching with the callback result.
+     * @param callback Callback which to receive the result.
+     * @param threadId The id of the calling thread.
+     * @return The current window scale, where zero means a failure.
      */
-    List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text,
-        int accessibilityWindowId, int accessibilityViewId);
+    float findAccessibilityNodeInfosByViewText(String text, int accessibilityWindowId,
+        int accessibilityViewId, int interractionId,
+        IAccessibilityInteractionConnectionCallback callback, long threadId);
 
     /**
      * 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.
+     * @param interactionId The id of the interaction for matching with the callback result.
+     * @param callback Callback which to receive the result.
+     * @param threadId The id of the calling thread.
+     * @return The current window scale, where zero means a failure.
      */
-    List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(String text);
+    float findAccessibilityNodeInfosByViewTextInActiveWindow(String text,
+        int interactionId, IAccessibilityInteractionConnectionCallback callback,
+        long threadId);
 
     /**
      * 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
-     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
-     *     of multiple instances.
-     *   </strong>
-     * </p>
+     * in the currently active window and starts from the root View in the window.
      *
      * @param id The id of the node.
-     * @return The node info.
+     * @param interactionId The id of the interaction for matching with the callback result.
+     * @param callback Callback which to receive the result.
+     * @param threadId The id of the calling thread.
+     * @return The current window scale, where zero means a failure.
      */
-    AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId);
+    float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback, long threadId);
 
     /**
      * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
      *
      * @param accessibilityWindowId The id of the window.
-     * @param accessibilityViewId The of a view in the .
+     * @param accessibilityViewId A unique View accessibility id.
+     * @param action The action to perform.
+     * @param interactionId The id of the interaction for matching with the callback result.
+     * @param callback Callback which to receive the result.
+     * @param threadId The id of the calling thread.
      * @return Whether the action was performed.
      */
     boolean performAccessibilityAction(int accessibilityWindowId, int accessibilityViewId,
-        int action);
+        int action, int interactionId, IAccessibilityInteractionConnectionCallback callback,
+        long threadId);
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fdf6ad7..6ea863f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -61,6 +61,7 @@
 import android.util.TypedValue;
 import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -4362,37 +4363,42 @@
         }
 
         public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId,
-                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+                int interactionId, IAccessibilityInteractionConnectionCallback callback,
+                int interrogatingPid, long interrogatingTid) {
             if (mViewAncestor.get() != null) {
                 getAccessibilityInteractionController()
                     .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId,
-                        interactionId, callback);
+                        interactionId, callback, interrogatingPid, interrogatingTid);
             }
         }
 
         public void performAccessibilityAction(int accessibilityId, int action,
-                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+                int interactionId, IAccessibilityInteractionConnectionCallback callback,
+                int interogatingPid, long interrogatingTid) {
             if (mViewAncestor.get() != null) {
                 getAccessibilityInteractionController()
                     .performAccessibilityActionClientThread(accessibilityId, action, interactionId,
-                            callback);
+                            callback, interogatingPid, interrogatingTid);
             }
         }
 
         public void findAccessibilityNodeInfoByViewId(int viewId,
-                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+                int interactionId, IAccessibilityInteractionConnectionCallback callback,
+                int interrogatingPid, long interrogatingTid) {
             if (mViewAncestor.get() != null) {
                 getAccessibilityInteractionController()
-                    .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback);
+                    .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback,
+                            interrogatingPid, interrogatingTid);
             }
         }
 
         public void findAccessibilityNodeInfosByViewText(String text, int accessibilityId,
-                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+                int interactionId, IAccessibilityInteractionConnectionCallback callback,
+                int interrogatingPid, long interrogatingTid) {
             if (mViewAncestor.get() != null) {
                 getAccessibilityInteractionController()
                     .findAccessibilityNodeInfosByViewTextClientThread(text, accessibilityId,
-                            interactionId, callback);
+                            interactionId, callback, interrogatingPid, interrogatingTid);
             }
         }
     }
@@ -4468,13 +4474,24 @@
         }
 
         public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId,
-                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+                int interactionId, IAccessibilityInteractionConnectionCallback callback,
+                int interrogatingPid, long interrogatingTid) {
             Message message = Message.obtain();
             message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
             message.arg1 = accessibilityId;
             message.arg2 = interactionId;
             message.obj = callback;
-            sendMessage(message);
+            // If the interrogation is performed by the same thread as the main UI
+            // thread in this process, set the message as a static reference so
+            // after this call completes the same thread but in the interrogating
+            // client can handle the message to generate the result.
+            if (interrogatingPid == Process.myPid()
+                    && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+                message.setTarget(ViewRootImpl.this);
+                AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
+            } else {
+                sendMessage(message);
+            }
         }
 
         public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
@@ -4502,13 +4519,24 @@
         }
 
         public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId,
-                IAccessibilityInteractionConnectionCallback callback) {
+                IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+                long interrogatingTid) {
             Message message = Message.obtain();
             message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
             message.arg1 = viewId;
             message.arg2 = interactionId;
             message.obj = callback;
-            sendMessage(message);
+            // If the interrogation is performed by the same thread as the main UI
+            // thread in this process, set the message as a static reference so
+            // after this call completes the same thread but in the interrogating
+            // client can handle the message to generate the result.
+            if (interrogatingPid == Process.myPid()
+                    && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+                message.setTarget(ViewRootImpl.this);
+                AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
+            } else {
+                sendMessage(message);
+            }
         }
 
         public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
@@ -4535,7 +4563,8 @@
 
         public void findAccessibilityNodeInfosByViewTextClientThread(String text,
                 int accessibilityViewId, int interactionId,
-                IAccessibilityInteractionConnectionCallback callback) {
+                IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+                long interrogatingTid) {
             Message message = Message.obtain();
             message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT;
             SomeArgs args = mPool.acquire();
@@ -4544,7 +4573,17 @@
             args.argi2 = interactionId;
             args.arg2 = callback;
             message.obj = args;
-            sendMessage(message);
+            // If the interrogation is performed by the same thread as the main UI
+            // thread in this process, set the message as a static reference so
+            // after this call completes the same thread but in the interrogating
+            // client can handle the message to generate the result.
+            if (interrogatingPid == Process.myPid()
+                    && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+                message.setTarget(ViewRootImpl.this);
+                AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
+            } else {
+                sendMessage(message);
+            }
         }
 
         public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
@@ -4597,7 +4636,8 @@
         }
 
         public void performAccessibilityActionClientThread(int accessibilityId, int action,
-                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+                int interactionId, IAccessibilityInteractionConnectionCallback callback,
+                int interogatingPid, long interrogatingTid) {
             Message message = Message.obtain();
             message.what = DO_PERFORM_ACCESSIBILITY_ACTION;
             SomeArgs args = mPool.acquire();
@@ -4606,7 +4646,17 @@
             args.argi3 = interactionId;
             args.arg1 = callback;
             message.obj = args;
-            sendMessage(message);
+            // If the interrogation is performed by the same thread as the main UI
+            // thread in this process, set the message as a static reference so
+            // after this call completes the same thread but in the interrogating
+            // client can handle the message to generate the result.
+            if (interogatingPid == Process.myPid()
+                    && interrogatingTid == Looper.getMainLooper().getThread().getId()) {
+                message.setTarget(ViewRootImpl.this);
+                AccessibilityInteractionClient.getInstance().setSameThreadMessage(message);
+            } else {
+                sendMessage(message);
+            }
         }
 
         public void perfromAccessibilityActionUiThread(Message message) {
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
new file mode 100644
index 0000000..071701e
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -0,0 +1,462 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ **     http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.graphics.Rect;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This class is a singleton that performs accessibility interaction
+ * which is it queries remote view hierarchies about snapshots of their
+ * views as well requests from these hierarchies to perform certain
+ * actions on their views.
+ *
+ * Rationale: The content retrieval APIs are synchronous from a client's
+ *     perspective but internally they are asynchronous. The client thread
+ *     calls into the system requesting an action and providing a callback
+ *     to receive the result after which it waits up to a timeout for that
+ *     result. The system enforces security and the delegates the request
+ *     to a given view hierarchy where a message is posted (from a binder
+ *     thread) describing what to be performed by the main UI thread the
+ *     result of which it delivered via the mentioned callback. However,
+ *     the blocked client thread and the main UI thread of the target view
+ *     hierarchy can be the same thread, for example an accessibility service
+ *     and an activity run in the same process, thus they are executed on the
+ *     same main thread. In such a case the retrieval will fail since the UI
+ *     thread that has to process the message describing the work to be done
+ *     is blocked waiting for a result is has to compute! To avoid this scenario
+ *     when making a call the client also passes its process and thread ids so
+ *     the accessed view hierarchy can detect if the client making the request
+ *     is running in its main UI thread. In such a case the view hierarchy,
+ *     specifically the binder thread performing the IPC to it, does not post a
+ *     message to be run on the UI thread but passes it to the singleton
+ *     interaction client through which all interactions occur and the latter is
+ *     responsible to execute the message before starting to wait for the
+ *     asynchronous result delivered via the callback. In this case the expected
+ *     result is already received so no waiting is performed.
+ *
+ * @hide
+ */
+public final class AccessibilityInteractionClient
+        extends IAccessibilityInteractionConnectionCallback.Stub {
+
+    private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
+
+    private static final Object sStaticLock = new Object();
+
+    private static AccessibilityInteractionClient sInstance;
+
+    private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
+
+    private final Object mInstanceLock = new Object();
+
+    private int mInteractionId = -1;
+
+    private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
+
+    private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
+
+    private boolean mPerformAccessibilityActionResult;
+
+    private Message mSameThreadMessage;
+
+    private final Rect mTempBounds = new Rect();
+
+    /**
+     * @return The singleton of this class.
+     */
+    public static AccessibilityInteractionClient getInstance() {
+        synchronized (sStaticLock) {
+            if (sInstance == null) {
+                sInstance = new AccessibilityInteractionClient();
+            }
+            return sInstance;
+        }
+    }
+
+    /**
+     * Sets the message to be processed if the interacted view hierarchy
+     * and the interacting client are running in the same thread.
+     *
+     * @param message The message.
+     */
+    public void setSameThreadMessage(Message message) {
+        synchronized (mInstanceLock) {
+            mSameThreadMessage = message;
+        }
+    }
+
+    /**
+     * Finds an {@link AccessibilityNodeInfo} by accessibility id.
+     *
+     * @param connection A connection for interacting with the system.
+     * @param accessibilityWindowId A unique window id.
+     * @param accessibilityViewId A unique View accessibility id.
+     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
+     */
+    public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
+            IAccessibilityServiceConnection connection, int accessibilityWindowId,
+            int accessibilityViewId) {
+        try {
+            final int interactionId = mInteractionIdCounter.getAndIncrement();
+            final float windowScale = connection.findAccessibilityNodeInfoByAccessibilityId(
+                    accessibilityWindowId, accessibilityViewId, interactionId, this,
+                    Thread.currentThread().getId());
+            // If the scale is zero the call has failed.
+            if (windowScale > 0) {
+                handleSameThreadMessageIfNeeded();
+                AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
+                        interactionId);
+                finalizeAccessibilityNodeInfo(info, connection, windowScale);
+                return info;
+            }
+        } catch (RemoteException re) {
+            /* ignore */
+        }
+        return null;
+    }
+
+    /**
+     * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed
+     * in the currently active window and starts from the root View in the window.
+     *
+     * @param connection A connection for interacting with the system.
+     * @param id The id of the node.
+     * @return An {@link AccessibilityNodeInfo} if found, null otherwise.
+     */
+    public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(
+            IAccessibilityServiceConnection connection, int viewId) {
+        try {
+            final int interactionId = mInteractionIdCounter.getAndIncrement();
+            final float windowScale = connection.findAccessibilityNodeInfoByViewIdInActiveWindow(
+                    viewId, interactionId, this, Thread.currentThread().getId());
+            // If the scale is zero the call has failed.
+            if (windowScale > 0) {
+                handleSameThreadMessageIfNeeded();
+                AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear(
+                        interactionId);
+                finalizeAccessibilityNodeInfo(info, connection, windowScale);
+                return info;
+            }
+        } catch (RemoteException re) {
+            /* ignore */
+        }
+        return null;
+    }
+
+    /**
+     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
+     * insensitive containment. The search is performed in the currently
+     * active window and starts from the root View in the window.
+     *
+     * @param connection A connection for interacting with the system.
+     * @param text The searched text.
+     * @return A list of found {@link AccessibilityNodeInfo}s.
+     */
+    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(
+            IAccessibilityServiceConnection connection, String text) {
+        try {
+            final int interactionId = mInteractionIdCounter.getAndIncrement();
+            final float windowScale = connection.findAccessibilityNodeInfosByViewTextInActiveWindow(
+                    text, interactionId, this, Thread.currentThread().getId());
+            // If the scale is zero the call has failed.
+            if (windowScale > 0) {
+                handleSameThreadMessageIfNeeded();
+                List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
+                        interactionId);
+                finalizeAccessibilityNodeInfos(infos, connection, windowScale);
+                return infos;
+            }
+        } catch (RemoteException re) {
+            /* ignore */
+        }
+        return null;
+    }
+
+    /**
+     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
+     * insensitive containment. The search is performed in the window whose
+     * id is specified and starts from the View whose accessibility id is
+     * specified.
+     *
+     * @param connection A connection for interacting with the system.
+     * @param text The searched text.
+     * @param accessibilityWindowId A unique window id.
+     * @param accessibilityViewId A unique View accessibility id from where to start the search.
+     *        Use {@link android.view.View#NO_ID} to start from the root.
+     * @return A list of found {@link AccessibilityNodeInfo}s.
+     */
+    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(
+            IAccessibilityServiceConnection connection, String text, int accessibilityWindowId,
+            int accessibilityViewId) {
+        try {
+            final int interactionId = mInteractionIdCounter.getAndIncrement();
+            final float windowScale = connection.findAccessibilityNodeInfosByViewText(text,
+                    accessibilityWindowId, accessibilityViewId, interactionId, this,
+                    Thread.currentThread().getId());
+            // If the scale is zero the call has failed.
+            if (windowScale > 0) {
+                handleSameThreadMessageIfNeeded();
+                List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear(
+                        interactionId);
+                finalizeAccessibilityNodeInfos(infos, connection, windowScale);
+                return infos;
+            }
+        } catch (RemoteException re) {
+            /* ignore */
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
+     *
+     * @param connection A connection for interacting with the system.
+     * @param accessibilityWindowId The id of the window.
+     * @param accessibilityViewId A unique View accessibility id.
+     * @param action The action to perform.
+     * @return Whether the action was performed.
+     */
+    public boolean performAccessibilityAction(IAccessibilityServiceConnection connection,
+            int accessibilityWindowId, int accessibilityViewId, int action) {
+        try {
+            final int interactionId = mInteractionIdCounter.getAndIncrement();
+            final boolean success = connection.performAccessibilityAction(
+                    accessibilityWindowId, accessibilityViewId, action, interactionId, this,
+                    Thread.currentThread().getId());
+            if (success) {
+                handleSameThreadMessageIfNeeded();
+                return getPerformAccessibilityActionResult(interactionId);
+            }
+        } catch (RemoteException re) {
+            /* ignore */
+        }
+        return false;
+    }
+
+    /**
+     * Gets the the result of an async request that returns an {@link AccessibilityNodeInfo}.
+     *
+     * @param interactionId The interaction id to match the result with the request.
+     * @return The result {@link AccessibilityNodeInfo}.
+     */
+    private AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
+        synchronized (mInstanceLock) {
+            final boolean success = waitForResultTimedLocked(interactionId);
+            AccessibilityNodeInfo result = success ? mFindAccessibilityNodeInfoResult : null;
+            clearResultLocked();
+            return result;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
+                int interactionId) {
+        synchronized (mInstanceLock) {
+            if (interactionId > mInteractionId) {
+                mFindAccessibilityNodeInfoResult = info;
+                mInteractionId = interactionId;
+            }
+            mInstanceLock.notifyAll();
+        }
+    }
+
+    /**
+     * Gets the the result of an async request that returns {@link AccessibilityNodeInfo}s.
+     *
+     * @param interactionId The interaction id to match the result with the request.
+     * @return The result {@link AccessibilityNodeInfo}s.
+     */
+    private List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
+                int interactionId) {
+        synchronized (mInstanceLock) {
+            final boolean success = waitForResultTimedLocked(interactionId);
+            List<AccessibilityNodeInfo> result = success ? mFindAccessibilityNodeInfosResult : null;
+            clearResultLocked();
+            return result;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
+                int interactionId) {
+        synchronized (mInstanceLock) {
+            if (interactionId > mInteractionId) {
+                mFindAccessibilityNodeInfosResult = infos;
+                mInteractionId = interactionId;
+            }
+            mInstanceLock.notifyAll();
+        }
+    }
+
+    /**
+     * Gets the result of a request to perform an accessibility action.
+     *
+     * @param interactionId The interaction id to match the result with the request.
+     * @return Whether the action was performed.
+     */
+    private boolean getPerformAccessibilityActionResult(int interactionId) {
+        synchronized (mInstanceLock) {
+            final boolean success = waitForResultTimedLocked(interactionId);
+            final boolean result = success ? mPerformAccessibilityActionResult : false;
+            clearResultLocked();
+            return result;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
+        synchronized (mInstanceLock) {
+            if (interactionId > mInteractionId) {
+                mPerformAccessibilityActionResult = succeeded;
+                mInteractionId = interactionId;
+            }
+            mInstanceLock.notifyAll();
+        }
+    }
+
+    /**
+     * Clears the result state.
+     */
+    private void clearResultLocked() {
+        mInteractionId = -1;
+        mFindAccessibilityNodeInfoResult = null;
+        mFindAccessibilityNodeInfosResult = null;
+        mPerformAccessibilityActionResult = false;
+    }
+
+    /**
+     * Waits up to a given bound for a result of a request and returns it.
+     *
+     * @param interactionId The interaction id to match the result with the request.
+     * @return Whether the result was received.
+     */
+    private boolean waitForResultTimedLocked(int interactionId) {
+        long waitTimeMillis = TIMEOUT_INTERACTION_MILLIS;
+        final long startTimeMillis = SystemClock.uptimeMillis();
+        while (true) {
+            try {
+                if (mInteractionId == interactionId) {
+                    return true;
+                }
+                if (mInteractionId > interactionId) {
+                    return false;
+                }
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
+                if (waitTimeMillis <= 0) {
+                    return false;
+                }
+                mInstanceLock.wait(waitTimeMillis);
+            } catch (InterruptedException ie) {
+                /* ignore */
+            }
+        }
+    }
+
+    /**
+     * Applies compatibility scale to the info bounds if it is not equal to one.
+     *
+     * @param info The info whose bounds to scale.
+     * @param scale The scale to apply.
+     */
+    private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info, float scale) {
+        if (scale == 1.0f) {
+            return;
+        }
+        Rect bounds = mTempBounds;
+        info.getBoundsInParent(bounds);
+        bounds.scale(scale);
+        info.setBoundsInParent(bounds);
+
+        info.getBoundsInScreen(bounds);
+        bounds.scale(scale);
+        info.setBoundsInScreen(bounds);
+    }
+
+    /**
+     * Handles the message stored if the interacted and interacting
+     * threads are the same otherwise this is a NOP.
+     */
+    private void handleSameThreadMessageIfNeeded() {
+        Message sameProcessMessage = getSameProcessMessageAndClear();
+        if (sameProcessMessage != null) {
+            sameProcessMessage.getTarget().handleMessage(sameProcessMessage);
+        }
+    }
+
+    /**
+     * Finalize an {@link AccessibilityNodeInfo} before passing it to the client.
+     *
+     * @param info The info.
+     * @param connection The current connection to the system.
+     * @param windowScale The source window compatibility scale.
+     */
+    private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info,
+            IAccessibilityServiceConnection connection, float windowScale) {
+        if (info != null) {
+            applyCompatibilityScaleIfNeeded(info, windowScale);
+            info.setConnection(connection);
+            info.setSealed(true);
+        }
+    }
+
+    /**
+     * Finalize {@link AccessibilityNodeInfo}s before passing them to the client.
+     *
+     * @param infos The {@link AccessibilityNodeInfo}s.
+     * @param connection The current connection to the system.
+     * @param windowScale The source window compatibility scale.
+     */
+    private void finalizeAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos,
+            IAccessibilityServiceConnection connection, float windowScale) {
+        if (infos != null) {
+            final int infosCount = infos.size();
+            for (int i = 0; i < infosCount; i++) {
+                AccessibilityNodeInfo info = infos.get(i);
+                finalizeAccessibilityNodeInfo(info, connection, windowScale);
+            }
+        }
+    }
+
+    /**
+     * Gets the message stored if the interacted and interacting
+     * threads are the same.
+     *
+     * @return The message.
+     */
+    private Message getSameProcessMessageAndClear() {
+        synchronized (mInstanceLock) {
+            Message result = mSameThreadMessage;
+            mSameThreadMessage = null;
+            return result;
+        }
+    }
+}
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 6469b35..f0e8005 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -20,7 +20,6 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.os.RemoteException;
 import android.text.TextUtils;
 import android.util.SparseIntArray;
 import android.view.View;
@@ -181,13 +180,9 @@
         if (!canPerformRequestOverConnection(childAccessibilityViewId)) {
             return null;
         }
-        try {
-            return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
-                    childAccessibilityViewId);
-        } catch (RemoteException re) {
-             /* ignore*/
-        }
-        return null;
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.findAccessibilityNodeInfoByAccessibilityId(mConnection,
+                mAccessibilityWindowId, childAccessibilityViewId);
     }
 
     /**
@@ -257,13 +252,9 @@
         if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
             return false;
         }
-        try {
-            return mConnection.performAccessibilityAction(mAccessibilityWindowId,
-                    mAccessibilityViewId, action);
-        } catch (RemoteException e) {
-            /* ignore */
-        }
-        return false;
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.performAccessibilityAction(mConnection, mAccessibilityWindowId,
+                mAccessibilityViewId, action);
     }
 
     /**
@@ -284,13 +275,9 @@
         if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
             return Collections.emptyList();
         }
-        try {
-            return mConnection.findAccessibilityNodeInfosByViewText(text, mAccessibilityWindowId,
-                    mAccessibilityViewId);
-        } catch (RemoteException e) {
-            /* ignore */
-        }
-        return Collections.emptyList();
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.findAccessibilityNodeInfosByViewText(mConnection, text,
+                mAccessibilityWindowId, mAccessibilityViewId);
     }
 
     /**
@@ -308,13 +295,9 @@
         if (!canPerformRequestOverConnection(mAccessibilityViewId)) {
             return null;
         }
-        try {
-            return mConnection.findAccessibilityNodeInfoByAccessibilityId(
-                    mAccessibilityWindowId, mParentAccessibilityViewId);
-        } catch (RemoteException e) {
-            /* ignore */
-        }
-        return null;
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.findAccessibilityNodeInfoByAccessibilityId(mConnection,
+                mAccessibilityWindowId, mParentAccessibilityViewId);
     }
 
     /**
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 210106f..afd7473 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -18,7 +18,6 @@
 
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.os.Parcelable;
-import android.os.RemoteException;
 import android.view.View;
 
 import java.util.ArrayList;
@@ -127,13 +126,9 @@
         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;
+        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
+        return client.findAccessibilityNodeInfoByAccessibilityId(mConnection, mSourceWindowId,
+                mSourceViewId);
     }
 
     /**
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionCallback.aidl
new file mode 100644
index 0000000..eeab4f2
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionCallback.aidl
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+import java.util.List;
+
+/**
+ * Callback for specifying the result for an asynchronous request made
+ * via calling a method on IAccessibilityInteractionCallback.
+ *
+ * @hide
+ */
+oneway interface IAccessibilityInteractionCallback {
+
+    /**
+     * Sets the result of an async request that returns an {@link AccessibilityNodeInfo}.
+     *
+     * @param infos The result {@link AccessibilityNodeInfo}.
+     * @param interactionId The interaction id to match the result with the request.
+     */
+    void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId);
+
+    /**
+     * Sets the result of an async request that returns {@link AccessibilityNodeInfo}s.
+     *
+     * @param infos The result {@link AccessibilityNodeInfo}s.
+     * @param interactionId The interaction id to match the result with the request.
+     */
+    void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos,
+        int interactionId);
+
+    /**
+     * Sets the result of a request to perform an accessibility action.
+     *
+     * @param Whether the action was performed.
+     * @param interactionId The interaction id to match the result with the request.
+     */
+    void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
index d35186b..535d594 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -28,14 +28,18 @@
 oneway interface IAccessibilityInteractionConnection {
 
     void findAccessibilityNodeInfoByAccessibilityId(int accessibilityViewId, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback);
+        IAccessibilityInteractionConnectionCallback callback,
+        int interrogatingPid, long interrogatingTid);
 
     void findAccessibilityNodeInfoByViewId(int id, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback);
+        IAccessibilityInteractionConnectionCallback callback,
+        int interrogatingPid, long interrogatingTid);
 
     void findAccessibilityNodeInfosByViewText(String text, int accessibilityViewId,
-        int interactionId, IAccessibilityInteractionConnectionCallback callback);
+        int interactionId, IAccessibilityInteractionConnectionCallback callback,
+        int interrogatingPid, long interrogatingTid);
 
     void performAccessibilityAction(int accessibilityId, int action, int interactionId,
-        IAccessibilityInteractionConnectionCallback callback);
+        IAccessibilityInteractionConnectionCallback callback, int interrogatingPid,
+        long interrogatingTid);
 }
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
index 9c5e8dc..c1a3ab7 100644
--- a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -27,10 +27,28 @@
  */
 oneway interface IAccessibilityInteractionConnectionCallback {
 
+    /**
+     * Sets the result of an async request that returns an {@link AccessibilityNodeInfo}.
+     *
+     * @param infos The result {@link AccessibilityNodeInfo}.
+     * @param interactionId The interaction id to match the result with the request.
+     */
     void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId);
 
+    /**
+     * Sets the result of an async request that returns {@link AccessibilityNodeInfo}s.
+     *
+     * @param infos The result {@link AccessibilityNodeInfo}s.
+     * @param interactionId The interaction id to match the result with the request.
+     */
     void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos,
         int interactionId);
 
+    /**
+     * Sets the result of a request to perform an accessibility action.
+     *
+     * @param Whether the action was performed.
+     * @param interactionId The interaction id to match the result with the request.
+     */
     void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
 }
diff --git a/core/tests/coretests/res/layout/interrogation_activity.xml b/core/tests/coretests/res/layout/interrogation_activity.xml
index 28d965b..44ed75c 100644
--- a/core/tests/coretests/res/layout/interrogation_activity.xml
+++ b/core/tests/coretests/res/layout/interrogation_activity.xml
@@ -30,20 +30,20 @@
         >
         <Button
             android:id="@+id/button1"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="160px"
+            android:layout_height="100px"
             android:text="@string/button1"
         />
         <Button
             android:id="@+id/button2"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="160px"
+            android:layout_height="100px"
             android:text="@string/button2"
         />
         <Button
             android:id="@+id/button3"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="160px"
+            android:layout_height="100px"
             android:text="@string/button3"
         />
     </LinearLayout>
@@ -55,20 +55,20 @@
         >
         <Button
             android:id="@+id/button4"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="160px"
+            android:layout_height="100px"
             android:text="@string/button4"
         />
         <Button
             android:id="@+id/button5"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="160px"
+            android:layout_height="100px"
             android:text="@string/button5"
         />
         <Button
             android:id="@+id/button6"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="160px"
+            android:layout_height="100px"
             android:text="@string/button6"
         />
     </LinearLayout>
@@ -80,20 +80,20 @@
         >
         <Button
             android:id="@+id/button7"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="160px"
+            android:layout_height="100px"
             android:text="@string/button7"
         />
         <Button
             android:id="@+id/button8"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="160px"
+            android:layout_height="100px"
             android:text="@string/button8"
         />
         <Button
             android:id="@+id/button9"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
+            android:layout_width="160px"
+            android:layout_height="100px"
             android:text="@string/button9"
         />
     </LinearLayout>
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
index 99d534c..a542a1b 100644
--- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
@@ -31,6 +31,7 @@
 import android.test.suitebuilder.annotation.LargeTest;
 import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.IAccessibilityManager;
@@ -81,8 +82,8 @@
             // bring up the activity
             getActivity();
 
-            AccessibilityNodeInfo button =
-                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
             assertNotNull(button);
             assertEquals(0, button.getChildCount());
 
@@ -91,8 +92,8 @@
             button.getBoundsInParent(bounds);
             assertEquals(0, bounds.left);
             assertEquals(0, bounds.top);
-            assertEquals(73, bounds.right);
-            assertEquals(48, bounds.bottom);
+            assertEquals(160, bounds.right);
+            assertEquals(100, bounds.bottom);
 
             // char sequence attributes
             assertEquals("com.android.frameworks.coretests", button.getPackageName());
@@ -133,8 +134,8 @@
             getActivity();
 
             // find a view by text
-            List<AccessibilityNodeInfo> buttons =
-                getConnection().findAccessibilityNodeInfosByViewTextInActiveWindow("butto");
+            List<AccessibilityNodeInfo> buttons =  AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfosByViewTextInActiveWindow(getConnection(), "butto");
             assertEquals(9, buttons.size());
         } finally {
             afterClassIfNeeded();
@@ -170,8 +171,8 @@
             classNameAndTextList.add("android.widget.ButtonButton8");
             classNameAndTextList.add("android.widget.ButtonButton9");
 
-            AccessibilityNodeInfo root =
-                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.root);
+            AccessibilityNodeInfo root = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.root);
             assertNotNull("We must find the existing root.", root);
 
             Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
@@ -214,15 +215,16 @@
             getActivity();
 
             // find a view and make sure it is not focused
-            AccessibilityNodeInfo button =
-                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), 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().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            button =  AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
             assertTrue(button.isFocused());
         } finally {
             afterClassIfNeeded();
@@ -242,22 +244,24 @@
             getActivity();
 
             // find a view and make sure it is not focused
-            AccessibilityNodeInfo button =
-                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), 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().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), 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().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
             assertFalse(button.isFocused());
         } finally {
             afterClassIfNeeded();
@@ -278,15 +282,16 @@
             getActivity();
 
             // find a view and make sure it is not selected
-            AccessibilityNodeInfo button =
-                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), 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().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
             assertTrue(button.isSelected());
         } finally {
             afterClassIfNeeded();
@@ -306,22 +311,24 @@
             getActivity();
 
             // find a view and make sure it is not selected
-            AccessibilityNodeInfo button =
-                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), 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().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), 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().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            button =  AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
             assertFalse(button.isSelected());
         } finally {
             afterClassIfNeeded();
@@ -342,8 +349,8 @@
             getActivity();
 
             // find a view and make sure it is not focused
-            AccessibilityNodeInfo button =
-                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
             assertFalse(button.isSelected());
 
             // focus the view
@@ -406,8 +413,8 @@
             getActivity();
 
             // find a view and make sure it is not focused
-            AccessibilityNodeInfo button =
-                getConnection().findAccessibilityNodeInfoByViewIdInActiveWindow(R.id.button5);
+            AccessibilityNodeInfo button = AccessibilityInteractionClient.getInstance()
+                .findAccessibilityNodeInfoByViewIdInActiveWindow(getConnection(), R.id.button5);
             AccessibilityNodeInfo parent = button.getParent();
             final int childCount = parent.getChildCount();
             for (int i = 0; i < childCount; i++) {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index af01c5b..09ddc2f 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -40,7 +40,6 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.TextUtils.SimpleStringSplitter;
@@ -72,7 +71,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * This class is instantiated by the system as a system level service and can be
@@ -871,10 +869,6 @@
 
         boolean mIsAutomation;
 
-        final Callback mCallback = new Callback();
-
-        final AtomicInteger mInteractionIdCounter = new AtomicInteger();
-
         final Rect mTempBounds = new Rect();
 
         // the events pending events to be dispatched to this service
@@ -974,14 +968,16 @@
             }
         }
 
-        public AccessibilityNodeInfo findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId)
+        public float findAccessibilityNodeInfoByViewIdInActiveWindow(int viewId,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback,
+                long interrogatingTid)
                 throws RemoteException {
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
                 mSecurityPolicy.enforceCanRetrieveWindowContent(this);
                 final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
                 if (!permissionGranted) {
-                    return null;
+                    return 0;
                 } else {
                     connection = getConnectionToRetrievalAllowingWindowLocked();
                     if (connection == null) {
@@ -989,22 +985,15 @@
                             Slog.e(LOG_TAG, "No interaction connection to a retrieve "
                                     + "allowing window.");
                         }
-                        return null;
+                        return 0;
                     }
                 }
             }
+            final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
-                final int interactionId = mInteractionIdCounter.getAndIncrement();
-                connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, mCallback);
-                AccessibilityNodeInfo info = mCallback.getFindAccessibilityNodeInfoResultAndClear(
-                        interactionId);
-                if (info != null) {
-                    applyCompatibilityScaleIfNeeded(info);
-                    info.setConnection(this);
-                    info.setSealed(true);
-                }
-                return info;
+                connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, callback,
+                        interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Error finding node.");
@@ -1012,51 +1001,44 @@
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
             }
-            return null;
+            return getCompatibilityScale(mSecurityPolicy.getRetrievalAllowingWindowLocked());
         }
 
-        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewTextInActiveWindow(
-                String text) throws RemoteException {
+        public float findAccessibilityNodeInfosByViewTextInActiveWindow(
+                String text, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, long threadId)
+                throws RemoteException {
             return findAccessibilityNodeInfosByViewText(text,
-                    mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID);
+                    mSecurityPolicy.mRetrievalAlowingWindowId, View.NO_ID, interactionId, callback,
+                    threadId);
         }
 
-        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text,
-                int accessibilityWindowId, int accessibilityViewId) throws RemoteException {
+        public float findAccessibilityNodeInfosByViewText(String text,
+                int accessibilityWindowId, int accessibilityViewId, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
+                throws RemoteException {
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
                 mSecurityPolicy.enforceCanRetrieveWindowContent(this);
                 final boolean permissionGranted =
                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
                 if (!permissionGranted) {
-                    return null;
+                    return 0;
                 } else {
                     connection = getConnectionToRetrievalAllowingWindowLocked();
                     if (connection == null) {
                         if (DEBUG) {
                             Slog.e(LOG_TAG, "No interaction connection to focused window.");
                         }
-                        return null;
+                        return 0;
                     }
                 }
             }
+            final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
-                final int interactionId = mInteractionIdCounter.getAndIncrement();
                 connection.findAccessibilityNodeInfosByViewText(text, accessibilityViewId,
-                        interactionId, mCallback);
-                List<AccessibilityNodeInfo> infos =
-                    mCallback.getFindAccessibilityNodeInfosResultAndClear(interactionId);
-                if (infos != null) {
-                    final int infoCount = infos.size();
-                    for (int i = 0; i < infoCount; i++) {
-                        AccessibilityNodeInfo info = infos.get(i);
-                        applyCompatibilityScaleIfNeeded(info);
-                        info.setConnection(this);
-                        info.setSealed(true);
-                    }
-                }
-                return infos;
+                        interactionId, callback, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Error finding node.");
@@ -1064,18 +1046,20 @@
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
             }
-            return null;
+            return getCompatibilityScale(accessibilityWindowId);
         }
 
-        public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
-                int accessibilityWindowId, int accessibilityViewId) throws RemoteException {
+        public float findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+                int accessibilityViewId, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
+                throws RemoteException {
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
                 mSecurityPolicy.enforceCanRetrieveWindowContent(this);
                 final boolean permissionGranted =
                     mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
                 if (!permissionGranted) {
-                    return null;
+                    return 0;
                 } else {
                     connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
                     if (connection == null) {
@@ -1083,23 +1067,15 @@
                             Slog.e(LOG_TAG, "No interaction connection to window: "
                                     + accessibilityWindowId);
                         }
-                        return null;
+                        return 0;
                     }
                 }
             }
+            final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
-                final int interactionId = mInteractionIdCounter.getAndIncrement();
                 connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId,
-                        interactionId, mCallback);
-                AccessibilityNodeInfo info =
-                     mCallback.getFindAccessibilityNodeInfoResultAndClear(interactionId);
-                if (info != null) {
-                    applyCompatibilityScaleIfNeeded(info);
-                    info.setConnection(this);
-                    info.setSealed(true);
-                }
-                return info;
+                        interactionId, callback, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
@@ -1108,11 +1084,12 @@
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
             }
-            return null;
+            return getCompatibilityScale(accessibilityWindowId);
         }
 
         public boolean performAccessibilityAction(int accessibilityWindowId,
-                int accessibilityViewId, int action) {
+                int accessibilityViewId, int action, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback, long interrogatingTid) {
             IAccessibilityInteractionConnection connection = null;
             synchronized (mLock) {
                 final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
@@ -1130,12 +1107,11 @@
                     }
                 }
             }
+            final int interrogatingPid = Binder.getCallingPid();
             final long identityToken = Binder.clearCallingIdentity();
             try {
-                final int interactionId = mInteractionIdCounter.getAndIncrement();
                 connection.performAccessibilityAction(accessibilityViewId, action, interactionId,
-                        mCallback);
-                return mCallback.getPerformAccessibilityActionResult(interactionId);
+                        callback, interrogatingPid, interrogatingTid);
             } catch (RemoteException re) {
                 if (DEBUG) {
                     Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
@@ -1144,7 +1120,7 @@
             } finally {
                 Binder.restoreCallingIdentity(identityToken);
             }
-            return false;
+            return true;
         }
 
         public void onServiceDisconnected(ComponentName componentName) {
@@ -1173,22 +1149,9 @@
             return mWindowIdToInteractionConnectionMap.get(windowId);
         }
 
-        private void applyCompatibilityScaleIfNeeded(AccessibilityNodeInfo info) {
-            IBinder windowToken = mWindowIdToWindowTokenMap.get(info.getWindowId());
-            final float scale = mWindowManagerService.getWindowCompatibilityScale(windowToken);
-
-            if (scale == 1.0f) {
-                return;
-            }
-
-            Rect bounds = mTempBounds;
-            info.getBoundsInParent(bounds);
-            bounds.scale(scale);
-            info.setBoundsInParent(bounds);
-
-            info.getBoundsInScreen(bounds);
-            bounds.scale(scale);
-            info.setBoundsInScreen(bounds);
+        private float getCompatibilityScale(int windowId) {
+            IBinder windowToken = mWindowIdToWindowTokenMap.get(windowId);
+            return mWindowManagerService.getWindowCompatibilityScale(windowToken);
         }
     }
 
@@ -1275,99 +1238,4 @@
             }
         }
     }
-
-    final class Callback extends IAccessibilityInteractionConnectionCallback.Stub {
-        private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
-
-        private int mInteractionId = -1;
-        private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
-        private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
-        private boolean mPerformAccessibilityActionResult;
-
-        public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
-                int interactionId) {
-            synchronized (mLock) {
-                if (interactionId > mInteractionId) {
-                    mFindAccessibilityNodeInfoResult = info;
-                    mInteractionId = interactionId;
-                }
-                mLock.notifyAll();
-            }
-        }
-
-        public AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
-            synchronized (mLock) {
-                waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
-                AccessibilityNodeInfo result = mFindAccessibilityNodeInfoResult;
-                clearLocked();
-                return result;
-            }
-        }
-
-        public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
-                int interactionId) {
-            synchronized (mLock) {
-                if (interactionId > mInteractionId) {
-                    mFindAccessibilityNodeInfosResult = infos;
-                    mInteractionId = interactionId;
-                }
-                mLock.notifyAll();
-            }
-        }
-
-        public List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
-                int interactionId) {
-            synchronized (mLock) {
-                waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
-                List<AccessibilityNodeInfo> result = mFindAccessibilityNodeInfosResult;
-                clearLocked();
-                return result;
-            }
-        }
-
-        public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
-            synchronized (mLock) {
-                if (interactionId > mInteractionId) {
-                    mPerformAccessibilityActionResult = succeeded;
-                    mInteractionId = interactionId;
-                }
-                mLock.notifyAll();
-            }
-        }
-
-        public boolean getPerformAccessibilityActionResult(int interactionId) {
-            synchronized (mLock) {
-                waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
-                final boolean result = mPerformAccessibilityActionResult;
-                clearLocked();
-                return result;
-            }
-        }
-
-        public void clearLocked() {
-            mInteractionId = -1;
-            mFindAccessibilityNodeInfoResult = null;
-            mFindAccessibilityNodeInfosResult = null;
-            mPerformAccessibilityActionResult = false;
-        }
-
-        private void waitForResultTimedLocked(long waitTimeMillis, int interactionId) {
-            final long startTimeMillis = SystemClock.uptimeMillis();
-            while (true) {
-                try {
-                    if (mInteractionId == interactionId) {
-                        return;
-                    }
-                    final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
-                    waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
-                    if (waitTimeMillis <= 0) {
-                        return;
-                    }
-                    mLock.wait(waitTimeMillis);
-                } catch (InterruptedException ie) {
-                    /* ignore */
-                }
-            }
-        }
-    }
 }