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