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