dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial