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