1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
19import static org.easymock.EasyMock.createStrictMock;
20import static org.easymock.EasyMock.expect;
21import static org.easymock.EasyMock.replay;
22import static org.easymock.EasyMock.reportMatcher;
23import static org.easymock.EasyMock.reset;
24import static org.easymock.EasyMock.verify;
25
26import org.easymock.IArgumentMatcher;
27
28import android.accessibilityservice.AccessibilityServiceInfo;
29import android.os.UserHandle;
30import android.test.AndroidTestCase;
31import android.test.suitebuilder.annotation.LargeTest;
32import android.test.suitebuilder.annotation.MediumTest;
33import android.view.accessibility.AccessibilityEvent;
34import android.view.accessibility.AccessibilityManager;
35import android.view.accessibility.IAccessibilityManager;
36import android.view.accessibility.IAccessibilityManagerClient;
37
38import java.util.ArrayList;
39import java.util.List;
40
41/**
42 * Tests for the AccessibilityManager which mocking the backing service.
43 */
44public class AccessibilityManagerTest extends AndroidTestCase {
45
46    /**
47     * Timeout required for pending Binder calls or event processing to
48     * complete.
49     */
50    public static final long TIMEOUT_BINDER_CALL = 50;
51
52    /**
53     * The reusable mock {@link IAccessibilityManager}.
54     */
55    private final IAccessibilityManager mMockServiceInterface =
56        createStrictMock(IAccessibilityManager.class);
57
58    @Override
59    public void setUp() throws Exception {
60        reset(mMockServiceInterface);
61    }
62
63    @MediumTest
64    public void testGetAccessibilityServiceList() throws Exception {
65        // create a list of installed accessibility services the mock service returns
66        List<AccessibilityServiceInfo> expectedServices = new ArrayList<AccessibilityServiceInfo>();
67        AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
68        accessibilityServiceInfo.packageNames = new String[] { "foo.bar" };
69        expectedServices.add(accessibilityServiceInfo);
70
71        // configure the mock service behavior
72        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
73        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
74                UserHandle.USER_OWNER)).andReturn(
75                AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
76        expect(mockServiceInterface.getInstalledAccessibilityServiceList(UserHandle.USER_OWNER))
77                .andReturn(expectedServices);
78        replay(mockServiceInterface);
79
80        // invoke the method under test
81        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
82                UserHandle.USER_OWNER);
83        List<AccessibilityServiceInfo> receivedServices =
84            manager.getInstalledAccessibilityServiceList();
85
86        // check expected result (list equals() compares it contents as well)
87        assertEquals("All expected services must be returned", receivedServices, expectedServices);
88
89        // verify the mock service was properly called
90        verify(mockServiceInterface);
91    }
92
93    @MediumTest
94    public void testInterrupt() throws Exception {
95        // configure the mock service behavior
96        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
97        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
98                UserHandle.USER_OWNER)).andReturn(
99                        AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
100        mockServiceInterface.interrupt(UserHandle.USER_OWNER);
101        replay(mockServiceInterface);
102
103        // invoke the method under test
104        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
105                UserHandle.USER_OWNER);
106        manager.interrupt();
107
108        // verify the mock service was properly called
109        verify(mockServiceInterface);
110    }
111
112    @LargeTest
113    public void testIsEnabled() throws Exception {
114        // configure the mock service behavior
115        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
116        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
117                UserHandle.USER_OWNER)).andReturn(
118                        AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
119        replay(mockServiceInterface);
120
121        // invoke the method under test
122        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
123                UserHandle.USER_OWNER);
124        boolean isEnabledServiceEnabled = manager.isEnabled();
125
126        // check expected result
127        assertTrue("Must be enabled since the mock service is enabled", isEnabledServiceEnabled);
128
129        // disable accessibility
130        manager.getClient().setState(0);
131
132        // wait for the asynchronous IBinder call to complete
133        Thread.sleep(TIMEOUT_BINDER_CALL);
134
135        // invoke the method under test
136        boolean isEnabledServcieDisabled = manager.isEnabled();
137
138        // check expected result
139        assertFalse("Must be disabled since the mock service is disabled",
140                isEnabledServcieDisabled);
141
142        // verify the mock service was properly called
143        verify(mockServiceInterface);
144    }
145
146    @MediumTest
147    public void testSendAccessibilityEvent_AccessibilityEnabled() throws Exception {
148        // create an event to be dispatched
149        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
150
151        // configure the mock service behavior
152        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
153        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
154                UserHandle.USER_OWNER)).andReturn(
155                        AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED);
156        expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent),
157                UserHandle.USER_OWNER)).andReturn(true);
158        expect(mockServiceInterface.sendAccessibilityEvent(eqAccessibilityEvent(sentEvent),
159                UserHandle.USER_OWNER)).andReturn(false);
160        replay(mockServiceInterface);
161
162        // invoke the method under test (manager and service in different processes)
163        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
164                UserHandle.USER_OWNER);
165        manager.sendAccessibilityEvent(sentEvent);
166
167        // check expected result
168        AccessibilityEvent nextEventDifferentProcesses = AccessibilityEvent.obtain();
169        assertSame("The manager and the service are in different processes, so the event must be " +
170                "recycled", sentEvent, nextEventDifferentProcesses);
171
172        // invoke the method under test (manager and service in the same process)
173        manager.sendAccessibilityEvent(sentEvent);
174
175        // check expected result
176        AccessibilityEvent nextEventSameProcess = AccessibilityEvent.obtain();
177        assertNotSame("The manager and the service are in the same process, so the event must not" +
178                "be recycled", sentEvent, nextEventSameProcess);
179
180        // verify the mock service was properly called
181        verify(mockServiceInterface);
182    }
183
184    @MediumTest
185    public void testSendAccessibilityEvent_AccessibilityDisabled() throws Exception {
186        // create an event to be dispatched
187        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
188
189        // configure the mock service behavior
190        IAccessibilityManager mockServiceInterface = mMockServiceInterface;
191        expect(mockServiceInterface.addClient(anyIAccessibilityManagerClient(),
192                UserHandle.USER_OWNER)).andReturn(0);
193        replay(mockServiceInterface);
194
195        // invoke the method under test (accessibility disabled)
196        AccessibilityManager manager = new AccessibilityManager(mContext, mockServiceInterface,
197                UserHandle.USER_OWNER);
198        try {
199            manager.sendAccessibilityEvent(sentEvent);
200            fail("No accessibility events are sent if accessibility is disabled");
201        } catch (IllegalStateException ise) {
202            // check expected result
203            assertEquals("Accessibility off. Did you forget to check that?", ise.getMessage());
204        }
205
206        // verify the mock service was properly called
207        verify(mockServiceInterface);
208    }
209
210    /**
211     * Determines if an {@link AccessibilityEvent} passed as a method argument
212     * matches expectations.
213     *
214     * @param matched The event to check.
215     * @return True if expectations are matched.
216     */
217    private static AccessibilityEvent eqAccessibilityEvent(AccessibilityEvent matched) {
218        reportMatcher(new AccessibilityEventMather(matched));
219        return null;
220    }
221
222    /**
223     * Determines if an {@link IAccessibilityManagerClient} passed as a method argument
224     * matches expectations which in this case are that any instance is accepted.
225     *
226     * @return <code>null</code>.
227     */
228    private static IAccessibilityManagerClient anyIAccessibilityManagerClient() {
229        reportMatcher(new AnyIAccessibilityManagerClientMather());
230        return null;
231    }
232
233    /**
234     * Matcher for {@link AccessibilityEvent}s.
235     */
236    private static class AccessibilityEventMather implements IArgumentMatcher {
237        private AccessibilityEvent mExpectedEvent;
238
239        public AccessibilityEventMather(AccessibilityEvent expectedEvent) {
240            mExpectedEvent = expectedEvent;
241        }
242
243        public boolean matches(Object matched) {
244            if (!(matched instanceof AccessibilityEvent)) {
245                return false;
246            }
247            AccessibilityEvent receivedEvent = (AccessibilityEvent) matched;
248            return mExpectedEvent.getEventType() == receivedEvent.getEventType();
249        }
250
251        public void appendTo(StringBuffer buffer) {
252            buffer.append("sendAccessibilityEvent()");
253            buffer.append(" with event type \"");
254            buffer.append(mExpectedEvent.getEventType());
255            buffer.append("\"");
256        }
257    }
258
259    /**
260     * Matcher for {@link IAccessibilityManagerClient}s.
261     */
262    private static class AnyIAccessibilityManagerClientMather implements IArgumentMatcher {
263        public boolean matches(Object matched) {
264            if (!(matched instanceof IAccessibilityManagerClient)) {
265                return false;
266            }
267            return true;
268        }
269
270        public void appendTo(StringBuffer buffer) {
271            buffer.append("addClient() with any IAccessibilityManagerClient");
272        }
273    }
274}
275