Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "base/basictypes.h"
9 #include "BluetoothA2dpManager.h"
11 #include <hardware/bluetooth.h>
12 #include <hardware/bt_av.h>
13 #if ANDROID_VERSION > 17
14 #include <hardware/bt_rc.h>
15 #endif
17 #include "BluetoothCommon.h"
18 #include "BluetoothService.h"
19 #include "BluetoothSocket.h"
20 #include "BluetoothUtils.h"
22 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
23 #include "mozilla/Services.h"
24 #include "mozilla/StaticPtr.h"
25 #include "MainThreadUtils.h"
26 #include "nsIObserverService.h"
27 #include "nsThreadUtils.h"
29 using namespace mozilla;
30 USING_BLUETOOTH_NAMESPACE
31 // AVRC_ID op code follows bluedroid avrc_defs.h
32 #define AVRC_ID_REWIND 0x48
33 #define AVRC_ID_FAST_FOR 0x49
34 #define AVRC_KEY_PRESS_STATE 1
35 #define AVRC_KEY_RELEASE_STATE 0
37 namespace {
38 StaticRefPtr<BluetoothA2dpManager> sBluetoothA2dpManager;
39 bool sInShutdown = false;
40 static const btav_interface_t* sBtA2dpInterface;
41 #if ANDROID_VERSION > 17
42 static const btrc_interface_t* sBtAvrcpInterface;
43 #endif
44 } // anonymous namespace
46 class SinkPropertyChangedHandler : public nsRunnable
47 {
48 public:
49 SinkPropertyChangedHandler(const BluetoothSignal& aSignal)
50 : mSignal(aSignal)
51 {
52 }
54 NS_IMETHOD
55 Run()
56 {
57 MOZ_ASSERT(NS_IsMainThread());
59 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
60 NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE);
61 a2dp->HandleSinkPropertyChanged(mSignal);
63 return NS_OK;
64 }
66 private:
67 BluetoothSignal mSignal;
68 };
70 class RequestPlayStatusTask : public nsRunnable
71 {
72 public:
73 RequestPlayStatusTask()
74 {
75 MOZ_ASSERT(!NS_IsMainThread());
76 }
78 nsresult Run()
79 {
80 MOZ_ASSERT(NS_IsMainThread());
82 BluetoothSignal signal(NS_LITERAL_STRING(REQUEST_MEDIA_PLAYSTATUS_ID),
83 NS_LITERAL_STRING(KEY_ADAPTER),
84 InfallibleTArray<BluetoothNamedValue>());
86 BluetoothService* bs = BluetoothService::Get();
87 NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
88 bs->DistributeSignal(signal);
90 return NS_OK;
91 }
92 };
94 #if ANDROID_VERSION > 17
95 class UpdateRegisterNotificationTask : public nsRunnable
96 {
97 public:
98 UpdateRegisterNotificationTask(btrc_event_id_t aEventId, uint32_t aParam)
99 : mEventId(aEventId)
100 , mParam(aParam)
101 {
102 MOZ_ASSERT(!NS_IsMainThread());
103 }
105 nsresult Run()
106 {
107 MOZ_ASSERT(NS_IsMainThread());
109 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
110 NS_ENSURE_TRUE(a2dp, NS_OK);
111 a2dp->UpdateRegisterNotification(mEventId, mParam);
112 return NS_OK;
113 }
114 private:
115 btrc_event_id_t mEventId;
116 uint32_t mParam;
117 };
119 /*
120 * This function maps attribute id and returns corresponding values
121 * Attribute id refers to btrc_media_attr_t in bt_rc.h
122 */
123 static void
124 ConvertAttributeString(int aAttrId, nsAString& aAttrStr)
125 {
126 BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
127 NS_ENSURE_TRUE_VOID(a2dp);
129 switch (aAttrId) {
130 case BTRC_MEDIA_ATTR_TITLE:
131 a2dp->GetTitle(aAttrStr);
132 break;
133 case BTRC_MEDIA_ATTR_ARTIST:
134 a2dp->GetArtist(aAttrStr);
135 break;
136 case BTRC_MEDIA_ATTR_ALBUM:
137 a2dp->GetAlbum(aAttrStr);
138 break;
139 case BTRC_MEDIA_ATTR_TRACK_NUM:
140 aAttrStr.AppendInt(a2dp->GetMediaNumber());
141 break;
142 case BTRC_MEDIA_ATTR_NUM_TRACKS:
143 aAttrStr.AppendInt(a2dp->GetTotalMediaNumber());
144 break;
145 case BTRC_MEDIA_ATTR_GENRE:
146 // TODO: we currently don't support genre from music player
147 aAttrStr.Truncate();
148 break;
149 case BTRC_MEDIA_ATTR_PLAYING_TIME:
150 aAttrStr.AppendInt(a2dp->GetDuration());
151 break;
152 }
153 }
155 class UpdateElementAttrsTask : public nsRunnable
156 {
157 public:
158 UpdateElementAttrsTask(uint8_t aNumAttr, btrc_media_attr_t* aPlayerAttrs)
159 : mNumAttr(aNumAttr)
160 , mPlayerAttrs(aPlayerAttrs)
161 {
162 MOZ_ASSERT(!NS_IsMainThread());
163 }
165 nsresult Run()
166 {
167 MOZ_ASSERT(NS_IsMainThread());
169 btrc_element_attr_val_t* attrs = new btrc_element_attr_val_t[mNumAttr];
170 for (int i = 0; i < mNumAttr; i++) {
171 nsAutoString attrText;
172 attrs[i].attr_id = mPlayerAttrs[i];
173 ConvertAttributeString(mPlayerAttrs[i], attrText);
174 strcpy((char *)attrs[i].text, NS_ConvertUTF16toUTF8(attrText).get());
175 }
177 NS_ENSURE_TRUE(sBtAvrcpInterface, NS_OK);
178 sBtAvrcpInterface->get_element_attr_rsp(mNumAttr, attrs);
180 return NS_OK;
181 }
182 private:
183 uint8_t mNumAttr;
184 btrc_media_attr_t* mPlayerAttrs;
185 };
187 class UpdatePassthroughCmdTask : public nsRunnable
188 {
189 public:
190 UpdatePassthroughCmdTask(const nsAString& aName)
191 : mName(aName)
192 {
193 MOZ_ASSERT(!NS_IsMainThread());
194 }
196 nsresult Run()
197 {
198 MOZ_ASSERT(NS_IsMainThread());
200 NS_NAMED_LITERAL_STRING(type, "media-button");
201 BroadcastSystemMessage(type, BluetoothValue(mName));
203 return NS_OK;
204 }
205 private:
206 nsString mName;
207 };
209 #endif
211 NS_IMETHODIMP
212 BluetoothA2dpManager::Observe(nsISupports* aSubject,
213 const char* aTopic,
214 const char16_t* aData)
215 {
216 MOZ_ASSERT(sBluetoothA2dpManager);
218 if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
219 HandleShutdown();
220 return NS_OK;
221 }
223 MOZ_ASSERT(false, "BluetoothA2dpManager got unexpected topic!");
224 return NS_ERROR_UNEXPECTED;
225 }
227 BluetoothA2dpManager::BluetoothA2dpManager()
228 {
229 Reset();
230 }
232 void
233 BluetoothA2dpManager::Reset()
234 {
235 ResetA2dp();
236 ResetAvrcp();
237 }
239 static void
240 AvStatusToSinkString(btav_connection_state_t aStatus, nsAString& aState)
241 {
242 nsAutoString state;
243 if (aStatus == BTAV_CONNECTION_STATE_DISCONNECTED) {
244 aState = NS_LITERAL_STRING("disconnected");
245 } else if (aStatus == BTAV_CONNECTION_STATE_CONNECTING) {
246 aState = NS_LITERAL_STRING("connecting");
247 } else if (aStatus == BTAV_CONNECTION_STATE_CONNECTED) {
248 aState = NS_LITERAL_STRING("connected");
249 } else if (aStatus == BTAV_CONNECTION_STATE_DISCONNECTING) {
250 aState = NS_LITERAL_STRING("disconnecting");
251 } else {
252 BT_WARNING("Unknown sink state");
253 }
254 }
256 static void
257 A2dpConnectionStateCallback(btav_connection_state_t aState,
258 bt_bdaddr_t* aBdAddress)
259 {
260 MOZ_ASSERT(!NS_IsMainThread());
262 nsString remoteDeviceBdAddress;
263 BdAddressTypeToString(aBdAddress, remoteDeviceBdAddress);
265 nsString a2dpState;
266 AvStatusToSinkString(aState, a2dpState);
268 InfallibleTArray<BluetoothNamedValue> props;
269 BT_APPEND_NAMED_VALUE(props, "State", a2dpState);
271 BluetoothSignal signal(NS_LITERAL_STRING("AudioSink"),
272 remoteDeviceBdAddress, props);
273 NS_DispatchToMainThread(new SinkPropertyChangedHandler(signal));
274 }
276 static void
277 A2dpAudioStateCallback(btav_audio_state_t aState,
278 bt_bdaddr_t* aBdAddress)
279 {
280 MOZ_ASSERT(!NS_IsMainThread());
282 nsString remoteDeviceBdAddress;
283 BdAddressTypeToString(aBdAddress, remoteDeviceBdAddress);
285 nsString a2dpState;
287 if (aState == BTAV_AUDIO_STATE_STARTED) {
288 a2dpState = NS_LITERAL_STRING("playing");
289 } else if (aState == BTAV_AUDIO_STATE_STOPPED) {
290 // for avdtp state stop stream
291 a2dpState = NS_LITERAL_STRING("connected");
292 } else if (aState == BTAV_AUDIO_STATE_REMOTE_SUSPEND) {
293 // for avdtp state suspend stream from remote side
294 a2dpState = NS_LITERAL_STRING("connected");
295 }
297 InfallibleTArray<BluetoothNamedValue> props;
298 BT_APPEND_NAMED_VALUE(props, "State", a2dpState);
300 BluetoothSignal signal(NS_LITERAL_STRING("AudioSink"),
301 remoteDeviceBdAddress, props);
302 NS_DispatchToMainThread(new SinkPropertyChangedHandler(signal));
303 }
305 #if ANDROID_VERSION > 17
306 /*
307 * Avrcp 1.3 callbacks
308 */
310 /*
311 * This function is to request Gaia player application to update
312 * current play status.
313 * Callback for play status request
314 */
315 static void
316 AvrcpGetPlayStatusCallback()
317 {
318 MOZ_ASSERT(!NS_IsMainThread());
320 NS_DispatchToMainThread(new RequestPlayStatusTask());
321 }
323 /*
324 * This function is trying to get element attributes, which request from CT
325 * Unlike BlueZ only calls UpdateMetaData, bluedroid does not cache meta data
326 * information, but instead uses callback AvrcpGetElementAttrCallback and
327 * call get_element_attr_rsp() to reply request.
328 *
329 * Callback to fetch the get element attributes of the current song
330 * aNumAttr: It represents the number of attributes requested in aPlayerAttrs
331 * aPlayerAttrs: It represents Attribute Ids
332 */
333 static void
334 AvrcpGetElementAttrCallback(uint8_t aNumAttr, btrc_media_attr_t* aPlayerAttrs)
335 {
336 MOZ_ASSERT(!NS_IsMainThread());
338 NS_DispatchToMainThread(new UpdateElementAttrsTask(aNumAttr, aPlayerAttrs));
339 }
341 /*
342 * Callback for register notification (Play state change/track change/...)
343 * To reply RegisterNotification INTERIM response
344 * See AVRCP 1.3 Spec 25.2
345 * aParam: It only valids if event_id is BTRC_EVT_PLAY_POS_CHANGED,
346 * which is playback interval time
347 */
348 static void
349 AvrcpRegisterNotificationCallback(btrc_event_id_t aEventId, uint32_t aParam)
350 {
351 MOZ_ASSERT(!NS_IsMainThread());
353 NS_DispatchToMainThread(new UpdateRegisterNotificationTask(aEventId, aParam));
354 }
356 /*
357 * Player application settings is optional for Avrcp 1.3
358 * B2G 1.3 currently does not support Player application setting
359 * related functions. Support Player Setting in the future version
360 */
361 static void
362 AvrcpListPlayerAppAttributeCallback()
363 {
364 MOZ_ASSERT(!NS_IsMainThread());
366 // TODO: Support avrcp application setting related functions
367 }
369 static void
370 AvrcpListPlayerAppValuesCallback(btrc_player_attr_t aAttrId)
371 {
372 MOZ_ASSERT(!NS_IsMainThread());
374 // TODO: Support avrcp application setting related functions
375 }
377 static void
378 AvrcpGetPlayerAppValueCallback(uint8_t aNumAttr,
379 btrc_player_attr_t* aPlayerAttrs)
380 {
381 MOZ_ASSERT(!NS_IsMainThread());
383 // TODO: Support avrcp application setting related functions
384 }
386 static void
387 AvrcpGetPlayerAppAttrsTextCallback(uint8_t aNumAttr,
388 btrc_player_attr_t* PlayerAttrs)
389 {
390 MOZ_ASSERT(!NS_IsMainThread());
392 // TODO: Support avrcp application setting related functions
393 }
395 static void
396 AvrcpGetPlayerAppValuesTextCallback(uint8_t aAttrId, uint8_t aNumVal,
397 uint8_t* PlayerVals)
398 {
399 MOZ_ASSERT(!NS_IsMainThread());
401 // TODO: Support avrcp application setting related functions
402 }
404 static void
405 AvrcpSetPlayerAppValueCallback(btrc_player_settings_t* aPlayerVals)
406 {
407 MOZ_ASSERT(!NS_IsMainThread());
409 // TODO: Support avrcp application setting related functions
410 }
411 #endif
413 #if ANDROID_VERSION > 18
414 /*
415 * This callback function is to get CT features from Feature Bit Mask.
416 * If Advanced Control Player bit is set, CT supports
417 * volume sync (absolute volume feature). If Browsing bit is set, Avrcp 1.4
418 * Browse feature will be supported
419 */
420 static void
421 AvrcpRemoteFeaturesCallback(bt_bdaddr_t* aBdAddress,
422 btrc_remote_features_t aFeatures)
423 {
424 // TODO: Support avrcp 1.4 absolute volume/browse
425 }
427 /*
428 * This callback function is to get notification that volume changed on the
429 * remote car kit (if it supports Avrcp 1.4), not notification from phone.
430 */
431 static void
432 AvrcpRemoteVolumeChangedCallback(uint8_t aVolume, uint8_t aCType)
433 {
434 // TODO: Support avrcp 1.4 absolute volume/browse
435 }
437 /*
438 * This callback function is to handle passthrough commands.
439 */
440 static void
441 AvrcpPassThroughCallback(int aId, int aKeyState)
442 {
443 // Fast-forward and rewind key events won't be generated from bluedroid
444 // stack after ANDROID_VERSION > 18, but via passthrough callback.
445 nsAutoString name;
446 NS_ENSURE_TRUE_VOID(aKeyState == AVRC_KEY_PRESS_STATE ||
447 aKeyState == AVRC_KEY_RELEASE_STATE);
448 switch (aId) {
449 case AVRC_ID_FAST_FOR:
450 if (aKeyState == AVRC_KEY_PRESS_STATE) {
451 name.AssignLiteral("media-fast-forward-button-press");
452 } else {
453 name.AssignLiteral("media-fast-forward-button-release");
454 }
455 break;
456 case AVRC_ID_REWIND:
457 if (aKeyState == AVRC_KEY_PRESS_STATE) {
458 name.AssignLiteral("media-rewind-button-press");
459 } else {
460 name.AssignLiteral("media-rewind-button-release");
461 }
462 break;
463 default:
464 BT_WARNING("Unable to handle the unknown PassThrough command %d", aId);
465 break;
466 }
467 if (!name.IsEmpty()) {
468 NS_DispatchToMainThread(new UpdatePassthroughCmdTask(name));
469 }
470 }
471 #endif
473 static btav_callbacks_t sBtA2dpCallbacks = {
474 sizeof(sBtA2dpCallbacks),
475 A2dpConnectionStateCallback,
476 A2dpAudioStateCallback
477 };
479 #if ANDROID_VERSION > 17
480 static btrc_callbacks_t sBtAvrcpCallbacks = {
481 sizeof(sBtAvrcpCallbacks),
482 #if ANDROID_VERSION > 18
483 AvrcpRemoteFeaturesCallback,
484 #endif
485 AvrcpGetPlayStatusCallback,
486 AvrcpListPlayerAppAttributeCallback,
487 AvrcpListPlayerAppValuesCallback,
488 AvrcpGetPlayerAppValueCallback,
489 AvrcpGetPlayerAppAttrsTextCallback,
490 AvrcpGetPlayerAppValuesTextCallback,
491 AvrcpSetPlayerAppValueCallback,
492 AvrcpGetElementAttrCallback,
493 AvrcpRegisterNotificationCallback,
494 #if ANDROID_VERSION > 18
495 AvrcpRemoteVolumeChangedCallback,
496 AvrcpPassThroughCallback
497 #endif
498 };
499 #endif
501 /*
502 * This function will be only called when Bluetooth is turning on.
503 * It is important to register a2dp callbacks before enable() gets called.
504 * It is required to register a2dp callbacks before a2dp media task
505 * starts up.
506 */
507 bool
508 BluetoothA2dpManager::Init()
509 {
510 const bt_interface_t* btInf = GetBluetoothInterface();
511 NS_ENSURE_TRUE(btInf, false);
513 sBtA2dpInterface = (btav_interface_t *)btInf->
514 get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);
515 NS_ENSURE_TRUE(sBtA2dpInterface, false);
517 int ret = sBtA2dpInterface->init(&sBtA2dpCallbacks);
518 if (ret != BT_STATUS_SUCCESS) {
519 BT_LOGR("Warning: failed to init a2dp module");
520 return false;
521 }
523 #if ANDROID_VERSION > 17
524 sBtAvrcpInterface = (btrc_interface_t *)btInf->
525 get_profile_interface(BT_PROFILE_AV_RC_ID);
526 NS_ENSURE_TRUE(sBtAvrcpInterface, false);
528 ret = sBtAvrcpInterface->init(&sBtAvrcpCallbacks);
529 if (ret != BT_STATUS_SUCCESS) {
530 BT_LOGR("Warning: failed to init avrcp module");
531 return false;
532 }
533 #endif
535 return true;
536 }
538 BluetoothA2dpManager::~BluetoothA2dpManager()
539 {
540 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
541 NS_ENSURE_TRUE_VOID(obs);
542 if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
543 BT_WARNING("Failed to remove shutdown observer!");
544 }
545 }
547 void
548 BluetoothA2dpManager::ResetA2dp()
549 {
550 mA2dpConnected = false;
551 mSinkState = SinkState::SINK_DISCONNECTED;
552 mController = nullptr;
553 }
555 void
556 BluetoothA2dpManager::ResetAvrcp()
557 {
558 mAvrcpConnected = false;
559 mDuration = 0;
560 mMediaNumber = 0;
561 mTotalMediaCount = 0;
562 mPosition = 0;
563 mPlayStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN;
564 }
566 /*
567 * Static functions
568 */
570 static BluetoothA2dpManager::SinkState
571 StatusStringToSinkState(const nsAString& aStatus)
572 {
573 BluetoothA2dpManager::SinkState state =
574 BluetoothA2dpManager::SinkState::SINK_UNKNOWN;
575 if (aStatus.EqualsLiteral("disconnected")) {
576 state = BluetoothA2dpManager::SinkState::SINK_DISCONNECTED;
577 } else if (aStatus.EqualsLiteral("connecting")) {
578 state = BluetoothA2dpManager::SinkState::SINK_CONNECTING;
579 } else if (aStatus.EqualsLiteral("connected")) {
580 state = BluetoothA2dpManager::SinkState::SINK_CONNECTED;
581 } else if (aStatus.EqualsLiteral("playing")) {
582 state = BluetoothA2dpManager::SinkState::SINK_PLAYING;
583 } else {
584 BT_WARNING("Unknown sink state");
585 }
586 return state;
587 }
589 //static
590 BluetoothA2dpManager*
591 BluetoothA2dpManager::Get()
592 {
593 MOZ_ASSERT(NS_IsMainThread());
595 // If sBluetoothA2dpManager already exists, exit early
596 if (sBluetoothA2dpManager) {
597 return sBluetoothA2dpManager;
598 }
600 // If we're in shutdown, don't create a new instance
601 NS_ENSURE_FALSE(sInShutdown, nullptr);
603 // Create a new instance, register, and return
604 BluetoothA2dpManager* manager = new BluetoothA2dpManager();
605 NS_ENSURE_TRUE(manager->Init(), nullptr);
607 sBluetoothA2dpManager = manager;
608 return sBluetoothA2dpManager;
609 }
611 void
612 BluetoothA2dpManager::HandleShutdown()
613 {
614 MOZ_ASSERT(NS_IsMainThread());
615 sInShutdown = true;
616 Disconnect(nullptr);
617 sBluetoothA2dpManager = nullptr;
618 }
620 void
621 BluetoothA2dpManager::Connect(const nsAString& aDeviceAddress,
622 BluetoothProfileController* aController)
623 {
624 MOZ_ASSERT(NS_IsMainThread());
625 MOZ_ASSERT(!aDeviceAddress.IsEmpty());
626 MOZ_ASSERT(aController && !mController);
628 BluetoothService* bs = BluetoothService::Get();
629 if (!bs || sInShutdown) {
630 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
631 return;
632 }
634 if (mA2dpConnected) {
635 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
636 return;
637 }
639 mDeviceAddress = aDeviceAddress;
640 mController = aController;
642 if (!sBtA2dpInterface) {
643 BT_LOGR("sBluetoothA2dpInterface is null");
644 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
645 return;
646 }
648 bt_bdaddr_t remoteAddress;
649 StringToBdAddressType(aDeviceAddress, &remoteAddress);
651 bt_status_t result = sBtA2dpInterface->connect(&remoteAddress);
652 if (BT_STATUS_SUCCESS != result) {
653 BT_LOGR("Failed to connect: %x", result);
654 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
655 return;
656 }
657 }
659 void
660 BluetoothA2dpManager::Disconnect(BluetoothProfileController* aController)
661 {
662 MOZ_ASSERT(NS_IsMainThread());
663 MOZ_ASSERT(!mController);
665 BluetoothService* bs = BluetoothService::Get();
666 if (!bs) {
667 if (aController) {
668 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
669 }
670 return;
671 }
673 if (!mA2dpConnected) {
674 if (aController) {
675 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
676 }
677 return;
678 }
680 MOZ_ASSERT(!mDeviceAddress.IsEmpty());
682 mController = aController;
684 if (!sBtA2dpInterface) {
685 BT_LOGR("sBluetoothA2dpInterface is null");
686 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
687 return;
688 }
690 bt_bdaddr_t remoteAddress;
691 StringToBdAddressType(mDeviceAddress, &remoteAddress);
693 bt_status_t result = sBtA2dpInterface->disconnect(&remoteAddress);
694 if (BT_STATUS_SUCCESS != result) {
695 BT_LOGR("Failed to disconnect: %x", result);
696 aController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
697 return;
698 }
699 }
701 void
702 BluetoothA2dpManager::OnConnect(const nsAString& aErrorStr)
703 {
704 MOZ_ASSERT(NS_IsMainThread());
706 /**
707 * On the one hand, notify the controller that we've done for outbound
708 * connections. On the other hand, we do nothing for inbound connections.
709 */
710 NS_ENSURE_TRUE_VOID(mController);
712 nsRefPtr<BluetoothProfileController> controller = mController.forget();
713 controller->NotifyCompletion(aErrorStr);
714 }
716 void
717 BluetoothA2dpManager::OnDisconnect(const nsAString& aErrorStr)
718 {
719 MOZ_ASSERT(NS_IsMainThread());
721 /**
722 * On the one hand, notify the controller that we've done for outbound
723 * connections. On the other hand, we do nothing for inbound connections.
724 */
725 NS_ENSURE_TRUE_VOID(mController);
727 nsRefPtr<BluetoothProfileController> controller = mController.forget();
728 controller->NotifyCompletion(aErrorStr);
730 Reset();
731 }
733 /* HandleSinkPropertyChanged update sink state in A2dp
734 *
735 * Possible values: "disconnected", "connecting", "connected", "playing"
736 *
737 * 1. "disconnected" -> "connecting"
738 * Either an incoming or outgoing connection attempt ongoing
739 * 2. "connecting" -> "disconnected"
740 * Connection attempt failed
741 * 3. "connecting" -> "connected"
742 * Successfully connected
743 * 4. "connected" -> "playing"
744 * Audio stream active
745 * 5. "playing" -> "connected"
746 * Audio stream suspended
747 * 6. "connected" -> "disconnected"
748 * "playing" -> "disconnected"
749 * Disconnected from local or the remote device
750 */
751 void
752 BluetoothA2dpManager::HandleSinkPropertyChanged(const BluetoothSignal& aSignal)
753 {
754 MOZ_ASSERT(NS_IsMainThread());
755 MOZ_ASSERT(aSignal.value().type() ==
756 BluetoothValue::TArrayOfBluetoothNamedValue);
758 const nsString& address = aSignal.path();
759 /**
760 * Update sink property only if
761 * - mDeviceAddress is empty (A2dp is disconnected), or
762 * - this property change is from the connected sink.
763 */
764 NS_ENSURE_TRUE_VOID(mDeviceAddress.IsEmpty() ||
765 mDeviceAddress.Equals(address));
767 const InfallibleTArray<BluetoothNamedValue>& arr =
768 aSignal.value().get_ArrayOfBluetoothNamedValue();
769 MOZ_ASSERT(arr.Length() == 1);
771 /**
772 * There are three properties:
773 * - "State": a string
774 * - "Connected": a boolean value
775 * - "Playing": a boolean value
776 *
777 * Note that only "State" is handled in this function.
778 */
780 const nsString& name = arr[0].name();
781 NS_ENSURE_TRUE_VOID(name.EqualsLiteral("State"));
783 const BluetoothValue& value = arr[0].value();
784 MOZ_ASSERT(value.type() == BluetoothValue::TnsString);
785 SinkState newState = StatusStringToSinkState(value.get_nsString());
786 NS_ENSURE_TRUE_VOID((newState != SinkState::SINK_UNKNOWN) &&
787 (newState != mSinkState));
789 SinkState prevState = mSinkState;
790 mSinkState = newState;
792 switch(mSinkState) {
793 case SinkState::SINK_CONNECTING:
794 // case 1: Either an incoming or outgoing connection attempt ongoing
795 MOZ_ASSERT(prevState == SinkState::SINK_DISCONNECTED);
796 break;
797 case SinkState::SINK_PLAYING:
798 // case 4: Audio stream active
799 MOZ_ASSERT(prevState == SinkState::SINK_CONNECTED);
800 break;
801 case SinkState::SINK_CONNECTED:
802 // case 5: Audio stream suspended
803 if (prevState == SinkState::SINK_PLAYING ||
804 prevState == SinkState::SINK_CONNECTED) {
805 break;
806 }
808 // case 3: Successfully connected
809 mA2dpConnected = true;
810 mDeviceAddress = address;
811 NotifyConnectionStatusChanged();
813 OnConnect(EmptyString());
814 break;
815 case SinkState::SINK_DISCONNECTED:
816 // case 2: Connection attempt failed
817 if (prevState == SinkState::SINK_CONNECTING) {
818 OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
819 break;
820 }
822 // case 6: Disconnected from the remote device
823 MOZ_ASSERT(prevState == SinkState::SINK_CONNECTED ||
824 prevState == SinkState::SINK_PLAYING) ;
826 mA2dpConnected = false;
827 NotifyConnectionStatusChanged();
828 mDeviceAddress.Truncate();
829 OnDisconnect(EmptyString());
830 break;
831 default:
832 break;
833 }
834 }
836 void
837 BluetoothA2dpManager::NotifyConnectionStatusChanged()
838 {
839 MOZ_ASSERT(NS_IsMainThread());
841 // Notify Gecko observers
842 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
843 NS_ENSURE_TRUE_VOID(obs);
845 if (NS_FAILED(obs->NotifyObservers(this,
846 BLUETOOTH_A2DP_STATUS_CHANGED_ID,
847 mDeviceAddress.get()))) {
848 BT_WARNING("Failed to notify bluetooth-a2dp-status-changed observsers!");
849 }
851 // Dispatch an event of status change
852 DispatchStatusChangedEvent(
853 NS_LITERAL_STRING(A2DP_STATUS_CHANGED_ID), mDeviceAddress, mA2dpConnected);
854 }
856 void
857 BluetoothA2dpManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
858 const nsAString& aServiceUuid,
859 int aChannel)
860 {
861 }
863 void
864 BluetoothA2dpManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
865 {
866 }
868 void
869 BluetoothA2dpManager::GetAddress(nsAString& aDeviceAddress)
870 {
871 aDeviceAddress = mDeviceAddress;
872 }
874 bool
875 BluetoothA2dpManager::IsConnected()
876 {
877 return mA2dpConnected;
878 }
880 /*
881 * In bluedroid stack case, there is no interface to know exactly
882 * avrcp connection status. All connection are managed by bluedroid stack.
883 */
884 void
885 BluetoothA2dpManager::SetAvrcpConnected(bool aConnected)
886 {
887 mAvrcpConnected = aConnected;
888 if (!aConnected) {
889 ResetAvrcp();
890 }
891 }
893 bool
894 BluetoothA2dpManager::IsAvrcpConnected()
895 {
896 return mAvrcpConnected;
897 }
899 /*
900 * This function only updates meta data in BluetoothA2dpManager
901 * Send "Get Element Attributes response" in AvrcpGetElementAttrCallback
902 */
903 void
904 BluetoothA2dpManager::UpdateMetaData(const nsAString& aTitle,
905 const nsAString& aArtist,
906 const nsAString& aAlbum,
907 uint64_t aMediaNumber,
908 uint64_t aTotalMediaCount,
909 uint32_t aDuration)
910 {
911 MOZ_ASSERT(NS_IsMainThread());
913 #if ANDROID_VERSION > 17
914 NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
916 // Send track changed and position changed if track num is not the same.
917 // See also AVRCP 1.3 Spec 5.4.2
918 if (mMediaNumber != aMediaNumber &&
919 mTrackChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
920 btrc_register_notification_t param;
921 // convert to network big endian format
922 // since track stores as uint8[8]
923 // 56 = 8 * (BTRC_UID_SIZE -1)
924 for (int i = 0; i < BTRC_UID_SIZE; ++i) {
925 param.track[i] = (aMediaNumber >> (56 - 8 * i));
926 }
927 mTrackChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
928 sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
929 BTRC_NOTIFICATION_TYPE_CHANGED,
930 ¶m);
931 if (mPlayPosChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
932 param.song_pos = mPosition;
933 // EVENT_PLAYBACK_POS_CHANGED shall be notified if changed current track
934 mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
935 sBtAvrcpInterface->register_notification_rsp(
936 BTRC_EVT_PLAY_POS_CHANGED,
937 BTRC_NOTIFICATION_TYPE_CHANGED,
938 ¶m);
939 }
940 }
942 mTitle.Assign(aTitle);
943 mArtist.Assign(aArtist);
944 mAlbum.Assign(aAlbum);
945 mMediaNumber = aMediaNumber;
946 mTotalMediaCount = aTotalMediaCount;
947 mDuration = aDuration;
948 #endif
949 }
951 /*
952 * This function is to reply AvrcpGetPlayStatusCallback (play-status-request)
953 * from media player application (Gaia side)
954 */
955 void
956 BluetoothA2dpManager::UpdatePlayStatus(uint32_t aDuration,
957 uint32_t aPosition,
958 ControlPlayStatus aPlayStatus)
959 {
960 MOZ_ASSERT(NS_IsMainThread());
962 #if ANDROID_VERSION > 17
963 NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
964 // always update playstatus first
965 sBtAvrcpInterface->get_play_status_rsp((btrc_play_status_t)aPlayStatus,
966 aDuration, aPosition);
967 // when play status changed, send both play status and position
968 if (mPlayStatus != aPlayStatus &&
969 mPlayStatusChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
970 btrc_register_notification_t param;
971 param.play_status = (btrc_play_status_t)aPlayStatus;
972 mPlayStatusChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
973 sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
974 BTRC_NOTIFICATION_TYPE_CHANGED,
975 ¶m);
976 }
978 if (mPosition != aPosition &&
979 mPlayPosChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
980 btrc_register_notification_t param;
981 param.song_pos = aPosition;
982 mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
983 sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED,
984 BTRC_NOTIFICATION_TYPE_CHANGED,
985 ¶m);
986 }
988 mDuration = aDuration;
989 mPosition = aPosition;
990 mPlayStatus = aPlayStatus;
991 #endif
992 }
994 /*
995 * This function handles RegisterNotification request from
996 * AvrcpRegisterNotificationCallback, which updates current
997 * track/status/position status in the INTERRIM response.
998 *
999 * aParam is only valid when position changed
1000 */
1001 void
1002 BluetoothA2dpManager::UpdateRegisterNotification(int aEventId, int aParam)
1003 {
1004 MOZ_ASSERT(NS_IsMainThread());
1006 #if ANDROID_VERSION > 17
1007 NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
1009 btrc_register_notification_t param;
1011 switch (aEventId) {
1012 case BTRC_EVT_PLAY_STATUS_CHANGED:
1013 mPlayStatusChangedNotifyType = BTRC_NOTIFICATION_TYPE_INTERIM;
1014 param.play_status = (btrc_play_status_t)mPlayStatus;
1015 break;
1016 case BTRC_EVT_TRACK_CHANGE:
1017 // In AVRCP 1.3 and 1.4, the identifier parameter of EVENT_TRACK_CHANGED
1018 // is different.
1019 // AVRCP 1.4: If no track is selected, we shall return 0xFFFFFFFFFFFFFFFF,
1020 // otherwise return 0x0 in the INTERRIM response. The expanded text in
1021 // version 1.4 is to allow for new UID feature. As for AVRCP 1.3, we shall
1022 // return 0xFFFFFFFF. Since PTS enforces to check this part to comply with
1023 // the most updated spec.
1024 mTrackChangedNotifyType = BTRC_NOTIFICATION_TYPE_INTERIM;
1025 // needs to convert to network big endian format since track stores
1026 // as uint8[8]. 56 = 8 * (BTRC_UID_SIZE -1).
1027 for (int index = 0; index < BTRC_UID_SIZE; ++index) {
1028 // We cannot easily check if a track is selected, so whenever A2DP is
1029 // streaming, we assume a track is selected.
1030 if (mSinkState == BluetoothA2dpManager::SinkState::SINK_PLAYING) {
1031 param.track[index] = 0x0;
1032 } else {
1033 param.track[index] = 0xFF;
1034 }
1035 }
1036 break;
1037 case BTRC_EVT_PLAY_POS_CHANGED:
1038 // If no track is selected, return 0xFFFFFFFF in the INTERIM response
1039 mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_INTERIM;
1040 if (mSinkState == BluetoothA2dpManager::SinkState::SINK_PLAYING) {
1041 param.song_pos = mPosition;
1042 } else {
1043 param.song_pos = 0xFFFFFFFF;
1044 }
1045 mPlaybackInterval = aParam;
1046 break;
1047 default:
1048 break;
1049 }
1051 sBtAvrcpInterface->register_notification_rsp((btrc_event_id_t)aEventId,
1052 BTRC_NOTIFICATION_TYPE_INTERIM,
1053 ¶m);
1054 #endif
1055 }
1057 void
1058 BluetoothA2dpManager::GetAlbum(nsAString& aAlbum)
1059 {
1060 aAlbum.Assign(mAlbum);
1061 }
1063 uint32_t
1064 BluetoothA2dpManager::GetDuration()
1065 {
1066 return mDuration;
1067 }
1069 ControlPlayStatus
1070 BluetoothA2dpManager::GetPlayStatus()
1071 {
1072 return mPlayStatus;
1073 }
1075 uint32_t
1076 BluetoothA2dpManager::GetPosition()
1077 {
1078 return mPosition;
1079 }
1081 uint64_t
1082 BluetoothA2dpManager::GetMediaNumber()
1083 {
1084 return mMediaNumber;
1085 }
1087 uint64_t
1088 BluetoothA2dpManager::GetTotalMediaNumber()
1089 {
1090 return mTotalMediaCount;
1091 }
1093 void
1094 BluetoothA2dpManager::GetTitle(nsAString& aTitle)
1095 {
1096 aTitle.Assign(mTitle);
1097 }
1099 void
1100 BluetoothA2dpManager::GetArtist(nsAString& aArtist)
1101 {
1102 aArtist.Assign(mArtist);
1103 }
1105 NS_IMPL_ISUPPORTS(BluetoothA2dpManager, nsIObserver)