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