A2dpStateMachine.java revision ede67c26e7b2564ea35db6d9b3027a269c150e13
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 String TAG = "A2dpStateMachine";
62    private static final boolean DBG = false;
63
64    static final int CONNECT = 1;
65    static final int DISCONNECT = 2;
66    private static final int STACK_EVENT = 101;
67    private static final int CONNECT_TIMEOUT = 201;
68
69    private Disconnected mDisconnected;
70    private Pending mPending;
71    private Connected mConnected;
72
73    private A2dpService mService;
74    private Context mContext;
75    private BluetoothAdapter mAdapter;
76    private final AudioManager mAudioManager;
77    private IntentBroadcastHandler mIntentBroadcastHandler;
78    private final WakeLock mWakeLock;
79
80    private static final int MSG_CONNECTION_STATE_CHANGED = 0;
81
82    private static final ParcelUuid[] A2DP_UUIDS = {
83        BluetoothUuid.AudioSink
84    };
85
86    // mCurrentDevice is the device connected before the state changes
87    // mTargetDevice is the device to be connected
88    // mIncomingDevice is the device connecting to us, valid only in Pending state
89    //                when mIncomingDevice is not null, both mCurrentDevice
90    //                  and mTargetDevice are null
91    //                when either mCurrentDevice or mTargetDevice is not null,
92    //                  mIncomingDevice is null
93    // Stable states
94    //   No connection, Disconnected state
95    //                  both mCurrentDevice and mTargetDevice are null
96    //   Connected, Connected state
97    //              mCurrentDevice is not null, mTargetDevice is null
98    // Interim states
99    //   Connecting to a device, Pending
100    //                           mCurrentDevice is null, mTargetDevice is not null
101    //   Disconnecting device, Connecting to new device
102    //     Pending
103    //     Both mCurrentDevice and mTargetDevice are not null
104    //   Disconnecting device Pending
105    //                        mCurrentDevice is not null, mTargetDevice is null
106    //   Incoming connections Pending
107    //                        Both mCurrentDevice and mTargetDevice are null
108    private BluetoothDevice mCurrentDevice = null;
109    private BluetoothDevice mTargetDevice = null;
110    private BluetoothDevice mIncomingDevice = null;
111    private BluetoothDevice mPlayingA2dpDevice = null;
112
113
114    static {
115        classInitNative();
116    }
117
118    private A2dpStateMachine(A2dpService svc, Context context) {
119        super(TAG);
120        mService = svc;
121        mContext = context;
122        mAdapter = BluetoothAdapter.getDefaultAdapter();
123
124        initNative();
125
126        mDisconnected = new Disconnected();
127        mPending = new Pending();
128        mConnected = new Connected();
129
130        addState(mDisconnected);
131        addState(mPending);
132        addState(mConnected);
133
134        setInitialState(mDisconnected);
135
136        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
137        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BluetoothA2dpService");
138
139        mIntentBroadcastHandler = new IntentBroadcastHandler();
140
141        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
142
143    }
144
145    static A2dpStateMachine make(A2dpService svc, Context context) {
146        Log.d(TAG, "make");
147        A2dpStateMachine a2dpSm = new A2dpStateMachine(svc, context);
148        a2dpSm.start();
149        return a2dpSm;
150    }
151
152    public void doQuit() {
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                Log.e(TAG, "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                            Log.e(TAG, "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                Log.w(TAG, "Ignore HF DISCONNECTED event, device: " + device);
225                break;
226            case CONNECTION_STATE_CONNECTING:
227                if (okToConnect(device)){
228                    Log.i(TAG,"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                    Log.i(TAG,"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                Log.w(TAG, "A2DP Connected from Disconnected state");
249                if (okToConnect(device)){
250                    Log.i(TAG,"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                    Log.i(TAG,"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                Log.w(TAG, "Ignore HF DISCONNECTING event, device: " + device);
271                break;
272            default:
273                Log.e(TAG, "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                            Log.e(TAG, "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                        Log.e(TAG, "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                    Log.e(TAG, "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                    Log.e(TAG, "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                    Log.e(TAG, "TargetDevice is getting disconnected");
446                } else if (mIncomingDevice != null && mIncomingDevice.equals(device)) {
447                    Log.e(TAG, "IncomingDevice is getting disconnected");
448                } else {
449                    Log.e(TAG, "Disconnecting unknow device: " + device);
450                }
451                break;
452            default:
453                Log.e(TAG, "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                Log.e(TAG, "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                            Log.e(TAG, "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                        Log.e(TAG, "Disconnected from unknown device: " + device);
549                    }
550                    break;
551              default:
552                  Log.e(TAG, "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                Log.e(TAG, "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                       broadcastAudioState(device, BluetoothA2dp.STATE_PLAYING,
567                                           BluetoothA2dp.STATE_NOT_PLAYING);
568                    }
569                    break;
570                case AUDIO_STATE_STOPPED:
571                    if(mPlayingA2dpDevice != null) {
572                        mPlayingA2dpDevice = null;
573                        broadcastAudioState(device, BluetoothA2dp.STATE_NOT_PLAYING,
574                                            BluetoothA2dp.STATE_PLAYING);
575                    }
576                    break;
577                default:
578                  Log.e(TAG, "Audio State Device: " + device + " bad state: " + state);
579                  break;
580            }
581        }
582    }
583
584    int getConnectionState(BluetoothDevice device) {
585        if (getCurrentState() == mDisconnected) {
586            return BluetoothProfile.STATE_DISCONNECTED;
587        }
588
589        synchronized (this) {
590            IState currentState = getCurrentState();
591            if (currentState == mPending) {
592                if ((mTargetDevice != null) && mTargetDevice.equals(device)) {
593                    return BluetoothProfile.STATE_CONNECTING;
594                }
595                if ((mCurrentDevice != null) && mCurrentDevice.equals(device)) {
596                    return BluetoothProfile.STATE_DISCONNECTING;
597                }
598                if ((mIncomingDevice != null) && mIncomingDevice.equals(device)) {
599                    return BluetoothProfile.STATE_CONNECTING; // incoming connection
600                }
601                return BluetoothProfile.STATE_DISCONNECTED;
602            }
603
604            if (currentState == mConnected) {
605                if (mCurrentDevice.equals(device)) {
606                    return BluetoothProfile.STATE_CONNECTED;
607                }
608                return BluetoothProfile.STATE_DISCONNECTED;
609            } else {
610                Log.e(TAG, "Bad currentState: " + currentState);
611                return BluetoothProfile.STATE_DISCONNECTED;
612            }
613        }
614    }
615
616    List<BluetoothDevice> getConnectedDevices() {
617        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
618        synchronized(this) {
619            if (getCurrentState() == mConnected) {
620                devices.add(mCurrentDevice);
621            }
622        }
623        return devices;
624    }
625
626    boolean isPlaying(BluetoothDevice device) {
627        synchronized(this) {
628            if (device.equals(mPlayingA2dpDevice)) {
629                return true;
630            }
631        }
632        return false;
633    }
634
635    boolean okToConnect(BluetoothDevice device) {
636        AdapterService adapterService = AdapterService.getAdapterService();
637        int priority = mService.getPriority(device);
638        boolean ret = false;
639        //check if this is an incoming connection in Quiet mode.
640        if((adapterService == null) ||
641           ((adapterService.isQuietModeEnabled() == true) &&
642           (mTargetDevice == null))){
643            ret = false;
644        }
645        // check priority and accept or reject the connection. if priority is undefined
646        // it is likely that our SDP has not completed and peer is initiating the
647        // connection. Allow this connection, provided the device is bonded
648        else if((BluetoothProfile.PRIORITY_OFF < priority) ||
649                ((BluetoothProfile.PRIORITY_UNDEFINED == priority) &&
650                (device.getBondState() != BluetoothDevice.BOND_NONE))){
651            ret= true;
652        }
653        return ret;
654    }
655
656    synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
657        List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
658        Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
659        int connectionState;
660
661        for (BluetoothDevice device : bondedDevices) {
662            ParcelUuid[] featureUuids = device.getUuids();
663            if (!BluetoothUuid.containsAnyUuid(featureUuids, A2DP_UUIDS)) {
664                continue;
665            }
666            connectionState = getConnectionState(device);
667            for(int i = 0; i < states.length; i++) {
668                if (connectionState == states[i]) {
669                    deviceList.add(device);
670                }
671            }
672        }
673        return deviceList;
674    }
675
676
677    // This method does not check for error conditon (newState == prevState)
678    private void broadcastConnectionState(BluetoothDevice device, int newState, int prevState) {
679
680        int delay = mAudioManager.setBluetoothA2dpDeviceConnectionState(device, newState);
681
682        mWakeLock.acquire();
683        mIntentBroadcastHandler.sendMessageDelayed(mIntentBroadcastHandler.obtainMessage(
684                                                        MSG_CONNECTION_STATE_CHANGED,
685                                                        prevState,
686                                                        newState,
687                                                        device),
688                                                        delay);
689    }
690
691    private void broadcastAudioState(BluetoothDevice device, int state, int prevState) {
692        Intent intent = new Intent(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
693        intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
694        intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
695        intent.putExtra(BluetoothProfile.EXTRA_STATE, state);
696        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
697        mContext.sendBroadcast(intent, A2dpService.BLUETOOTH_PERM);
698
699        log("A2DP Playing state : device: " + device + " State:" + prevState + "->" + state);
700    }
701
702    private byte[] getByteAddress(BluetoothDevice device) {
703        return Utils.getBytesFromAddress(device.getAddress());
704    }
705
706    private void onConnectionStateChanged(int state, byte[] address) {
707        StackEvent event = new StackEvent(EVENT_TYPE_CONNECTION_STATE_CHANGED);
708        event.valueInt = state;
709        event.device = getDevice(address);
710        sendMessage(STACK_EVENT, event);
711    }
712
713    private void onAudioStateChanged(int state, byte[] address) {
714        StackEvent event = new StackEvent(EVENT_TYPE_AUDIO_STATE_CHANGED);
715        event.valueInt = state;
716        event.device = getDevice(address);
717        sendMessage(STACK_EVENT, event);
718    }
719    private BluetoothDevice getDevice(byte[] address) {
720        return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
721    }
722
723    private void log(String msg) {
724        Log.d(TAG, msg);
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
762    // Event types for STACK_EVENT message
763    final private static int EVENT_TYPE_NONE = 0;
764    final private static int EVENT_TYPE_CONNECTION_STATE_CHANGED = 1;
765    final private static int EVENT_TYPE_AUDIO_STATE_CHANGED = 2;
766
767   // Do not modify without updating the HAL bt_av.h files.
768
769    // match up with btav_connection_state_t enum of bt_av.h
770    final static int CONNECTION_STATE_DISCONNECTED = 0;
771    final static int CONNECTION_STATE_CONNECTING = 1;
772    final static int CONNECTION_STATE_CONNECTED = 2;
773    final static int CONNECTION_STATE_DISCONNECTING = 3;
774
775    // match up with btav_audio_state_t enum of bt_av.h
776    final static int AUDIO_STATE_REMOTE_SUSPEND = 0;
777    final static int AUDIO_STATE_STOPPED = 1;
778    final static int AUDIO_STATE_STARTED = 2;
779
780    private native static void classInitNative();
781    private native void initNative();
782    private native void cleanupNative();
783    private native boolean connectA2dpNative(byte[] address);
784    private native boolean disconnectA2dpNative(byte[] address);
785}
786