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