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