Adding CTS tests for the new accessibility APIs in ICS.

1. Added tests for the new APIs introduced in ICS.

2. Fixed a failing CTS test.

NOTE: This change does *not* affect the system image, rather CTS.

Change-Id: I15eabeb1f60404707f99ac2048455463fce4c0c8
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 29164bc..81758f6 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -28,6 +28,8 @@
       <intent-filter>
         <action android:name="android.accessibilityservice.AccessibilityService"/>
       </intent-filter>
+      <meta-data android:name="android.accessibilityservice"
+                android:resource="@xml/accessibilityservice" />
     </service>
 
     <service android:name=".DelegatingAccessibilityService$DelegatingConnectionService"
diff --git a/tests/accessibilityservice/res/xml/accessibilityservice.xml b/tests/accessibilityservice/res/xml/accessibilityservice.xml
new file mode 100644
index 0000000..31a3f71
--- /dev/null
+++ b/tests/accessibilityservice/res/xml/accessibilityservice.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accessibilityEventTypes="typeAllMask"
+    android:accessibilityFeedbackType="feedbackGeneric"
+    android:accessibilityFlags="flagDefault"
+    android:canRetrieveWindowContent="true" />
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/IAccessibilityServiceDelegate.aidl b/tests/accessibilityservice/src/android/accessibilityservice/IAccessibilityServiceDelegate.aidl
index b5ebf19..868bd72 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/IAccessibilityServiceDelegate.aidl
+++ b/tests/accessibilityservice/src/android/accessibilityservice/IAccessibilityServiceDelegate.aidl
@@ -20,7 +20,7 @@
 /**
  * Interface for interacting with the accessibility service mock.
  */
