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