tenfourfox/dom/bluetooth/bluedroid/BluetoothAvrcpManager.cpp
Cameron Kaiser c9b2922b70 hello FPR
2017-04-19 00:56:45 -07:00

941 lines
25 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "base/basictypes.h"
#include "BluetoothAvrcpManager.h"
#include "BluetoothCommon.h"
#include "BluetoothService.h"
#include "BluetoothSocket.h"
#include "BluetoothUtils.h"
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "MainThreadUtils.h"
#include "nsIObserverService.h"
#include "nsThreadUtils.h"
using namespace mozilla;
USING_BLUETOOTH_NAMESPACE
// AVRC_ID op code follows bluedroid avrc_defs.h
#define AVRC_ID_REWIND 0x48
#define AVRC_ID_FAST_FOR 0x49
#define AVRC_KEY_PRESS_STATE 1
#define AVRC_KEY_RELEASE_STATE 0
// bluedroid bt_rc.h
#define AVRC_MAX_ATTR_STR_LEN 255
namespace {
StaticRefPtr<BluetoothAvrcpManager> sBluetoothAvrcpManager;
bool sInShutdown = false;
static BluetoothAvrcpInterface* sBtAvrcpInterface;
} // namespace
const int BluetoothAvrcpManager::MAX_NUM_CLIENTS = 1;
/*
* This function maps attribute id and returns corresponding values
*/
static void
ConvertAttributeString(BluetoothAvrcpMediaAttribute aAttrId,
nsAString& aAttrStr)
{
BluetoothAvrcpManager* avrcp = BluetoothAvrcpManager::Get();
NS_ENSURE_TRUE_VOID(avrcp);
switch (aAttrId) {
case AVRCP_MEDIA_ATTRIBUTE_TITLE:
avrcp->GetTitle(aAttrStr);
/*
* bluedroid can only send string length AVRC_MAX_ATTR_STR_LEN - 1
*/
if (aAttrStr.Length() >= AVRC_MAX_ATTR_STR_LEN) {
aAttrStr.Truncate(AVRC_MAX_ATTR_STR_LEN - 1);
BT_WARNING("Truncate media item attribute title, length is over 255");
}
break;
case AVRCP_MEDIA_ATTRIBUTE_ARTIST:
avrcp->GetArtist(aAttrStr);
if (aAttrStr.Length() >= AVRC_MAX_ATTR_STR_LEN) {
aAttrStr.Truncate(AVRC_MAX_ATTR_STR_LEN - 1);
BT_WARNING("Truncate media item attribute artist, length is over 255");
}
break;
case AVRCP_MEDIA_ATTRIBUTE_ALBUM:
avrcp->GetAlbum(aAttrStr);
if (aAttrStr.Length() >= AVRC_MAX_ATTR_STR_LEN) {
aAttrStr.Truncate(AVRC_MAX_ATTR_STR_LEN - 1);
BT_WARNING("Truncate media item attribute album, length is over 255");
}
break;
case AVRCP_MEDIA_ATTRIBUTE_TRACK_NUM:
aAttrStr.AppendInt(avrcp->GetMediaNumber());
break;
case AVRCP_MEDIA_ATTRIBUTE_NUM_TRACKS:
aAttrStr.AppendInt(avrcp->GetTotalMediaNumber());
break;
case AVRCP_MEDIA_ATTRIBUTE_GENRE:
// TODO: we currently don't support genre from music player
aAttrStr.Truncate();
break;
case AVRCP_MEDIA_ATTRIBUTE_PLAYING_TIME:
aAttrStr.AppendInt(avrcp->GetDuration());
break;
}
}
NS_IMETHODIMP
BluetoothAvrcpManager::Observe(nsISupports* aSubject,
const char* aTopic,
const char16_t* aData)
{
MOZ_ASSERT(sBluetoothAvrcpManager);
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
HandleShutdown();
return NS_OK;
}
MOZ_ASSERT(false, "BluetoothAvrcpManager got unexpected topic!");
return NS_ERROR_UNEXPECTED;
}
BluetoothAvrcpManager::BluetoothAvrcpManager()
{
Reset();
}
void
BluetoothAvrcpManager::Reset()
{
mAvrcpConnected = false;
mDuration = 0;
mMediaNumber = 0;
mTotalMediaCount = 0;
mPosition = 0;
mPlayStatus = ControlPlayStatus::PLAYSTATUS_STOPPED;
}
class BluetoothAvrcpManager::RegisterModuleResultHandler final
: public BluetoothSetupResultHandler
{
public:
RegisterModuleResultHandler(BluetoothAvrcpInterface* aInterface,
BluetoothProfileResultHandler* aRes)
: mInterface(aInterface)
, mRes(aRes)
{
MOZ_ASSERT(mInterface);
}
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
BT_WARNING("BluetoothSetupInterface::RegisterModule failed for AVRCP: %d",
(int)aStatus);
mInterface->SetNotificationHandler(nullptr);
if (mRes) {
if (aStatus == STATUS_UNSUPPORTED) {
/* Not all versions of Bluedroid support AVRCP. So if the
* initialization fails with STATUS_UNSUPPORTED, we still
* signal success.
*/
mRes->Init();
} else {
mRes->OnError(NS_ERROR_FAILURE);
}
}
}
void RegisterModule() override
{
MOZ_ASSERT(NS_IsMainThread());
sBtAvrcpInterface = mInterface;
if (mRes) {
mRes->Init();
}
}
private:
BluetoothAvrcpInterface* mInterface;
RefPtr<BluetoothProfileResultHandler> mRes;
};
class BluetoothAvrcpManager::InitProfileResultHandlerRunnable final
: public nsRunnable
{
public:
InitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
nsresult aRv)
: mRes(aRes)
, mRv(aRv)
{
MOZ_ASSERT(mRes);
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_SUCCEEDED(mRv)) {
mRes->Init();
} else {
mRes->OnError(mRv);
}
return NS_OK;
}
private:
RefPtr<BluetoothProfileResultHandler> mRes;
nsresult mRv;
};
/*
* This function will be only called when Bluetooth is turning on.
*/
// static
void
BluetoothAvrcpManager::InitAvrcpInterface(BluetoothProfileResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
if (sBtAvrcpInterface) {
BT_LOGR("Bluetooth AVRCP interface is already initalized.");
RefPtr<nsRunnable> r =
new InitProfileResultHandlerRunnable(aRes, NS_OK);
if (NS_FAILED(NS_DispatchToMainThread(r))) {
BT_LOGR("Failed to dispatch AVRCP Init runnable");
}
return;
}
auto btInf = BluetoothInterface::GetInstance();
if (NS_WARN_IF(!btInf)) {
// If there's no Bluetooth interface, we dispatch a runnable
// that calls the profile result handler.
RefPtr<nsRunnable> r =
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
if (NS_FAILED(NS_DispatchToMainThread(r))) {
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
}
return;
}
auto setupInterface = btInf->GetBluetoothSetupInterface();
if (NS_WARN_IF(!setupInterface)) {
// If there's no Setup interface, we dispatch a runnable
// that calls the profile result handler.
RefPtr<nsRunnable> r =
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
if (NS_FAILED(NS_DispatchToMainThread(r))) {
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
}
return;
}
auto avrcpInterface = btInf->GetBluetoothAvrcpInterface();
if (NS_WARN_IF(!avrcpInterface)) {
// If there's no AVRCP interface, we dispatch a runnable
// that calls the profile result handler.
RefPtr<nsRunnable> r =
new InitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
if (NS_FAILED(NS_DispatchToMainThread(r))) {
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
}
return;
}
// Set notification handler _before_ registering the module. It could
// happen that we receive notifications, before the result handler runs.
avrcpInterface->SetNotificationHandler(BluetoothAvrcpManager::Get());
setupInterface->RegisterModule(
SETUP_SERVICE_ID_AVRCP, 0, MAX_NUM_CLIENTS,
new RegisterModuleResultHandler(avrcpInterface, aRes));
}
BluetoothAvrcpManager::~BluetoothAvrcpManager()
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
NS_ENSURE_TRUE_VOID(obs);
if (NS_FAILED(obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID))) {
BT_WARNING("Failed to remove shutdown observer!");
}
}
/*
* Static functions
*/
//static
BluetoothAvrcpManager*
BluetoothAvrcpManager::Get()
{
MOZ_ASSERT(NS_IsMainThread());
// If sBluetoothAvrcpManager already exists, exit early
if (sBluetoothAvrcpManager) {
return sBluetoothAvrcpManager;
}
// If we're in shutdown, don't create a new instance
NS_ENSURE_FALSE(sInShutdown, nullptr);
// Create a new instance, register, and return
BluetoothAvrcpManager* manager = new BluetoothAvrcpManager();
sBluetoothAvrcpManager = manager;
return sBluetoothAvrcpManager;
}
class BluetoothAvrcpManager::UnregisterModuleResultHandler final
: public BluetoothSetupResultHandler
{
public:
UnregisterModuleResultHandler(BluetoothProfileResultHandler* aRes)
: mRes(aRes)
{ }
void OnError(BluetoothStatus aStatus) override
{
MOZ_ASSERT(NS_IsMainThread());
BT_WARNING("BluetoothSetupInterface::UnregisterModule failed for AVRCP: %d",
(int)aStatus);
sBtAvrcpInterface->SetNotificationHandler(nullptr);
sBtAvrcpInterface = nullptr;
if (mRes) {
mRes->OnError(NS_ERROR_FAILURE);
}
}
void UnregisterModule() override
{
MOZ_ASSERT(NS_IsMainThread());
sBtAvrcpInterface->SetNotificationHandler(nullptr);
sBtAvrcpInterface = nullptr;
if (mRes) {
mRes->Deinit();
}
}
private:
RefPtr<BluetoothProfileResultHandler> mRes;
};
class BluetoothAvrcpManager::DeinitProfileResultHandlerRunnable final
: public nsRunnable
{
public:
DeinitProfileResultHandlerRunnable(BluetoothProfileResultHandler* aRes,
nsresult aRv)
: mRes(aRes)
, mRv(aRv)
{
MOZ_ASSERT(mRes);
}
NS_IMETHOD Run() override
{
MOZ_ASSERT(NS_IsMainThread());
if (NS_SUCCEEDED(mRv)) {
mRes->Deinit();
} else {
mRes->OnError(mRv);
}
return NS_OK;
}
private:
RefPtr<BluetoothProfileResultHandler> mRes;
nsresult mRv;
};
// static
void
BluetoothAvrcpManager::DeinitAvrcpInterface(BluetoothProfileResultHandler* aRes)
{
MOZ_ASSERT(NS_IsMainThread());
if (!sBtAvrcpInterface) {
BT_LOGR("Bluetooth AVRCP interface has not been initalized.");
RefPtr<nsRunnable> r =
new DeinitProfileResultHandlerRunnable(aRes, NS_OK);
if (NS_FAILED(NS_DispatchToMainThread(r))) {
BT_LOGR("Failed to dispatch AVRCP Deinit runnable");
}
return;
}
auto btInf = BluetoothInterface::GetInstance();
if (NS_WARN_IF(!btInf)) {
// If there's no backend interface, we dispatch a runnable
// that calls the profile result handler.
RefPtr<nsRunnable> r =
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
if (NS_FAILED(NS_DispatchToMainThread(r))) {
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
}
return;
}
auto setupInterface = btInf->GetBluetoothSetupInterface();
if (NS_WARN_IF(!setupInterface)) {
// If there's no Setup interface, we dispatch a runnable
// that calls the profile result handler.
RefPtr<nsRunnable> r =
new DeinitProfileResultHandlerRunnable(aRes, NS_ERROR_FAILURE);
if (NS_FAILED(NS_DispatchToMainThread(r))) {
BT_LOGR("Failed to dispatch AVRCP OnError runnable");
}
return;
}
setupInterface->UnregisterModule(
SETUP_SERVICE_ID_AVRCP,
new UnregisterModuleResultHandler(aRes));
}
void
BluetoothAvrcpManager::HandleShutdown()
{
MOZ_ASSERT(NS_IsMainThread());
sInShutdown = true;
Disconnect(nullptr);
sBluetoothAvrcpManager = nullptr;
}
class BluetoothAvrcpManager::ConnectRunnable final : public nsRunnable
{
public:
ConnectRunnable(BluetoothAvrcpManager* aManager)
: mManager(aManager)
{
MOZ_ASSERT(mManager);
}
NS_METHOD Run() override
{
mManager->OnConnect(EmptyString());
return NS_OK;
}
private:
BluetoothAvrcpManager* mManager;
};
void
BluetoothAvrcpManager::Connect(const BluetoothAddress& aDeviceAddress,
BluetoothProfileController* aController)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!aDeviceAddress.IsCleared());
MOZ_ASSERT(aController);
// AVRCP doesn't require connecting. We just set the remote address here.
mDeviceAddress = aDeviceAddress;
mController = aController;
SetConnected(true);
NS_DispatchToMainThread(new ConnectRunnable(this));
}
class BluetoothAvrcpManager::DisconnectRunnable final : public nsRunnable
{
public:
DisconnectRunnable(BluetoothAvrcpManager* aManager)
: mManager(aManager)
{
MOZ_ASSERT(mManager);
}
NS_METHOD Run() override
{
mManager->OnDisconnect(EmptyString());
return NS_OK;
}
private:
BluetoothAvrcpManager* mManager;
};
void
BluetoothAvrcpManager::Disconnect(BluetoothProfileController* aController)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!mController);
mDeviceAddress.Clear();
mController = aController;
SetConnected(false);
NS_DispatchToMainThread(new DisconnectRunnable(this));
}
void
BluetoothAvrcpManager::OnConnect(const nsAString& aErrorStr)
{
MOZ_ASSERT(NS_IsMainThread());
/**
* On the one hand, notify the controller that we've done for outbound
* connections. On the other hand, we do nothing for inbound connections.
*/
NS_ENSURE_TRUE_VOID(mController);
RefPtr<BluetoothProfileController> controller = mController.forget();
controller->NotifyCompletion(aErrorStr);
}
void
BluetoothAvrcpManager::OnDisconnect(const nsAString& aErrorStr)
{
MOZ_ASSERT(NS_IsMainThread());
/**
* On the one hand, notify the controller that we've done for outbound
* connections. On the other hand, we do nothing for inbound connections.
*/
NS_ENSURE_TRUE_VOID(mController);
RefPtr<BluetoothProfileController> controller = mController.forget();
controller->NotifyCompletion(aErrorStr);
Reset();
}
void
BluetoothAvrcpManager::OnGetServiceChannel(
const BluetoothAddress& aDeviceAddress,
const BluetoothUuid& aServiceUuid,
int aChannel)
{ }
void
BluetoothAvrcpManager::OnUpdateSdpRecords(
const BluetoothAddress& aDeviceAddress)
{ }
void
BluetoothAvrcpManager::GetAddress(BluetoothAddress& aDeviceAddress)
{
aDeviceAddress = mDeviceAddress;
}
bool
BluetoothAvrcpManager::IsConnected()
{
return mAvrcpConnected;
}
/*
* In bluedroid stack case, there is no interface to know exactly
* avrcp connection status. All connection are managed by bluedroid stack.
*/
void
BluetoothAvrcpManager::SetConnected(bool aConnected)
{
mAvrcpConnected = aConnected;
if (!aConnected) {
Reset();
}
}
/*
* This function only updates meta data in BluetoothAvrcpManager. Send
* "Get Element Attributes response" in AvrcpGetElementAttrCallback
*/
void
BluetoothAvrcpManager::UpdateMetaData(const nsAString& aTitle,
const nsAString& aArtist,
const nsAString& aAlbum,
uint64_t aMediaNumber,
uint64_t aTotalMediaCount,
uint32_t aDuration)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
// Send track changed and position changed if track num is not the same.
// See also AVRCP 1.3 Spec 5.4.2
if (mMediaNumber != aMediaNumber &&
mTrackChangedNotifyType == AVRCP_NTF_INTERIM) {
BluetoothAvrcpNotificationParam param;
// convert to network big endian format
// since track stores as uint8[8]
// 56 = 8 * (AVRCP_UID_SIZE -1)
for (int i = 0; i < AVRCP_UID_SIZE; ++i) {
param.mTrack[i] = (aMediaNumber >> (56 - 8 * i));
}
mTrackChangedNotifyType = AVRCP_NTF_CHANGED;
sBtAvrcpInterface->RegisterNotificationRsp(AVRCP_EVENT_TRACK_CHANGE,
AVRCP_NTF_CHANGED,
param, nullptr);
if (mPlayPosChangedNotifyType == AVRCP_NTF_INTERIM) {
param.mSongPos = mPosition;
// EVENT_PLAYBACK_POS_CHANGED shall be notified if changed current track
mPlayPosChangedNotifyType = AVRCP_NTF_CHANGED;
sBtAvrcpInterface->RegisterNotificationRsp(AVRCP_EVENT_PLAY_POS_CHANGED,
AVRCP_NTF_CHANGED,
param, nullptr);
}
}
mTitle.Assign(aTitle);
mArtist.Assign(aArtist);
mAlbum.Assign(aAlbum);
mMediaNumber = aMediaNumber;
mTotalMediaCount = aTotalMediaCount;
mDuration = aDuration;
}
/*
* This function is to reply AvrcpGetPlayStatusCallback (play-status-request)
* from media player application (Gaia side)
*/
void
BluetoothAvrcpManager::UpdatePlayStatus(uint32_t aDuration,
uint32_t aPosition,
ControlPlayStatus aPlayStatus)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
// always update playstatus first
sBtAvrcpInterface->GetPlayStatusRsp(aPlayStatus, aDuration,
aPosition, nullptr);
// when play status changed, send both play status and position
if (mPlayStatus != aPlayStatus &&
mPlayStatusChangedNotifyType == AVRCP_NTF_INTERIM) {
BluetoothAvrcpNotificationParam param;
param.mPlayStatus = aPlayStatus;
mPlayStatusChangedNotifyType = AVRCP_NTF_CHANGED;
sBtAvrcpInterface->RegisterNotificationRsp(AVRCP_EVENT_PLAY_STATUS_CHANGED,
AVRCP_NTF_CHANGED,
param, nullptr);
}
if (mPosition != aPosition &&
mPlayPosChangedNotifyType == AVRCP_NTF_INTERIM) {
BluetoothAvrcpNotificationParam param;
param.mSongPos = aPosition;
mPlayPosChangedNotifyType = AVRCP_NTF_CHANGED;
sBtAvrcpInterface->RegisterNotificationRsp(AVRCP_EVENT_PLAY_POS_CHANGED,
AVRCP_NTF_CHANGED,
param, nullptr);
}
mDuration = aDuration;
mPosition = aPosition;
mPlayStatus = aPlayStatus;
}
/*
* This function handles RegisterNotification request from
* AvrcpRegisterNotificationCallback, which updates current
* track/status/position status in the INTERRIM response.
*
* aParam is only valid when position changed
*/
void
BluetoothAvrcpManager::UpdateRegisterNotification(BluetoothAvrcpEvent aEvent,
uint32_t aParam)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
BluetoothAvrcpNotificationParam param;
switch (aEvent) {
case AVRCP_EVENT_PLAY_STATUS_CHANGED:
mPlayStatusChangedNotifyType = AVRCP_NTF_INTERIM;
param.mPlayStatus = mPlayStatus;
break;
case AVRCP_EVENT_TRACK_CHANGE:
// In AVRCP 1.3 and 1.4, the identifier parameter of EVENT_TRACK_CHANGED
// is different.
// AVRCP 1.4: If no track is selected, we shall return 0xFFFFFFFFFFFFFFFF,
// otherwise return 0x0 in the INTERRIM response. The expanded text in
// version 1.4 is to allow for new UID feature. As for AVRCP 1.3, we shall
// return 0xFFFFFFFF. Since PTS enforces to check this part to comply with
// the most updated spec.
mTrackChangedNotifyType = AVRCP_NTF_INTERIM;
// needs to convert to network big endian format since track stores
// as uint8[8]. 56 = 8 * (BTRC_UID_SIZE -1).
for (int index = 0; index < AVRCP_UID_SIZE; ++index) {
// We cannot easily check if a track is selected, so whenever A2DP is
// streaming, we assume a track is selected.
if (mPlayStatus == ControlPlayStatus::PLAYSTATUS_PLAYING) {
param.mTrack[index] = 0x0;
} else {
param.mTrack[index] = 0xFF;
}
}
break;
case AVRCP_EVENT_PLAY_POS_CHANGED:
// If no track is selected, return 0xFFFFFFFF in the INTERIM response
mPlayPosChangedNotifyType = AVRCP_NTF_INTERIM;
if (mPlayStatus == ControlPlayStatus::PLAYSTATUS_PLAYING) {
param.mSongPos = mPosition;
} else {
param.mSongPos = 0xFFFFFFFF;
}
mPlaybackInterval = aParam;
break;
case AVRCP_EVENT_APP_SETTINGS_CHANGED:
mAppSettingsChangedNotifyType = AVRCP_NTF_INTERIM;
param.mNumAttr = 2;
param.mIds[0] = AVRCP_PLAYER_ATTRIBUTE_REPEAT;
param.mValues[0] = AVRCP_PLAYER_VAL_OFF_REPEAT;
param.mIds[1] = AVRCP_PLAYER_ATTRIBUTE_SHUFFLE;
param.mValues[1] = AVRCP_PLAYER_VAL_OFF_SHUFFLE;
break;
default:
break;
}
sBtAvrcpInterface->RegisterNotificationRsp(aEvent, AVRCP_NTF_INTERIM,
param, nullptr);
}
void
BluetoothAvrcpManager::GetAlbum(nsAString& aAlbum)
{
aAlbum.Assign(mAlbum);
}
uint32_t
BluetoothAvrcpManager::GetDuration()
{
return mDuration;
}
ControlPlayStatus
BluetoothAvrcpManager::GetPlayStatus()
{
return mPlayStatus;
}
uint32_t
BluetoothAvrcpManager::GetPosition()
{
return mPosition;
}
uint64_t
BluetoothAvrcpManager::GetMediaNumber()
{
return mMediaNumber;
}
uint64_t
BluetoothAvrcpManager::GetTotalMediaNumber()
{
return mTotalMediaCount;
}
void
BluetoothAvrcpManager::GetTitle(nsAString& aTitle)
{
aTitle.Assign(mTitle);
}
void
BluetoothAvrcpManager::GetArtist(nsAString& aArtist)
{
aArtist.Assign(mArtist);
}
/*
* Notifications
*/
void
BluetoothAvrcpManager::GetPlayStatusNotification()
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothService* bs = BluetoothService::Get();
if (!bs) {
return;
}
bs->DistributeSignal(NS_LITERAL_STRING(REQUEST_MEDIA_PLAYSTATUS_ID),
NS_LITERAL_STRING(KEY_ADAPTER));
}
/* Player application settings is optional for AVRCP 1.3. B2G
* currently does not support player-application-setting related
* functionality.
*/
void
BluetoothAvrcpManager::ListPlayerAppAttrNotification()
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Support AVRCP application-setting-related functions
}
void
BluetoothAvrcpManager::ListPlayerAppValuesNotification(
BluetoothAvrcpPlayerAttribute aAttrId)
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Support AVRCP application-setting-related functions
}
void
BluetoothAvrcpManager::GetPlayerAppValueNotification(
uint8_t aNumAttrs, const BluetoothAvrcpPlayerAttribute* aAttrs)
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Support AVRCP application-setting-related functions
}
void
BluetoothAvrcpManager::GetPlayerAppAttrsTextNotification(
uint8_t aNumAttrs, const BluetoothAvrcpPlayerAttribute* aAttrs)
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Support AVRCP application-setting-related functions
}
void
BluetoothAvrcpManager::GetPlayerAppValuesTextNotification(
uint8_t aAttrId, uint8_t aNumVals, const uint8_t* aValues)
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Support AVRCP application-setting-related functions
}
void
BluetoothAvrcpManager::SetPlayerAppValueNotification(
const BluetoothAvrcpPlayerSettings& aSettings)
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Support AVRCP application-setting-related functions
}
/* This method returns element attributes, which are requested from
* CT. Unlike BlueZ it calls only UpdateMetaData. Bluedroid does not cache
* meta-data information, but instead uses |GetElementAttrNotifications|
* and |GetElementAttrRsp| request them.
*/
void
BluetoothAvrcpManager::GetElementAttrNotification(
uint8_t aNumAttrs, const BluetoothAvrcpMediaAttribute* aAttrs)
{
MOZ_ASSERT(NS_IsMainThread());
nsAutoArrayPtr<BluetoothAvrcpElementAttribute> attrs(
new BluetoothAvrcpElementAttribute[aNumAttrs]);
for (uint8_t i = 0; i < aNumAttrs; ++i) {
attrs[i].mId = aAttrs[i];
ConvertAttributeString(
static_cast<BluetoothAvrcpMediaAttribute>(attrs[i].mId),
attrs[i].mValue);
}
MOZ_ASSERT(sBtAvrcpInterface);
sBtAvrcpInterface->GetElementAttrRsp(aNumAttrs, attrs, nullptr);
}
void
BluetoothAvrcpManager::RegisterNotificationNotification(
BluetoothAvrcpEvent aEvent, uint32_t aParam)
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothAvrcpManager* avrcp = BluetoothAvrcpManager::Get();
if (!avrcp) {
return;
}
avrcp->UpdateRegisterNotification(aEvent, aParam);
}
/* This method is used to get CT features from the Feature Bit Mask. If
* Advanced Control Player bit is set, the CT supports volume sync (absolute
* volume feature). If Browsing bit is set, AVRCP 1.4 Browse feature will be
* supported.
*/
void
BluetoothAvrcpManager::RemoteFeatureNotification(
const BluetoothAddress& aBdAddr, unsigned long aFeatures)
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Support AVRCP 1.4 absolute volume/browse
}
/* This method is used to get notifications about volume changes on the
* remote car kit (if it supports AVRCP 1.4), not notification from phone.
*/
void
BluetoothAvrcpManager::VolumeChangeNotification(uint8_t aVolume,
uint8_t aCType)
{
MOZ_ASSERT(NS_IsMainThread());
// TODO: Support AVRCP 1.4 absolute volume/browse
}
void
BluetoothAvrcpManager::PassthroughCmdNotification(int aId, int aKeyState)
{
MOZ_ASSERT(NS_IsMainThread());
// Fast-forward and rewind key events won't be generated from bluedroid
// stack after ANDROID_VERSION > 18, but via passthrough callback.
nsAutoString name;
NS_ENSURE_TRUE_VOID(aKeyState == AVRC_KEY_PRESS_STATE ||
aKeyState == AVRC_KEY_RELEASE_STATE);
switch (aId) {
case AVRC_ID_FAST_FOR:
if (aKeyState == AVRC_KEY_PRESS_STATE) {
name.AssignLiteral("media-fast-forward-button-press");
} else {
name.AssignLiteral("media-fast-forward-button-release");
}
break;
case AVRC_ID_REWIND:
if (aKeyState == AVRC_KEY_PRESS_STATE) {
name.AssignLiteral("media-rewind-button-press");
} else {
name.AssignLiteral("media-rewind-button-release");
}
break;
default:
BT_WARNING("Unable to handle the unknown PassThrough command %d", aId);
return;
}
NS_NAMED_LITERAL_STRING(type, "media-button");
BroadcastSystemMessage(type, BluetoothValue(name));
}
NS_IMPL_ISUPPORTS(BluetoothAvrcpManager, nsIObserver)