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