A2dpStateMachine.java revision 7aec8406176f8cef0b608c22b6e631df71ccd363
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 if ((mTargetDevice != null) && 254 (getConnectionState(mTargetDevice) == BluetoothProfile.STATE_CONNECTING)) { 255 log("doQuit()- Move A2DP State to DISCONNECTED"); 256 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 257 BluetoothProfile.STATE_CONNECTING); 258 } 259 quitNow(); 260 } 261 262 public void cleanup() { 263 cleanupNative(); 264 } 265 266 private class Disconnected extends State { 267 @Override 268 public void enter() { 269 log("Enter Disconnected: " + getCurrentMessage().what); 270 } 271 272 @Override 273 public boolean processMessage(Message message) { 274 log("Disconnected process message: " + message.what); 275 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 276 loge("ERROR: current, target, or mIncomingDevice not null in Disconnected"); 277 return NOT_HANDLED; 278 } 279 280 boolean retValue = HANDLED; 281 switch(message.what) { 282 case CONNECT: 283 BluetoothDevice device = (BluetoothDevice) message.obj; 284 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 285 BluetoothProfile.STATE_DISCONNECTED); 286 287 if (!connectA2dpNative(getByteAddress(device)) ) { 288 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 289 BluetoothProfile.STATE_CONNECTING); 290 break; 291 } 292 293 synchronized (A2dpStateMachine.this) { 294 mTargetDevice = device; 295 transitionTo(mPending); 296 } 297 // TODO(BT) remove CONNECT_TIMEOUT when the stack 298 // sends back events consistently 299 sendMessageDelayed(CONNECT_TIMEOUT, 30000); 300 break; 301 case DISCONNECT: 302 // ignore 303 break; 304 case STACK_EVENT: 305 StackEvent event = (StackEvent) message.obj; 306 switch (event.type) { 307 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 308 processConnectionEvent(event.valueInt, event.device); 309 break; 310 default: 311 loge("Unexpected stack event: " + event.type); 312 break; 313 } 314 break; 315 default: 316 return NOT_HANDLED; 317 } 318 return retValue; 319 } 320 321 @Override 322 public void exit() { 323 log("Exit Disconnected: " + getCurrentMessage().what); 324 } 325 326 // in Disconnected state 327 private void processConnectionEvent(int state, BluetoothDevice device) { 328 switch (state) { 329 case CONNECTION_STATE_DISCONNECTED: 330 logw("Ignore HF DISCONNECTED event, device: " + device); 331 break; 332 case CONNECTION_STATE_CONNECTING: 333 if (okToConnect(device)){ 334 logi("Incoming A2DP accepted"); 335 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 336 BluetoothProfile.STATE_DISCONNECTED); 337 synchronized (A2dpStateMachine.this) { 338 mIncomingDevice = device; 339 transitionTo(mPending); 340 } 341 } else { 342 //reject the connection and stay in Disconnected state itself 343 logi("Incoming A2DP rejected"); 344 disconnectA2dpNative(getByteAddress(device)); 345 } 346 break; 347 case CONNECTION_STATE_CONNECTED: 348 logw("A2DP Connected from Disconnected state"); 349 if (okToConnect(device)){ 350 logi("Incoming A2DP accepted"); 351 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 352 BluetoothProfile.STATE_DISCONNECTED); 353 synchronized (A2dpStateMachine.this) { 354 mCurrentDevice = device; 355 transitionTo(mConnected); 356 } 357 } else { 358 //reject the connection and stay in Disconnected state itself 359 logi("Incoming A2DP rejected"); 360 disconnectA2dpNative(getByteAddress(device)); 361 } 362 break; 363 case CONNECTION_STATE_DISCONNECTING: 364 logw("Ignore A2dp DISCONNECTING event, device: " + device); 365 break; 366 default: 367 loge("Incorrect state: " + state); 368 break; 369 } 370 } 371 } 372 373 private class Pending extends State { 374 @Override 375 public void enter() { 376 log("Enter Pending: " + getCurrentMessage().what); 377 } 378 379 @Override 380 public boolean processMessage(Message message) { 381 log("Pending process message: " + message.what); 382 383 boolean retValue = HANDLED; 384 switch(message.what) { 385 case CONNECT: 386 deferMessage(message); 387 break; 388 case CONNECT_TIMEOUT: 389 onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED, 390 getByteAddress(mTargetDevice)); 391 break; 392 case DISCONNECT: 393 BluetoothDevice device = (BluetoothDevice) message.obj; 394 if (mCurrentDevice != null && mTargetDevice != null && 395 mTargetDevice.equals(device) ) { 396 // cancel connection to the mTargetDevice 397 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 398 BluetoothProfile.STATE_CONNECTING); 399 synchronized (A2dpStateMachine.this) { 400 mTargetDevice = null; 401 } 402 } else { 403 deferMessage(message); 404 } 405 break; 406 case STACK_EVENT: 407 StackEvent event = (StackEvent) message.obj; 408 switch (event.type) { 409 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 410 removeMessages(CONNECT_TIMEOUT); 411 processConnectionEvent(event.valueInt, event.device); 412 break; 413 default: 414 loge("Unexpected stack event: " + event.type); 415 break; 416 } 417 break; 418 default: 419 return NOT_HANDLED; 420 } 421 return retValue; 422 } 423 424 // in Pending state 425 private void processConnectionEvent(int state, BluetoothDevice device) { 426 switch (state) { 427 case CONNECTION_STATE_DISCONNECTED: 428 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 429 broadcastConnectionState(mCurrentDevice, 430 BluetoothProfile.STATE_DISCONNECTED, 431 BluetoothProfile.STATE_DISCONNECTING); 432 synchronized (A2dpStateMachine.this) { 433 mCurrentDevice = null; 434 } 435 436 if (mTargetDevice != null) { 437 if (!connectA2dpNative(getByteAddress(mTargetDevice))) { 438 broadcastConnectionState(mTargetDevice, 439 BluetoothProfile.STATE_DISCONNECTED, 440 BluetoothProfile.STATE_CONNECTING); 441 synchronized (A2dpStateMachine.this) { 442 mTargetDevice = null; 443 transitionTo(mDisconnected); 444 } 445 } 446 } else { 447 synchronized (A2dpStateMachine.this) { 448 mIncomingDevice = null; 449 transitionTo(mDisconnected); 450 } 451 } 452 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 453 // outgoing connection failed 454 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 455 BluetoothProfile.STATE_CONNECTING); 456 // check if there is some incoming connection request 457 if (mIncomingDevice != null) { 458 logi("disconnect for outgoing in pending state"); 459 synchronized (A2dpStateMachine.this) { 460 mTargetDevice = null; 461 } 462 break; 463 } 464 synchronized (A2dpStateMachine.this) { 465 mTargetDevice = null; 466 transitionTo(mDisconnected); 467 } 468 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 469 broadcastConnectionState(mIncomingDevice, 470 BluetoothProfile.STATE_DISCONNECTED, 471 BluetoothProfile.STATE_CONNECTING); 472 synchronized (A2dpStateMachine.this) { 473 mIncomingDevice = null; 474 transitionTo(mDisconnected); 475 } 476 } else { 477 loge("Unknown device Disconnected: " + device); 478 } 479 break; 480 case CONNECTION_STATE_CONNECTED: 481 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 482 // disconnection failed 483 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 484 BluetoothProfile.STATE_DISCONNECTING); 485 if (mTargetDevice != null) { 486 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 487 BluetoothProfile.STATE_CONNECTING); 488 } 489 synchronized (A2dpStateMachine.this) { 490 mTargetDevice = null; 491 transitionTo(mConnected); 492 } 493 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 494 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 495 BluetoothProfile.STATE_CONNECTING); 496 synchronized (A2dpStateMachine.this) { 497 mCurrentDevice = mTargetDevice; 498 mTargetDevice = null; 499 transitionTo(mConnected); 500 } 501 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 502 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 503 BluetoothProfile.STATE_CONNECTING); 504 // check for a2dp connection allowed for this device in race condition 505 if (okToConnect(mIncomingDevice)) { 506 logi("Ready to connect incoming Connection from pending state"); 507 synchronized (A2dpStateMachine.this) { 508 mCurrentDevice = mIncomingDevice; 509 mIncomingDevice = null; 510 transitionTo(mConnected); 511 } 512 } else { 513 // A2dp connection unchecked for this device 514 loge("Incoming A2DP rejected from pending state"); 515 disconnectA2dpNative(getByteAddress(device)); 516 } 517 } else { 518 loge("Unknown device Connected: " + device); 519 // something is wrong here, but sync our state with stack 520 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 521 BluetoothProfile.STATE_DISCONNECTED); 522 synchronized (A2dpStateMachine.this) { 523 mCurrentDevice = device; 524 mTargetDevice = null; 525 mIncomingDevice = null; 526 transitionTo(mConnected); 527 } 528 } 529 break; 530 case CONNECTION_STATE_CONNECTING: 531 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 532 log("current device tries to connect back"); 533 // TODO(BT) ignore or reject 534 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 535 // The stack is connecting to target device or 536 // there is an incoming connection from the target device at the same time 537 // we already broadcasted the intent, doing nothing here 538 log("Stack and target device are connecting"); 539 } 540 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 541 loge("Another connecting event on the incoming device"); 542 } else { 543 // We get an incoming connecting request while Pending 544 // TODO(BT) is stack handing this case? let's ignore it for now 545 log("Incoming connection while pending, accept it"); 546 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 547 BluetoothProfile.STATE_DISCONNECTED); 548 mIncomingDevice = device; 549 } 550 break; 551 case CONNECTION_STATE_DISCONNECTING: 552 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 553 // we already broadcasted the intent, doing nothing here 554 if (DBG) { 555 log("stack is disconnecting mCurrentDevice"); 556 } 557 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 558 loge("TargetDevice is getting disconnected"); 559 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 560 loge("IncomingDevice is getting disconnected"); 561 } else { 562 loge("Disconnecting unknow device: " + device); 563 } 564 break; 565 default: 566 loge("Incorrect state: " + state); 567 break; 568 } 569 } 570 571 } 572 573 private class Connected extends State { 574 @Override 575 public void enter() { 576 // Remove pending connection attempts that were deferred during the pending 577 // state. This is to prevent auto connect attempts from disconnecting 578 // devices that previously successfully connected. 579 // TODO: This needs to check for multiple A2DP connections, once supported... 580 removeDeferredMessages(CONNECT); 581 582 log("Enter Connected: " + getCurrentMessage().what); 583 // Upon connected, the audio starts out as stopped 584 broadcastAudioState(mCurrentDevice, BluetoothA2dp.STATE_NOT_PLAYING, 585 BluetoothA2dp.STATE_PLAYING); 586 } 587 588 @Override 589 public boolean processMessage(Message message) { 590 log("Connected process message: " + message.what); 591 if (mCurrentDevice == null) { 592 loge("ERROR: mCurrentDevice is null in Connected"); 593 return NOT_HANDLED; 594 } 595 596 boolean retValue = HANDLED; 597 switch(message.what) { 598 case CONNECT: 599 { 600 BluetoothDevice device = (BluetoothDevice) message.obj; 601 if (mCurrentDevice.equals(device)) { 602 break; 603 } 604 605 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 606 BluetoothProfile.STATE_DISCONNECTED); 607 if (!disconnectA2dpNative(getByteAddress(mCurrentDevice))) { 608 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 609 BluetoothProfile.STATE_CONNECTING); 610 break; 611 } 612 613 synchronized (A2dpStateMachine.this) { 614 mTargetDevice = device; 615 transitionTo(mPending); 616 } 617 } 618 break; 619 case DISCONNECT: 620 { 621 BluetoothDevice device = (BluetoothDevice) message.obj; 622 if (!mCurrentDevice.equals(device)) { 623 break; 624 } 625 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 626 BluetoothProfile.STATE_CONNECTED); 627 if (!disconnectA2dpNative(getByteAddress(device))) { 628 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 629 BluetoothProfile.STATE_DISCONNECTED); 630 break; 631 } 632 transitionTo(mPending); 633 } 634 break; 635 case STACK_EVENT: 636 StackEvent event = (StackEvent) message.obj; 637 switch (event.type) { 638 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 639 processConnectionEvent(event.valueInt, event.device); 640 break; 641 case EVENT_TYPE_AUDIO_STATE_CHANGED: 642 processAudioStateEvent(event.valueInt, event.device); 643 break; 644 default: 645 loge("Unexpected stack event: " + event.type); 646 break; 647 } 648 break; 649 default: 650 return NOT_HANDLED; 651 } 652 return retValue; 653 } 654 655 // in Connected state 656 private void processConnectionEvent(int state, BluetoothDevice device) { 657 switch (state) { 658 case CONNECTION_STATE_DISCONNECTED: 659 if (mCurrentDevice.equals(device)) { 660 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 661 BluetoothProfile.STATE_CONNECTED); 662 synchronized (A2dpStateMachine.this) { 663 mCurrentDevice = null; 664 transitionTo(mDisconnected); 665 } 666 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 667 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 668 BluetoothProfile.STATE_CONNECTING); 669 synchronized (A2dpStateMachine.this) { 670 mTargetDevice = null; 671 } 672 logi("Disconnected from mTargetDevice in connected state device: " + device); 673 } else { 674 loge("Disconnected from unknown device: " + device); 675 } 676 break; 677 default: 678 loge("Connection State Device: " + device + " bad state: " + state); 679 break; 680 } 681 } 682 private void processAudioStateEvent(int state, BluetoothDevice device) { 683 if (!mCurrentDevice.equals(device)) { 684 loge("Audio State Device:" + device + "is different from ConnectedDevice:" + 685 mCurrentDevice); 686 return; 687 } 688 switch (state) { 689 case AUDIO_STATE_STARTED: 690 if (mPlayingA2dpDevice == null) { 691 mPlayingA2dpDevice = device; 692 mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING); 693 broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING, 694 BluetoothA2dp.STATE_NOT_PLAYING); 695 } 696 break; 697 case AUDIO_STATE_REMOTE_SUSPEND: 698 case AUDIO_STATE_STOPPED: 699 if (mPlayingA2dpDevice != null) { 700 mPlayingA2dpDevice = null; 701 mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING); 702 broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING, 703 BluetoothA2dp.STATE_PLAYING); 704 } 705 break; 706 default: 707 loge("Audio State Device: " + device + " bad state: " + state); 708 break; 709 } 710 } 711 } 712 713 int getConnectionState(BluetoothDevice device) { 714 if (getCurrentState() == mDisconnected) { 715 return BluetoothProfile.STATE_DISCONNECTED; 716 } 717 718 synchronized (this) { 719 IState currentState = getCurrentState(); 720 if (currentState == mPending) { 721 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 722 return BluetoothProfile.STATE_CONNECTING; 723 } 724 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 725 return BluetoothProfile.STATE_DISCONNECTING; 726 } 727 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 728 return BluetoothProfile.STATE_CONNECTING; // incoming connection 729 } 730 return BluetoothProfile.STATE_DISCONNECTED; 731 } 732 733 if (currentState == mConnected) { 734 if (mCurrentDevice.equals(device)) { 735 return BluetoothProfile.STATE_CONNECTED; 736 } 737 return BluetoothProfile.STATE_DISCONNECTED; 738 } else { 739 loge("Bad currentState: " + currentState); 740 return BluetoothProfile.STATE_DISCONNECTED; 741 } 742 } 743 } 744 745 List<BluetoothDevice> getConnectedDevices() { 746 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 747 synchronized (this) { 748 if (getCurrentState() == mConnected) { 749 devices.add(mCurrentDevice); 750 } 751 } 752 return devices; 753 } 754 755 boolean isPlaying(BluetoothDevice device) { 756 synchronized (this) { 757 if (device.equals(mPlayingA2dpDevice)) { 758 return true; 759 } 760 } 761 return false; 762 } 763 764 BluetoothCodecStatus getCodecStatus() { 765 synchronized (this) { 766 return mCodecStatus; 767 } 768 } 769 770 private void onCodecConfigChanged(BluetoothCodecConfig newCodecConfig, 771 BluetoothCodecConfig[] codecsLocalCapabilities, 772 BluetoothCodecConfig[] codecsSelectableCapabilities) { 773 BluetoothCodecConfig prevCodecConfig = null; 774 synchronized (this) { 775 if (mCodecStatus != null) { 776 prevCodecConfig = mCodecStatus.getCodecConfig(); 777 } 778 mCodecStatus = new BluetoothCodecStatus( 779 newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities); 780 } 781 782 Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED); 783 intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, mCodecStatus); 784 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 785 786 log("A2DP Codec Config: " + prevCodecConfig + "->" + newCodecConfig); 787 for (BluetoothCodecConfig codecConfig : codecsLocalCapabilities) { 788 log("A2DP Codec Local Capability: " + codecConfig); 789 } 790 for (BluetoothCodecConfig codecConfig : codecsSelectableCapabilities) { 791 log("A2DP Codec Selectable Capability: " + codecConfig); 792 } 793 794 // Inform the Audio Service about the codec configuration change, 795 // so the Audio Service can reset accordingly the audio feeding 796 // parameters in the Audio HAL to the Bluetooth stack. 797 if (!newCodecConfig.sameAudioFeedingParameters(prevCodecConfig) && (mCurrentDevice != null) 798 && (getCurrentState() == mConnected)) { 799 // Add the device only if it is currently connected 800 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mCurrentDevice); 801 mAudioManager.handleBluetoothA2dpDeviceConfigChange(mCurrentDevice); 802 } 803 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 804 } 805 806 void setCodecConfigPreference(BluetoothCodecConfig codecConfig) { 807 BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1]; 808 codecConfigArray[0] = codecConfig; 809 setCodecConfigPreferenceNative(codecConfigArray); 810 } 811 812 void enableOptionalCodecs() { 813 BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities(); 814 if (codecConfigArray == null) { 815 return; 816 } 817 818 // Set the mandatory codec's priority to default, and remove the rest 819 for (int i = 0; i < codecConfigArray.length; i++) { 820 BluetoothCodecConfig codecConfig = codecConfigArray[i]; 821 if (!codecConfig.isMandatoryCodec()) { 822 codecConfigArray[i] = null; 823 } 824 } 825 826 setCodecConfigPreferenceNative(codecConfigArray); 827 } 828 829 void disableOptionalCodecs() { 830 BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities(); 831 if (codecConfigArray == null) { 832 return; 833 } 834 // Set the mandatory codec's priority to highest, and ignore the rest 835 for (int i = 0; i < codecConfigArray.length; i++) { 836 BluetoothCodecConfig codecConfig = codecConfigArray[i]; 837 if (codecConfig.isMandatoryCodec()) { 838 codecConfig.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST); 839 } else { 840 codecConfigArray[i] = null; 841 } 842 } 843 setCodecConfigPreferenceNative(codecConfigArray); 844 } 845 846 boolean okToConnect(BluetoothDevice device) { 847 AdapterService adapterService = AdapterService.getAdapterService(); 848 int priority = mService.getPriority(device); 849 boolean ret = false; 850 //check if this is an incoming connection in Quiet mode. 851 if((adapterService == null) || 852 ((adapterService.isQuietModeEnabled() == true) && 853 (mTargetDevice == null))){ 854 ret = false; 855 } 856 // check priority and accept or reject the connection. if priority is undefined 857 // it is likely that our SDP has not completed and peer is initiating the 858 // connection. Allow this connection, provided the device is bonded 859 else if((BluetoothProfile.PRIORITY_OFF < priority) || 860 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) && 861 (device.getBondState() != BluetoothDevice.BOND_NONE))){ 862 ret= true; 863 } 864 return ret; 865 } 866 867 synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 868 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 869 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 870 int connectionState; 871 872 for (BluetoothDevice device : bondedDevices) { 873 ParcelUuid[] featureUuids = device.getUuids(); 874 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSink)) { 875 continue; 876 } 877 connectionState = getConnectionState(device); 878 for(int i = 0; i < states.length; i++) { 879 if (connectionState == states[i]) { 880 deviceList.add(device); 881 } 882 } 883 } 884 return deviceList; 885 } 886 887 888 // This method does not check for error conditon (newState == prevState) 889 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 890 891 int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, newState, 892 BluetoothProfile.A2DP); 893 894 mWakeLock.acquire(); 895 mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage( 896 MSG_CONNECTION_STATE_CHANGED, 897 prevState, 898 newState, 899 device), 900 delay); 901 } 902 903 private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { 904 Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED); 905 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 906 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 907 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 908 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 909 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 910 911 log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); 912 } 913 914 private byte[] getByteAddress(BluetoothDevice device) { 915 return Utils.getBytesFromAddress(device.getAddress()); 916 } 917 918 private void onConnectionStateChanged(int state, byte[] address) { 919 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 920 event.valueInt = state; 921 event.device = getDevice(address); 922 sendMessage(STACK_EVENT, event); 923 } 924 925 private void onAudioStateChanged(int state, byte[] address) { 926 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 927 event.valueInt = state; 928 event.device = getDevice(address); 929 sendMessage(STACK_EVENT, event); 930 } 931 private BluetoothDevice getDevice(byte[] address) { 932 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 933 } 934 935 private class StackEvent { 936 int type = EVENT_TYPE_NONE; 937 int valueInt = 0; 938 BluetoothDevice device = null; 939 940 private StackEvent(int type) { 941 this.type = type; 942 } 943 } 944 /** Handles A2DP connection state change intent broadcasts. */ 945 private class IntentBroadcastHandler extends Handler { 946 947 private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { 948 Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 949 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 950 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 951 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 952 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 953 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 954 log("Connection state " + device + ": " + prevState + "->" + state); 955 } 956 957 @Override 958 public void handleMessage(Message msg) { 959 switch (msg.what) { 960 case MSG_CONNECTION_STATE_CHANGED: 961 onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2); 962 mWakeLock.release(); 963 break; 964 } 965 } 966 } 967 968 public void dump(StringBuilder sb) { 969 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 970 ProfileService.println(sb, "mTargetDevice: " + mTargetDevice); 971 ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice); 972 ProfileService.println(sb, "mPlayingA2dpDevice: " + mPlayingA2dpDevice); 973 ProfileService.println(sb, "StateMachine: " + this.toString()); 974 } 975 976 // Event types for STACK_EVENT message 977 final private static int EVENT_TYPE_NONE = 0; 978 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 979 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 980 981 // Do not modify without updating the HAL bt_av.h files. 982 983 // match up with btav_connection_state_t enum of bt_av.h 984 final static int CONNECTION_STATE_DISCONNECTED = 0; 985 final static int CONNECTION_STATE_CONNECTING = 1; 986 final static int CONNECTION_STATE_CONNECTED = 2; 987 final static int CONNECTION_STATE_DISCONNECTING = 3; 988 989 // match up with btav_audio_state_t enum of bt_av.h 990 final static int AUDIO_STATE_REMOTE_SUSPEND = 0; 991 final static int AUDIO_STATE_STOPPED = 1; 992 final static int AUDIO_STATE_STARTED = 2; 993 994 private native static void classInitNative(); 995 private native void initNative(BluetoothCodecConfig[] codecConfigPriorites); 996 private native void cleanupNative(); 997 private native boolean connectA2dpNative(byte[] address); 998 private native boolean disconnectA2dpNative(byte[] address); 999 private native boolean setCodecConfigPreferenceNative(BluetoothCodecConfig[] codecConfigArray); 1000} 1001