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