A2dpStateMachine.java revision ede67c26e7b2564ea35db6d9b3027a269c150e13
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 String TAG = "A2dpStateMachine"; 62 private static final boolean DBG = false; 63 64 static final int CONNECT = 1; 65 static final int DISCONNECT = 2; 66 private static final int STACK_EVENT = 101; 67 private static final int CONNECT_TIMEOUT = 201; 68 69 private Disconnected mDisconnected; 70 private Pending mPending; 71 private Connected mConnected; 72 73 private A2dpService mService; 74 private Context mContext; 75 private BluetoothAdapter mAdapter; 76 private final AudioManager mAudioManager; 77 private IntentBroadcastHandler mIntentBroadcastHandler; 78 private final WakeLock mWakeLock; 79 80 private static final int MSG_CONNECTION_STATE_CHANGED = 0; 81 82 private static final ParcelUuid[] A2DP_UUIDS = { 83 BluetoothUuid.AudioSink 84 }; 85 86 // mCurrentDevice is the device connected before the state changes 87 // mTargetDevice is the device to be connected 88 // mIncomingDevice is the device connecting to us, valid only in Pending state 89 // when mIncomingDevice is not null, both mCurrentDevice 90 // and mTargetDevice are null 91 // when either mCurrentDevice or mTargetDevice is not null, 92 // mIncomingDevice is null 93 // Stable states 94 // No connection, Disconnected state 95 // both mCurrentDevice and mTargetDevice are null 96 // Connected, Connected state 97 // mCurrentDevice is not null, mTargetDevice is null 98 // Interim states 99 // Connecting to a device, Pending 100 // mCurrentDevice is null, mTargetDevice is not null 101 // Disconnecting device, Connecting to new device 102 // Pending 103 // Both mCurrentDevice and mTargetDevice are not null 104 // Disconnecting device Pending 105 // mCurrentDevice is not null, mTargetDevice is null 106 // Incoming connections Pending 107 // Both mCurrentDevice and mTargetDevice are null 108 private BluetoothDevice mCurrentDevice = null; 109 private BluetoothDevice mTargetDevice = null; 110 private BluetoothDevice mIncomingDevice = null; 111 private BluetoothDevice mPlayingA2dpDevice = null; 112 113 114 static { 115 classInitNative(); 116 } 117 118 private A2dpStateMachine(A2dpService svc, Context context) { 119 super(TAG); 120 mService = svc; 121 mContext = context; 122 mAdapter = BluetoothAdapter.getDefaultAdapter(); 123 124 initNative(); 125 126 mDisconnected = new Disconnected(); 127 mPending = new Pending(); 128 mConnected = new Connected(); 129 130 addState(mDisconnected); 131 addState(mPending); 132 addState(mConnected); 133 134 setInitialState(mDisconnected); 135 136 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 137 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService"); 138 139 mIntentBroadcastHandler = new IntentBroadcastHandler(); 140 141 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 142 143 } 144 145 static A2dpStateMachine make(A2dpService svc, Context context) { 146 Log.d(TAG, "make"); 147 A2dpStateMachine a2dpSm = new A2dpStateMachine(svc, context); 148 a2dpSm.start(); 149 return a2dpSm; 150 } 151 152 public void doQuit() { 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 Log.e(TAG, "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 Log.e(TAG, "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 Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device); 225 break; 226 case CONNECTION_STATE_CONNECTING: 227 if (okToConnect(device)){ 228 Log.i(TAG,"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 Log.i(TAG,"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 Log.w(TAG, "A2DP Connected from Disconnected state"); 249 if (okToConnect(device)){ 250 Log.i(TAG,"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 Log.i(TAG,"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 Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device); 271 break; 272 default: 273 Log.e(TAG, "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 Log.e(TAG, "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 Log.e(TAG, "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 Log.e(TAG, "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 Log.e(TAG, "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 Log.e(TAG, "TargetDevice is getting disconnected"); 446 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 447 Log.e(TAG, "IncomingDevice is getting disconnected"); 448 } else { 449 Log.e(TAG, "Disconnecting unknow device: " + device); 450 } 451 break; 452 default: 453 Log.e(TAG, "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 Log.e(TAG, "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 Log.e(TAG, "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 Log.e(TAG, "Disconnected from unknown device: " + device); 549 } 550 break; 551 default: 552 Log.e(TAG, "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 Log.e(TAG, "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 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 broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING, 574 BluetoothA2dp.STATE_PLAYING); 575 } 576 break; 577 default: 578 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 579 break; 580 } 581 } 582 } 583 584 int getConnectionState(BluetoothDevice device) { 585 if (getCurrentState() == mDisconnected) { 586 return BluetoothProfile.STATE_DISCONNECTED; 587 } 588 589 synchronized (this) { 590 IState currentState = getCurrentState(); 591 if (currentState == mPending) { 592 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 593 return BluetoothProfile.STATE_CONNECTING; 594 } 595 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 596 return BluetoothProfile.STATE_DISCONNECTING; 597 } 598 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 599 return BluetoothProfile.STATE_CONNECTING; // incoming connection 600 } 601 return BluetoothProfile.STATE_DISCONNECTED; 602 } 603 604 if (currentState == mConnected) { 605 if (mCurrentDevice.equals(device)) { 606 return BluetoothProfile.STATE_CONNECTED; 607 } 608 return BluetoothProfile.STATE_DISCONNECTED; 609 } else { 610 Log.e(TAG, "Bad currentState: " + currentState); 611 return BluetoothProfile.STATE_DISCONNECTED; 612 } 613 } 614 } 615 616 List<BluetoothDevice> getConnectedDevices() { 617 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 618 synchronized(this) { 619 if (getCurrentState() == mConnected) { 620 devices.add(mCurrentDevice); 621 } 622 } 623 return devices; 624 } 625 626 boolean isPlaying(BluetoothDevice device) { 627 synchronized(this) { 628 if (device.equals(mPlayingA2dpDevice)) { 629 return true; 630 } 631 } 632 return false; 633 } 634 635 boolean okToConnect(BluetoothDevice device) { 636 AdapterService adapterService = AdapterService.getAdapterService(); 637 int priority = mService.getPriority(device); 638 boolean ret = false; 639 //check if this is an incoming connection in Quiet mode. 640 if((adapterService == null) || 641 ((adapterService.isQuietModeEnabled() == true) && 642 (mTargetDevice == null))){ 643 ret = false; 644 } 645 // check priority and accept or reject the connection. if priority is undefined 646 // it is likely that our SDP has not completed and peer is initiating the 647 // connection. Allow this connection, provided the device is bonded 648 else if((BluetoothProfile.PRIORITY_OFF < priority) || 649 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 650 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 651 ret= true; 652 } 653 return ret; 654 } 655 656 synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 657 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 658 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 659 int connectionState; 660 661 for (BluetoothDevice device : bondedDevices) { 662 ParcelUuid[] featureUuids = device.getUuids(); 663 if (!BluetoothUuid.containsAnyUuid(featureUuids, A2DP_UUIDS)) { 664 continue; 665 } 666 connectionState = getConnectionState(device); 667 for(int i = 0; i < states.length; i++) { 668 if (connectionState == states[i]) { 669 deviceList.add(device); 670 } 671 } 672 } 673 return deviceList; 674 } 675 676 677 // This method does not check for error conditon (newState == prevState) 678 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 679 680 int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, newState); 681 682 mWakeLock.acquire(); 683 mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage( 684 MSG_CONNECTION_STATE_CHANGED, 685 prevState, 686 newState, 687 device), 688 delay); 689 } 690 691 private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { 692 Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED); 693 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 694 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 695 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 696 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 697 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 698 699 log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); 700 } 701 702 private byte[] getByteAddress(BluetoothDevice device) { 703 return Utils.getBytesFromAddress(device.getAddress()); 704 } 705 706 private void onConnectionStateChanged(int state, byte[] address) { 707 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 708 event.valueInt = state; 709 event.device = getDevice(address); 710 sendMessage(STACK_EVENT, event); 711 } 712 713 private void onAudioStateChanged(int state, byte[] address) { 714 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 715 event.valueInt = state; 716 event.device = getDevice(address); 717 sendMessage(STACK_EVENT, event); 718 } 719 private BluetoothDevice getDevice(byte[] address) { 720 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 721 } 722 723 private void log(String msg) { 724 Log.d(TAG, msg); 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 762 // Event types for STACK_EVENT message 763 final private static int EVENT_TYPE_NONE = 0; 764 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 765 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 766 767 // Do not modify without updating the HAL bt_av.h files. 768 769 // match up with btav_connection_state_t enum of bt_av.h 770 final static int CONNECTION_STATE_DISCONNECTED = 0; 771 final static int CONNECTION_STATE_CONNECTING = 1; 772 final static int CONNECTION_STATE_CONNECTED = 2; 773 final static int CONNECTION_STATE_DISCONNECTING = 3; 774 775 // match up with btav_audio_state_t enum of bt_av.h 776 final static int AUDIO_STATE_REMOTE_SUSPEND = 0; 777 final static int AUDIO_STATE_STOPPED = 1; 778 final static int AUDIO_STATE_STARTED = 2; 779 780 private native static void classInitNative(); 781 private native void initNative(); 782 private native void cleanupNative(); 783 private native boolean connectA2dpNative(byte[] address); 784 private native boolean disconnectA2dpNative(byte[] address); 785} 786