dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1106 @@
     1.4 +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
     1.5 +/* vim: set ts=2 et sw=2 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "base/basictypes.h"
    1.11 +
    1.12 +#include "BluetoothA2dpManager.h"
    1.13 +
    1.14 +#include <hardware/bluetooth.h>
    1.15 +#include <hardware/bt_av.h>
    1.16 +#if ANDROID_VERSION > 17
    1.17 +#include <hardware/bt_rc.h>
    1.18 +#endif
    1.19 +
    1.20 +#include "BluetoothCommon.h"
    1.21 +#include "BluetoothService.h"
    1.22 +#include "BluetoothSocket.h"
    1.23 +#include "BluetoothUtils.h"
    1.24 +
    1.25 +#include "mozilla/dom/bluetooth/BluetoothTypes.h"
    1.26 +#include "mozilla/Services.h"
    1.27 +#include "mozilla/StaticPtr.h"
    1.28 +#include "MainThreadUtils.h"
    1.29 +#include "nsIObserverService.h"
    1.30 +#include "nsThreadUtils.h"
    1.31 +
    1.32 +using namespace mozilla;
    1.33 +USING_BLUETOOTH_NAMESPACE
    1.34 +// AVRC_ID op code follows bluedroid avrc_defs.h
    1.35 +#define AVRC_ID_REWIND  0x48
    1.36 +#define AVRC_ID_FAST_FOR 0x49
    1.37 +#define AVRC_KEY_PRESS_STATE  1
    1.38 +#define AVRC_KEY_RELEASE_STATE  0
    1.39 +
    1.40 +namespace {
    1.41 +  StaticRefPtr<BluetoothA2dpManager> sBluetoothA2dpManager;
    1.42 +  bool sInShutdown = false;
    1.43 +  static const btav_interface_t* sBtA2dpInterface;
    1.44 +#if ANDROID_VERSION > 17
    1.45 +  static const btrc_interface_t* sBtAvrcpInterface;
    1.46 +#endif
    1.47 +} // anonymous namespace
    1.48 +
    1.49 +class SinkPropertyChangedHandler : public nsRunnable
    1.50 +{
    1.51 +public:
    1.52 +  SinkPropertyChangedHandler(const BluetoothSignal& aSignal)
    1.53 +    : mSignal(aSignal)
    1.54 +  {
    1.55 +  }
    1.56 +
    1.57 +  NS_IMETHOD
    1.58 +  Run()
    1.59 +  {
    1.60 +    MOZ_ASSERT(NS_IsMainThread());
    1.61 +
    1.62 +    BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
    1.63 +    NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE);
    1.64 +    a2dp->HandleSinkPropertyChanged(mSignal);
    1.65 +
    1.66 +    return NS_OK;
    1.67 +  }
    1.68 +
    1.69 +private:
    1.70 +  BluetoothSignal mSignal;
    1.71 +};
    1.72 +
    1.73 +class RequestPlayStatusTask : public nsRunnable
    1.74 +{
    1.75 +public:
    1.76 +  RequestPlayStatusTask()
    1.77 +  {
    1.78 +    MOZ_ASSERT(!NS_IsMainThread());
    1.79 +  }
    1.80 +
    1.81 +  nsresult Run()
    1.82 +  {
    1.83 +    MOZ_ASSERT(NS_IsMainThread());
    1.84 +
    1.85 +    BluetoothSignal signal(NS_LITERAL_STRING(REQUEST_MEDIA_PLAYSTATUS_ID),
    1.86 +                           NS_LITERAL_STRING(KEY_ADAPTER),
    1.87 +                           InfallibleTArray<BluetoothNamedValue>());
    1.88 +
    1.89 +    BluetoothService* bs = BluetoothService::Get();
    1.90 +    NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
    1.91 +    bs->DistributeSignal(signal);
    1.92 +
    1.93 +    return NS_OK;
    1.94 +  }
    1.95 +};
    1.96 +
    1.97 +#if ANDROID_VERSION > 17
    1.98 +class UpdateRegisterNotificationTask : public nsRunnable
    1.99 +{
   1.100 +public:
   1.101 +  UpdateRegisterNotificationTask(btrc_event_id_t aEventId, uint32_t aParam)
   1.102 +    : mEventId(aEventId)
   1.103 +    , mParam(aParam)
   1.104 +  {
   1.105 +    MOZ_ASSERT(!NS_IsMainThread());
   1.106 +  }
   1.107 +
   1.108 +  nsresult Run()
   1.109 +  {
   1.110 +    MOZ_ASSERT(NS_IsMainThread());
   1.111 +
   1.112 +    BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
   1.113 +    NS_ENSURE_TRUE(a2dp, NS_OK);
   1.114 +    a2dp->UpdateRegisterNotification(mEventId, mParam);
   1.115 +    return NS_OK;
   1.116 +  }
   1.117 +private:
   1.118 +  btrc_event_id_t mEventId;
   1.119 +  uint32_t mParam;
   1.120 +};
   1.121 +
   1.122 +/*
   1.123 + * This function maps attribute id and returns corresponding values
   1.124 + * Attribute id refers to btrc_media_attr_t in bt_rc.h
   1.125 + */
   1.126 +static void
   1.127 +ConvertAttributeString(int aAttrId, nsAString& aAttrStr)
   1.128 +{
   1.129 +  BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
   1.130 +  NS_ENSURE_TRUE_VOID(a2dp);
   1.131 +
   1.132 +  switch (aAttrId) {
   1.133 +    case BTRC_MEDIA_ATTR_TITLE:
   1.134 +      a2dp->GetTitle(aAttrStr);
   1.135 +      break;
   1.136 +    case BTRC_MEDIA_ATTR_ARTIST:
   1.137 +      a2dp->GetArtist(aAttrStr);
   1.138 +      break;
   1.139 +    case BTRC_MEDIA_ATTR_ALBUM:
   1.140 +      a2dp->GetAlbum(aAttrStr);
   1.141 +      break;
   1.142 +    case BTRC_MEDIA_ATTR_TRACK_NUM:
   1.143 +      aAttrStr.AppendInt(a2dp->GetMediaNumber());
   1.144 +      break;
   1.145 +    case BTRC_MEDIA_ATTR_NUM_TRACKS:
   1.146 +      aAttrStr.AppendInt(a2dp->GetTotalMediaNumber());
   1.147 +      break;
   1.148 +    case BTRC_MEDIA_ATTR_GENRE:
   1.149 +      // TODO: we currently don't support genre from music player
   1.150 +      aAttrStr.Truncate();
   1.151 +      break;
   1.152 +    case BTRC_MEDIA_ATTR_PLAYING_TIME:
   1.153 +      aAttrStr.AppendInt(a2dp->GetDuration());
   1.154 +      break;
   1.155 +  }
   1.156 +}
   1.157 +
   1.158 +class UpdateElementAttrsTask : public nsRunnable
   1.159 +{
   1.160 +public:
   1.161 +  UpdateElementAttrsTask(uint8_t aNumAttr, btrc_media_attr_t* aPlayerAttrs)
   1.162 +    : mNumAttr(aNumAttr)
   1.163 +    , mPlayerAttrs(aPlayerAttrs)
   1.164 +  {
   1.165 +    MOZ_ASSERT(!NS_IsMainThread());
   1.166 +  }
   1.167 +
   1.168 +  nsresult Run()
   1.169 +  {
   1.170 +    MOZ_ASSERT(NS_IsMainThread());
   1.171 +
   1.172 +    btrc_element_attr_val_t* attrs = new btrc_element_attr_val_t[mNumAttr];
   1.173 +    for (int i = 0; i < mNumAttr; i++) {
   1.174 +      nsAutoString attrText;
   1.175 +      attrs[i].attr_id = mPlayerAttrs[i];
   1.176 +      ConvertAttributeString(mPlayerAttrs[i], attrText);
   1.177 +      strcpy((char *)attrs[i].text, NS_ConvertUTF16toUTF8(attrText).get());
   1.178 +    }
   1.179 +
   1.180 +    NS_ENSURE_TRUE(sBtAvrcpInterface, NS_OK);
   1.181 +    sBtAvrcpInterface->get_element_attr_rsp(mNumAttr, attrs);
   1.182 +
   1.183 +    return NS_OK;
   1.184 +  }
   1.185 +private:
   1.186 +  uint8_t mNumAttr;
   1.187 +  btrc_media_attr_t* mPlayerAttrs;
   1.188 +};
   1.189 +
   1.190 +class UpdatePassthroughCmdTask : public nsRunnable
   1.191 +{
   1.192 +public:
   1.193 +  UpdatePassthroughCmdTask(const nsAString& aName)
   1.194 +    : mName(aName)
   1.195 +  {
   1.196 +    MOZ_ASSERT(!NS_IsMainThread());
   1.197 +  }
   1.198 +
   1.199 +  nsresult Run()
   1.200 +  {
   1.201 +    MOZ_ASSERT(NS_IsMainThread());
   1.202 +
   1.203 +    NS_NAMED_LITERAL_STRING(type, "media-button");
   1.204 +    BroadcastSystemMessage(type, BluetoothValue(mName));
   1.205 +
   1.206 +    return NS_OK;
   1.207 +  }
   1.208 +private:
   1.209 +  nsString mName;
   1.210 +};
   1.211 +
   1.212 +#endif
   1.213 +
   1.214 +NS_IMETHODIMP
   1.215 +BluetoothA2dpManager::Observe(nsISupports* aSubject,
   1.216 +                              const char* aTopic,
   1.217 +                              const char16_t* aData)
   1.218 +{
   1.219 +  MOZ_ASSERT(sBluetoothA2dpManager);
   1.220 +
   1.221 +  if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
   1.222 +    HandleShutdown();
   1.223 +    return NS_OK;
   1.224 +  }
   1.225 +
   1.226 +  MOZ_ASSERT(false, "BluetoothA2dpManager got unexpected topic!");
   1.227 +  return NS_ERROR_UNEXPECTED;
   1.228 +}
   1.229 +
   1.230 +BluetoothA2dpManager::BluetoothA2dpManager()
   1.231 +{
   1.232 +  Reset();
   1.233 +}
   1.234 +
   1.235 +void
   1.236 +BluetoothA2dpManager::Reset()
   1.237 +{
   1.238 +  ResetA2dp();
   1.239 +  ResetAvrcp();
   1.240 +}
   1.241 +
   1.242 +static void
   1.243 +AvStatusToSinkString(btav_connection_state_t aStatus, nsAString& aState)
   1.244 +{
   1.245 +  nsAutoString state;
   1.246 +  if (aStatus == BTAV_CONNECTION_STATE_DISCONNECTED) {
   1.247 +    aState = NS_LITERAL_STRING("disconnected");
   1.248 +  } else if (aStatus == BTAV_CONNECTION_STATE_CONNECTING) {
   1.249 +    aState = NS_LITERAL_STRING("connecting");
   1.250 +  } else if (aStatus == BTAV_CONNECTION_STATE_CONNECTED) {
   1.251 +    aState = NS_LITERAL_STRING("connected");
   1.252 +  } else if (aStatus == BTAV_CONNECTION_STATE_DISCONNECTING) {
   1.253 +    aState = NS_LITERAL_STRING("disconnecting");
   1.254 +  } else {
   1.255 +    BT_WARNING("Unknown sink state");
   1.256 +  }
   1.257 +}
   1.258 +
   1.259 +static void
   1.260 +A2dpConnectionStateCallback(btav_connection_state_t aState,
   1.261 +                            bt_bdaddr_t* aBdAddress)
   1.262 +{
   1.263 +  MOZ_ASSERT(!NS_IsMainThread());
   1.264 +
   1.265 +  nsString remoteDeviceBdAddress;
   1.266 +  BdAddressTypeToString(aBdAddress, remoteDeviceBdAddress);
   1.267 +
   1.268 +  nsString a2dpState;
   1.269 +  AvStatusToSinkString(aState, a2dpState);
   1.270 +
   1.271 +  InfallibleTArray<BluetoothNamedValue> props;
   1.272 +  BT_APPEND_NAMED_VALUE(props, "State", a2dpState);
   1.273 +
   1.274 +  BluetoothSignal signal(NS_LITERAL_STRING("AudioSink"),
   1.275 +                         remoteDeviceBdAddress, props);
   1.276 +  NS_DispatchToMainThread(new SinkPropertyChangedHandler(signal));
   1.277 +}
   1.278 +
   1.279 +static void
   1.280 +A2dpAudioStateCallback(btav_audio_state_t aState,
   1.281 +                       bt_bdaddr_t* aBdAddress)
   1.282 +{
   1.283 +  MOZ_ASSERT(!NS_IsMainThread());
   1.284 +
   1.285 +  nsString remoteDeviceBdAddress;
   1.286 +  BdAddressTypeToString(aBdAddress, remoteDeviceBdAddress);
   1.287 +
   1.288 +  nsString a2dpState;
   1.289 +
   1.290 +  if (aState == BTAV_AUDIO_STATE_STARTED) {
   1.291 +    a2dpState = NS_LITERAL_STRING("playing");
   1.292 +  } else if (aState == BTAV_AUDIO_STATE_STOPPED) {
   1.293 +    // for avdtp state stop stream
   1.294 +    a2dpState = NS_LITERAL_STRING("connected");
   1.295 +  } else if (aState == BTAV_AUDIO_STATE_REMOTE_SUSPEND) {
   1.296 +    // for avdtp state suspend stream from remote side
   1.297 +    a2dpState = NS_LITERAL_STRING("connected");
   1.298 +  }
   1.299 +
   1.300 +  InfallibleTArray<BluetoothNamedValue> props;
   1.301 +  BT_APPEND_NAMED_VALUE(props, "State", a2dpState);
   1.302 +
   1.303 +  BluetoothSignal signal(NS_LITERAL_STRING("AudioSink"),
   1.304 +                         remoteDeviceBdAddress, props);
   1.305 +  NS_DispatchToMainThread(new SinkPropertyChangedHandler(signal));
   1.306 +}
   1.307 +
   1.308 +#if ANDROID_VERSION > 17
   1.309 +/*
   1.310 + * Avrcp 1.3 callbacks
   1.311 + */
   1.312 +
   1.313 +/*
   1.314 + * This function is to request Gaia player application to update
   1.315 + * current play status.
   1.316 + * Callback for play status request
   1.317 + */
   1.318 +static void
   1.319 +AvrcpGetPlayStatusCallback()
   1.320 +{
   1.321 +  MOZ_ASSERT(!NS_IsMainThread());
   1.322 +
   1.323 +  NS_DispatchToMainThread(new RequestPlayStatusTask());
   1.324 +}
   1.325 +
   1.326 +/*
   1.327 + * This function is trying to get element attributes, which request from CT
   1.328 + * Unlike BlueZ only calls UpdateMetaData, bluedroid does not cache meta data
   1.329 + * information, but instead uses callback AvrcpGetElementAttrCallback and
   1.330 + * call get_element_attr_rsp() to reply request.
   1.331 + *
   1.332 + * Callback to fetch the get element attributes of the current song
   1.333 + * aNumAttr: It represents the number of attributes requested in aPlayerAttrs
   1.334 + * aPlayerAttrs: It represents Attribute Ids
   1.335 + */
   1.336 +static void
   1.337 +AvrcpGetElementAttrCallback(uint8_t aNumAttr, btrc_media_attr_t* aPlayerAttrs)
   1.338 +{
   1.339 +  MOZ_ASSERT(!NS_IsMainThread());
   1.340 +
   1.341 +  NS_DispatchToMainThread(new UpdateElementAttrsTask(aNumAttr, aPlayerAttrs));
   1.342 +}
   1.343 +
   1.344 +/*
   1.345 + * Callback for register notification (Play state change/track change/...)
   1.346 + * To reply RegisterNotification INTERIM response
   1.347 + * See AVRCP 1.3 Spec 25.2
   1.348 + * aParam: It only valids if event_id is BTRC_EVT_PLAY_POS_CHANGED,
   1.349 + * which is playback interval time
   1.350 + */
   1.351 +static void
   1.352 +AvrcpRegisterNotificationCallback(btrc_event_id_t aEventId, uint32_t aParam)
   1.353 +{
   1.354 +  MOZ_ASSERT(!NS_IsMainThread());
   1.355 +
   1.356 +  NS_DispatchToMainThread(new UpdateRegisterNotificationTask(aEventId, aParam));
   1.357 +}
   1.358 +
   1.359 +/*
   1.360 + * Player application settings is optional for Avrcp 1.3
   1.361 + * B2G 1.3 currently does not support Player application setting
   1.362 + * related functions. Support Player Setting in the future version
   1.363 + */
   1.364 +static void
   1.365 +AvrcpListPlayerAppAttributeCallback()
   1.366 +{
   1.367 +  MOZ_ASSERT(!NS_IsMainThread());
   1.368 +
   1.369 +// TODO: Support avrcp application setting related functions
   1.370 +}
   1.371 +
   1.372 +static void
   1.373 +AvrcpListPlayerAppValuesCallback(btrc_player_attr_t aAttrId)
   1.374 +{
   1.375 +  MOZ_ASSERT(!NS_IsMainThread());
   1.376 +
   1.377 +// TODO: Support avrcp application setting related functions
   1.378 +}
   1.379 +
   1.380 +static void
   1.381 +AvrcpGetPlayerAppValueCallback(uint8_t aNumAttr,
   1.382 +                               btrc_player_attr_t* aPlayerAttrs)
   1.383 +{
   1.384 +  MOZ_ASSERT(!NS_IsMainThread());
   1.385 +
   1.386 +// TODO: Support avrcp application setting related functions
   1.387 +}
   1.388 +
   1.389 +static void
   1.390 +AvrcpGetPlayerAppAttrsTextCallback(uint8_t aNumAttr,
   1.391 +                                   btrc_player_attr_t* PlayerAttrs)
   1.392 +{
   1.393 +  MOZ_ASSERT(!NS_IsMainThread());
   1.394 +
   1.395 +// TODO: Support avrcp application setting related functions
   1.396 +}
   1.397 +
   1.398 +static void
   1.399 +AvrcpGetPlayerAppValuesTextCallback(uint8_t aAttrId, uint8_t aNumVal,
   1.400 +                                    uint8_t* PlayerVals)
   1.401 +{
   1.402 +  MOZ_ASSERT(!NS_IsMainThread());
   1.403 +
   1.404 +// TODO: Support avrcp application setting related functions
   1.405 +}
   1.406 +
   1.407 +static void
   1.408 +AvrcpSetPlayerAppValueCallback(btrc_player_settings_t* aPlayerVals)
   1.409 +{
   1.410 +  MOZ_ASSERT(!NS_IsMainThread());
   1.411 +
   1.412 +// TODO: Support avrcp application setting related functions
   1.413 +}
   1.414 +#endif
   1.415 +
   1.416 +#if ANDROID_VERSION > 18
   1.417 +/*
   1.418 + * This callback function is to get CT features from Feature Bit Mask.
   1.419 + * If Advanced Control Player bit is set, CT supports
   1.420 + * volume sync (absolute volume feature). If Browsing bit is set, Avrcp 1.4
   1.421 + * Browse feature will be supported
   1.422 + */
   1.423 +static void
   1.424 +AvrcpRemoteFeaturesCallback(bt_bdaddr_t* aBdAddress,
   1.425 +                            btrc_remote_features_t aFeatures)
   1.426 +{
   1.427 +// TODO: Support avrcp 1.4 absolute volume/browse
   1.428 +}
   1.429 +
   1.430 +/*
   1.431 + * This callback function is to get notification that volume changed on the
   1.432 + * remote car kit (if it supports Avrcp 1.4), not notification from phone.
   1.433 + */
   1.434 +static void
   1.435 +AvrcpRemoteVolumeChangedCallback(uint8_t aVolume, uint8_t aCType)
   1.436 +{
   1.437 +// TODO: Support avrcp 1.4 absolute volume/browse
   1.438 +}
   1.439 +
   1.440 +/*
   1.441 + * This callback function is to handle passthrough commands.
   1.442 + */
   1.443 +static void
   1.444 +AvrcpPassThroughCallback(int aId, int aKeyState)
   1.445 +{
   1.446 +  // Fast-forward and rewind key events won't be generated from bluedroid
   1.447 +  // stack after ANDROID_VERSION > 18, but via passthrough callback.
   1.448 +  nsAutoString name;
   1.449 +  NS_ENSURE_TRUE_VOID(aKeyState == AVRC_KEY_PRESS_STATE ||
   1.450 +                      aKeyState == AVRC_KEY_RELEASE_STATE);
   1.451 +  switch (aId) {
   1.452 +    case AVRC_ID_FAST_FOR:
   1.453 +      if (aKeyState == AVRC_KEY_PRESS_STATE) {
   1.454 +        name.AssignLiteral("media-fast-forward-button-press");
   1.455 +      } else {
   1.456 +        name.AssignLiteral("media-fast-forward-button-release");
   1.457 +      }
   1.458 +      break;
   1.459 +    case AVRC_ID_REWIND:
   1.460 +      if (aKeyState == AVRC_KEY_PRESS_STATE) {
   1.461 +        name.AssignLiteral("media-rewind-button-press");
   1.462 +      } else {
   1.463 +        name.AssignLiteral("media-rewind-button-release");
   1.464 +      }
   1.465 +      break;
   1.466 +    default:
   1.467 +      BT_WARNING("Unable to handle the unknown PassThrough command %d", aId);
   1.468 +      break;
   1.469 +  }
   1.470 +  if (!name.IsEmpty()) {
   1.471 +    NS_DispatchToMainThread(new UpdatePassthroughCmdTask(name));
   1.472 +  }
   1.473 +}
   1.474 +#endif
   1.475 +
   1.476 +static btav_callbacks_t sBtA2dpCallbacks = {
   1.477 +  sizeof(sBtA2dpCallbacks),
   1.478 +  A2dpConnectionStateCallback,
   1.479 +  A2dpAudioStateCallback
   1.480 +};
   1.481 +
   1.482 +#if ANDROID_VERSION > 17
   1.483 +static btrc_callbacks_t sBtAvrcpCallbacks = {
   1.484 +  sizeof(sBtAvrcpCallbacks),
   1.485 +#if ANDROID_VERSION > 18
   1.486 +  AvrcpRemoteFeaturesCallback,
   1.487 +#endif
   1.488 +  AvrcpGetPlayStatusCallback,
   1.489 +  AvrcpListPlayerAppAttributeCallback,
   1.490 +  AvrcpListPlayerAppValuesCallback,
   1.491 +  AvrcpGetPlayerAppValueCallback,
   1.492 +  AvrcpGetPlayerAppAttrsTextCallback,
   1.493 +  AvrcpGetPlayerAppValuesTextCallback,
   1.494 +  AvrcpSetPlayerAppValueCallback,
   1.495 +  AvrcpGetElementAttrCallback,
   1.496 +  AvrcpRegisterNotificationCallback,
   1.497 +#if ANDROID_VERSION > 18
   1.498 +  AvrcpRemoteVolumeChangedCallback,
   1.499 +  AvrcpPassThroughCallback
   1.500 +#endif
   1.501 +};
   1.502 +#endif
   1.503 +
   1.504 +/*
   1.505 + * This function will be only called when Bluetooth is turning on.
   1.506 + * It is important to register a2dp callbacks before enable() gets called.
   1.507 + * It is required to register a2dp callbacks before a2dp media task
   1.508 + * starts up.
   1.509 + */
   1.510 +bool
   1.511 +BluetoothA2dpManager::Init()
   1.512 +{
   1.513 +  const bt_interface_t* btInf = GetBluetoothInterface();
   1.514 +  NS_ENSURE_TRUE(btInf, false);
   1.515 +
   1.516 +  sBtA2dpInterface = (btav_interface_t *)btInf->
   1.517 +    get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);
   1.518 +  NS_ENSURE_TRUE(sBtA2dpInterface, false);
   1.519 +
   1.520 +  int ret = sBtA2dpInterface->init(&sBtA2dpCallbacks);
   1.521 +  if (ret != BT_STATUS_SUCCESS) {
   1.522 +    BT_LOGR("Warning: failed to init a2dp module");
   1.523 +    return false;
   1.524 +  }
   1.525 +
   1.526 +#if ANDROID_VERSION > 17
   1.527 +  sBtAvrcpInterface = (btrc_interface_t *)btInf->
   1.528 +    get_profile_interface(BT_PROFILE_AV_RC_ID);
   1.529 +  NS_ENSURE_TRUE(sBtAvrcpInterface, false);
   1.530 +
   1.531 +  ret = sBtAvrcpInterface->init(&sBtAvrcpCallbacks);
   1.532 +  if (ret != BT_STATUS_SUCCESS) {
   1.533 +    BT_LOGR("Warning: failed to init avrcp module");
   1.534 +    return false;
   1.535 +  }
   1.536 +#endif
   1.537 +
   1.538 +  return true;
   1.539 +}
   1.540 +
   1.541 +BluetoothA2dpManager::~BluetoothA2dpManager()
   1.542 +{
   1.543 +  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   1.544 +  NS_ENSURE_TRUE_VOID(obs);
   1.545 +  if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
   1.546 +    BT_WARNING("Failed to remove shutdown observer!");
   1.547 +  }
   1.548 +}
   1.549 +
   1.550 +void
   1.551 +BluetoothA2dpManager::ResetA2dp()
   1.552 +{
   1.553 +  mA2dpConnected = false;
   1.554 +  mSinkState = SinkState::SINK_DISCONNECTED;
   1.555 +  mController = nullptr;
   1.556 +}
   1.557 +
   1.558 +void
   1.559 +BluetoothA2dpManager::ResetAvrcp()
   1.560 +{
   1.561 +  mAvrcpConnected = false;
   1.562 +  mDuration = 0;
   1.563 +  mMediaNumber = 0;
   1.564 +  mTotalMediaCount = 0;
   1.565 +  mPosition = 0;
   1.566 +  mPlayStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN;
   1.567 +}
   1.568 +
   1.569 +/*
   1.570 + * Static functions
   1.571 + */
   1.572 +
   1.573 +static BluetoothA2dpManager::SinkState
   1.574 +StatusStringToSinkState(const nsAString& aStatus)
   1.575 +{
   1.576 +  BluetoothA2dpManager::SinkState state =
   1.577 +    BluetoothA2dpManager::SinkState::SINK_UNKNOWN;
   1.578 +  if (aStatus.EqualsLiteral("disconnected")) {
   1.579 +    state = BluetoothA2dpManager::SinkState::SINK_DISCONNECTED;
   1.580 +  } else if (aStatus.EqualsLiteral("connecting")) {
   1.581 +    state = BluetoothA2dpManager::SinkState::SINK_CONNECTING;
   1.582 +  } else if (aStatus.EqualsLiteral("connected")) {
   1.583 +    state = BluetoothA2dpManager::SinkState::SINK_CONNECTED;
   1.584 +  } else if (aStatus.EqualsLiteral("playing")) {
   1.585 +    state = BluetoothA2dpManager::SinkState::SINK_PLAYING;
   1.586 +  } else {
   1.587 +    BT_WARNING("Unknown sink state");
   1.588 +  }
   1.589 +  return state;
   1.590 +}
   1.591 +
   1.592 +//static
   1.593 +BluetoothA2dpManager*
   1.594 +BluetoothA2dpManager::Get()
   1.595 +{
   1.596 +  MOZ_ASSERT(NS_IsMainThread());
   1.597 +
   1.598 +  // If sBluetoothA2dpManager already exists, exit early
   1.599 +  if (sBluetoothA2dpManager) {
   1.600 +    return sBluetoothA2dpManager;
   1.601 +  }
   1.602 +
   1.603 +  // If we're in shutdown, don't create a new instance
   1.604 +  NS_ENSURE_FALSE(sInShutdown, nullptr);
   1.605 +
   1.606 +  // Create a new instance, register, and return
   1.607 +  BluetoothA2dpManager* manager = new BluetoothA2dpManager();
   1.608 +  NS_ENSURE_TRUE(manager->Init(), nullptr);
   1.609 +
   1.610 +  sBluetoothA2dpManager = manager;
   1.611 +  return sBluetoothA2dpManager;
   1.612 +}
   1.613 +
   1.614 +void
   1.615 +BluetoothA2dpManager::HandleShutdown()
   1.616 +{
   1.617 +  MOZ_ASSERT(NS_IsMainThread());
   1.618 +  sInShutdown = true;
   1.619 +  Disconnect(nullptr);
   1.620 +  sBluetoothA2dpManager = nullptr;
   1.621 +}
   1.622 +
   1.623 +void
   1.624 +BluetoothA2dpManager::Connect(const nsAString& aDeviceAddress,
   1.625 +                              BluetoothProfileController* aController)
   1.626 +{
   1.627 +  MOZ_ASSERT(NS_IsMainThread());
   1.628 +  MOZ_ASSERT(!aDeviceAddress.IsEmpty());
   1.629 +  MOZ_ASSERT(aController && !mController);
   1.630 +
   1.631 +  BluetoothService* bs = BluetoothService::Get();
   1.632 +  if (!bs || sInShutdown) {
   1.633 +    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
   1.634 +    return;
   1.635 +  }
   1.636 +
   1.637 +  if (mA2dpConnected) {
   1.638 +    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
   1.639 +    return;
   1.640 +  }
   1.641 +
   1.642 +  mDeviceAddress = aDeviceAddress;
   1.643 +  mController = aController;
   1.644 +
   1.645 +  if (!sBtA2dpInterface) {
   1.646 +    BT_LOGR("sBluetoothA2dpInterface is null");
   1.647 +    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
   1.648 +    return;
   1.649 +  }
   1.650 +
   1.651 +  bt_bdaddr_t remoteAddress;
   1.652 +  StringToBdAddressType(aDeviceAddress, &remoteAddress);
   1.653 +
   1.654 +  bt_status_t result = sBtA2dpInterface->connect(&remoteAddress);
   1.655 +  if (BT_STATUS_SUCCESS != result) {
   1.656 +    BT_LOGR("Failed to connect: %x", result);
   1.657 +    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
   1.658 +    return;
   1.659 +  }
   1.660 +}
   1.661 +
   1.662 +void
   1.663 +BluetoothA2dpManager::Disconnect(BluetoothProfileController* aController)
   1.664 +{
   1.665 +  MOZ_ASSERT(NS_IsMainThread());
   1.666 +  MOZ_ASSERT(!mController);
   1.667 +
   1.668 +  BluetoothService* bs = BluetoothService::Get();
   1.669 +  if (!bs) {
   1.670 +    if (aController) {
   1.671 +      aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
   1.672 +    }
   1.673 +    return;
   1.674 +  }
   1.675 +
   1.676 +  if (!mA2dpConnected) {
   1.677 +    if (aController) {
   1.678 +      aController->NotifyCompletion(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
   1.679 +    }
   1.680 +    return;
   1.681 +  }
   1.682 +
   1.683 +  MOZ_ASSERT(!mDeviceAddress.IsEmpty());
   1.684 +
   1.685 +  mController = aController;
   1.686 +
   1.687 +  if (!sBtA2dpInterface) {
   1.688 +    BT_LOGR("sBluetoothA2dpInterface is null");
   1.689 +    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_NO_AVAILABLE_RESOURCE));
   1.690 +    return;
   1.691 +  }
   1.692 +
   1.693 +  bt_bdaddr_t remoteAddress;
   1.694 +  StringToBdAddressType(mDeviceAddress, &remoteAddress);
   1.695 +
   1.696 +  bt_status_t result = sBtA2dpInterface->disconnect(&remoteAddress);
   1.697 +  if (BT_STATUS_SUCCESS != result) {
   1.698 +    BT_LOGR("Failed to disconnect: %x", result);
   1.699 +    aController->NotifyCompletion(NS_LITERAL_STRING(ERR_DISCONNECTION_FAILED));
   1.700 +    return;
   1.701 +  }
   1.702 +}
   1.703 +
   1.704 +void
   1.705 +BluetoothA2dpManager::OnConnect(const nsAString& aErrorStr)
   1.706 +{
   1.707 +  MOZ_ASSERT(NS_IsMainThread());
   1.708 +
   1.709 +  /**
   1.710 +   * On the one hand, notify the controller that we've done for outbound
   1.711 +   * connections. On the other hand, we do nothing for inbound connections.
   1.712 +   */
   1.713 +  NS_ENSURE_TRUE_VOID(mController);
   1.714 +
   1.715 +  nsRefPtr<BluetoothProfileController> controller = mController.forget();
   1.716 +  controller->NotifyCompletion(aErrorStr);
   1.717 +}
   1.718 +
   1.719 +void
   1.720 +BluetoothA2dpManager::OnDisconnect(const nsAString& aErrorStr)
   1.721 +{
   1.722 +  MOZ_ASSERT(NS_IsMainThread());
   1.723 +
   1.724 +  /**
   1.725 +   * On the one hand, notify the controller that we've done for outbound
   1.726 +   * connections. On the other hand, we do nothing for inbound connections.
   1.727 +   */
   1.728 +  NS_ENSURE_TRUE_VOID(mController);
   1.729 +
   1.730 +  nsRefPtr<BluetoothProfileController> controller = mController.forget();
   1.731 +  controller->NotifyCompletion(aErrorStr);
   1.732 +
   1.733 +  Reset();
   1.734 +}
   1.735 +
   1.736 +/* HandleSinkPropertyChanged update sink state in A2dp
   1.737 + *
   1.738 + * Possible values: "disconnected", "connecting", "connected", "playing"
   1.739 + *
   1.740 + * 1. "disconnected" -> "connecting"
   1.741 + *    Either an incoming or outgoing connection attempt ongoing
   1.742 + * 2. "connecting" -> "disconnected"
   1.743 + *    Connection attempt failed
   1.744 + * 3. "connecting" -> "connected"
   1.745 + *    Successfully connected
   1.746 + * 4. "connected" -> "playing"
   1.747 + *    Audio stream active
   1.748 + * 5. "playing" -> "connected"
   1.749 + *    Audio stream suspended
   1.750 + * 6. "connected" -> "disconnected"
   1.751 + *    "playing" -> "disconnected"
   1.752 + *    Disconnected from local or the remote device
   1.753 + */
   1.754 +void
   1.755 +BluetoothA2dpManager::HandleSinkPropertyChanged(const BluetoothSignal& aSignal)
   1.756 +{
   1.757 +  MOZ_ASSERT(NS_IsMainThread());
   1.758 +  MOZ_ASSERT(aSignal.value().type() ==
   1.759 +             BluetoothValue::TArrayOfBluetoothNamedValue);
   1.760 +
   1.761 +  const nsString& address = aSignal.path();
   1.762 +  /**
   1.763 +   * Update sink property only if
   1.764 +   * - mDeviceAddress is empty (A2dp is disconnected), or
   1.765 +   * - this property change is from the connected sink.
   1.766 +   */
   1.767 +  NS_ENSURE_TRUE_VOID(mDeviceAddress.IsEmpty() ||
   1.768 +                      mDeviceAddress.Equals(address));
   1.769 +
   1.770 +  const InfallibleTArray<BluetoothNamedValue>& arr =
   1.771 +    aSignal.value().get_ArrayOfBluetoothNamedValue();
   1.772 +  MOZ_ASSERT(arr.Length() == 1);
   1.773 +
   1.774 +  /**
   1.775 +   * There are three properties:
   1.776 +   * - "State": a string
   1.777 +   * - "Connected": a boolean value
   1.778 +   * - "Playing": a boolean value
   1.779 +   *
   1.780 +   * Note that only "State" is handled in this function.
   1.781 +   */
   1.782 +
   1.783 +  const nsString& name = arr[0].name();
   1.784 +  NS_ENSURE_TRUE_VOID(name.EqualsLiteral("State"));
   1.785 +
   1.786 +  const BluetoothValue& value = arr[0].value();
   1.787 +  MOZ_ASSERT(value.type() == BluetoothValue::TnsString);
   1.788 +  SinkState newState = StatusStringToSinkState(value.get_nsString());
   1.789 +  NS_ENSURE_TRUE_VOID((newState != SinkState::SINK_UNKNOWN) &&
   1.790 +                      (newState != mSinkState));
   1.791 +
   1.792 +  SinkState prevState = mSinkState;
   1.793 +  mSinkState = newState;
   1.794 +
   1.795 +  switch(mSinkState) {
   1.796 +    case SinkState::SINK_CONNECTING:
   1.797 +      // case 1: Either an incoming or outgoing connection attempt ongoing
   1.798 +      MOZ_ASSERT(prevState == SinkState::SINK_DISCONNECTED);
   1.799 +      break;
   1.800 +    case SinkState::SINK_PLAYING:
   1.801 +      // case 4: Audio stream active
   1.802 +      MOZ_ASSERT(prevState == SinkState::SINK_CONNECTED);
   1.803 +      break;
   1.804 +    case SinkState::SINK_CONNECTED:
   1.805 +      // case 5: Audio stream suspended
   1.806 +      if (prevState == SinkState::SINK_PLAYING ||
   1.807 +          prevState == SinkState::SINK_CONNECTED) {
   1.808 +        break;
   1.809 +      }
   1.810 +
   1.811 +      // case 3: Successfully connected
   1.812 +      mA2dpConnected = true;
   1.813 +      mDeviceAddress = address;
   1.814 +      NotifyConnectionStatusChanged();
   1.815 +
   1.816 +      OnConnect(EmptyString());
   1.817 +      break;
   1.818 +    case SinkState::SINK_DISCONNECTED:
   1.819 +      // case 2: Connection attempt failed
   1.820 +      if (prevState == SinkState::SINK_CONNECTING) {
   1.821 +        OnConnect(NS_LITERAL_STRING(ERR_CONNECTION_FAILED));
   1.822 +        break;
   1.823 +      }
   1.824 +
   1.825 +      // case 6: Disconnected from the remote device
   1.826 +      MOZ_ASSERT(prevState == SinkState::SINK_CONNECTED ||
   1.827 +                 prevState == SinkState::SINK_PLAYING) ;
   1.828 +
   1.829 +      mA2dpConnected = false;
   1.830 +      NotifyConnectionStatusChanged();
   1.831 +      mDeviceAddress.Truncate();
   1.832 +      OnDisconnect(EmptyString());
   1.833 +      break;
   1.834 +    default:
   1.835 +      break;
   1.836 +  }
   1.837 +}
   1.838 +
   1.839 +void
   1.840 +BluetoothA2dpManager::NotifyConnectionStatusChanged()
   1.841 +{
   1.842 +  MOZ_ASSERT(NS_IsMainThread());
   1.843 +
   1.844 +  // Notify Gecko observers
   1.845 +  nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   1.846 +  NS_ENSURE_TRUE_VOID(obs);
   1.847 +
   1.848 +  if (NS_FAILED(obs->NotifyObservers(this,
   1.849 +                                     BLUETOOTH_A2DP_STATUS_CHANGED_ID,
   1.850 +                                     mDeviceAddress.get()))) {
   1.851 +    BT_WARNING("Failed to notify bluetooth-a2dp-status-changed observsers!");
   1.852 +  }
   1.853 +
   1.854 +  // Dispatch an event of status change
   1.855 +  DispatchStatusChangedEvent(
   1.856 +    NS_LITERAL_STRING(A2DP_STATUS_CHANGED_ID), mDeviceAddress, mA2dpConnected);
   1.857 +}
   1.858 +
   1.859 +void
   1.860 +BluetoothA2dpManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
   1.861 +                                          const nsAString& aServiceUuid,
   1.862 +                                          int aChannel)
   1.863 +{
   1.864 +}
   1.865 +
   1.866 +void
   1.867 +BluetoothA2dpManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
   1.868 +{
   1.869 +}
   1.870 +
   1.871 +void
   1.872 +BluetoothA2dpManager::GetAddress(nsAString& aDeviceAddress)
   1.873 +{
   1.874 +  aDeviceAddress = mDeviceAddress;
   1.875 +}
   1.876 +
   1.877 +bool
   1.878 +BluetoothA2dpManager::IsConnected()
   1.879 +{
   1.880 +  return mA2dpConnected;
   1.881 +}
   1.882 +
   1.883 +/*
   1.884 + * In bluedroid stack case, there is no interface to know exactly
   1.885 + * avrcp connection status. All connection are managed by bluedroid stack.
   1.886 + */
   1.887 +void
   1.888 +BluetoothA2dpManager::SetAvrcpConnected(bool aConnected)
   1.889 +{
   1.890 +  mAvrcpConnected = aConnected;
   1.891 +  if (!aConnected) {
   1.892 +    ResetAvrcp();
   1.893 +  }
   1.894 +}
   1.895 +
   1.896 +bool
   1.897 +BluetoothA2dpManager::IsAvrcpConnected()
   1.898 +{
   1.899 +  return mAvrcpConnected;
   1.900 +}
   1.901 +
   1.902 +/*
   1.903 + * This function only updates meta data in BluetoothA2dpManager
   1.904 + * Send "Get Element Attributes response" in AvrcpGetElementAttrCallback
   1.905 + */
   1.906 +void
   1.907 +BluetoothA2dpManager::UpdateMetaData(const nsAString& aTitle,
   1.908 +                                     const nsAString& aArtist,
   1.909 +                                     const nsAString& aAlbum,
   1.910 +                                     uint64_t aMediaNumber,
   1.911 +                                     uint64_t aTotalMediaCount,
   1.912 +                                     uint32_t aDuration)
   1.913 +{
   1.914 +  MOZ_ASSERT(NS_IsMainThread());
   1.915 +
   1.916 +#if ANDROID_VERSION > 17
   1.917 +  NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
   1.918 +
   1.919 +  // Send track changed and position changed if track num is not the same.
   1.920 +  // See also AVRCP 1.3 Spec 5.4.2
   1.921 +  if (mMediaNumber != aMediaNumber &&
   1.922 +      mTrackChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
   1.923 +    btrc_register_notification_t param;
   1.924 +    // convert to network big endian format
   1.925 +    // since track stores as uint8[8]
   1.926 +    // 56 = 8 * (BTRC_UID_SIZE -1)
   1.927 +    for (int i = 0; i < BTRC_UID_SIZE; ++i) {
   1.928 +      param.track[i] = (aMediaNumber >> (56 - 8 * i));
   1.929 +    }
   1.930 +    mTrackChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
   1.931 +    sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
   1.932 +                                                 BTRC_NOTIFICATION_TYPE_CHANGED,
   1.933 +                                                 &param);
   1.934 +    if (mPlayPosChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
   1.935 +      param.song_pos = mPosition;
   1.936 +      // EVENT_PLAYBACK_POS_CHANGED shall be notified if changed current track
   1.937 +      mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
   1.938 +      sBtAvrcpInterface->register_notification_rsp(
   1.939 +        BTRC_EVT_PLAY_POS_CHANGED,
   1.940 +        BTRC_NOTIFICATION_TYPE_CHANGED,
   1.941 +        &param);
   1.942 +    }
   1.943 +  }
   1.944 +
   1.945 +  mTitle.Assign(aTitle);
   1.946 +  mArtist.Assign(aArtist);
   1.947 +  mAlbum.Assign(aAlbum);
   1.948 +  mMediaNumber = aMediaNumber;
   1.949 +  mTotalMediaCount = aTotalMediaCount;
   1.950 +  mDuration = aDuration;
   1.951 +#endif
   1.952 +}
   1.953 +
   1.954 +/*
   1.955 + * This function is to reply AvrcpGetPlayStatusCallback (play-status-request)
   1.956 + * from media player application (Gaia side)
   1.957 + */
   1.958 +void
   1.959 +BluetoothA2dpManager::UpdatePlayStatus(uint32_t aDuration,
   1.960 +                                       uint32_t aPosition,
   1.961 +                                       ControlPlayStatus aPlayStatus)
   1.962 +{
   1.963 +  MOZ_ASSERT(NS_IsMainThread());
   1.964 +
   1.965 +#if ANDROID_VERSION > 17
   1.966 +  NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
   1.967 +  // always update playstatus first
   1.968 +  sBtAvrcpInterface->get_play_status_rsp((btrc_play_status_t)aPlayStatus,
   1.969 +                                         aDuration, aPosition);
   1.970 +  // when play status changed, send both play status and position
   1.971 +  if (mPlayStatus != aPlayStatus &&
   1.972 +      mPlayStatusChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
   1.973 +    btrc_register_notification_t param;
   1.974 +    param.play_status = (btrc_play_status_t)aPlayStatus;
   1.975 +    mPlayStatusChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
   1.976 +    sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
   1.977 +                                                 BTRC_NOTIFICATION_TYPE_CHANGED,
   1.978 +                                                 &param);
   1.979 +  }
   1.980 +
   1.981 +  if (mPosition != aPosition &&
   1.982 +      mPlayPosChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
   1.983 +    btrc_register_notification_t param;
   1.984 +    param.song_pos = aPosition;
   1.985 +    mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
   1.986 +    sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED,
   1.987 +                                                 BTRC_NOTIFICATION_TYPE_CHANGED,
   1.988 +                                                 &param);
   1.989 +  }
   1.990 +
   1.991 +  mDuration = aDuration;
   1.992 +  mPosition = aPosition;
   1.993 +  mPlayStatus = aPlayStatus;
   1.994 +#endif
   1.995 +}
   1.996 +
   1.997 +/*
   1.998 + * This function handles RegisterNotification request from
   1.999 + * AvrcpRegisterNotificationCallback, which updates current
  1.1000 + * track/status/position status in the INTERRIM response.
  1.1001 + *
  1.1002 + * aParam is only valid when position changed
  1.1003 + */
  1.1004 +void
  1.1005 +BluetoothA2dpManager::UpdateRegisterNotification(int aEventId, int aParam)
  1.1006 +{
  1.1007 +  MOZ_ASSERT(NS_IsMainThread());
  1.1008 +
  1.1009 +#if ANDROID_VERSION > 17
  1.1010 +  NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
  1.1011 +
  1.1012 +  btrc_register_notification_t param;
  1.1013 +
  1.1014 +  switch (aEventId) {
  1.1015 +    case BTRC_EVT_PLAY_STATUS_CHANGED:
  1.1016 +      mPlayStatusChangedNotifyType = BTRC_NOTIFICATION_TYPE_INTERIM;
  1.1017 +      param.play_status = (btrc_play_status_t)mPlayStatus;
  1.1018 +      break;
  1.1019 +    case BTRC_EVT_TRACK_CHANGE:
  1.1020 +      // In AVRCP 1.3 and 1.4, the identifier parameter of EVENT_TRACK_CHANGED
  1.1021 +      // is different.
  1.1022 +      // AVRCP 1.4: If no track is selected, we shall return 0xFFFFFFFFFFFFFFFF,
  1.1023 +      // otherwise return 0x0 in the INTERRIM response. The expanded text in
  1.1024 +      // version 1.4 is to allow for new UID feature. As for AVRCP 1.3, we shall
  1.1025 +      // return 0xFFFFFFFF. Since PTS enforces to check this part to comply with
  1.1026 +      // the most updated spec.
  1.1027 +      mTrackChangedNotifyType = BTRC_NOTIFICATION_TYPE_INTERIM;
  1.1028 +      // needs to convert to network big endian format since track stores
  1.1029 +      // as uint8[8]. 56 = 8 * (BTRC_UID_SIZE -1).
  1.1030 +      for (int index = 0; index < BTRC_UID_SIZE; ++index) {
  1.1031 +        // We cannot easily check if a track is selected, so whenever A2DP is
  1.1032 +        // streaming, we assume a track is selected.
  1.1033 +        if (mSinkState == BluetoothA2dpManager::SinkState::SINK_PLAYING) {
  1.1034 +          param.track[index] = 0x0;
  1.1035 +        } else {
  1.1036 +          param.track[index] = 0xFF;
  1.1037 +        }
  1.1038 +      }
  1.1039 +      break;
  1.1040 +    case BTRC_EVT_PLAY_POS_CHANGED:
  1.1041 +      // If no track is selected, return 0xFFFFFFFF in the INTERIM response
  1.1042 +      mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_INTERIM;
  1.1043 +      if (mSinkState == BluetoothA2dpManager::SinkState::SINK_PLAYING) {
  1.1044 +        param.song_pos = mPosition;
  1.1045 +      } else {
  1.1046 +        param.song_pos = 0xFFFFFFFF;
  1.1047 +      }
  1.1048 +      mPlaybackInterval = aParam;
  1.1049 +      break;
  1.1050 +    default:
  1.1051 +      break;
  1.1052 +  }
  1.1053 +
  1.1054 +  sBtAvrcpInterface->register_notification_rsp((btrc_event_id_t)aEventId,
  1.1055 +                                               BTRC_NOTIFICATION_TYPE_INTERIM,
  1.1056 +                                               &param);
  1.1057 +#endif
  1.1058 +}
  1.1059 +
  1.1060 +void
  1.1061 +BluetoothA2dpManager::GetAlbum(nsAString& aAlbum)
  1.1062 +{
  1.1063 +  aAlbum.Assign(mAlbum);
  1.1064 +}
  1.1065 +
  1.1066 +uint32_t
  1.1067 +BluetoothA2dpManager::GetDuration()
  1.1068 +{
  1.1069 +  return mDuration;
  1.1070 +}
  1.1071 +
  1.1072 +ControlPlayStatus
  1.1073 +BluetoothA2dpManager::GetPlayStatus()
  1.1074 +{
  1.1075 +  return mPlayStatus;
  1.1076 +}
  1.1077 +
  1.1078 +uint32_t
  1.1079 +BluetoothA2dpManager::GetPosition()
  1.1080 +{
  1.1081 +  return mPosition;
  1.1082 +}
  1.1083 +
  1.1084 +uint64_t
  1.1085 +BluetoothA2dpManager::GetMediaNumber()
  1.1086 +{
  1.1087 +  return mMediaNumber;
  1.1088 +}
  1.1089 +
  1.1090 +uint64_t
  1.1091 +BluetoothA2dpManager::GetTotalMediaNumber()
  1.1092 +{
  1.1093 +  return mTotalMediaCount;
  1.1094 +}
  1.1095 +
  1.1096 +void
  1.1097 +BluetoothA2dpManager::GetTitle(nsAString& aTitle)
  1.1098 +{
  1.1099 +  aTitle.Assign(mTitle);
  1.1100 +}
  1.1101 +
  1.1102 +void
  1.1103 +BluetoothA2dpManager::GetArtist(nsAString& aArtist)
  1.1104 +{
  1.1105 +  aArtist.Assign(mArtist);
  1.1106 +}
  1.1107 +
  1.1108 +NS_IMPL_ISUPPORTS(BluetoothA2dpManager, nsIObserver)
  1.1109 +

mercurial