• 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 = 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
setContext(Context context)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      */
AccessibilityManagerServiceTest()88     public AccessibilityManagerServiceTest() {
89         IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
90         mManagerService = IAccessibilityManager.Stub.asInterface(iBinder);
91     }
92 
93     @LargeTest
testAddClient_AccessibilityDisabledThenEnabled()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
testAddClient_AccessibilityEnabledThenDisabled()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
testGetAccessibilityServicesList()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
testSendAccessibilityEvent_OneService_MatchingPackageAndEventType()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
testSendAccessibilityEvent_OneService_NotMatchingPackage()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
testSendAccessibilityEvent_OneService_NotMatchingEventType()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
testSendAccessibilityEvent_OneService_NotifivationAfterTimeout()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
testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_DiffFeedback()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
testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType()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
testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_OneDefault()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
testSendAccessibilityEvent_TwoServices_MatchingPackageAndEventType_TwoDefault()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
testInterrupt()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      */
fullyPopulateDefaultAccessibilityEvent(AccessibilityEvent sentEvent)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 
setState(int state)554         public void setState(int state) {
555             mState = state;
556         }
557 
setTouchExplorationEnabled(boolean enabled)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      */
ensureAccessibilityEnabled(Context context, boolean enabled)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      */
ensureOnlyMockServicesEnabled(Context context, boolean firstMockServiceEnabled, boolean secondMockServiceEnabled)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      */
assertMockServiceVerifiedWithinTimeout(MockAccessibilityService service)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          */
MyFirstMockAccessibilityService()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          */
MySecondMockAccessibilityService()737         public MySecondMockAccessibilityService() {
738             sInstance = this;
739         }
740     }
741 }
742