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 package com.android.bluetooth.hfp; 18 19 import android.bluetooth.BluetoothAdapter; 20 import android.bluetooth.BluetoothDevice; 21 import android.util.Log; 22 23 import com.android.bluetooth.Utils; 24 import com.android.internal.annotations.VisibleForTesting; 25 26 /** 27 * Defines native calls that are used by state machine/service to either send or receive 28 * messages to/from the native stack. This file is registered for the native methods in 29 * corresponding CPP file. 30 */ 31 public class HeadsetNativeInterface { 32 private static final String TAG = "HeadsetNativeInterface"; 33 34 private final BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter(); 35 36 static { classInitNative()37 classInitNative(); 38 } 39 40 private static HeadsetNativeInterface sInterface; 41 private static final Object INSTANCE_LOCK = new Object(); 42 HeadsetNativeInterface()43 private HeadsetNativeInterface() {} 44 45 /** 46 * This class is a singleton because native library should only be loaded once 47 * 48 * @return default instance 49 */ getInstance()50 public static HeadsetNativeInterface getInstance() { 51 synchronized (INSTANCE_LOCK) { 52 if (sInterface == null) { 53 sInterface = new HeadsetNativeInterface(); 54 } 55 } 56 return sInterface; 57 } 58 sendMessageToService(HeadsetStackEvent event)59 private void sendMessageToService(HeadsetStackEvent event) { 60 HeadsetService service = HeadsetService.getHeadsetService(); 61 if (service != null) { 62 service.messageFromNative(event); 63 } else { 64 // Service must call cleanup() when quiting and native stack shouldn't send any event 65 // after cleanup() -> cleanupNative() is called. 66 Log.wtf(TAG, "FATAL: Stack sent event while service is not available: " + event); 67 } 68 } 69 getDevice(byte[] address)70 private BluetoothDevice getDevice(byte[] address) { 71 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 72 } 73 onConnectionStateChanged(int state, byte[] address)74 void onConnectionStateChanged(int state, byte[] address) { 75 HeadsetStackEvent event = 76 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED, state, 77 getDevice(address)); 78 sendMessageToService(event); 79 } 80 81 // Callbacks for native code 82 onAudioStateChanged(int state, byte[] address)83 private void onAudioStateChanged(int state, byte[] address) { 84 HeadsetStackEvent event = 85 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED, state, 86 getDevice(address)); 87 sendMessageToService(event); 88 } 89 onVrStateChanged(int state, byte[] address)90 private void onVrStateChanged(int state, byte[] address) { 91 HeadsetStackEvent event = 92 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VR_STATE_CHANGED, state, 93 getDevice(address)); 94 sendMessageToService(event); 95 } 96 onAnswerCall(byte[] address)97 private void onAnswerCall(byte[] address) { 98 HeadsetStackEvent event = 99 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL, getDevice(address)); 100 sendMessageToService(event); 101 } 102 onHangupCall(byte[] address)103 private void onHangupCall(byte[] address) { 104 HeadsetStackEvent event = 105 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL, getDevice(address)); 106 sendMessageToService(event); 107 } 108 onVolumeChanged(int type, int volume, byte[] address)109 private void onVolumeChanged(int type, int volume, byte[] address) { 110 HeadsetStackEvent event = 111 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_VOLUME_CHANGED, type, volume, 112 getDevice(address)); 113 sendMessageToService(event); 114 } 115 onDialCall(String number, byte[] address)116 private void onDialCall(String number, byte[] address) { 117 HeadsetStackEvent event = 118 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_DIAL_CALL, number, 119 getDevice(address)); 120 sendMessageToService(event); 121 } 122 onSendDtmf(int dtmf, byte[] address)123 private void onSendDtmf(int dtmf, byte[] address) { 124 HeadsetStackEvent event = 125 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SEND_DTMF, dtmf, 126 getDevice(address)); 127 sendMessageToService(event); 128 } 129 onNoiseReductionEnable(boolean enable, byte[] address)130 private void onNoiseReductionEnable(boolean enable, byte[] address) { 131 HeadsetStackEvent event = 132 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_NOISE_REDUCTION, enable ? 1 : 0, 133 getDevice(address)); 134 sendMessageToService(event); 135 } 136 onWBS(int codec, byte[] address)137 private void onWBS(int codec, byte[] address) { 138 HeadsetStackEvent event = 139 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_WBS, codec, getDevice(address)); 140 sendMessageToService(event); 141 } 142 onAtChld(int chld, byte[] address)143 private void onAtChld(int chld, byte[] address) { 144 HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CHLD, chld, 145 getDevice(address)); 146 sendMessageToService(event); 147 } 148 onAtCnum(byte[] address)149 private void onAtCnum(byte[] address) { 150 HeadsetStackEvent event = 151 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST, 152 getDevice(address)); 153 sendMessageToService(event); 154 } 155 onAtCind(byte[] address)156 private void onAtCind(byte[] address) { 157 HeadsetStackEvent event = 158 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CIND, getDevice(address)); 159 sendMessageToService(event); 160 } 161 onAtCops(byte[] address)162 private void onAtCops(byte[] address) { 163 HeadsetStackEvent event = 164 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_COPS, getDevice(address)); 165 sendMessageToService(event); 166 } 167 onAtClcc(byte[] address)168 private void onAtClcc(byte[] address) { 169 HeadsetStackEvent event = 170 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CLCC, getDevice(address)); 171 sendMessageToService(event); 172 } 173 onUnknownAt(String atString, byte[] address)174 private void onUnknownAt(String atString, byte[] address) { 175 HeadsetStackEvent event = 176 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT, atString, 177 getDevice(address)); 178 sendMessageToService(event); 179 } 180 onKeyPressed(byte[] address)181 private void onKeyPressed(byte[] address) { 182 HeadsetStackEvent event = 183 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED, getDevice(address)); 184 sendMessageToService(event); 185 } 186 onATBind(String atString, byte[] address)187 private void onATBind(String atString, byte[] address) { 188 HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIND, atString, 189 getDevice(address)); 190 sendMessageToService(event); 191 } 192 onATBiev(int indId, int indValue, byte[] address)193 private void onATBiev(int indId, int indValue, byte[] address) { 194 HeadsetStackEvent event = 195 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIEV, indId, indValue, 196 getDevice(address)); 197 sendMessageToService(event); 198 } 199 onAtBia(boolean service, boolean roam, boolean signal, boolean battery, byte[] address)200 private void onAtBia(boolean service, boolean roam, boolean signal, boolean battery, 201 byte[] address) { 202 HeadsetAgIndicatorEnableState agIndicatorEnableState = 203 new HeadsetAgIndicatorEnableState(service, roam, signal, battery); 204 HeadsetStackEvent event = 205 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIA, agIndicatorEnableState, 206 getDevice(address)); 207 sendMessageToService(event); 208 } 209 210 // Native wrappers to help unit testing 211 212 /** 213 * Initialize native stack 214 * 215 * @param maxHfClients maximum number of headset clients that can be connected simultaneously 216 * @param inbandRingingEnabled whether in-band ringing is enabled on this AG 217 */ 218 @VisibleForTesting init(int maxHfClients, boolean inbandRingingEnabled)219 public void init(int maxHfClients, boolean inbandRingingEnabled) { 220 initializeNative(maxHfClients, inbandRingingEnabled); 221 } 222 223 /** 224 * Closes the interface 225 */ 226 @VisibleForTesting cleanup()227 public void cleanup() { 228 cleanupNative(); 229 } 230 231 /** 232 * ok/error response 233 * 234 * @param device target device 235 * @param responseCode 0 - ERROR, 1 - OK 236 * @param errorCode error code in case of ERROR 237 * @return True on success, False on failure 238 */ 239 @VisibleForTesting atResponseCode(BluetoothDevice device, int responseCode, int errorCode)240 public boolean atResponseCode(BluetoothDevice device, int responseCode, int errorCode) { 241 return atResponseCodeNative(responseCode, errorCode, Utils.getByteAddress(device)); 242 } 243 244 /** 245 * Pre-formatted AT response, typically in response to unknown AT cmd 246 * 247 * @param device target device 248 * @param responseString formatted AT response string 249 * @return True on success, False on failure 250 */ 251 @VisibleForTesting atResponseString(BluetoothDevice device, String responseString)252 public boolean atResponseString(BluetoothDevice device, String responseString) { 253 return atResponseStringNative(responseString, Utils.getByteAddress(device)); 254 } 255 256 /** 257 * Connect to headset 258 * 259 * @param device target headset 260 * @return True on success, False on failure 261 */ 262 @VisibleForTesting connectHfp(BluetoothDevice device)263 public boolean connectHfp(BluetoothDevice device) { 264 return connectHfpNative(Utils.getByteAddress(device)); 265 } 266 267 /** 268 * Disconnect from headset 269 * 270 * @param device target headset 271 * @return True on success, False on failure 272 */ 273 @VisibleForTesting disconnectHfp(BluetoothDevice device)274 public boolean disconnectHfp(BluetoothDevice device) { 275 return disconnectHfpNative(Utils.getByteAddress(device)); 276 } 277 278 /** 279 * Connect HFP audio (SCO) to headset 280 * 281 * @param device target headset 282 * @return True on success, False on failure 283 */ 284 @VisibleForTesting connectAudio(BluetoothDevice device)285 public boolean connectAudio(BluetoothDevice device) { 286 return connectAudioNative(Utils.getByteAddress(device)); 287 } 288 289 /** 290 * Disconnect HFP audio (SCO) from to headset 291 * 292 * @param device target headset 293 * @return True on success, False on failure 294 */ 295 @VisibleForTesting disconnectAudio(BluetoothDevice device)296 public boolean disconnectAudio(BluetoothDevice device) { 297 return disconnectAudioNative(Utils.getByteAddress(device)); 298 } 299 300 /** 301 * Checks whether the device support echo cancellation and/or noise reduction via the AT+BRSF 302 * bitmask 303 * 304 * @param device target headset 305 * @return true if the device support echo cancellation or noise reduction, false otherwise 306 */ isNoiseReductionSupported(BluetoothDevice device)307 public boolean isNoiseReductionSupported(BluetoothDevice device) { 308 return isNoiseReductionSupportedNative(Utils.getByteAddress(device)); 309 } 310 311 /** 312 * Checks whether the device supports voice recognition via the AT+BRSF bitmask 313 * 314 * @param device target headset 315 * @return true if the device supports voice recognition, false otherwise 316 */ isVoiceRecognitionSupported(BluetoothDevice device)317 public boolean isVoiceRecognitionSupported(BluetoothDevice device) { 318 return isVoiceRecognitionSupportedNative(Utils.getByteAddress(device)); 319 } 320 321 /** 322 * Start voice recognition 323 * 324 * @param device target headset 325 * @return True on success, False on failure 326 */ 327 @VisibleForTesting startVoiceRecognition(BluetoothDevice device)328 public boolean startVoiceRecognition(BluetoothDevice device) { 329 return startVoiceRecognitionNative(Utils.getByteAddress(device)); 330 } 331 332 333 /** 334 * Stop voice recognition 335 * 336 * @param device target headset 337 * @return True on success, False on failure 338 */ 339 @VisibleForTesting stopVoiceRecognition(BluetoothDevice device)340 public boolean stopVoiceRecognition(BluetoothDevice device) { 341 return stopVoiceRecognitionNative(Utils.getByteAddress(device)); 342 } 343 344 /** 345 * Set HFP audio (SCO) volume 346 * 347 * @param device target headset 348 * @param volumeType type of volume 349 * @param volume value value 350 * @return True on success, False on failure 351 */ 352 @VisibleForTesting setVolume(BluetoothDevice device, int volumeType, int volume)353 public boolean setVolume(BluetoothDevice device, int volumeType, int volume) { 354 return setVolumeNative(volumeType, volume, Utils.getByteAddress(device)); 355 } 356 357 /** 358 * Response for CIND command 359 * 360 * @param device target device 361 * @param service service availability, 0 - no service, 1 - presence of service 362 * @param numActive number of active calls 363 * @param numHeld number of held calls 364 * @param callState overall call state [0-6] 365 * @param signal signal quality [0-5] 366 * @param roam roaming indicator, 0 - not roaming, 1 - roaming 367 * @param batteryCharge battery charge level [0-5] 368 * @return True on success, False on failure 369 */ 370 @VisibleForTesting cindResponse(BluetoothDevice device, int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge)371 public boolean cindResponse(BluetoothDevice device, int service, int numActive, int numHeld, 372 int callState, int signal, int roam, int batteryCharge) { 373 return cindResponseNative(service, numActive, numHeld, callState, signal, roam, 374 batteryCharge, Utils.getByteAddress(device)); 375 } 376 377 /** 378 * Combined device status change notification 379 * 380 * @param device target device 381 * @param deviceState device status object 382 * @return True on success, False on failure 383 */ 384 @VisibleForTesting notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState)385 public boolean notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState) { 386 return notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, 387 deviceState.mSignal, deviceState.mBatteryCharge, Utils.getByteAddress(device)); 388 } 389 390 /** 391 * Response for CLCC command. Can be iteratively called for each call index. Call index of 0 392 * will be treated as NULL termination (Completes response) 393 * 394 * @param device target device 395 * @param index index of the call given by the sequence of setting up or receiving the calls 396 * as seen by the served subscriber. Calls hold their number until they are released. New 397 * calls take the lowest available number. 398 * @param dir direction of the call, 0 (outgoing), 1 (incoming) 399 * @param status 0 = Active, 1 = Held, 2 = Dialing (outgoing calls only), 3 = Alerting 400 * (outgoing calls only), 4 = Incoming (incoming calls only), 5 = Waiting (incoming calls 401 * only), 6 = Call held by Response and Hold 402 * @param mode 0 (Voice), 1 (Data), 2 (FAX) 403 * @param mpty 0 - this call is NOT a member of a multi-party (conference) call, 1 - this 404 * call IS a member of a multi-party (conference) call 405 * @param number optional 406 * @param type optional 407 * @return True on success, False on failure 408 */ 409 @VisibleForTesting clccResponse(BluetoothDevice device, int index, int dir, int status, int mode, boolean mpty, String number, int type)410 public boolean clccResponse(BluetoothDevice device, int index, int dir, int status, int mode, 411 boolean mpty, String number, int type) { 412 return clccResponseNative(index, dir, status, mode, mpty, number, type, 413 Utils.getByteAddress(device)); 414 } 415 416 /** 417 * Response for COPS command 418 * 419 * @param device target device 420 * @param operatorName operator name 421 * @return True on success, False on failure 422 */ 423 @VisibleForTesting copsResponse(BluetoothDevice device, String operatorName)424 public boolean copsResponse(BluetoothDevice device, String operatorName) { 425 return copsResponseNative(operatorName, Utils.getByteAddress(device)); 426 } 427 428 /** 429 * Notify of a call state change 430 * Each update notifies 431 * 1. Number of active/held/ringing calls 432 * 2. call_state: This denotes the state change that triggered this msg 433 * This will take one of the values from BtHfCallState 434 * 3. number & type: valid only for incoming & waiting call 435 * 436 * @param device target device for this update 437 * @param callState callstate structure 438 * @return True on success, False on failure 439 */ 440 @VisibleForTesting phoneStateChange(BluetoothDevice device, HeadsetCallState callState)441 public boolean phoneStateChange(BluetoothDevice device, HeadsetCallState callState) { 442 return phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 443 callState.mCallState, callState.mNumber, callState.mType, callState.mName, 444 Utils.getByteAddress(device)); 445 } 446 447 /** 448 * Set whether we will initiate SCO or not 449 * 450 * @param value True to enable, False to disable 451 * @return True on success, False on failure 452 */ 453 @VisibleForTesting setScoAllowed(boolean value)454 public boolean setScoAllowed(boolean value) { 455 return setScoAllowedNative(value); 456 } 457 458 /** 459 * Enable or disable in-band ringing for the current service level connection through sending 460 * +BSIR AT command 461 * 462 * @param value True to enable, False to disable 463 * @return True on success, False on failure 464 */ 465 @VisibleForTesting sendBsir(BluetoothDevice device, boolean value)466 public boolean sendBsir(BluetoothDevice device, boolean value) { 467 return sendBsirNative(value, Utils.getByteAddress(device)); 468 } 469 470 /** 471 * Set the current active headset device for SCO audio 472 * @param device current active SCO device 473 * @return true on success 474 */ 475 @VisibleForTesting setActiveDevice(BluetoothDevice device)476 public boolean setActiveDevice(BluetoothDevice device) { 477 return setActiveDeviceNative(Utils.getByteAddress(device)); 478 } 479 480 /* Native methods */ classInitNative()481 private static native void classInitNative(); 482 atResponseCodeNative(int responseCode, int errorCode, byte[] address)483 private native boolean atResponseCodeNative(int responseCode, int errorCode, byte[] address); 484 atResponseStringNative(String responseString, byte[] address)485 private native boolean atResponseStringNative(String responseString, byte[] address); 486 initializeNative(int maxHfClients, boolean inbandRingingEnabled)487 private native void initializeNative(int maxHfClients, boolean inbandRingingEnabled); 488 cleanupNative()489 private native void cleanupNative(); 490 connectHfpNative(byte[] address)491 private native boolean connectHfpNative(byte[] address); 492 disconnectHfpNative(byte[] address)493 private native boolean disconnectHfpNative(byte[] address); 494 connectAudioNative(byte[] address)495 private native boolean connectAudioNative(byte[] address); 496 disconnectAudioNative(byte[] address)497 private native boolean disconnectAudioNative(byte[] address); 498 isNoiseReductionSupportedNative(byte[] address)499 private native boolean isNoiseReductionSupportedNative(byte[] address); 500 isVoiceRecognitionSupportedNative(byte[] address)501 private native boolean isVoiceRecognitionSupportedNative(byte[] address); 502 startVoiceRecognitionNative(byte[] address)503 private native boolean startVoiceRecognitionNative(byte[] address); 504 stopVoiceRecognitionNative(byte[] address)505 private native boolean stopVoiceRecognitionNative(byte[] address); 506 setVolumeNative(int volumeType, int volume, byte[] address)507 private native boolean setVolumeNative(int volumeType, int volume, byte[] address); 508 cindResponseNative(int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge, byte[] address)509 private native boolean cindResponseNative(int service, int numActive, int numHeld, 510 int callState, int signal, int roam, int batteryCharge, byte[] address); 511 notifyDeviceStatusNative(int networkState, int serviceType, int signal, int batteryCharge, byte[] address)512 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 513 int batteryCharge, byte[] address); 514 clccResponseNative(int index, int dir, int status, int mode, boolean mpty, String number, int type, byte[] address)515 private native boolean clccResponseNative(int index, int dir, int status, int mode, 516 boolean mpty, String number, int type, byte[] address); 517 copsResponseNative(String operatorName, byte[] address)518 private native boolean copsResponseNative(String operatorName, byte[] address); 519 phoneStateChangeNative(int numActive, int numHeld, int callState, String number, int type, String name, byte[] address)520 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 521 String number, int type, String name, byte[] address); 522 setScoAllowedNative(boolean value)523 private native boolean setScoAllowedNative(boolean value); 524 sendBsirNative(boolean value, byte[] address)525 private native boolean sendBsirNative(boolean value, byte[] address); 526 setActiveDeviceNative(byte[] address)527 private native boolean setActiveDeviceNative(byte[] address); 528 } 529