-interface IAccessibilityServiceDelegate {
+oneway interface IAccessibilityServiceDelegate {
 
     /**
      * Delegate an {@link android.view.accessibility.AccessibilityEvent}.
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/delegate/DelegatingAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/delegate/DelegatingAccessibilityService.java
index 37a9c54..5680b7c 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/delegate/DelegatingAccessibilityService.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/delegate/DelegatingAccessibilityService.java
@@ -17,7 +17,6 @@
 package android.accessibilityservice.delegate;
 
 import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceDelegate;
 import android.accessibilityservice.IAccessibilityServiceDelegateConnection;
 import android.app.Service;
@@ -64,14 +63,6 @@
 
     @Override
     protected void onServiceConnected() {
-        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
-        info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
-        info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
-        info.packageNames = new String[] {
-            "com.android.cts.accessibilityservice"
-        };
-        setServiceInfo(info);
-
         // the service is ready to be used only
         // after the system has bound to it
         sServiceDelegate = this;
diff --git a/tests/tests/accessibilityservice/AndroidManifest.xml b/tests/tests/accessibilityservice/AndroidManifest.xml
index 70e1092..c75ffb2 100644
--- a/tests/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/tests/accessibilityservice/AndroidManifest.xml
@@ -26,6 +26,9 @@
       <activity android:label="@string/accessibility_end_to_end_test_activity"
               android:name="android.accessibilityservice.cts.AccessibilityEndToEndTestActivity"/>
 
+      <activity android:label="@string/accessibility_query_window_test_activity"
+              android:name="android.accessibilityservice.cts.AccessibilityWindowQueryActivity"/>
+
   </application>
 
   <instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/tests/tests/accessibilityservice/res/layout/query_window_test.xml b/tests/tests/accessibilityservice/res/layout/query_window_test.xml
new file mode 100644
index 0000000..4b32eb1
--- /dev/null
+++ b/tests/tests/accessibilityservice/res/layout/query_window_test.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    >
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        >
+        <Button
+            android:id="@+id/button1"
+            android:layout_width="160px"
+            android:layout_height="100px"
+            android:text="@string/button1"
+        />
+        <Button
+            android:id="@+id/button2"
+            android:layout_width="160px"
+            android:layout_height="100px"
+            android:text="@string/button2"
+        />
+        <Button
+            android:id="@+id/button3"
+            android:layout_width="160px"
+            android:layout_height="100px"
+            android:text="@string/button3"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        >
+        <Button
+            android:id="@+id/button4"
+            android:layout_width="160px"
+            android:layout_height="100px"
+            android:text="@string/button4"
+        />
+        <Button
+            android:id="@+id/button5"
+            android:layout_width="160px"
+            android:layout_height="100px"
+            android:text="@string/button5"
+        />
+        <Button
+            android:id="@+id/button6"
+            android:layout_width="160px"
+            android:layout_height="100px"
+            android:text="@string/button6"
+            android:contentDescription="@string/contentDescription"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        >
+        <Button
+            android:id="@+id/button7"
+            android:layout_width="160px"
+            android:layout_height="100px"
+            android:text="@string/button7"
+        />
+        <Button
+            android:id="@+id/button8"
+            android:layout_width="160px"
+            android:layout_height="100px"
+            android:text="@string/button8"
+        />
+        <Button
+            android:id="@+id/button9"
+            android:layout_width="160px"
+            android:layout_height="100px"
+            android:text="@string/button9"
+        />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/tests/accessibilityservice/res/values/strings.xml b/tests/tests/accessibilityservice/res/values/strings.xml
index e86d3cc..7b3cd0f 100644
--- a/tests/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/tests/accessibilityservice/res/values/strings.xml
@@ -44,4 +44,38 @@
     <!-- String notification message -->
     <string name="notification_message">Notification message</string>
 
+
+    <!-- String title of the accessibility query window test activity -->
+    <string name="accessibility_query_window_test_activity">Query window test</string>
+
+    <!-- String Button1 text -->
+    <string name="button1">Button1</string>
+
+    <!-- String Button2 text -->
+    <string name="button2">Button2</string>
+
+    <!-- String Button3 text -->
+    <string name="button3">Button3</string>
+
+    <!-- String Button4 text -->
+    <string name="button4">Button4</string>
+
+    <!-- String Button5 text -->
+    <string name="button5">Button5</string>
+
+    <!-- String Button6 text -->
+    <string name="button6">Button6</string>
+
+    <!-- String with content description for Button6 -->
+    <string name="contentDescription">contentDescription</string>
+
+    <!-- String Button7 text -->
+    <string name="button7">Button7</string>
+
+    <!-- String Button8 text -->
+    <string name="button8">Button8</string>
+
+    <!-- String Button9 text -->
+    <string name="button9">Button9</string>
+
 </resources>
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/IAccessibilityServiceDelegate.aidl b/tests/tests/accessibilityservice/src/android/accessibilityservice/IAccessibilityServiceDelegate.aidl
index b5ebf19..868bd72 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/IAccessibilityServiceDelegate.aidl
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/IAccessibilityServiceDelegate.aidl
@@ -20,7 +20,7 @@
 /**
  * Interface for interacting with the accessibility service mock.
  */
-interface IAccessibilityServiceDelegate {
+oneway interface IAccessibilityServiceDelegate {
 
     /**
      * Delegate an {@link android.view.accessibility.AccessibilityEvent}.
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityDelegateHelper.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityDelegateHelper.java
new file mode 100644
index 0000000..1e7c625
--- /dev/null
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityDelegateHelper.java
@@ -0,0 +1,212 @@
+/*
+ * 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.accessibilityservice.cts;
+
+import static junit.framework.Assert.fail;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceDelegate;
+import android.accessibilityservice.IAccessibilityServiceDelegateConnection;
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.List;
+
+/**
+ * This class is a helper for implementing delegation of accessibility events.
+ * This is required because changing accessibility settings can be performed
+ * either via writing to the secure settings, which a CTS test cannot do, or
+ * by manual interaction from the settings application. However, manually
+ * enabling the testing accessibility service does not work because the test
+ * runner restarts the package before running the tests, thus breaking the
+ * bond between the system and the manually enabled testing service. So,
+ * we have a delegating service that is manually enabled which services as a
+ * proxy to deliver accessibility events to the testing service.
+ */
+class AccessibilityDelegateHelper implements ServiceConnection {
+
+   /**
+    * Timeout required for pending Binder calls or event processing to
+    * complete.
+    */
+    public static final long TIMEOUT_ASYNC_PROCESSING = 500;
+
+    /**
+     * The package of the accessibility service mock interface.
+     */
+    private static final String DELEGATING_SERVICE_PACKAGE =
+        "android.accessibilityservice.delegate";
+
+    /**
+     * The package of the delegating accessibility service interface.
+     */
+    private static final String DELEGATING_SERVICE_CLASS_NAME =
+        "android.accessibilityservice.delegate.DelegatingAccessibilityService";
+
+    /**
+     * The package of the delegating accessibility service connection interface.
+     */
+    private static final String DELEGATING_SERVICE_CONNECTION_CLASS_NAME =
+        "android.accessibilityservice.delegate."
+            + "DelegatingAccessibilityService$DelegatingConnectionService";
+
+    /**
+     * The client accessibility service to which to delegate.
+     */
+    private final AccessibilityService mAccessibilityService;
+
+    /**
+     * Lock for synchronization.
+     */
+    private final Object mLock = new Object();
+
+    /**
+     * Whether this delegate is initialized.
+     */
+    private boolean mInitialized;
+
+    /**
+     * Creates a new instance.
+     *
+     * @param service The service to which to delegate.
+     */
+    public AccessibilityDelegateHelper(AccessibilityService service) {
+        mAccessibilityService = service;
+    }
+
+    /**
+     * Ensures the required setup for the test performed and that it is bound to the
+     * DelegatingAccessibilityService which runs in another process. The setup is
+     * enabling accessibility and installing and enabling the delegating accessibility
+     * service this test binds to.
+     * </p>
+     * Note: Please look at the class description for information why such an
+     *       approach is taken.
+     */
+    public void bindToDelegatingAccessibilityService(Context context) {
+        // check if accessibility is enabled
+        AccessibilityManager accessibilityManager = (AccessibilityManager) context
+                .getSystemService(Service.ACCESSIBILITY_SERVICE);
+
+        if (!accessibilityManager.isEnabled()) {
+            throw new IllegalStateException("Delegating service not enabled. "
+                    + "(Settings -> Accessibility -> Delegating Accessibility Service)");
+        }
+
+        // check if the delegating service is running
+        List<AccessibilityServiceInfo> enabledServices =
+            accessibilityManager.getEnabledAccessibilityServiceList(
+                    AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+        boolean delegatingServiceRunning = false;
+        for (AccessibilityServiceInfo enabledService : enabledServices) {
+            ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo;
+            if (DELEGATING_SERVICE_PACKAGE.equals(serviceInfo.packageName)
+                    && DELEGATING_SERVICE_CLASS_NAME.equals(serviceInfo.name)) {
+                delegatingServiceRunning = true;
+                break;
+            }
+        }
+
+        if (!delegatingServiceRunning) {
+            // delegating service not running, so check if it is installed at all
+            try {
+                PackageManager packageManager = context.getPackageManager();
+                packageManager.getServiceInfo(new ComponentName(DELEGATING_SERVICE_PACKAGE,
+                        DELEGATING_SERVICE_CLASS_NAME), 0);
+            } catch (NameNotFoundException nnfe) {
+                throw new IllegalStateException("CtsDelegatingAccessibilityService.apk" +
+                        " not installed.");
+            }
+
+            throw new IllegalStateException("Delegating Accessibility Service not running."
+                     + "(Settings -> Accessibility -> Delegating Accessibility Service)");
+        }
+
+        Intent intent = new Intent().setClassName(DELEGATING_SERVICE_PACKAGE,
+                DELEGATING_SERVICE_CONNECTION_CLASS_NAME);
+        context.bindService(intent, this, Context.BIND_AUTO_CREATE);
+
+        final long beginTime = SystemClock.uptimeMillis();
+        synchronized (mLock) {
+            while (true) {
+                if (mInitialized) {
+                    return;
+                }
+                final long elapsedTime = (SystemClock.uptimeMillis() - beginTime);
+                final long remainingTime = TIMEOUT_ASYNC_PROCESSING - elapsedTime;
+                if (remainingTime <= 0) {
+                    if (!mInitialized) {
+                        throw new IllegalStateException("Cound not connect to the delegating"
+                                + " accessibility service");
+                    }
+                    return;
+                }
+                try {
+                    mLock.wait(remainingTime);
+                } catch (InterruptedException ie) {
+                    /* ignore */
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc ServiceConnection#onServiceConnected(ComponentName,IBinder)}
+     */
+    public void onServiceConnected(ComponentName name, IBinder service) {
+        IAccessibilityServiceDelegateConnection connection =
+            IAccessibilityServiceDelegateConnection.Stub.asInterface(service);
+        try {
+            connection.setAccessibilityServiceDelegate(new IAccessibilityServiceDelegate.Stub() {
+                @Override
+                public void onAccessibilityEvent(AccessibilityEvent event) {
+                    mAccessibilityService.onAccessibilityEvent(event);
+                }
+                @Override
+                public void onInterrupt() {
+                    mAccessibilityService.onInterrupt();
+                }
+            });
+            mInitialized = true;
+            synchronized (mLock) {
+                mLock.notifyAll();
+            }
+        } catch (RemoteException re) {
+            fail("Could not set delegate to the delegating service.");
+        }
+    }
+
+    /**
+     * {@inheritDoc ServiceConnection#onServiceDisconnected(ComponentName)}
+     */
+    public void onServiceDisconnected(ComponentName name) {
+        mInitialized = false;
+        /* do nothing */
+    }
+}
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index af6d37d..5a565ec 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -16,40 +16,25 @@
 
 package android.accessibilityservice.cts;
 
-import com.android.cts.accessibilityservice.R;
-
 import android.accessibilityservice.AccessibilityService;
-import android.accessibilityservice.IAccessibilityServiceDelegate;
-import android.accessibilityservice.IAccessibilityServiceDelegateConnection;
 import android.app.Activity;
-import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
-import android.app.ActivityManager.RunningServiceInfo;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Build;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
 import android.os.SystemClock;
 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.widget.Button;
 import android.widget.EditText;
 import android.widget.ListView;
 
+import com.android.cts.accessibilityservice.R;
+
 import junit.framework.TestCase;
 
 import java.util.Iterator;
@@ -62,18 +47,22 @@
  * creating an {@link Activity} and poking around so {@link AccessibilityEvent}s
  * are generated and their correct dispatch verified.
  * <p>
- * Note: The end-to-end test is composed of two APKs, one with a mock accessibility
- * service, another with the instrumented activity and test cases. The motivation for
- * two APKs design is that CTS tests cannot access the secure settings which is
- * required for enabling accessibility and accessibility services. Therefore, manual
- * installation of the <strong>CtsAccessibilityServiceTestMockService.apk</strong>
+ * Note: The accessibility CTS tests are composed of two APKs, one with delegating
+ * accessibility service and another with the instrumented activity and test cases.
+ * The motivation for two APKs design is that CTS tests cannot access the secure
+ * settings which is required for enabling accessibility services, hence there is
+ * no way to manipulate accessibility settings programmaticaly. Further, manually
+ * enabling an accessibility service in the tests APK will not work either because
+ * the instrumentation restarts the process under test which would break the binding
+ * between the accessibility service and the system.
+ * <p>
+ * Therefore, manual installation of the
+ * <strong>CtsAccessibilityServiceTestMockService.apk</strong>
  * whose source is located at <strong>cts/tests/accessibility</strong> is required.
- * Once the former package has been installed accessibility must be enabled (Settings ->
- * Accessibility), the mock service must be enabled (Settings -> Accessibility
- * -> Mock Accessibility Service), and then the CTS tests in this package can be
- * successfully run. Further, the mock and tests run in separate processes since
- * the instrumentation restarts the process in which it is running and this
- * breaks the binding between the mock accessibility service and the system.
+ * Once the former package has been installed the service must be enabled
+ * (Settings -> Accessibility -> Delegating Accessibility Service), and then the CTS tests
+ * in this package can be successfully run.
+ * </p>
  */
 public class AccessibilityEndToEndTest extends
         ActivityInstrumentationTestCase2<AccessibilityEndToEndTestActivity> {
@@ -82,35 +71,10 @@
      * Timeout required for pending Binder calls or event processing to
      * complete.
      */
-    private static final long MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING = 500;
+    private static final long TIMEOUT_ASYNC_PROCESSING = 500;
 
     /**
-     * The count of the polling attempts during {@link #MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING}
-     */
-    private static final long COUNT_POLLING_ATTEMPTS = 10;
-
-    /**
-     * The package of the accessibility service mock interface.
-     */
-    private static final String DELEGATING_SERVICE_PACKAGE =
-        "android.accessibilityservice.delegate";
-
-    /**
-     * The package of the delegating accessibility service interface.
-     */
-    private static final String DELEGATING_SERVICE_CLASS_NAME =
-        "android.accessibilityservice.delegate.DelegatingAccessibilityService";
-
-    /**
-     * The package of the delegating accessibility service connection interface.
-     */
-    private static final String DELEGATING_SERVICE_CONNECTION_CLASS_NAME =
-        "android.accessibilityservice.delegate."
-            + "DelegatingAccessibilityService$DelegatingConnectionService";
-
-    /**
-     * Creates a new instance for testing
-     * {@link AccessibilityEndToEndTestActivity}.
+     * Creates a new instance for testing {@link AccessibilityEndToEndTestActivity}.
      *
      * @throws Exception If any error occurs.
      */
@@ -124,7 +88,7 @@
 
         // Wait for accessibility events to settle i.e. for all events generated
         // while bringing the activity up to be delivered so they do not interfere.
-        SystemClock.sleep(MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING);
+        SystemClock.sleep(TIMEOUT_ASYNC_PROCESSING);
 
         // create and populate the expected event
         AccessibilityEvent selectedEvent = AccessibilityEvent.obtain();
@@ -135,7 +99,7 @@
         selectedEvent.setItemCount(2);
         selectedEvent.setCurrentItemIndex(1);
         selectedEvent.setEnabled(true);
-        selectedEvent.setScrollable(true);
+        selectedEvent.setScrollable(false);
         selectedEvent.setFromIndex(0);
         selectedEvent.setToIndex(1);
 
@@ -153,7 +117,7 @@
         });
 
         // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
+        service.verify();
     }
 
     @LargeTest
@@ -162,7 +126,7 @@
 
         // Wait for accessibility events to settle i.e. for all events generated
         // while bringing the activity up to be delivered so they do not interfere.
-        SystemClock.sleep(MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING);
+        SystemClock.sleep(TIMEOUT_ASYNC_PROCESSING);
 
         // create and populate the expected event
         AccessibilityEvent clickedEvent = AccessibilityEvent.obtain();
@@ -186,7 +150,7 @@
         });
 
         // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
+        service.verify();
     }
 
     @LargeTest
@@ -195,7 +159,7 @@
 
         // Wait for accessibility events to settle i.e. for all events generated
         // while bringing the activity up to be delivered so they do not interfere.
-        SystemClock.sleep(MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING);
+        SystemClock.sleep(TIMEOUT_ASYNC_PROCESSING);
 
         // create and populate the expected event
         AccessibilityEvent longClickedEvent = AccessibilityEvent.obtain();
@@ -219,7 +183,7 @@
         });
 
         // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
+        service.verify();
     }
 
     @LargeTest
@@ -228,7 +192,7 @@
 
         // Wait for accessibility events to settle i.e. for all events generated
         // while bringing the activity up to be delivered so they do not interfere.
-        SystemClock.sleep(MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING);
+        SystemClock.sleep(TIMEOUT_ASYNC_PROCESSING);
 
         // create and populate the expected event
         AccessibilityEvent focusedEvent = AccessibilityEvent.obtain();
@@ -254,7 +218,7 @@
         });
 
         // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
