A2dpSinkStateMachine.java revision 1aa7d25cdc80a4653d5e9465f58008398ac03d96
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 */ 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 61final class A2dpSinkStateMachine extends StateMachine { 62 private static final boolean DBG = true; 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 124 private final HashMap<BluetoothDevice,BluetoothAudioConfig> mAudioConfigs 125 = new HashMap<BluetoothDevice,BluetoothAudioConfig>(); 126 127 static { 128 classInitNative(); 129 } 130 131 private A2dpSinkStateMachine(A2dpSinkService svc, Context context) { 132 super("A2dpSinkStateMachine"); 133 mService = svc; 134 mContext = context; 135 mAdapter = BluetoothAdapter.getDefaultAdapter(); 136 137 initNative(); 138 139 mDisconnected = new Disconnected(); 140 mPending = new Pending(); 141 mConnected = new Connected(); 142 143 addState(mDisconnected); 144 addState(mPending); 145 addState(mConnected); 146 147 setInitialState(mDisconnected); 148 149 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 150 151 mIntentBroadcastHandler = new IntentBroadcastHandler(); 152 } 153 154 static A2dpSinkStateMachine make(A2dpSinkService svc, Context context) { 155 Log.d("A2dpSinkStateMachine", "make"); 156 A2dpSinkStateMachine a2dpSm = new A2dpSinkStateMachine(svc, context); 157 a2dpSm.start(); 158 return a2dpSm; 159 } 160 161 public void doQuit() { 162 quitNow(); 163 } 164 165 public void cleanup() { 166 cleanupNative(); 167 mAudioConfigs.clear(); 168 } 169 170 public void dump(StringBuilder sb) { 171 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 172 ProfileService.println(sb, "mTargetDevice: " + mTargetDevice); 173 ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice); 174 ProfileService.println(sb, "StateMachine: " + this.toString()); 175 } 176 177 private class Disconnected extends State { 178 @Override 179 public void enter() { 180 log("Enter Disconnected: " + getCurrentMessage().what); 181 } 182 183 @Override 184 public boolean processMessage(Message message) { 185 log("Disconnected process message: " + message.what); 186 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 187 loge("ERROR: current, target, or mIncomingDevice not null in Disconnected"); 188 return NOT_HANDLED; 189 } 190 191 boolean retValue = HANDLED; 192 switch(message.what) { 193 case CONNECT: 194 BluetoothDevice device = (BluetoothDevice) message.obj; 195 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 196 BluetoothProfile.STATE_DISCONNECTED); 197 198 if (!connectA2dpNative(getByteAddress(device)) ) { 199 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 200 BluetoothProfile.STATE_CONNECTING); 201 break; 202 } 203 204 synchronized (A2dpSinkStateMachine.this) { 205 mTargetDevice = device; 206 transitionTo(mPending); 207 } 208 // TODO(BT) remove CONNECT_TIMEOUT when the stack 209 // sends back events consistently 210 sendMessageDelayed(CONNECT_TIMEOUT, 30000); 211 break; 212 case DISCONNECT: 213 // ignore 214 break; 215 case STACK_EVENT: 216 StackEvent event = (StackEvent) message.obj; 217 switch (event.type) { 218 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 219 processConnectionEvent(event.valueInt, event.device); 220 break; 221 case EVENT_TYPE_AUDIO_CONFIG_CHANGED: 222 processAudioConfigEvent(event.audioConfig, event.device); 223 break; 224 default: 225 loge("Unexpected stack event: " + event.type); 226 break; 227 } 228 break; 229 default: 230 return NOT_HANDLED; 231 } 232 return retValue; 233 } 234 235 @Override 236 public void exit() { 237 log("Exit Disconnected: " + getCurrentMessage().what); 238 } 239 240 // in Disconnected state 241 private void processConnectionEvent(int state, BluetoothDevice device) { 242 switch (state) { 243 case CONNECTION_STATE_DISCONNECTED: 244 logw("Ignore A2DP DISCONNECTED event, device: " + device); 245 break; 246 case CONNECTION_STATE_CONNECTING: 247 if (okToConnect(device)){ 248 logi("Incoming A2DP accepted"); 249 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 250 BluetoothProfile.STATE_DISCONNECTED); 251 synchronized (A2dpSinkStateMachine.this) { 252 mIncomingDevice = device; 253 transitionTo(mPending); 254 } 255 } else { 256 //reject the connection and stay in Disconnected state itself 257 logi("Incoming A2DP rejected"); 258 disconnectA2dpNative(getByteAddress(device)); 259 // the other profile connection should be initiated 260 AdapterService adapterService = AdapterService.getAdapterService(); 261 if (adapterService != null) { 262 adapterService.connectOtherProfile(device, 263 AdapterService.PROFILE_CONN_REJECTED); 264 } 265 } 266 break; 267 case CONNECTION_STATE_CONNECTED: 268 logw("A2DP Connected from Disconnected state"); 269 if (okToConnect(device)){ 270 logi("Incoming A2DP accepted"); 271 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 272 BluetoothProfile.STATE_DISCONNECTED); 273 synchronized (A2dpSinkStateMachine.this) { 274 mCurrentDevice = device; 275 transitionTo(mConnected); 276 } 277 } else { 278 //reject the connection and stay in Disconnected state itself 279 logi("Incoming A2DP rejected"); 280 disconnectA2dpNative(getByteAddress(device)); 281 // the other profile connection should be initiated 282 AdapterService adapterService = AdapterService.getAdapterService(); 283 if (adapterService != null) { 284 adapterService.connectOtherProfile(device, 285 AdapterService.PROFILE_CONN_REJECTED); 286 } 287 } 288 break; 289 case CONNECTION_STATE_DISCONNECTING: 290 logw("Ignore HF DISCONNECTING event, device: " + device); 291 break; 292 default: 293 loge("Incorrect state: " + state); 294 break; 295 } 296 } 297 } 298 299 private class Pending extends State { 300 @Override 301 public void enter() { 302 log("Enter Pending: " + getCurrentMessage().what); 303 } 304 305 @Override 306 public boolean processMessage(Message message) { 307 log("Pending process message: " + message.what); 308 309 boolean retValue = HANDLED; 310 switch(message.what) { 311 case CONNECT: 312 deferMessage(message); 313 break; 314 case CONNECT_TIMEOUT: 315 onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED, 316 getByteAddress(mTargetDevice)); 317 break; 318 case DISCONNECT: 319 BluetoothDevice device = (BluetoothDevice) message.obj; 320 if (mCurrentDevice != null && mTargetDevice != null && 321 mTargetDevice.equals(device) ) { 322 // cancel connection to the mTargetDevice 323 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 324 BluetoothProfile.STATE_CONNECTING); 325 synchronized (A2dpSinkStateMachine.this) { 326 mTargetDevice = null; 327 } 328 } else { 329 deferMessage(message); 330 } 331 break; 332 case STACK_EVENT: 333 StackEvent event = (StackEvent) message.obj; 334 log("STACK_EVENT " + event.type); 335 switch (event.type) { 336 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 337 removeMessages(CONNECT_TIMEOUT); 338 processConnectionEvent(event.valueInt, event.device); 339 break; 340 case EVENT_TYPE_AUDIO_CONFIG_CHANGED: 341 processAudioConfigEvent(event.audioConfig, event.device); 342 break; 343 default: 344 loge("Unexpected stack event: " + event.type); 345 break; 346 } 347 break; 348 default: 349 return NOT_HANDLED; 350 } 351 return retValue; 352 } 353 354 // in Pending state 355 private void processConnectionEvent(int state, BluetoothDevice device) { 356 log("processConnectionEvent state " + state); 357 log("Devices curr: " + mCurrentDevice + " target: " + mTargetDevice + 358 " incoming: " + mIncomingDevice + " device: " + device); 359 switch (state) { 360 case CONNECTION_STATE_DISCONNECTED: 361 mAudioConfigs.remove(device); 362 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 363 broadcastConnectionState(mCurrentDevice, 364 BluetoothProfile.STATE_DISCONNECTED, 365 BluetoothProfile.STATE_DISCONNECTING); 366 synchronized (A2dpSinkStateMachine.this) { 367 mCurrentDevice = null; 368 } 369 370 if (mTargetDevice != null) { 371 if (!connectA2dpNative(getByteAddress(mTargetDevice))) { 372 broadcastConnectionState(mTargetDevice, 373 BluetoothProfile.STATE_DISCONNECTED, 374 BluetoothProfile.STATE_CONNECTING); 375 synchronized (A2dpSinkStateMachine.this) { 376 mTargetDevice = null; 377 transitionTo(mDisconnected); 378 } 379 } 380 } else { 381 synchronized (A2dpSinkStateMachine.this) { 382 mIncomingDevice = null; 383 transitionTo(mDisconnected); 384 } 385 } 386 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 387 // outgoing connection failed 388 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 389 BluetoothProfile.STATE_CONNECTING); 390 synchronized (A2dpSinkStateMachine.this) { 391 mTargetDevice = null; 392 transitionTo(mDisconnected); 393 } 394 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 395 broadcastConnectionState(mIncomingDevice, 396 BluetoothProfile.STATE_DISCONNECTED, 397 BluetoothProfile.STATE_CONNECTING); 398 synchronized (A2dpSinkStateMachine.this) { 399 mIncomingDevice = null; 400 transitionTo(mDisconnected); 401 } 402 } else { 403 loge("Unknown device Disconnected: " + device); 404 } 405 break; 406 case CONNECTION_STATE_CONNECTED: 407 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 408 loge("current device is not null"); 409 // disconnection failed 410 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 411 BluetoothProfile.STATE_DISCONNECTING); 412 if (mTargetDevice != null) { 413 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 414 BluetoothProfile.STATE_CONNECTING); 415 } 416 synchronized (A2dpSinkStateMachine.this) { 417 mTargetDevice = null; 418 transitionTo(mConnected); 419 } 420 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 421 loge("target device is not null"); 422 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 423 BluetoothProfile.STATE_CONNECTING); 424 synchronized (A2dpSinkStateMachine.this) { 425 mCurrentDevice = mTargetDevice; 426 mTargetDevice = null; 427 transitionTo(mConnected); 428 } 429 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 430 loge("incoming device is not null"); 431 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 432 BluetoothProfile.STATE_CONNECTING); 433 synchronized (A2dpSinkStateMachine.this) { 434 mCurrentDevice = mIncomingDevice; 435 mIncomingDevice = null; 436 transitionTo(mConnected); 437 } 438 } else { 439 loge("Unknown device Connected: " + device); 440 // something is wrong here, but sync our state with stack by connecting to 441 // the new device and disconnect from previous device. 442 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 443 BluetoothProfile.STATE_DISCONNECTED); 444 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 445 BluetoothProfile.STATE_CONNECTING); 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 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 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 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 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 640 private void processAudioConfigEvent(BluetoothAudioConfig audioConfig, BluetoothDevice device) { 641 log("processAudioConfigEvent: " + device); 642 mAudioConfigs.put(device, audioConfig); 643 broadcastAudioConfig(device, audioConfig); 644 } 645 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 678 BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { 679 return mAudioConfigs.get(device); 680 } 681 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 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 701 // Utility Functions 702 boolean okToConnect(BluetoothDevice device) { 703 AdapterService adapterService = AdapterService.getAdapterService(); 704 int priority = mService.getPriority(device); 705 706 // check priority and accept or reject the connection. if priority is undefined 707 // it is likely that our SDP has not completed and peer is initiating the 708 // connection. Allow this connection, provided the device is bonded 709 if((BluetoothProfile.PRIORITY_OFF < priority) || 710 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 711 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 712 return true; 713 } 714 logw("okToConnect not OK to connect " + device); 715 return false; 716 } 717 718 synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 719 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 720 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 721 int connectionState; 722 723 for (BluetoothDevice device : bondedDevices) { 724 ParcelUuid[] featureUuids = device.getUuids(); 725 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSource)) { 726 continue; 727 } 728 connectionState = getConnectionState(device); 729 for(int i = 0; i < states.length; i++) { 730 if (connectionState == states[i]) { 731 deviceList.add(device); 732 } 733 } 734 } 735 return deviceList; 736 } 737 738 739 // This method does not check for error conditon (newState == prevState) 740 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 741 742 int delay = 0; 743 mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage( 744 MSG_CONNECTION_STATE_CHANGED, 745 prevState, 746 newState, 747 device), 748 delay); 749 } 750 751 private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { 752 Intent intent = new Intent(BluetoothA2dpSink.ACTION_PLAYING_STATE_CHANGED); 753 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 754 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 755 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 756//FIXME intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 757 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 758 759 log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); 760 } 761 762 private void broadcastAudioConfig(BluetoothDevice device, BluetoothAudioConfig audioConfig) { 763 Intent intent = new Intent(BluetoothA2dpSink.ACTION_AUDIO_CONFIG_CHANGED); 764 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 765 intent.putExtra(BluetoothA2dpSink.EXTRA_AUDIO_CONFIG, audioConfig); 766//FIXME intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 767 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 768 769 log("A2DP Audio Config : device: " + device + " config: " + audioConfig); 770 } 771 772 private byte[] getByteAddress(BluetoothDevice device) { 773 return Utils.getBytesFromAddress(device.getAddress()); 774 } 775 776 private void onConnectionStateChanged(int state, byte[] address) { 777 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 778 event.valueInt = state; 779 event.device = getDevice(address); 780 sendMessage(STACK_EVENT, event); 781 } 782 783 private void onAudioStateChanged(int state, byte[] address) { 784 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 785 event.valueInt = state; 786 event.device = getDevice(address); 787 sendMessage(STACK_EVENT, event); 788 } 789 790 private void onAudioConfigChanged(byte[] address, int sampleRate, int channelCount) { 791 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_CONFIG_CHANGED); 792 int channelConfig = (channelCount == 1 ? AudioFormat.CHANNEL_IN_MONO 793 : AudioFormat.CHANNEL_IN_STEREO); 794 event.audioConfig = new BluetoothAudioConfig(sampleRate, channelConfig, 795 AudioFormat.ENCODING_PCM_16BIT); 796 event.device = getDevice(address); 797 sendMessage(STACK_EVENT, event); 798 } 799 800 private BluetoothDevice getDevice(byte[] address) { 801 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 802 } 803 804 private class StackEvent { 805 int type = EVENT_TYPE_NONE; 806 int valueInt = 0; 807 BluetoothDevice device = null; 808 BluetoothAudioConfig audioConfig = null; 809 810 private StackEvent(int type) { 811 this.type = type; 812 } 813 } 814 815 /** Handles A2DP connection state change intent broadcasts. */ 816 private class IntentBroadcastHandler extends Handler { 817 818 private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { 819 Intent intent = new Intent(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); 820 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 821 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 822 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 823//FIXME intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 824 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 825 log("Connection state " + device + ": " + prevState + "->" + state); 826 mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.A2DP_SINK, 827 state, prevState); 828 } 829 830 @Override 831 public void handleMessage(Message msg) { 832 switch (msg.what) { 833 case MSG_CONNECTION_STATE_CHANGED: 834 onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2); 835 break; 836 } 837 } 838 } 839 840 public boolean SendPassThruPlay(BluetoothDevice mDevice) { 841 log("SendPassThruPlay + "); 842 AvrcpControllerService avrcpCtrlService = AvrcpControllerService.getAvrcpControllerService(); 843 if ((avrcpCtrlService != null) && (mDevice != null) && 844 (avrcpCtrlService.getConnectedDevices().contains(mDevice))){ 845 avrcpCtrlService.sendPassThroughCmd(mDevice, 846 AvrcpControllerService.PASS_THRU_CMD_ID_PLAY, 847 AvrcpControllerService.KEY_STATE_PRESSED); 848 avrcpCtrlService.sendPassThroughCmd(mDevice, 849 AvrcpControllerService.PASS_THRU_CMD_ID_PLAY, 850 AvrcpControllerService.KEY_STATE_RELEASED); 851 log(" SendPassThruPlay command sent - "); 852 return true; 853 } else { 854 log("passthru command not sent, connection unavailable"); 855 return false; 856 } 857 } 858 859 // Event types for STACK_EVENT message 860 final private static int EVENT_TYPE_NONE = 0; 861 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 862 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 863 final private static int EVENT_TYPE_AUDIO_CONFIG_CHANGED = 3; 864 865 // Do not modify without updating the HAL bt_av.h files. 866 867 // match up with btav_connection_state_t enum of bt_av.h 868 final static int CONNECTION_STATE_DISCONNECTED = 0; 869 final static int CONNECTION_STATE_CONNECTING = 1; 870 final static int CONNECTION_STATE_CONNECTED = 2; 871 final static int CONNECTION_STATE_DISCONNECTING = 3; 872 873 // match up with btav_audio_state_t enum of bt_av.h 874 final static int AUDIO_STATE_REMOTE_SUSPEND = 0; 875 final static int AUDIO_STATE_STOPPED = 1; 876 final static int AUDIO_STATE_STARTED = 2; 877 878 private native static void classInitNative(); 879 private native void initNative(); 880 private native void cleanupNative(); 881 private native boolean connectA2dpNative(byte[] address); 882 private native boolean disconnectA2dpNative(byte[] address); 883 public native void informAudioFocusStateNative(int focusGranted); 884 public native void informAudioTrackGainNative(float focusGranted); 885} 886