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