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