Bug 939500 - [bluedroid] Avrcp 1.3 prototype, r=echou

This commit is contained in:
Shawn Huang 2013-11-22 23:40:22 +08:00
Родитель a15afd889a
Коммит fa230ff4f0
3 изменённых файлов: 435 добавлений и 8 удалений

Просмотреть файл

@ -10,6 +10,7 @@
#include <hardware/bluetooth.h>
#include <hardware/bt_av.h>
#include <hardware/bt_rc.h>
#include "BluetoothCommon.h"
#include "BluetoothService.h"
@ -31,9 +32,9 @@ namespace {
StaticRefPtr<BluetoothA2dpManager> sBluetoothA2dpManager;
bool sInShutdown = false;
static const btav_interface_t* sBtA2dpInterface;
static const btrc_interface_t* sBtAvrcpInterface;
} // anonymous namespace
class SinkPropertyChangedHandler : public nsRunnable
{
public:
@ -50,6 +51,7 @@ public:
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
NS_ENSURE_TRUE(a2dp, NS_ERROR_FAILURE);
a2dp->HandleSinkPropertyChanged(mSignal);
return NS_OK;
}
@ -57,6 +59,122 @@ private:
BluetoothSignal mSignal;
};
class RequestPlayStatusTask : public nsRunnable
{
public:
RequestPlayStatusTask()
{
MOZ_ASSERT(!NS_IsMainThread());
}
nsresult Run()
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothSignal signal(NS_LITERAL_STRING(REQUEST_MEDIA_PLAYSTATUS_ID),
NS_LITERAL_STRING(KEY_ADAPTER),
InfallibleTArray<BluetoothNamedValue>());
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
bs->DistributeSignal(signal);
return NS_OK;
}
};
class UpdateRegisterNotificationTask : public nsRunnable
{
public:
UpdateRegisterNotificationTask(btrc_event_id_t aEventId, uint32_t aParam)
: mEventId(aEventId)
, mParam(aParam)
{
MOZ_ASSERT(!NS_IsMainThread());
}
nsresult Run()
{
MOZ_ASSERT(NS_IsMainThread());
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
NS_ENSURE_TRUE(a2dp, NS_OK);
a2dp->UpdateRegisterNotification(mEventId, mParam);
return NS_OK;
}
private:
btrc_event_id_t mEventId;
uint32_t mParam;
};
/*
* This function maps attribute id and returns corresponding values
* Attribute id refers to btrc_media_attr_t in bt_rc.h
*/
static void
ConvertAttributeString(int aAttrId, nsAString& aAttrStr)
{
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
NS_ENSURE_TRUE_VOID(a2dp);
switch (aAttrId) {
case BTRC_MEDIA_ATTR_TITLE:
a2dp->GetTitle(aAttrStr);
break;
case BTRC_MEDIA_ATTR_ARTIST:
a2dp->GetArtist(aAttrStr);
break;
case BTRC_MEDIA_ATTR_ALBUM:
a2dp->GetAlbum(aAttrStr);
break;
case BTRC_MEDIA_ATTR_TRACK_NUM:
aAttrStr.AppendInt(a2dp->GetMediaNumber());
break;
case BTRC_MEDIA_ATTR_NUM_TRACKS:
aAttrStr.AppendInt(a2dp->GetTotalMediaNumber());
break;
case BTRC_MEDIA_ATTR_GENRE:
// TODO: we currently don't support genre from music player
aAttrStr.Truncate();
break;
case BTRC_MEDIA_ATTR_PLAYING_TIME:
aAttrStr.AppendInt(a2dp->GetDuration());
break;
}
}
class UpdateElementAttrsTask : public nsRunnable
{
public:
UpdateElementAttrsTask(uint8_t aNumAttr, btrc_media_attr_t* aPlayerAttrs)
: mNumAttr(aNumAttr)
, mPlayerAttrs(aPlayerAttrs)
{
MOZ_ASSERT(!NS_IsMainThread());
}
nsresult Run()
{
MOZ_ASSERT(NS_IsMainThread());
btrc_element_attr_val_t* attrs = new btrc_element_attr_val_t[mNumAttr];
for (int i = 0; i < mNumAttr; i++) {
nsAutoString attrText;
attrs[i].attr_id = mPlayerAttrs[i];
ConvertAttributeString(mPlayerAttrs[i], attrText);
strcpy((char *)attrs[i].text, NS_ConvertUTF16toUTF8(attrText).get());
}
NS_ENSURE_TRUE(sBtAvrcpInterface, NS_OK);
sBtAvrcpInterface->get_element_attr_rsp(mNumAttr, attrs);
return NS_OK;
}
private:
uint8_t mNumAttr;
btrc_media_attr_t* mPlayerAttrs;
};
NS_IMETHODIMP
BluetoothA2dpManager::Observe(nsISupports* aSubject,
const char* aTopic,
@ -147,12 +265,131 @@ A2dpAudioStateCallback(btav_audio_state_t aState,
NS_DispatchToMainThread(new SinkPropertyChangedHandler(signal));
}
/*
* Avrcp 1.3 callbacks
*/
/*
* This function is to request Gaia player application to update
* current play status.
* Callback for play status request
*/
static void
AvrcpGetPlayStatusCallback()
{
MOZ_ASSERT(!NS_IsMainThread());
NS_DispatchToMainThread(new RequestPlayStatusTask());
}
/*
* This function is trying to get element attributes, which request from CT
* Unlike BlueZ only calls UpdateMetaData, bluedroid does not cache meta data
* information, but instead uses callback AvrcpGetElementAttrCallback and
* call get_element_attr_rsp() to reply request.
*
* Callback to fetch the get element attributes of the current song
* aNumAttr: It represents the number of attributes requested in aPlayerAttrs
* aPlayerAttrs: It represents Attribute Ids
*/
static void
AvrcpGetElementAttrCallback(uint8_t aNumAttr, btrc_media_attr_t* aPlayerAttrs)
{
MOZ_ASSERT(!NS_IsMainThread());
NS_DispatchToMainThread(new UpdateElementAttrsTask(aNumAttr, aPlayerAttrs));
}
/*
* Callback for register notification (Play state change/track change/...)
* To reply RegisterNotification INTERIM response
* See AVRCP 1.3 Spec 25.2
* aParam: It only valids if event_id is BTRC_EVT_PLAY_POS_CHANGED,
* which is playback interval time
*/
static void
AvrcpRegisterNotificationCallback(btrc_event_id_t aEventId, uint32_t aParam)
{
MOZ_ASSERT(!NS_IsMainThread());
NS_DispatchToMainThread(new UpdateRegisterNotificationTask(aEventId, aParam));
}
/*
* Player application settings is optional for Avrcp 1.3
* B2G 1.3 currently does not support Player application setting
* related functions. Support Player Setting in the future version
*/
static void
AvrcpListPlayerAppAttributeCallback()
{
MOZ_ASSERT(!NS_IsMainThread());
// TODO: Support avrcp application setting related functions
}
static void
AvrcpListPlayerAppValuesCallback(btrc_player_attr_t aAttrId)
{
MOZ_ASSERT(!NS_IsMainThread());
// TODO: Support avrcp application setting related functions
}
static void
AvrcpGetPlayerAppValueCallback(uint8_t aNumAttr,
btrc_player_attr_t* aPlayerAttrs)
{
MOZ_ASSERT(!NS_IsMainThread());
// TODO: Support avrcp application setting related functions
}
static void
AvrcpGetPlayerAppAttrsTextCallback(uint8_t aNumAttr,
btrc_player_attr_t* PlayerAttrs)
{
MOZ_ASSERT(!NS_IsMainThread());
// TODO: Support avrcp application setting related functions
}
static void
AvrcpGetPlayerAppValuesTextCallback(uint8_t aAttrId, uint8_t aNumVal,
uint8_t* PlayerVals)
{
MOZ_ASSERT(!NS_IsMainThread());
// TODO: Support avrcp application setting related functions
}
static void
AvrcpSetPlayerAppValueCallback(btrc_player_settings_t* aPlayerVals)
{
MOZ_ASSERT(!NS_IsMainThread());
// TODO: Support avrcp application setting related functions
}
static btav_callbacks_t sBtA2dpCallbacks = {
sizeof(sBtA2dpCallbacks),
A2dpConnectionStateCallback,
A2dpAudioStateCallback
};
static btrc_callbacks_t sBtAvrcpCallbacks = {
sizeof(sBtAvrcpCallbacks),
AvrcpGetPlayStatusCallback,
AvrcpListPlayerAppAttributeCallback,
AvrcpListPlayerAppValuesCallback,
AvrcpGetPlayerAppValueCallback,
AvrcpGetPlayerAppAttrsTextCallback,
AvrcpGetPlayerAppValuesTextCallback,
AvrcpSetPlayerAppValueCallback,
AvrcpGetElementAttrCallback,
AvrcpRegisterNotificationCallback
};
/*
* This function will be only called when Bluetooth is turning on.
* It is important to register a2dp callbacks before enable() gets called.
@ -164,15 +401,27 @@ BluetoothA2dpManager::Init()
{
const bt_interface_t* btInf = GetBluetoothInterface();
NS_ENSURE_TRUE(btInf, false);
sBtA2dpInterface = (btav_interface_t *)btInf->
get_profile_interface(BT_PROFILE_ADVANCED_AUDIO_ID);
NS_ENSURE_TRUE(sBtA2dpInterface, false);
int ret = sBtA2dpInterface->init(&sBtA2dpCallbacks);
if (ret != BT_STATUS_SUCCESS) {
BT_LOGR("failed to init a2dp module");
BT_LOGR("Warning: failed to init a2dp module");
return false;
}
sBtAvrcpInterface = (btrc_interface_t *)btInf->
get_profile_interface(BT_PROFILE_AV_RC_ID);
NS_ENSURE_TRUE(sBtAvrcpInterface, false);
ret = sBtAvrcpInterface->init(&sBtAvrcpCallbacks);
if (ret != BT_STATUS_SUCCESS) {
BT_LOGR("Warning: failed to init avrcp module");
return false;
}
return true;
}
@ -487,6 +736,10 @@ BluetoothA2dpManager::IsConnected()
return mA2dpConnected;
}
/*
* In bluedroid stack case, there is no interface to know exactly
* avrcp connection status. All connection are managed by bluedroid stack.
*/
void
BluetoothA2dpManager::SetAvrcpConnected(bool aConnected)
{
@ -502,6 +755,10 @@ BluetoothA2dpManager::IsAvrcpConnected()
return mAvrcpConnected;
}
/*
* This function only updates meta data in BluetoothA2dpManager
* Send "Get Element Attributes response" in AvrcpGetElementAttrCallback
*/
void
BluetoothA2dpManager::UpdateMetaData(const nsAString& aTitle,
const nsAString& aArtist,
@ -510,6 +767,36 @@ BluetoothA2dpManager::UpdateMetaData(const nsAString& aTitle,
uint32_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 == BTRC_NOTIFICATION_TYPE_INTERIM) {
btrc_register_notification_t param;
// convert to network big endian format
// since track stores as uint8[8]
// 56 = 8 * (BTRC_UID_SIZE -1)
for (int i = 0; i < BTRC_UID_SIZE; ++i) {
param.track[i] = (aMediaNumber >> (56 - 8 * i));
}
mTrackChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_TRACK_CHANGE,
BTRC_NOTIFICATION_TYPE_CHANGED,
&param);
if (mPlayPosChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
param.song_pos = mPosition;
// EVENT_PLAYBACK_POS_CHANGED shall be notified if changed current track
mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
sBtAvrcpInterface->register_notification_rsp(
BTRC_EVT_PLAY_POS_CHANGED,
BTRC_NOTIFICATION_TYPE_CHANGED,
&param);
}
}
mTitle.Assign(aTitle);
mArtist.Assign(aArtist);
mAlbum.Assign(aAlbum);
@ -518,20 +805,93 @@ BluetoothA2dpManager::UpdateMetaData(const nsAString& aTitle,
mDuration = aDuration;
}
/*
* This function is to reply AvrcpGetPlayStatusCallback (play-status-request)
* from media player application (Gaia side)
*/
void
BluetoothA2dpManager::UpdatePlayStatus(uint32_t aDuration,
uint32_t aPosition,
ControlPlayStatus aPlayStatus)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
// when play status changed, send both play status and position
if (mPlayStatus != aPlayStatus &&
mPlayStatusChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
btrc_register_notification_t param;
param.play_status = (btrc_play_status_t)aPlayStatus;
mPlayStatusChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_STATUS_CHANGED,
BTRC_NOTIFICATION_TYPE_CHANGED,
&param);
}
if (mPosition != aPosition &&
mPlayPosChangedNotifyType == BTRC_NOTIFICATION_TYPE_INTERIM) {
btrc_register_notification_t param;
param.song_pos = aPosition;
mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_CHANGED;
sBtAvrcpInterface->register_notification_rsp(BTRC_EVT_PLAY_POS_CHANGED,
BTRC_NOTIFICATION_TYPE_CHANGED,
&param);
}
sBtAvrcpInterface->get_play_status_rsp((btrc_play_status_t)aPlayStatus,
aDuration, aPosition);
mDuration = aDuration;
mPosition = aPosition;
mPlayStatus = aPlayStatus;
}
/*
* This function handles RegisterNotification request from
* AvrcpRegisterNotificationCallback, which updates current
* track/status/position status.
*
* aParam is only valid when position changed
*/
void
BluetoothA2dpManager::UpdateRegisterNotification(int aEventId, int aParam)
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE_VOID(sBtAvrcpInterface);
btrc_register_notification_t param;
switch (aEventId) {
case BTRC_EVT_PLAY_STATUS_CHANGED:
mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_INTERIM;
param.play_status = (btrc_play_status_t)mPlayStatus;
break;
case BTRC_EVT_TRACK_CHANGE:
mTrackChangedNotifyType = BTRC_NOTIFICATION_TYPE_INTERIM;
// needs to convert to network big endian format since track stores
// as uint8[8]. 56 = 8 * (BTRC_UID_SIZE -1)
for (int i = 0; i < BTRC_UID_SIZE; ++i) {
param.track[i] = (mMediaNumber >> (56 - 8 * i));
}
break;
case BTRC_EVT_PLAY_POS_CHANGED:
mPlayPosChangedNotifyType = BTRC_NOTIFICATION_TYPE_INTERIM;
param.song_pos = mPosition;
mPlaybackInterval = aParam;
break;
default:
break;
}
sBtAvrcpInterface->register_notification_rsp((btrc_event_id_t)aEventId,
BTRC_NOTIFICATION_TYPE_INTERIM,
&param);
}
void
BluetoothA2dpManager::GetAlbum(nsAString& aAlbum)
{
aAlbum.Assign(mAlbum);
aAlbum.Assign(mAlbum);
}
uint32_t
@ -558,11 +918,23 @@ BluetoothA2dpManager::GetMediaNumber()
return mMediaNumber;
}
uint32_t
BluetoothA2dpManager::GetTotalMediaNumber()
{
return mTotalMediaCount;
}
void
BluetoothA2dpManager::GetTitle(nsAString& aTitle)
{
aTitle.Assign(mTitle);
}
void
BluetoothA2dpManager::GetArtist(nsAString& aArtist)
{
aArtist.Assign(mArtist);
}
NS_IMPL_ISUPPORTS1(BluetoothA2dpManager, nsIObserver)