+        service.verify();
     }
 
     @LargeTest
@@ -270,7 +234,7 @@
         });
 
         // wait for the generated focus event to be dispatched
-        SystemClock.sleep(MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING);
+        SystemClock.sleep(TIMEOUT_ASYNC_PROCESSING);
 
 
         final String beforeText = activity.getString(R.string.text_input_blah);
@@ -302,7 +266,7 @@
         });
 
         // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
+        service.verify();
     }
 
     @LargeTest
@@ -311,7 +275,7 @@
 
         // Wait for accessibility events to settle i.e. for all events generated
         // while bringing the activity up to be delivered so they do not interfere.
-        SystemClock.sleep(MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING);
+        SystemClock.sleep(TIMEOUT_ASYNC_PROCESSING);
 
         String title = activity.getString(R.string.alert_title);
         String message = activity.getString(R.string.alert_message);
@@ -342,7 +306,7 @@
         });
 
         // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
+        service.verify();
     }
 
     @LargeTest
@@ -351,7 +315,7 @@
 
         // Wait for accessibility events to settle i.e. for all events generated
         // while bringing the activity up to be delivered so they do not interfere.
-        SystemClock.sleep(MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING);
+        SystemClock.sleep(TIMEOUT_ASYNC_PROCESSING);
 
         String message = activity.getString(R.string.notification_message);
 
