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 * Start voice recognition 302 * 303 * @param device target headset 304 * @return True on success, False on failure 305 */ 306 @VisibleForTesting startVoiceRecognition(BluetoothDevice device)307 public boolean startVoiceRecognition(BluetoothDevice device) { 308 return startVoiceRecognitionNative(Utils.getByteAddress(device)); 309 } 310 311 312 /** 313 * Stop voice recognition 314 * 315 * @param device target headset 316 * @return True on success, False on failure 317 */ 318 @VisibleForTesting stopVoiceRecognition(BluetoothDevice device)319 public boolean stopVoiceRecognition(BluetoothDevice device) { 320 return stopVoiceRecognitionNative(Utils.getByteAddress(device)); 321 } 322 323 /** 324 * Set HFP audio (SCO) volume 325 * 326 * @param device target headset 327 * @param volumeType type of volume 328 * @param volume value value 329 * @return True on success, False on failure 330 */ 331 @VisibleForTesting setVolume(BluetoothDevice device, int volumeType, int volume)332 public boolean setVolume(BluetoothDevice device, int volumeType, int volume) { 333 return setVolumeNative(volumeType, volume, Utils.getByteAddress(device)); 334 } 335 336 /** 337 * Response for CIND command 338 * 339 * @param device target device 340 * @param service service availability, 0 - no service, 1 - presence of service 341 * @param numActive number of active calls 342 * @param numHeld number of held calls 343 * @param callState overall call state [0-6] 344 * @param signal signal quality [0-5] 345 * @param roam roaming indicator, 0 - not roaming, 1 - roaming 346 * @param batteryCharge battery charge level [0-5] 347 * @return True on success, False on failure 348 */ 349 @VisibleForTesting cindResponse(BluetoothDevice device, int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge)350 public boolean cindResponse(BluetoothDevice device, int service, int numActive, int numHeld, 351 int callState, int signal, int roam, int batteryCharge) { 352 return cindResponseNative(service, numActive, numHeld, callState, signal, roam, 353 batteryCharge, Utils.getByteAddress(device)); 354 } 355 356 /** 357 * Combined device status change notification 358 * 359 * @param device target device 360 * @param deviceState device status object 361 * @return True on success, False on failure 362 */ 363 @VisibleForTesting notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState)364 public boolean notifyDeviceStatus(BluetoothDevice device, HeadsetDeviceState deviceState) { 365 return notifyDeviceStatusNative(deviceState.mService, deviceState.mRoam, 366 deviceState.mSignal, deviceState.mBatteryCharge, Utils.getByteAddress(device)); 367 } 368 369 /** 370 * Response for CLCC command. Can be iteratively called for each call index. Call index of 0 371 * will be treated as NULL termination (Completes response) 372 * 373 * @param device target device 374 * @param index index of the call given by the sequence of setting up or receiving the calls 375 * as seen by the served subscriber. Calls hold their number until they are released. New 376 * calls take the lowest available number. 377 * @param dir direction of the call, 0 (outgoing), 1 (incoming) 378 * @param status 0 = Active, 1 = Held, 2 = Dialing (outgoing calls only), 3 = Alerting 379 * (outgoing calls only), 4 = Incoming (incoming calls only), 5 = Waiting (incoming calls 380 * only), 6 = Call held by Response and Hold 381 * @param mode 0 (Voice), 1 (Data), 2 (FAX) 382 * @param mpty 0 - this call is NOT a member of a multi-party (conference) call, 1 - this 383 * call IS a member of a multi-party (conference) call 384 * @param number optional 385 * @param type optional 386 * @return True on success, False on failure 387 */ 388 @VisibleForTesting clccResponse(BluetoothDevice device, int index, int dir, int status, int mode, boolean mpty, String number, int type)389 public boolean clccResponse(BluetoothDevice device, int index, int dir, int status, int mode, 390 boolean mpty, String number, int type) { 391 return clccResponseNative(index, dir, status, mode, mpty, number, type, 392 Utils.getByteAddress(device)); 393 } 394 395 /** 396 * Response for COPS command 397 * 398 * @param device target device 399 * @param operatorName operator name 400 * @return True on success, False on failure 401 */ 402 @VisibleForTesting copsResponse(BluetoothDevice device, String operatorName)403 public boolean copsResponse(BluetoothDevice device, String operatorName) { 404 return copsResponseNative(operatorName, Utils.getByteAddress(device)); 405 } 406 407 /** 408 * Notify of a call state change 409 * Each update notifies 410 * 1. Number of active/held/ringing calls 411 * 2. call_state: This denotes the state change that triggered this msg 412 * This will take one of the values from BtHfCallState 413 * 3. number & type: valid only for incoming & waiting call 414 * 415 * @param device target device for this update 416 * @param callState callstate structure 417 * @return True on success, False on failure 418 */ 419 @VisibleForTesting phoneStateChange(BluetoothDevice device, HeadsetCallState callState)420 public boolean phoneStateChange(BluetoothDevice device, HeadsetCallState callState) { 421 return phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 422 callState.mCallState, callState.mNumber, callState.mType, callState.mName, 423 Utils.getByteAddress(device)); 424 } 425 426 /** 427 * Set whether we will initiate SCO or not 428 * 429 * @param value True to enable, False to disable 430 * @return True on success, False on failure 431 */ 432 @VisibleForTesting setScoAllowed(boolean value)433 public boolean setScoAllowed(boolean value) { 434 return setScoAllowedNative(value); 435 } 436 437 /** 438 * Enable or disable in-band ringing for the current service level connection through sending 439 * +BSIR AT command 440 * 441 * @param value True to enable, False to disable 442 * @return True on success, False on failure 443 */ 444 @VisibleForTesting sendBsir(BluetoothDevice device, boolean value)445 public boolean sendBsir(BluetoothDevice device, boolean value) { 446 return sendBsirNative(value, Utils.getByteAddress(device)); 447 } 448 449 /** 450 * Set the current active headset device for SCO audio 451 * @param device current active SCO device 452 * @return true on success 453 */ 454 @VisibleForTesting setActiveDevice(BluetoothDevice device)455 public boolean setActiveDevice(BluetoothDevice device) { 456 return setActiveDeviceNative(Utils.getByteAddress(device)); 457 } 458 459 /* Native methods */ classInitNative()460 private static native void classInitNative(); 461 atResponseCodeNative(int responseCode, int errorCode, byte[] address)462 private native boolean atResponseCodeNative(int responseCode, int errorCode, byte[] address); 463 atResponseStringNative(String responseString, byte[] address)464 private native boolean atResponseStringNative(String responseString, byte[] address); 465 initializeNative(int maxHfClients, boolean inbandRingingEnabled)466 private native void initializeNative(int maxHfClients, boolean inbandRingingEnabled); 467 cleanupNative()468 private native void cleanupNative(); 469 connectHfpNative(byte[] address)470 private native boolean connectHfpNative(byte[] address); 471 disconnectHfpNative(byte[] address)472 private native boolean disconnectHfpNative(byte[] address); 473 connectAudioNative(byte[] address)474 private native boolean connectAudioNative(byte[] address); 475 disconnectAudioNative(byte[] address)476 private native boolean disconnectAudioNative(byte[] address); 477 startVoiceRecognitionNative(byte[] address)478 private native boolean startVoiceRecognitionNative(byte[] address); 479 stopVoiceRecognitionNative(byte[] address)480 private native boolean stopVoiceRecognitionNative(byte[] address); 481 setVolumeNative(int volumeType, int volume, byte[] address)482 private native boolean setVolumeNative(int volumeType, int volume, byte[] address); 483 cindResponseNative(int service, int numActive, int numHeld, int callState, int signal, int roam, int batteryCharge, byte[] address)484 private native boolean cindResponseNative(int service, int numActive, int numHeld, 485 int callState, int signal, int roam, int batteryCharge, byte[] address); 486 notifyDeviceStatusNative(int networkState, int serviceType, int signal, int batteryCharge, byte[] address)487 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 488 int batteryCharge, byte[] address); 489 clccResponseNative(int index, int dir, int status, int mode, boolean mpty, String number, int type, byte[] address)490 private native boolean clccResponseNative(int index, int dir, int status, int mode, 491 boolean mpty, String number, int type, byte[] address); 492 copsResponseNative(String operatorName, byte[] address)493 private native boolean copsResponseNative(String operatorName, byte[] address); 494 phoneStateChangeNative(int numActive, int numHeld, int callState, String number, int type, String name, byte[] address)495 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 496 String number, int type, String name, byte[] address); 497 setScoAllowedNative(boolean value)498 private native boolean setScoAllowedNative(boolean value); 499 sendBsirNative(boolean value, byte[] address)500 private native boolean sendBsirNative(boolean value, byte[] address); 501 setActiveDeviceNative(byte[] address)502 private native boolean setActiveDeviceNative(byte[] address); 503 } 504