1 /* 2 * Copyright (c) 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 /* 18 * Defines the native inteface that is used by state machine/service to either or receive messages 19 * from the native stack. This file is registered for the native methods in corresponding CPP file. 20 */ 21 package com.android.bluetooth.hfpclient; 22 23 import android.bluetooth.BluetoothAdapter; 24 import android.bluetooth.BluetoothDevice; 25 import android.util.Log; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 29 /** 30 * Defines native calls that are used by state machine/service to either send or receive 31 * messages to/from the native stack. This file is registered for the native methods in 32 * corresponding CPP file. 33 */ 34 public class NativeInterface { 35 private static final String TAG = "NativeInterface"; 36 private static final boolean DBG = false; 37 38 static { classInitNative()39 classInitNative(); 40 } 41 NativeInterface()42 private NativeInterface() {} 43 private static NativeInterface sInterface; 44 private static final Object INSTANCE_LOCK = new Object(); 45 46 /** 47 * This class is a singleton because native library should only be loaded once 48 * 49 * @return default instance 50 */ getInstance()51 public static NativeInterface getInstance() { 52 synchronized (INSTANCE_LOCK) { 53 if (sInterface == null) { 54 sInterface = new NativeInterface(); 55 } 56 } 57 return sInterface; 58 } 59 60 // Native wrappers to help unit testing 61 /** 62 * Initialize native stack 63 */ 64 @VisibleForTesting initialize()65 public void initialize() { 66 initializeNative(); 67 } 68 69 /** 70 * Close and clean up native stack 71 */ 72 @VisibleForTesting cleanup()73 public void cleanup() { 74 cleanupNative(); 75 } 76 77 /** 78 * Connect to the specified paired device 79 * 80 * @param address target device's address 81 * @return True on success, False on failure 82 */ 83 @VisibleForTesting connect(byte[] address)84 public boolean connect(byte[] address) { 85 return connectNative(address); 86 } 87 88 /** 89 * Disconnect from the specified paired device 90 * 91 * @param address target device's address 92 * @return True on success, False on failure 93 */ 94 @VisibleForTesting disconnect(byte[] address)95 public boolean disconnect(byte[] address) { 96 return disconnectNative(address); 97 } 98 99 /** 100 * Initiate audio connection to the specified paired device 101 * 102 * @param address target device's address 103 * @return True on success, False on failure 104 */ 105 @VisibleForTesting connectAudio(byte[] address)106 public boolean connectAudio(byte[] address) { 107 return connectAudioNative(address); 108 } 109 110 /** 111 * Close audio connection from the specified paired device 112 * 113 * @param address target device's address 114 * @return True on success, False on failure 115 */ disconnectAudio(byte[] address)116 public boolean disconnectAudio(byte[] address) { 117 return disconnectAudioNative(address); 118 } 119 120 /** 121 * Initiate voice recognition to the specified paired device 122 * 123 * @param address target device's address 124 * @return True on success, False on failure 125 */ 126 @VisibleForTesting startVoiceRecognition(byte[] address)127 public boolean startVoiceRecognition(byte[] address) { 128 return startVoiceRecognitionNative(address); 129 } 130 131 /** 132 * Close voice recognition to the specified paired device 133 * 134 * @param address target device's address 135 * @return True on success, False on failure 136 */ 137 @VisibleForTesting stopVoiceRecognition(byte[] address)138 public boolean stopVoiceRecognition(byte[] address) { 139 return stopVoiceRecognitionNative(address); 140 } 141 142 /** 143 * Set volume to the specified paired device 144 * 145 * @param volumeType type of volume as in 146 * HeadsetClientHalConstants.VOLUME_TYPE_xxxx 147 * @param volume volume level 148 * @param address target device's address 149 * @return True on success, False on failure 150 */ 151 @VisibleForTesting setVolume(byte[] address, int volumeType, int volume)152 public boolean setVolume(byte[] address, int volumeType, int volume) { 153 return setVolumeNative(address, volumeType, volume); 154 } 155 156 /** 157 * dial number from the specified paired device 158 * 159 * @param number phone number to be dialed 160 * @param address target device's address 161 * @return True on success, False on failure 162 */ 163 @VisibleForTesting dial(byte[] address, String number)164 public boolean dial(byte[] address, String number) { 165 return dialNative(address, number); 166 } 167 168 /** 169 * Memory dialing from the specified paired device 170 * 171 * @param location memory location 172 * @param address target device's address 173 * @return True on success, False on failure 174 */ 175 @VisibleForTesting dialMemory(byte[] address, int location)176 public boolean dialMemory(byte[] address, int location) { 177 return dialMemoryNative(address, location); 178 } 179 180 /** 181 * Apply action to call 182 * 183 * @action action (e.g. hold, terminate etc) 184 * @index call index 185 * @param address target device's address 186 * @return True on success, False on failure 187 */ 188 @VisibleForTesting handleCallAction(byte[] address, int action, int index)189 public boolean handleCallAction(byte[] address, int action, int index) { 190 return handleCallActionNative(address, action, index); 191 } 192 193 /** 194 * Query current call status from the specified paired device 195 * 196 * @param address target device's address 197 * @return True on success, False on failure 198 */ 199 @VisibleForTesting queryCurrentCalls(byte[] address)200 public boolean queryCurrentCalls(byte[] address) { 201 return queryCurrentCallsNative(address); 202 } 203 204 /** 205 * Query operator name from the specified paired device 206 * 207 * @param address target device's address 208 * @return True on success, False on failure 209 */ 210 @VisibleForTesting queryCurrentOperatorName(byte[] address)211 public boolean queryCurrentOperatorName(byte[] address) { 212 return queryCurrentOperatorNameNative(address); 213 } 214 215 /** 216 * Retrieve subscriber number from the specified paired device 217 * 218 * @param address target device's address 219 * @return True on success, False on failure 220 */ 221 @VisibleForTesting retrieveSubscriberInfo(byte[] address)222 public boolean retrieveSubscriberInfo(byte[] address) { 223 return retrieveSubscriberInfoNative(address); 224 } 225 226 /** 227 * Transmit DTMF code 228 * 229 * @param code DTMF code 230 * @param address target device's address 231 * @return True on success, False on failure 232 */ 233 @VisibleForTesting sendDtmf(byte[] address, byte code)234 public boolean sendDtmf(byte[] address, byte code) { 235 return sendDtmfNative(address, code); 236 } 237 238 /** 239 * Request last voice tag 240 * 241 * @param address target device's address 242 * @return True on success, False on failure 243 */ 244 @VisibleForTesting requestLastVoiceTagNumber(byte[] address)245 public boolean requestLastVoiceTagNumber(byte[] address) { 246 return requestLastVoiceTagNumberNative(address); 247 } 248 249 /** 250 * Send an AT command 251 * 252 * @param atCmd command code 253 * @param val1 command specific argurment1 254 * @param val2 command specific argurment2 255 * @param arg other command specific argurments 256 * @return True on success, False on failure 257 */ 258 @VisibleForTesting sendATCmd(byte[] address, int atCmd, int val1, int val2, String arg)259 public boolean sendATCmd(byte[] address, int atCmd, int val1, int val2, String arg) { 260 return sendATCmdNative(address, atCmd, val1, val2, arg); 261 } 262 263 // Native methods that call into the JNI interface classInitNative()264 private static native void classInitNative(); 265 initializeNative()266 private native void initializeNative(); 267 cleanupNative()268 private native void cleanupNative(); 269 connectNative(byte[] address)270 private static native boolean connectNative(byte[] address); 271 disconnectNative(byte[] address)272 private static native boolean disconnectNative(byte[] address); 273 connectAudioNative(byte[] address)274 private static native boolean connectAudioNative(byte[] address); 275 disconnectAudioNative(byte[] address)276 private static native boolean disconnectAudioNative(byte[] address); 277 startVoiceRecognitionNative(byte[] address)278 private static native boolean startVoiceRecognitionNative(byte[] address); 279 stopVoiceRecognitionNative(byte[] address)280 private static native boolean stopVoiceRecognitionNative(byte[] address); 281 setVolumeNative(byte[] address, int volumeType, int volume)282 private static native boolean setVolumeNative(byte[] address, int volumeType, int volume); 283 dialNative(byte[] address, String number)284 private static native boolean dialNative(byte[] address, String number); 285 dialMemoryNative(byte[] address, int location)286 private static native boolean dialMemoryNative(byte[] address, int location); 287 handleCallActionNative(byte[] address, int action, int index)288 private static native boolean handleCallActionNative(byte[] address, int action, int index); 289 queryCurrentCallsNative(byte[] address)290 private static native boolean queryCurrentCallsNative(byte[] address); 291 queryCurrentOperatorNameNative(byte[] address)292 private static native boolean queryCurrentOperatorNameNative(byte[] address); 293 retrieveSubscriberInfoNative(byte[] address)294 private static native boolean retrieveSubscriberInfoNative(byte[] address); 295 sendDtmfNative(byte[] address, byte code)296 private static native boolean sendDtmfNative(byte[] address, byte code); 297 requestLastVoiceTagNumberNative(byte[] address)298 private static native boolean requestLastVoiceTagNumberNative(byte[] address); 299 sendATCmdNative(byte[] address, int atCmd, int val1, int val2, String arg)300 private static native boolean sendATCmdNative(byte[] address, int atCmd, int val1, int val2, 301 String arg); 302 getDevice(byte[] address)303 private BluetoothDevice getDevice(byte[] address) { 304 return BluetoothAdapter.getDefaultAdapter().getRemoteDevice(address); 305 } 306 307 // Callbacks from the native back into the java framework. All callbacks are routed via the 308 // Service which will disambiguate which state machine the message should be routed through. onConnectionStateChanged(int state, int peerFeat, int chldFeat, byte[] address)309 private void onConnectionStateChanged(int state, int peerFeat, int chldFeat, byte[] address) { 310 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED); 311 event.valueInt = state; 312 event.valueInt2 = peerFeat; 313 event.valueInt3 = chldFeat; 314 event.device = getDevice(address); 315 // BluetoothAdapter.getDefaultAdapter().getRemoteDevice(Utils.getAddressStringFromByte 316 // (address)); 317 if (DBG) { 318 Log.d(TAG, "Device addr " + event.device.getAddress() + " State " + state); 319 } 320 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 321 if (service != null) { 322 service.messageFromNative(event); 323 } else { 324 Log.w(TAG, "Ignoring message because service not available: " + event); 325 } 326 } 327 onAudioStateChanged(int state, byte[] address)328 private void onAudioStateChanged(int state, byte[] address) { 329 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_AUDIO_STATE_CHANGED); 330 event.valueInt = state; 331 event.device = getDevice(address); 332 if (DBG) { 333 Log.d(TAG, "onAudioStateChanged: address " + address + " event " + event); 334 } 335 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 336 if (service != null) { 337 service.messageFromNative(event); 338 } else { 339 Log.w(TAG, "onAudioStateChanged: Ignoring message because service not available: " 340 + event); 341 } 342 } 343 onVrStateChanged(int state, byte[] address)344 private void onVrStateChanged(int state, byte[] address) { 345 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_VR_STATE_CHANGED); 346 event.valueInt = state; 347 event.device = getDevice(address); 348 if (DBG) { 349 Log.d(TAG, "onVrStateChanged: address " + address + " event " + event); 350 } 351 352 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 353 if (service != null) { 354 service.messageFromNative(event); 355 } else { 356 Log.w(TAG, 357 "onVrStateChanged: Ignoring message because service not available: " + event); 358 } 359 } 360 onNetworkState(int state, byte[] address)361 private void onNetworkState(int state, byte[] address) { 362 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_NETWORK_STATE); 363 event.valueInt = state; 364 event.device = getDevice(address); 365 if (DBG) { 366 Log.d(TAG, "onNetworkStateChanged: address " + address + " event " + event); 367 } 368 369 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 370 if (service != null) { 371 service.messageFromNative(event); 372 } else { 373 Log.w(TAG, 374 "onNetworkStateChanged: Ignoring message because service not available: " 375 + event); 376 } 377 } 378 onNetworkRoaming(int state, byte[] address)379 private void onNetworkRoaming(int state, byte[] address) { 380 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_ROAMING_STATE); 381 event.valueInt = state; 382 event.device = getDevice(address); 383 if (DBG) { 384 Log.d(TAG, "onNetworkRoaming: incoming: " + event); 385 } 386 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 387 if (service != null) { 388 service.messageFromNative(event); 389 } else { 390 Log.w(TAG, 391 "onNetworkRoaming: Ignoring message because service not available: " + event); 392 } 393 } 394 onNetworkSignal(int signal, byte[] address)395 private void onNetworkSignal(int signal, byte[] address) { 396 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_NETWORK_SIGNAL); 397 event.valueInt = signal; 398 event.device = getDevice(address); 399 if (DBG) { 400 Log.d(TAG, "onNetworkSignal: address " + address + " event " + event); 401 } 402 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 403 if (service != null) { 404 service.messageFromNative(event); 405 } else { 406 Log.w(TAG, "onNetworkSignal: Ignoring message because service not available: " + event); 407 } 408 } 409 onBatteryLevel(int level, byte[] address)410 private void onBatteryLevel(int level, byte[] address) { 411 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_BATTERY_LEVEL); 412 event.valueInt = level; 413 event.device = getDevice(address); 414 if (DBG) { 415 Log.d(TAG, "onBatteryLevel: address " + address + " event " + event); 416 } 417 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 418 if (service != null) { 419 service.messageFromNative(event); 420 } else { 421 Log.w(TAG, "onBatteryLevel: Ignoring message because service not available: " + event); 422 } 423 } 424 onCurrentOperator(String name, byte[] address)425 private void onCurrentOperator(String name, byte[] address) { 426 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_OPERATOR_NAME); 427 event.valueString = name; 428 event.device = getDevice(address); 429 if (DBG) { 430 Log.d(TAG, "onCurrentOperator: address " + address + " event " + event); 431 } 432 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 433 if (service != null) { 434 service.messageFromNative(event); 435 } else { 436 Log.w(TAG, 437 "onCurrentOperator: Ignoring message because service not available: " + event); 438 } 439 } 440 onCall(int call, byte[] address)441 private void onCall(int call, byte[] address) { 442 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALL); 443 event.valueInt = call; 444 event.device = getDevice(address); 445 if (DBG) { 446 Log.d(TAG, "onCall: address " + address + " event " + event); 447 } 448 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 449 if (service != null) { 450 service.messageFromNative(event); 451 } else { 452 Log.w(TAG, "onCall: Ignoring message because service not available: " + event); 453 } 454 } 455 456 /** 457 * CIEV (Call indicators) notifying if call(s) are getting set up. 458 * 459 * Values include: 460 * 0 - No current call is in setup 461 * 1 - Incoming call process ongoing 462 * 2 - Outgoing call process ongoing 463 * 3 - Remote party being alerted for outgoing call 464 */ onCallSetup(int callsetup, byte[] address)465 private void onCallSetup(int callsetup, byte[] address) { 466 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALLSETUP); 467 event.valueInt = callsetup; 468 event.device = getDevice(address); 469 if (DBG) { 470 Log.d(TAG, "onCallSetup: addr " + address + " device" + event.device); 471 Log.d(TAG, "onCallSetup: address " + address + " event " + event); 472 } 473 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 474 if (service != null) { 475 service.messageFromNative(event); 476 } else { 477 Log.w(TAG, "onCallSetup: Ignoring message because service not available: " + event); 478 } 479 } 480 481 /** 482 * CIEV (Call indicators) notifying call held states. 483 * 484 * Values include: 485 * 0 - No calls held 486 * 1 - Call is placed on hold or active/held calls wapped (The AG has both an ACTIVE and HELD 487 * call) 488 * 2 - Call on hold, no active call 489 */ onCallHeld(int callheld, byte[] address)490 private void onCallHeld(int callheld, byte[] address) { 491 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALLHELD); 492 event.valueInt = callheld; 493 event.device = getDevice(address); 494 if (DBG) { 495 Log.d(TAG, "onCallHeld: address " + address + " event " + event); 496 } 497 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 498 if (service != null) { 499 service.messageFromNative(event); 500 } else { 501 Log.w(TAG, "onCallHeld: Ignoring message because service not available: " + event); 502 } 503 } 504 onRespAndHold(int respAndHold, byte[] address)505 private void onRespAndHold(int respAndHold, byte[] address) { 506 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_RESP_AND_HOLD); 507 event.valueInt = respAndHold; 508 event.device = getDevice(address); 509 if (DBG) { 510 Log.d(TAG, "onRespAndHold: address " + address + " event " + event); 511 } 512 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 513 if (service != null) { 514 service.messageFromNative(event); 515 } else { 516 Log.w(TAG, "onRespAndHold: Ignoring message because service not available: " + event); 517 } 518 } 519 onClip(String number, byte[] address)520 private void onClip(String number, byte[] address) { 521 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CLIP); 522 event.valueString = number; 523 event.device = getDevice(address); 524 if (DBG) { 525 Log.d(TAG, "onClip: address " + address + " event " + event); 526 } 527 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 528 if (service != null) { 529 service.messageFromNative(event); 530 } else { 531 Log.w(TAG, "onClip: Ignoring message because service not available: " + event); 532 } 533 } 534 onCallWaiting(String number, byte[] address)535 private void onCallWaiting(String number, byte[] address) { 536 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CALL_WAITING); 537 event.valueString = number; 538 event.device = getDevice(address); 539 if (DBG) { 540 Log.d(TAG, "onCallWaiting: address " + address + " event " + event); 541 } 542 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 543 if (service != null) { 544 service.messageFromNative(event); 545 } else { 546 Log.w(TAG, "onCallWaiting: Ignoring message because service not available: " + event); 547 } 548 } 549 onCurrentCalls(int index, int dir, int state, int mparty, String number, byte[] address)550 private void onCurrentCalls(int index, int dir, int state, int mparty, String number, 551 byte[] address) { 552 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CURRENT_CALLS); 553 event.valueInt = index; 554 event.valueInt2 = dir; 555 event.valueInt3 = state; 556 event.valueInt4 = mparty; 557 event.valueString = number; 558 event.device = getDevice(address); 559 if (DBG) { 560 Log.d(TAG, "onCurrentCalls: address " + address + " event " + event); 561 } 562 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 563 if (service != null) { 564 service.messageFromNative(event); 565 } else { 566 Log.w(TAG, "onCurrentCalls: Ignoring message because service not available: " + event); 567 } 568 } 569 onVolumeChange(int type, int volume, byte[] address)570 private void onVolumeChange(int type, int volume, byte[] address) { 571 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_VOLUME_CHANGED); 572 event.valueInt = type; 573 event.valueInt2 = volume; 574 event.device = getDevice(address); 575 if (DBG) { 576 Log.d(TAG, "onVolumeChange: address " + address + " event " + event); 577 } 578 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 579 if (service != null) { 580 service.messageFromNative(event); 581 } else { 582 Log.w(TAG, "onVolumeChange: Ignoring message because service not available: " + event); 583 } 584 } 585 onCmdResult(int type, int cme, byte[] address)586 private void onCmdResult(int type, int cme, byte[] address) { 587 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_CMD_RESULT); 588 event.valueInt = type; 589 event.valueInt2 = cme; 590 event.device = getDevice(address); 591 if (DBG) { 592 Log.d(TAG, "onCmdResult: address " + address + " event " + event); 593 } 594 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 595 if (service != null) { 596 service.messageFromNative(event); 597 } else { 598 Log.w(TAG, "onCmdResult: Ignoring message because service not available: " + event); 599 } 600 } 601 onSubscriberInfo(String number, int type, byte[] address)602 private void onSubscriberInfo(String number, int type, byte[] address) { 603 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_SUBSCRIBER_INFO); 604 event.valueInt = type; 605 event.valueString = number; 606 event.device = getDevice(address); 607 if (DBG) { 608 Log.d(TAG, "onSubscriberInfo: address " + address + " event " + event); 609 } 610 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 611 if (service != null) { 612 service.messageFromNative(event); 613 } else { 614 Log.w(TAG, 615 "onSubscriberInfo: Ignoring message because service not available: " + event); 616 } 617 } 618 onInBandRing(int inBand, byte[] address)619 private void onInBandRing(int inBand, byte[] address) { 620 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_IN_BAND_RINGTONE); 621 event.valueInt = inBand; 622 event.device = getDevice(address); 623 if (DBG) { 624 Log.d(TAG, "onInBandRing: address " + address + " event " + event); 625 } 626 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 627 if (service != null) { 628 service.messageFromNative(event); 629 } else { 630 Log.w(TAG, 631 "onInBandRing: Ignoring message because service not available: " + event); 632 } 633 } 634 onLastVoiceTagNumber(String number, byte[] address)635 private void onLastVoiceTagNumber(String number, byte[] address) { 636 Log.w(TAG, "onLastVoiceTagNumber not supported"); 637 } 638 onRingIndication(byte[] address)639 private void onRingIndication(byte[] address) { 640 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_RING_INDICATION); 641 event.device = getDevice(address); 642 if (DBG) { 643 Log.d(TAG, "onRingIndication: address " + address + " event " + event); 644 } 645 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 646 if (service != null) { 647 service.messageFromNative(event); 648 } else { 649 Log.w(TAG, 650 "onRingIndication: Ignoring message because service not available: " + event); 651 } 652 } 653 onUnknownEvent(String eventString, byte[] address)654 private void onUnknownEvent(String eventString, byte[] address) { 655 StackEvent event = new StackEvent(StackEvent.EVENT_TYPE_UNKNOWN_EVENT); 656 event.device = getDevice(address); 657 event.valueString = eventString; 658 if (DBG) { 659 Log.d(TAG, "onUnknownEvent: address " + address + " event " + event); 660 } 661 HeadsetClientService service = HeadsetClientService.getHeadsetClientService(); 662 if (service != null) { 663 service.messageFromNative(event); 664 } else { 665 Log.w(TAG, 666 "onUnknowEvent: Ignoring message because service not available: " + event); 667 } 668 } 669 670 } 671