A2dpStateMachine.java revision 8bc413b0b749ea9df59e858493273e05087fe887
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.BluetoothCodecStatus; 35import android.bluetooth.BluetoothDevice; 36import android.bluetooth.BluetoothProfile; 37import android.bluetooth.BluetoothUuid; 38import android.content.Context; 39import android.content.Intent; 40import android.content.res.Resources; 41import android.content.res.Resources.NotFoundException; 42import android.media.AudioManager; 43import android.os.Handler; 44import android.os.Message; 45import android.os.ParcelUuid; 46import android.os.PowerManager; 47import android.os.PowerManager.WakeLock; 48import android.util.Log; 49 50import com.android.bluetooth.R; 51import com.android.bluetooth.Utils; 52import com.android.bluetooth.btservice.AdapterService; 53import com.android.bluetooth.btservice.ProfileService; 54import com.android.internal.util.IState; 55import com.android.internal.util.State; 56import com.android.internal.util.StateMachine; 57 58import java.util.ArrayList; 59import java.util.List; 60import java.util.Set; 61 62final class A2dpStateMachine extends StateMachine { 63 private static final boolean DBG = false; 64 private static final String TAG = "A2dpStateMachine"; 65 66 static final int CONNECT = 1; 67 static final int DISCONNECT = 2; 68 private static final int STACK_EVENT = 101; 69 private static final int CONNECT_TIMEOUT = 201; 70 71 private Disconnected mDisconnected; 72 private Pending mPending; 73 private Connected mConnected; 74 75 private A2dpService mService; 76 private Context mContext; 77 private BluetoothAdapter mAdapter; 78 private final AudioManager mAudioManager; 79 private IntentBroadcastHandler mIntentBroadcastHandler; 80 private final WakeLock mWakeLock; 81 private BluetoothCodecConfig[] mCodecConfigPriorities; 82 83 private static final int MSG_CONNECTION_STATE_CHANGED = 0; 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 private BluetoothCodecStatus mCodecStatus = null; 113 private int mA2dpSourceCodecPrioritySbc = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 114 private int mA2dpSourceCodecPriorityAac = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 115 private int mA2dpSourceCodecPriorityAptx = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 116 private int mA2dpSourceCodecPriorityAptxHd = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 117 private int mA2dpSourceCodecPriorityLdac = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 118 119 static { 120 classInitNative(); 121 } 122 123 private A2dpStateMachine(A2dpService svc, Context context) { 124 super("A2dpStateMachine"); 125 mService = svc; 126 mContext = context; 127 mAdapter = BluetoothAdapter.getDefaultAdapter(); 128 mCodecConfigPriorities = assignCodecConfigPriorities(); 129 130 initNative(mCodecConfigPriorities); 131 132 mDisconnected = new Disconnected(); 133 mPending = new Pending(); 134 mConnected = new Connected(); 135 136 addState(mDisconnected); 137 addState(mPending); 138 addState(mConnected); 139 140 setInitialState(mDisconnected); 141 142 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 143 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService"); 144 145 mIntentBroadcastHandler = new IntentBroadcastHandler(); 146 147 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 148 } 149 150 static A2dpStateMachine make(A2dpService svc, Context context) { 151 Log.d(TAG, "make"); 152 A2dpStateMachine a2dpSm = new A2dpStateMachine(svc, context); 153 a2dpSm.start(); 154 return a2dpSm; 155 } 156 157 // Assign the A2DP Source codec config priorities 158 private BluetoothCodecConfig[] assignCodecConfigPriorities() { 159 Resources resources = mContext.getResources(); 160 if (resources == null) { 161 return null; 162 } 163 164 int value; 165 try { 166 value = resources.getInteger(R.integer.a2dp_source_codec_priority_sbc); 167 } catch (NotFoundException e) { 168 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 169 } 170 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) 171 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 172 mA2dpSourceCodecPrioritySbc = value; 173 } 174 175 try { 176 value = resources.getInteger(R.integer.a2dp_source_codec_priority_aac); 177 } catch (NotFoundException e) { 178 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 179 } 180 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) 181 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 182 mA2dpSourceCodecPriorityAac = value; 183 } 184 185 try { 186 value = resources.getInteger(R.integer.a2dp_source_codec_priority_aptx); 187 } catch (NotFoundException e) { 188 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 189 } 190 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) 191 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 192 mA2dpSourceCodecPriorityAptx = value; 193 } 194 195 try { 196 value = resources.getInteger(R.integer.a2dp_source_codec_priority_aptx_hd); 197 } catch (NotFoundException e) { 198 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 199 } 200 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) 201 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 202 mA2dpSourceCodecPriorityAptxHd = value; 203 } 204 205 try { 206 value = resources.getInteger(R.integer.a2dp_source_codec_priority_ldac); 207 } catch (NotFoundException e) { 208 value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT; 209 } 210 if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED) 211 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) { 212 mA2dpSourceCodecPriorityLdac = value; 213 } 214 215 BluetoothCodecConfig codecConfig; 216 BluetoothCodecConfig[] codecConfigArray = 217 new BluetoothCodecConfig[BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX]; 218 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC, 219 mA2dpSourceCodecPrioritySbc, BluetoothCodecConfig.SAMPLE_RATE_NONE, 220 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE, 221 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 222 0 /* codecSpecific4 */); 223 codecConfigArray[0] = codecConfig; 224 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC, 225 mA2dpSourceCodecPriorityAac, BluetoothCodecConfig.SAMPLE_RATE_NONE, 226 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE, 227 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 228 0 /* codecSpecific4 */); 229 codecConfigArray[1] = codecConfig; 230 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX, 231 mA2dpSourceCodecPriorityAptx, BluetoothCodecConfig.SAMPLE_RATE_NONE, 232 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE, 233 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 234 0 /* codecSpecific4 */); 235 codecConfigArray[2] = codecConfig; 236 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD, 237 mA2dpSourceCodecPriorityAptxHd, BluetoothCodecConfig.SAMPLE_RATE_NONE, 238 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE, 239 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 240 0 /* codecSpecific4 */); 241 codecConfigArray[3] = codecConfig; 242 codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC, 243 mA2dpSourceCodecPriorityLdac, BluetoothCodecConfig.SAMPLE_RATE_NONE, 244 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE, 245 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */, 246 0 /* codecSpecific4 */); 247 codecConfigArray[4] = codecConfig; 248 249 return codecConfigArray; 250 } 251 252 public void doQuit() { 253 quitNow(); 254 } 255 256 public void cleanup() { 257 cleanupNative(); 258 } 259 260 private class Disconnected extends State { 261 @Override 262 public void enter() { 263 log("Enter Disconnected: " + getCurrentMessage().what); 264 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 265 loge("ERROR: enter() inconsistent state in Disconnected: current = " 266 + mCurrentDevice + " target = " + mTargetDevice + " incoming = " 267 + mIncomingDevice); 268 } 269 // Remove Timeout msg when moved to stable state 270 removeMessages(CONNECT_TIMEOUT); 271 } 272 273 @Override 274 public boolean processMessage(Message message) { 275 log("Disconnected process message: " + message.what); 276 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 277 loge("ERROR: not null state in Disconnected: current = " + mCurrentDevice 278 + " target = " + mTargetDevice + " incoming = " + mIncomingDevice); 279 mCurrentDevice = null; 280 mTargetDevice = null; 281 mIncomingDevice = null; 282 } 283 284 boolean retValue = HANDLED; 285 switch(message.what) { 286 case CONNECT: 287 BluetoothDevice device = (BluetoothDevice) message.obj; 288 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 289 BluetoothProfile.STATE_DISCONNECTED); 290 291 if (!connectA2dpNative(getByteAddress(device)) ) { 292 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 293 BluetoothProfile.STATE_CONNECTING); 294 break; 295 } 296 297 synchronized (A2dpStateMachine.this) { 298 mTargetDevice = device; 299 transitionTo(mPending); 300 } 301 // TODO(BT) remove CONNECT_TIMEOUT when the stack 302 // sends back events consistently 303 sendMessageDelayed(CONNECT_TIMEOUT, 30000); 304 break; 305 case DISCONNECT: 306 // ignore 307 break; 308 case STACK_EVENT: 309 StackEvent event = (StackEvent) message.obj; 310 switch (event.type) { 311 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 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 @Override 326 public void exit() { 327 log("Exit Disconnected: " + getCurrentMessage().what); 328 } 329 330 // in Disconnected state 331 private void processConnectionEvent(int state, BluetoothDevice device) { 332 switch (state) { 333 case CONNECTION_STATE_DISCONNECTED: 334 logw("Ignore HF DISCONNECTED event, device: " + device); 335 break; 336 case CONNECTION_STATE_CONNECTING: 337 if (okToConnect(device)){ 338 logi("Incoming A2DP accepted"); 339 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 340 BluetoothProfile.STATE_DISCONNECTED); 341 synchronized (A2dpStateMachine.this) { 342 mIncomingDevice = device; 343 transitionTo(mPending); 344 } 345 } else { 346 //reject the connection and stay in Disconnected state itself 347 logi("Incoming A2DP rejected"); 348 disconnectA2dpNative(getByteAddress(device)); 349 } 350 break; 351 case CONNECTION_STATE_CONNECTED: 352 logw("A2DP Connected from Disconnected state"); 353 if (okToConnect(device)){ 354 logi("Incoming A2DP accepted"); 355 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 356 BluetoothProfile.STATE_DISCONNECTED); 357 synchronized (A2dpStateMachine.this) { 358 mCurrentDevice = device; 359 transitionTo(mConnected); 360 } 361 } else { 362 //reject the connection and stay in Disconnected state itself 363 logi("Incoming A2DP rejected"); 364 disconnectA2dpNative(getByteAddress(device)); 365 } 366 break; 367 case CONNECTION_STATE_DISCONNECTING: 368 logw("Ignore A2dp DISCONNECTING event, device: " + device); 369 break; 370 default: 371 loge("Incorrect state: " + state); 372 break; 373 } 374 } 375 } 376 377 private class Pending extends State { 378 @Override 379 public void enter() { 380 log("Enter Pending: " + getCurrentMessage().what); 381 if (mTargetDevice != null && mIncomingDevice != null) { 382 loge("ERROR: enter() inconsistent state in Pending: current = " + mCurrentDevice 383 + " target = " + mTargetDevice + " incoming = " + mIncomingDevice); 384 } 385 } 386 387 @Override 388 public boolean processMessage(Message message) { 389 log("Pending process message: " + message.what); 390 391 boolean retValue = HANDLED; 392 switch(message.what) { 393 case CONNECT: 394 deferMessage(message); 395 break; 396 case CONNECT_TIMEOUT: 397 disconnectA2dpNative(getByteAddress(mTargetDevice)); 398 onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED, 399 getByteAddress(mTargetDevice)); 400 break; 401 case DISCONNECT: 402 BluetoothDevice device = (BluetoothDevice) message.obj; 403 if (mCurrentDevice != null && mTargetDevice != null && 404 mTargetDevice.equals(device) ) { 405 // cancel connection to the mTargetDevice 406 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 407 BluetoothProfile.STATE_CONNECTING); 408 synchronized (A2dpStateMachine.this) { 409 mTargetDevice = null; 410 } 411 } else { 412 deferMessage(message); 413 } 414 break; 415 case STACK_EVENT: 416 StackEvent event = (StackEvent) message.obj; 417 switch (event.type) { 418 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 419 processConnectionEvent(event.valueInt, event.device); 420 break; 421 default: 422 loge("Unexpected stack event: " + event.type); 423 break; 424 } 425 break; 426 default: 427 return NOT_HANDLED; 428 } 429 return retValue; 430 } 431 432 // in Pending state 433 private void processConnectionEvent(int state, BluetoothDevice device) { 434 switch (state) { 435 case CONNECTION_STATE_DISCONNECTED: 436 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 437 broadcastConnectionState(mCurrentDevice, 438 BluetoothProfile.STATE_DISCONNECTED, 439 BluetoothProfile.STATE_DISCONNECTING); 440 synchronized (A2dpStateMachine.this) { 441 mCurrentDevice = null; 442 } 443 444 if (mTargetDevice != null) { 445 if (!connectA2dpNative(getByteAddress(mTargetDevice))) { 446 broadcastConnectionState(mTargetDevice, 447 BluetoothProfile.STATE_DISCONNECTED, 448 BluetoothProfile.STATE_CONNECTING); 449 synchronized (A2dpStateMachine.this) { 450 mTargetDevice = null; 451 transitionTo(mDisconnected); 452 } 453 } 454 } else { 455 synchronized (A2dpStateMachine.this) { 456 mIncomingDevice = null; 457 transitionTo(mDisconnected); 458 } 459 } 460 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 461 // outgoing connection failed 462 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 463 BluetoothProfile.STATE_CONNECTING); 464 // check if there is some incoming connection request 465 if (mIncomingDevice != null) { 466 logi("disconnect for outgoing in pending state"); 467 synchronized (A2dpStateMachine.this) { 468 mTargetDevice = null; 469 } 470 break; 471 } 472 synchronized (A2dpStateMachine.this) { 473 mTargetDevice = null; 474 transitionTo(mDisconnected); 475 } 476 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 477 broadcastConnectionState(mIncomingDevice, 478 BluetoothProfile.STATE_DISCONNECTED, 479 BluetoothProfile.STATE_CONNECTING); 480 synchronized (A2dpStateMachine.this) { 481 mIncomingDevice = null; 482 transitionTo(mDisconnected); 483 } 484 } else { 485 loge("Unknown device Disconnected: " + device); 486 } 487 break; 488 case CONNECTION_STATE_CONNECTED: 489 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 490 // disconnection failed 491 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 492 BluetoothProfile.STATE_DISCONNECTING); 493 if (mTargetDevice != null) { 494 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 495 BluetoothProfile.STATE_CONNECTING); 496 } 497 synchronized (A2dpStateMachine.this) { 498 mTargetDevice = null; 499 transitionTo(mConnected); 500 } 501 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 502 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 503 BluetoothProfile.STATE_CONNECTING); 504 synchronized (A2dpStateMachine.this) { 505 mCurrentDevice = mTargetDevice; 506 mTargetDevice = null; 507 transitionTo(mConnected); 508 } 509 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 510 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 511 BluetoothProfile.STATE_CONNECTING); 512 // check for a2dp connection allowed for this device in race condition 513 if (okToConnect(mIncomingDevice)) { 514 logi("Ready to connect incoming Connection from pending state"); 515 synchronized (A2dpStateMachine.this) { 516 mCurrentDevice = mIncomingDevice; 517 mIncomingDevice = null; 518 transitionTo(mConnected); 519 } 520 } else { 521 // A2dp connection unchecked for this device 522 loge("Incoming A2DP rejected from pending state"); 523 disconnectA2dpNative(getByteAddress(device)); 524 } 525 } else { 526 loge("Unknown device Connected: " + device); 527 // something is wrong here, but sync our state with stack 528 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 529 BluetoothProfile.STATE_DISCONNECTED); 530 synchronized (A2dpStateMachine.this) { 531 mCurrentDevice = device; 532 mTargetDevice = null; 533 mIncomingDevice = null; 534 transitionTo(mConnected); 535 } 536 } 537 break; 538 case CONNECTION_STATE_CONNECTING: 539 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 540 log("current device tries to connect back"); 541 // TODO(BT) ignore or reject 542 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 543 // The stack is connecting to target device or 544 // there is an incoming connection from the target device at the same time 545 // we already broadcasted the intent, doing nothing here 546 log("Stack and target device are connecting"); 547 } 548 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 549 loge("Another connecting event on the incoming device"); 550 } else { 551 // We get an incoming connecting request while Pending 552 // TODO(BT) is stack handing this case? let's ignore it for now 553 log("Incoming connection while pending, accept it"); 554 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 555 BluetoothProfile.STATE_DISCONNECTED); 556 mIncomingDevice = device; 557 } 558 break; 559 case CONNECTION_STATE_DISCONNECTING: 560 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 561 // we already broadcasted the intent, doing nothing here 562 if (DBG) { 563 log("stack is disconnecting mCurrentDevice"); 564 } 565 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 566 loge("TargetDevice is getting disconnected"); 567 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 568 loge("IncomingDevice is getting disconnected"); 569 } else { 570 loge("Disconnecting unknow device: " + device); 571 } 572 break; 573 default: 574 loge("Incorrect state: " + state); 575 break; 576 } 577 } 578 579 } 580 581 private class Connected extends State { 582 @Override 583 public void enter() { 584 // Remove pending connection attempts that were deferred during the pending 585 // state. This is to prevent auto connect attempts from disconnecting 586 // devices that previously successfully connected. 587 // TODO: This needs to check for multiple A2DP connections, once supported... 588 removeDeferredMessages(CONNECT); 589 590 log("Enter Connected: " + getCurrentMessage().what); 591 if (mTargetDevice != null || mIncomingDevice != null) { 592 loge("ERROR: enter() inconsistent state in Connected: current = " + mCurrentDevice 593 + " target = " + mTargetDevice + " incoming = " + mIncomingDevice); 594 } 595 596 // remove timeout for connected device only. 597 if (mTargetDevice == null) { 598 removeMessages(CONNECT_TIMEOUT); 599 } 600 // Upon connected, the audio starts out as stopped 601 broadcastAudioState(mCurrentDevice, BluetoothA2dp.STATE_NOT_PLAYING, 602 BluetoothA2dp.STATE_PLAYING); 603 } 604 605 @Override 606 public boolean processMessage(Message message) { 607 log("Connected process message: " + message.what); 608 if (mCurrentDevice == null) { 609 loge("ERROR: mCurrentDevice is null in Connected"); 610 return NOT_HANDLED; 611 } 612 613 boolean retValue = HANDLED; 614 switch(message.what) { 615 case CONNECT: 616 { 617 BluetoothDevice device = (BluetoothDevice) message.obj; 618 if (mCurrentDevice.equals(device)) { 619 break; 620 } 621 622 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 623 BluetoothProfile.STATE_DISCONNECTED); 624 if (!disconnectA2dpNative(getByteAddress(mCurrentDevice))) { 625 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 626 BluetoothProfile.STATE_CONNECTING); 627 break; 628 } else { 629 broadcastConnectionState(mCurrentDevice, 630 BluetoothProfile.STATE_DISCONNECTING, 631 BluetoothProfile.STATE_CONNECTED); 632 } 633 634 synchronized (A2dpStateMachine.this) { 635 mTargetDevice = device; 636 transitionTo(mPending); 637 } 638 } 639 break; 640 case DISCONNECT: 641 { 642 BluetoothDevice device = (BluetoothDevice) message.obj; 643 if (!mCurrentDevice.equals(device)) { 644 break; 645 } 646 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 647 BluetoothProfile.STATE_CONNECTED); 648 if (!disconnectA2dpNative(getByteAddress(device))) { 649 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 650 BluetoothProfile.STATE_DISCONNECTING); 651 break; 652 } 653 synchronized (A2dpStateMachine.this) { 654 transitionTo(mPending); 655 } 656 } 657 break; 658 case CONNECT_TIMEOUT: 659 if (mTargetDevice == null) { 660 loge("CONNECT_TIMEOUT received for unknown device"); 661 } else { 662 loge("CONNECT_TIMEOUT received : connected device : " + mCurrentDevice 663 + " : timedout device : " + mTargetDevice); 664 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 665 BluetoothProfile.STATE_CONNECTING); 666 mTargetDevice = null; 667 } 668 break; 669 case STACK_EVENT: 670 StackEvent event = (StackEvent) message.obj; 671 switch (event.type) { 672 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 673 processConnectionEvent(event.valueInt, event.device); 674 break; 675 case EVENT_TYPE_AUDIO_STATE_CHANGED: 676 processAudioStateEvent(event.valueInt, event.device); 677 break; 678 default: 679 loge("Unexpected stack event: " + event.type); 680 break; 681 } 682 break; 683 default: 684 return NOT_HANDLED; 685 } 686 return retValue; 687 } 688 689 // in Connected state 690 private void processConnectionEvent(int state, BluetoothDevice device) { 691 switch (state) { 692 case CONNECTION_STATE_DISCONNECTED: 693 if (mCurrentDevice.equals(device)) { 694 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 695 BluetoothProfile.STATE_CONNECTED); 696 synchronized (A2dpStateMachine.this) { 697 mCurrentDevice = null; 698 transitionTo(mDisconnected); 699 } 700 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 701 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 702 BluetoothProfile.STATE_CONNECTING); 703 synchronized (A2dpStateMachine.this) { 704 mTargetDevice = null; 705 } 706 logi("Disconnected from mTargetDevice in connected state device: " + device); 707 } else { 708 loge("Disconnected from unknown device: " + device); 709 } 710 break; 711 default: 712 loge("Connection State Device: " + device + " bad state: " + state); 713 break; 714 } 715 } 716 private void processAudioStateEvent(int state, BluetoothDevice device) { 717 if (!mCurrentDevice.equals(device)) { 718 loge("Audio State Device:" + device + "is different from ConnectedDevice:" + 719 mCurrentDevice); 720 return; 721 } 722 switch (state) { 723 case AUDIO_STATE_STARTED: 724 if (mPlayingA2dpDevice == null) { 725 mPlayingA2dpDevice = device; 726 mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING); 727 broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING, 728 BluetoothA2dp.STATE_NOT_PLAYING); 729 } 730 break; 731 case AUDIO_STATE_REMOTE_SUSPEND: 732 case AUDIO_STATE_STOPPED: 733 if (mPlayingA2dpDevice != null) { 734 mPlayingA2dpDevice = null; 735 mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); 736 broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING, 737 BluetoothA2dp.STATE_PLAYING); 738 } 739 break; 740 default: 741 loge("Audio State Device: " + device + " bad state: " + state); 742 break; 743 } 744 } 745 } 746 747 int getConnectionState(BluetoothDevice device) { 748 if (getCurrentState() == mDisconnected) { 749 return BluetoothProfile.STATE_DISCONNECTED; 750 } 751 752 synchronized (this) { 753 IState currentState = getCurrentState(); 754 if (currentState == mPending) { 755 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 756 return BluetoothProfile.STATE_CONNECTING; 757 } 758 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 759 return BluetoothProfile.STATE_DISCONNECTING; 760 } 761 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 762 return BluetoothProfile.STATE_CONNECTING; // incoming connection 763 } 764 return BluetoothProfile.STATE_DISCONNECTED; 765 } 766 767 if (currentState == mConnected) { 768 if (mCurrentDevice.equals(device)) { 769 return BluetoothProfile.STATE_CONNECTED; 770 } 771 return BluetoothProfile.STATE_DISCONNECTED; 772 } else { 773 loge("Bad currentState: " + currentState); 774 return BluetoothProfile.STATE_DISCONNECTED; 775 } 776 } 777 } 778 779 List<BluetoothDevice> getConnectedDevices() { 780 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 781 synchronized (this) { 782 if (getCurrentState() == mConnected) { 783 devices.add(mCurrentDevice); 784 } 785 } 786 return devices; 787 } 788 789 boolean isPlaying(BluetoothDevice device) { 790 synchronized (this) { 791 if (device.equals(mPlayingA2dpDevice)) { 792 return true; 793 } 794 } 795 return false; 796 } 797 798 BluetoothCodecStatus getCodecStatus() { 799 synchronized (this) { 800 return mCodecStatus; 801 } 802 } 803 804 private void onCodecConfigChanged(BluetoothCodecConfig newCodecConfig, 805 BluetoothCodecConfig[] codecsLocalCapabilities, 806 BluetoothCodecConfig[] codecsSelectableCapabilities) { 807 BluetoothCodecConfig prevCodecConfig = null; 808 synchronized (this) { 809 if (mCodecStatus != null) { 810 prevCodecConfig = mCodecStatus.getCodecConfig(); 811 } 812 mCodecStatus = new BluetoothCodecStatus( 813 newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities); 814 } 815 816 Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED); 817 intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, mCodecStatus); 818 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 819 820 log("A2DP Codec Config: " + prevCodecConfig + "->" + newCodecConfig); 821 for (BluetoothCodecConfig codecConfig : codecsLocalCapabilities) { 822 log("A2DP Codec Local Capability: " + codecConfig); 823 } 824 for (BluetoothCodecConfig codecConfig : codecsSelectableCapabilities) { 825 log("A2DP Codec Selectable Capability: " + codecConfig); 826 } 827 828 // Inform the Audio Service about the codec configuration change, 829 // so the Audio Service can reset accordingly the audio feeding 830 // parameters in the Audio HAL to the Bluetooth stack. 831 if (!newCodecConfig.sameAudioFeedingParameters(prevCodecConfig) && (mCurrentDevice != null) 832 && (getCurrentState() == mConnected)) { 833 // Add the device only if it is currently connected 834 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mCurrentDevice); 835 mAudioManager.handleBluetoothA2dpDeviceConfigChange(mCurrentDevice); 836 } 837 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 838 } 839 840 void setCodecConfigPreference(BluetoothCodecConfig codecConfig) { 841 BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1]; 842 codecConfigArray[0] = codecConfig; 843 setCodecConfigPreferenceNative(codecConfigArray); 844 } 845 846 void enableOptionalCodecs() { 847 BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities(); 848 if (codecConfigArray == null) { 849 return; 850 } 851 852 // Set the mandatory codec's priority to default, and remove the rest 853 for (int i = 0; i < codecConfigArray.length; i++) { 854 BluetoothCodecConfig codecConfig = codecConfigArray[i]; 855 if (!codecConfig.isMandatoryCodec()) { 856 codecConfigArray[i] = null; 857 } 858 } 859 860 setCodecConfigPreferenceNative(codecConfigArray); 861 } 862 863 void disableOptionalCodecs() { 864 BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities(); 865 if (codecConfigArray == null) { 866 return; 867 } 868 // Set the mandatory codec's priority to highest, and ignore the rest 869 for (int i = 0; i < codecConfigArray.length; i++) { 870 BluetoothCodecConfig codecConfig = codecConfigArray[i]; 871 if (codecConfig.isMandatoryCodec()) { 872 codecConfig.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST); 873 } else { 874 codecConfigArray[i] = null; 875 } 876 } 877 setCodecConfigPreferenceNative(codecConfigArray); 878 } 879 880 boolean okToConnect(BluetoothDevice device) { 881 AdapterService adapterService = AdapterService.getAdapterService(); 882 int priority = mService.getPriority(device); 883 boolean ret = false; 884 //check if this is an incoming connection in Quiet mode. 885 if((adapterService == null) || 886 ((adapterService.isQuietModeEnabled()) && 887 (mTargetDevice == null))){ 888 ret = false; 889 } 890 // check priority and accept or reject the connection. if priority is undefined 891 // it is likely that our SDP has not completed and peer is initiating the 892 // connection. Allow this connection, provided the device is bonded 893 else if((BluetoothProfile.PRIORITY_OFF < priority) || 894 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 895 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 896 ret= true; 897 } 898 return ret; 899 } 900 901 synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 902 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 903 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 904 int connectionState; 905 906 for (BluetoothDevice device : bondedDevices) { 907 ParcelUuid[] featureUuids = device.getUuids(); 908 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSink)) { 909 continue; 910 } 911 connectionState = getConnectionState(device); 912 for(int i = 0; i < states.length; i++) { 913 if (connectionState == states[i]) { 914 deviceList.add(device); 915 } 916 } 917 } 918 return deviceList; 919 } 920 921 922 // This method does not check for error conditon (newState == prevState) 923 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 924 mAudioManager.setBluetoothA2dpDeviceConnectionState( 925 device, newState, BluetoothProfile.A2DP); 926 927 mWakeLock.acquire(); 928 mIntentBroadcastHandler.sendMessage(mIntentBroadcastHandler.obtainMessage( 929 MSG_CONNECTION_STATE_CHANGED, prevState, newState, device)); 930 } 931 932 private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { 933 Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED); 934 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 935 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 936 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 937 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 938 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 939 940 log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); 941 } 942 943 private byte[] getByteAddress(BluetoothDevice device) { 944 return Utils.getBytesFromAddress(device.getAddress()); 945 } 946 947 private void onConnectionStateChanged(int state, byte[] address) { 948 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 949 event.valueInt = state; 950 event.device = getDevice(address); 951 sendMessage(STACK_EVENT, event); 952 } 953 954 private void onAudioStateChanged(int state, byte[] address) { 955 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 956 event.valueInt = state; 957 event.device = getDevice(address); 958 sendMessage(STACK_EVENT, event); 959 } 960 private BluetoothDevice getDevice(byte[] address) { 961 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 962 } 963 964 private class StackEvent { 965 public int type = EVENT_TYPE_NONE; 966 public int valueInt = 0; 967 public BluetoothDevice device = null; 968 969 private StackEvent(int type) { 970 this.type = type; 971 } 972 } 973 /** Handles A2DP connection state change intent broadcasts. */ 974 private class IntentBroadcastHandler extends Handler { 975 976 private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { 977 Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 978 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 979 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 980 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 981 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 982 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 983 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 984 log("Connection state " + device + ": " + prevState + "->" + state); 985 } 986 987 @Override 988 public void handleMessage(Message msg) { 989 switch (msg.what) { 990 case MSG_CONNECTION_STATE_CHANGED: 991 onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2); 992 mWakeLock.release(); 993 break; 994 } 995 } 996 } 997 998 public void dump(StringBuilder sb) { 999 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 1000 ProfileService.println(sb, "mTargetDevice: " + mTargetDevice); 1001 ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice); 1002 ProfileService.println(sb, "mPlayingA2dpDevice: " + mPlayingA2dpDevice); 1003 ProfileService.println(sb, "StateMachine: " + this.toString()); 1004 } 1005 1006 // Event types for STACK_EVENT message 1007 private static final int EVENT_TYPE_NONE = 0; 1008 private static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 1009 private static final int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 1010 1011 // Do not modify without updating the HAL bt_av.h files. 1012 1013 // match up with btav_connection_state_t enum of bt_av.h 1014 static final int CONNECTION_STATE_DISCONNECTED = 0; 1015 static final int CONNECTION_STATE_CONNECTING = 1; 1016 static final int CONNECTION_STATE_CONNECTED = 2; 1017 static final int CONNECTION_STATE_DISCONNECTING = 3; 1018 1019 // match up with btav_audio_state_t enum of bt_av.h 1020 static final int AUDIO_STATE_REMOTE_SUSPEND = 0; 1021 static final int AUDIO_STATE_STOPPED = 1; 1022 static final int AUDIO_STATE_STARTED = 2; 1023 1024 private static native void classInitNative(); 1025 private native void initNative(BluetoothCodecConfig[] codecConfigPriorites); 1026 private native void cleanupNative(); 1027 private native boolean connectA2dpNative(byte[] address); 1028 private native boolean disconnectA2dpNative(byte[] address); 1029 private native boolean setCodecConfigPreferenceNative(BluetoothCodecConfig[] codecConfigArray); 1030} 1031