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