Fixing bugs exposed when moving accessibility CTS tests to UiAutomation.

1. UiAutomation#executeAndWaitForEvent method was invoking the passed
   runnable while holding the lock which may lead to a deadlock. For
   example: a runnable that calls getActivity() gets us into a state
   like this.

2. UI automation services did not get all capabilities such a
   service can have. Now a UI test service gets all of them.

3. When UiAutomation was exiting for event fired as a result of a
   performed action, it was checking whether the received evnet time
   is strictly before the time of executing the command that should
   fire the event. However, if the execution is fast enough, i.e.
   less than one millisecond, then the event time and the execution
   time are the same. This was leading to a missed signal in rare
   cases.

4. AccessibilityNodeInfoCache was not clearing the relevant state
   for accessibility focus clearing event.

5. Accessibility text traversal in TextView was partially using text
   and partially content description - broken. Now we are using the
   text since for text view and content desc for other views. In other
   words, we are using the most precise text we have.

6. AccessibilityManagerService was not granting capabilities of a
   UiAutomation service - plainly wrong.

CTS change:https://googleplex-android-review.googlesource.com/#/c/300693/

bug:8695422
bug:8657560

Change-Id: I9afc5c3c69eb51f1c01930959232f44681b15e86
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 40f45b7..7e21db3 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -378,6 +378,23 @@
     /**
      * Creates a new instance.
      *
+     * @param isAutomation Whether this is a test automation service.
+     *
+     * @hide
+     */
+    public AccessibilityServiceInfo(boolean isAutomation) {
+        // Automation service can do anything.
+        if (isAutomation) {
+            mCapabilities |= CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT
+                    | CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION
+                    | CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY
+                    | CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS;
+        }
+    }
+
+    /**
+     * Creates a new instance.
+     *
      * @param resolveInfo The service resolve info.
      * @param context Context for accessing resources.
      * @throws XmlPullParserException If a XML parsing error occurs.
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 05b79c1..498fa42 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -443,18 +443,25 @@
      */
     public AccessibilityEvent executeAndWaitForEvent(Runnable command,
             AccessibilityEventFilter filter, long timeoutMillis) throws TimeoutException {
+        // Acquire the lock and prepare for receiving events.
         synchronized (mLock) {
             throwIfNotConnectedLocked();
-
             mEventQueue.clear();
             // Prepare to wait for an event.
             mWaitingForEventDelivery = true;
+        }
 
-            // We will ignore events from previous interactions.
-            final long executionStartTimeMillis = SystemClock.uptimeMillis();
+        // Note: We have to release the lock since calling out with this lock held
+        // can bite. We will correctly filter out events from other interactions,
+        // so starting to collect events before running the action is just fine.
 
-            // Execute the command.
-            command.run();
+        // We will ignore events from previous interactions.
+        final long executionStartTimeMillis = SystemClock.uptimeMillis();
+        // Execute the command *without* the lock being held.
+        command.run();
+
+        // Acquire the lock and wait for the event.
+        synchronized (mLock) {
             try {
                 // Wait for the event.
                 final long startTimeMillis = SystemClock.uptimeMillis();
@@ -463,7 +470,7 @@
                     while (!mEventQueue.isEmpty()) {
                         AccessibilityEvent event = mEventQueue.remove(0);
                         // Ignore events from previous interactions.
-                        if (event.getEventTime() <= executionStartTimeMillis) {
+                        if (event.getEventTime() < executionStartTimeMillis) {
                             continue;
                         }
                         if (filter.accept(event)) {
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 97c7ff3..5bc17fa 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -158,7 +158,7 @@
     private void registerUiTestAutomationServiceLocked(IAccessibilityServiceClient client) {
         IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
                 ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
-        AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+        AccessibilityServiceInfo info = new AccessibilityServiceInfo(true);
         info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
         info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
         info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
index 14954be..28518aa 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
@@ -83,6 +83,7 @@
                 } break;
                 case AccessibilityEvent.TYPE_VIEW_FOCUSED:
                 case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED:
+                case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED:
                 case AccessibilityEvent.TYPE_VIEW_SELECTED:
                 case AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED:
                 case AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED: {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 1246051..9e3f87f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8042,7 +8042,7 @@
             info.setEditable(true);
         }
 
-        if (TextUtils.isEmpty(getContentDescription()) && !TextUtils.isEmpty(mText)) {
+        if (!TextUtils.isEmpty(mText)) {
             info.addAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
             info.addAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
             info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
@@ -8051,6 +8051,7 @@
                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
                     | AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE);
         }
+
         if (isFocused()) {
             if (canSelectText()) {
                 info.addAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
@@ -8655,13 +8656,10 @@
      */
     @Override
     public CharSequence getIterableTextForAccessibility() {
-        if (!TextUtils.isEmpty(mText)) {
-            if (!(mText instanceof Spannable)) {
-                setText(mText, BufferType.SPANNABLE);
-            }
-            return mText;
+        if (!(mText instanceof Spannable)) {
+            setText(mText, BufferType.SPANNABLE);
         }
-        return super.getIterableTextForAccessibility();
+        return mText;
     }
 
     /**
@@ -8697,13 +8695,7 @@
      */
     @Override
     public int getAccessibilitySelectionStart() {
-        if (TextUtils.isEmpty(getContentDescription())) {
-            final int selectionStart = getSelectionStart();
-            if (selectionStart >= 0) {
-                return selectionStart;
-            }
-        }
-        return ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
+        return getSelectionStart();
     }
 
     /**
@@ -8718,13 +8710,7 @@
      */
     @Override
     public int getAccessibilitySelectionEnd() {
-        if (TextUtils.isEmpty(getContentDescription())) {
-            final int selectionEnd = getSelectionEnd();
-            if (selectionEnd >= 0) {
-                return selectionEnd;
-            }
-        }
-        return ACCESSIBILITY_CURSOR_POSITION_UNDEFINED;
+        return getSelectionEnd();
     }
 
     /**
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2f64908..64dfd67 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -542,7 +542,8 @@
         return -1;
     }
 
-    public void registerUiTestAutomationService(IBinder owner, IAccessibilityServiceClient serviceClient,
+    public void registerUiTestAutomationService(IBinder owner,
+            IAccessibilityServiceClient serviceClient,
             AccessibilityServiceInfo accessibilityServiceInfo) {
         mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
                 FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE);
@@ -1732,14 +1733,12 @@
                 mFetchFlags &= ~AccessibilityNodeInfo.FLAG_REPORT_VIEW_IDS;
             }
 
-            if (mResolveInfo != null) {
-                mRequestTouchExplorationMode = (info.flags
-                        & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
-                mRequestEnhancedWebAccessibility = (info.flags
-                        & AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0;
-                mRequestFilterKeyEvents = (info.flags
-                        & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS)  != 0;
-            }
+            mRequestTouchExplorationMode = (info.flags
+                    & AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE) != 0;
+            mRequestEnhancedWebAccessibility = (info.flags
+                    & AccessibilityServiceInfo.FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY) != 0;
+            mRequestFilterKeyEvents = (info.flags
+                    & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS)  != 0;
         }
 
         /**