1 /* 2 * Copyright 2017 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 /* 18 * Defines the native inteface that is used by HID Device service to 19 * send or receive messages from the native stack. This file is registered 20 * for the native methods in the corresponding JNI C++ file. 21 */ 22 23 package com.android.bluetooth.hid; 24 25 import android.bluetooth.BluetoothAdapter; 26 import android.bluetooth.BluetoothDevice; 27 import android.util.Log; 28 29 import com.android.bluetooth.Utils; 30 import com.android.bluetooth.btservice.AdapterService; 31 import com.android.bluetooth.flags.Flags; 32 import com.android.internal.annotations.GuardedBy; 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.util.Objects; 36 37 /** HID Device Native Interface to/from JNI. */ 38 public class HidDeviceNativeInterface { 39 private static final String TAG = "HidDeviceNativeInterface"; 40 private BluetoothAdapter mAdapter; 41 private AdapterService mAdapterService; 42 43 @GuardedBy("INSTANCE_LOCK") 44 private static HidDeviceNativeInterface sInstance; 45 46 private static final Object INSTANCE_LOCK = new Object(); 47 48 @VisibleForTesting HidDeviceNativeInterface()49 private HidDeviceNativeInterface() { 50 mAdapter = BluetoothAdapter.getDefaultAdapter(); 51 if (mAdapter == null) { 52 Log.wtf(TAG, "No Bluetooth Adapter Available"); 53 } 54 mAdapterService = 55 Objects.requireNonNull( 56 AdapterService.getAdapterService(), 57 "AdapterService cannot be null when HidDeviceNativeInterface init"); 58 } 59 60 /** Get the singleton instance. */ getInstance()61 public static HidDeviceNativeInterface getInstance() { 62 synchronized (INSTANCE_LOCK) { 63 if (sInstance == null) { 64 sInstance = new HidDeviceNativeInterface(); 65 } 66 return sInstance; 67 } 68 } 69 70 /** Set singleton instance. */ 71 @VisibleForTesting setInstance(HidDeviceNativeInterface instance)72 public static void setInstance(HidDeviceNativeInterface instance) { 73 synchronized (INSTANCE_LOCK) { 74 sInstance = instance; 75 } 76 } 77 78 /** Initializes the native interface. */ init()79 public void init() { 80 initNative(); 81 } 82 83 /** Cleanup the native interface. */ cleanup()84 public void cleanup() { 85 cleanupNative(); 86 } 87 88 /** 89 * Registers the application 90 * 91 * @param name name of the HID Device application 92 * @param description description of the HID Device application 93 * @param provider provider of the HID Device application 94 * @param subclass subclass of the HID Device application 95 * @param descriptors HID descriptors 96 * @param inQos incoming QoS settings 97 * @param outQos outgoing QoS settings 98 * @return the result of the native call 99 */ registerApp( String name, String description, String provider, byte subclass, byte[] descriptors, int[] inQos, int[] outQos)100 public boolean registerApp( 101 String name, 102 String description, 103 String provider, 104 byte subclass, 105 byte[] descriptors, 106 int[] inQos, 107 int[] outQos) { 108 return registerAppNative(name, description, provider, subclass, descriptors, inQos, outQos); 109 } 110 111 /** 112 * Unregisters the application 113 * 114 * @return the result of the native call 115 */ unregisterApp()116 public boolean unregisterApp() { 117 return unregisterAppNative(); 118 } 119 120 /** 121 * Send report to the remote host 122 * 123 * @param id report ID 124 * @param data report data array 125 * @return the result of the native call 126 */ sendReport(int id, byte[] data)127 public boolean sendReport(int id, byte[] data) { 128 return sendReportNative(id, data); 129 } 130 131 /** 132 * Reply report to the remote host 133 * 134 * @param type report type 135 * @param id report ID 136 * @param data report data array 137 * @return the result of the native call 138 */ replyReport(byte type, byte id, byte[] data)139 public boolean replyReport(byte type, byte id, byte[] data) { 140 return replyReportNative(type, id, data); 141 } 142 143 /** 144 * Send virtual unplug to the remote host 145 * 146 * @return the result of the native call 147 */ unplug()148 public boolean unplug() { 149 return unplugNative(); 150 } 151 152 /** 153 * Connect to the remote host 154 * 155 * @param device remote host device 156 * @return the result of the native call 157 */ connect(BluetoothDevice device)158 public boolean connect(BluetoothDevice device) { 159 return connectNative(getByteAddress(device)); 160 } 161 162 /** 163 * Disconnect from the remote host 164 * 165 * @return the result of the native call 166 */ disconnect()167 public boolean disconnect() { 168 return disconnectNative(); 169 } 170 171 /** 172 * Report error to the remote host 173 * 174 * @param error error byte 175 * @return the result of the native call 176 */ reportError(byte error)177 public boolean reportError(byte error) { 178 return reportErrorNative(error); 179 } 180 181 @VisibleForTesting onApplicationStateChanged(byte[] address, boolean registered)182 synchronized void onApplicationStateChanged(byte[] address, boolean registered) { 183 HidDeviceService service = HidDeviceService.getHidDeviceService(); 184 if (service != null) { 185 service.onApplicationStateChangedFromNative(getDevice(address), registered); 186 } else { 187 Log.wtf( 188 TAG, 189 "FATAL: onApplicationStateChanged() " 190 + "is called from the stack while service is not available."); 191 } 192 } 193 194 @VisibleForTesting onConnectStateChanged(byte[] address, int state)195 synchronized void onConnectStateChanged(byte[] address, int state) { 196 HidDeviceService service = HidDeviceService.getHidDeviceService(); 197 if (service != null) { 198 service.onConnectStateChangedFromNative(getDevice(address), state); 199 } else { 200 Log.wtf( 201 TAG, 202 "FATAL: onConnectStateChanged() " 203 + "is called from the stack while service is not available."); 204 } 205 } 206 207 @VisibleForTesting onGetReport(byte type, byte id, short bufferSize)208 synchronized void onGetReport(byte type, byte id, short bufferSize) { 209 HidDeviceService service = HidDeviceService.getHidDeviceService(); 210 if (service != null) { 211 service.onGetReportFromNative(type, id, bufferSize); 212 } else { 213 Log.wtf( 214 TAG, 215 "FATAL: onGetReport() " 216 + "is called from the stack while service is not available."); 217 } 218 } 219 220 @VisibleForTesting onSetReport(byte reportType, byte reportId, byte[] data)221 synchronized void onSetReport(byte reportType, byte reportId, byte[] data) { 222 HidDeviceService service = HidDeviceService.getHidDeviceService(); 223 if (service != null) { 224 service.onSetReportFromNative(reportType, reportId, data); 225 } else { 226 Log.wtf( 227 TAG, 228 "FATAL: onSetReport() " 229 + "is called from the stack while service is not available."); 230 } 231 } 232 233 @VisibleForTesting onSetProtocol(byte protocol)234 synchronized void onSetProtocol(byte protocol) { 235 HidDeviceService service = HidDeviceService.getHidDeviceService(); 236 if (service != null) { 237 service.onSetProtocolFromNative(protocol); 238 } else { 239 Log.wtf( 240 TAG, 241 "FATAL: onSetProtocol() " 242 + "is called from the stack while service is not available."); 243 } 244 } 245 246 @VisibleForTesting onInterruptData(byte reportId, byte[] data)247 synchronized void onInterruptData(byte reportId, byte[] data) { 248 HidDeviceService service = HidDeviceService.getHidDeviceService(); 249 if (service != null) { 250 service.onInterruptDataFromNative(reportId, data); 251 } else { 252 Log.wtf( 253 TAG, 254 "FATAL: onInterruptData() " 255 + "is called from the stack while service is not available."); 256 } 257 } 258 259 @VisibleForTesting onVirtualCableUnplug()260 synchronized void onVirtualCableUnplug() { 261 HidDeviceService service = HidDeviceService.getHidDeviceService(); 262 if (service != null) { 263 service.onVirtualCableUnplugFromNative(); 264 } else { 265 Log.wtf( 266 TAG, 267 "FATAL: onVirtualCableUnplug() " 268 + "is called from the stack while service is not available."); 269 } 270 } 271 getDevice(byte[] address)272 private BluetoothDevice getDevice(byte[] address) { 273 if (address == null) { 274 return null; 275 } 276 return mAdapterService.getDeviceFromByte(address); 277 } 278 getByteAddress(BluetoothDevice device)279 private byte[] getByteAddress(BluetoothDevice device) { 280 if (Flags.identityAddressNullIfUnknown()) { 281 return Utils.getByteBrEdrAddress(device); 282 } else { 283 return mAdapterService.getByteIdentityAddress(device); 284 } 285 } 286 initNative()287 private native void initNative(); 288 cleanupNative()289 private native void cleanupNative(); 290 registerAppNative( String name, String description, String provider, byte subclass, byte[] descriptors, int[] inQos, int[] outQos)291 private native boolean registerAppNative( 292 String name, 293 String description, 294 String provider, 295 byte subclass, 296 byte[] descriptors, 297 int[] inQos, 298 int[] outQos); 299 unregisterAppNative()300 private native boolean unregisterAppNative(); 301 sendReportNative(int id, byte[] data)302 private native boolean sendReportNative(int id, byte[] data); 303 replyReportNative(byte type, byte id, byte[] data)304 private native boolean replyReportNative(byte type, byte id, byte[] data); 305 unplugNative()306 private native boolean unplugNative(); 307 connectNative(byte[] btAddress)308 private native boolean connectNative(byte[] btAddress); 309 disconnectNative()310 private native boolean disconnectNative(); 311 reportErrorNative(byte error)312 private native boolean reportErrorNative(byte error); 313 } 314