A2dpStateMachine.java revision ac234ef0ddd67471d82f77c41535a5ea1b68b443
1/* 2 * Copyright (C) 2012 Google Inc. 3 */ 4 5/** 6 * Bluetooth A2dp StateMachine 7 * (Disconnected) 8 * | ^ 9 * CONNECT | | DISCONNECTED 10 * V | 11 * (Pending) 12 * | ^ 13 * CONNECTED | | CONNECT 14 * V | 15 * (Connected) 16 */ 17package com.android.bluetooth.a2dp; 18 19import android.bluetooth.BluetoothA2dp; 20import android.bluetooth.BluetoothAdapter; 21import android.bluetooth.BluetoothDevice; 22import android.bluetooth.BluetoothProfile; 23import android.bluetooth.BluetoothUuid; 24import android.bluetooth.IBluetooth; 25import android.content.Context; 26import android.content.Intent; 27import android.os.Message; 28import android.os.RemoteException; 29import android.os.ServiceManager; 30import android.os.ParcelUuid; 31import android.util.Log; 32import com.android.bluetooth.Utils; 33import com.android.bluetooth.btservice.AdapterService; 34import com.android.internal.util.IState; 35import com.android.internal.util.State; 36import com.android.internal.util.StateMachine; 37import java.util.ArrayList; 38import java.util.List; 39import java.util.Set; 40 41final class A2dpStateMachine extends StateMachine { 42 private static final String TAG = "A2dpStateMachine"; 43 private static final boolean DBG = true; 44 45 static final int CONNECT = 1; 46 static final int DISCONNECT = 2; 47 private static final int STACK_EVENT = 101; 48 private static final int CONNECT_TIMEOUT = 201; 49 50 private Disconnected mDisconnected; 51 private Pending mPending; 52 private Connected mConnected; 53 54 private Context mContext; 55 private BluetoothAdapter mAdapter; 56 private static final ParcelUuid[] A2DP_UUIDS = { 57 BluetoothUuid.AudioSink 58 }; 59 60 // mCurrentDevice is the device connected before the state changes 61 // mTargetDevice is the device to be connected 62 // mIncomingDevice is the device connecting to us, valid only in Pending state 63 // when mIncomingDevice is not null, both mCurrentDevice 64 // and mTargetDevice are null 65 // when either mCurrentDevice or mTargetDevice is not null, 66 // mIncomingDevice is null 67 // Stable states 68 // No connection, Disconnected state 69 // both mCurrentDevice and mTargetDevice are null 70 // Connected, Connected state 71 // mCurrentDevice is not null, mTargetDevice is null 72 // Interim states 73 // Connecting to a device, Pending 74 // mCurrentDevice is null, mTargetDevice is not null 75 // Disconnecting device, Connecting to new device 76 // Pending 77 // Both mCurrentDevice and mTargetDevice are not null 78 // Disconnecting device Pending 79 // mCurrentDevice is not null, mTargetDevice is null 80 // Incoming connections Pending 81 // Both mCurrentDevice and mTargetDevice are null 82 private BluetoothDevice mCurrentDevice = null; 83 private BluetoothDevice mTargetDevice = null; 84 private BluetoothDevice mIncomingDevice = null; 85 private BluetoothDevice mPlayingA2dpDevice = null; 86 87 static { 88 classInitNative(); 89 } 90 91 A2dpStateMachine(Context context) { 92 super(TAG); 93 mContext = context; 94 mAdapter = BluetoothAdapter.getDefaultAdapter(); 95 96 initNative(); 97 98 mDisconnected = new Disconnected(); 99 mPending = new Pending(); 100 mConnected = new Connected(); 101 102 addState(mDisconnected); 103 addState(mPending); 104 addState(mConnected); 105 106 setInitialState(mDisconnected); 107 } 108 109 public void cleanup() { 110 cleanupNative(); 111 } 112 113 private class Disconnected extends State { 114 @Override 115 public void enter() { 116 log("Enter Disconnected: " + getCurrentMessage().what); 117 } 118 119 @Override 120 public boolean processMessage(Message message) { 121 log("Disconnected process message: " + message.what); 122 if (DBG) { 123 if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) { 124 log("ERROR: current, target, or mIncomingDevice not null in Disconnected"); 125 return NOT_HANDLED; 126 } 127 } 128 129 boolean retValue = HANDLED; 130 switch(message.what) { 131 case CONNECT: 132 BluetoothDevice device = (BluetoothDevice) message.obj; 133 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 134 BluetoothProfile.STATE_DISCONNECTED); 135 136 if (!connectA2dpNative(getByteAddress(device)) ) { 137 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 138 BluetoothProfile.STATE_CONNECTING); 139 break; 140 } 141 142 synchronized (A2dpStateMachine.this) { 143 mTargetDevice = device; 144 transitionTo(mPending); 145 } 146 // TODO(BT) remove CONNECT_TIMEOUT when the stack 147 // sends back events consistently 148 sendMessageDelayed(CONNECT_TIMEOUT, 30000); 149 break; 150 case DISCONNECT: 151 // ignore 152 break; 153 case STACK_EVENT: 154 StackEvent event = (StackEvent) message.obj; 155 switch (event.type) { 156 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 157 processConnectionEvent(event.valueInt, event.device); 158 break; 159 default: 160 Log.e(TAG, "Unexpected stack event: " + event.type); 161 break; 162 } 163 break; 164 default: 165 return NOT_HANDLED; 166 } 167 return retValue; 168 } 169 170 @Override 171 public void exit() { 172 log("Exit Disconnected: " + getCurrentMessage().what); 173 } 174 175 // in Disconnected state 176 private void processConnectionEvent(int state, BluetoothDevice device) { 177 switch (state) { 178 case CONNECTION_STATE_DISCONNECTED: 179 Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device); 180 break; 181 case CONNECTION_STATE_CONNECTING: 182 // TODO(BT) Assume it's incoming connection 183 // Do we need to check priority and accept/reject accordingly? 184 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 185 BluetoothProfile.STATE_DISCONNECTED); 186 synchronized (A2dpStateMachine.this) { 187 mIncomingDevice = device; 188 transitionTo(mPending); 189 } 190 break; 191 case CONNECTION_STATE_CONNECTED: 192 Log.w(TAG, "A2DP Connected from Disconnected state"); 193 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 194 BluetoothProfile.STATE_DISCONNECTED); 195 synchronized (A2dpStateMachine.this) { 196 mCurrentDevice = device; 197 transitionTo(mConnected); 198 } 199 break; 200 case CONNECTION_STATE_DISCONNECTING: 201 Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device); 202 break; 203 default: 204 Log.e(TAG, "Incorrect state: " + state); 205 break; 206 } 207 } 208 } 209 210 private class Pending extends State { 211 @Override 212 public void enter() { 213 log("Enter Pending: " + getCurrentMessage().what); 214 } 215 216 @Override 217 public boolean processMessage(Message message) { 218 log("Pending process message: " + message.what); 219 220 boolean retValue = HANDLED; 221 switch(message.what) { 222 case CONNECT: 223 deferMessage(message); 224 break; 225 case CONNECT_TIMEOUT: 226 onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED, 227 getByteAddress(mTargetDevice)); 228 break; 229 case DISCONNECT: 230 BluetoothDevice device = (BluetoothDevice) message.obj; 231 if (mCurrentDevice != null && mTargetDevice != null && 232 mTargetDevice.equals(device) ) { 233 // cancel connection to the mTargetDevice 234 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 235 BluetoothProfile.STATE_CONNECTING); 236 synchronized (A2dpStateMachine.this) { 237 mTargetDevice = null; 238 } 239 } else { 240 deferMessage(message); 241 } 242 break; 243 case STACK_EVENT: 244 StackEvent event = (StackEvent) message.obj; 245 switch (event.type) { 246 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 247 removeMessages(CONNECT_TIMEOUT); 248 processConnectionEvent(event.valueInt, event.device); 249 break; 250 default: 251 Log.e(TAG, "Unexpected stack event: " + event.type); 252 break; 253 } 254 break; 255 default: 256 return NOT_HANDLED; 257 } 258 return retValue; 259 } 260 261 // in Pending state 262 private void processConnectionEvent(int state, BluetoothDevice device) { 263 switch (state) { 264 case CONNECTION_STATE_DISCONNECTED: 265 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 266 broadcastConnectionState(mCurrentDevice, 267 BluetoothProfile.STATE_DISCONNECTED, 268 BluetoothProfile.STATE_DISCONNECTING); 269 synchronized (A2dpStateMachine.this) { 270 mCurrentDevice = null; 271 } 272 273 if (mTargetDevice != null) { 274 if (!connectA2dpNative(getByteAddress(mTargetDevice))) { 275 broadcastConnectionState(mTargetDevice, 276 BluetoothProfile.STATE_DISCONNECTED, 277 BluetoothProfile.STATE_CONNECTING); 278 synchronized (A2dpStateMachine.this) { 279 mTargetDevice = null; 280 transitionTo(mDisconnected); 281 } 282 } 283 } else { 284 synchronized (A2dpStateMachine.this) { 285 mIncomingDevice = null; 286 transitionTo(mDisconnected); 287 } 288 } 289 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 290 // outgoing connection failed 291 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 292 BluetoothProfile.STATE_CONNECTING); 293 synchronized (A2dpStateMachine.this) { 294 mTargetDevice = null; 295 transitionTo(mDisconnected); 296 } 297 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 298 broadcastConnectionState(mIncomingDevice, 299 BluetoothProfile.STATE_DISCONNECTED, 300 BluetoothProfile.STATE_CONNECTING); 301 synchronized (A2dpStateMachine.this) { 302 mIncomingDevice = null; 303 transitionTo(mDisconnected); 304 } 305 } else { 306 Log.e(TAG, "Unknown device Disconnected: " + device); 307 } 308 break; 309 case CONNECTION_STATE_CONNECTED: 310 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 311 // disconnection failed 312 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED, 313 BluetoothProfile.STATE_DISCONNECTING); 314 if (mTargetDevice != null) { 315 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED, 316 BluetoothProfile.STATE_CONNECTING); 317 } 318 synchronized (A2dpStateMachine.this) { 319 mTargetDevice = null; 320 transitionTo(mConnected); 321 } 322 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 323 broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED, 324 BluetoothProfile.STATE_CONNECTING); 325 synchronized (A2dpStateMachine.this) { 326 mCurrentDevice = mTargetDevice; 327 mTargetDevice = null; 328 transitionTo(mConnected); 329 } 330 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 331 broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED, 332 BluetoothProfile.STATE_CONNECTING); 333 synchronized (A2dpStateMachine.this) { 334 mCurrentDevice = mIncomingDevice; 335 mIncomingDevice = null; 336 transitionTo(mConnected); 337 } 338 } else { 339 Log.e(TAG, "Unknown device Connected: " + device); 340 // something is wrong here, but sync our state with stack 341 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 342 BluetoothProfile.STATE_DISCONNECTED); 343 synchronized (A2dpStateMachine.this) { 344 mCurrentDevice = device; 345 mTargetDevice = null; 346 mIncomingDevice = null; 347 transitionTo(mConnected); 348 } 349 } 350 break; 351 case CONNECTION_STATE_CONNECTING: 352 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 353 log("current device tries to connect back"); 354 // TODO(BT) ignore or reject 355 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 356 // The stack is connecting to target device or 357 // there is an incoming connection from the target device at the same time 358 // we already broadcasted the intent, doing nothing here 359 if (DBG) { 360 log("Stack and target device are connecting"); 361 } 362 } 363 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 364 Log.e(TAG, "Another connecting event on the incoming device"); 365 } else { 366 // We get an incoming connecting request while Pending 367 // TODO(BT) is stack handing this case? let's ignore it for now 368 log("Incoming connection while pending, ignore"); 369 } 370 break; 371 case CONNECTION_STATE_DISCONNECTING: 372 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 373 // we already broadcasted the intent, doing nothing here 374 if (DBG) { 375 log("stack is disconnecting mCurrentDevice"); 376 } 377 } else if (mTargetDevice != null && mTargetDevice.equals(device)) { 378 Log.e(TAG, "TargetDevice is getting disconnected"); 379 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) { 380 Log.e(TAG, "IncomingDevice is getting disconnected"); 381 } else { 382 Log.e(TAG, "Disconnecting unknow device: " + device); 383 } 384 break; 385 default: 386 Log.e(TAG, "Incorrect state: " + state); 387 break; 388 } 389 } 390 391 } 392 393 private class Connected extends State { 394 @Override 395 public void enter() { 396 log("Enter Connected: " + getCurrentMessage().what); 397 // Upon connected, the audio starts out as stopped 398 broadcastAudioState(mCurrentDevice, BluetoothA2dp.STATE_NOT_PLAYING, 399 BluetoothA2dp.STATE_PLAYING); 400 } 401 402 @Override 403 public boolean processMessage(Message message) { 404 log("Connected process message: " + message.what); 405 if (DBG) { 406 if (mCurrentDevice == null) { 407 log("ERROR: mCurrentDevice is null in Connected"); 408 return NOT_HANDLED; 409 } 410 } 411 412 boolean retValue = HANDLED; 413 switch(message.what) { 414 case CONNECT: 415 { 416 BluetoothDevice device = (BluetoothDevice) message.obj; 417 if (mCurrentDevice.equals(device)) { 418 break; 419 } 420 421 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING, 422 BluetoothProfile.STATE_DISCONNECTED); 423 if (!disconnectA2dpNative(getByteAddress(mCurrentDevice))) { 424 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED, 425 BluetoothProfile.STATE_CONNECTING); 426 break; 427 } 428 429 synchronized (A2dpStateMachine.this) { 430 mTargetDevice = device; 431 transitionTo(mPending); 432 } 433 } 434 break; 435 case DISCONNECT: 436 { 437 BluetoothDevice device = (BluetoothDevice) message.obj; 438 if (!mCurrentDevice.equals(device)) { 439 break; 440 } 441 broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING, 442 BluetoothProfile.STATE_CONNECTED); 443 if (!disconnectA2dpNative(getByteAddress(device))) { 444 broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED, 445 BluetoothProfile.STATE_DISCONNECTED); 446 break; 447 } 448 transitionTo(mPending); 449 } 450 break; 451 case STACK_EVENT: 452 StackEvent event = (StackEvent) message.obj; 453 switch (event.type) { 454 case EVENT_TYPE_CONNECTION_STATE_CHANGED: 455 processConnectionEvent(event.valueInt, event.device); 456 break; 457 case EVENT_TYPE_AUDIO_STATE_CHANGED: 458 processAudioStateEvent(event.valueInt, event.device); 459 break; 460 default: 461 Log.e(TAG, "Unexpected stack event: " + event.type); 462 break; 463 } 464 break; 465 default: 466 return NOT_HANDLED; 467 } 468 return retValue; 469 } 470 471 // in Connected state 472 private void processConnectionEvent(int state, BluetoothDevice device) { 473 switch (state) { 474 case CONNECTION_STATE_DISCONNECTED: 475 if (mCurrentDevice.equals(device)) { 476 broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED, 477 BluetoothProfile.STATE_CONNECTED); 478 synchronized (A2dpStateMachine.this) { 479 mCurrentDevice = null; 480 transitionTo(mDisconnected); 481 } 482 } else { 483 Log.e(TAG, "Disconnected from unknown device: " + device); 484 } 485 break; 486 default: 487 Log.e(TAG, "Connection State Device: " + device + " bad state: " + state); 488 break; 489 } 490 } 491 private void processAudioStateEvent(int state, BluetoothDevice device) { 492 if (!mCurrentDevice.equals(device)) { 493 Log.e(TAG, "Audio State Device:" + device + "is different from ConnectedDevice:" + 494 mCurrentDevice); 495 return; 496 } 497 switch (state) { 498 case AUDIO_STATE_STARTED: 499 if (mPlayingA2dpDevice == null) { 500 mPlayingA2dpDevice = device; 501 broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING, 502 BluetoothA2dp.STATE_NOT_PLAYING); 503 } 504 break; 505 case AUDIO_STATE_STOPPED: 506 if(mPlayingA2dpDevice != null) { 507 mPlayingA2dpDevice = null; 508 broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING, 509 BluetoothA2dp.STATE_PLAYING); 510 } 511 break; 512 default: 513 Log.e(TAG, "Audio State Device: " + device + " bad state: " + state); 514 break; 515 } 516 } 517 } 518 519 int getConnectionState(BluetoothDevice device) { 520 if (getCurrentState() == mDisconnected) { 521 return BluetoothProfile.STATE_DISCONNECTED; 522 } 523 524 synchronized (this) { 525 IState currentState = getCurrentState(); 526 if (currentState == mPending) { 527 if ((mTargetDevice != null) && mTargetDevice.equals(device)) { 528 return BluetoothProfile.STATE_CONNECTING; 529 } 530 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) { 531 return BluetoothProfile.STATE_DISCONNECTING; 532 } 533 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) { 534 return BluetoothProfile.STATE_CONNECTING; // incoming connection 535 } 536 return BluetoothProfile.STATE_DISCONNECTED; 537 } 538 539 if (currentState == mConnected) { 540 if (mCurrentDevice.equals(device)) { 541 return BluetoothProfile.STATE_CONNECTED; 542 } 543 return BluetoothProfile.STATE_DISCONNECTED; 544 } else { 545 Log.e(TAG, "Bad currentState: " + currentState); 546 return BluetoothProfile.STATE_DISCONNECTED; 547 } 548 } 549 } 550 551 List<BluetoothDevice> getConnectedDevices() { 552 List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>(); 553 synchronized(this) { 554 if (getCurrentState() == mConnected) { 555 devices.add(mCurrentDevice); 556 } 557 } 558 return devices; 559 } 560 561 boolean isPlaying(BluetoothDevice device) { 562 synchronized(this) { 563 if (device.equals(mPlayingA2dpDevice)) { 564 return true; 565 } 566 } 567 return false; 568 } 569 570 synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 571 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 572 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 573 int connectionState; 574 575 for (BluetoothDevice device : bondedDevices) { 576 ParcelUuid[] featureUuids = device.getUuids(); 577 if (!BluetoothUuid.containsAnyUuid(featureUuids, A2DP_UUIDS)) { 578 continue; 579 } 580 connectionState = getConnectionState(device); 581 for(int i = 0; i < states.length; i++) { 582 if (connectionState == states[i]) { 583 deviceList.add(device); 584 } 585 } 586 } 587 return deviceList; 588 } 589 590 // This method does not check for error conditon (newState == prevState) 591 private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) { 592 Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 593 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 594 intent.putExtra(BluetoothProfile.EXTRA_STATE, newState); 595 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 596 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 597 if (DBG) log("Connection state " + device + ": " + prevState + "->" + newState); 598 AdapterService svc = AdapterService.getAdapterService(); 599 if (svc != null) { 600 svc.onProfileConnectionStateChanged(device, BluetoothProfile.A2DP, newState, prevState); 601 } 602 } 603 604 private void broadcastAudioState(BluetoothDevice device, int state, int prevState) { 605 Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED); 606 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 607 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState); 608 intent.putExtra(BluetoothProfile.EXTRA_STATE, state); 609 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 610 mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM); 611 612 if (DBG) log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state); 613 } 614 615 private byte[] getByteAddress(BluetoothDevice device) { 616 return Utils.getBytesFromAddress(device.getAddress()); 617 } 618 619 private void onConnectionStateChanged(int state, byte[] address) { 620 StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED); 621 event.valueInt = state; 622 event.device = getDevice(address); 623 sendMessage(STACK_EVENT, event); 624 } 625 626 private void onAudioStateChanged(int state, byte[] address) { 627 StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED); 628 event.valueInt = state; 629 event.device = getDevice(address); 630 sendMessage(STACK_EVENT, event); 631 } 632 private BluetoothDevice getDevice(byte[] address) { 633 return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address)); 634 } 635 636 private void log(String msg) { 637 if (DBG) { 638 Log.d(TAG, msg); 639 } 640 } 641 642 private class StackEvent { 643 int type = EVENT_TYPE_NONE; 644 int valueInt = 0; 645 BluetoothDevice device = null; 646 647 private StackEvent(int type) { 648 this.type = type; 649 } 650 } 651 652 // Event types for STACK_EVENT message 653 final private static int EVENT_TYPE_NONE = 0; 654 final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1; 655 final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2; 656 657 // Do not modify without upating the HAL bt_av.h files. 658 659 // match up with btav_connection_state_t enum of bt_av.h 660 final static int CONNECTION_STATE_DISCONNECTED = 0; 661 final static int CONNECTION_STATE_CONNECTING = 1; 662 final static int CONNECTION_STATE_CONNECTED = 2; 663 final static int CONNECTION_STATE_DISCONNECTING = 3; 664 665 // match up with btav_audio_state_t enum of bt_av.h 666 final static int AUDIO_STATE_REMOTE_SUSPEND = 0; 667 final static int AUDIO_STATE_STOPPED = 1; 668 final static int AUDIO_STATE_STARTED = 2; 669 670 private native static void classInitNative(); 671 private native void initNative(); 672 private native void cleanupNative(); 673 private native boolean connectA2dpNative(byte[] address); 674 private native boolean disconnectA2dpNative(byte[] address); 675} 676