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