1 /* 2 * Copyright (C) 2014 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 Sink StateMachine 19 * (Disconnected) 20 * | ^ 21 * CONNECT | | DISCONNECTED 22 * V | 23 * (Pending) 24 * | ^ 25 * CONNECTED | | CONNECT 26 * V | 27 * (Connected -- See A2dpSinkStreamHandler) 28 */ 29 package com.android.bluetooth.a2dpsink; 30 31 import android.bluetooth.BluetoothA2dpSink; 32 import android.bluetooth.BluetoothAdapter; 33 import android.bluetooth.BluetoothAudioConfig; 34 import android.bluetooth.BluetoothDevice; 35 import android.bluetooth.BluetoothProfile; 36 import android.bluetooth.BluetoothUuid; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.media.AudioFormat; 40 import android.media.AudioManager; 41 import android.os.Handler; 42 import android.os.Message; 43 import android.os.ParcelUuid; 44 import android.os.PowerManager; 45 import android.util.Log; 46 47 import com.android.bluetooth.BluetoothMetricsProto; 48 import com.android.bluetooth.Utils; 49 import com.android.bluetooth.avrcpcontroller.AvrcpControllerService; 50 import com.android.bluetooth.btservice.AdapterService; 51 import com.android.bluetooth.btservice.MetricsLogger; 52 import com.android.bluetooth.btservice.ProfileService; 53 import com.android.internal.util.IState; 54 import com.android.internal.util.State; 55 import com.android.internal.util.StateMachine; 56 57 import java.util.ArrayList; 58 import java.util.HashMap; 59 import java.util.List; 60 import java.util.Set; 61 62 public class A2dpSinkStateMachine extends StateMachine { 63 private static final boolean DBG = false; 64 65 static final int CONNECT = 1; 66 static final int DISCONNECT = 2; 67 private static final int STACK_EVENT = 101; 68 private static final int CONNECT_TIMEOUT = 201; 69 public static final int EVENT_AVRCP_CT_PLAY = 301; 70 public static final int EVENT_AVRCP_CT_PAUSE = 302; 71 public static final int EVENT_AVRCP_TG_PLAY = 303; 72 public static final int EVENT_AVRCP_TG_PAUSE = 304; 73 public static final int EVENT_REQUEST_FOCUS = 305; 74 75 private static final int IS_INVALID_DEVICE = 0; 76 private static final int IS_VALID_DEVICE = 1; 77 public static final int AVRC_ID_PLAY = 0x44; 78 public static final int AVRC_ID_PAUSE = 0x46; 79 public static final int KEY_STATE_PRESSED = 0; 80 public static final int KEY_STATE_RELEASED = 1; 81 82 // Connection states. 83 // 1. Disconnected: The connection does not exist. 84 // 2. Pending: The connection is being established. 85 // 3. Connected: The connection is established. The audio connection is in Idle state. 86 private Disconnected mDisconnected; 87 private Pending mPending; 88 private Connected mConnected; 89 90 private A2dpSinkService mService; 91 private Context mContext; 92 private BluetoothAdapter mAdapter; 93 private IntentBroadcastHandler mIntentBroadcastHandler; 94 95 private static final int MSG_CONNECTION_STATE_CHANGED = 0; 96 97 private final Object mLockForPatch = new Object(); 98 99 // mCurrentDevice is the device connected before the state changes 100 // mTargetDevice is the device to be connected 101 // mIncomingDevice is the device connecting to us, valid only in Pending state 102 // when mIncomingDevice is not null, both mCurrentDevice 103 // and mTargetDevice are null 104 // when either mCurrentDevice or mTargetDevice is not null, 105 // mIncomingDevice is null 106 // Stable states 107 // No connection, Disconnected state 108 // both mCurrentDevice and mTargetDevice are null 109 // Connected, Connected state 110 // mCurrentDevice is not null, mTargetDevice is null 111 // Interim states 112 // Connecting to a device, Pending 113 // mCurrentDevice is null, mTargetDevice is not null 114 // Disconnecting device, Connecting to new device 115 // Pending 116 // Both mCurrentDevice and mTargetDevice are not null 117 // Disconnecting device Pending 118 // mCurrentDevice is not null, mTargetDevice is null 119 // Incoming connections Pending 120 // Both mCurrentDevice and mTargetDevice are null 121 private BluetoothDevice mCurrentDevice = null; 122 private BluetoothDevice mTargetDevice = null; 123 private BluetoothDevice mIncomingDevice = null; 124 private BluetoothDevice mPlayingDevice = null; 125 private A2dpSinkStreamHandler mStreaming = null; 126 127 private final HashMap<BluetoothDevice, BluetoothAudioConfig> mAudioConfigs = 128 new HashMap<BluetoothDevice, BluetoothAudioConfig>(); 129 130 static { 131 classInitNative(); 132 } 133 134 private A2dpSinkStateMachine(A2dpSinkService svc, Context context) { 135 super("A2dpSinkStateMachine"); 136 mService = svc; 137 mContext = context; 138 mAdapter = BluetoothAdapter.getDefaultAdapter(); 139 140 initNative(); 141 142 mDisconnected = new Disconnected(); 143 mPending = new Pending(); 144 mConnected = new Connected(); 145 146 addState(mDisconnected); 147 addState(mPending); 148 addState(mConnected); 149 150 setInitialState(mDisconnected); 151 152 PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); 153 154 mIntentBroadcastHandler = new IntentBroadcastHandler(); 155 } 156 157 static A2dpSinkStateMachine make(A2dpSinkService svc, Context context) { 158 Log.d("A2dpSinkStateMachine", "make"); 159 A2dpSinkStateMachine a2dpSm = new A2dpSinkStateMachine(svc, context); 160 a2dpSm.start(); 161 return a2dpSm; 162 } 163 164 public void doQuit() { 165 if (DBG) { 166 Log.d("A2dpSinkStateMachine", "Quit"); 167 } 168 synchronized (A2dpSinkStateMachine.this) { 169 mStreaming = null; 170 } 171 quitNow(); 172 } 173 174 public void cleanup() { 175 cleanupNative(); 176 mAudioConfigs.clear(); 177 } 178 179 public void dump(StringBuilder sb) { 180 ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice); 181 ProfileService.println(sb, "mTargetDevice: " + mTargetDevice); 182 ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice); 183 ProfileService.println(sb, "StateMachine: " + this.toString()); 184 } 185 186 private class Disconnected extends State { 187 @Override 188 public void enter() { 189 log("Enter Disconnected: " + getCurrentMessage().what); 190 } 191 192 @Override 193 public boolean processMessage(Message message) { 194 log("Disconnected process message: " + message.what); 195 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 196 loge("ERROR: current, target, or mIncomingDevice not null in Disconnected"); 197 return NOT_HANDLED; 198 } 199 200 boolean retValue = HANDLED; 201 switch (message.what) { 202 case CONNECT: 203 BluetoothDevice device = (BluetoothDevice) message.obj; 204 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 205 BluetoothProfile.STATE_DISCONNECTED); 206 207 if (!connectA2dpNative(getByteAddress(device))) { 208 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 209 BluetoothProfile.STATE_CONNECTING); 210 break; 211 } 212 213 synchronized (A2dpSinkStateMachine.this) { 214 mTargetDevice = device; 215 transitionTo(mPending); 216 } 217 // TODO(BT) remove CONNECT_TIMEOUT when the stack 218 // sends back events consistently 219 sendMessageDelayed(CONNECT_TIMEOUT, 30000); 220 break; 221 case DISCONNECT: 222 // ignore 223 break; 224 case STACK_EVENT: 225 StackEvent event = (StackEvent) message.obj; 226 switch (event.type) { 227 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 228 processConnectionEvent(event.device, event.valueInt); 229 break; 230 case EVENT_TYPE_AUDIO_CONFIG_CHANGED: 231 processAudioConfigEvent(event.device, event.audioConfig); 232 break; 233 default: 234 loge("Unexpected stack event: " + event.type); 235 break; 236 } 237 break; 238 default: 239 return NOT_HANDLED; 240 } 241 return retValue; 242 } 243 244 @Override 245 public void exit() { 246 log("Exit Disconnected: " + getCurrentMessage().what); 247 } 248 249 // in Disconnected state 250 private void processConnectionEvent(BluetoothDevice device, int state) { 251 switch (state) { 252 case CONNECTION_STATE_DISCONNECTED: 253 logw("Ignore A2DP DISCONNECTED event, device: " + device); 254 break; 255 case CONNECTION_STATE_CONNECTING: 256 if (okToConnect(device)) { 257 logi("Incoming A2DP accepted"); 258 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 259 BluetoothProfile.STATE_DISCONNECTED); 260 synchronized (A2dpSinkStateMachine.this) { 261 mIncomingDevice = device; 262 transitionTo(mPending); 263 } 264 } else { 265 //reject the connection and stay in Disconnected state itself 266 logi("Incoming A2DP rejected"); 267 disconnectA2dpNative(getByteAddress(device)); 268 } 269 break; 270 case CONNECTION_STATE_CONNECTED: 271 logw("A2DP Connected from Disconnected state"); 272 if (okToConnect(device)) { 273 logi("Incoming A2DP accepted"); 274 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 275 BluetoothProfile.STATE_DISCONNECTED); 276 synchronized (A2dpSinkStateMachine.this) { 277 mCurrentDevice = device; 278 transitionTo(mConnected); 279 } 280 } else { 281 //reject the connection and stay in Disconnected state itself 282 logi("Incoming A2DP rejected"); 283 disconnectA2dpNative(getByteAddress(device)); 284 } 285 break; 286 case CONNECTION_STATE_DISCONNECTING: 287 logw("Ignore HF DISCONNECTING event, device: " + device); 288 break; 289 default: 290 loge("Incorrect state: " + state); 291 break; 292 } 293 } 294 } 295 296 private class Pending extends State { 297 @Override 298 public void enter() { 299 log("Enter Pending: " + getCurrentMessage().what); 300 } 301 302 @Override 303 public boolean processMessage(Message message) { 304 log("Pending process message: " + message.what); 305 306 boolean retValue = HANDLED; 307 switch (message.what) { 308 case CONNECT: 309 logd("Disconnect before connecting to another target"); 310 break; 311 case CONNECT_TIMEOUT: 312 onConnectionStateChanged(getByteAddress(mTargetDevice), 313 CONNECTION_STATE_DISCONNECTED); 314 break; 315 case DISCONNECT: 316 BluetoothDevice device = (BluetoothDevice) message.obj; 317 if (mCurrentDevice != null && mTargetDevice != null && mTargetDevice.equals( 318 device)) { 319 // cancel connection to the mTargetDevice 320 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 321 BluetoothProfile.STATE_CONNECTING); 322 synchronized (A2dpSinkStateMachine.this) { 323 mTargetDevice = null; 324 } 325 } 326 break; 327 case STACK_EVENT: 328 StackEvent event = (StackEvent) message.obj; 329 log("STACK_EVENT " + event.type); 330 switch (event.type) { 331 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 332 removeMessages(CONNECT_TIMEOUT); 333 processConnectionEvent(event.device, event.valueInt); 334 break; 335 case EVENT_TYPE_AUDIO_CONFIG_CHANGED: 336 processAudioConfigEvent(event.device, event.audioConfig); 337 break; 338 default: 339 loge("Unexpected stack event: " + event.type); 340 break; 341 } 342 break; 343 default: 344 return NOT_HANDLED; 345 } 346 return retValue; 347 } 348 349 // in Pending state 350 private void processConnectionEvent(BluetoothDevice device, int state) { 351 log("processConnectionEvent state " + state); 352 log("Devices curr: " + mCurrentDevice + " target: " + mTargetDevice + " incoming: " 353 + mIncomingDevice + " device: " + device); 354 switch (state) { 355 case CONNECTION_STATE_DISCONNECTED: 356 mAudioConfigs.remove(device); 357 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 358 broadcastConnectionState(mCurrentDevice, 359 BluetoothProfile.STATE_DISCONNECTED, 360 BluetoothProfile.STATE_DISCONNECTING); 361 synchronized (A2dpSinkStateMachine.this) { 362 mCurrentDevice = null; 363 } 364 365 if (mTargetDevice != null) { 366 if (!connectA2dpNative(getByteAddress(mTargetDevice))) { 367 broadcastConnectionState(mTargetDevice, 368 BluetoothProfile.STATE_DISCONNECTED, 369 BluetoothProfile.STATE_CONNECTING); 370 synchronized (A2dpSinkStateMachine.this) { 371 mTargetDevice = null; 372 transitionTo(mDisconnected); 373 } 374 } 375 } else { 376 synchronized (A2dpSinkStateMachine.this) { 377 mIncomingDevice = null; 378 transitionTo(mDisconnected); 379 } 380 } 381 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 382 // outgoing connection failed 383 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 384 BluetoothProfile.STATE_CONNECTING); 385 synchronized (A2dpSinkStateMachine.this) { 386 mTargetDevice = null; 387 transitionTo(mDisconnected); 388 } 389 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 390 broadcastConnectionState(mIncomingDevice, 391 BluetoothProfile.STATE_DISCONNECTED, 392 BluetoothProfile.STATE_CONNECTING); 393 synchronized (A2dpSinkStateMachine.this) { 394 mIncomingDevice = null; 395 transitionTo(mDisconnected); 396 } 397 } else { 398 loge("Unknown device Disconnected: " + device); 399 } 400 break; 401 case CONNECTION_STATE_CONNECTED: 402 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 403 loge("current device is not null"); 404 // disconnection failed 405 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 406 BluetoothProfile.STATE_DISCONNECTING); 407 if (mTargetDevice != null) { 408 broadcastConnectionState(mTargetDevice, 409 BluetoothProfile.STATE_DISCONNECTED, 410 BluetoothProfile.STATE_CONNECTING); 411 } 412 synchronized (A2dpSinkStateMachine.this) { 413 mTargetDevice = null; 414 transitionTo(mConnected); 415 } 416 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 417 loge("target device is not null"); 418 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 419 BluetoothProfile.STATE_CONNECTING); 420 synchronized (A2dpSinkStateMachine.this) { 421 mCurrentDevice = mTargetDevice; 422 mTargetDevice = null; 423 transitionTo(mConnected); 424 } 425 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 426 loge("incoming device is not null"); 427 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 428 BluetoothProfile.STATE_CONNECTING); 429 synchronized (A2dpSinkStateMachine.this) { 430 mCurrentDevice = mIncomingDevice; 431 mIncomingDevice = null; 432 transitionTo(mConnected); 433 } 434 } else { 435 loge("Unknown device Connected: " + device); 436 // something is wrong here, but sync our state with stack by connecting to 437 // the new device and disconnect from previous device. 438 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 439 BluetoothProfile.STATE_DISCONNECTED); 440 broadcastConnectionState(mCurrentDevice, 441 BluetoothProfile.STATE_DISCONNECTED, 442 BluetoothProfile.STATE_CONNECTING); 443 synchronized (A2dpSinkStateMachine.this) { 444 mCurrentDevice = device; 445 mTargetDevice = null; 446 mIncomingDevice = null; 447 transitionTo(mConnected); 448 } 449 } 450 break; 451 case CONNECTION_STATE_CONNECTING: 452 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 453 log("current device tries to connect back"); 454 // TODO(BT) ignore or reject 455 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 456 // The stack is connecting to target device or 457 // there is an incoming connection from the target device at the same time 458 // we already broadcasted the intent, doing nothing here 459 log("Stack and target device are connecting"); 460 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 461 loge("Another connecting event on the incoming device"); 462 } else { 463 // We get an incoming connecting request while Pending 464 // TODO(BT) is stack handing this case? let's ignore it for now 465 log("Incoming connection while pending, ignore"); 466 } 467 break; 468 case CONNECTION_STATE_DISCONNECTING: 469 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 470 // we already broadcasted the intent, doing nothing here 471 if (DBG) { 472 log("stack is disconnecting mCurrentDevice"); 473 } 474 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 475 loge("TargetDevice is getting disconnected"); 476 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 477 loge("IncomingDevice is getting disconnected"); 478 } else { 479 loge("Disconnecting unknown device: " + device); 480 } 481 break; 482 default: 483 loge("Incorrect state: " + state); 484 break; 485 } 486 } 487 488 } 489 490 private class Connected extends State { 491 @Override 492 public void enter() { 493 log("Enter Connected: " + getCurrentMessage().what); 494 // Upon connected, the audio starts out as stopped 495 broadcastAudioState(mCurrentDevice, BluetoothA2dpSink.STATE_NOT_PLAYING, 496 BluetoothA2dpSink.STATE_PLAYING); 497 synchronized (A2dpSinkStateMachine.this) { 498 if (mStreaming == null) { 499 if (DBG) { 500 log("Creating New A2dpSinkStreamHandler"); 501 } 502 mStreaming = new A2dpSinkStreamHandler(A2dpSinkStateMachine.this, mContext); 503 } 504 } 505 if (mStreaming.getAudioFocus() == AudioManager.AUDIOFOCUS_NONE) { 506 informAudioFocusStateNative(0); 507 } 508 } 509 510 @Override 511 public boolean processMessage(Message message) { 512 log("Connected process message: " + message.what); 513 if (mCurrentDevice == null) { 514 loge("ERROR: mCurrentDevice is null in Connected"); 515 return NOT_HANDLED; 516 } 517 518 switch (message.what) { 519 case CONNECT: 520 logd("Disconnect before connecting to another target"); 521 break; 522 523 case DISCONNECT: { 524 BluetoothDevice device = (BluetoothDevice) message.obj; 525 if (!mCurrentDevice.equals(device)) { 526 break; 527 } 528 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 529 BluetoothProfile.STATE_CONNECTED); 530 if (!disconnectA2dpNative(getByteAddress(device))) { 531 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 532 BluetoothProfile.STATE_DISCONNECTED); 533 break; 534 } 535 mPlayingDevice = null; 536 mStreaming.obtainMessage(A2dpSinkStreamHandler.DISCONNECT).sendToTarget(); 537 transitionTo(mPending); 538 } 539 break; 540 541 case STACK_EVENT: 542 StackEvent event = (StackEvent) message.obj; 543 switch (event.type) { 544 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 545 processConnectionEvent(event.device, event.valueInt); 546 break; 547 case EVENT_TYPE_AUDIO_STATE_CHANGED: 548 processAudioStateEvent(event.device, event.valueInt); 549 break; 550 case EVENT_TYPE_AUDIO_CONFIG_CHANGED: 551 processAudioConfigEvent(event.device, event.audioConfig); 552 break; 553 default: 554 loge("Unexpected stack event: " + event.type); 555 break; 556 } 557 break; 558 559 case EVENT_AVRCP_CT_PLAY: 560 mStreaming.obtainMessage(A2dpSinkStreamHandler.SNK_PLAY).sendToTarget(); 561 break; 562 563 case EVENT_AVRCP_TG_PLAY: 564 mStreaming.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY).sendToTarget(); 565 break; 566 567 case EVENT_AVRCP_CT_PAUSE: 568 mStreaming.obtainMessage(A2dpSinkStreamHandler.SNK_PAUSE).sendToTarget(); 569 break; 570 571 case EVENT_AVRCP_TG_PAUSE: 572 mStreaming.obtainMessage(A2dpSinkStreamHandler.SRC_PAUSE).sendToTarget(); 573 break; 574 575 case EVENT_REQUEST_FOCUS: 576 mStreaming.obtainMessage(A2dpSinkStreamHandler.REQUEST_FOCUS).sendToTarget(); 577 break; 578 579 default: 580 return NOT_HANDLED; 581 } 582 return HANDLED; 583 } 584 585 // in Connected state 586 private void processConnectionEvent(BluetoothDevice device, int state) { 587 switch (state) { 588 case CONNECTION_STATE_DISCONNECTED: 589 mAudioConfigs.remove(device); 590 if ((mPlayingDevice != null) && (device.equals(mPlayingDevice))) { 591 mPlayingDevice = null; 592 } 593 if (mCurrentDevice.equals(device)) { 594 broadcastConnectionState(mCurrentDevice, 595 BluetoothProfile.STATE_DISCONNECTED, 596 BluetoothProfile.STATE_CONNECTED); 597 synchronized (A2dpSinkStateMachine.this) { 598 // Take care of existing audio focus in the streaming state machine. 599 mStreaming.obtainMessage(A2dpSinkStreamHandler.DISCONNECT) 600 .sendToTarget(); 601 mCurrentDevice = null; 602 transitionTo(mDisconnected); 603 } 604 } else { 605 loge("Disconnected from unknown device: " + device); 606 } 607 break; 608 default: 609 loge("Connection State Device: " + device + " bad state: " + state); 610 break; 611 } 612 } 613 614 private void processAudioStateEvent(BluetoothDevice device, int state) { 615 if (!mCurrentDevice.equals(device)) { 616 loge("Audio State Device:" + device + "is different from ConnectedDevice:" 617 + mCurrentDevice); 618 return; 619 } 620 log(" processAudioStateEvent in state " + state); 621 switch (state) { 622 case AUDIO_STATE_STARTED: 623 mStreaming.obtainMessage(A2dpSinkStreamHandler.SRC_STR_START).sendToTarget(); 624 break; 625 case AUDIO_STATE_REMOTE_SUSPEND: 626 case AUDIO_STATE_STOPPED: 627 mStreaming.obtainMessage(A2dpSinkStreamHandler.SRC_STR_STOP).sendToTarget(); 628 break; 629 default: 630 loge("Audio State Device: " + device + " bad state: " + state); 631 break; 632 } 633 } 634 } 635 636 private void processAudioConfigEvent(BluetoothDevice device, BluetoothAudioConfig audioConfig) { 637 log("processAudioConfigEvent: " + device); 638 mAudioConfigs.put(device, audioConfig); 639 broadcastAudioConfig(device, audioConfig); 640 } 641 642 int getConnectionState(BluetoothDevice device) { 643 if (getCurrentState() == mDisconnected) { 644 return BluetoothProfile.STATE_DISCONNECTED; 645 } 646 647 synchronized (this) { 648 IState currentState = getCurrentState(); 649 if (currentState == mPending) { 650 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 651 return BluetoothProfile.STATE_CONNECTING; 652 } 653 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 654 return BluetoothProfile.STATE_DISCONNECTING; 655 } 656 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 657 return BluetoothProfile.STATE_CONNECTING; // incoming connection 658 } 659 return BluetoothProfile.STATE_DISCONNECTED; 660 } 661 662 if (currentState == mConnected) { 663 if (mCurrentDevice.equals(device)) { 664 return BluetoothProfile.STATE_CONNECTED; 665 } 666 return BluetoothProfile.STATE_DISCONNECTED; 667 } else { 668 loge("Bad currentState: " + currentState); 669 return BluetoothProfile.STATE_DISCONNECTED; 670 } 671 } 672 } 673 674 BluetoothAudioConfig getAudioConfig(BluetoothDevice device) { 675 return mAudioConfigs.get(device); 676 } 677 678 List<BluetoothDevice> getConnectedDevices() { 679 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 680 synchronized (this) { 681 if (getCurrentState() == mConnected) { 682 devices.add(mCurrentDevice); 683 } 684 } 685 return devices; 686 } 687 688 boolean isPlaying(BluetoothDevice device) { 689 synchronized (this) { 690 if ((mPlayingDevice != null) && (device.equals(mPlayingDevice))) { 691 return true; 692 } 693 } 694 return false; 695 } 696 697 // Utility Functions 698 boolean okToConnect(BluetoothDevice device) { 699 AdapterService adapterService = AdapterService.getAdapterService(); 700 int priority = mService.getPriority(device); 701 702 // check priority and accept or reject the connection. if priority is undefined 703 // it is likely that our SDP has not completed and peer is initiating the 704 // connection. Allow this connection, provided the device is bonded 705 if ((BluetoothProfile.PRIORITY_OFF < priority) || ( 706 (BluetoothProfile.PRIORITY_UNDEFINED == priority) && (device.getBondState() 707 != BluetoothDevice.BOND_NONE))) { 708 return true; 709 } 710 logw("okToConnect not OK to connect " + device); 711 return false; 712 } 713 714 synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 715 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 716 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 717 int connectionState; 718 719 for (BluetoothDevice device : bondedDevices) { 720 ParcelUuid[] featureUuids = device.getUuids(); 721 if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSource)) { 722 continue; 723 } 724 connectionState = getConnectionState(device); 725 for (int i = 0; i < states.length; i++) { 726 if (connectionState == states[i]) { 727 deviceList.add(device); 728 } 729 } 730 } 731 return deviceList; 732 } 733 734 735 // This method does not check for error conditon (newState == prevState) 736 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 737 738 int delay = 0; 739 mIntentBroadcastHandler.sendMessageDelayed( 740 mIntentBroadcastHandler.obtainMessage(MSG_CONNECTION_STATE_CHANGED, prevState, 741 newState, device), delay); 742 } 743 744 private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { 745 Intent intent = new Intent(BluetoothA2dpSink.ACTION_PLAYING_STATE_CHANGED); 746 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 747 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 748 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 749 //FIXME intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 750 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 751 752 log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); 753 } 754 755 private void broadcastAudioConfig(BluetoothDevice device, BluetoothAudioConfig audioConfig) { 756 Intent intent = new Intent(BluetoothA2dpSink.ACTION_AUDIO_CONFIG_CHANGED); 757 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 758 intent.putExtra(BluetoothA2dpSink.EXTRA_AUDIO_CONFIG, audioConfig); 759 //FIXME intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 760 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 761 762 log("A2DP Audio Config : device: " + device + " config: " + audioConfig); 763 } 764 765 private byte[] getByteAddress(BluetoothDevice device) { 766 return Utils.getBytesFromAddress(device.getAddress()); 767 } 768 769 private void onConnectionStateChanged(byte[] address, int state) { 770 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 771 event.device = getDevice(address); 772 event.valueInt = state; 773 sendMessage(STACK_EVENT, event); 774 } 775 776 private void onAudioStateChanged(byte[] address, int state) { 777 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 778 event.device = getDevice(address); 779 event.valueInt = state; 780 sendMessage(STACK_EVENT, event); 781 } 782 783 private void onAudioConfigChanged(byte[] address, int sampleRate, int channelCount) { 784 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_CONFIG_CHANGED); 785 event.device = getDevice(address); 786 int channelConfig = 787 (channelCount == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO); 788 event.audioConfig = 789 new BluetoothAudioConfig(sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT); 790 sendMessage(STACK_EVENT, event); 791 } 792 793 private BluetoothDevice getDevice(byte[] address) { 794 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 795 } 796 797 private class StackEvent { 798 public int type = EVENT_TYPE_NONE; 799 public BluetoothDevice device = null; 800 public int valueInt = 0; 801 public BluetoothAudioConfig audioConfig = null; 802 803 private StackEvent(int type) { 804 this.type = type; 805 } 806 } 807 808 /** Handles A2DP connection state change intent broadcasts. */ 809 private class IntentBroadcastHandler extends Handler { 810 811 private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) { 812 if (prevState != state && state == BluetoothProfile.STATE_CONNECTED) { 813 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.A2DP_SINK); 814 } 815 Intent intent = new Intent(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED); 816 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 817 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 818 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 819 //FIXME intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 820 mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM); 821 log("Connection state " + device + ": " + prevState + "->" + state); 822 } 823 824 @Override 825 public void handleMessage(Message msg) { 826 switch (msg.what) { 827 case MSG_CONNECTION_STATE_CHANGED: 828 onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2); 829 break; 830 } 831 } 832 } 833 834 public boolean sendPassThruPlay(BluetoothDevice mDevice) { 835 log("sendPassThruPlay + "); 836 AvrcpControllerService avrcpCtrlService = 837 AvrcpControllerService.getAvrcpControllerService(); 838 if ((avrcpCtrlService != null) && (mDevice != null) 839 && (avrcpCtrlService.getConnectedDevices().contains(mDevice))) { 840 avrcpCtrlService.sendPassThroughCmd(mDevice, 841 AvrcpControllerService.PASS_THRU_CMD_ID_PLAY, 842 AvrcpControllerService.KEY_STATE_PRESSED); 843 avrcpCtrlService.sendPassThroughCmd(mDevice, 844 AvrcpControllerService.PASS_THRU_CMD_ID_PLAY, 845 AvrcpControllerService.KEY_STATE_RELEASED); 846 log(" sendPassThruPlay command sent - "); 847 return true; 848 } else { 849 log("passthru command not sent, connection unavailable"); 850 return false; 851 } 852 } 853 854 // Event types for STACK_EVENT message 855 private static final int EVENT_TYPE_NONE = 0; 856 private static final int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 857 private static final int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 858 private static final int EVENT_TYPE_AUDIO_CONFIG_CHANGED = 3; 859 860 // Do not modify without updating the HAL bt_av.h files. 861 862 // match up with btav_connection_state_t enum of bt_av.h 863 static final int CONNECTION_STATE_DISCONNECTED = 0; 864 static final int CONNECTION_STATE_CONNECTING = 1; 865 static final int CONNECTION_STATE_CONNECTED = 2; 866 static final int CONNECTION_STATE_DISCONNECTING = 3; 867 868 // match up with btav_audio_state_t enum of bt_av.h 869 static final int AUDIO_STATE_REMOTE_SUSPEND = 0; 870 static final int AUDIO_STATE_STOPPED = 1; 871 static final int AUDIO_STATE_STARTED = 2; 872 873 private static native void classInitNative(); 874 875 private native void initNative(); 876 877 private native void cleanupNative(); 878 879 private native boolean connectA2dpNative(byte[] address); 880 881 private native boolean disconnectA2dpNative(byte[] address); 882 883 public native void informAudioFocusStateNative(int focusGranted); 884 885 public native void informAudioTrackGainNative(float focusGranted); 886 } 887