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