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