AccessibilityManagerServiceTest.java revision 383742c16d3dcbfcf748a89f0cb88ac8d2b31bb5
1344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com/*
2344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com * Copyright (C) 2010 The Android Open Source Project
3344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com *
4344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com * Licensed under the Apache License, Version 2.0 (the "License");
5344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com * you may not use this file except in compliance with the License.
6344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com * You may obtain a copy of the License at
7344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com *
8344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com *      http://www.apache.org/licenses/LICENSE-2.0
9344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com *
10344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com * Unless required by applicable law or agreed to in writing, software
11344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com * distributed under the License is distributed on an "AS IS" BASIS,
12344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com * See the License for the specific language governing permissions and
14344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com * limitations under the License.
15344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com */
16344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com
17344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.compackage com.android.server;
18344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com
19344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.accessibilityservice.AccessibilityService;
20344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.accessibilityservice.AccessibilityServiceInfo;
21344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.content.ComponentName;
22344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.content.Context;
23344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.content.pm.ServiceInfo;
24344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.os.IBinder;
25344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.os.Message;
26344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.os.ServiceManager;
27344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.os.SystemClock;
28344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.provider.Settings;
29344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.test.AndroidTestCase;
30344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.test.suitebuilder.annotation.LargeTest;
31344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.view.accessibility.AccessibilityEvent;
32344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.view.accessibility.IAccessibilityManager;
33344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.comimport android.view.accessibility.IAccessibilityManagerClient;
34344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com
35344caf0bfb20161349dbf2f658a7f096a08ea76plundblad@google.com/**
36 * This test exercises the
37 * {@link com.android.server.AccessibilityManagerService} by mocking the
38 * {@link android.view.accessibility.AccessibilityManager} which talks to to the
39 * service. The service itself is interacting with the platform. Note: Testing
40 * the service in full isolation would require significant amount of work for
41 * mocking all system interactions. It would also require a lot of mocking code.
42 */
43public class AccessibilityManagerServiceTest extends AndroidTestCase {
44
45    /**
46     * Timeout required for pending Binder calls or event processing to
47     * complete.
48     */
49    private static final long TIMEOUT_BINDER_CALL = 100;
50
51    /**
52     * Timeout in which we are waiting for the system to start the mock
53     * accessibility services.
54     */
55    private static final long TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES = 300;
56
57    /**
58     * Timeout used for testing that a service is notified only upon a
59     * notification timeout.
60     */
61    private static final long TIMEOUT_TEST_NOTIFICATION_TIMEOUT = 300;
62
63    /**
64     * The interface used to talk to the tested service.
65     */
66    private IAccessibilityManager mManagerService;
67
68    @Override
69    public void setContext(Context context) {
70        super.setContext(context);
71        if (MyFirstMockAccessibilityService.sComponentName == null) {
72            MyFirstMockAccessibilityService.sComponentName = new ComponentName(
73                    context.getPackageName(), MyFirstMockAccessibilityService.class.getName())
74                    .flattenToShortString();
75        }
76        if (MySecondMockAccessibilityService.sComponentName == null) {
77            MySecondMockAccessibilityService.sComponentName = new ComponentName(
78                    context.getPackageName(), MySecondMockAccessibilityService.class.getName())
79                    .flattenToShortString();
80        }
81    }
82
83    /**
84     * Creates a new instance.
85     */
86    public AccessibilityManagerServiceTest() {
87        IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
88        mManagerService = IAccessibilityManager.Stub.asInterface(iBinder);
89    }
90
91    @LargeTest
92    public void testAddClient_AccessibilityDisabledThenEnabled() throws Exception {
93        // make sure accessibility is disabled
94        ensureAccessibilityEnabled(mContext, false);
95
96        // create a client mock instance
97        MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
98
99        // invoke the method under test
100        boolean enabledAccessibilityDisabled = mManagerService.addClient(mockClient);
101
102        // check expected result
103        assertFalse("The client must be disabled since accessibility is disabled.",
104                enabledAccessibilityDisabled);
105
106        // enable accessibility
107        ensureAccessibilityEnabled(mContext, true);
108
109        // invoke the method under test
110        boolean enabledAccessibilityEnabled = mManagerService.addClient(mockClient);
111
112        // check expected result
113        assertTrue("The client must be enabled since accessibility is enabled.",
114                enabledAccessibilityEnabled);
115    }
116
117    @LargeTest
118    public void testAddClient_AccessibilityEnabledThenDisabled() throws Exception {
119        // enable accessibility before registering the client
120        ensureAccessibilityEnabled(mContext, true);
121
122        // create a client mock instance
123        MyMockAccessibilityManagerClient mockClient = new MyMockAccessibilityManagerClient();
124
125        // invoke the method under test
126        boolean enabledAccessibilityEnabled = mManagerService.addClient(mockClient);
127
128        // check expected result
129        assertTrue("The client must be enabled since accessibility is enabled.",
130                enabledAccessibilityEnabled);
131
132        // disable accessibility
133        ensureAccessibilityEnabled(mContext, false);
134
135        // invoke the method under test
136        boolean enabledAccessibilityDisabled = mManagerService.addClient(mockClient);
137
138        // check expected result
139        assertFalse("The client must be disabled since accessibility is disabled.",
140                enabledAccessibilityDisabled);
141    }
142
143    @LargeTest
144    public void testGetAccessibilityServicesList() throws Exception {
145        boolean firstMockServiceInstalled = false;
146        boolean secondMockServiceInstalled = false;
147
148        String packageName = getContext().getPackageName();
149        String firstMockServiceClassName = MyFirstMockAccessibilityService.class.getName();
150        String secondMockServiceClassName = MySecondMockAccessibilityService.class.getName();
151
152        // look for the two mock services
153        for (ServiceInfo serviceInfo : mManagerService.getAccessibilityServiceList()) {
154            if (packageName.equals(serviceInfo.packageName)) {
155                if (firstMockServiceClassName.equals(serviceInfo.name)) {
156                    firstMockServiceInstalled = true;
157                } else if (secondMockServiceClassName.equals(serviceInfo.name)) {
158                    secondMockServiceInstalled = true;
159                }
160            }
161        }
162
163        // check expected result
164        assertTrue("First mock service must be installed", firstMockServiceInstalled);
165        assertTrue("Second mock service must be installed", secondMockServiceInstalled);
166    }
167
168    @LargeTest
169    public void testSendAccessibilityEvent_OneService_MatchingPackageAndEventType()
170            throws Exception {
171        // set the accessibility setting value
172        ensureAccessibilityEnabled(mContext, true);
173
174        // enable the mock accessibility service
175        ensureOnlyMockServicesEnabled(mContext, true, false);
176
177        // configure the mock service
178        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
179        service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
180
181        // wait for the binder call to #setService to complete
182        Thread.sleep(TIMEOUT_BINDER_CALL);
183
184        // create and populate an event to be sent
185        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
186        fullyPopulateDefaultAccessibilityEvent(sentEvent);
187
188        // set expectations
189        service.expectEvent(sentEvent);
190        service.replay();
191
192        // send the event
193        mManagerService.sendAccessibilityEvent(sentEvent);
194
195        // verify if all expected methods have been called
196        assertMockServiceVerifiedWithinTimeout(service);
197    }
198
199    @LargeTest
200    public void testSendAccessibilityEvent_OneService_NotMatchingPackage() throws Exception {
201        // set the accessibility setting value
202        ensureAccessibilityEnabled(mContext, true);
203
204        // enable the mock accessibility service
205        ensureOnlyMockServicesEnabled(mContext, true, false);
206
207        // configure the mock service
208        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
209        service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
210
211        // wait for the binder call to #setService to complete
212        Thread.sleep(TIMEOUT_BINDER_CALL);
213
214        // create and populate an event to be sent
215        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
216        fullyPopulateDefaultAccessibilityEvent(sentEvent);
217        sentEvent.setPackageName("no.service.registered.for.this.package");
218
219        // set expectations
220        service.replay();
221
222        // send the event
223        mManagerService.sendAccessibilityEvent(sentEvent);
224
225        // verify if all expected methods have been called
226        assertMockServiceVerifiedWithinTimeout(service);
227    }
228
229    @LargeTest
230    public void testSendAccessibilityEvent_OneService_NotMatchingEventType() throws Exception {
231        // set the accessibility setting value
232        ensureAccessibilityEnabled(mContext, true);
233
234        // enable the mock accessibility service
235        ensureOnlyMockServicesEnabled(mContext, true, false);
236
237        // configure the mock service
238        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
239        service.setServiceInfo(MockAccessibilityService.createDefaultInfo());
240
241        // wait for the binder call to #setService to complete
242        Thread.sleep(TIMEOUT_BINDER_CALL);
243
244        // create and populate an event to be sent
245        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
246        fullyPopulateDefaultAccessibilityEvent(sentEvent);
247        sentEvent.setEventType(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
248
249        // set expectations
250        service.replay();
251
252        // send the event
253        mManagerService.sendAccessibilityEvent(sentEvent);
254
255        // verify if all expected methods have been called
256        assertMockServiceVerifiedWithinTimeout(service);
257    }
258
259    @LargeTest
260    public void testSendAccessibilityEvent_OneService_NotifivationAfterTimeout() throws Exception {
261        // set the accessibility setting value
262        ensureAccessibilityEnabled(mContext, true);
263
264        // enable the mock accessibility service
265        ensureOnlyMockServicesEnabled(mContext, true, false);
266
267        // configure the mock service
268        MockAccessibilityService service = MyFirstMockAccessibilityService.sInstance;
269        AccessibilityServiceInfo info = MockAccessibilityService.createDefaultInfo();
270        info.notificationTimeout = TIMEOUT_TEST_NOTIFICATION_TIMEOUT;
271        service.setServiceInfo(info);
272
273        // wait for the binder call to #setService to complete
274        Thread.sleep(TIMEOUT_BINDER_CALL);
275
276        // create and populate the first event to be sent
277        AccessibilityEvent firstEvent = AccessibilityEvent.obtain();
278        fullyPopulateDefaultAccessibilityEvent(firstEvent);
279
280        // create and populate the second event to be sent
281        AccessibilityEvent secondEvent = AccessibilityEvent.obtain();
282        fullyPopulateDefaultAccessibilityEvent(secondEvent);
283
284        // set expectations
285        service.expectEvent(secondEvent);
286        service.replay();
287
288        // send the events
289        mManagerService.sendAccessibilityEvent(firstEvent);
290        mManagerService.sendAccessibilityEvent(secondEvent);
291
292        // wait for #sendAccessibilityEvent to reach the backing service
293        Thread.sleep(TIMEOUT_BINDER_CALL);
294
295        try {
296            service.verify();
297            fail("No events must be dispatched before the expiration of the notification timeout.");
298        } catch (IllegalStateException ise) {
299            /* expected */
300        }
301
302        // wait for the configured notification timeout to expire
303        Thread.sleep(TIMEOUT_TEST_NOTIFICATION_TIMEOUT);
304
305        // verify if all expected methods have been called
306        assertMockServiceVerifiedWithinTimeout(service);
307    }
308
309    @LargeTest
310    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_DiffFeedback()
311            throws Exception {
312        // set the accessibility setting value
313        ensureAccessibilityEnabled(mContext, true);
314
315        // enable the mock accessibility services
316        ensureOnlyMockServicesEnabled(mContext, true, true);
317
318        // configure the first mock service
319        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
320        AccessibilityServiceInfo firstInfo = MockAccessibilityService.createDefaultInfo();
321        firstInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
322        firstService.setServiceInfo(firstInfo);
323
324        // configure the second mock service
325        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
326        AccessibilityServiceInfo secondInfo = MockAccessibilityService.createDefaultInfo();
327        secondInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_HAPTIC;
328        secondService.setServiceInfo(secondInfo);
329
330        // wait for the binder calls to #setService to complete
331        Thread.sleep(TIMEOUT_BINDER_CALL);
332
333        // create and populate an event to be sent
334        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
335        fullyPopulateDefaultAccessibilityEvent(sentEvent);
336
337        // set expectations for the first mock service
338        firstService.expectEvent(sentEvent);
339        firstService.replay();
340
341        // set expectations for the second mock service
342        secondService.expectEvent(sentEvent);
343        secondService.replay();
344
345        // send the event
346        mManagerService.sendAccessibilityEvent(sentEvent);
347
348        // verify if all expected methods have been called
349        assertMockServiceVerifiedWithinTimeout(firstService);
350        assertMockServiceVerifiedWithinTimeout(secondService);
351    }
352
353    @LargeTest
354    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType()
355            throws Exception {
356        // set the accessibility setting value
357        ensureAccessibilityEnabled(mContext, true);
358
359        // enable the mock accessibility services
360        ensureOnlyMockServicesEnabled(mContext, true, true);
361
362        // configure the first mock service
363        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
364        firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
365
366        // configure the second mock service
367        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
368        secondService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
369
370        // wait for the binder calls to #setService to complete
371        Thread.sleep(TIMEOUT_BINDER_CALL);
372
373        // create and populate an event to be sent
374        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
375        fullyPopulateDefaultAccessibilityEvent(sentEvent);
376
377        // set expectations for the first mock service
378        firstService.expectEvent(sentEvent);
379        firstService.replay();
380
381        // set expectations for the second mock service
382        secondService.replay();
383
384        // send the event
385        mManagerService.sendAccessibilityEvent(sentEvent);
386
387        // verify if all expected methods have been called
388        assertMockServiceVerifiedWithinTimeout(firstService);
389        assertMockServiceVerifiedWithinTimeout(secondService);
390    }
391
392    @LargeTest
393    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_OneDefault()
394            throws Exception {
395        // set the accessibility setting value
396        ensureAccessibilityEnabled(mContext, true);
397
398        // enable the mock accessibility services
399        ensureOnlyMockServicesEnabled(mContext, true, true);
400
401        // configure the first mock service
402        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
403        AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo();
404        firstInfo.flags = AccessibilityServiceInfo.DEFAULT;
405        firstService.setServiceInfo(firstInfo);
406
407        // configure the second mock service
408        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
409        secondService.setServiceInfo(MySecondMockAccessibilityService.createDefaultInfo());
410
411        // wait for the binder calls to #setService to complete
412        Thread.sleep(TIMEOUT_BINDER_CALL);
413
414        // create and populate an event to be sent
415        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
416        fullyPopulateDefaultAccessibilityEvent(sentEvent);
417
418        // set expectations for the first mock service
419        firstService.replay();
420
421        // set expectations for the second mock service
422        secondService.expectEvent(sentEvent);
423        secondService.replay();
424
425        // send the event
426        mManagerService.sendAccessibilityEvent(sentEvent);
427
428        // verify if all expected methods have been called
429        assertMockServiceVerifiedWithinTimeout(firstService);
430        assertMockServiceVerifiedWithinTimeout(secondService);
431    }
432
433    @LargeTest
434    public void testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_TwoDefault()
435            throws Exception {
436        // set the accessibility setting value
437        ensureAccessibilityEnabled(mContext, true);
438
439        // enable the mock accessibility services
440        ensureOnlyMockServicesEnabled(mContext, true, true);
441
442        // configure the first mock service
443        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
444        AccessibilityServiceInfo firstInfo = MyFirstMockAccessibilityService.createDefaultInfo();
445        firstInfo.flags = AccessibilityServiceInfo.DEFAULT;
446        firstService.setServiceInfo(firstInfo);
447
448        // configure the second mock service
449        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
450        AccessibilityServiceInfo secondInfo = MyFirstMockAccessibilityService.createDefaultInfo();
451        secondInfo.flags = AccessibilityServiceInfo.DEFAULT;
452        secondService.setServiceInfo(firstInfo);
453
454        // wait for the binder calls to #setService to complete
455        Thread.sleep(TIMEOUT_BINDER_CALL);
456
457        // create and populate an event to be sent
458        AccessibilityEvent sentEvent = AccessibilityEvent.obtain();
459        fullyPopulateDefaultAccessibilityEvent(sentEvent);
460
461        // set expectations for the first mock service
462        firstService.expectEvent(sentEvent);
463        firstService.replay();
464
465        // set expectations for the second mock service
466        secondService.replay();
467
468        // send the event
469        mManagerService.sendAccessibilityEvent(sentEvent);
470
471        // verify if all expected methods have been called
472        assertMockServiceVerifiedWithinTimeout(firstService);
473        assertMockServiceVerifiedWithinTimeout(secondService);
474    }
475
476    @LargeTest
477    public void testInterrupt() throws Exception {
478        // set the accessibility setting value
479        ensureAccessibilityEnabled(mContext, true);
480
481        // enable the mock accessibility services
482        ensureOnlyMockServicesEnabled(mContext, true, true);
483
484        // configure the first mock service
485        MockAccessibilityService firstService = MyFirstMockAccessibilityService.sInstance;
486        firstService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
487
488        // configure the second mock service
489        MockAccessibilityService secondService = MySecondMockAccessibilityService.sInstance;
490        secondService.setServiceInfo(MockAccessibilityService.createDefaultInfo());
491
492        // wait for the binder calls to #setService to complete
493        Thread.sleep(TIMEOUT_BINDER_CALL);
494
495        // set expectations for the first mock service
496        firstService.expectInterrupt();
497        firstService.replay();
498
499        // set expectations for the second mock service
500        secondService.expectInterrupt();
501        secondService.replay();
502
503        // call the method under test
504        mManagerService.interrupt();
505
506        // verify if all expected methods have been called
507        assertMockServiceVerifiedWithinTimeout(firstService);
508        assertMockServiceVerifiedWithinTimeout(secondService);
509    }
510
511    /**
512     * Fully populates the {@link AccessibilityEvent} to marshal.
513     *
514     * @param sentEvent The event to populate.
515     */
516    private void fullyPopulateDefaultAccessibilityEvent(AccessibilityEvent sentEvent) {
517        sentEvent.setAddedCount(1);
518        sentEvent.setBeforeText("BeforeText");
519        sentEvent.setChecked(true);
520        sentEvent.setClassName("foo.bar.baz.Class");
521        sentEvent.setContentDescription("ContentDescription");
522        sentEvent.setCurrentItemIndex(1);
523        sentEvent.setEnabled(true);
524        sentEvent.setEventType(AccessibilityEvent.TYPE_VIEW_CLICKED);
525        sentEvent.setEventTime(1000);
526        sentEvent.setFromIndex(1);
527        sentEvent.setFullScreen(true);
528        sentEvent.setItemCount(1);
529        sentEvent.setPackageName("foo.bar.baz");
530        sentEvent.setParcelableData(Message.obtain(null, 1, null));
531        sentEvent.setPassword(true);
532        sentEvent.setRemovedCount(1);
533    }
534
535    /**
536     * This class is a mock {@link IAccessibilityManagerClient}.
537     */
538    public class MyMockAccessibilityManagerClient extends IAccessibilityManagerClient.Stub {
539        boolean mIsEnabled;
540
541        public void setEnabled(boolean enabled) {
542            mIsEnabled = enabled;
543        }
544    }
545
546    /**
547     * Ensures accessibility is in a given state by writing the state to the
548     * settings and waiting until the accessibility manager service pick it up.
549     *
550     * @param context A context handle to access the settings.
551     * @param enabled The accessibility state to write to the settings.
552     * @throws Exception If any error occurs.
553     */
554    private void ensureAccessibilityEnabled(Context context, boolean enabled) throws Exception {
555        boolean isEnabled = (Settings.Secure.getInt(context.getContentResolver(),
556                Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1 ? true : false);
557
558        if (isEnabled == enabled) {
559            return;
560        }
561
562        Settings.Secure.putInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED,
563                enabled ? 1 : 0);
564
565        // wait the accessibility manager service to pick the change up
566        Thread.sleep(TIMEOUT_BINDER_CALL);
567    }
568
569    /**
570     * Ensures the only {@link MockAccessibilityService}s with given component
571     * names are enabled by writing to the system settings and waiting until the
572     * accessibility manager service picks that up or the
573     * {@link #TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES} is exceeded.
574     *
575     * @param context A context handle to access the settings.
576     * @param firstMockServiceEnabled If the first mock accessibility service is enabled.
577     * @param secondMockServiceEnabled If the second mock accessibility service is enabled.
578     * @throws IllegalStateException If some of the requested for enabling mock services
579     *         is not properly started.
580     * @throws Exception Exception If any error occurs.
581     */
582    private void ensureOnlyMockServicesEnabled(Context context, boolean firstMockServiceEnabled,
583            boolean secondMockServiceEnabled) throws Exception {
584        String enabledServices = Settings.Secure.getString(context.getContentResolver(),
585                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
586
587        StringBuilder servicesToEnable = new StringBuilder();
588        if (firstMockServiceEnabled) {
589            servicesToEnable.append(MyFirstMockAccessibilityService.sComponentName).append(":");
590        }
591        if (secondMockServiceEnabled) {
592            servicesToEnable.append(MySecondMockAccessibilityService.sComponentName).append(":");
593        }
594
595        if (servicesToEnable.equals(enabledServices)) {
596            return;
597        }
598
599        Settings.Secure.putString(context.getContentResolver(),
600                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, servicesToEnable.toString());
601
602        // we have enabled the services of interest and need to wait until they
603        // are instantiated and started (if needed) and the system binds to them
604        boolean firstMockServiceOK = false;
605        boolean secondMockServiceOK = false;
606        long start = SystemClock.uptimeMillis();
607        long pollingInterval = TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES / 6;
608
609        while (SystemClock.uptimeMillis() - start < TIMEOUT_START_MOCK_ACCESSIBILITY_SERVICES)  {
610            firstMockServiceOK = !firstMockServiceEnabled
611                    || (MyFirstMockAccessibilityService.sInstance != null
612                    && MyFirstMockAccessibilityService.sInstance.isSystemBoundAsClient());
613
614            secondMockServiceOK = !secondMockServiceEnabled
615                    || (MySecondMockAccessibilityService.sInstance != null
616                    && MySecondMockAccessibilityService.sInstance.isSystemBoundAsClient());
617
618            if (firstMockServiceOK && secondMockServiceOK) {
619                return;
620            }
621
622            Thread.sleep(pollingInterval);
623        }
624
625        StringBuilder message = new StringBuilder();
626        message.append("Mock accessibility services not started or system not bound as a client: ");
627        if (!firstMockServiceOK) {
628            message.append(MyFirstMockAccessibilityService.sComponentName);
629            message.append(" ");
630        }
631        if (!secondMockServiceOK) {
632            message.append(MySecondMockAccessibilityService.sComponentName);
633        }
634        throw new IllegalStateException(message.toString());
635    }
636
637    /**
638     * Asserts the the mock accessibility service has been successfully verified
639     * (which is it has received the expected method calls with expected
640     * arguments) within the {@link #TIMEOUT_BINDER_CALL}. The verified state is
641     * checked by polling upon small intervals.
642     *
643     * @param service The service to verify.
644     * @throws Exception If the verification has failed with exception after the
645     *             {@link #TIMEOUT_BINDER_CALL}.
646     */
647    private void assertMockServiceVerifiedWithinTimeout(MockAccessibilityService service)
648            throws Exception {
649        Exception lastVerifyException = null;
650        long beginTime = SystemClock.uptimeMillis();
651        long pollTmeout = TIMEOUT_BINDER_CALL / 5;
652
653        // poll until the timeout has elapsed
654        while (SystemClock.uptimeMillis() - beginTime < TIMEOUT_BINDER_CALL) {
655            // sleep first since immediate call will always fail
656            try {
657                Thread.sleep(pollTmeout);
658            } catch (InterruptedException ie) {
659                /* ignore */
660            }
661            // poll for verification and if this fails save the exception and
662            // keep polling
663            try {
664                service.verify();
665                // reset so it does not accept more events
666                service.reset();
667                return;
668            } catch (Exception e) {
669                lastVerifyException = e;
670            }
671        }
672
673        // reset, we have already failed
674        service.reset();
675
676        // always not null
677        throw lastVerifyException;
678    }
679
680    /**
681     * This class is the first mock {@link AccessibilityService}.
682     */
683    public static class MyFirstMockAccessibilityService extends MockAccessibilityService {
684
685        /**
686         * The service {@link ComponentName} flattened as a string.
687         */
688        static String sComponentName;
689
690        /**
691         * Handle to the service instance.
692         */
693        static MyFirstMockAccessibilityService sInstance;
694
695        /**
696         * Creates a new instance.
697         */
698        public MyFirstMockAccessibilityService() {
699            sInstance = this;
700        }
701    }
702
703    /**
704     * This class is the first mock {@link AccessibilityService}.
705     */
706    public static class MySecondMockAccessibilityService extends MockAccessibilityService {
707
708        /**
709         * The service {@link ComponentName} flattened as a string.
710         */
711        static String sComponentName;
712
713        /**
714         * Handle to the service instance.
715         */
716        static MySecondMockAccessibilityService sInstance;
717
718        /**
719         * Creates a new instance.
720         */
721        public MySecondMockAccessibilityService() {
722            sInstance = this;
723        }
724    }
725}
726