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