A2dpStateMachine.java revision 33b6b7d2b08dcd7ced1809e2ac653c5e8c2514af
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.BluetoothCodecConfig; 34import android.bluetooth.BluetoothDevice; 35import android.bluetooth.BluetoothProfile; 36import android.bluetooth.BluetoothUuid; 37import android.content.Context; 38import android.content.Intent; 39import android.media.AudioManager; 40import android.os.Handler; 41import android.os.Message; 42import android.os.ParcelUuid; 43import android.os.PowerManager; 44import android.os.PowerManager.WakeLock; 45import android.util.Log; 46 47import com.android.bluetooth.Utils; 48import com.android.bluetooth.btservice.AdapterService; 49import com.android.bluetooth.btservice.ProfileService; 50import com.android.internal.util.IState; 51import com.android.internal.util.State; 52import com.android.internal.util.StateMachine; 53 54import java.util.ArrayList; 55import java.util.List; 56import java.util.Set; 57 58final class A2dpStateMachine extends StateMachine { 59 private static final boolean DBG = false; 60 61 static final int CONNECT = 1; 62 static final int DISCONNECT = 2; 63 private static final int STACK_EVENT = 101; 64 private static final int CONNECT_TIMEOUT = 201; 65 66 private Disconnected mDisconnected; 67 private Pending mPending; 68 private Connected mConnected; 69 70 private A2dpService mService; 71 private Context mContext; 72 private BluetoothAdapter mAdapter; 73 private final AudioManager mAudioManager; 74 private IntentBroadcastHandler mIntentBroadcastHandler; 75 private final WakeLock mWakeLock; 76 77 private static final int MSG_CONNECTION_STATE_CHANGED = 0; 78 79 // mCurrentDevice is the device connected before the state changes 80 // mTargetDevice is the device to be connected 81 // mIncomingDevice is the device connecting to us, valid only in Pending state 82 // when mIncomingDevice is not null, both mCurrentDevice 83 // and mTargetDevice are null 84 // when either mCurrentDevice or mTargetDevice is not null, 85 // mIncomingDevice is null 86 // Stable states 87 // No connection, Disconnected state 88 // both mCurrentDevice and mTargetDevice are null 89 // Connected, Connected state 90 // mCurrentDevice is not null, mTargetDevice is null 91 // Interim states 92 // Connecting to a device, Pending 93 // mCurrentDevice is null, mTargetDevice is not null 94 // Disconnecting device, Connecting to new device 95 // Pending 96 // Both mCurrentDevice and mTargetDevice are not null 97 // Disconnecting device Pending 98 // mCurrentDevice is not null, mTargetDevice is null 99 // Incoming connections Pending 100 // Both mCurrentDevice and mTargetDevice are null 101 private BluetoothDevice mCurrentDevice = null; 102 private BluetoothDevice mTargetDevice = null; 103 private BluetoothDevice mIncomingDevice = null; 104 private BluetoothDevice mPlayingA2dpDevice = null; 105 106 private BluetoothCodecConfig mCodecConfig = null; 107 108 static { 109 classInitNative(); 110 } 111 112 private A2dpStateMachine(A2dpService svc, Context context) { 113 super("A2dpStateMachine"); 114 mService = svc; 115 mContext = context; 116 mAdapter = BluetoothAdapter.getDefaultAdapter(); 117 118 initNative(); 119 120 mDisconnected = new Disconnected(); 121 mPending = new Pending(); 122 mConnected = new Connected(); 123 124 addState(mDisconnected); 125 addState(mPending); 126 addState(mConnected); 127 128 setInitialState(mDisconnected); 129 130 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 131 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService"); 132 133 mIntentBroadcastHandler = new IntentBroadcastHandler(); 134 135 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 136 } 137 138 static A2dpStateMachine make(A2dpService svc, Context context) { 139 Log.d("A2dpStateMachine", "make"); 140 A2dpStateMachine a2dpSm = new A2dpStateMachine(svc, context); 141 a2dpSm.start(); 142 return a2dpSm; 143 } 144 145 public void doQuit() { 146 if ((mTargetDevice != null) && 147 (getConnectionState(mTargetDevice) == BluetoothProfile.STATE_CONNECTING)) { 148 log("doQuit()- Move A2DP State to DISCONNECTED"); 149 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 150 BluetoothProfile.STATE_CONNECTING); 151 } 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 A2dp 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 // check if there is some incoming connection request 362 if (mIncomingDevice != null) { 363 logi("disconnect for outgoing in pending state"); 364 synchronized (A2dpStateMachine.this) { 365 mTargetDevice = null; 366 } 367 break; 368 } 369 synchronized (A2dpStateMachine.this) { 370 mTargetDevice = null; 371 transitionTo(mDisconnected); 372 } 373 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 374 broadcastConnectionState(mIncomingDevice, 375 BluetoothProfile.STATE_DISCONNECTED, 376 BluetoothProfile.STATE_CONNECTING); 377 synchronized (A2dpStateMachine.this) { 378 mIncomingDevice = null; 379 transitionTo(mDisconnected); 380 } 381 } else { 382 loge("Unknown device Disconnected: " + device); 383 } 384 break; 385 case CONNECTION_STATE_CONNECTED: 386 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 387 // disconnection failed 388 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 389 BluetoothProfile.STATE_DISCONNECTING); 390 if (mTargetDevice != null) { 391 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 392 BluetoothProfile.STATE_CONNECTING); 393 } 394 synchronized (A2dpStateMachine.this) { 395 mTargetDevice = null; 396 transitionTo(mConnected); 397 } 398 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 399 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 400 BluetoothProfile.STATE_CONNECTING); 401 synchronized (A2dpStateMachine.this) { 402 mCurrentDevice = mTargetDevice; 403 mTargetDevice = null; 404 transitionTo(mConnected); 405 } 406 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 407 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 408 BluetoothProfile.STATE_CONNECTING); 409 // check for a2dp connection allowed for this device in race condition 410 if (okToConnect(mIncomingDevice)) { 411 logi("Ready to connect incoming Connection from pending state"); 412 synchronized (A2dpStateMachine.this) { 413 mCurrentDevice = mIncomingDevice; 414 mIncomingDevice = null; 415 transitionTo(mConnected); 416 } 417 } else { 418 // A2dp connection unchecked for this device 419 loge("Incoming A2DP rejected from pending state"); 420 disconnectA2dpNative(getByteAddress(device)); 421 } 422 } else { 423 loge("Unknown device Connected: " + device); 424 // something is wrong here, but sync our state with stack 425 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 426 BluetoothProfile.STATE_DISCONNECTED); 427 synchronized (A2dpStateMachine.this) { 428 mCurrentDevice = device; 429 mTargetDevice = null; 430 mIncomingDevice = null; 431 transitionTo(mConnected); 432 } 433 } 434 break; 435 case CONNECTION_STATE_CONNECTING: 436 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 437 log("current device tries to connect back"); 438 // TODO(BT) ignore or reject 439 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 440 // The stack is connecting to target device or 441 // there is an incoming connection from the target device at the same time 442 // we already broadcasted the intent, doing nothing here 443 log("Stack and target device are connecting"); 444 } 445 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 446 loge("Another connecting event on the incoming device"); 447 } else { 448 // We get an incoming connecting request while Pending 449 // TODO(BT) is stack handing this case? let's ignore it for now 450 log("Incoming connection while pending, accept it"); 451 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 452 BluetoothProfile.STATE_DISCONNECTED); 453 mIncomingDevice = device; 454 } 455 break; 456 case CONNECTION_STATE_DISCONNECTING: 457 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 458 // we already broadcasted the intent, doing nothing here 459 if (DBG) { 460 log("stack is disconnecting mCurrentDevice"); 461 } 462 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 463 loge("TargetDevice is getting disconnected"); 464 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 465 loge("IncomingDevice is getting disconnected"); 466 } else { 467 loge("Disconnecting unknow device: " + device); 468 } 469 break; 470 default: 471 loge("Incorrect state: " + state); 472 break; 473 } 474 } 475 476 } 477 478 private class Connected extends State { 479 @Override 480 public void enter() { 481 // Remove pending connection attempts that were deferred during the pending 482 // state. This is to prevent auto connect attempts from disconnecting 483 // devices that previously successfully connected. 484 // TODO: This needs to check for multiple A2DP connections, once supported... 485 removeDeferredMessages(CONNECT); 486 487 log("Enter Connected: " + getCurrentMessage().what); 488 // Upon connected, the audio starts out as stopped 489 broadcastAudioState(mCurrentDevice, BluetoothA2dp.STATE_NOT_PLAYING, 490 BluetoothA2dp.STATE_PLAYING); 491 } 492 493 @Override 494 public boolean processMessage(Message message) { 495 log("Connected process message: " + message.what); 496 if (mCurrentDevice == null) { 497 loge("ERROR: mCurrentDevice is null in Connected"); 498 return NOT_HANDLED; 499 } 500 501 boolean retValue = HANDLED; 502 switch(message.what) { 503 case CONNECT: 504 { 505 BluetoothDevice device = (BluetoothDevice) message.obj; 506 if (mCurrentDevice.equals(device)) { 507 break; 508 } 509 510 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 511 BluetoothProfile.STATE_DISCONNECTED); 512 if (!disconnectA2dpNative(getByteAddress(mCurrentDevice))) { 513 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 514 BluetoothProfile.STATE_CONNECTING); 515 break; 516 } 517 518 synchronized (A2dpStateMachine.this) { 519 mTargetDevice = device; 520 transitionTo(mPending); 521 } 522 } 523 break; 524 case DISCONNECT: 525 { 526 BluetoothDevice device = (BluetoothDevice) message.obj; 527 if (!mCurrentDevice.equals(device)) { 528 break; 529 } 530 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 531 BluetoothProfile.STATE_CONNECTED); 532 if (!disconnectA2dpNative(getByteAddress(device))) { 533 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 534 BluetoothProfile.STATE_DISCONNECTED); 535 break; 536 } 537 transitionTo(mPending); 538 } 539 break; 540 case STACK_EVENT: 541 StackEvent event = (StackEvent) message.obj; 542 switch (event.type) { 543 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 544 processConnectionEvent(event.valueInt, event.device); 545 break; 546 case EVENT_TYPE_AUDIO_STATE_CHANGED: 547 processAudioStateEvent(event.valueInt, event.device); 548 break; 549 default: 550 loge("Unexpected stack event: " + event.type); 551 break; 552 } 553 break; 554 default: 555 return NOT_HANDLED; 556 } 557 return retValue; 558 } 559 560 // in Connected state 561 private void processConnectionEvent(int state, BluetoothDevice device) { 562 switch (state) { 563 case CONNECTION_STATE_DISCONNECTED: 564 if (mCurrentDevice.equals(device)) { 565 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 566 BluetoothProfile.STATE_CONNECTED); 567 synchronized (A2dpStateMachine.this) { 568 mCurrentDevice = null; 569 transitionTo(mDisconnected); 570 } 571 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 572 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 573 BluetoothProfile.STATE_CONNECTING); 574 synchronized (A2dpStateMachine.this) { 575 mTargetDevice = null; 576 } 577 logi("Disconnected from mTargetDevice in connected state device: " + device); 578 } else { 579 loge("Disconnected from unknown device: " + device); 580 } 581 break; 582 default: 583 loge("Connection State Device: " + device + " bad state: " + state); 584 break; 585 } 586 } 587 private void processAudioStateEvent(int state, BluetoothDevice device) { 588 if (!mCurrentDevice.equals(device)) { 589 loge("Audio State Device:" + device + "is different from ConnectedDevice:" + 590 mCurrentDevice); 591 return; 592 } 593 switch (state) { 594 case AUDIO_STATE_STARTED: 595 if (mPlayingA2dpDevice == null) { 596 mPlayingA2dpDevice = device; 597 mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING); 598 broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING, 599 BluetoothA2dp.STATE_NOT_PLAYING); 600 } 601 break; 602 case AUDIO_STATE_REMOTE_SUSPEND: 603 case AUDIO_STATE_STOPPED: 604 if (mPlayingA2dpDevice != null) { 605 mPlayingA2dpDevice = null; 606 mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); 607 broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING, 608 BluetoothA2dp.STATE_PLAYING); 609 } 610 break; 611 default: 612 loge("Audio State Device: " + device + " bad state: " + state); 613 break; 614 } 615 } 616 } 617 618 int getConnectionState(BluetoothDevice device) { 619 if (getCurrentState() == mDisconnected) { 620 return BluetoothProfile.STATE_DISCONNECTED; 621 } 622 623 synchronized (this) { 624 IState currentState = getCurrentState(); 625 if (currentState == mPending) { 626 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 627 return BluetoothProfile.STATE_CONNECTING; 628 } 629 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 630 return BluetoothProfile.STATE_DISCONNECTING; 631 } 632 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 633 return BluetoothProfile.STATE_CONNECTING; // incoming connection 634 } 635 return BluetoothProfile.STATE_DISCONNECTED; 636 } 637 638 if (currentState == mConnected) { 639 if (mCurrentDevice.equals(device)) { 640 return BluetoothProfile.STATE_CONNECTED; 641 } 642 return BluetoothProfile.STATE_DISCONNECTED; 643 } else { 644 loge("Bad currentState: " + currentState); 645 return BluetoothProfile.STATE_DISCONNECTED; 646 } 647 } 648 } 649 650 List<BluetoothDevice> getConnectedDevices() { 651 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 652 synchronized (this) { 653 if (getCurrentState() == mConnected) { 654 devices.add(mCurrentDevice); 655 } 656 } 657 return devices; 658 } 659 660 boolean isPlaying(BluetoothDevice device) { 661 synchronized (this) { 662 if (device.equals(mPlayingA2dpDevice)) { 663 return true; 664 } 665 } 666 return false; 667 } 668 669 BluetoothCodecConfig getCodecConfig() { 670 synchronized (this) { 671 return mCodecConfig; 672 } 673 } 674 675 private void onCodecConfigChanged(int codecType, int codecPriority, int sampleRate, 676 int bitsPerSample, int channelMode, long codecSpecific1, long codecSpecific2, 677 long codecSpecific3, long codecSpecific4) { 678 BluetoothCodecConfig prevCodecConfig; 679 BluetoothCodecConfig newCodecConfig = new BluetoothCodecConfig(codecType, codecPriority, 680 sampleRate, bitsPerSample, channelMode, codecSpecific1, codecSpecific2, 681 codecSpecific3, codecSpecific4); 682 synchronized (this) { 683 prevCodecConfig = mCodecConfig; 684 mCodecConfig = newCodecConfig; 685 } 686 687 Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED); 688 intent.putExtra(BluetoothCodecConfig.EXTRA_CODEC_CONFIG, newCodecConfig); 689 intent.putExtra(BluetoothCodecConfig.EXTRA_PREVIOUS_CODEC_CONFIG, prevCodecConfig); 690 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 691 692 log("A2DP Codec Config : " + prevCodecConfig + "->" + newCodecConfig); 693 694 // Inform the Audio Service about the codec configuration change, 695 // so the Audio Service can reset accordingly the audio feeding 696 // parameters in the Audio HAL to the Bluetooth stack. 697 if (!newCodecConfig.sameAudioFeedingParameters(prevCodecConfig) && (mCurrentDevice != null) 698 && (getCurrentState() == mConnected)) { 699 // Add the device only if it is currently connected 700 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mCurrentDevice); 701 mAudioManager.handleBluetoothA2dpDeviceConfigChange(mCurrentDevice); 702 } 703 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 704 } 705 706 void setCodecConfigPreference(BluetoothCodecConfig codecConfig) { 707 setCodecConfigPreferenceNative(codecConfig.getCodecType(), codecConfig.getCodecPriority(), 708 codecConfig.getSampleRate(), codecConfig.getBitsPerSample(), 709 codecConfig.getChannelMode(), codecConfig.getCodecSpecific1(), 710 codecConfig.getCodecSpecific2(), codecConfig.getCodecSpecific3(), 711 codecConfig.getCodecSpecific4()); 712 } 713 714 boolean okToConnect(BluetoothDevice device) { 715 AdapterService adapterService = AdapterService.getAdapterService(); 716 int priority = mService.getPriority(device); 717 boolean ret = false; 718 //check if this is an incoming connection in Quiet mode. 719 if((adapterService == null) || 720 ((adapterService.isQuietModeEnabled() == true) && 721 (mTargetDevice == null))){ 722 ret = false; 723 } 724 // check priority and accept or reject the connection. if priority is undefined 725 // it is likely that our SDP has not completed and peer is initiating the 726 // connection. Allow this connection, provided the device is bonded 727 else if((BluetoothProfile.PRIORITY_OFF < priority) || 728 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 729 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 730 ret= true; 731 } 732 return ret; 733 } 734 735 synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 736 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 737 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 738 int connectionState; 739 740 for (BluetoothDevice device : bondedDevices) { 741 ParcelUuid[] featureUuids = device.getUuids(); 742 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSink)) { 743 continue; 744 } 745 connectionState = getConnectionState(device); 746 for(int i = 0; i < states.length; i++) { 747 if (connectionState == states[i]) { 748 deviceList.add(device); 749 } 750 } 751 } 752 return deviceList; 753 } 754 755 756 // This method does not check for error conditon (newState == prevState) 757 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 758 759 int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, newState, 760 BluetoothProfile.A2DP); 761 762 mWakeLock.acquire(); 763 mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage( 764 MSG_CONNECTION_STATE_CHANGED, 765 prevState, 766 newState, 767 device), 768 delay); 769 } 770 771 private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { 772 Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED); 773 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 774 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 775 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 776 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 777 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 778 779 log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); 780 } 781 782 private byte[] getByteAddress(BluetoothDevice device) { 783 return Utils.getBytesFromAddress(device.getAddress()); 784 } 785 786 private void onConnectionStateChanged(int state, byte[] address) { 787 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 788 event.valueInt = state; 789 event.device = getDevice(address); 790 sendMessage(STACK_EVENT, event); 791 } 792 793 private void onAudioStateChanged(int state, byte[] address) { 794 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 795 event.valueInt = state; 796 event.device = getDevice(address); 797 sendMessage(STACK_EVENT, event); 798 } 799 private BluetoothDevice getDevice(byte[] address) { 800 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 801 } 802 803 private class StackEvent { 804 int type = EVENT_TYPE_NONE; 805 int valueInt = 0; 806 BluetoothDevice device = null; 807 808 private StackEvent(int type) { 809 this.type = type; 810 } 811 } 812 /** Handles A2DP connection state change intent broadcasts. */ 813 private class IntentBroadcastHandler extends Handler { 814 815 private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { 816 Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 817 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 818 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 819 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 820 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 821 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 822 log("Connection state " + device + ": " + prevState + "->" + state); 823 mService.notifyProfileConnectionStateChanged(device, BluetoothProfile.A2DP, state, prevState); 824 } 825 826 @Override 827 public void handleMessage(Message msg) { 828 switch (msg.what) { 829 case MSG_CONNECTION_STATE_CHANGED: 830 onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2); 831 mWakeLock.release(); 832 break; 833 } 834 } 835 } 836 837 public void dump(StringBuilder sb) { 838 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 839 ProfileService.println(sb, "mTargetDevice: " + mTargetDevice); 840 ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice); 841 ProfileService.println(sb, "mPlayingA2dpDevice: " + mPlayingA2dpDevice); 842 ProfileService.println(sb, "StateMachine: " + this.toString()); 843 } 844 845 // Event types for STACK_EVENT message 846 final private static int EVENT_TYPE_NONE = 0; 847 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 848 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 849 850 // Do not modify without updating the HAL bt_av.h files. 851 852 // match up with btav_connection_state_t enum of bt_av.h 853 final static int CONNECTION_STATE_DISCONNECTED = 0; 854 final static int CONNECTION_STATE_CONNECTING = 1; 855 final static int CONNECTION_STATE_CONNECTED = 2; 856 final static int CONNECTION_STATE_DISCONNECTING = 3; 857 858 // match up with btav_audio_state_t enum of bt_av.h 859 final static int AUDIO_STATE_REMOTE_SUSPEND = 0; 860 final static int AUDIO_STATE_STOPPED = 1; 861 final static int AUDIO_STATE_STARTED = 2; 862 863 private native static void classInitNative(); 864 private native void initNative(); 865 private native void cleanupNative(); 866 private native boolean connectA2dpNative(byte[] address); 867 private native boolean disconnectA2dpNative(byte[] address); 868 private native boolean setCodecConfigPreferenceNative(int codecType, int codecPriority, 869 int sampleRate, int bitsPerSample, int channelMode, long codecSpecific1, 870 long codecSpecific2, long codecSpecific3, long codecSpecific4); 871} 872