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

     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                                                  &param);
   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         &param);
   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                                                  &param);
   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                                                  &param);
   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)
  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;
  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;
  1045       mPlaybackInterval = aParam;
  1046       break;
  1047     default:
  1048       break;
  1051   sBtAvrcpInterface->register_notification_rsp((btrc_event_id_t)aEventId,
  1052                                                BTRC_NOTIFICATION_TYPE_INTERIM,
  1053                                                &param);
  1054 #endif
  1057 void
  1058 BluetoothA2dpManager::GetAlbum(nsAString& aAlbum)
  1060   aAlbum.Assign(mAlbum);
  1063 uint32_t
  1064 BluetoothA2dpManager::GetDuration()
  1066   return mDuration;
  1069 ControlPlayStatus
  1070 BluetoothA2dpManager::GetPlayStatus()
  1072   return mPlayStatus;
  1075 uint32_t
  1076 BluetoothA2dpManager::GetPosition()
  1078   return mPosition;
  1081 uint64_t
  1082 BluetoothA2dpManager::GetMediaNumber()
  1084   return mMediaNumber;
  1087 uint64_t
  1088 BluetoothA2dpManager::GetTotalMediaNumber()
  1090   return mTotalMediaCount;
  1093 void
  1094 BluetoothA2dpManager::GetTitle(nsAString& aTitle)
  1096   aTitle.Assign(mTitle);
  1099 void
  1100 BluetoothA2dpManager::GetArtist(nsAString& aArtist)
  1102   aArtist.Assign(mArtist);
  1105 NS_IMPL_ISUPPORTS(BluetoothA2dpManager, nsIObserver)

mercurial