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