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