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