• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /**
18  * Bluetooth A2dp StateMachine
19  *                      (Disconnected)
20  *                           |    ^
21  *                   CONNECT |    | DISCONNECTED
22  *                           V    |
23  *                         (Pending)
24  *                           |    ^
25  *                 CONNECTED |    | CONNECT
26  *                           V    |
27  *                        (Connected)
28  */
29 package com.android.bluetooth.a2dp;
30 
31 import android.bluetooth.BluetoothA2dp;
32 import android.bluetooth.BluetoothAdapter;
33 import android.bluetooth.BluetoothDevice;
34 import android.bluetooth.BluetoothProfile;
35 import android.bluetooth.BluetoothUuid;
36 import android.bluetooth.IBluetooth;
37 import android.content.Context;
38 import android.media.AudioManager;
39 import android.os.Handler;
40 import android.os.Message;
41 import android.os.ParcelUuid;
42 import android.os.PowerManager;
43 import android.os.PowerManager.WakeLock;
44 import android.content.Intent;
45 import android.os.Message;
46 import android.os.RemoteException;
47 import android.os.ServiceManager;
48 import android.os.ParcelUuid;
49 import android.util.Log;
50 import com.android.bluetooth.Utils;
51 import com.android.bluetooth.btservice.AdapterService;
52 import com.android.bluetooth.btservice.ProfileService;
53 import com.android.internal.util.IState;
54 import com.android.internal.util.State;
55 import com.android.internal.util.StateMachine;
56 import java.util.ArrayList;
57 import java.util.List;
58 import java.util.Set;
59 
60 final 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 {
classInitNative()115         classInitNative();
116     }
117 
A2dpStateMachine(A2dpService svc, Context context)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 
make(A2dpService svc, Context context)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 
doQuit()152     public void doQuit() {
153         quitNow();
154     }
155 
cleanup()156     public void cleanup() {
157         cleanupNative();
158     }
159 
160         private class Disconnected extends State {
161         @Override
enter()162         public void enter() {
163             log("Enter Disconnected: " + getCurrentMessage().what);
164         }
165 
166         @Override
processMessage(Message message)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
exit()216         public void exit() {
217             log("Exit Disconnected: " + getCurrentMessage().what);
218         }
219 
220         // in Disconnected state
processConnectionEvent(int state, BluetoothDevice device)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
enter()281         public void enter() {
282             log("Enter Pending: " + getCurrentMessage().what);
283         }
284 
285         @Override
processMessage(Message message)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
processConnectionEvent(int state, BluetoothDevice device)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
enter()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
processMessage(Message message)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
processConnectionEvent(int state, BluetoothDevice device)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         }
processAudioStateEvent(int state, BluetoothDevice device)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 
getConnectionState(BluetoothDevice device)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 
getConnectedDevices()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 
isPlaying(BluetoothDevice device)626     boolean isPlaying(BluetoothDevice device) {
627         synchronized(this) {
628             if (device.equals(mPlayingA2dpDevice)) {
629                 return true;
630             }
631         }
632         return false;
633     }
634 
okToConnect(BluetoothDevice device)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 
getDevicesMatchingConnectionStates(int[] states)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)
broadcastConnectionState(BluetoothDevice device, int newState, int 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 
broadcastAudioState(BluetoothDevice device, int state, int prevState)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 
getByteAddress(BluetoothDevice device)702     private byte[] getByteAddress(BluetoothDevice device) {
703         return Utils.getBytesFromAddress(device.getAddress());
704     }
705 
onConnectionStateChanged(int state, byte[] address)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 
onAudioStateChanged(int state, byte[] address)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     }
getDevice(byte[] address)719     private BluetoothDevice getDevice(byte[] address) {
720         return mAdapter.getRemoteDevice(Utils.getAddressStringFromByte(address));
721     }
722 
log(String msg)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 
StackEvent(int type)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 
onConnectionStateChanged(BluetoothDevice device, int prevState, int state)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
handleMessage(Message msg)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 
classInitNative()780     private native static void classInitNative();
initNative()781     private native void initNative();
cleanupNative()782     private native void cleanupNative();
connectA2dpNative(byte[] address)783     private native boolean connectA2dpNative(byte[] address);
disconnectA2dpNative(byte[] address)784     private native boolean disconnectA2dpNative(byte[] address);
785 }
786