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