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