Просмотреть файл

@ -12,7 +12,6 @@
#include "BluetoothProfileManagerBase.h"
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothA2dpManager : public BluetoothProfileManagerBase
{
public:
@ -52,13 +51,15 @@ public:
void UpdatePlayStatus(uint32_t aDuration,
uint32_t aPosition,
ControlPlayStatus aPlayStatus);
void UpdateRegisterNotification(int aEventId, int aParam);
void GetAlbum(nsAString& aAlbum);
uint32_t GetDuration();
ControlPlayStatus GetPlayStatus();
uint32_t GetPosition();
uint32_t GetMediaNumber();
uint32_t GetTotalMediaNumber();
void GetTitle(nsAString& aTitle);
void GetArtist(nsAString& aArtist);
private:
class SinkPropertyChangedHandler;
BluetoothA2dpManager();
@ -82,7 +83,27 @@ private:
uint32_t mMediaNumber;
uint32_t mTotalMediaCount;
uint32_t mPosition;
/*
* mPlaybackInterval specifies the time interval (in seconds) at which
* the change in playback position will be notified. If the song is being
* forwarded / rewound, a notification will be received whenever the playback
* position will change by this value.
*/
uint32_t mPlaybackInterval;
ControlPlayStatus mPlayStatus;
/*
* Notification types: 1. INTERIM 2. CHANGED
* 1. The initial response to this Notify command shall be an INTERIM
* response with current status.
* 2. The following response shall be a CHANGED response with the updated
* status.
* mPlayStatusChangedNotifType, mTrackChangedNotifType,
* mPlayPosChangedNotifType represents current RegisterNotification
* notification type.
*/
int mPlayStatusChangedNotifyType;
int mTrackChangedNotifyType;
int mPlayPosChangedNotifyType;
};
END_BLUETOOTH_NAMESPACE

