1 /* 2 * Copyright (C) 2014 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 * Bluetooth A2dp Sink StateMachine 19 * (Disconnected) 20 * | ^ 21 * CONNECT | | DISCONNECTED 22 * V | 23 * (Pending) 24 * | ^ 25 * CONNECTED | | CONNECT 26 * V | 27 * (Connected -- See A2dpSinkStreamingStateMachine) 28 */ 29 package com.android.bluetooth.a2dpsink; 30 31 import android.bluetooth.BluetoothA2dpSink; 32 import android.bluetooth.BluetoothAvrcpController; 33 import android.bluetooth.BluetoothAdapter; 34 import android.bluetooth.BluetoothAudioConfig; 35 import android.bluetooth.BluetoothDevice; 36 import android.bluetooth.BluetoothProfile; 37 import android.bluetooth.BluetoothUuid; 38 import android.bluetooth.IBluetooth; 39 import android.content.BroadcastReceiver; 40 import android.content.Context; 41 import android.content.Intent; 42 import android.media.AudioFormat; 43 import android.os.Handler; 44 import android.os.Message; 45 import android.os.ParcelUuid; 46 import android.os.PowerManager; 47 import android.os.PowerManager.WakeLock; 48 import android.os.RemoteException; 49 import android.os.ServiceManager; 50 import android.os.ParcelUuid; 51 import android.util.Log; 52 53 import com.android.bluetooth.Utils; 54 import com.android.bluetooth.btservice.AdapterService; 55 import com.android.bluetooth.btservice.ProfileService; 56 import com.android.bluetooth.avrcp.AvrcpControllerService; 57 import com.android.internal.util.IState; 58 import com.android.internal.util.State; 59 import com.android.internal.util.StateMachine; 60 61 import java.util.ArrayList; 62 import java.util.List; 63 import java.util.HashMap; 64 import java.util.Set; 65 66 final class A2dpSinkStateMachine extends StateMachine { 67 private static final boolean DBG = true; 68 69 static final int CONNECT = 1; 70 static final int DISCONNECT = 2; 71 private static final int STACK_EVENT = 101; 72 private static final int CONNECT_TIMEOUT = 201; 73 public static final int EVENT_AVRCP_CT_PLAY = 301; 74 public static final int EVENT_AVRCP_CT_PAUSE = 302; 75 public static final int EVENT_AVRCP_TG_PLAY = 303; 76 public static final int EVENT_AVRCP_TG_PAUSE = 304; 77 78 private static final int IS_INVALID_DEVICE = 0; 79 private static final int IS_VALID_DEVICE = 1; 80 public static final int AVRC_ID_PLAY = 0x44; 81 public static final int AVRC_ID_PAUSE = 0x46; 82 public static final int KEY_STATE_PRESSED = 0; 83 public static final int KEY_STATE_RELEASED = 1; 84 85 // Connection states. 86 // 1. Disconnected: The connection does not exist. 87 // 2. Pending: The connection is being established. 88 // 3. Connected: The connection is established. The audio connection is in Idle state. 89 private Disconnected mDisconnected; 90 private Pending mPending; 91 private Connected mConnected; 92 93 private A2dpSinkService mService; 94 private Context mContext; 95 private BluetoothAdapter mAdapter; 96 private IntentBroadcastHandler mIntentBroadcastHandler; 97 98 private static final int MSG_CONNECTION_STATE_CHANGED = 0; 99 100 private final Object mLockForPatch = new Object(); 101 102 // mCurrentDevice is the device connected before the state changes 103 // mTargetDevice is the device to be connected 104 // mIncomingDevice is the device connecting to us, valid only in Pending state 105 // when mIncomingDevice is not null, both mCurrentDevice 106 // and mTargetDevice are null 107 // when either mCurrentDevice or mTargetDevice is not null, 108 // mIncomingDevice is null 109 // Stable states 110 // No connection, Disconnected state 111 // both mCurrentDevice and mTargetDevice are null 112 // Connected, Connected state 113 // mCurrentDevice is not null, mTargetDevice is null 114 // Interim states 115 // Connecting to a device, Pending 116 // mCurrentDevice is null, mTargetDevice is not null 117 // Disconnecting device, Connecting to new device 118 // Pending 119 // Both mCurrentDevice and mTargetDevice are not null 120 // Disconnecting device Pending 121 // mCurrentDevice is not null, mTargetDevice is null 122 // Incoming connections Pending 123 // Both mCurrentDevice and mTargetDevice are null 124 private BluetoothDevice mCurrentDevice = null; 125 private BluetoothDevice mTargetDevice = null; 126 private BluetoothDevice mIncomingDevice = null; 127 private BluetoothDevice mPlayingDevice = null; 128 129 private final HashMap<BluetoothDevice,BluetoothAudioConfig> mAudioConfigs 130 = new HashMap<BluetoothDevice,BluetoothAudioConfig>(); 131 132 static { classInitNative()133 classInitNative(); 134 } 135 A2dpSinkStateMachine(A2dpSinkService svc, Context context)136 private A2dpSinkStateMachine(A2dpSinkService svc, Context context) { 137 super("A2dpSinkStateMachine"); 138 mService = svc; 139 mContext = context; 140 mAdapter = BluetoothAdapter.getDefaultAdapter(); 141 142 initNative(); 143 144 mDisconnected = new Disconnected(); 145 mPending = new Pending(); 146 mConnected = new Connected(); 147 148 addState(mDisconnected); 149 addState(mPending); 150 addState(mConnected); 151 152 setInitialState(mDisconnected); 153 154 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 155 156 mIntentBroadcastHandler = new IntentBroadcastHandler(); 157 } 158 make(A2dpSinkService svc, Context context)159 static A2dpSinkStateMachine make(A2dpSinkService svc, Context context) { 160 Log.d("A2dpSinkStateMachine", "make"); 161 A2dpSinkStateMachine a2dpSm = new A2dpSinkStateMachine(svc, context); 162 a2dpSm.start(); 163 return a2dpSm; 164 } 165 doQuit()166 public void doQuit() { 167 quitNow(); 168 } 169 cleanup()170 public void cleanup() { 171 cleanupNative(); 172 mAudioConfigs.clear(); 173 } 174 dump(StringBuilder sb)175 public void dump(StringBuilder sb) { 176 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 177 ProfileService.println(sb, "mTargetDevice: " + mTargetDevice); 178 ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice); 179 ProfileService.println(sb, "StateMachine: " + this.toString()); 180 } 181 182 private class Disconnected extends State { 183 @Override enter()184 public void enter() { 185 log("Enter Disconnected: " + getCurrentMessage().what); 186 } 187 188 @Override processMessage(Message message)189 public boolean processMessage(Message message) { 190 log("Disconnected process message: " + message.what); 191 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 192 loge("ERROR: current, target, or mIncomingDevice not null in Disconnected"); 193 return NOT_HANDLED; 194 } 195 196 boolean retValue = HANDLED; 197 switch(message.what) { 198 case CONNECT: 199 BluetoothDevice device = (BluetoothDevice) message.obj; 200 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 201 BluetoothProfile.STATE_DISCONNECTED); 202 203 if (!connectA2dpNative(getByteAddress(device)) ) { 204 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 205 BluetoothProfile.STATE_CONNECTING); 206 break; 207 } 208 209 synchronized (A2dpSinkStateMachine.this) { 210 mTargetDevice = device; 211 transitionTo(mPending); 212 } 213 // TODO(BT) remove CONNECT_TIMEOUT when the stack 214 // sends back events consistently 215 sendMessageDelayed(CONNECT_TIMEOUT, 30000); 216 break; 217 case DISCONNECT: 218 // ignore 219 break; 220 case STACK_EVENT: 221 StackEvent event = (StackEvent) message.obj; 222 switch (event.type) { 223 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 224 processConnectionEvent(event.valueInt, event.device); 225 break; 226 case EVENT_TYPE_AUDIO_CONFIG_CHANGED: 227 processAudioConfigEvent(event.audioConfig, event.device); 228 break; 229 default: 230 loge("Unexpected stack event: " + event.type); 231 break; 232 } 233 break; 234 default: 235 return NOT_HANDLED; 236 } 237 return retValue; 238 } 239 240 @Override exit()241 public void exit() { 242 log("Exit Disconnected: " + getCurrentMessage().what); 243 } 244 245 // in Disconnected state processConnectionEvent(int state, BluetoothDevice device)246 private void processConnectionEvent(int state, BluetoothDevice device) { 247 switch (state) { 248 case CONNECTION_STATE_DISCONNECTED: 249 logw("Ignore HF DISCONNECTED event, device: " + device); 250 break; 251 case CONNECTION_STATE_CONNECTING: 252 if (okToConnect(device)){ 253 logi("Incoming A2DP accepted"); 254 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 255 BluetoothProfile.STATE_DISCONNECTED); 256 synchronized (A2dpSinkStateMachine.this) { 257 mIncomingDevice = device; 258 transitionTo(mPending); 259 } 260 } else { 261 //reject the connection and stay in Disconnected state itself 262 logi("Incoming A2DP rejected"); 263 disconnectA2dpNative(getByteAddress(device)); 264 // the other profile connection should be initiated 265 AdapterService adapterService = AdapterService.getAdapterService(); 266 if (adapterService != null) { 267 adapterService.connectOtherProfile(device, 268 AdapterService.PROFILE_CONN_REJECTED); 269 } 270 } 271 break; 272 case CONNECTION_STATE_CONNECTED: 273 logw("A2DP Connected from Disconnected state"); 274 if (okToConnect(device)){ 275 logi("Incoming A2DP accepted"); 276 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 277 BluetoothProfile.STATE_DISCONNECTED); 278 synchronized (A2dpSinkStateMachine.this) { 279 mCurrentDevice = device; 280 transitionTo(mConnected); 281 } 282 } else { 283 //reject the connection and stay in Disconnected state itself 284 logi("Incoming A2DP rejected"); 285 disconnectA2dpNative(getByteAddress(device)); 286 // the other profile connection should be initiated 287 AdapterService adapterService = AdapterService.getAdapterService(); 288 if (adapterService != null) { 289 adapterService.connectOtherProfile(device, 290 AdapterService.PROFILE_CONN_REJECTED); 291 } 292 } 293 break; 294 case CONNECTION_STATE_DISCONNECTING: 295 logw("Ignore HF DISCONNECTING event, device: " + device); 296 break; 297 default: 298 loge("Incorrect state: " + state); 299 break; 300 } 301 } 302 } 303 304 private class Pending extends State { 305 @Override enter()306 public void enter() { 307 log("Enter Pending: " + getCurrentMessage().what); 308 } 309 310 @Override processMessage(Message message)311 public boolean processMessage(Message message) { 312 log("Pending process message: " + message.what); 313 314 boolean retValue = HANDLED; 315 switch(message.what) { 316 case CONNECT: 317 deferMessage(message); 318 break; 319 case CONNECT_TIMEOUT: 320 onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED, 321 getByteAddress(mTargetDevice)); 322 break; 323 case DISCONNECT: 324 BluetoothDevice device = (BluetoothDevice) message.obj; 325 if (mCurrentDevice != null && mTargetDevice != null && 326 mTargetDevice.equals(device) ) { 327 // cancel connection to the mTargetDevice 328 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 329 BluetoothProfile.STATE_CONNECTING); 330 synchronized (A2dpSinkStateMachine.this) { 331 mTargetDevice = null; 332 } 333 } else { 334 deferMessage(message); 335 } 336 break; 337 case STACK_EVENT: 338 StackEvent event = (StackEvent) message.obj; 339 log("STACK_EVENT " + event.type); 340 switch (event.type) { 341 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 342 removeMessages(CONNECT_TIMEOUT); 343 processConnectionEvent(event.valueInt, event.device); 344 break; 345 case EVENT_TYPE_AUDIO_CONFIG_CHANGED: 346 processAudioConfigEvent(event.audioConfig, event.device); 347 break; 348 default: 349 loge("Unexpected stack event: " + event.type); 350 break; 351 } 352 break; 353 default: 354 return NOT_HANDLED; 355 } 356 return retValue; 357 } 358 359 // in Pending state processConnectionEvent(int state, BluetoothDevice device)360 private void processConnectionEvent(int state, BluetoothDevice device) { 361 log("processConnectionEvent state " + state); 362 switch (state) { 363 case CONNECTION_STATE_DISCONNECTED: 364 mAudioConfigs.remove(device); 365 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 366 broadcastConnectionState(mCurrentDevice, 367 BluetoothProfile.STATE_DISCONNECTED, 368 BluetoothProfile.STATE_DISCONNECTING); 369 synchronized (A2dpSinkStateMachine.this) { 370 mCurrentDevice = null; 371 } 372 373 if (mTargetDevice != null) { 374 if (!connectA2dpNative(getByteAddress(mTargetDevice))) { 375 broadcastConnectionState(mTargetDevice, 376 BluetoothProfile.STATE_DISCONNECTED, 377 BluetoothProfile.STATE_CONNECTING); 378 synchronized (A2dpSinkStateMachine.this) { 379 mTargetDevice = null; 380 transitionTo(mDisconnected); 381 } 382 } 383 } else { 384 synchronized (A2dpSinkStateMachine.this) { 385 mIncomingDevice = null; 386 transitionTo(mDisconnected); 387 } 388 } 389 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 390 // outgoing connection failed 391 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 392 BluetoothProfile.STATE_CONNECTING); 393 synchronized (A2dpSinkStateMachine.this) { 394 mTargetDevice = null; 395 transitionTo(mDisconnected); 396 } 397 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 398 broadcastConnectionState(mIncomingDevice, 399 BluetoothProfile.STATE_DISCONNECTED, 400 BluetoothProfile.STATE_CONNECTING); 401 synchronized (A2dpSinkStateMachine.this) { 402 mIncomingDevice = null; 403 transitionTo(mDisconnected); 404 } 405 } else { 406 loge("Unknown device Disconnected: " + device); 407 } 408 break; 409 case CONNECTION_STATE_CONNECTED: 410 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 411 loge("current device is not null"); 412 // disconnection failed 413 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 414 BluetoothProfile.STATE_DISCONNECTING); 415 if (mTargetDevice != null) { 416 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 417 BluetoothProfile.STATE_CONNECTING); 418 } 419 synchronized (A2dpSinkStateMachine.this) { 420 mTargetDevice = null; 421 transitionTo(mConnected); 422 } 423 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 424 loge("target device is not null"); 425 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 426 BluetoothProfile.STATE_CONNECTING); 427 synchronized (A2dpSinkStateMachine.this) { 428 mCurrentDevice = mTargetDevice; 429 mTargetDevice = null; 430 transitionTo(mConnected); 431 } 432 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 433 loge("incoming device is not null"); 434 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 435 BluetoothProfile.STATE_CONNECTING); 436 synchronized (A2dpSinkStateMachine.this) { 437 mCurrentDevice = mIncomingDevice; 438 mIncomingDevice = null; 439 transitionTo(mConnected); 440 } 441 } else { 442 loge("Unknown device Connected: " + device); 443 // something is wrong here, but sync our state with stack 444 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 445 BluetoothProfile.STATE_DISCONNECTED); 446 synchronized (A2dpSinkStateMachine.this) { 447 mCurrentDevice = device; 448 mTargetDevice = null; 449 mIncomingDevice = null; 450 transitionTo(mConnected); 451 } 452 } 453 break; 454 case CONNECTION_STATE_CONNECTING: 455 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 456 log("current device tries to connect back"); 457 // TODO(BT) ignore or reject 458 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 459 // The stack is connecting to target device or 460 // there is an incoming connection from the target device at the same time 461 // we already broadcasted the intent, doing nothing here 462 log("Stack and target device are connecting"); 463 } 464 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 465 loge("Another connecting event on the incoming device"); 466 } else { 467 // We get an incoming connecting request while Pending 468 // TODO(BT) is stack handing this case? let's ignore it for now 469 log("Incoming connection while pending, ignore"); 470 } 471 break; 472 case CONNECTION_STATE_DISCONNECTING: 473 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 474 // we already broadcasted the intent, doing nothing here 475 if (DBG) { 476 log("stack is disconnecting mCurrentDevice"); 477 } 478 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 479 loge("TargetDevice is getting disconnected"); 480 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 481 loge("IncomingDevice is getting disconnected"); 482 } else { 483 loge("Disconnecting unknown device: " + device); 484 } 485 break; 486 default: 487 loge("Incorrect state: " + state); 488 break; 489 } 490 } 491 492 } 493 494 private class Connected extends State { 495 private A2dpSinkStreamingStateMachine mStreaming; 496 @Override enter()497 public void enter() { 498 log("Enter Connected: " + getCurrentMessage().what); 499 // Upon connected, the audio starts out as stopped 500 broadcastAudioState(mCurrentDevice, BluetoothA2dpSink.STATE_NOT_PLAYING, 501 BluetoothA2dpSink.STATE_PLAYING); 502 mStreaming = A2dpSinkStreamingStateMachine.make(A2dpSinkStateMachine.this, mContext); 503 } 504 505 @Override processMessage(Message message)506 public boolean processMessage(Message message) { 507 log("Connected process message: " + message.what); 508 if (mCurrentDevice == null) { 509 loge("ERROR: mCurrentDevice is null in Connected"); 510 return NOT_HANDLED; 511 } 512 513 switch(message.what) { 514 case CONNECT: 515 { 516 BluetoothDevice device = (BluetoothDevice) message.obj; 517 if (mCurrentDevice.equals(device)) { 518 break; 519 } 520 521 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 522 BluetoothProfile.STATE_DISCONNECTED); 523 if (!disconnectA2dpNative(getByteAddress(mCurrentDevice))) { 524 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 525 BluetoothProfile.STATE_CONNECTING); 526 break; 527 } 528 529 synchronized (A2dpSinkStateMachine.this) { 530 mTargetDevice = device; 531 mStreaming.sendMessage(A2dpSinkStreamingStateMachine.DISCONNECT); 532 transitionTo(mPending); 533 } 534 } 535 break; 536 537 case DISCONNECT: 538 { 539 BluetoothDevice device = (BluetoothDevice) message.obj; 540 if (!mCurrentDevice.equals(device)) { 541 break; 542 } 543 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 544 BluetoothProfile.STATE_CONNECTED); 545 if (!disconnectA2dpNative(getByteAddress(device))) { 546 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 547 BluetoothProfile.STATE_DISCONNECTED); 548 break; 549 } 550 mPlayingDevice = null; 551 mStreaming.sendMessage(A2dpSinkStreamingStateMachine.DISCONNECT); 552 transitionTo(mPending); 553 } 554 break; 555 556 case STACK_EVENT: 557 StackEvent event = (StackEvent) message.obj; 558 switch (event.type) { 559 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 560 processConnectionEvent(event.valueInt, event.device); 561 break; 562 case EVENT_TYPE_AUDIO_STATE_CHANGED: 563 processAudioStateEvent(event.valueInt, event.device); 564 break; 565 case EVENT_TYPE_AUDIO_CONFIG_CHANGED: 566 processAudioConfigEvent(event.audioConfig, event.device); 567 break; 568 default: 569 loge("Unexpected stack event: " + event.type); 570 break; 571 } 572 break; 573 574 case EVENT_AVRCP_CT_PLAY: 575 case EVENT_AVRCP_TG_PLAY: 576 mStreaming.sendMessage(A2dpSinkStreamingStateMachine.ACT_PLAY); 577 break; 578 579 case EVENT_AVRCP_CT_PAUSE: 580 case EVENT_AVRCP_TG_PAUSE: 581 mStreaming.sendMessage(A2dpSinkStreamingStateMachine.ACT_PAUSE); 582 break; 583 584 585 default: 586 return NOT_HANDLED; 587 } 588 return HANDLED; 589 } 590 591 // in Connected state processConnectionEvent(int state, BluetoothDevice device)592 private void processConnectionEvent(int state, BluetoothDevice device) { 593 switch (state) { 594 case CONNECTION_STATE_DISCONNECTED: 595 mAudioConfigs.remove(device); 596 if ((mPlayingDevice != null) && (device.equals(mPlayingDevice))) { 597 mPlayingDevice = null; 598 } 599 if (mCurrentDevice.equals(device)) { 600 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 601 BluetoothProfile.STATE_CONNECTED); 602 synchronized (A2dpSinkStateMachine.this) { 603 // Take care of existing audio focus in the streaming state machine. 604 mStreaming.sendMessage(A2dpSinkStreamingStateMachine.DISCONNECT); 605 mCurrentDevice = null; 606 transitionTo(mDisconnected); 607 } 608 } else { 609 loge("Disconnected from unknown device: " + device); 610 } 611 break; 612 default: 613 loge("Connection State Device: " + device + " bad state: " + state); 614 break; 615 } 616 } 617 processAudioStateEvent(int state, BluetoothDevice device)618 private void processAudioStateEvent(int state, BluetoothDevice device) { 619 if (!mCurrentDevice.equals(device)) { 620 loge("Audio State Device:" + device + "is different from ConnectedDevice:" + 621 mCurrentDevice); 622 return; 623 } 624 log(" processAudioStateEvent in state " + state); 625 switch (state) { 626 case AUDIO_STATE_STARTED: 627 mStreaming.sendMessage(A2dpSinkStreamingStateMachine.SRC_STR_START); 628 break; 629 case AUDIO_STATE_REMOTE_SUSPEND: 630 case AUDIO_STATE_STOPPED: 631 mStreaming.sendMessage(A2dpSinkStreamingStateMachine.SRC_STR_STOP); 632 break; 633 default: 634 loge("Audio State Device: " + device + " bad state: " + state); 635 break; 636 } 637 } 638 } 639 processAudioConfigEvent(BluetoothAudioConfig audioConfig, BluetoothDevice device)640 private void processAudioConfigEvent(BluetoothAudioConfig audioConfig, BluetoothDevice device) { 641 log("processAudioConfigEvent: " + device); 642 mAudioConfigs.put(device, audioConfig); 643 broadcastAudioConfig(device, audioConfig); 644 } 645 getConnectionState(BluetoothDevice device)646 int getConnectionState(BluetoothDevice device) { 647 if (getCurrentState() == mDisconnected) { 648 return BluetoothProfile.STATE_DISCONNECTED; 649 } 650 651 synchronized (this) { 652 IState currentState = getCurrentState(); 653 if (currentState == mPending) { 654 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 655 return BluetoothProfile.STATE_CONNECTING; 656 } 657 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 658 return BluetoothProfile.STATE_DISCONNECTING; 659 } 660 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 661 return BluetoothProfile.STATE_CONNECTING; // incoming connection 662 } 663 return BluetoothProfile.STATE_DISCONNECTED; 664 } 665 666 if (currentState == mConnected) { 667 if (mCurrentDevice.equals(device)) { 668 return BluetoothProfile.STATE_CONNECTED; 669 } 670 return BluetoothProfile.STATE_DISCONNECTED; 671 } else { 672 loge("Bad currentState: " + currentState); 673 return BluetoothProfile.STATE_DISCONNECTED; 674 } 675 } 676 } 677 getAudioConfig(BluetoothDevice device)678 BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { 679 return mAudioConfigs.get(device); 680 } 681 getConnectedDevices()682 List<BluetoothDevice> getConnectedDevices() { 683 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 684 synchronized(this) { 685 if (getCurrentState() == mConnected) { 686 devices.add(mCurrentDevice); 687 } 688 } 689 return devices; 690 } 691 isPlaying(BluetoothDevice device)692 boolean isPlaying(BluetoothDevice device) { 693 synchronized(this) { 694 if ((mPlayingDevice != null) && (device.equals(mPlayingDevice))) { 695 return true; 696 } 697 } 698 return false; 699 } 700 okToConnect(BluetoothDevice device)701 boolean okToConnect(BluetoothDevice device) { 702 AdapterService adapterService = AdapterService.getAdapterService(); 703 boolean ret = true; 704 //check if this is an incoming connection in Quiet mode. 705 if((adapterService == null) || 706 ((adapterService.isQuietModeEnabled() == true) && 707 (mTargetDevice == null))){ 708 ret = false; 709 } 710 return ret; 711 } 712 getDevicesMatchingConnectionStates(int[] states)713 synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 714 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 715 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 716 int connectionState; 717 718 for (BluetoothDevice device : bondedDevices) { 719 ParcelUuid[] featureUuids = device.getUuids(); 720 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSource)) { 721 continue; 722 } 723 connectionState = getConnectionState(device); 724 for(int i = 0; i < states.length; i++) { 725 if (connectionState == states[i]) { 726 deviceList.add(device); 727 } 728 } 729 } 730 return deviceList; 731 } 732 733 734 // This method does not check for error conditon (newState == prevState) broadcastConnectionState(BluetoothDevice device, int newState, int prevState)735 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 736 737 int delay = 0; 738 mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage( 739 MSG_CONNECTION_STATE_CHANGED, 740 prevState, 741 newState, 742 device), 743 delay); 744 } 745 broadcastAudioState(BluetoothDevice device, int state, int prevState)746 private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { 747 Intent intent = new Intent(BluetoothA2dpSink.ACTION_PLAYING_STATE_CHANGED); 748 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 749 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 750 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 751 //FIXME intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 752 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 753 754 log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); 755 } 756 broadcastAudioConfig(BluetoothDevice device, BluetoothAudioConfig audioConfig)757 private void broadcastAudioConfig(BluetoothDevice device, BluetoothAudioConfig audioConfig) { 758 Intent intent = new Intent(BluetoothA2dpSink.ACTION_AUDIO_CONFIG_CHANGED); 759 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 760 intent.putExtra(BluetoothA2dpSink.EXTRA_AUDIO_CONFIG, audioConfig); 761 //FIXME intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 762 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 763 764 log("A2DP Audio Config : device: " + device + " config: " + audioConfig); 765 } 766 getByteAddress(BluetoothDevice device)767 private byte[] getByteAddress(BluetoothDevice device) { 768 return Utils.getBytesFromAddress(device.getAddress()); 769 } 770 onConnectionStateChanged(int state, byte[] address)771 private void onConnectionStateChanged(int state, byte[] address) { 772 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 773 event.valueInt = state; 774 event.device = getDevice(address); 775 sendMessage(STACK_EVENT, event); 776 } 777 onAudioStateChanged(int state, byte[] address)778 private void onAudioStateChanged(int state, byte[] address) { 779 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 780 event.valueInt = state; 781 event.device = getDevice(address); 782 sendMessage(STACK_EVENT, event); 783 } 784 onAudioConfigChanged(byte[] address, int sampleRate, int channelCount)785 private void onAudioConfigChanged(byte[] address, int sampleRate, int channelCount) { 786 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_CONFIG_CHANGED); 787 int channelConfig = (channelCount == 1 ? AudioFormat.CHANNEL_IN_MONO 788 : AudioFormat.CHANNEL_IN_STEREO); 789 event.audioConfig = new BluetoothAudioConfig(sampleRate, channelConfig, 790 AudioFormat.ENCODING_PCM_16BIT); 791 event.device = getDevice(address); 792 sendMessage(STACK_EVENT, event); 793 } 794 getDevice(byte[] address)795 private BluetoothDevice getDevice(byte[] address) { 796 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 797 } 798 799 private class StackEvent { 800 int type = EVENT_TYPE_NONE; 801 int valueInt = 0; 802 BluetoothDevice device = null; 803 BluetoothAudioConfig audioConfig = null; 804 StackEvent(int type)805 private StackEvent(int type) { 806 this.type = type; 807 } 808 } 809 /** Handles A2DP connection state change intent broadcasts. */ 810 private class IntentBroadcastHandler extends Handler { 811 onConnectionStateChanged(BluetoothDevice device, int prevState, int state)812 private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { 813 Intent intent = new Intent(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); 814 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 815 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 816 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 817 //FIXME intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 818 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 819 log("Connection state " + device + ": " + prevState + "->" + state); 820 mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.A2DP_SINK, 821 state, prevState); 822 } 823 824 @Override handleMessage(Message msg)825 public void handleMessage(Message msg) { 826 switch (msg.what) { 827 case MSG_CONNECTION_STATE_CHANGED: 828 onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2); 829 break; 830 } 831 } 832 } 833 SendPassThruPlay(BluetoothDevice mDevice)834 public boolean SendPassThruPlay(BluetoothDevice mDevice) { 835 log("SendPassThruPlay + "); 836 AvrcpControllerService avrcpCtrlService = AvrcpControllerService.getAvrcpControllerService(); 837 if ((avrcpCtrlService != null) && (mDevice != null) && 838 (avrcpCtrlService.getConnectedDevices().contains(mDevice))){ 839 avrcpCtrlService.sendPassThroughCmd(mDevice, 840 BluetoothAvrcpController.PASS_THRU_CMD_ID_PLAY, 841 BluetoothAvrcpController.KEY_STATE_PRESSED); 842 avrcpCtrlService.sendPassThroughCmd(mDevice, 843 BluetoothAvrcpController.PASS_THRU_CMD_ID_PLAY, 844 BluetoothAvrcpController.KEY_STATE_RELEASED); 845 log(" SendPassThruPlay command sent - "); 846 return true; 847 } else { 848 log("passthru command not sent, connection unavailable"); 849 return false; 850 } 851 } 852 853 // Event types for STACK_EVENT message 854 final private static int EVENT_TYPE_NONE = 0; 855 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 856 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 857 final private static int EVENT_TYPE_AUDIO_CONFIG_CHANGED = 3; 858 859 // Do not modify without updating the HAL bt_av.h files. 860 861 // match up with btav_connection_state_t enum of bt_av.h 862 final static int CONNECTION_STATE_DISCONNECTED = 0; 863 final static int CONNECTION_STATE_CONNECTING = 1; 864 final static int CONNECTION_STATE_CONNECTED = 2; 865 final static int CONNECTION_STATE_DISCONNECTING = 3; 866 867 // match up with btav_audio_state_t enum of bt_av.h 868 final static int AUDIO_STATE_REMOTE_SUSPEND = 0; 869 final static int AUDIO_STATE_STOPPED = 1; 870 final static int AUDIO_STATE_STARTED = 2; 871 classInitNative()872 private native static void classInitNative(); initNative()873 private native void initNative(); cleanupNative()874 private native void cleanupNative(); connectA2dpNative(byte[] address)875 private native boolean connectA2dpNative(byte[] address); disconnectA2dpNative(byte[] address)876 private native boolean disconnectA2dpNative(byte[] address); informAudioFocusStateNative(int focusGranted)877 public native void informAudioFocusStateNative(int focusGranted); informAudioTrackGainNative(float focusGranted)878 public native void informAudioTrackGainNative(float focusGranted); 879 } 880