A2dpStateMachine.java revision 6ddb5d5b31b3e4e21b57bbe0457692fdbcc97eba
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.bluetooth.IBluetooth; 37import android.content.Context; 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.content.Intent; 45import android.os.Message; 46import android.os.RemoteException; 47import android.os.ServiceManager; 48import android.os.ParcelUuid; 49import android.util.Log; 50import com.android.bluetooth.Utils; 51import com.android.bluetooth.btservice.AdapterService; 52import com.android.bluetooth.btservice.ProfileService; 53import com.android.internal.util.IState; 54import com.android.internal.util.State; 55import com.android.internal.util.StateMachine; 56import java.util.ArrayList; 57import java.util.List; 58import java.util.Set; 59 60final 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 { 110 classInitNative(); 111 } 112 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 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 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 156 public void cleanup() { 157 cleanupNative(); 158 } 159 160 private class Disconnected extends State { 161 @Override 162 public void enter() { 163 log("Enter Disconnected: " + getCurrentMessage().what); 164 } 165 166 @Override 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 216 public void exit() { 217 log("Exit Disconnected: " + getCurrentMessage().what); 218 } 219 220 // in Disconnected state 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 HF 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 281 public void enter() { 282 log("Enter Pending: " + getCurrentMessage().what); 283 } 284 285 @Override 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 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 synchronized (A2dpStateMachine.this) { 363 mTargetDevice = null; 364 transitionTo(mDisconnected); 365 } 366 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 367 broadcastConnectionState(mIncomingDevice, 368 BluetoothProfile.STATE_DISCONNECTED, 369 BluetoothProfile.STATE_CONNECTING); 370 synchronized (A2dpStateMachine.this) { 371 mIncomingDevice = null; 372 transitionTo(mDisconnected); 373 } 374 } else { 375 loge("Unknown device Disconnected: " + device); 376 } 377 break; 378 case CONNECTION_STATE_CONNECTED: 379 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 380 // disconnection failed 381 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 382 BluetoothProfile.STATE_DISCONNECTING); 383 if (mTargetDevice != null) { 384 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 385 BluetoothProfile.STATE_CONNECTING); 386 } 387 synchronized (A2dpStateMachine.this) { 388 mTargetDevice = null; 389 transitionTo(mConnected); 390 } 391 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 392 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 393 BluetoothProfile.STATE_CONNECTING); 394 synchronized (A2dpStateMachine.this) { 395 mCurrentDevice = mTargetDevice; 396 mTargetDevice = null; 397 transitionTo(mConnected); 398 } 399 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 400 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 401 BluetoothProfile.STATE_CONNECTING); 402 synchronized (A2dpStateMachine.this) { 403 mCurrentDevice = mIncomingDevice; 404 mIncomingDevice = null; 405 transitionTo(mConnected); 406 } 407 } else { 408 loge("Unknown device Connected: " + device); 409 // something is wrong here, but sync our state with stack 410 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 411 BluetoothProfile.STATE_DISCONNECTED); 412 synchronized (A2dpStateMachine.this) { 413 mCurrentDevice = device; 414 mTargetDevice = null; 415 mIncomingDevice = null; 416 transitionTo(mConnected); 417 } 418 } 419 break; 420 case CONNECTION_STATE_CONNECTING: 421 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 422 log("current device tries to connect back"); 423 // TODO(BT) ignore or reject 424 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 425 // The stack is connecting to target device or 426 // there is an incoming connection from the target device at the same time 427 // we already broadcasted the intent, doing nothing here 428 log("Stack and target device are connecting"); 429 } 430 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 431 loge("Another connecting event on the incoming device"); 432 } else { 433 // We get an incoming connecting request while Pending 434 // TODO(BT) is stack handing this case? let's ignore it for now 435 log("Incoming connection while pending, ignore"); 436 } 437 break; 438 case CONNECTION_STATE_DISCONNECTING: 439 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 440 // we already broadcasted the intent, doing nothing here 441 if (DBG) { 442 log("stack is disconnecting mCurrentDevice"); 443 } 444 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 445 loge("TargetDevice is getting disconnected"); 446 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 447 loge("IncomingDevice is getting disconnected"); 448 } else { 449 loge("Disconnecting unknow device: " + device); 450 } 451 break; 452 default: 453 loge("Incorrect state: " + state); 454 break; 455 } 456 } 457 458 } 459 460 private class Connected extends State { 461 @Override 462 public void enter() { 463 log("Enter Connected: " + getCurrentMessage().what); 464 // Upon connected, the audio starts out as stopped 465 broadcastAudioState(mCurrentDevice, BluetoothA2dp.STATE_NOT_PLAYING, 466 BluetoothA2dp.STATE_PLAYING); 467 } 468 469 @Override 470 public boolean processMessage(Message message) { 471 log("Connected process message: " + message.what); 472 if (mCurrentDevice == null) { 473 loge("ERROR: mCurrentDevice is null in Connected"); 474 return NOT_HANDLED; 475 } 476 477 boolean retValue = HANDLED; 478 switch(message.what) { 479 case CONNECT: 480 { 481 BluetoothDevice device = (BluetoothDevice) message.obj; 482 if (mCurrentDevice.equals(device)) { 483 break; 484 } 485 486 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 487 BluetoothProfile.STATE_DISCONNECTED); 488 if (!disconnectA2dpNative(getByteAddress(mCurrentDevice))) { 489 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 490 BluetoothProfile.STATE_CONNECTING); 491 break; 492 } 493 494 synchronized (A2dpStateMachine.this) { 495 mTargetDevice = device; 496 transitionTo(mPending); 497 } 498 } 499 break; 500 case DISCONNECT: 501 { 502 BluetoothDevice device = (BluetoothDevice) message.obj; 503 if (!mCurrentDevice.equals(device)) { 504 break; 505 } 506 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 507 BluetoothProfile.STATE_CONNECTED); 508 if (!disconnectA2dpNative(getByteAddress(device))) { 509 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 510 BluetoothProfile.STATE_DISCONNECTED); 511 break; 512 } 513 transitionTo(mPending); 514 } 515 break; 516 case STACK_EVENT: 517 StackEvent event = (StackEvent) message.obj; 518 switch (event.type) { 519 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 520 processConnectionEvent(event.valueInt, event.device); 521 break; 522 case EVENT_TYPE_AUDIO_STATE_CHANGED: 523 processAudioStateEvent(event.valueInt, event.device); 524 break; 525 default: 526 loge("Unexpected stack event: " + event.type); 527 break; 528 } 529 break; 530 default: 531 return NOT_HANDLED; 532 } 533 return retValue; 534 } 535 536 // in Connected state 537 private void processConnectionEvent(int state, BluetoothDevice device) { 538 switch (state) { 539 case CONNECTION_STATE_DISCONNECTED: 540 if (mCurrentDevice.equals(device)) { 541 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 542 BluetoothProfile.STATE_CONNECTED); 543 synchronized (A2dpStateMachine.this) { 544 mCurrentDevice = null; 545 transitionTo(mDisconnected); 546 } 547 } else { 548 loge("Disconnected from unknown device: " + device); 549 } 550 break; 551 default: 552 loge("Connection State Device: " + device + " bad state: " + state); 553 break; 554 } 555 } 556 private void processAudioStateEvent(int state, BluetoothDevice device) { 557 if (!mCurrentDevice.equals(device)) { 558 loge("Audio State Device:" + device + "is different from ConnectedDevice:" + 559 mCurrentDevice); 560 return; 561 } 562 switch (state) { 563 case AUDIO_STATE_STARTED: 564 if (mPlayingA2dpDevice == null) { 565 mPlayingA2dpDevice = device; 566 mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING); 567 broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING, 568 BluetoothA2dp.STATE_NOT_PLAYING); 569 } 570 break; 571 case AUDIO_STATE_REMOTE_SUSPEND: 572 case AUDIO_STATE_STOPPED: 573 if (mPlayingA2dpDevice != null) { 574 mPlayingA2dpDevice = null; 575 mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); 576 broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING, 577 BluetoothA2dp.STATE_PLAYING); 578 } 579 break; 580 default: 581 loge("Audio State Device: " + device + " bad state: " + state); 582 break; 583 } 584 } 585 } 586 587 int getConnectionState(BluetoothDevice device) { 588 if (getCurrentState() == mDisconnected) { 589 return BluetoothProfile.STATE_DISCONNECTED; 590 } 591 592 synchronized (this) { 593 IState currentState = getCurrentState(); 594 if (currentState == mPending) { 595 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 596 return BluetoothProfile.STATE_CONNECTING; 597 } 598 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 599 return BluetoothProfile.STATE_DISCONNECTING; 600 } 601 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 602 return BluetoothProfile.STATE_CONNECTING; // incoming connection 603 } 604 return BluetoothProfile.STATE_DISCONNECTED; 605 } 606 607 if (currentState == mConnected) { 608 if (mCurrentDevice.equals(device)) { 609 return BluetoothProfile.STATE_CONNECTED; 610 } 611 return BluetoothProfile.STATE_DISCONNECTED; 612 } else { 613 loge("Bad currentState: " + currentState); 614 return BluetoothProfile.STATE_DISCONNECTED; 615 } 616 } 617 } 618 619 List<BluetoothDevice> getConnectedDevices() { 620 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 621 synchronized(this) { 622 if (getCurrentState() == mConnected) { 623 devices.add(mCurrentDevice); 624 } 625 } 626 return devices; 627 } 628 629 boolean isPlaying(BluetoothDevice device) { 630 synchronized(this) { 631 if (device.equals(mPlayingA2dpDevice)) { 632 return true; 633 } 634 } 635 return false; 636 } 637 638 boolean okToConnect(BluetoothDevice device) { 639 AdapterService adapterService = AdapterService.getAdapterService(); 640 int priority = mService.getPriority(device); 641 boolean ret = false; 642 //check if this is an incoming connection in Quiet mode. 643 if((adapterService == null) || 644 ((adapterService.isQuietModeEnabled() == true) && 645 (mTargetDevice == null))){ 646 ret = false; 647 } 648 // check priority and accept or reject the connection. if priority is undefined 649 // it is likely that our SDP has not completed and peer is initiating the 650 // connection. Allow this connection, provided the device is bonded 651 else if((BluetoothProfile.PRIORITY_OFF < priority) || 652 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 653 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 654 ret= true; 655 } 656 return ret; 657 } 658 659 synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 660 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 661 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 662 int connectionState; 663 664 for (BluetoothDevice device : bondedDevices) { 665 ParcelUuid[] featureUuids = device.getUuids(); 666 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSink)) { 667 continue; 668 } 669 connectionState = getConnectionState(device); 670 for(int i = 0; i < states.length; i++) { 671 if (connectionState == states[i]) { 672 deviceList.add(device); 673 } 674 } 675 } 676 return deviceList; 677 } 678 679 680 // This method does not check for error conditon (newState == prevState) 681 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 682 683 int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, newState, 684 BluetoothProfile.A2DP); 685 686 mWakeLock.acquire(); 687 mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage( 688 MSG_CONNECTION_STATE_CHANGED, 689 prevState, 690 newState, 691 device), 692 delay); 693 } 694 695 private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { 696 Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED); 697 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 698 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 699 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 700 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 701 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 702 703 log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); 704 } 705 706 private byte[] getByteAddress(BluetoothDevice device) { 707 return Utils.getBytesFromAddress(device.getAddress()); 708 } 709 710 private void onConnectionStateChanged(int state, byte[] address) { 711 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 712 event.valueInt = state; 713 event.device = getDevice(address); 714 sendMessage(STACK_EVENT, event); 715 } 716 717 private void onAudioStateChanged(int state, byte[] address) { 718 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 719 event.valueInt = state; 720 event.device = getDevice(address); 721 sendMessage(STACK_EVENT, event); 722 } 723 private BluetoothDevice getDevice(byte[] address) { 724 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 725 } 726 727 private class StackEvent { 728 int type = EVENT_TYPE_NONE; 729 int valueInt = 0; 730 BluetoothDevice device = null; 731 732 private StackEvent(int type) { 733 this.type = type; 734 } 735 } 736 /** Handles A2DP connection state change intent broadcasts. */ 737 private class IntentBroadcastHandler extends Handler { 738 739 private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { 740 Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 741 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 742 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 743 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 744 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 745 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 746 log("Connection state " + device + ": " + prevState + "->" + state); 747 mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.A2DP, state, prevState); 748 } 749 750 @Override 751 public void handleMessage(Message msg) { 752 switch (msg.what) { 753 case MSG_CONNECTION_STATE_CHANGED: 754 onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2); 755 mWakeLock.release(); 756 break; 757 } 758 } 759 } 760 761 public void dump(StringBuilder sb) { 762 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 763 ProfileService.println(sb, "mTargetDevice: " + mTargetDevice); 764 ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice); 765 ProfileService.println(sb, "mPlayingA2dpDevice: " + mPlayingA2dpDevice); 766 ProfileService.println(sb, "StateMachine: " + this.toString()); 767 } 768 769 // Event types for STACK_EVENT message 770 final private static int EVENT_TYPE_NONE = 0; 771 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 772 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 773 774 // Do not modify without updating the HAL bt_av.h files. 775 776 // match up with btav_connection_state_t enum of bt_av.h 777 final static int CONNECTION_STATE_DISCONNECTED = 0; 778 final static int CONNECTION_STATE_CONNECTING = 1; 779 final static int CONNECTION_STATE_CONNECTED = 2; 780 final static int CONNECTION_STATE_DISCONNECTING = 3; 781 782 // match up with btav_audio_state_t enum of bt_av.h 783 final static int AUDIO_STATE_REMOTE_SUSPEND = 0; 784 final static int AUDIO_STATE_STOPPED = 1; 785 final static int AUDIO_STATE_STARTED = 2; 786 787 private native static void classInitNative(); 788 private native void initNative(); 789 private native void cleanupNative(); 790 private native boolean connectA2dpNative(byte[] address); 791 private native boolean disconnectA2dpNative(byte[] address); 792} 793