1 /**
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the
10  * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11  * express or implied. See the License for the specific language governing permissions and
12  * limitations under the License.
13  */
14 
15 package android.accessibilityservice.cts;
16 
17 import static org.junit.Assert.assertEquals;
18 import static org.junit.Assert.fail;
19 
20 import android.accessibility.cts.common.InstrumentedAccessibilityService;
21 import android.accessibilityservice.AccessibilityGestureEvent;
22 import android.view.Display;
23 import android.view.accessibility.AccessibilityEvent;
24 
25 import java.util.ArrayList;
26 import java.util.List;
27 
28 /** Accessibility service stub, which will collect recognized gestures. */
29 public class GestureDetectionStubAccessibilityService extends InstrumentedAccessibilityService {
30     private static final long GESTURE_RECOGNIZE_TIMEOUT_MS = 3000;
31     protected static final long EVENT_RECOGNIZE_TIMEOUT_MS = 5000;
32     // Member variables
33     protected final Object mLock = new Object();
34     private ArrayList<Integer> mCollectedGestures = new ArrayList();
35     private ArrayList<AccessibilityGestureEvent> mCollectedGestureEvents = new ArrayList();
36     protected ArrayList<Integer> mCollectedEvents = new ArrayList();
37 
38     @Override
onGesture(int gestureId)39     protected boolean onGesture(int gestureId) {
40         synchronized (mCollectedGestures) {
41             mCollectedGestures.add(gestureId);
42         }
43         return true;
44     }
45 
46     @Override
onGesture(AccessibilityGestureEvent gestureEvent)47     public boolean onGesture(AccessibilityGestureEvent gestureEvent) {
48         super.onGesture(gestureEvent);
49         synchronized (mCollectedGestureEvents) {
50             mCollectedGestureEvents.add(gestureEvent);
51             mCollectedGestureEvents.notifyAll(); // Stop waiting for gesture.
52         }
53         return true;
54     }
55 
clearGestures()56     public void clearGestures() {
57         synchronized (mCollectedGestures) {
58             mCollectedGestures.clear();
59         }
60         synchronized (mCollectedGestureEvents) {
61             mCollectedGestureEvents.clear();
62         }
63     }
64 
getGesturesSize()65     public int getGesturesSize() {
66         synchronized (mCollectedGestures) {
67             return mCollectedGestures.size();
68         }
69     }
70 
getGesture(int index)71     public int getGesture(int index) {
72         synchronized (mCollectedGestures) {
73             return mCollectedGestures.get(index);
74         }
75     }
76 
77     /** Waits for {@link #onGesture(AccessibilityGestureEvent)} to collect next gesture. */
waitUntilGestureInfo()78     public void waitUntilGestureInfo() {
79         synchronized (mCollectedGestureEvents) {
80             //Assume the size of mCollectedGestures is changed before mCollectedGestureEvents.
81             if (mCollectedGestureEvents.size() > 0) {
82                 return;
83             }
84             try {
85                 mCollectedGestureEvents.wait(GESTURE_RECOGNIZE_TIMEOUT_MS);
86             } catch (InterruptedException e) {
87                 throw new RuntimeException(e);
88             }
89         }
90     }
91 
getGestureInfoSize()92     public int getGestureInfoSize() {
93         synchronized (mCollectedGestureEvents) {
94             return mCollectedGestureEvents.size();
95         }
96     }
97 
getGestureInfo(int index)98     public AccessibilityGestureEvent getGestureInfo(int index) {
99         synchronized (mCollectedGestureEvents) {
100             return mCollectedGestureEvents.get(index);
101         }
102     }
103 
104     @Override
onAccessibilityEvent(AccessibilityEvent event)105     public void onAccessibilityEvent(AccessibilityEvent event) {
106         synchronized (mLock) {
107             switch (event.getEventType()) {
108                 case AccessibilityEvent.TYPE_TOUCH_INTERACTION_END:
109                     mCollectedEvents.add(event.getEventType());
110                     mLock.notifyAll();
111                     break;
112                 case AccessibilityEvent.TYPE_TOUCH_INTERACTION_START:
113                 case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START:
114                 case AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END:
115                     mCollectedEvents.add(event.getEventType());
116             }
117         }
118         super.onAccessibilityEvent(event);
119     }
120 
clearEvents()121     public void clearEvents() {
122         synchronized (mLock) {
123             mCollectedEvents.clear();
124         }
125     }
126 
getEventsSize()127     public int getEventsSize() {
128         synchronized (mLock) {
129             return mCollectedEvents.size();
130         }
131     }
132 
getEvent(int index)133     public int getEvent(int index) {
134         synchronized (mLock) {
135             return mCollectedEvents.get(index);
136         }
137     }
138 
139     /** Wait for onAccessibilityEvent() to collect next gesture. */
waitUntilEvent(int count)140     public void waitUntilEvent(int count) {
141         synchronized (mLock) {
142             if (mCollectedEvents.size() >= count) {
143                 return;
144             }
145             try {
146                 mLock.wait(EVENT_RECOGNIZE_TIMEOUT_MS);
147             } catch (InterruptedException e) {
148                 throw new RuntimeException(e);
149             }
150         }
151     }
152 
153     /** Ensure that the specified accessibility gesture event has been received. */
assertGestureReceived(int gestureId, int displayId)154     public void assertGestureReceived(int gestureId, int displayId) {
155         // Wait for gesture recognizer, and check recognized gesture.
156         waitUntilGestureInfo();
157         if (displayId == Display.DEFAULT_DISPLAY) {
158             String expected = AccessibilityGestureEvent.gestureIdToString(gestureId);
159             if (getGesturesSize() == 0) {
160                 fail("No gesture received when expecting " + expected);
161             } else if (getGesturesSize() > 1) {
162                 List<String> received = new ArrayList<>();
163                 for (int i = 0; i < getGesturesSize(); ++i) {
164                     received.add(AccessibilityGestureEvent.gestureIdToString(getGesture(i)));
165                 }
166                 fail("Expected " + expected + " but received " + received);
167             } else {
168                 String received = AccessibilityGestureEvent.gestureIdToString(getGesture(0));
169                 assertEquals(expected, received);
170             }
171         }
172         String expected = AccessibilityGestureEvent.gestureIdToString(gestureId);
173         if (getGestureInfoSize() == 0) {
174             fail("No gesture received when expecting " + expected);
175         } else if (getGestureInfoSize() > 1) {
176             List<String> received = new ArrayList<>();
177             for (int i = 0; i < getGesturesSize(); ++i) {
178                 received.add(
179                         AccessibilityGestureEvent.gestureIdToString(
180                                 getGestureInfo(i).getGestureId()));
181             }
182             fail("Expected " + expected + " but received " + received);
183         }
184         AccessibilityGestureEvent expectedGestureEvent =
185                 new AccessibilityGestureEvent(gestureId, displayId);
186         AccessibilityGestureEvent actualGestureEvent = getGestureInfo(0);
187         if (!expectedGestureEvent.toString().equals(actualGestureEvent.toString())) {
188             fail(
189                     "Unexpected gesture received, "
190                             + "Received "
191                             + actualGestureEvent
192                             + ", Expected "
193                             + expectedGestureEvent);
194         }
195         clearGestures();
196     }
197 
198     /** Insure that the specified accessibility events have been received. */
assertPropagated(int... events)199     public void assertPropagated(int... events) {
200         waitUntilEvent(events.length);
201         // Set up readable error reporting.
202         List<String> received = new ArrayList<>();
203         List<String> expected = new ArrayList<>();
204         for (int event : events) {
205             expected.add(AccessibilityEvent.eventTypeToString(event));
206         }
207         for (int i = 0; i < getEventsSize(); ++i) {
208             received.add(AccessibilityEvent.eventTypeToString(getEvent(i)));
209         }
210 
211         if (events.length != getEventsSize()) {
212             String message =
213                     String.format(
214                             "Received %d events when expecting %d. Received %s, expected %s",
215                             received.size(),
216                             expected.size(),
217                             received.toString(),
218                             expected.toString());
219             fail(message);
220         }
221         else if (!expected.equals(received)) {
222             String message =
223                     String.format(
224                             "Received %s, expected %s",
225                             received.toString(),
226                             expected.toString());
227             fail(message);
228         }
229     }
230 }
231