Просмотреть файл

@ -160,6 +160,26 @@ ClassToIcon(uint32_t aClass, nsAString& aRetIcon)
break;
}
}
static ControlPlayStatus
PlayStatusStringToControlPlayStatus(const nsAString& aPlayStatus)
{
ControlPlayStatus playStatus = ControlPlayStatus::PLAYSTATUS_UNKNOWN;
if (aPlayStatus.EqualsLiteral("STOPPED")) {
playStatus = ControlPlayStatus::PLAYSTATUS_STOPPED;
} else if (aPlayStatus.EqualsLiteral("PLAYING")) {
playStatus = ControlPlayStatus::PLAYSTATUS_PLAYING;
} else if (aPlayStatus.EqualsLiteral("PAUSED")) {
playStatus = ControlPlayStatus::PLAYSTATUS_PAUSED;
} else if (aPlayStatus.EqualsLiteral("FWD_SEEK")) {
playStatus = ControlPlayStatus::PLAYSTATUS_FWD_SEEK;
} else if (aPlayStatus.EqualsLiteral("REV_SEEK")) {
playStatus = ControlPlayStatus::PLAYSTATUS_REV_SEEK;
} else if (aPlayStatus.EqualsLiteral("ERROR")) {
playStatus = ControlPlayStatus::PLAYSTATUS_ERROR;
}
return playStatus;
}
static bool
IsReady()
@ -1300,7 +1320,12 @@ BluetoothServiceBluedroid::SendMetaData(const nsAString& aTitle,
int64_t aDuration,
BluetoothReplyRunnable* aRunnable)
{
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
if (a2dp) {
a2dp->UpdateMetaData(aTitle, aArtist, aAlbum, aMediaNumber,
aTotalMediaCount, aDuration);
}
DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
}
void
@ -1309,14 +1334,23 @@ BluetoothServiceBluedroid::SendPlayStatus(
const nsAString& aPlayStatus,
BluetoothReplyRunnable* aRunnable)
{
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
if (a2dp) {
ControlPlayStatus playStatus =
PlayStatusStringToControlPlayStatus(aPlayStatus);
a2dp->UpdatePlayStatus(aDuration, aPosition, playStatus);
}
DispatchBluetoothReply(aRunnable, BluetoothValue(true), EmptyString());
}
void
BluetoothServiceBluedroid::UpdatePlayStatus(
uint32_t aDuration, uint32_t aPosition, ControlPlayStatus aPlayStatus)
{
// We don't need this function for bluedroid.
// In bluez, it only calls dbus api
// But it does not update BluetoothA2dpManager member fields
MOZ_ASSERT(false);
}
nsresult