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