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