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