Home | History | Annotate | Download | only in a2dp
      1 /*
      2  * Copyright (C) 2012 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /**
     18  * Bluetooth A2dp StateMachine
     19  *                      (Disconnected)
     20  *                           |    ^
     21  *                   CONNECT |    | DISCONNECTED
     22  *                           V    |
     23  *                         (Pending)
     24  *                           |    ^
     25  *                 CONNECTED |    | CONNECT
     26  *                           V    |
     27  *                        (Connected)
     28  */
     29 package com.android.bluetooth.a2dp;
     30 
     31 import android.bluetooth.BluetoothA2dp;
     32 import android.bluetooth.BluetoothAdapter;
     33 import android.bluetooth.BluetoothCodecConfig;
     34 import android.bluetooth.BluetoothCodecStatus;
     35 import android.bluetooth.BluetoothDevice;
     36 import android.bluetooth.BluetoothProfile;
     37 import android.bluetooth.BluetoothUuid;
     38 import android.content.Context;
     39 import android.content.Intent;
     40 import android.content.res.Resources;
     41 import android.content.res.Resources.NotFoundException;
     42 import android.media.AudioManager;
     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.util.Log;
     49 
     50 import com.android.bluetooth.R;
     51 import com.android.bluetooth.Utils;
     52 import com.android.bluetooth.btservice.AdapterService;
     53 import com.android.bluetooth.btservice.ProfileService;
     54 import com.android.internal.util.IState;
     55 import com.android.internal.util.State;
     56 import com.android.internal.util.StateMachine;
     57 
     58 import java.util.ArrayList;
     59 import java.util.List;
     60 import java.util.Set;
     61 
     62 final class A2dpStateMachine extends StateMachine {
     63     private static final boolean DBG = false;
     64     private static final String TAG = "A2dpStateMachine";
     65 
     66     static final int CONNECT = 1;
     67     static final int DISCONNECT = 2;
     68     private static final int STACK_EVENT = 101;
     69     private static final int CONNECT_TIMEOUT = 201;
     70 
     71     private Disconnected mDisconnected;
     72     private Pending mPending;
     73     private Connected mConnected;
     74 
     75     private A2dpService mService;
     76     private Context mContext;
     77     private BluetoothAdapter mAdapter;
     78     private final AudioManager mAudioManager;
     79     private IntentBroadcastHandler mIntentBroadcastHandler;
     80     private final WakeLock mWakeLock;
     81     private BluetoothCodecConfig[] mCodecConfigPriorities;
     82 
     83     private static final int MSG_CONNECTION_STATE_CHANGED = 0;
     84 
     85     // mCurrentDevice is the device connected before the state changes
     86     // mTargetDevice is the device to be connected
     87     // mIncomingDevice is the device connecting to us, valid only in Pending state
     88     //                when mIncomingDevice is not null, both mCurrentDevice
     89     //                  and mTargetDevice are null
     90     //                when either mCurrentDevice or mTargetDevice is not null,
     91     //                  mIncomingDevice is null
     92     // Stable states
     93     //   No connection, Disconnected state
     94     //                  both mCurrentDevice and mTargetDevice are null
     95     //   Connected, Connected state
     96     //              mCurrentDevice is not null, mTargetDevice is null
     97     // Interim states
     98     //   Connecting to a device, Pending
     99     //                           mCurrentDevice is null, mTargetDevice is not null
    100     //   Disconnecting device, Connecting to new device
    101     //     Pending
    102     //     Both mCurrentDevice and mTargetDevice are not null
    103     //   Disconnecting device Pending
    104     //                        mCurrentDevice is not null, mTargetDevice is null
    105     //   Incoming connections Pending
    106     //                        Both mCurrentDevice and mTargetDevice are null
    107     private BluetoothDevice mCurrentDevice = null;
    108     private BluetoothDevice mTargetDevice = null;
    109     private BluetoothDevice mIncomingDevice = null;
    110     private BluetoothDevice mPlayingA2dpDevice = null;
    111 
    112     private BluetoothCodecStatus mCodecStatus = null;
    113     private int mA2dpSourceCodecPrioritySbc = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    114     private int mA2dpSourceCodecPriorityAac = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    115     private int mA2dpSourceCodecPriorityAptx = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    116     private int mA2dpSourceCodecPriorityAptxHd = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    117     private int mA2dpSourceCodecPriorityLdac = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    118 
    119     static {
    120         classInitNative();
    121     }
    122 
    123     private A2dpStateMachine(A2dpService svc, Context context) {
    124         super("A2dpStateMachine");
    125         mService = svc;
    126         mContext = context;
    127         mAdapter = BluetoothAdapter.getDefaultAdapter();
    128         mCodecConfigPriorities = assignCodecConfigPriorities();
    129 
    130         initNative(mCodecConfigPriorities);
    131 
    132         mDisconnected = new Disconnected();
    133         mPending = new Pending();
    134         mConnected = new Connected();
    135 
    136         addState(mDisconnected);
    137         addState(mPending);
    138         addState(mConnected);
    139 
    140         setInitialState(mDisconnected);
    141 
    142         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
    143         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService");
    144 
    145         mIntentBroadcastHandler = new IntentBroadcastHandler();
    146 
    147         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    148     }
    149 
    150     static A2dpStateMachine make(A2dpService svc, Context context) {
    151         Log.d(TAG, "make");
    152         A2dpStateMachine a2dpSm = new A2dpStateMachine(svc, context);
    153         a2dpSm.start();
    154         return a2dpSm;
    155     }
    156 
    157     // Assign the A2DP Source codec config priorities
    158     private BluetoothCodecConfig[] assignCodecConfigPriorities() {
    159         Resources resources = mContext.getResources();
    160         if (resources == null) {
    161             return null;
    162         }
    163 
    164         int value;
    165         try {
    166             value = resources.getInteger(R.integer.a2dp_source_codec_priority_sbc);
    167         } catch (NotFoundException e) {
    168             value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    169         }
    170         if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED)
    171                 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) {
    172             mA2dpSourceCodecPrioritySbc = value;
    173         }
    174 
    175         try {
    176             value = resources.getInteger(R.integer.a2dp_source_codec_priority_aac);
    177         } catch (NotFoundException e) {
    178             value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    179         }
    180         if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED)
    181                 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) {
    182             mA2dpSourceCodecPriorityAac = value;
    183         }
    184 
    185         try {
    186             value = resources.getInteger(R.integer.a2dp_source_codec_priority_aptx);
    187         } catch (NotFoundException e) {
    188             value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    189         }
    190         if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED)
    191                 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) {
    192             mA2dpSourceCodecPriorityAptx = value;
    193         }
    194 
    195         try {
    196             value = resources.getInteger(R.integer.a2dp_source_codec_priority_aptx_hd);
    197         } catch (NotFoundException e) {
    198             value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    199         }
    200         if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED)
    201                 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) {
    202             mA2dpSourceCodecPriorityAptxHd = value;
    203         }
    204 
    205         try {
    206             value = resources.getInteger(R.integer.a2dp_source_codec_priority_ldac);
    207         } catch (NotFoundException e) {
    208             value = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    209         }
    210         if ((value >= BluetoothCodecConfig.CODEC_PRIORITY_DISABLED)
    211                 && (value < BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST)) {
    212             mA2dpSourceCodecPriorityLdac = value;
    213         }
    214 
    215         BluetoothCodecConfig codecConfig;
    216         BluetoothCodecConfig[] codecConfigArray =
    217                 new BluetoothCodecConfig[BluetoothCodecConfig.SOURCE_CODEC_TYPE_MAX];
    218         codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC,
    219                 mA2dpSourceCodecPrioritySbc, BluetoothCodecConfig.SAMPLE_RATE_NONE,
    220                 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE,
    221                 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */,
    222                 0 /* codecSpecific4 */);
    223         codecConfigArray[0] = codecConfig;
    224         codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC,
    225                 mA2dpSourceCodecPriorityAac, BluetoothCodecConfig.SAMPLE_RATE_NONE,
    226                 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE,
    227                 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */,
    228                 0 /* codecSpecific4 */);
    229         codecConfigArray[1] = codecConfig;
    230         codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX,
    231                 mA2dpSourceCodecPriorityAptx, BluetoothCodecConfig.SAMPLE_RATE_NONE,
    232                 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE,
    233                 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */,
    234                 0 /* codecSpecific4 */);
    235         codecConfigArray[2] = codecConfig;
    236         codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD,
    237                 mA2dpSourceCodecPriorityAptxHd, BluetoothCodecConfig.SAMPLE_RATE_NONE,
    238                 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE,
    239                 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */,
    240                 0 /* codecSpecific4 */);
    241         codecConfigArray[3] = codecConfig;
    242         codecConfig = new BluetoothCodecConfig(BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC,
    243                 mA2dpSourceCodecPriorityLdac, BluetoothCodecConfig.SAMPLE_RATE_NONE,
    244                 BluetoothCodecConfig.BITS_PER_SAMPLE_NONE, BluetoothCodecConfig.CHANNEL_MODE_NONE,
    245                 0 /* codecSpecific1 */, 0 /* codecSpecific2 */, 0 /* codecSpecific3 */,
    246                 0 /* codecSpecific4 */);
    247         codecConfigArray[4] = codecConfig;
    248 
    249         return codecConfigArray;
    250     }
    251 
    252     public void doQuit() {
    253         if ((mTargetDevice != null) &&
    254             (getConnectionState(mTargetDevice) == BluetoothProfile.STATE_CONNECTING)) {
    255             log("doQuit()- Move A2DP State to DISCONNECTED");
    256             broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
    257                                      BluetoothProfile.STATE_CONNECTING);
    258         }
    259         quitNow();
    260     }
    261 
    262     public void cleanup() {
    263         cleanupNative();
    264     }
    265 
    266         private class Disconnected extends State {
    267         @Override
    268         public void enter() {
    269             log("Enter Disconnected: " + getCurrentMessage().what);
    270             if (mCurrentDevice != null || mTargetDevice != null || mIncomingDevice != null) {
    271                 loge("ERROR: enter() inconsistent state in Disconnected: current = "
    272                         + mCurrentDevice + " target = " + mTargetDevice + " incoming = "
    273                         + mIncomingDevice);
    274             }
    275         }
    276 
    277         @Override
    278         public boolean processMessage(Message message) {
    279             log("Disconnected process message: " + message.what);
    280             if (mCurrentDevice != null || mTargetDevice != null  || mIncomingDevice != null) {
    281                 loge("ERROR: not null state in Disconnected: current = " + mCurrentDevice
    282                         + " target = " + mTargetDevice + " incoming = " + mIncomingDevice);
    283                 mCurrentDevice = null;
    284                 mTargetDevice = null;
    285                 mIncomingDevice = null;
    286             }
    287 
    288             boolean retValue = HANDLED;
    289             switch(message.what) {
    290                 case CONNECT:
    291                     BluetoothDevice device = (BluetoothDevice) message.obj;
    292                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
    293                                    BluetoothProfile.STATE_DISCONNECTED);
    294 
    295                     if (!connectA2dpNative(getByteAddress(device)) ) {
    296                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
    297                                        BluetoothProfile.STATE_CONNECTING);
    298                         break;
    299                     }
    300 
    301                     synchronized (A2dpStateMachine.this) {
    302                         mTargetDevice = device;
    303                         transitionTo(mPending);
    304                     }
    305                     // TODO(BT) remove CONNECT_TIMEOUT when the stack
    306                     //          sends back events consistently
    307                     sendMessageDelayed(CONNECT_TIMEOUT, 30000);
    308                     break;
    309                 case DISCONNECT:
    310                     // ignore
    311                     break;
    312                 case STACK_EVENT:
    313                     StackEvent event = (StackEvent) message.obj;
    314                     switch (event.type) {
    315                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
    316                             processConnectionEvent(event.valueInt, event.device);
    317                             break;
    318                         default:
    319                             loge("Unexpected stack event: " + event.type);
    320                             break;
    321                     }
    322                     break;
    323                 default:
    324                     return NOT_HANDLED;
    325             }
    326             return retValue;
    327         }
    328 
    329         @Override
    330         public void exit() {
    331             log("Exit Disconnected: " + getCurrentMessage().what);
    332         }
    333 
    334         // in Disconnected state
    335         private void processConnectionEvent(int state, BluetoothDevice device) {
    336             switch (state) {
    337             case CONNECTION_STATE_DISCONNECTED:
    338                 logw("Ignore HF DISCONNECTED event, device: " + device);
    339                 break;
    340             case CONNECTION_STATE_CONNECTING:
    341                 if (okToConnect(device)){
    342                     logi("Incoming A2DP accepted");
    343                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
    344                                              BluetoothProfile.STATE_DISCONNECTED);
    345                     synchronized (A2dpStateMachine.this) {
    346                         mIncomingDevice = device;
    347                         transitionTo(mPending);
    348                     }
    349                 } else {
    350                     //reject the connection and stay in Disconnected state itself
    351                     logi("Incoming A2DP rejected");
    352                     disconnectA2dpNative(getByteAddress(device));
    353                 }
    354                 break;
    355             case CONNECTION_STATE_CONNECTED:
    356                 logw("A2DP Connected from Disconnected state");
    357                 if (okToConnect(device)){
    358                     logi("Incoming A2DP accepted");
    359                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
    360                                              BluetoothProfile.STATE_DISCONNECTED);
    361                     synchronized (A2dpStateMachine.this) {
    362                         mCurrentDevice = device;
    363                         transitionTo(mConnected);
    364                     }
    365                 } else {
    366                     //reject the connection and stay in Disconnected state itself
    367                     logi("Incoming A2DP rejected");
    368                     disconnectA2dpNative(getByteAddress(device));
    369                 }
    370                 break;
    371             case CONNECTION_STATE_DISCONNECTING:
    372                 logw("Ignore A2dp DISCONNECTING event, device: " + device);
    373                 break;
    374             default:
    375                 loge("Incorrect state: " + state);
    376                 break;
    377             }
    378         }
    379     }
    380 
    381     private class Pending extends State {
    382         @Override
    383         public void enter() {
    384             log("Enter Pending: " + getCurrentMessage().what);
    385             if (mTargetDevice != null && mIncomingDevice != null) {
    386                 loge("ERROR: enter() inconsistent state in Pending: current = " + mCurrentDevice
    387                         + " target = " + mTargetDevice + " incoming = " + mIncomingDevice);
    388             }
    389         }
    390 
    391         @Override
    392         public boolean processMessage(Message message) {
    393             log("Pending process message: " + message.what);
    394 
    395             boolean retValue = HANDLED;
    396             switch(message.what) {
    397                 case CONNECT:
    398                     deferMessage(message);
    399                     break;
    400                 case CONNECT_TIMEOUT:
    401                     onConnectionStateChanged(CONNECTION_STATE_DISCONNECTED,
    402                                              getByteAddress(mTargetDevice));
    403                     break;
    404                 case DISCONNECT:
    405                     BluetoothDevice device = (BluetoothDevice) message.obj;
    406                     if (mCurrentDevice != null && mTargetDevice != null &&
    407                         mTargetDevice.equals(device) ) {
    408                         // cancel connection to the mTargetDevice
    409                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
    410                                        BluetoothProfile.STATE_CONNECTING);
    411                         synchronized (A2dpStateMachine.this) {
    412                             mTargetDevice = null;
    413                         }
    414                     } else {
    415                         deferMessage(message);
    416                     }
    417                     break;
    418                 case STACK_EVENT:
    419                     StackEvent event = (StackEvent) message.obj;
    420                     switch (event.type) {
    421                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
    422                             removeMessages(CONNECT_TIMEOUT);
    423                             processConnectionEvent(event.valueInt, event.device);
    424                             break;
    425                         default:
    426                             loge("Unexpected stack event: " + event.type);
    427                             break;
    428                     }
    429                     break;
    430                 default:
    431                     return NOT_HANDLED;
    432             }
    433             return retValue;
    434         }
    435 
    436         // in Pending state
    437         private void processConnectionEvent(int state, BluetoothDevice device) {
    438             switch (state) {
    439                 case CONNECTION_STATE_DISCONNECTED:
    440                     if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
    441                         broadcastConnectionState(mCurrentDevice,
    442                                                  BluetoothProfile.STATE_DISCONNECTED,
    443                                                  BluetoothProfile.STATE_DISCONNECTING);
    444                         synchronized (A2dpStateMachine.this) {
    445                             mCurrentDevice = null;
    446                         }
    447 
    448                         if (mTargetDevice != null) {
    449                             if (!connectA2dpNative(getByteAddress(mTargetDevice))) {
    450                                 broadcastConnectionState(mTargetDevice,
    451                                                          BluetoothProfile.STATE_DISCONNECTED,
    452                                                          BluetoothProfile.STATE_CONNECTING);
    453                                 synchronized (A2dpStateMachine.this) {
    454                                     mTargetDevice = null;
    455                                     transitionTo(mDisconnected);
    456                                 }
    457                             }
    458                         } else {
    459                             synchronized (A2dpStateMachine.this) {
    460                                 mIncomingDevice = null;
    461                                 transitionTo(mDisconnected);
    462                             }
    463                         }
    464                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
    465                         // outgoing connection failed
    466                         broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
    467                                                  BluetoothProfile.STATE_CONNECTING);
    468                         // check if there is some incoming connection request
    469                         if (mIncomingDevice != null) {
    470                             logi("disconnect for outgoing in pending state");
    471                             synchronized (A2dpStateMachine.this) {
    472                                 mTargetDevice = null;
    473                             }
    474                             break;
    475                         }
    476                         synchronized (A2dpStateMachine.this) {
    477                             mTargetDevice = null;
    478                             transitionTo(mDisconnected);
    479                         }
    480                     } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
    481                         broadcastConnectionState(mIncomingDevice,
    482                                                  BluetoothProfile.STATE_DISCONNECTED,
    483                                                  BluetoothProfile.STATE_CONNECTING);
    484                         synchronized (A2dpStateMachine.this) {
    485                             mIncomingDevice = null;
    486                             transitionTo(mDisconnected);
    487                         }
    488                     } else {
    489                         loge("Unknown device Disconnected: " + device);
    490                     }
    491                     break;
    492             case CONNECTION_STATE_CONNECTED:
    493                 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
    494                     // disconnection failed
    495                     broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_CONNECTED,
    496                                              BluetoothProfile.STATE_DISCONNECTING);
    497                     if (mTargetDevice != null) {
    498                         broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_DISCONNECTED,
    499                                                  BluetoothProfile.STATE_CONNECTING);
    500                     }
    501                     synchronized (A2dpStateMachine.this) {
    502                         mTargetDevice = null;
    503                         transitionTo(mConnected);
    504                     }
    505                 } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
    506                     broadcastConnectionState(mTargetDevice, BluetoothProfile.STATE_CONNECTED,
    507                                              BluetoothProfile.STATE_CONNECTING);
    508                     synchronized (A2dpStateMachine.this) {
    509                         mCurrentDevice = mTargetDevice;
    510                         mTargetDevice = null;
    511                         transitionTo(mConnected);
    512                     }
    513                 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
    514                     broadcastConnectionState(mIncomingDevice, BluetoothProfile.STATE_CONNECTED,
    515                                              BluetoothProfile.STATE_CONNECTING);
    516                     // check for a2dp connection allowed for this device in race condition
    517                     if (okToConnect(mIncomingDevice)) {
    518                         logi("Ready to connect incoming Connection from pending state");
    519                         synchronized (A2dpStateMachine.this) {
    520                             mCurrentDevice = mIncomingDevice;
    521                             mIncomingDevice = null;
    522                             transitionTo(mConnected);
    523                         }
    524                     } else {
    525                         // A2dp connection unchecked for this device
    526                         loge("Incoming A2DP rejected from pending state");
    527                         disconnectA2dpNative(getByteAddress(device));
    528                     }
    529                 } else {
    530                     loge("Unknown device Connected: " + device);
    531                     // something is wrong here, but sync our state with stack
    532                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
    533                                              BluetoothProfile.STATE_DISCONNECTED);
    534                     synchronized (A2dpStateMachine.this) {
    535                         mCurrentDevice = device;
    536                         mTargetDevice = null;
    537                         mIncomingDevice = null;
    538                         transitionTo(mConnected);
    539                     }
    540                 }
    541                 break;
    542             case CONNECTION_STATE_CONNECTING:
    543                 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
    544                     log("current device tries to connect back");
    545                     // TODO(BT) ignore or reject
    546                 } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
    547                     // The stack is connecting to target device or
    548                     // there is an incoming connection from the target device at the same time
    549                     // we already broadcasted the intent, doing nothing here
    550                     log("Stack and target device are connecting");
    551                 }
    552                 else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
    553                     loge("Another connecting event on the incoming device");
    554                 } else {
    555                     // We get an incoming connecting request while Pending
    556                     // TODO(BT) is stack handing this case? let's ignore it for now
    557                     log("Incoming connection while pending, accept it");
    558                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
    559                                              BluetoothProfile.STATE_DISCONNECTED);
    560                     mIncomingDevice = device;
    561                 }
    562                 break;
    563             case CONNECTION_STATE_DISCONNECTING:
    564                 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
    565                     // we already broadcasted the intent, doing nothing here
    566                     if (DBG) {
    567                         log("stack is disconnecting mCurrentDevice");
    568                     }
    569                 } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
    570                     loge("TargetDevice is getting disconnected");
    571                 } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
    572                     loge("IncomingDevice is getting disconnected");
    573                 } else {
    574                     loge("Disconnecting unknow device: " + device);
    575                 }
    576                 break;
    577             default:
    578                 loge("Incorrect state: " + state);
    579                 break;
    580             }
    581         }
    582 
    583     }
    584 
    585     private class Connected extends State {
    586         @Override
    587         public void enter() {
    588             // Remove pending connection attempts that were deferred during the pending
    589             // state. This is to prevent auto connect attempts from disconnecting
    590             // devices that previously successfully connected.
    591             // TODO: This needs to check for multiple A2DP connections, once supported...
    592             removeDeferredMessages(CONNECT);
    593 
    594             log("Enter Connected: " + getCurrentMessage().what);
    595             if (mTargetDevice != null || mIncomingDevice != null) {
    596                 loge("ERROR: enter() inconsistent state in Connected: current = " + mCurrentDevice
    597                         + " target = " + mTargetDevice + " incoming = " + mIncomingDevice);
    598             }
    599 
    600             // Upon connected, the audio starts out as stopped
    601             broadcastAudioState(mCurrentDevice, BluetoothA2dp.STATE_NOT_PLAYING,
    602                                 BluetoothA2dp.STATE_PLAYING);
    603         }
    604 
    605         @Override
    606         public boolean processMessage(Message message) {
    607             log("Connected process message: " + message.what);
    608             if (mCurrentDevice == null) {
    609                 loge("ERROR: mCurrentDevice is null in Connected");
    610                 return NOT_HANDLED;
    611             }
    612 
    613             boolean retValue = HANDLED;
    614             switch(message.what) {
    615                 case CONNECT:
    616                 {
    617                     BluetoothDevice device = (BluetoothDevice) message.obj;
    618                     if (mCurrentDevice.equals(device)) {
    619                         break;
    620                     }
    621 
    622                     broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTING,
    623                                    BluetoothProfile.STATE_DISCONNECTED);
    624                     if (!disconnectA2dpNative(getByteAddress(mCurrentDevice))) {
    625                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
    626                                        BluetoothProfile.STATE_CONNECTING);
    627                         break;
    628                     } else {
    629                         broadcastConnectionState(mCurrentDevice,
    630                                 BluetoothProfile.STATE_DISCONNECTING,
    631                                 BluetoothProfile.STATE_CONNECTED);
    632                     }
    633 
    634                     synchronized (A2dpStateMachine.this) {
    635                         mTargetDevice = device;
    636                         transitionTo(mPending);
    637                     }
    638                 }
    639                     break;
    640                 case DISCONNECT:
    641                 {
    642                     BluetoothDevice device = (BluetoothDevice) message.obj;
    643                     if (!mCurrentDevice.equals(device)) {
    644                         break;
    645                     }
    646                     broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTING,
    647                                    BluetoothProfile.STATE_CONNECTED);
    648                     if (!disconnectA2dpNative(getByteAddress(device))) {
    649                         broadcastConnectionState(device, BluetoothProfile.STATE_CONNECTED,
    650                                 BluetoothProfile.STATE_DISCONNECTING);
    651                         break;
    652                     }
    653                     synchronized (A2dpStateMachine.this) {
    654                         transitionTo(mPending);
    655                     }
    656                 }
    657                     break;
    658                 case STACK_EVENT:
    659                     StackEvent event = (StackEvent) message.obj;
    660                     switch (event.type) {
    661                         case EVENT_TYPE_CONNECTION_STATE_CHANGED:
    662                             processConnectionEvent(event.valueInt, event.device);
    663                             break;
    664                         case EVENT_TYPE_AUDIO_STATE_CHANGED:
    665                             processAudioStateEvent(event.valueInt, event.device);
    666                             break;
    667                         default:
    668                             loge("Unexpected stack event: " + event.type);
    669                             break;
    670                     }
    671                     break;
    672                 default:
    673                     return NOT_HANDLED;
    674             }
    675             return retValue;
    676         }
    677 
    678         // in Connected state
    679         private void processConnectionEvent(int state, BluetoothDevice device) {
    680             switch (state) {
    681                 case CONNECTION_STATE_DISCONNECTED:
    682                     if (mCurrentDevice.equals(device)) {
    683                         broadcastConnectionState(mCurrentDevice, BluetoothProfile.STATE_DISCONNECTED,
    684                                                  BluetoothProfile.STATE_CONNECTED);
    685                         synchronized (A2dpStateMachine.this) {
    686                             mCurrentDevice = null;
    687                             transitionTo(mDisconnected);
    688                         }
    689                     } else if (mTargetDevice != null && mTargetDevice.equals(device)) {
    690                         broadcastConnectionState(device, BluetoothProfile.STATE_DISCONNECTED,
    691                                                  BluetoothProfile.STATE_CONNECTING);
    692                         synchronized (A2dpStateMachine.this) {
    693                             mTargetDevice = null;
    694                         }
    695                         logi("Disconnected from mTargetDevice in connected state device: " + device);
    696                     } else {
    697                         loge("Disconnected from unknown device: " + device);
    698                     }
    699                     break;
    700               default:
    701                   loge("Connection State Device: " + device + " bad state: " + state);
    702                   break;
    703             }
    704         }
    705         private void processAudioStateEvent(int state, BluetoothDevice device) {
    706             if (!mCurrentDevice.equals(device)) {
    707                 loge("Audio State Device:" + device + "is different from ConnectedDevice:" +
    708                                                            mCurrentDevice);
    709                 return;
    710             }
    711             switch (state) {
    712                 case AUDIO_STATE_STARTED:
    713                     if (mPlayingA2dpDevice == null) {
    714                         mPlayingA2dpDevice = device;
    715                         mService.setAvrcpAudioState(BluetoothA2dp.STATE_PLAYING);
    716                         broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING,
    717                                             BluetoothA2dp.STATE_NOT_PLAYING);
    718                     }
    719                     break;
    720                 case AUDIO_STATE_REMOTE_SUSPEND:
    721                 case AUDIO_STATE_STOPPED:
    722                     if (mPlayingA2dpDevice != null) {
    723                         mPlayingA2dpDevice = null;
    724                         mService.setAvrcpAudioState(BluetoothA2dp.STATE_NOT_PLAYING);
    725                         broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING,
    726                                             BluetoothA2dp.STATE_PLAYING);
    727                     }
    728                     break;
    729                 default:
    730                   loge("Audio State Device: " + device + " bad state: " + state);
    731                   break;
    732             }
    733         }
    734     }
    735 
    736     int getConnectionState(BluetoothDevice device) {
    737         if (getCurrentState() == mDisconnected) {
    738             return BluetoothProfile.STATE_DISCONNECTED;
    739         }
    740 
    741         synchronized (this) {
    742             IState currentState = getCurrentState();
    743             if (currentState == mPending) {
    744                 if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
    745                     return BluetoothProfile.STATE_CONNECTING;
    746                 }
    747                 if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
    748                     return BluetoothProfile.STATE_DISCONNECTING;
    749                 }
    750                 if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
    751                     return BluetoothProfile.STATE_CONNECTING; // incoming connection
    752                 }
    753                 return BluetoothProfile.STATE_DISCONNECTED;
    754             }
    755 
    756             if (currentState == mConnected) {
    757                 if (mCurrentDevice.equals(device)) {
    758                     return BluetoothProfile.STATE_CONNECTED;
    759                 }
    760                 return BluetoothProfile.STATE_DISCONNECTED;
    761             } else {
    762                 loge("Bad currentState: " + currentState);
    763                 return BluetoothProfile.STATE_DISCONNECTED;
    764             }
    765         }
    766     }
    767 
    768     List<BluetoothDevice> getConnectedDevices() {
    769         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
    770         synchronized (this) {
    771             if (getCurrentState() == mConnected) {
    772                 devices.add(mCurrentDevice);
    773             }
    774         }
    775         return devices;
    776     }
    777 
    778     boolean isPlaying(BluetoothDevice device) {
    779         synchronized (this) {
    780             if (device.equals(mPlayingA2dpDevice)) {
    781                 return true;
    782             }
    783         }
    784         return false;
    785     }
    786 
    787     BluetoothCodecStatus getCodecStatus() {
    788         synchronized (this) {
    789             return mCodecStatus;
    790         }
    791     }
    792 
    793     private void onCodecConfigChanged(BluetoothCodecConfig newCodecConfig,
    794             BluetoothCodecConfig[] codecsLocalCapabilities,
    795             BluetoothCodecConfig[] codecsSelectableCapabilities) {
    796         BluetoothCodecConfig prevCodecConfig = null;
    797         synchronized (this) {
    798             if (mCodecStatus != null) {
    799                 prevCodecConfig = mCodecStatus.getCodecConfig();
    800             }
    801             mCodecStatus = new BluetoothCodecStatus(
    802                     newCodecConfig, codecsLocalCapabilities, codecsSelectableCapabilities);
    803         }
    804 
    805         Intent intent = new Intent(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
    806         intent.putExtra(BluetoothCodecStatus.EXTRA_CODEC_STATUS, mCodecStatus);
    807         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    808 
    809         log("A2DP Codec Config: " + prevCodecConfig + "->" + newCodecConfig);
    810         for (BluetoothCodecConfig codecConfig : codecsLocalCapabilities) {
    811             log("A2DP Codec Local Capability: " + codecConfig);
    812         }
    813         for (BluetoothCodecConfig codecConfig : codecsSelectableCapabilities) {
    814             log("A2DP Codec Selectable Capability: " + codecConfig);
    815         }
    816 
    817         // Inform the Audio Service about the codec configuration change,
    818         // so the Audio Service can reset accordingly the audio feeding
    819         // parameters in the Audio HAL to the Bluetooth stack.
    820         if (!newCodecConfig.sameAudioFeedingParameters(prevCodecConfig) && (mCurrentDevice != null)
    821                 && (getCurrentState() == mConnected)) {
    822             // Add the device only if it is currently connected
    823             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mCurrentDevice);
    824             mAudioManager.handleBluetoothA2dpDeviceConfigChange(mCurrentDevice);
    825         }
    826         mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM);
    827     }
    828 
    829     void setCodecConfigPreference(BluetoothCodecConfig codecConfig) {
    830         BluetoothCodecConfig[] codecConfigArray = new BluetoothCodecConfig[1];
    831         codecConfigArray[0] = codecConfig;
    832         setCodecConfigPreferenceNative(codecConfigArray);
    833     }
    834 
    835     void enableOptionalCodecs() {
    836         BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities();
    837         if (codecConfigArray == null) {
    838             return;
    839         }
    840 
    841         // Set the mandatory codec's priority to default, and remove the rest
    842         for (int i = 0; i < codecConfigArray.length; i++) {
    843             BluetoothCodecConfig codecConfig = codecConfigArray[i];
    844             if (!codecConfig.isMandatoryCodec()) {
    845                 codecConfigArray[i] = null;
    846             }
    847         }
    848 
    849         setCodecConfigPreferenceNative(codecConfigArray);
    850     }
    851 
    852     void disableOptionalCodecs() {
    853         BluetoothCodecConfig[] codecConfigArray = assignCodecConfigPriorities();
    854         if (codecConfigArray == null) {
    855             return;
    856         }
    857         // Set the mandatory codec's priority to highest, and ignore the rest
    858         for (int i = 0; i < codecConfigArray.length; i++) {
    859             BluetoothCodecConfig codecConfig = codecConfigArray[i];
    860             if (codecConfig.isMandatoryCodec()) {
    861                 codecConfig.setCodecPriority(BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST);
    862             } else {
    863                 codecConfigArray[i] = null;
    864             }
    865         }
    866         setCodecConfigPreferenceNative(codecConfigArray);
    867     }
    868 
    869     boolean okToConnect(BluetoothDevice device) {
    870         AdapterService adapterService = AdapterService.getAdapterService();
    871         int priority = mService.getPriority(device);
    872         boolean ret = false;
    873         //check if this is an incoming connection in Quiet mode.
    874         if((adapterService == null) ||
    875            ((adapterService.isQuietModeEnabled() == true) &&
    876            (mTargetDevice == null))){
    877             ret = false;
    878         }
    879         // check priority and accept or reject the connection. if priority is undefined
    880         // it is likely that our SDP has not completed and peer is initiating the
    881         // connection. Allow this connection, provided the device is bonded
    882         else if((BluetoothProfile.PRIORITY_OFF < priority) ||
    883                 ((BluetoothProfile.PRIORITY_UNDEFINED == priority) &&
    884                 (device.getBondState() != BluetoothDevice.BOND_NONE))){
    885             ret= true;
    886         }
    887         return ret;
    888     }
    889 
    890     synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    891         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
    892         Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
    893         int connectionState;
    894 
    895         for (BluetoothDevice device : bondedDevices) {
    896             ParcelUuid[] featureUuids = device.getUuids();
    897             if (!BluetoothUuid.isUuidPresent(featureUuids, BluetoothUuid.AudioSink)) {
    898                 continue;
    899             }
    900             connectionState = getConnectionState(device);
    901             for(int i = 0; i < states.length; i++) {
    902                 if (connectionState == states[i]) {
    903                     deviceList.add(device);
    904                 }
    905             }
    906         }
    907         return deviceList;
    908     }
    909 
    910 
    911     // This method does not check for error conditon (newState == prevState)
    912     private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
    913         mAudioManager.setBluetoothA2dpDeviceConnectionState(
    914                 device, newState, BluetoothProfile.A2DP);
    915 
    916         mWakeLock.acquire();
    917         mIntentBroadcastHandler.sendMessage(mIntentBroadcastHandler.obtainMessage(
    918                 MSG_CONNECTION_STATE_CHANGED, prevState, newState, device));
    919     }
    920 
    921     private void broadcastAudioState(BluetoothDevice device, int state, int prevState) {
    922         Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
    923         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    924         intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
    925         intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
    926         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
    927         mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM);
    928 
    929         log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state);
    930     }
    931 
    932     private byte[] getByteAddress(BluetoothDevice device) {
    933         return Utils.getBytesFromAddress(device.getAddress());
    934     }
    935 
    936     private void onConnectionStateChanged(int state, byte[] address) {
    937         StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
    938         event.valueInt = state;
    939         event.device = getDevice(address);
    940         sendMessage(STACK_EVENT, event);
    941     }
    942 
    943     private void onAudioStateChanged(int state, byte[] address) {
    944         StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
    945         event.valueInt = state;
    946         event.device = getDevice(address);
    947         sendMessage(STACK_EVENT, event);
    948     }
    949     private BluetoothDevice getDevice(byte[] address) {
    950         return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
    951     }
    952 
    953     private class StackEvent {
    954         int type = EVENT_TYPE_NONE;
    955         int valueInt = 0;
    956         BluetoothDevice device = null;
    957 
    958         private StackEvent(int type) {
    959             this.type = type;
    960         }
    961     }
    962     /** Handles A2DP connection state change intent broadcasts. */
    963     private class IntentBroadcastHandler extends Handler {
    964 
    965         private void onConnectionStateChanged(BluetoothDevice device, int prevState, int state) {
    966             Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
    967             intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
    968             intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
    969             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    970             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
    971                     | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
    972             mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
    973             log("Connection state " + device + ": " + prevState + "->" + state);
    974         }
    975 
    976         @Override
    977         public void handleMessage(Message msg) {
    978             switch (msg.what) {
    979                 case MSG_CONNECTION_STATE_CHANGED:
    980                     onConnectionStateChanged((BluetoothDevice) msg.obj, msg.arg1, msg.arg2);
    981                     mWakeLock.release();
    982                     break;
    983             }
    984         }
    985     }
    986 
    987     public void dump(StringBuilder sb) {
    988         ProfileService.println(sb, "mCurrentDevice: " + mCurrentDevice);
    989         ProfileService.println(sb, "mTargetDevice: " + mTargetDevice);
    990         ProfileService.println(sb, "mIncomingDevice: " + mIncomingDevice);
    991         ProfileService.println(sb, "mPlayingA2dpDevice: " + mPlayingA2dpDevice);
    992         ProfileService.println(sb, "StateMachine: " + this.toString());
    993     }
    994 
    995     // Event types for STACK_EVENT message
    996     final private static int EVENT_TYPE_NONE = 0;
    997     final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
    998     final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
    999 
   1000    // Do not modify without updating the HAL bt_av.h files.
   1001 
   1002     // match up with btav_connection_state_t enum of bt_av.h
   1003     final static int CONNECTION_STATE_DISCONNECTED = 0;
   1004     final static int CONNECTION_STATE_CONNECTING = 1;
   1005     final static int CONNECTION_STATE_CONNECTED = 2;
   1006     final static int CONNECTION_STATE_DISCONNECTING = 3;
   1007 
   1008     // match up with btav_audio_state_t enum of bt_av.h
   1009     final static int AUDIO_STATE_REMOTE_SUSPEND = 0;
   1010     final static int AUDIO_STATE_STOPPED = 1;
   1011     final static int AUDIO_STATE_STARTED = 2;
   1012 
   1013     private native static void classInitNative();
   1014     private native void initNative(BluetoothCodecConfig[] codecConfigPriorites);
   1015     private native void cleanupNative();
   1016     private native boolean connectA2dpNative(byte[] address);
   1017     private native boolean disconnectA2dpNative(byte[] address);
   1018     private native boolean setCodecConfigPreferenceNative(BluetoothCodecConfig[] codecConfigArray);
   1019 }
   1020