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