1 /* 2 * Copyright (C) 2012 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 android.view.accessibility.cts; 18 19 import static android.accessibility.cts.common.InstrumentedAccessibilityService.TIMEOUT_SERVICE_ENABLE; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertSame; 26 import static org.junit.Assert.assertThrows; 27 import static org.junit.Assert.assertTrue; 28 import static org.junit.Assert.fail; 29 30 31 import android.Manifest; 32 import android.accessibility.cts.common.AccessibilityDumpOnFailureRule; 33 import android.accessibility.cts.common.InstrumentedAccessibilityService; 34 import android.accessibility.cts.common.InstrumentedAccessibilityServiceTestRule; 35 import android.accessibilityservice.AccessibilityServiceInfo; 36 import android.app.Instrumentation; 37 import android.app.Service; 38 import android.app.UiAutomation; 39 import android.content.Context; 40 import android.content.pm.PackageManager; 41 import android.content.pm.ServiceInfo; 42 import android.os.Handler; 43 import android.platform.test.annotations.AsbSecurityTest; 44 import android.platform.test.annotations.RequiresFlagsEnabled; 45 import android.platform.test.flag.junit.CheckFlagsRule; 46 import android.platform.test.flag.junit.DeviceFlagsValueProvider; 47 import android.view.InputEvent; 48 import android.view.MotionEvent; 49 import android.view.accessibility.AccessibilityEvent; 50 import android.view.accessibility.AccessibilityManager; 51 import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; 52 import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; 53 import android.view.accessibility.AccessibilityManager.AudioDescriptionRequestedChangeListener; 54 import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener; 55 import android.view.accessibility.Flags; 56 57 import androidx.test.InstrumentationRegistry; 58 import androidx.test.runner.AndroidJUnit4; 59 60 import com.android.compatibility.common.util.PollingCheck; 61 import com.android.compatibility.common.util.SettingsStateChangerRule; 62 import com.android.compatibility.common.util.SystemUtil; 63 import com.android.compatibility.common.util.TestUtils; 64 import com.android.sts.common.util.StsExtraBusinessLogicTestCase; 65 66 import org.junit.Before; 67 import org.junit.Rule; 68 import org.junit.Test; 69 import org.junit.rules.RuleChain; 70 import org.junit.runner.RunWith; 71 72 import java.io.IOException; 73 import java.lang.reflect.Field; 74 import java.lang.reflect.InvocationTargetException; 75 import java.lang.reflect.Method; 76 import java.util.List; 77 import java.util.concurrent.atomic.AtomicBoolean; 78 79 /** 80 * Class for testing {@link AccessibilityManager}. 81 */ 82 @RunWith(AndroidJUnit4.class) 83 public class AccessibilityManagerTest extends StsExtraBusinessLogicTestCase { 84 85 private AccessibilityDumpOnFailureRule mDumpOnFailureRule = 86 new AccessibilityDumpOnFailureRule(); 87 88 private InstrumentedAccessibilityServiceTestRule<SpeakingAccessibilityService> 89 mSpeakingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>( 90 SpeakingAccessibilityService.class, false); 91 92 private InstrumentedAccessibilityServiceTestRule<VibratingAccessibilityService> 93 mVibratingAccessibilityServiceRule = new InstrumentedAccessibilityServiceTestRule<>( 94 VibratingAccessibilityService.class, false); 95 96 private InstrumentedAccessibilityServiceTestRule<SpeakingAndVibratingAccessibilityService> 97 mSpeakingAndVibratingAccessibilityServiceRule = 98 new InstrumentedAccessibilityServiceTestRule<>( 99 SpeakingAndVibratingAccessibilityService.class, false); 100 101 private InstrumentedAccessibilityServiceTestRule<NoFeedbackAccessibilityService> 102 mNoFeedbackAccessibilityServiceRule = 103 new InstrumentedAccessibilityServiceTestRule<>( 104 NoFeedbackAccessibilityService.class, false); 105 106 private static final Instrumentation sInstrumentation = 107 InstrumentationRegistry.getInstrumentation(); 108 109 private static final String SPEAKING_ACCESSIBLITY_SERVICE_NAME = 110 "android.view.accessibility.cts.SpeakingAccessibilityService"; 111 112 private static final String VIBRATING_ACCESSIBLITY_SERVICE_NAME = 113 "android.view.accessibility.cts.VibratingAccessibilityService"; 114 115 private static final String MULTIPLE_FEEDBACK_TYPES_ACCESSIBILITY_SERVICE_NAME = 116 "android.view.accessibility.cts.SpeakingAndVibratingAccessibilityService"; 117 118 private static final String NO_FEEDBACK_ACCESSIBILITY_SERVICE_NAME = 119 "android.view.accessibility.cts.NoFeedbackAccessibilityService"; 120 121 public static final String ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS = 122 "accessibility_non_interactive_ui_timeout_ms"; 123 124 public static final String ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS = 125 "accessibility_interactive_ui_timeout_ms"; 126 private static final String ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT = 127 "enabled_accessibility_audio_description_by_default"; 128 129 private final SettingsStateChangerRule mAudioDescriptionSetterRule = 130 new SettingsStateChangerRule( 131 sInstrumentation.getContext(), 132 ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, 133 "0"); 134 135 @Rule 136 public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); 137 138 @Rule 139 public final RuleChain mRuleChain = RuleChain 140 // SettingsStateChangerRule will suppress accessibility services, so it should be 141 // executed before enabling a11y services and after disabling a11y services. 142 .outerRule(mAudioDescriptionSetterRule) 143 .around(mNoFeedbackAccessibilityServiceRule) 144 .around(mSpeakingAndVibratingAccessibilityServiceRule) 145 .around(mVibratingAccessibilityServiceRule) 146 .around(mSpeakingAccessibilityServiceRule) 147 // Inner rule capture failure and dump data before finishing activity and a11y service 148 .around(mDumpOnFailureRule); 149 150 private AccessibilityManager mAccessibilityManager; 151 152 private Context mTargetContext; 153 154 private Handler mHandler; 155 156 @Before setUp()157 public void setUp() throws Exception { 158 mAccessibilityManager = (AccessibilityManager) 159 sInstrumentation.getContext().getSystemService(Service.ACCESSIBILITY_SERVICE); 160 mTargetContext = sInstrumentation.getTargetContext(); 161 mHandler = new Handler(mTargetContext.getMainLooper()); 162 // In case the test runner started a UiAutomation, destroy it to start with a clean slate. 163 sInstrumentation.getUiAutomation().destroy(); 164 InstrumentedAccessibilityService.disableAllServices(); 165 } 166 167 @Test testAddAndRemoveAccessibilityStateChangeListener()168 public void testAddAndRemoveAccessibilityStateChangeListener() throws Exception { 169 AccessibilityStateChangeListener listener = (state) -> { 170 /* do nothing */ 171 }; 172 assertTrue(mAccessibilityManager.addAccessibilityStateChangeListener(listener)); 173 assertTrue(mAccessibilityManager.removeAccessibilityStateChangeListener(listener)); 174 assertFalse(mAccessibilityManager.removeAccessibilityStateChangeListener(listener)); 175 } 176 177 @Test testAddAndRemoveTouchExplorationStateChangeListener()178 public void testAddAndRemoveTouchExplorationStateChangeListener() throws Exception { 179 TouchExplorationStateChangeListener listener = (boolean enabled) -> { 180 // Do nothing. 181 }; 182 assertTrue(mAccessibilityManager.addTouchExplorationStateChangeListener(listener)); 183 assertTrue(mAccessibilityManager.removeTouchExplorationStateChangeListener(listener)); 184 assertFalse(mAccessibilityManager.removeTouchExplorationStateChangeListener(listener)); 185 } 186 187 @Test testAddAndRemoveAudioDescriptionRequestedChangeListener()188 public void testAddAndRemoveAudioDescriptionRequestedChangeListener() throws Exception { 189 AudioDescriptionRequestedChangeListener listener = (boolean enabled) -> { 190 // Do nothing. 191 }; 192 mAccessibilityManager.addAudioDescriptionRequestedChangeListener( 193 mTargetContext.getMainExecutor(), listener); 194 assertTrue( 195 mAccessibilityManager.removeAudioDescriptionRequestedChangeListener(listener)); 196 assertFalse( 197 mAccessibilityManager.removeAudioDescriptionRequestedChangeListener(listener)); 198 } 199 200 @Test testIsTouchExplorationEnabled()201 public void testIsTouchExplorationEnabled() throws Exception { 202 mSpeakingAccessibilityServiceRule.enableService(); 203 mVibratingAccessibilityServiceRule.enableService(); 204 new PollingCheck() { 205 @Override 206 protected boolean check() { 207 return mAccessibilityManager.isTouchExplorationEnabled(); 208 } 209 }.run(); 210 } 211 212 @Test testRemoveAccessibilityServicesStateChangeListener()213 public void testRemoveAccessibilityServicesStateChangeListener() throws Exception { 214 AccessibilityServicesStateChangeListener listener = (state) -> { 215 /* do nothing */ 216 }; 217 mAccessibilityManager.addAccessibilityServicesStateChangeListener(listener); 218 219 assertTrue(mAccessibilityManager.removeAccessibilityServicesStateChangeListener(listener)); 220 assertFalse(mAccessibilityManager.removeAccessibilityServicesStateChangeListener(listener)); 221 } 222 223 @AsbSecurityTest(cveBugId = {309426390}) 224 @Test testInjectInputEventToInputFilter_throwsWithoutInjectEventsPermission()225 public void testInjectInputEventToInputFilter_throwsWithoutInjectEventsPermission() 226 throws Exception { 227 // Ensure the test itself doesn't have INJECT_EVENTS permission before 228 // calling the method that requires it and expecting failure. 229 assertThat(sInstrumentation.getContext().checkSelfPermission( 230 Manifest.permission.INJECT_EVENTS)).isEqualTo(PackageManager.PERMISSION_DENIED); 231 232 // Use reflection to directly invoke IAccessibilityManager#injectInputEventToInputFilter. 233 final AccessibilityManager accessibilityManager = (AccessibilityManager) 234 sInstrumentation.getContext().getSystemService(Service.ACCESSIBILITY_SERVICE); 235 final Field serviceField = AccessibilityManager.class.getDeclaredField("mService"); 236 serviceField.setAccessible(true); 237 final Method injectInputEventToInputFilter = 238 Class.forName("android.view.accessibility.IAccessibilityManager") 239 .getDeclaredMethod("injectInputEventToInputFilter", InputEvent.class); 240 241 final InvocationTargetException exception = assertThrows(InvocationTargetException.class, 242 () -> injectInputEventToInputFilter.invoke( 243 serviceField.get(accessibilityManager), 244 MotionEvent.obtain(0, 0, 0, 0, 0, 0))); 245 assertThat(exception).hasCauseThat().isInstanceOf(SecurityException.class); 246 assertThat(exception).hasCauseThat().hasMessageThat().contains("INJECT_EVENTS"); 247 } 248 249 @Test testGetInstalledAccessibilityServicesList()250 public void testGetInstalledAccessibilityServicesList() throws Exception { 251 List<AccessibilityServiceInfo> installedServices = 252 mAccessibilityManager.getInstalledAccessibilityServiceList(); 253 assertFalse("There must be at least one installed service.", installedServices.isEmpty()); 254 boolean speakingServiceInstalled = false; 255 boolean vibratingServiceInstalled = false; 256 final int serviceCount = installedServices.size(); 257 for (int i = 0; i < serviceCount; i++) { 258 AccessibilityServiceInfo installedService = installedServices.get(i); 259 ServiceInfo serviceInfo = installedService.getResolveInfo().serviceInfo; 260 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 261 && SPEAKING_ACCESSIBLITY_SERVICE_NAME.equals(serviceInfo.name)) { 262 speakingServiceInstalled = true; 263 } 264 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 265 && VIBRATING_ACCESSIBLITY_SERVICE_NAME.equals(serviceInfo.name)) { 266 vibratingServiceInstalled = true; 267 } 268 } 269 assertTrue("The speaking service should be installed.", speakingServiceInstalled); 270 assertTrue("The vibrating service should be installed.", vibratingServiceInstalled); 271 } 272 273 @Test testGetEnabledAccessibilityServiceList()274 public void testGetEnabledAccessibilityServiceList() throws Exception { 275 mSpeakingAccessibilityServiceRule.enableService(); 276 mVibratingAccessibilityServiceRule.enableService(); 277 List<AccessibilityServiceInfo> enabledServices = 278 mAccessibilityManager.getEnabledAccessibilityServiceList( 279 AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 280 boolean speakingServiceEnabled = false; 281 boolean vibratingServiceEnabled = false; 282 final int serviceCount = enabledServices.size(); 283 for (int i = 0; i < serviceCount; i++) { 284 AccessibilityServiceInfo enabledService = enabledServices.get(i); 285 ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo; 286 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 287 && SPEAKING_ACCESSIBLITY_SERVICE_NAME.equals(serviceInfo.name)) { 288 speakingServiceEnabled = true; 289 } 290 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 291 && VIBRATING_ACCESSIBLITY_SERVICE_NAME.equals(serviceInfo.name)) { 292 vibratingServiceEnabled = true; 293 } 294 } 295 assertTrue("The speaking service should be enabled.", speakingServiceEnabled); 296 assertTrue("The vibrating service should be enabled.", vibratingServiceEnabled); 297 } 298 299 @AsbSecurityTest(cveBugId = {243849844}) 300 @Test testGetEnabledAccessibilityServiceList_NoFeedback()301 public void testGetEnabledAccessibilityServiceList_NoFeedback() { 302 mNoFeedbackAccessibilityServiceRule.enableService(); 303 List<AccessibilityServiceInfo> enabledServices = 304 mAccessibilityManager.getEnabledAccessibilityServiceList( 305 AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 306 boolean noFeedbackServiceEnabled = false; 307 final int serviceCount = enabledServices.size(); 308 for (int i = 0; i < serviceCount; i++) { 309 AccessibilityServiceInfo enabledService = enabledServices.get(i); 310 ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo; 311 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 312 && NO_FEEDBACK_ACCESSIBILITY_SERVICE_NAME.equals(serviceInfo.name)) { 313 noFeedbackServiceEnabled = true; 314 } 315 } 316 assertTrue("The no-feedback service should be enabled.", noFeedbackServiceEnabled); 317 } 318 319 @Test testGetEnabledAccessibilityServiceListForType()320 public void testGetEnabledAccessibilityServiceListForType() throws Exception { 321 mSpeakingAccessibilityServiceRule.enableService(); 322 mVibratingAccessibilityServiceRule.enableService(); 323 List<AccessibilityServiceInfo> enabledServices = 324 mAccessibilityManager.getEnabledAccessibilityServiceList( 325 AccessibilityServiceInfo.FEEDBACK_SPOKEN); 326 assertSame("There should be only one enabled speaking service.", 1, enabledServices.size()); 327 final int serviceCount = enabledServices.size(); 328 for (int i = 0; i < serviceCount; i++) { 329 AccessibilityServiceInfo enabledService = enabledServices.get(i); 330 ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo; 331 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 332 && SPEAKING_ACCESSIBLITY_SERVICE_NAME.equals(serviceInfo.name)) { 333 return; 334 } 335 } 336 fail("The speaking service is not enabled."); 337 } 338 339 @Test testGetEnabledAccessibilityServiceListForTypes()340 public void testGetEnabledAccessibilityServiceListForTypes() throws Exception { 341 mSpeakingAccessibilityServiceRule.enableService(); 342 mVibratingAccessibilityServiceRule.enableService(); 343 // For this test, also enable a service with multiple feedback types 344 mSpeakingAndVibratingAccessibilityServiceRule.enableService(); 345 346 List<AccessibilityServiceInfo> enabledServices = 347 mAccessibilityManager.getEnabledAccessibilityServiceList( 348 AccessibilityServiceInfo.FEEDBACK_SPOKEN 349 | AccessibilityServiceInfo.FEEDBACK_HAPTIC); 350 assertSame("There should be 3 enabled accessibility services.", 3, enabledServices.size()); 351 boolean speakingServiceEnabled = false; 352 boolean vibratingServiceEnabled = false; 353 boolean multipleFeedbackTypesServiceEnabled = false; 354 final int serviceCount = enabledServices.size(); 355 for (int i = 0; i < serviceCount; i++) { 356 AccessibilityServiceInfo enabledService = enabledServices.get(i); 357 ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo; 358 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 359 && SPEAKING_ACCESSIBLITY_SERVICE_NAME.equals(serviceInfo.name)) { 360 speakingServiceEnabled = true; 361 } 362 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 363 && VIBRATING_ACCESSIBLITY_SERVICE_NAME.equals(serviceInfo.name)) { 364 vibratingServiceEnabled = true; 365 } 366 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 367 && MULTIPLE_FEEDBACK_TYPES_ACCESSIBILITY_SERVICE_NAME.equals( 368 serviceInfo.name)) { 369 multipleFeedbackTypesServiceEnabled = true; 370 } 371 } 372 assertTrue("The speaking service should be enabled.", speakingServiceEnabled); 373 assertTrue("The vibrating service should be enabled.", vibratingServiceEnabled); 374 assertTrue("The multiple feedback types service should be enabled.", 375 multipleFeedbackTypesServiceEnabled); 376 } 377 378 @SuppressWarnings("deprecation") 379 @Test testGetAccessibilityServiceList()380 public void testGetAccessibilityServiceList() throws Exception { 381 List<ServiceInfo> services = mAccessibilityManager.getAccessibilityServiceList(); 382 boolean speakingServiceInstalled = false; 383 boolean vibratingServiceInstalled = false; 384 final int serviceCount = services.size(); 385 for (int i = 0; i < serviceCount; i++) { 386 ServiceInfo serviceInfo = services.get(i); 387 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 388 && SPEAKING_ACCESSIBLITY_SERVICE_NAME.equals(serviceInfo.name)) { 389 speakingServiceInstalled = true; 390 } 391 if (mTargetContext.getPackageName().equals(serviceInfo.packageName) 392 && VIBRATING_ACCESSIBLITY_SERVICE_NAME.equals(serviceInfo.name)) { 393 vibratingServiceInstalled = true; 394 } 395 } 396 assertTrue("The speaking service should be installed.", speakingServiceInstalled); 397 assertTrue("The vibrating service should be installed.", vibratingServiceInstalled); 398 } 399 400 @Test testInterrupt()401 public void testInterrupt() throws Exception { 402 // The APIs are heavily tested in the android.accessibilityservice package. 403 // This just makes sure the call does not throw an exception. 404 mSpeakingAccessibilityServiceRule.enableService(); 405 mVibratingAccessibilityServiceRule.enableService(); 406 waitForAccessibilityEnabled(); 407 mAccessibilityManager.interrupt(); 408 } 409 410 @Test testSendAccessibilityEvent()411 public void testSendAccessibilityEvent() throws Exception { 412 // The APIs are heavily tested in the android.accessibilityservice package. 413 // This just makes sure the call does not throw an exception. 414 mSpeakingAccessibilityServiceRule.enableService(); 415 mVibratingAccessibilityServiceRule.enableService(); 416 waitForAccessibilityEnabled(); 417 mAccessibilityManager.sendAccessibilityEvent(AccessibilityEvent.obtain( 418 AccessibilityEvent.TYPE_VIEW_CLICKED)); 419 } 420 421 @Test testTouchExplorationListenerNoHandler()422 public void testTouchExplorationListenerNoHandler() throws Exception { 423 final Object waitObject = new Object(); 424 final AtomicBoolean atomicBoolean = new AtomicBoolean(false); 425 426 TouchExplorationStateChangeListener listener = (boolean b) -> { 427 synchronized (waitObject) { 428 atomicBoolean.set(b); 429 waitObject.notifyAll(); 430 } 431 }; 432 mAccessibilityManager.addTouchExplorationStateChangeListener(listener); 433 mSpeakingAccessibilityServiceRule.enableService(); 434 mVibratingAccessibilityServiceRule.enableService(); 435 waitForAtomicBooleanBecomes(atomicBoolean, true, waitObject, 436 "Touch exploration state listener called when services enabled"); 437 assertTrue("Listener told that touch exploration is enabled, but manager says disabled", 438 mAccessibilityManager.isTouchExplorationEnabled()); 439 InstrumentedAccessibilityService.disableAllServices(); 440 waitForAtomicBooleanBecomes(atomicBoolean, false, waitObject, 441 "Touch exploration state listener called when services disabled"); 442 assertFalse("Listener told that touch exploration is disabled, but manager says it enabled", 443 mAccessibilityManager.isTouchExplorationEnabled()); 444 mAccessibilityManager.removeTouchExplorationStateChangeListener(listener); 445 } 446 447 @Test testTouchExplorationListenerWithHandler()448 public void testTouchExplorationListenerWithHandler() throws Exception { 449 final Object waitObject = new Object(); 450 final AtomicBoolean atomicBoolean = new AtomicBoolean(false); 451 452 TouchExplorationStateChangeListener listener = (boolean b) -> { 453 synchronized (waitObject) { 454 atomicBoolean.set(b); 455 waitObject.notifyAll(); 456 } 457 }; 458 mAccessibilityManager.addTouchExplorationStateChangeListener(listener, mHandler); 459 mSpeakingAccessibilityServiceRule.enableService(); 460 mVibratingAccessibilityServiceRule.enableService(); 461 waitForAtomicBooleanBecomes(atomicBoolean, true, waitObject, 462 "Touch exploration state listener called when services enabled"); 463 assertTrue("Listener told that touch exploration is enabled, but manager says disabled", 464 mAccessibilityManager.isTouchExplorationEnabled()); 465 InstrumentedAccessibilityService.disableAllServices(); 466 waitForAtomicBooleanBecomes(atomicBoolean, false, waitObject, 467 "Touch exploration state listener called when services disabled"); 468 assertFalse("Listener told that touch exploration is disabled, but manager says it enabled", 469 mAccessibilityManager.isTouchExplorationEnabled()); 470 mAccessibilityManager.removeTouchExplorationStateChangeListener(listener); 471 } 472 473 @Test testAccessibilityServicesStateListenerNoExecutor()474 public void testAccessibilityServicesStateListenerNoExecutor() { 475 final Object waitObject = new Object(); 476 final AtomicBoolean serviceEnabled = new AtomicBoolean(false); 477 final UiAutomation automan = sInstrumentation.getUiAutomation( 478 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 479 final AccessibilityServicesStateChangeListener listener = (AccessibilityManager manager) -> 480 checkServiceEnabled(waitObject, manager, serviceEnabled, 481 VibratingAccessibilityService.class.getSimpleName()); 482 try { 483 mAccessibilityManager.addAccessibilityServicesStateChangeListener(listener); 484 485 mVibratingAccessibilityServiceRule.enableService(); 486 487 waitForAtomicBooleanBecomes(serviceEnabled, true, waitObject, 488 "Accessibility services state listener called when service is enabled"); 489 } finally { 490 automan.destroy(); 491 } 492 } 493 494 @Test testAccessibilityServicesStateListenerWithExecutor()495 public void testAccessibilityServicesStateListenerWithExecutor() { 496 final Object waitObject = new Object(); 497 final AtomicBoolean serviceEnabled = new AtomicBoolean(false); 498 final UiAutomation automan = sInstrumentation.getUiAutomation( 499 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 500 final AccessibilityServicesStateChangeListener listener = (AccessibilityManager manager) -> 501 checkServiceEnabled(waitObject, manager, serviceEnabled, 502 VibratingAccessibilityService.class.getSimpleName()); 503 try { 504 mAccessibilityManager.addAccessibilityServicesStateChangeListener( 505 mTargetContext.getMainExecutor(), listener); 506 507 mVibratingAccessibilityServiceRule.enableService(); 508 509 waitForAtomicBooleanBecomes(serviceEnabled, true, waitObject, 510 "Accessibility services state listener called when service is enabled"); 511 } finally { 512 automan.destroy(); 513 } 514 } 515 516 517 @Test testAccessibilityStateListenerNoHandler()518 public void testAccessibilityStateListenerNoHandler() throws Exception { 519 final Object waitObject = new Object(); 520 final AtomicBoolean atomicBoolean = new AtomicBoolean(false); 521 522 AccessibilityStateChangeListener listener = (boolean b) -> { 523 synchronized (waitObject) { 524 atomicBoolean.set(b); 525 waitObject.notifyAll(); 526 } 527 }; 528 mAccessibilityManager.addAccessibilityStateChangeListener(listener); 529 mSpeakingAndVibratingAccessibilityServiceRule.enableService(); 530 waitForAtomicBooleanBecomes(atomicBoolean, true, waitObject, 531 "Accessibility state listener called when services enabled"); 532 assertTrue("Listener told that accessibility is enabled, but manager says disabled", 533 mAccessibilityManager.isEnabled()); 534 InstrumentedAccessibilityService.disableAllServices(); 535 waitForAtomicBooleanBecomes(atomicBoolean, false, waitObject, 536 "Accessibility state listener called when services disabled"); 537 assertFalse("Listener told that accessibility is disabled, but manager says enabled", 538 mAccessibilityManager.isEnabled()); 539 mAccessibilityManager.removeAccessibilityStateChangeListener(listener); 540 } 541 542 @Test testAudioDescriptionRequestedChangeListenerWithExecutor()543 public void testAudioDescriptionRequestedChangeListenerWithExecutor() { 544 final UiAutomation automan = sInstrumentation.getUiAutomation( 545 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 546 final Object waitObject = new Object(); 547 final AtomicBoolean atomicBoolean = new AtomicBoolean(false); 548 549 AudioDescriptionRequestedChangeListener listener = (boolean b) -> { 550 synchronized (waitObject) { 551 atomicBoolean.set(b); 552 waitObject.notifyAll(); 553 } 554 }; 555 556 try { 557 mAccessibilityManager.addAudioDescriptionRequestedChangeListener( 558 mTargetContext.getMainExecutor(), listener); 559 putSecureSetting(automan, ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, "1"); 560 waitForAtomicBooleanBecomes(atomicBoolean, true, waitObject, 561 "Audio description state listener called when services enabled"); 562 assertTrue("Listener told that audio description by default is request.", 563 mAccessibilityManager.isAudioDescriptionRequested()); 564 565 putSecureSetting(automan, ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, "0"); 566 waitForAtomicBooleanBecomes(atomicBoolean, false, waitObject, 567 "Audio description state listener called when services disabled"); 568 assertFalse("Listener told that audio description by default is not request.", 569 mAccessibilityManager.isAudioDescriptionRequested()); 570 assertTrue( 571 mAccessibilityManager.removeAudioDescriptionRequestedChangeListener( 572 listener)); 573 } finally { 574 automan.destroy(); 575 } 576 } 577 578 @Test testIsAudioDescriptionEnabled()579 public void testIsAudioDescriptionEnabled() throws Exception { 580 final UiAutomation automan = sInstrumentation.getUiAutomation( 581 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 582 583 try { 584 putSecureSetting(automan, ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, "1"); 585 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 586 @Override 587 public boolean canProceed() { 588 return mAccessibilityManager.isAudioDescriptionRequested(); 589 } 590 }); 591 assertTrue(mAccessibilityManager.isAudioDescriptionRequested()); 592 593 putSecureSetting(automan, ENABLED_ACCESSIBILITY_AUDIO_DESCRIPTION_BY_DEFAULT, "0"); 594 PollingCheck.waitFor(new PollingCheck.PollingCheckCondition() { 595 @Override 596 public boolean canProceed() { 597 return !mAccessibilityManager.isAudioDescriptionRequested(); 598 } 599 }); 600 assertFalse(mAccessibilityManager.isAudioDescriptionRequested()); 601 } finally { 602 automan.destroy(); 603 } 604 } 605 606 @Test testAccessibilityStateListenerWithHandler()607 public void testAccessibilityStateListenerWithHandler() throws Exception { 608 final Object waitObject = new Object(); 609 final AtomicBoolean atomicBoolean = new AtomicBoolean(false); 610 611 AccessibilityStateChangeListener listener = (boolean b) -> { 612 synchronized (waitObject) { 613 atomicBoolean.set(b); 614 waitObject.notifyAll(); 615 } 616 }; 617 mAccessibilityManager.addAccessibilityStateChangeListener(listener, mHandler); 618 mSpeakingAndVibratingAccessibilityServiceRule.enableService(); 619 waitForAtomicBooleanBecomes(atomicBoolean, true, waitObject, 620 "Accessibility state listener called when services enabled"); 621 assertTrue("Listener told that accessibility is enabled, but manager says disabled", 622 mAccessibilityManager.isEnabled()); 623 InstrumentedAccessibilityService.disableAllServices(); 624 waitForAtomicBooleanBecomes(atomicBoolean, false, waitObject, 625 "Accessibility state listener called when services disabled"); 626 assertFalse("Listener told that accessibility is disabled, but manager says enabled", 627 mAccessibilityManager.isEnabled()); 628 mAccessibilityManager.removeAccessibilityStateChangeListener(listener); 629 } 630 631 @Test testGetRecommendedTimeoutMillis()632 public void testGetRecommendedTimeoutMillis() throws Exception { 633 mSpeakingAccessibilityServiceRule.enableService(); 634 mVibratingAccessibilityServiceRule.enableService(); 635 waitForAccessibilityEnabled(); 636 UiAutomation automan = sInstrumentation.getUiAutomation( 637 UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 638 try { 639 // SpeakingA11yService interactive/nonInteractive timeout is 6000/1000 640 // vibratingA11yService interactive/nonInteractive timeout is 5000/2000 641 turnOffRecommendedUiTimoutSettings(automan); 642 PollingCheck.waitFor(() -> sameRecommendedTimeout(6000, 2000)); 643 turnOnRecommendedUiTimoutSettings(automan, 7000, 0); 644 PollingCheck.waitFor(() -> sameRecommendedTimeout(7000, 2000)); 645 turnOnRecommendedUiTimoutSettings(automan, 0, 4000); 646 PollingCheck.waitFor(() -> sameRecommendedTimeout(6000, 4000)); 647 turnOnRecommendedUiTimoutSettings(automan, 9000, 8000); 648 PollingCheck.waitFor(() -> sameRecommendedTimeout(9000, 8000)); 649 turnOffRecommendedUiTimoutSettings(automan); 650 PollingCheck.waitFor(() -> sameRecommendedTimeout(6000, 2000)); 651 assertEquals("Should return original timeout", 3000, 652 mAccessibilityManager.getRecommendedTimeoutMillis(3000, 653 AccessibilityManager.FLAG_CONTENT_ICONS)); 654 assertEquals("Should return original timeout", 7000, 655 mAccessibilityManager.getRecommendedTimeoutMillis(7000, 656 AccessibilityManager.FLAG_CONTENT_CONTROLS)); 657 } finally { 658 automan.destroy(); 659 } 660 } 661 662 @Test 663 @RequiresFlagsEnabled(Flags.FLAG_FLASH_NOTIFICATION_SYSTEM_API) testStartAndStopFlashNotificationSequence()664 public void testStartAndStopFlashNotificationSequence() throws Exception { 665 assertTrue("Start flash notification sequence failed.", 666 mAccessibilityManager.startFlashNotificationSequence(mTargetContext, 1)); 667 assertTrue("Stop flash notification sequence failed.", 668 mAccessibilityManager.stopFlashNotificationSequence(mTargetContext)); 669 } 670 checkServiceEnabled(Object waitObject, AccessibilityManager manager, AtomicBoolean serviceEnabled, String serviceName)671 private void checkServiceEnabled(Object waitObject, AccessibilityManager manager, 672 AtomicBoolean serviceEnabled, String serviceName) { 673 synchronized (waitObject) { 674 List<AccessibilityServiceInfo> infos = manager.getEnabledAccessibilityServiceList( 675 AccessibilityServiceInfo.FEEDBACK_ALL_MASK); 676 for (AccessibilityServiceInfo info : infos) { 677 final String serviceId = info.getId(); 678 if (serviceId.endsWith(serviceName)) { 679 serviceEnabled.set(true); 680 waitObject.notifyAll(); 681 } 682 } 683 } 684 } 685 waitForAtomicBooleanBecomes(AtomicBoolean atomicBoolean, boolean expectedValue, Object waitObject, String condition)686 private void waitForAtomicBooleanBecomes(AtomicBoolean atomicBoolean, 687 boolean expectedValue, Object waitObject, String condition) { 688 long timeoutTime = TIMEOUT_SERVICE_ENABLE; 689 TestUtils.waitOn(waitObject, () -> atomicBoolean.get() == expectedValue, timeoutTime, 690 condition); 691 } 692 waitForAccessibilityEnabled()693 private void waitForAccessibilityEnabled() throws InterruptedException { 694 final Object waitObject = new Object(); 695 696 AccessibilityStateChangeListener listener = (boolean b) -> { 697 synchronized (waitObject) { 698 waitObject.notifyAll(); 699 } 700 }; 701 mAccessibilityManager.addAccessibilityStateChangeListener(listener); 702 long timeoutTime = 703 System.currentTimeMillis() + TIMEOUT_SERVICE_ENABLE; 704 synchronized (waitObject) { 705 while (!mAccessibilityManager.isEnabled() 706 && (System.currentTimeMillis() < timeoutTime)) { 707 waitObject.wait(timeoutTime - System.currentTimeMillis()); 708 } 709 } 710 mAccessibilityManager.removeAccessibilityStateChangeListener(listener); 711 assertTrue("Timed out enabling accessibility", mAccessibilityManager.isEnabled()); 712 } 713 turnOffRecommendedUiTimoutSettings(UiAutomation automan)714 private void turnOffRecommendedUiTimoutSettings(UiAutomation automan) { 715 putSecureSetting(automan, ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, null); 716 putSecureSetting(automan, ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, null); 717 } 718 turnOnRecommendedUiTimoutSettings(UiAutomation automan, int interactiveUiTimeout, int nonInteractiveUiTimeout)719 private void turnOnRecommendedUiTimoutSettings(UiAutomation automan, 720 int interactiveUiTimeout, int nonInteractiveUiTimeout) { 721 putSecureSetting(automan, ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 722 Integer.toString(interactiveUiTimeout)); 723 putSecureSetting(automan, ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, 724 Integer.toString(nonInteractiveUiTimeout)); 725 } 726 sameRecommendedTimeout(int interactiveUiTimeout, int nonInteractiveUiTimeout)727 private boolean sameRecommendedTimeout(int interactiveUiTimeout, 728 int nonInteractiveUiTimeout) { 729 final int currentInteractiveUiTimeout = mAccessibilityManager 730 .getRecommendedTimeoutMillis(0, AccessibilityManager.FLAG_CONTENT_CONTROLS); 731 final int currentNonInteractiveUiTimeout = mAccessibilityManager 732 .getRecommendedTimeoutMillis(0, AccessibilityManager.FLAG_CONTENT_ICONS); 733 return (currentInteractiveUiTimeout == interactiveUiTimeout 734 && currentNonInteractiveUiTimeout == nonInteractiveUiTimeout); 735 } 736 putSecureSetting(UiAutomation automan, String name, String value)737 private void putSecureSetting(UiAutomation automan, String name, String value) { 738 final StringBuilder cmd = new StringBuilder("settings put secure ") 739 .append(name).append(" ") 740 .append(value); 741 try { 742 SystemUtil.runShellCommand(automan, cmd.toString()); 743 } catch (IOException e) { 744 fail("Fail to run shell command"); 745 } 746 } 747 } 748