1 /* 2 * Copyright (C) 2024 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.accessibilityservice; 18 19 import android.annotation.CallbackExecutor; 20 import android.annotation.FlaggedApi; 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.RequiresPermission; 24 import android.annotation.SuppressLint; 25 import android.annotation.TestApi; 26 import android.bluetooth.BluetoothDevice; 27 import android.hardware.usb.UsbDevice; 28 import android.os.Bundle; 29 import android.os.IBinder; 30 import android.os.RemoteException; 31 import android.view.accessibility.AccessibilityInteractionClient; 32 import android.view.accessibility.Flags; 33 34 import java.io.IOException; 35 import java.lang.annotation.Retention; 36 import java.lang.annotation.RetentionPolicy; 37 import java.util.List; 38 import java.util.concurrent.Executor; 39 40 /** 41 * Used to communicate with a Braille display that supports the Braille display HID standard 42 * (usage page 0x41). 43 * 44 * <p>Only one Braille display may be connected at a time. 45 */ 46 // This interface doesn't actually own resources. Its I/O connections are owned, monitored, 47 // and automatically closed by the system after the accessibility service is disconnected. 48 @SuppressLint("NotCloseable") 49 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 50 public interface BrailleDisplayController { 51 52 /** 53 * Throw {@link IllegalStateException} if this feature's aconfig flag is disabled. 54 * 55 * @hide 56 */ checkApiFlagIsEnabled()57 static void checkApiFlagIsEnabled() { 58 if (!Flags.brailleDisplayHid()) { 59 throw new IllegalStateException("Flag BRAILLE_DISPLAY_HID not enabled"); 60 } 61 } 62 63 /** 64 * Interface provided to {@link BrailleDisplayController} connection methods to 65 * receive callbacks from the system. 66 */ 67 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 68 interface BrailleDisplayCallback { 69 /** 70 * The system cannot access connected HID devices. 71 */ 72 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 73 int FLAG_ERROR_CANNOT_ACCESS = 1 << 0; 74 /** 75 * A unique Braille display matching the requested properties could not be identified. 76 */ 77 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 78 int FLAG_ERROR_BRAILLE_DISPLAY_NOT_FOUND = 1 << 1; 79 80 /** @hide */ 81 @Retention(RetentionPolicy.SOURCE) 82 @IntDef(flag = true, prefix = "FLAG_ERROR_", value = { 83 FLAG_ERROR_CANNOT_ACCESS, 84 FLAG_ERROR_BRAILLE_DISPLAY_NOT_FOUND, 85 }) 86 @interface ErrorCode { 87 } 88 89 /** 90 * Callback to observe a successful Braille display connection. 91 * 92 * <p>The provided HID report descriptor should be used to understand the input bytes 93 * received from the Braille display via {@link #onInput} and to prepare 94 * the output sent to the Braille display via {@link #write}. 95 * 96 * @param hidDescriptor The HID report descriptor for this Braille display. 97 * @see #connect(BluetoothDevice, BrailleDisplayCallback) 98 * @see #connect(UsbDevice, BrailleDisplayCallback) 99 */ 100 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) onConnected(@onNull byte[] hidDescriptor)101 void onConnected(@NonNull byte[] hidDescriptor); 102 103 /** 104 * Callback to observe a failed Braille display connection. 105 * 106 * @param errorFlags A bitmask of error codes for the connection failure. 107 * @see #connect(BluetoothDevice, BrailleDisplayCallback) 108 * @see #connect(UsbDevice, BrailleDisplayCallback) 109 */ 110 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) onConnectionFailed(@rrorCode int errorFlags)111 void onConnectionFailed(@ErrorCode int errorFlags); 112 113 /** 114 * Callback to observe input bytes from the currently connected Braille display. 115 * 116 * @param input The input bytes from the Braille display, formatted according to the HID 117 * report descriptor and the HIDRAW kernel driver. 118 */ 119 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) onInput(@onNull byte[] input)120 void onInput(@NonNull byte[] input); 121 122 /** 123 * Callback to observe when the currently connected Braille display is disconnected by the 124 * system. 125 */ 126 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) onDisconnected()127 void onDisconnected(); 128 } 129 130 /** 131 * Connects to the requested bluetooth Braille display using the Braille 132 * display HID standard (usage page 0x41). 133 * 134 * <p>If successful then the HID report descriptor will be provided to 135 * {@link BrailleDisplayCallback#onConnected} 136 * and the Braille display will start sending incoming input bytes to 137 * {@link BrailleDisplayCallback#onInput}. If there is an error in reading input 138 * then the system will disconnect the Braille display. 139 * 140 * <p>Note that the callbacks will be executed on the main thread using 141 * {@link AccessibilityService#getMainExecutor()}. To specify the execution thread, use 142 * {@link #connect(BluetoothDevice, Executor, BrailleDisplayCallback)}. 143 * 144 * @param bluetoothDevice The Braille display device. 145 * @param callback Callbacks used to provide connection results. 146 * @see BrailleDisplayCallback#onConnected 147 * @see BrailleDisplayCallback#onConnectionFailed 148 * @throws IllegalStateException if a Braille display is already connected to this controller. 149 */ 150 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 151 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connect(@onNull BluetoothDevice bluetoothDevice, @NonNull BrailleDisplayCallback callback)152 void connect(@NonNull BluetoothDevice bluetoothDevice, 153 @NonNull BrailleDisplayCallback callback); 154 155 /** 156 * Connects to the requested bluetooth Braille display using the Braille 157 * display HID standard (usage page 0x41). 158 * 159 * <p>If successful then the HID report descriptor will be provided to 160 * {@link BrailleDisplayCallback#onConnected} 161 * and the Braille display will start sending incoming input bytes to 162 * {@link BrailleDisplayCallback#onInput}. If there is an error in reading input 163 * then the system will disconnect the Braille display. 164 * 165 * @param bluetoothDevice The Braille display device. 166 * @param callbackExecutor Executor for executing the provided callbacks. 167 * @param callback Callbacks used to provide connection results. 168 * @see BrailleDisplayCallback#onConnected 169 * @see BrailleDisplayCallback#onConnectionFailed 170 * @throws IllegalStateException if a Braille display is already connected to this controller. 171 */ 172 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 173 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) connect(@onNull BluetoothDevice bluetoothDevice, @NonNull @CallbackExecutor Executor callbackExecutor, @NonNull BrailleDisplayCallback callback)174 void connect(@NonNull BluetoothDevice bluetoothDevice, 175 @NonNull @CallbackExecutor Executor callbackExecutor, 176 @NonNull BrailleDisplayCallback callback); 177 178 /** 179 * Connects to the requested USB Braille display using the Braille 180 * display HID standard (usage page 0x41). 181 * 182 * <p>If successful then the HID report descriptor will be provided to 183 * {@link BrailleDisplayCallback#onConnected} 184 * and the Braille display will start sending incoming input bytes to 185 * {@link BrailleDisplayCallback#onInput}. If there is an error in reading input 186 * then the system will disconnect the Braille display. 187 * 188 * <p>The accessibility service app must already have approval to access the USB device 189 * from the standard {@link android.hardware.usb.UsbManager} access approval process. 190 * 191 * <p>Note that the callbacks will be executed on the main thread using 192 * {@link AccessibilityService#getMainExecutor()}. To specify the execution thread, use 193 * {@link #connect(UsbDevice, Executor, BrailleDisplayCallback)}. 194 * 195 * @param usbDevice The Braille display device. 196 * @param callback Callbacks used to provide connection results. 197 * @see BrailleDisplayCallback#onConnected 198 * @see BrailleDisplayCallback#onConnectionFailed 199 * @throws SecurityException if the caller does not have USB device approval. 200 * @throws IllegalStateException if a Braille display is already connected to this controller. 201 */ 202 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) connect(@onNull UsbDevice usbDevice, @NonNull BrailleDisplayCallback callback)203 void connect(@NonNull UsbDevice usbDevice, 204 @NonNull BrailleDisplayCallback callback); 205 206 /** 207 * Connects to the requested USB Braille display using the Braille 208 * display HID standard (usage page 0x41). 209 * 210 * <p>If successful then the HID report descriptor will be provided to 211 * {@link BrailleDisplayCallback#onConnected} 212 * and the Braille display will start sending incoming input bytes to 213 * {@link BrailleDisplayCallback#onInput}. If there is an error in reading input 214 * then the system will disconnect the Braille display. 215 * 216 * <p>The accessibility service app must already have approval to access the USB device 217 * from the standard {@link android.hardware.usb.UsbManager} access approval process. 218 * 219 * @param usbDevice The Braille display device. 220 * @param callbackExecutor Executor for executing the provided callbacks. 221 * @param callback Callbacks used to provide connection results. 222 * @see BrailleDisplayCallback#onConnected 223 * @see BrailleDisplayCallback#onConnectionFailed 224 * @throws SecurityException if the caller does not have USB device approval. 225 * @throws IllegalStateException if a Braille display is already connected to this controller. 226 */ 227 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) connect(@onNull UsbDevice usbDevice, @NonNull @CallbackExecutor Executor callbackExecutor, @NonNull BrailleDisplayCallback callback)228 void connect(@NonNull UsbDevice usbDevice, 229 @NonNull @CallbackExecutor Executor callbackExecutor, 230 @NonNull BrailleDisplayCallback callback); 231 232 /** 233 * Returns true if a Braille display is currently connected, otherwise false. 234 * 235 * @see #connect 236 */ 237 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) isConnected()238 boolean isConnected(); 239 240 /** 241 * Writes a HID report to the currently connected Braille display. 242 * 243 * <p>This method returns immediately after dispatching the write request to the system. 244 * If the system experiences an error in writing output (e.g. the Braille display is unplugged 245 * after the system receives the write request but before writing the bytes to the Braille 246 * display) then the system will disconnect the Braille display, which calls 247 * {@link BrailleDisplayCallback#onDisconnected()}. 248 * 249 * @param buffer The bytes to write to the Braille display. These bytes should be formatted 250 * according to the HID report descriptor and the HIDRAW kernel driver. 251 * @throws IOException if there is no currently connected Braille display. 252 * @throws IllegalArgumentException if the buffer exceeds the maximum safe payload size for 253 * binder transactions of 254 * {@link IBinder#getSuggestedMaxIpcSizeBytes()} 255 */ 256 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) write(@onNull byte[] buffer)257 void write(@NonNull byte[] buffer) throws IOException; 258 259 /** 260 * Disconnects from the currently connected Braille display. 261 * 262 * @see #isConnected() 263 */ 264 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) disconnect()265 void disconnect(); 266 267 /** 268 * Provides test Braille display data to be used for automated CTS tests. 269 * 270 * <p>See {@code TEST_BRAILLE_DISPLAY_*} bundle keys. 271 * 272 * @hide 273 */ 274 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 275 @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) 276 @TestApi setTestBrailleDisplayData( @onNull AccessibilityService service, @NonNull List<Bundle> brailleDisplays)277 static void setTestBrailleDisplayData( 278 @NonNull AccessibilityService service, 279 @NonNull List<Bundle> brailleDisplays) { 280 checkApiFlagIsEnabled(); 281 final IAccessibilityServiceConnection serviceConnection = 282 AccessibilityInteractionClient.getConnection(service.getConnectionId()); 283 if (serviceConnection != null) { 284 try { 285 serviceConnection.setTestBrailleDisplayData(brailleDisplays); 286 } catch (RemoteException e) { 287 throw e.rethrowFromSystemServer(); 288 } 289 } 290 } 291 292 /** @hide */ 293 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 294 @TestApi 295 String TEST_BRAILLE_DISPLAY_HIDRAW_PATH = "HIDRAW_PATH"; 296 /** @hide */ 297 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 298 @TestApi 299 String TEST_BRAILLE_DISPLAY_DESCRIPTOR = "DESCRIPTOR"; 300 /** @hide */ 301 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 302 @TestApi 303 String TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH = "BUS_BLUETOOTH"; 304 /** @hide */ 305 @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) 306 @TestApi 307 String TEST_BRAILLE_DISPLAY_UNIQUE_ID = "UNIQUE_ID"; 308 /** @hide */ 309 String TEST_BRAILLE_DISPLAY_NAME = "NAME"; 310 } 311