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.support.annotation.VisibleForTesting; 22 import android.util.Log; 23 24 import com.android.bluetooth.Utils; 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 { 37 classInitNative(); 38 } 39 40 private static HeadsetNativeInterface sInterface; 41 private static final Object INSTANCE_LOCK = new Object(); 42 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 */ 50 public static HeadsetNativeInterface getInstance() { 51 synchronized (INSTANCE_LOCK) { 52 if (sInterface == null) { 53 sInterface = new HeadsetNativeInterface(); 54 } 55 } 56 return sInterface; 57 } 58 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.wtfStack(TAG, "FATAL: Stack sent event while service is not available: " + event); 67 } 68 } 69 70 private BluetoothDevice getDevice(byte[] address) { 71 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 72 } 73 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 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 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 97 private void onAnswerCall(byte[] address) { 98 HeadsetStackEvent event = 99 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_ANSWER_CALL, getDevice(address)); 100 sendMessageToService(event); 101 } 102 103 private void onHangupCall(byte[] address) { 104 HeadsetStackEvent event = 105 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_HANGUP_CALL, getDevice(address)); 106 sendMessageToService(event); 107 } 108 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 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 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 130 private void onNoiceReductionEnable(boolean enable, byte[] address) { 131 HeadsetStackEvent event = 132 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_NOICE_REDUCTION, enable ? 1 : 0, 133 getDevice(address)); 134 sendMessageToService(event); 135 } 136 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 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 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 156 private void onAtCind(byte[] address) { 157 HeadsetStackEvent event = 158 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CIND, getDevice(address)); 159 sendMessageToService(event); 160 } 161 162 private void onAtCops(byte[] address) { 163 HeadsetStackEvent event = 164 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_COPS, getDevice(address)); 165 sendMessageToService(event); 166 } 167 168 private void onAtClcc(byte[] address) { 169 HeadsetStackEvent event = 170 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_AT_CLCC, getDevice(address)); 171 sendMessageToService(event); 172 } 173 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 181 private void onKeyPressed(byte[] address) { 182 HeadsetStackEvent event = 183 new HeadsetStackEvent(HeadsetStackEvent.EVENT_TYPE_KEY_PRESSED, getDevice(address)); 184 sendMessageToService(event); 185 } 186 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 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 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 219 public void init(int maxHfClients, boolean inbandRingingEnabled) { 220 initializeNative(maxHfClients, inbandRingingEnabled); 221 } 222 223 /** 224 * Closes the interface 225 */ 226 @VisibleForTesting 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 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 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 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 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 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 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 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 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 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 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 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 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 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 420 public boolean phoneStateChange(BluetoothDevice device, HeadsetCallState callState) { 421 return phoneStateChangeNative(callState.mNumActive, callState.mNumHeld, 422 callState.mCallState, callState.mNumber, callState.mType, 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 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 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 455 public boolean setActiveDevice(BluetoothDevice device) { 456 return setActiveDeviceNative(Utils.getByteAddress(device)); 457 } 458 459 /* Native methods */ 460 private static native void classInitNative(); 461 462 private native boolean atResponseCodeNative(int responseCode, int errorCode, byte[] address); 463 464 private native boolean atResponseStringNative(String responseString, byte[] address); 465 466 private native void initializeNative(int maxHfClients, boolean inbandRingingEnabled); 467 468 private native void cleanupNative(); 469 470 private native boolean connectHfpNative(byte[] address); 471 472 private native boolean disconnectHfpNative(byte[] address); 473 474 private native boolean connectAudioNative(byte[] address); 475 476 private native boolean disconnectAudioNative(byte[] address); 477 478 private native boolean startVoiceRecognitionNative(byte[] address); 479 480 private native boolean stopVoiceRecognitionNative(byte[] address); 481 482 private native boolean setVolumeNative(int volumeType, int volume, byte[] address); 483 484 private native boolean cindResponseNative(int service, int numActive, int numHeld, 485 int callState, int signal, int roam, int batteryCharge, byte[] address); 486 487 private native boolean notifyDeviceStatusNative(int networkState, int serviceType, int signal, 488 int batteryCharge, byte[] address); 489 490 private native boolean clccResponseNative(int index, int dir, int status, int mode, 491 boolean mpty, String number, int type, byte[] address); 492 493 private native boolean copsResponseNative(String operatorName, byte[] address); 494 495 private native boolean phoneStateChangeNative(int numActive, int numHeld, int callState, 496 String number, int type, byte[] address); 497 498 private native boolean setScoAllowedNative(boolean value); 499 500 private native boolean sendBsirNative(boolean value, byte[] address); 501 502 private native boolean setActiveDeviceNative(byte[] address); 503 } 504