@@ -383,61 +347,18 @@
         notificationManager.notify(notificationId, notification);
 
         // verify if all expected methods have been called
-        assertMockServiceVerifiedWithinTimeout(service);
+        service.verify();
 
         // remove the notification
         notificationManager.cancel(notificationId);
     }
 
-    /**
-     * Asserts the the mock accessibility service has been successfully verified
-     * (which is it has received the expected method calls with expected
-     * arguments) within the {@link #MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING}. The
-     * verified state is checked by polling upon small intervals.
-     *
-     * @param service The service to verify.
-     * @throws Exception If the verification has failed with exception after the
-     *             {@link #MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING}.
-     */
-    private void assertMockServiceVerifiedWithinTimeout(MockAccessibilityService service)
-            throws Throwable {
-        Throwable lastVerifyThrowable = null;
-        long beginTime = SystemClock.uptimeMillis();
-        long pollTmeout = MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING / COUNT_POLLING_ATTEMPTS;
+    static class MockAccessibilityService extends AccessibilityService {
 
-        // poll until the timeout has elapsed
-        while (SystemClock.uptimeMillis() - beginTime < MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING) {
-            // sleep first since immediate call will always fail
-            try {
-                Thread.sleep(pollTmeout);
-            } catch (InterruptedException ie) {
-                /* ignore */
-            }
-
-            try {
-                service.verify();
-                // success - reset so it is not accept more events
-                service.reset();
-                return;
-            } catch (IllegalStateException ise) {
-                // this exception is thrown if the expected event is not
-                // received yet, so we will keep trying within the timeout
-                lastVerifyThrowable = ise;
-                continue;
-            } catch (Throwable t) {
-                // we have just failed
-                lastVerifyThrowable = t;
-                break;
-            }
-        }
-
-        // failure - reset so it is not accept more events
-        service.reset();
-        throw lastVerifyThrowable;
-    }
-
-    static class MockAccessibilityService extends AccessibilityService implements
-            ServiceConnection {
+        /**
+         * Helper for connecting to the delegating accessibility service.
+         */
+        private final AccessibilityDelegateHelper mAccessibilityDelegateHelper;
 
         /**
          * The singleton instance.
@@ -451,6 +372,11 @@
             new LinkedList<AccessibilityEvent>();
 
         /**
+         * Reusable temporary builder.
+         */
+        private final StringBuilder mTempBuilder = new StringBuilder();
+
+        /**
          * Interruption call this service expects to receive.
          */
         private boolean mExpectedInterrupt;
@@ -461,14 +387,9 @@
         private boolean mReplaying;
 
         /**
-         * Flag indicating if this mock is initialized.
+         * Lock for synchronization.
          */
-        private boolean mInitialized;
-
-        /**
-         * The {@link Context} whose services to utilize.
-         */
-        private Context mContext;
+        private final Object mLock = new Object();
 
         /**
          * Gets the {@link MockAccessibilityService} singleton.
@@ -491,100 +412,57 @@
          * Creates a new instance.
          */
         private MockAccessibilityService(Context context) {
-            mContext = context;
-            ensureSetupAndBoundToDelegatingAccessibilityService();
-        }
-
-        /**
-         * Ensures the required setup for the test performed and that it is bound to the
-         * DelegatingAccessibilityService which runs in another process. The setup is
-         * enabling accessibility and installing and enabling the delegating accessibility
-         * service this test binds to.
-         * </p>
-         * Note: Please look at the class description for information why such an
-         *       approach is taken.
-         */
-        public void ensureSetupAndBoundToDelegatingAccessibilityService() {
-            // check if accessibility is enabled
-            AccessibilityManager accessibilityManager = (AccessibilityManager) mContext
-                    .getSystemService(Service.ACCESSIBILITY_SERVICE);
-
-            if (!accessibilityManager.isEnabled()) {
-                throw new IllegalStateException("Accessibility not enabled. "
-                        + "(Settings -> Accessibility)");
-            }
-
-            // check if the delegating service is running
-            ComponentName delegatingServiceName = new ComponentName(
-                    DELEGATING_SERVICE_PACKAGE, DELEGATING_SERVICE_CLASS_NAME);
-            ActivityManager activityManager = (ActivityManager) mContext
-                    .getSystemService(Service.ACTIVITY_SERVICE);
-            boolean delegatingServiceRunning = false;
-
-            for (RunningServiceInfo runningServiceInfo : activityManager.getRunningServices(100)) {
-                if (delegatingServiceName.equals(runningServiceInfo.service)) {
-                    delegatingServiceRunning = true;
-                    break;
-                }
-            }
-
-            if (!delegatingServiceRunning) {
-                // delegating service not running, so check if it is installed at all
-                try {
-                    PackageManager packageManager = mContext.getPackageManager();
-                    packageManager.getServiceInfo(delegatingServiceName, 0);
-                } catch (NameNotFoundException nnfe) {
-                    throw new IllegalStateException("CtsDelegatingAccessibilityService.apk" +
-                            " not installed.");
-                }
-
-                throw new IllegalStateException("Delegating Accessibility Service not running."
-                         + "(Settings -> Accessibility -> Delegating Accessibility Service)");
-            }
-
-            Intent intent = new Intent().setClassName(DELEGATING_SERVICE_PACKAGE,
-                    DELEGATING_SERVICE_CONNECTION_CLASS_NAME);
-            mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
-
-            long beginTime = SystemClock.uptimeMillis();
-            long pollTmeout = MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING / COUNT_POLLING_ATTEMPTS;
-
-            // bind to the delegating service which runs in another process by
-            // polling until the binder connection is established
-            while (SystemClock.uptimeMillis() - beginTime < MAX_TIMEOUT_ASYNCHRONOUS_PROCESSING) {
-                if (mInitialized) {
-                    // success
-                    return;
-                }
-                try {
-                    Thread.sleep(pollTmeout);
-                } catch (InterruptedException ie) {
-                    /* ignore */
-                }
-            }
+            mAccessibilityDelegateHelper = new AccessibilityDelegateHelper(this);
+            mAccessibilityDelegateHelper.bindToDelegatingAccessibilityService(
+                    context);
         }
 
         /**
          * Starts replaying the mock.
          */
-        private void replay() {
+        public void replay() {
             mReplaying = true;
         }
 
         /**
-         * Verifies if all expected service methods have been called.
+         * Verifies the mock service.
+         *
+         * @throws IllegalStateException If the verification has failed.
          */
-        private void verify() {
-            synchronized (this) {
-                if (!mReplaying) {
-                    throw new IllegalStateException("Did you forget to call replay()");
-                }
-                if (mExpectedInterrupt) {
-                    throw new IllegalStateException("Expected call to #interrupt() not received");
-                }
-                if (!mExpectedEvents.isEmpty()) {
-                    throw new IllegalStateException("Expected a call to onAccessibilityEvent() for "
-                            + "events \"" + mExpectedEvents + "\" not received");
+        public void verify() throws IllegalStateException {
+            StringBuilder problems = mTempBuilder;
+            final long startTime = SystemClock.uptimeMillis();
+            synchronized (mLock) {
+                while (true) {
+                    if (!mReplaying) {
+                        throw new IllegalStateException("Did you forget to call replay()?");
+                    }
+                    if (!mExpectedInterrupt && mExpectedEvents.isEmpty()) {
+                        reset();
+                        return; // success
+                    }
+                    problems.setLength(0);
+                    if (mExpectedInterrupt) {
+                        problems.append("Expected call to #interrupt() not received.");
+                    }
+                    if (!mExpectedEvents.isEmpty()) {
+                        problems.append("Expected a call to onAccessibilityEvent() for events \""
+                                + mExpectedEvents + "\" not received.");
+                    }
+                    final long elapsedTime = SystemClock.uptimeMillis() - startTime;
+                    final long remainingTime = TIMEOUT_ASYNC_PROCESSING - elapsedTime;
+                    if (remainingTime <= 0) {
+                        reset();
+                        if (problems.length() > 0) {
+                            throw new IllegalStateException(problems.toString());
+                        }
+                        return;
+                    }
+                    try {
+                        mLock.wait(remainingTime);
+                    } catch (InterruptedException ie) {
+                        /* ignore */
+                    }
                 }
             }
         }
@@ -593,10 +471,11 @@
          * Resets this instance so it can be reused.
          */
         private void reset() {
-            synchronized (this) {
+            synchronized (mLock) {
                 mExpectedEvents.clear();
                 mExpectedInterrupt = false;
                 mReplaying = false;
+                mLock.notifyAll();
             }
         }
 
@@ -620,7 +499,7 @@
 
         @Override
         public void onAccessibilityEvent(AccessibilityEvent receivedEvent) {
-            synchronized (this) {
+            synchronized (mLock) {
                 if (!mReplaying) {
                     return;
                 }
@@ -629,48 +508,25 @@
                 }
                 AccessibilityEvent expectedEvent = mExpectedEvents.poll();
                 assertEqualsAccessiblityEvent(expectedEvent, receivedEvent);
+                mLock.notifyAll();
             }
         }
 
         @Override
         public void onInterrupt() {
-            synchronized (this) {
+            synchronized (mLock) {
                 if (!mReplaying) {
                     return;
                 }
-
                 if (!mExpectedInterrupt) {
                     throw new IllegalStateException("Unexpected call to onInterrupt()");
                 }
-
                 mExpectedInterrupt = false;
+                mLock.notifyAll();
             }
         }
 
         /**
-         * {@inheritDoc ServiceConnection#onServiceConnected(ComponentName,IBinder)}
-         */
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            IAccessibilityServiceDelegateConnection connection =
-                IAccessibilityServiceDelegateConnection.Stub
-                    .asInterface(service);
-            try {
-                connection.setAccessibilityServiceDelegate(new AccessibilityServiceDelegate(this));
-                mInitialized = true;
-            } catch (RemoteException re) {
-                fail("Could not set delegate to the delegating service.");
-            }
-        }
-
-        /**
-         * {@inheritDoc ServiceConnection#onServiceDisconnected(ComponentName)}
-         */
-        public void onServiceDisconnected(ComponentName name) {
-            mInitialized = false;
-            /* do nothing */
-        }
-
-        /**
          * Compares all properties of the <code>expectedEvent</code> and the
          * <code>receviedEvent</code> to verify that the received event is the
          * one that is expected.
@@ -764,84 +620,5 @@
                         receivedTextIterator.next().toString());
             }
         }
-
-        /**
-         * This class is the delegate called by the DelegatingAccessibilityService.
-         */
-        private class AccessibilityServiceDelegate extends
-                IAccessibilityServiceDelegate.Stub implements Handler.Callback {
-
-            /**
-             * Tag for logging.
-             */
-            private static final String LOG_TAG = "AccessibilityServiceDelegate";
-
-            /**
-             * Message type for calling {@link #onInterrupt()}
-             */
-            private static final int DO_ON_INTERRUPT = 10;
-
-            /**
-             * Message type for calling {@link #onAccessibilityEvent(AccessibilityEvent)}
-             */
-            private static final int DO_ON_ACCESSIBILITY_EVENT = 20;
-
-            /**
-             * Caller for handling {@link Message}s
-             */
-            private final Handler mHandler;
-
-            /**
-             * The {@link MockAccessibilityService} to which to delegate;
-             */
-            private MockAccessibilityService mMockAccessibilityService;
-
-            /**
-             * Creates a new instance.
-             *
-             * @param mockAccessibilityService The service to whcih to delegate.
-             */
-            public AccessibilityServiceDelegate(MockAccessibilityService mockAccessibilityService) {
-                mMockAccessibilityService = mockAccessibilityService;
-                mHandler = new Handler(this);
-            }
-
-            /**
-             * {@inheritDoc IAccessibilityServiceDelegate#onAccessibilityEvent(AccessibilityEvent)}
-             */
-            public void onAccessibilityEvent(AccessibilityEvent event) {
-                Message message = Message.obtain(mHandler, DO_ON_ACCESSIBILITY_EVENT, event);
-                mHandler.sendMessage(message);
-            }
-
-            /**
-             * {@inheritDoc IAccessibilityServiceDelegate#onInterrupt()}
-             */
-            public void onInterrupt() {
-                Message message = mHandler.obtainMessage(DO_ON_INTERRUPT);
-                mHandler.sendMessage(message);
-            }
-
-            /**
-             * {@inheritDoc Handler.Callback#handleMessage(Message)}
-             */
-            public boolean handleMessage(Message message) {
-                switch (message.what) {
-                    case DO_ON_ACCESSIBILITY_EVENT:
-                        AccessibilityEvent event = (AccessibilityEvent) message.obj;
-                        if (event != null) {
-                            mMockAccessibilityService.onAccessibilityEvent(event);
-                            event.recycle();
-                        }
-                        return true;
-                    case DO_ON_INTERRUPT:
-                        mMockAccessibilityService.onInterrupt();
-                        return true;
-                    default:
-                        Log.w(LOG_TAG, "Unknown message type " + message.what);
-                        return false;
-                }
-            }
-        }
     }
 }
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java
new file mode 100644
index 0000000..6f72a75
--- /dev/null
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java
@@ -0,0 +1,47 @@
+/**
+ * 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.accessibilityservice.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+import com.android.cts.accessibilityservice.R;
+
+/**
+ * Activity for testing the accessibility APIs for querying of
+ * the screen content. These APIs allow exploring the screen and
+ * requesting an action to be performed on a given view from an
+ * AccessiiblityService.
+ */
+public class AccessibilityWindowQueryActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.query_window_test);
+
+        findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                /* do nothing */
+            }
+        });
+        findViewById(R.id.button5).setOnLongClickListener(new View.OnLongClickListener() {
+            public boolean onLongClick(View v) {
+                return true;
+            }
+        });
+    }
+}
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivityTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivityTest.java
new file mode 100644
index 0000000..d798052
--- /dev/null
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivityTest.java
@@ -0,0 +1,497 @@
+/**
+ * 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.accessibilityservice.cts;
+
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SELECT;
+
+import android.accessibilityservice.AccessibilityService;
+import android.content.Context;
+import android.graphics.Rect;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.cts.accessibilityservice.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.BrokenBarrierException;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Activity for testing the accessibility APIs for querying of
+ * the screen content. These APIs allow exploring the screen and
+ * requesting an action to be performed on a given view from an
+ * AccessiiblityService.
+ * <p>
+ * Note: The accessibility CTS tests are composed of two APKs, one with delegating
+ * accessibility service and another with the instrumented activity and test cases.
+ * The motivation for two APKs design is that CTS tests cannot access the secure
+ * settings which is required for enabling accessibility services, hence there is
+ * no way to manipulate accessibility settings programmaticaly. Further, manually
+ * enabling an accessibility service in the tests APK will not work either because
+ * the instrumentation restarts the process under test which would break the binding
+ * between the accessibility service and the system.
+ * <p>
+ * Therefore, manual installation of the
+ * <strong>CtsAccessibilityServiceTestMockService.apk</strong>
+ * whose source is located at <strong>cts/tests/accessibility</strong> is required.
+ * Once the former package has been installed the service must be enabled
+ * (Settings -> Accessibility -> Delegating Accessibility Service), and then the CTS tests
+ * in this package can be successfully run.
+ * </p>
+ */
+public class AccessibilityWindowQueryActivityTest
+        extends ActivityInstrumentationTestCase2<AccessibilityWindowQueryActivity> {
+
+    private interface AccessibilityEventFilter {
+        public boolean accept(AccessibilityEvent event);
+    }
+
+    public AccessibilityWindowQueryActivityTest() {
+        super(AccessibilityWindowQueryActivity.class);
+    }
+
+    @Override
+    public void setUp() {
+        // start the activity and wait for a handle to its window root.
+        startActivityAndWaitForFirstEvent();
+    }
+
+    @LargeTest
+    public void testFindByText() throws Exception {
+        // find a view by text
+        List<AccessibilityNodeInfo> buttons = findAccessibilityNodeInfosByText(
+                getAwaitedAccessibilityEventSource(), "butto");
+        assertEquals(9, buttons.size());
+    }
+
+    @LargeTest
+    public void testFindByContentDescription() throws Exception {
+        // find a view by text
+        AccessibilityNodeInfo button = findAccessibilityNodeInfoByText(
+                getAwaitedAccessibilityEventSource(), R.string.contentDescription);
+        assertNotNull(button);
+    }
+
+    @LargeTest
+    public void testTraverseWindow() throws Exception {
+        // make list of expected nodes
+        List<String> classNameAndTextList = new ArrayList<String>();
+        classNameAndTextList.add("com.android.internal.policy.impl.PhoneWindow$DecorView");
+        classNameAndTextList.add("android.widget.LinearLayout");
+        classNameAndTextList.add("android.widget.FrameLayout");
+        classNameAndTextList.add("android.widget.LinearLayout");
+        classNameAndTextList.add("android.widget.LinearLayout");
+        classNameAndTextList.add("android.widget.LinearLayout");
+        classNameAndTextList.add("android.widget.LinearLayout");
+        classNameAndTextList.add("android.widget.ButtonButton1");
+        classNameAndTextList.add("android.widget.ButtonButton2");
+        classNameAndTextList.add("android.widget.ButtonButton3");
+        classNameAndTextList.add("android.widget.ButtonButton4");
+        classNameAndTextList.add("android.widget.ButtonButton5");
+        classNameAndTextList.add("android.widget.ButtonButton6");
+        classNameAndTextList.add("android.widget.ButtonButton7");
+        classNameAndTextList.add("android.widget.ButtonButton8");
+        classNameAndTextList.add("android.widget.ButtonButton9");
+
+        Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
+        fringe.add(getAwaitedAccessibilityEventSource());
+
+        // do a BFS traversal and check nodes
+        while (!fringe.isEmpty()) {
+            AccessibilityNodeInfo current = fringe.poll();
+
+            CharSequence text = current.getText();
+            String receivedClassNameAndText = current.getClassName().toString()
+                + ((text != null) ? text.toString() : "");
+            String expectedClassNameAndText = classNameAndTextList.remove(0);
+
+            assertEquals("Did not get the expected node info",
+                    expectedClassNameAndText, receivedClassNameAndText);
+
+            final int childCount = current.getChildCount();
+            for (int i = 0; i < childCount; i++) {
+                AccessibilityNodeInfo child = current.getChild(i);
+                fringe.add(child);
+            }
+        }
+    }
+
+    @LargeTest
+    public void testPerformActionFocus() throws Exception {
+        // find a view and make sure it is not focused
+        AccessibilityNodeInfo button = findAccessibilityNodeInfoByText(
+                getAwaitedAccessibilityEventSource(), R.string.button5);
+        assertFalse(button.isFocused());
+
+        // focus the view
+        assertTrue(button.performAction(ACTION_FOCUS));
+
+        // find the view again and make sure it is focused
+        button = findAccessibilityNodeInfoByText(getAwaitedAccessibilityEventSource(),
+                R.string.button5);
+        assertTrue(button.isFocused());
+    }
+
+    @LargeTest
+    public void testPerformActionClearFocus() throws Exception {
+        // find a view and make sure it is not focused
+        AccessibilityNodeInfo button = findAccessibilityNodeInfoByText(
+                getAwaitedAccessibilityEventSource(), R.string.button5);
+        assertFalse(button.isFocused());
+
+        // focus the view
+        assertTrue(button.performAction(ACTION_FOCUS));
+
+        // find the view again and make sure it is focused
+        button = findAccessibilityNodeInfoByText(getAwaitedAccessibilityEventSource(),
+                R.string.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 = findAccessibilityNodeInfoByText(getAwaitedAccessibilityEventSource(),
+                R.string.button5);
+        assertFalse(button.isFocused());
+    }
+
+    @LargeTest
+    public void testPerformActionSelect() throws Exception {
+        // find a view and make sure it is not selected
+        AccessibilityNodeInfo button = findAccessibilityNodeInfoByText(
+                getAwaitedAccessibilityEventSource(), R.string.button5);
+        assertFalse(button.isSelected());
+
+        // select the view
+        assertTrue(button.performAction(ACTION_SELECT));
+
+        // find the view again and make sure it is selected
+        button = findAccessibilityNodeInfoByText(getAwaitedAccessibilityEventSource(),
+                R.string.button5);
+        assertTrue(button.isSelected());
+    }
+
+    @LargeTest
+    public void testPerformActionClearSelection() throws Exception {
+        // find a view and make sure it is not selected
+        AccessibilityNodeInfo button = findAccessibilityNodeInfoByText(
+                getAwaitedAccessibilityEventSource(), R.string.button5);
+        assertFalse(button.isSelected());
+
+        // select the view
+        assertTrue(button.performAction(ACTION_SELECT));
+
+        // find the view again and make sure it is selected
+        button = findAccessibilityNodeInfoByText(getAwaitedAccessibilityEventSource(),R
+                .string.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 = findAccessibilityNodeInfoByText(getAwaitedAccessibilityEventSource(),
+                R.string.button5);
+        assertFalse(button.isSelected());
+    }
+
+    @LargeTest
+    public void testGetEventSource() throws Exception {
+        // find a view and make sure it is not focused
+        final AccessibilityNodeInfo button = findAccessibilityNodeInfoByText(
+                getAwaitedAccessibilityEventSource(), R.string.button5);
+        assertFalse(button.isSelected());
+
+        // focus and wait for the event
+        AccessibilityQueryBridge bridge = AccessibilityQueryBridge.getInstance(
+                getInstrumentation().getContext());
+        bridge.perfromActionAndWaitForEvent(
+                new Runnable() {
+            @Override
+            public void run() {
+                assertTrue(button.performAction(ACTION_FOCUS));
+            }
+        },
+                new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                return (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED);
+            }
+        });
+
+        // check that last event source
+        AccessibilityNodeInfo source = getAwaitedAccessibilityEventSource();
+        assertNotNull(source);
+
+        // bounds
+        Rect buttonBounds = new Rect();
+        button.getBoundsInParent(buttonBounds);
+        Rect sourceBounds = new Rect();
+        source.getBoundsInParent(sourceBounds);
+
+        assertEquals(buttonBounds.left, sourceBounds.left);
+        assertEquals(buttonBounds.right, sourceBounds.right);
+        assertEquals(buttonBounds.top, sourceBounds.top);
+        assertEquals(buttonBounds.bottom, sourceBounds.bottom);
+
+        // char sequence attributes
+        assertEquals(button.getPackageName(), source.getPackageName());
+        assertEquals(button.getClassName(), source.getClassName());
+        assertEquals(button.getText(), source.getText());
+        assertSame(button.getContentDescription(), source.getContentDescription());
+
+        // boolean attributes
+        assertSame(button.isFocusable(), source.isFocusable());
+        assertSame(button.isClickable(), source.isClickable());
+        assertSame(button.isEnabled(), source.isEnabled());
+        assertNotSame(button.isFocused(), source.isFocused());
+        assertSame(button.isLongClickable(), source.isLongClickable());
+        assertSame(button.isPassword(), source.isPassword());
+        assertSame(button.isSelected(), source.isSelected());
+        assertSame(button.isCheckable(), source.isCheckable());
+        assertSame(button.isChecked(), source.isChecked());
+    }
+
+    @LargeTest
+    public void testObjectContract() throws Exception {
+        // find a view and make sure it is not focused
+        AccessibilityNodeInfo button = findAccessibilityNodeInfoByText(
+                getAwaitedAccessibilityEventSource(), R.string.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.");
+    }
+
+    @Override
+    protected void scrubClass(Class<?> testCaseClass) {
+        /* intentionally do not scrub */
+    }
+
+    /**
+     * Starts the activity under tests and waits for the first accessibility
+     * event from that activity.
+     */
+    private void startActivityAndWaitForFirstEvent() {
+        AccessibilityQueryBridge bridge = AccessibilityQueryBridge.getInstance(
+                getInstrumentation().getContext());
+        bridge.perfromActionAndWaitForEvent(
+                new Runnable() {
+            @Override
+            public void run() {
+                getActivity();
+            }
+        },
+                new AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                final int eventType = event.getEventType();
+                CharSequence packageName = event.getPackageName();
+                Context targetContext = getInstrumentation().getTargetContext();
+                return (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
+                        && targetContext.getPackageName().equals(packageName));
+            }
+        });
+    }
+
+    /**
+     * @return The source of the last accessibility event.
+     */
+    private AccessibilityNodeInfo getAwaitedAccessibilityEventSource() {
+        AccessibilityQueryBridge bridge = AccessibilityQueryBridge.getInstance(
+                getInstrumentation().getContext());
+        AccessibilityEvent event = bridge.getAwaitedAccessibilityEvent();
+        if (event != null) {
+            return event.getSource();
+        }
+        return null;
+    }
+
+    private List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(AccessibilityNodeInfo root,
+            String text) {
+        if (root != null) {
+            return root.findAccessibilityNodeInfosByText(text);
+        }
+        return Collections.emptyList();
+    }
+
+    /**
+     * Finds the first accessibility info that contains text. The search starts
+     * from the given <code>root</code>
+     *
+     * @param root Node from which to start the search.
+     * @param resId Resource id of the searched text.
+     * @return The node with this text or null.
+     */
+    private AccessibilityNodeInfo findAccessibilityNodeInfoByText(AccessibilityNodeInfo root,
+            int resId) {
+        return findAccessibilityNodeInfoByText(root,
+                getInstrumentation().getContext().getString(resId));
+    }
+
+    /**
+     * Finds the first accessibility info that contains text. The search starts
+     * from the given <code>root</code>
+     *
+     * @param root Node from which to start the search.
+     * @param text The searched text.
+     * @return The node with this text or null.
+     */
+    private AccessibilityNodeInfo findAccessibilityNodeInfoByText(AccessibilityNodeInfo root,
+            String text) {
+        List<AccessibilityNodeInfo> nodes = findAccessibilityNodeInfosByText(root, text);
+        if (nodes != null && !nodes.isEmpty()) {
+            return nodes.get(0);
+        }
+        return null;
+    }
+
+    /**
+     * This class serves as a bridge for querying the screen content.
+     * The bride is connected of a delegating accessibility service.
+     */
+    static class AccessibilityQueryBridge extends AccessibilityService {
+
+        /**
+         * The singleton instance.
+         */
+        private static AccessibilityQueryBridge sInstance;
+
+        /**
+         * Helper for connecting to the delegating accessibility service.
+         */
+        private final AccessibilityDelegateHelper mAccessibilityDelegateHelper;
+
+        /**
+         * The last received accessibility event.
+         */
+        private AccessibilityEvent mAwaitedAccessbiliyEvent;
+
+        /**
+         * Barrier for synchronizing waiting client and this bridge.
+         */
+        private CyclicBarrier mBarrier = new CyclicBarrier(2);
+
+        /**
+         * Filter for the currently waited event.
+         */
+        private AccessibilityEventFilter mWaitedFilter;
+
+        /**
+         * Gets the {@link AccessibilityQueryBridge} singleton.
+         *
+         * @param context A context handle.
+         * @return The mock service.
+         */
+        public static AccessibilityQueryBridge getInstance(Context context) {
+            if (sInstance == null) {
+                // since we do bind once and do not unbind from the delegating
+                // service and JUnit3 does not support @BeforeTest and @AfterTest,
+                // we will leak a service connection after the test but this
+                // does not affect the test results and the test is twice as fast
+                sInstance = new AccessibilityQueryBridge(context);
+            }
+            return sInstance;
+        }
+
+        private AccessibilityQueryBridge(Context context) {
+            mAccessibilityDelegateHelper = new AccessibilityDelegateHelper(this);
+            mAccessibilityDelegateHelper.bindToDelegatingAccessibilityService(context);
+        }
+
+        @Override
+        public void onAccessibilityEvent(AccessibilityEvent event) {
+            if (mWaitedFilter != null && mWaitedFilter.accept(event)) {
+                mAwaitedAccessbiliyEvent = AccessibilityEvent.obtain(event);
+                awaitOnBarrier();
+            }
+        }
+
+        @Override
+        public void onInterrupt() {
+            /* do nothing */
+        }
+
+        /**
+         * @return The event that was waited for.
+         */
+        public AccessibilityEvent getAwaitedAccessibilityEvent() {
+            return mAwaitedAccessbiliyEvent;
+        }
+
+        /**
+         * Performs an action and waits for the resulting event.
+         *
+         * @param action The action to perform.
+         * @param filter Filter for recognizing the waited event.
+         */
+        public void perfromActionAndWaitForEvent(Runnable action,
+                AccessibilityEventFilter filter) {
+            reset();
+            mWaitedFilter = filter;
+            action.run();
+            awaitOnBarrier();
+        }
+
+        /**
+         * Rests the internal state.
+         */
+        private void reset() {
+            if (mAwaitedAccessbiliyEvent != null) {
+                mAwaitedAccessbiliyEvent.recycle();
+                mAwaitedAccessbiliyEvent = null;
+            }
+            mBarrier.reset();
+            mWaitedFilter = null;
+        }
+
+        /**
+         * Calls await of the barrier taking care of the exceptions.
+         */
+        private void awaitOnBarrier() {
+            try {
+                mBarrier.await(AccessibilityDelegateHelper.TIMEOUT_ASYNC_PROCESSING,
+                        TimeUnit.MILLISECONDS);
+            } catch (InterruptedException ie) {
+                /* ignore */
+            } catch (BrokenBarrierException bbe) {
+                /* ignore */
+            } catch (TimeoutException te) {
+                /* ignore */
+            }
+        }
+    }
+}