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 onAtChld(int chld, byte[] address)153 private void onAtChld(int chld, byte[] address) { 154 HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CHLD, chld, 155 getDevice(address)); 156 sendMessageToService(event); 157 } 158 onAtCnum(byte[] address)159 private void onAtCnum(byte[] address) { 160 HeadsetStackEvent event = 161 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_SUBSCRIBER_NUMBER_REQUEST, 162 getDevice(address)); 163 sendMessageToService(event); 164 } 165 onAtCind(byte[] address)166 private void onAtCind(byte[] address) { 167 HeadsetStackEvent event = 168 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CIND, getDevice(address)); 169 sendMessageToService(event); 170 } 171 onAtCops(byte[] address)172 private void onAtCops(byte[] address) { 173 HeadsetStackEvent event = 174 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_COPS, getDevice(address)); 175 sendMessageToService(event); 176 } 177 onAtClcc(byte[] address)178 private void onAtClcc(byte[] address) { 179 HeadsetStackEvent event = 180 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CLCC, getDevice(address)); 181 sendMessageToService(event); 182 } 183 onUnknownAt(String atString, byte[] address)184 private void onUnknownAt(String atString, byte[] address) { 185 HeadsetStackEvent event = 186 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_UNKNOWN_AT, atString, 187 getDevice(address)); 188 sendMessageToService(event); 189 } 190 onKeyPressed(byte[] address)191 private void onKeyPressed(byte[] address) { 192 HeadsetStackEvent event = 193 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED, getDevice(address)); 194 sendMessageToService(event); 195 } 196 onATBind(String atString, byte[] address)197 private void onATBind(String atString, byte[] address) { 198 HeadsetStackEvent event = new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIND, atString, 199 getDevice(address)); 200 sendMessageToService(event); 201 } 202 onATBiev(int indId, int indValue, byte[] address)203 private void onATBiev(int indId, int indValue, byte[] address) { 204 HeadsetStackEvent event = 205 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIEV, indId, indValue, 206 getDevice(address)); 207 sendMessageToService(event); 208 } 209 onAtBia(boolean service, boolean roam, boolean signal, boolean battery, byte[] address)210 private void onAtBia(boolean service, boolean roam, boolean signal, boolean battery, 211 byte[] address) { 212 HeadsetAgIndicatorEnableState agIndicatorEnableState = 213 new HeadsetAgIndicatorEnableState(service, roam, signal, battery); 214 HeadsetStackEvent event = 215 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_BIA, agIndicatorEnableState, 216 getDevice(address)); 217 sendMessageToService(event); 218 } 219 220 // Native wrappers to help unit testing 221 222 /** 223 * Initialize native stack 224 * 225 * @param maxHfClients maximum number of headset clients that can be connected simultaneously 226 * @param inbandRingingEnabled whether in-band ringing is enabled on this AG 227 */ 228 @VisibleForTesting init(int maxHfClients, boolean inbandRingingEnabled)229 public void init(int maxHfClients, boolean inbandRingingEnabled) { 230 initializeNative(maxHfClients, inbandRingingEnabled); 231 } 232 233 /** 234 * Closes the interface 235 */ 236 @VisibleForTesting cleanup()237 public void cleanup() { 238 cleanupNative(); 239 } 240 241 /** 242 * ok/error response 243 * 244 * @param device target device 245 * @param responseCode 0 - ERROR, 1 - OK 246 * @param errorCode error code in case of ERROR 247 * @return True on success, False on failure 248 */ 249 @VisibleForTesting atResponseCode(BluetoothDevice device, int responseCode, int errorCode)250 public boolean atResponseCode(BluetoothDevice device, int responseCode, int errorCode) { 251 return atResponseCodeNative(responseCode, errorCode, getByteAddress(device)); 252 } 253 254 /** 255 * Pre-formatted AT response, typically in response to unknown AT cmd 256 * 257 * @param device target device 258 * @param responseString formatted AT response string 259 * @return True on success, False on failure 260 */ 261 @VisibleForTesting atResponseString(BluetoothDevice device, String responseString)262 public boolean atResponseString(BluetoothDevice device, String responseString) { 263 return atResponseStringNative(responseString, getByteAddress(device)); 264 } 265 266 /** 267 * Connect to headset 268 * 269 * @param device target headset 270 * @return True on success, False on failure 271 */ 272 @VisibleForTesting connectHfp(BluetoothDevice device)273 public boolean connectHfp(BluetoothDevice device) { 274 return connectHfpNative(getByteAddress(device)); 275 } 276 277 /** 278 * Disconnect from headset 279 * 280 * @param device target headset 281 * @return True on success, False on failure 282 */ 283 @VisibleForTesting disconnectHfp(BluetoothDevice device)284 public boolean disconnectHfp(BluetoothDevice device) { 285 return disconnectHfpNative(getByteAddress(device)); 286 } 287 288 /** 289 * Connect HFP audio (SCO) to headset 290 * 291 * @param device target headset 292 * @return True on success, False on failure 293 */ 294 @VisibleForTesting connectAudio(BluetoothDevice device)295 public boolean connectAudio(BluetoothDevice device) { 296 return connectAudioNative(getByteAddress(device)); 297 } 298 299 /** 300 * Disconnect HFP audio (SCO) from to headset 301 * 302 * @param device target headset 303 * @return True on success, False on failure 304 */ 305 @VisibleForTesting disconnectAudio(BluetoothDevice device)306 public boolean disconnectAudio(BluetoothDevice device) { 307 return disconnectAudioNative(getByteAddress(device)); 308 } 309 310 /** 311 * Checks whether the device support echo cancellation and/or noise reduction via the AT+BRSF 312 * bitmask 313 * 314 * @param device target headset 315 * @return true if the device support echo cancellation or noise reduction, false otherwise 316 */ isNoiseReductionSupported(BluetoothDevice device)317 public boolean isNoiseReductionSupported(BluetoothDevice device) { 318 return isNoiseReductionSupportedNative(getByteAddress(device)); 319 } 320 321 /** 322 * Checks whether the device supports voice recognition via the AT+BRSF bitmask 323 * 324 * @param device target headset 325 * @return true if the device supports voice recognition, false otherwise 326 */ isVoiceRecognitionSupported(BluetoothDevice device)327 public boolean isVoiceRecognitionSupported(BluetoothDevice device) { 328 return isVoiceRecognitionSupportedNative(getByteAddress(device)); 329 } 330 331 /** 332 * Start voice recognition 333 * 334 * @param device target headset 335 * @return True on success, False on failure 336 */ 337 @VisibleForTesting startVoiceRecognition(BluetoothDevice device)338 public boolean startVoiceRecognition(BluetoothDevice device) { 339 return startVoiceRecognitionNative(getByteAddress(device)); 340 } 341 342 343 /** 344 * Stop voice recognition 345 * 346 * @param device target headset 347 * @return True on success, False on failure 348 */ 349 @VisibleForTesting stopVoiceRecognition(BluetoothDevice device)350 public boolean stopVoiceRecognition(BluetoothDevice device) { 351 return stopVoiceRecognitionNative(getByteAddress(device)); 352 } 353 354 /** 355 * Set HFP audio (SCO) volume 356 * 357 * @param device target headset 358 * @param volumeType type of volume 359 * @param volume value value 360 * @return True on success, False on failure 361 */ 362 @VisibleForTesting setVolume(BluetoothDevice device, int volumeType, int volume)363 public boolean setVolume(BluetoothDevice device, int volumeType, int volume) { 364 return setVolumeNative(volumeType, volume, getByteAddress(device)); 365 } 366 367 /** 368 * Response for CIND command 369 * 370 * @param device target device 371 * @param service service availability, 0 - no service, 1 - presence of service 372 * @param numActive number of active calls 373 * @param numHeld number of held calls 374 * @param callState overall call state [0-6] 375 * @param signal signal quality [0-5] 376 * @param roam roaming indicator, 0 - not roaming, 1 - roaming 377 * @param batteryCharge battery charge level [0-5] 378 * @return True on success, False on failure 379 */ 380 @VisibleForTesting cindResponse(BluetoothDevice device, int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge)381 public boolean cindResponse(BluetoothDevice device, int service, int numActive, int numHeld, 382 int callState, int signal, int roam, int batteryCharge) { 383 return cindResponseNative(service, numActive, numHeld, callState, signal, roam, 384 batteryCharge, getByteAddress(device)); 385 } 386 387 /** 388 * Combined device status change notification 389 * 390 * @param device target device 391 * @param deviceState device status object 392 * @return True on success, False on failure 393 */ 394 @VisibleForTesting notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState)395 public boolean notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState) { 396 return notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, 397 deviceState.mSignal, deviceState.mBatteryCharge, getByteAddress(device)); 398 } 399 400 /** 401 * Response for CLCC command. Can be iteratively called for each call index. Call index of 0 402 * will be treated as NULL termination (Completes response) 403 * 404 * @param device target device 405 * @param index index of the call given by the sequence of setting up or receiving the calls 406 * as seen by the served subscriber. Calls hold their number until they are released. New 407 * calls take the lowest available number. 408 * @param dir direction of the call, 0 (outgoing), 1 (incoming) 409 * @param status 0 = Active, 1 = Held, 2 = Dialing (outgoing calls only), 3 = Alerting 410 * (outgoing calls only), 4 = Incoming (incoming calls only), 5 = Waiting (incoming calls 411 * only), 6 = Call held by Response and Hold 412 * @param mode 0 (Voice), 1 (Data), 2 (FAX) 413 * @param mpty 0 - this call is NOT a member of a multi-party (conference) call, 1 - this 414 * call IS a member of a multi-party (conference) call 415 * @param number optional 416 * @param type optional 417 * @return True on success, False on failure 418 */ 419 @VisibleForTesting clccResponse(BluetoothDevice device, int index, int dir, int status, int mode, boolean mpty, String number, int type)420 public boolean clccResponse(BluetoothDevice device, int index, int dir, int status, int mode, 421 boolean mpty, String number, int type) { 422 return clccResponseNative(index, dir, status, mode, mpty, number, type, 423 getByteAddress(device)); 424 } 425 426 /** 427 * Response for COPS command 428 * 429 * @param device target device 430 * @param operatorName operator name 431 * @return True on success, False on failure 432 */ 433 @VisibleForTesting copsResponse(BluetoothDevice device, String operatorName)434 public boolean copsResponse(BluetoothDevice device, String operatorName) { 435 return copsResponseNative(operatorName, getByteAddress(device)); 436 } 437 438 /** 439 * Notify of a call state change 440 * Each update notifies 441 * 1. Number of active/held/ringing calls 442 * 2. call_state: This denotes the state change that triggered this msg 443 * This will take one of the values from BtHfCallState 444 * 3. number & type: valid only for incoming & waiting call 445 * 446 * @param device target device for this update 447 * @param callState callstate structure 448 * @return True on success, False on failure 449 */ 450 @VisibleForTesting phoneStateChange(BluetoothDevice device, HeadsetCallState callState)451 public boolean phoneStateChange(BluetoothDevice device, HeadsetCallState callState) { 452 return phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 453 callState.mCallState, callState.mNumber, callState.mType, callState.mName, 454 getByteAddress(device)); 455 } 456 457 /** 458 * Set whether we will initiate SCO or not 459 * 460 * @param value True to enable, False to disable 461 * @return True on success, False on failure 462 */ 463 @VisibleForTesting setScoAllowed(boolean value)464 public boolean setScoAllowed(boolean value) { 465 return setScoAllowedNative(value); 466 } 467 468 /** 469 * Enable or disable in-band ringing for the current service level connection through sending 470 * +BSIR AT command 471 * 472 * @param value True to enable, False to disable 473 * @return True on success, False on failure 474 */ 475 @VisibleForTesting sendBsir(BluetoothDevice device, boolean value)476 public boolean sendBsir(BluetoothDevice device, boolean value) { 477 return sendBsirNative(value, getByteAddress(device)); 478 } 479 480 /** 481 * Set the current active headset device for SCO audio 482 * @param device current active SCO device 483 * @return true on success 484 */ 485 @VisibleForTesting setActiveDevice(BluetoothDevice device)486 public boolean setActiveDevice(BluetoothDevice device) { 487 return setActiveDeviceNative(getByteAddress(device)); 488 } 489 490 /* Native methods */ classInitNative()491 private static native void classInitNative(); 492 atResponseCodeNative(int responseCode, int errorCode, byte[] address)493 private native boolean atResponseCodeNative(int responseCode, int errorCode, byte[] address); 494 atResponseStringNative(String responseString, byte[] address)495 private native boolean atResponseStringNative(String responseString, byte[] address); 496 initializeNative(int maxHfClients, boolean inbandRingingEnabled)497 private native void initializeNative(int maxHfClients, boolean inbandRingingEnabled); 498 cleanupNative()499 private native void cleanupNative(); 500 connectHfpNative(byte[] address)501 private native boolean connectHfpNative(byte[] address); 502 disconnectHfpNative(byte[] address)503 private native boolean disconnectHfpNative(byte[] address); 504 connectAudioNative(byte[] address)505 private native boolean connectAudioNative(byte[] address); 506 disconnectAudioNative(byte[] address)507 private native boolean disconnectAudioNative(byte[] address); 508 isNoiseReductionSupportedNative(byte[] address)509 private native boolean isNoiseReductionSupportedNative(byte[] address); 510 isVoiceRecognitionSupportedNative(byte[] address)511 private native boolean isVoiceRecognitionSupportedNative(byte[] address); 512 startVoiceRecognitionNative(byte[] address)513 private native boolean startVoiceRecognitionNative(byte[] address); 514 stopVoiceRecognitionNative(byte[] address)515 private native boolean stopVoiceRecognitionNative(byte[] address); 516 setVolumeNative(int volumeType, int volume, byte[] address)517 private native boolean setVolumeNative(int volumeType, int volume, byte[] address); 518 cindResponseNative(int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge, byte[] address)519 private native boolean cindResponseNative(int service, int numActive, int numHeld, 520 int callState, int signal, int roam, int batteryCharge, byte[] address); 521 notifyDeviceStatusNative(int networkState, int serviceType, int signal, int batteryCharge, byte[] address)522 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 523 int batteryCharge, byte[] address); 524 clccResponseNative(int index, int dir, int status, int mode, boolean mpty, String number, int type, byte[] address)525 private native boolean clccResponseNative(int index, int dir, int status, int mode, 526 boolean mpty, String number, int type, byte[] address); 527 copsResponseNative(String operatorName, byte[] address)528 private native boolean copsResponseNative(String operatorName, byte[] address); 529 phoneStateChangeNative(int numActive, int numHeld, int callState, String number, int type, String name, byte[] address)530 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 531 String number, int type, String name, byte[] address); 532 setScoAllowedNative(boolean value)533 private native boolean setScoAllowedNative(boolean value); 534 sendBsirNative(boolean value, byte[] address)535 private native boolean sendBsirNative(boolean value, byte[] address); 536 setActiveDeviceNative(byte[] address)537 private native boolean setActiveDeviceNative(byte[] address); 538 } 539