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