Home | History | Annotate | Download | only in cts
      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