Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2016 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 org.hamcrest.Matchers.is;
     20 import static org.hamcrest.core.IsEqual.equalTo;
     21 import static org.junit.Assert.assertThat;
     22 
     23 import android.app.Instrumentation;
     24 import android.app.UiAutomation;
     25 import android.content.ContentResolver;
     26 import android.content.Context;
     27 import android.os.ParcelFileDescriptor;
     28 import android.os.SystemClock;
     29 import android.provider.Settings;
     30 import android.view.accessibility.AccessibilityManager;
     31 
     32 import java.io.FileInputStream;
     33 import java.io.IOException;
     34 import java.io.InputStream;
     35 import java.util.concurrent.atomic.AtomicBoolean;
     36 
     37 /**
     38  * Utility methods for enabling and disabling the services used in this package
     39  */
     40 public class ServiceControlUtils {
     41     public static final int TIMEOUT_FOR_SERVICE_ENABLE = 10000; // millis; 10s
     42 
     43     private static final String SETTING_ENABLE_SPEAKING_AND_VIBRATING_SERVICES =
     44             "android.view.accessibility.cts/.SpeakingAccessibilityService:"
     45             + "android.view.accessibility.cts/.VibratingAccessibilityService";
     46 
     47     private static final String SETTING_ENABLE_MULTIPLE_FEEDBACK_TYPES_SERVICE =
     48             "android.view.accessibility.cts/.SpeakingAndVibratingAccessibilityService";
     49 
     50     /**
     51      * Enable {@code SpeakingAccessibilityService} and {@code VibratingAccessibilityService}
     52      *
     53      * @param instrumentation A valid instrumentation
     54      */
     55     public static void enableSpeakingAndVibratingServices(Instrumentation instrumentation)
     56             throws IOException {
     57         Context context = instrumentation.getContext();
     58 
     59         // Get permission to enable accessibility
     60         UiAutomation uiAutomation = instrumentation.getUiAutomation();
     61 
     62         // Change the settings to enable the two services
     63         ContentResolver cr = context.getContentResolver();
     64         String alreadyEnabledServices = Settings.Secure.getString(
     65                 cr, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
     66         ParcelFileDescriptor fd = uiAutomation.executeShellCommand("settings --user cur put secure "
     67                 + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES + " "
     68                 + alreadyEnabledServices + ":"
     69                 + SETTING_ENABLE_SPEAKING_AND_VIBRATING_SERVICES);
     70         InputStream in = new FileInputStream(fd.getFileDescriptor());
     71         byte[] buffer = new byte[4096];
     72         while (in.read(buffer) > 0);
     73         uiAutomation.destroy();
     74 
     75         // Wait for speaking service to be connected
     76         long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
     77         boolean speakingServiceStarted = false;
     78         while (!speakingServiceStarted && (SystemClock.uptimeMillis() < timeoutTimeMillis)) {
     79             synchronized (SpeakingAccessibilityService.sWaitObjectForConnecting) {
     80                 if (SpeakingAccessibilityService.sConnectedInstance != null) {
     81                     speakingServiceStarted = true;
     82                     break;
     83                 }
     84                 if (!speakingServiceStarted) {
     85                     try {
     86                         SpeakingAccessibilityService.sWaitObjectForConnecting.wait(
     87                                 timeoutTimeMillis - SystemClock.uptimeMillis());
     88                     } catch (InterruptedException e) {
     89                     }
     90                 }
     91             }
     92         }
     93         if (!speakingServiceStarted) {
     94             throw new RuntimeException("Speaking accessibility service not starting");
     95         }
     96 
     97         // Wait for vibrating service to be connected
     98         while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
     99             synchronized (VibratingAccessibilityService.sWaitObjectForConnecting) {
    100                 if (VibratingAccessibilityService.sConnectedInstance != null) {
    101                     return;
    102                 }
    103 
    104                 try {
    105                     VibratingAccessibilityService.sWaitObjectForConnecting.wait(
    106                             timeoutTimeMillis - SystemClock.uptimeMillis());
    107                 } catch (InterruptedException e) {
    108                 }
    109             }
    110         }
    111         throw new RuntimeException("Vibrating accessibility service not starting");
    112     }
    113 
    114     /**
    115      * Enable {@link SpeakingAndVibratingAccessibilityService} for tests requiring a service with
    116      * multiple feedback types
    117      *
    118      * @param instrumentation A valid instrumentation
    119      */
    120     public static void enableMultipleFeedbackTypesService(Instrumentation instrumentation)
    121             throws IOException {
    122         Context context = instrumentation.getContext();
    123 
    124         // Get permission to enable accessibility
    125         UiAutomation uiAutomation = instrumentation.getUiAutomation();
    126 
    127         // Change the settings to enable the services
    128         ContentResolver cr = context.getContentResolver();
    129         String alreadyEnabledServices = Settings.Secure.getString(
    130                 cr, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
    131         ParcelFileDescriptor fd = uiAutomation.executeShellCommand("settings --user cur put secure "
    132                 + Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES + " "
    133                 + alreadyEnabledServices + ":"
    134                 + SETTING_ENABLE_MULTIPLE_FEEDBACK_TYPES_SERVICE);
    135         InputStream in = new FileInputStream(fd.getFileDescriptor());
    136         byte[] buffer = new byte[4096];
    137         while (in.read(buffer) > 0);
    138         uiAutomation.destroy();
    139 
    140         // Wait for the service to be connected
    141         long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
    142         boolean multipleFeedbackTypesServiceEnabled = false;
    143         while (!multipleFeedbackTypesServiceEnabled && (SystemClock.uptimeMillis()
    144                 < timeoutTimeMillis)) {
    145             synchronized (SpeakingAndVibratingAccessibilityService.sWaitObjectForConnecting) {
    146                 if (SpeakingAndVibratingAccessibilityService.sConnectedInstance != null) {
    147                     multipleFeedbackTypesServiceEnabled = true;
    148                     break;
    149                 }
    150                 if (!multipleFeedbackTypesServiceEnabled) {
    151                     try {
    152                         SpeakingAndVibratingAccessibilityService.sWaitObjectForConnecting.wait(
    153                                 timeoutTimeMillis - SystemClock.uptimeMillis());
    154                     } catch (InterruptedException e) {
    155                     }
    156                 }
    157             }
    158         }
    159         if (!multipleFeedbackTypesServiceEnabled) {
    160             throw new RuntimeException(
    161                     "Multiple feedback types accessibility service not starting");
    162         }
    163     }
    164 
    165     /**
    166      * Turn off all accessibility services. Assumes permissions to write settings are already
    167      * set, which they are in
    168      * {@link ServiceControlUtils#enableSpeakingAndVibratingServices(Instrumentation)}.
    169      *
    170      * @param instrumentation A valid instrumentation
    171      */
    172     public static void turnAccessibilityOff(Instrumentation instrumentation) {
    173         if (SpeakingAccessibilityService.sConnectedInstance != null) {
    174             SpeakingAccessibilityService.sConnectedInstance.disableSelf();
    175             SpeakingAccessibilityService.sConnectedInstance = null;
    176         }
    177         if (VibratingAccessibilityService.sConnectedInstance != null) {
    178             VibratingAccessibilityService.sConnectedInstance.disableSelf();
    179             VibratingAccessibilityService.sConnectedInstance = null;
    180         }
    181         if (SpeakingAndVibratingAccessibilityService.sConnectedInstance != null) {
    182             SpeakingAndVibratingAccessibilityService.sConnectedInstance.disableSelf();
    183             SpeakingAndVibratingAccessibilityService.sConnectedInstance = null;
    184         }
    185 
    186         final Object waitLockForA11yOff = new Object();
    187         AccessibilityManager manager = (AccessibilityManager) instrumentation
    188                 .getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
    189         // Updates to manager.isEnabled() aren't synchronized
    190         AtomicBoolean accessibilityEnabled = new AtomicBoolean(manager.isEnabled());
    191         AccessibilityManager.AccessibilityStateChangeListener listener = (boolean b) -> {
    192             synchronized (waitLockForA11yOff) {
    193                 waitLockForA11yOff.notifyAll();
    194                 accessibilityEnabled.set(b);
    195             }
    196         };
    197         manager.addAccessibilityStateChangeListener(listener);
    198         try {
    199             long timeoutTimeMillis = SystemClock.uptimeMillis() + TIMEOUT_FOR_SERVICE_ENABLE;
    200             while (SystemClock.uptimeMillis() < timeoutTimeMillis) {
    201                 synchronized (waitLockForA11yOff) {
    202                     if (!accessibilityEnabled.get()) {
    203                         return;
    204                     }
    205                     try {
    206                         waitLockForA11yOff.wait(timeoutTimeMillis - SystemClock.uptimeMillis());
    207                     } catch (InterruptedException e) {
    208                         // Ignored; loop again
    209                     }
    210                 }
    211             }
    212         } finally {
    213             manager.removeAccessibilityStateChangeListener(listener);
    214         }
    215         assertThat("Unable to turn accessibility off", manager.isEnabled(), is(equalTo(false)));
    216     }
    217 }
    218