1 /* 2 * Copyright (C) 2014 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.app.uiautomation.cts; 18 19 import android.accessibilityservice.AccessibilityServiceInfo; 20 import android.app.Activity; 21 import android.app.UiAutomation; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.os.ParcelFileDescriptor; 26 import android.os.SystemClock; 27 import android.platform.test.annotations.AppModeFull; 28 import android.platform.test.annotations.Presubmit; 29 import android.provider.Settings; 30 import android.test.InstrumentationTestCase; 31 import android.view.FrameStats; 32 import android.view.WindowAnimationFrameStats; 33 import android.view.WindowContentFrameStats; 34 import android.view.accessibility.AccessibilityEvent; 35 import android.view.accessibility.AccessibilityManager; 36 import android.view.accessibility.AccessibilityWindowInfo; 37 import android.widget.ListView; 38 39 import java.io.IOException; 40 import java.util.List; 41 import java.util.concurrent.TimeoutException; 42 43 /** 44 * Tests for the UiAutomation APIs. 45 */ 46 public class UiAutomationTest extends InstrumentationTestCase { 47 private static final long QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE = 1000;//ms 48 49 private static final long TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE = 1000 * 10;//ms 50 51 // Used to enable/disable accessibility services 52 private static final String COMPONENT_NAME_SEPARATOR = ":"; 53 private static final int TIMEOUT_FOR_SERVICE_ENABLE = 10000; // millis; 10s 54 55 @Override 56 public void setUp() throws Exception { 57 super.setUp(); 58 UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); 59 AccessibilityServiceInfo info = uiAutomation.getServiceInfo(); 60 info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS; 61 uiAutomation.setServiceInfo(info); 62 grantWriteSecureSettingsPermission(uiAutomation); 63 } 64 65 @Presubmit 66 public void testWindowContentFrameStats() throws Exception { 67 Activity activity = null; 68 try { 69 UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); 70 71 // Start an activity. 72 Intent intent = new Intent(getInstrumentation().getContext(), 73 UiAutomationTestFirstActivity.class); 74 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 75 activity = getInstrumentation().startActivitySync(intent); 76 77 // Wait for things to settle. 78 uiAutomation.waitForIdle( 79 QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE, TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE); 80 81 // Find the application window. 82 final int windowId = findAppWindowId(uiAutomation.getWindows()); 83 assertTrue(windowId >= 0); 84 85 // Clear stats to be with a clean slate. 86 assertTrue(uiAutomation.clearWindowContentFrameStats(windowId)); 87 88 // Find the list to scroll around. 89 final ListView listView = (ListView) activity.findViewById(R.id.list_view); 90 91 // Scroll a bit. 92 scrollListView(uiAutomation, listView, listView.getAdapter().getCount() - 1); 93 scrollListView(uiAutomation, listView, 0); 94 95 // Get the frame stats. 96 WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId); 97 98 // Check the frame stats... 99 100 // We should have something. 101 assertNotNull(stats); 102 103 // The refresh period is always positive. 104 assertTrue(stats.getRefreshPeriodNano() > 0); 105 106 // There is some frame data. 107 final int frameCount = stats.getFrameCount(); 108 assertTrue(frameCount > 0); 109 110 // The frames are ordered in ascending order. 111 assertWindowContentTimestampsInAscendingOrder(stats); 112 113 // The start and end times are based on first and last frame. 114 assertEquals(stats.getStartTimeNano(), stats.getFramePresentedTimeNano(0)); 115 assertEquals(stats.getEndTimeNano(), stats.getFramePresentedTimeNano(frameCount - 1)); 116 } finally { 117 // Clean up. 118 if (activity != null) { 119 activity.finish(); 120 } 121 } 122 } 123 124 public void testWindowContentFrameStatsNoAnimation() throws Exception { 125 Activity activity = null; 126 try { 127 UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); 128 129 // Start an activity. 130 Intent intent = new Intent(getInstrumentation().getContext(), 131 UiAutomationTestFirstActivity.class); 132 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 133 activity = getInstrumentation().startActivitySync(intent); 134 135 // Wait for things to settle. 136 uiAutomation.waitForIdle( 137 QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE, TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE); 138 139 // Wait for Activity draw finish 140 getInstrumentation().waitForIdleSync(); 141 142 // Find the application window. 143 final int windowId = findAppWindowId(uiAutomation.getWindows()); 144 assertTrue(windowId >= 0); 145 146 // Clear stats to be with a clean slate. 147 assertTrue(uiAutomation.clearWindowContentFrameStats(windowId)); 148 149 // Get the frame stats. 150 WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId); 151 152 // Check the frame stats... 153 154 // We should have something. 155 assertNotNull(stats); 156 157 // The refresh period is always positive. 158 assertTrue(stats.getRefreshPeriodNano() > 0); 159 160 // There is no data. 161 assertTrue(stats.getFrameCount() == 0); 162 163 // The start and end times are undefibed as we have no data. 164 assertEquals(stats.getStartTimeNano(), FrameStats.UNDEFINED_TIME_NANO); 165 assertEquals(stats.getEndTimeNano(), FrameStats.UNDEFINED_TIME_NANO); 166 } finally { 167 // Clean up. 168 if (activity != null) { 169 activity.finish(); 170 } 171 } 172 } 173 174 @Presubmit 175 public void testWindowAnimationFrameStats() throws Exception { 176 Activity firstActivity = null; 177 Activity secondActivity = null; 178 try { 179 UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); 180 181 // Start the frist activity. 182 Intent firstIntent = new Intent(getInstrumentation().getContext(), 183 UiAutomationTestFirstActivity.class); 184 firstIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 185 firstActivity = getInstrumentation().startActivitySync(firstIntent); 186 187 // Wait for things to settle. 188 uiAutomation.waitForIdle( 189 QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE, TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE); 190 191 // Wait for Activity draw finish 192 getInstrumentation().waitForIdleSync(); 193 194 // Clear the window animation stats to be with a clean slate. 195 uiAutomation.clearWindowAnimationFrameStats(); 196 197 // Start the second activity 198 Intent secondIntent = new Intent(getInstrumentation().getContext(), 199 UiAutomationTestSecondActivity.class); 200 secondIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 201 secondActivity = getInstrumentation().startActivitySync(secondIntent); 202 203 // Wait for things to settle. 204 uiAutomation.waitForIdle( 205 QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE, TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE); 206 207 // Wait for Activity draw finish 208 getInstrumentation().waitForIdleSync(); 209 210 // Get the frame stats. 211 WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats(); 212 213 // Check the frame stats... 214 215 // We should have something. 216 assertNotNull(stats); 217 218 // The refresh presiod is always positive. 219 assertTrue(stats.getRefreshPeriodNano() > 0); 220 221 // There is some frame data. 222 final int frameCount = stats.getFrameCount(); 223 assertTrue(frameCount > 0); 224 225 // The frames are ordered in ascending order. 226 assertWindowAnimationTimestampsInAscendingOrder(stats); 227 228 // The start and end times are based on first and last frame. 229 assertEquals(stats.getStartTimeNano(), stats.getFramePresentedTimeNano(0)); 230 assertEquals(stats.getEndTimeNano(), stats.getFramePresentedTimeNano(frameCount - 1)); 231 } finally { 232 // Clean up. 233 if (firstActivity != null) { 234 firstActivity.finish(); 235 } 236 if (secondActivity != null) { 237 secondActivity.finish(); 238 } 239 } 240 } 241 242 public void testWindowAnimationFrameStatsNoAnimation() throws Exception { 243 UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); 244 245 // Wait for things to settle. 246 uiAutomation.waitForIdle( 247 QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE, TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE); 248 249 // Clear the window animation stats to be with a clean slate. 250 uiAutomation.clearWindowAnimationFrameStats(); 251 252 // Get the frame stats. 253 WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats(); 254 255 // Check the frame stats... 256 257 // We should have something. 258 assertNotNull(stats); 259 260 // The refresh presiod is always positive. 261 assertTrue(stats.getRefreshPeriodNano() > 0); 262 263 // There is no data. 264 assertTrue(stats.getFrameCount() == 0); 265 266 // The start and end times are undefibed as we have no data. 267 assertEquals(stats.getStartTimeNano(), FrameStats.UNDEFINED_TIME_NANO); 268 assertEquals(stats.getEndTimeNano(), FrameStats.UNDEFINED_TIME_NANO); 269 } 270 271 @Presubmit 272 public void testUsingUiAutomationAfterDestroy_shouldThrowException() { 273 UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); 274 uiAutomation.destroy(); 275 try { 276 uiAutomation.getServiceInfo(); 277 fail("Expected exception when using destroyed UiAutomation"); 278 } catch (RuntimeException e) { 279 } 280 } 281 282 @AppModeFull 283 public void testDontSuppressAccessibility_canStartA11yService() throws IOException, 284 InterruptedException { 285 turnAccessibilityOff(); 286 try { 287 getInstrumentation() 288 .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 289 enableAccessibilityService(); 290 assertTrue(UiAutomationTestA11yService.sConnectedInstance.isConnected()); 291 } finally { 292 turnAccessibilityOff(); 293 } 294 } 295 296 @AppModeFull 297 public void testServiceWithNoFlags_shutsDownA11yService() throws IOException { 298 turnAccessibilityOff(); 299 try { 300 UiAutomation uiAutomation = getInstrumentation() 301 .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 302 enableAccessibilityService(); 303 assertTrue(UiAutomationTestA11yService.sConnectedInstance.isConnected()); 304 uiAutomation.destroy(); 305 assertTrue(UiAutomationTestA11yService.sConnectedInstance.isConnected()); 306 getInstrumentation().getUiAutomation(); // Should suppress 307 assertFalse(UiAutomationTestA11yService.sConnectedInstance.isConnected()); 308 } finally { 309 turnAccessibilityOff(); 310 } 311 } 312 313 @AppModeFull 314 public void testServiceSupressingA11yServices_a11yServiceStartsWhenDestroyed() 315 throws IOException, InterruptedException { 316 turnAccessibilityOff(); 317 try { 318 UiAutomation uiAutomation = getInstrumentation() 319 .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 320 enableAccessibilityService(); 321 uiAutomation.destroy(); 322 UiAutomation suppressingUiAutomation = getInstrumentation().getUiAutomation(); 323 // We verify above that the connection is broken here. Make sure we see a new one 324 // after we destroy it 325 UiAutomationTestA11yService.sConnectedInstance = null; 326 suppressingUiAutomation.destroy(); 327 waitForAccessibilityServiceToStart(); 328 } finally { 329 turnAccessibilityOff(); 330 } 331 } 332 333 @AppModeFull 334 public void testServiceSupressingA11yServices_a11yServiceStartsWhenFlagsChange() 335 throws IOException, InterruptedException { 336 turnAccessibilityOff(); 337 try { 338 getInstrumentation() 339 .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 340 enableAccessibilityService(); 341 getInstrumentation().getUiAutomation(); 342 // We verify above that the connection is broken here. Make sure we see a new one 343 // after we change the flags 344 UiAutomationTestA11yService.sConnectedInstance = null; 345 getInstrumentation() 346 .getUiAutomation(UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES); 347 waitForAccessibilityServiceToStart(); 348 } finally { 349 turnAccessibilityOff(); 350 } 351 } 352 353 private void scrollListView(UiAutomation uiAutomation, final ListView listView, 354 final int position) throws TimeoutException { 355 getInstrumentation().runOnMainSync(new Runnable() { 356 @Override 357 public void run() { 358 listView.smoothScrollToPosition(position); 359 } 360 }); 361 Runnable emptyRunnable = new Runnable() { 362 @Override 363 public void run() { 364 } 365 }; 366 UiAutomation.AccessibilityEventFilter scrollFilter = 367 new UiAutomation.AccessibilityEventFilter() { 368 @Override 369 public boolean accept(AccessibilityEvent accessibilityEvent) { 370 return accessibilityEvent.getEventType() 371 == AccessibilityEvent.TYPE_VIEW_SCROLLED; 372 } 373 }; 374 uiAutomation.executeAndWaitForEvent(emptyRunnable, scrollFilter, 375 TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE); 376 uiAutomation.waitForIdle( 377 QUIET_TIME_TO_BE_CONSIDERED_IDLE_STATE, TOTAL_TIME_TO_WAIT_FOR_IDLE_STATE); 378 } 379 380 private void grantWriteSecureSettingsPermission(UiAutomation uiAutomation) throws IOException { 381 uiAutomation.grantRuntimePermission(getInstrumentation().getContext().getPackageName(), 382 android.Manifest.permission.WRITE_SECURE_SETTINGS); 383 } 384 385 private void enableAccessibilityService() { 386 Context context = getInstrumentation().getContext(); 387 AccessibilityManager manager = 388 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); 389 List<AccessibilityServiceInfo> serviceInfos = 390 manager.getInstalledAccessibilityServiceList(); 391 for (int i = 0; i < serviceInfos.size(); i++) { 392 AccessibilityServiceInfo serviceInfo = serviceInfos.get(i); 393 if (context.getString(R.string.uiautomation_a11y_service_description) 394 .equals(serviceInfo.getDescription())) { 395 ContentResolver cr = context.getContentResolver(); 396 String enabledServices = Settings.Secure.getString(cr, 397 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 398 Settings.Secure.putString(cr, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, 399 enabledServices + COMPONENT_NAME_SEPARATOR + serviceInfo.getId()); 400 Settings.Secure.putInt(cr, Settings.Secure.ACCESSIBILITY_ENABLED, 1); 401 waitForAccessibilityServiceToStart(); 402 return; 403 } 404 } 405 throw new RuntimeException("Test accessibility service not found"); 406 } 407 408 private void waitForAccessibilityServiceToStart() { 409 long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE; 410 while (SystemClock.uptimeMillis() < timeoutTimeMillis) { 411 synchronized(UiAutomationTestA11yService.sWaitObjectForConnecting) { 412 if (UiAutomationTestA11yService.sConnectedInstance != null) { 413 return; 414 } 415 try { 416 UiAutomationTestA11yService.sWaitObjectForConnecting.wait( 417 timeoutTimeMillis - SystemClock.uptimeMillis()); 418 } catch (InterruptedException e) { 419 // Ignored; loop again 420 } 421 } 422 } 423 throw new RuntimeException("Test accessibility service not starting"); 424 } 425 426 private void turnAccessibilityOff() { 427 getInstrumentation().getUiAutomation().destroy(); 428 final Object waitLockForA11yOff = new Object(); 429 Context context = getInstrumentation().getContext(); 430 AccessibilityManager manager = 431 (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE); 432 manager.addAccessibilityStateChangeListener( 433 new AccessibilityManager.AccessibilityStateChangeListener() { 434 @Override 435 public void onAccessibilityStateChanged(boolean b) { 436 synchronized (waitLockForA11yOff) { 437 waitLockForA11yOff.notifyAll(); 438 } 439 } 440 }); 441 ContentResolver cr = context.getContentResolver(); 442 Settings.Secure.putString( 443 cr, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, null); 444 UiAutomationTestA11yService.sConnectedInstance = null; 445 long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE; 446 while (SystemClock.uptimeMillis() < timeoutTimeMillis) { 447 synchronized (waitLockForA11yOff) { 448 if (!manager.isEnabled()) { 449 return; 450 } 451 try { 452 waitLockForA11yOff.wait(timeoutTimeMillis - SystemClock.uptimeMillis()); 453 } catch (InterruptedException e) { 454 // Ignored; loop again 455 } 456 } 457 } 458 throw new RuntimeException("Unable to turn accessibility off"); 459 } 460 461 private void assertWindowContentTimestampsInAscendingOrder(WindowContentFrameStats stats) { 462 long lastExpectedTimeNano = 0; 463 long lastPresentedTimeNano = 0; 464 long lastPreparedTimeNano = 0; 465 466 final int frameCount = stats.getFrameCount(); 467 for (int i = 0; i < frameCount; i++) { 468 final long expectedTimeNano = stats.getFramePostedTimeNano(i); 469 assertTrue(expectedTimeNano >= lastExpectedTimeNano); 470 lastExpectedTimeNano = expectedTimeNano; 471 472 final long presentedTimeNano = stats.getFramePresentedTimeNano(i); 473 if (lastPresentedTimeNano == FrameStats.UNDEFINED_TIME_NANO) { 474 assertTrue(presentedTimeNano == FrameStats.UNDEFINED_TIME_NANO); 475 } else if (presentedTimeNano != FrameStats.UNDEFINED_TIME_NANO) { 476 assertTrue(presentedTimeNano >= lastPresentedTimeNano); 477 } 478 lastPresentedTimeNano = presentedTimeNano; 479 480 final long preparedTimeNano = stats.getFrameReadyTimeNano(i); 481 if (lastPreparedTimeNano == FrameStats.UNDEFINED_TIME_NANO) { 482 assertTrue(preparedTimeNano == FrameStats.UNDEFINED_TIME_NANO); 483 } else if (preparedTimeNano != FrameStats.UNDEFINED_TIME_NANO) { 484 assertTrue(preparedTimeNano >= lastPreparedTimeNano); 485 } 486 lastPreparedTimeNano = preparedTimeNano; 487 } 488 } 489 490 private void assertWindowAnimationTimestampsInAscendingOrder(WindowAnimationFrameStats stats) { 491 long lastPresentedTimeNano = 0; 492 493 final int frameCount = stats.getFrameCount(); 494 for (int i = 0; i < frameCount; i++) { 495 final long presentedTimeNano = stats.getFramePresentedTimeNano(i); 496 if (lastPresentedTimeNano == FrameStats.UNDEFINED_TIME_NANO) { 497 assertTrue(presentedTimeNano == FrameStats.UNDEFINED_TIME_NANO); 498 } else if (presentedTimeNano != FrameStats.UNDEFINED_TIME_NANO) { 499 assertTrue(presentedTimeNano >= lastPresentedTimeNano); 500 } 501 lastPresentedTimeNano = presentedTimeNano; 502 } 503 } 504 505 private int findAppWindowId(List<AccessibilityWindowInfo> windows) { 506 final int windowCount = windows.size(); 507 for (int i = 0; i < windowCount; i++) { 508 AccessibilityWindowInfo window = windows.get(i); 509 if (window.getType() == AccessibilityWindowInfo.TYPE_APPLICATION) { 510 return window.getId(); 511 } 512 } 513 return -1; 514 } 515 } 516