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