Bug 842948 - Patch 1: Implement API for AVRCP 1.3, r=echou, sr=mrbkap

This commit is contained in:
Gina Yeh 2013-07-29 17:32:34 +08:00
parent 0dc50dc734
commit a07f368f4e
12 changed files with 395 additions and 7 deletions

View File

@ -17,6 +17,7 @@
#include "nsDOMClassInfo.h"
#include "nsIDOMBluetoothDeviceEvent.h"
#include "nsTArrayHelpers.h"
#include "DictionaryHelpers.h"
#include "DOMRequest.h"
#include "nsThreadUtils.h"
@ -833,4 +834,71 @@ BluetoothAdapter::IsScoConnected(nsIDOMDOMRequest** aRequest)
return NS_OK;
}
NS_IMETHODIMP
BluetoothAdapter::SendMediaMetaData(const JS::Value& aOptions,
nsIDOMDOMRequest** aRequest)
{
idl::MediaMetaData metadata;
nsresult rv;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
NS_ENSURE_SUCCESS(rv, rv);
AutoPushJSContext cx(sc->GetNativeContext());
rv = metadata.Init(cx, &aOptions);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMDOMRequest> req;
rv = PrepareDOMRequest(GetOwner(), getter_AddRefs(req));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<BluetoothReplyRunnable> results =
new BluetoothVoidReplyRunnable(req);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
bs->SendMetaData(metadata.title,
metadata.artist,
metadata.album,
metadata.mediaNumber,
metadata.totalMediaCount,
metadata.duration,
results);
req.forget(aRequest);
return NS_OK;
}
NS_IMETHODIMP
BluetoothAdapter::SendMediaPlayStatus(const JS::Value& aOptions,
nsIDOMDOMRequest** aRequest)
{
idl::MediaPlayStatus status;
nsresult rv;
nsIScriptContext* sc = GetContextForEventHandlers(&rv);
NS_ENSURE_SUCCESS(rv, rv);
AutoPushJSContext cx(sc->GetNativeContext());
rv = status.Init(cx, &aOptions);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMDOMRequest> req;
rv = PrepareDOMRequest(GetOwner(), getter_AddRefs(req));
NS_ENSURE_SUCCESS(rv, rv);
nsRefPtr<BluetoothReplyRunnable> results =
new BluetoothVoidReplyRunnable(req);
BluetoothService* bs = BluetoothService::Get();
NS_ENSURE_TRUE(bs, NS_ERROR_FAILURE);
bs->SendPlayStatus(status.duration,
status.position,
status.playStatus,
results);
req.forget(aRequest);
return NS_OK;
}
NS_IMPL_EVENT_HANDLER(BluetoothAdapter, devicefound)

View File

@ -85,6 +85,16 @@ enum BluetoothObjectType {
TYPE_INVALID
};
enum ControlPlayStatus {
PLAYSTATUS_STOPPED = 0x00,
PLAYSTATUS_PLAYING = 0x01,
PLAYSTATUS_PAUSED = 0x02,
PLAYSTATUS_FWD_SEEK = 0x03,
PLAYSTATUS_REV_SEEK = 0x04,
PLAYSTATUS_UNKNOWN,
PLAYSTATUS_ERROR = 0xFF,
};
END_BLUETOOTH_NAMESPACE
#endif // mozilla_dom_bluetooth_bluetoothcommon_h__

View File

@ -270,6 +270,21 @@ public:
virtual void
IsScoConnected(BluetoothReplyRunnable* aRunnable) = 0;
virtual void
SendMetaData(const nsAString& aTitle,
const nsAString& aArtist,
const nsAString& aAlbum,
uint32_t aMediaNumber,
uint32_t aTotalMediaCount,
uint32_t aDuration,
BluetoothReplyRunnable* aRunnable) = 0;
virtual void
SendPlayStatus(uint32_t aDuration,
uint32_t aPosition,
const nsAString& aPlayStatus,
BluetoothReplyRunnable* aRunnable) = 0;
virtual nsresult
SendSinkMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage) = 0;

View File

@ -230,6 +230,10 @@ BluetoothParent::RecvPBluetoothRequestConstructor(
return actor->DoRequest(aRequest.get_DisconnectScoRequest());
case Request::TIsScoConnectedRequest:
return actor->DoRequest(aRequest.get_IsScoConnectedRequest());
case Request::TSendMetaDataRequest:
return actor->DoRequest(aRequest.get_SendMetaDataRequest());
case Request::TSendPlayStatusRequest:
return actor->DoRequest(aRequest.get_SendPlayStatusRequest());
default:
MOZ_CRASH("Unknown type!");
}
@ -603,3 +607,32 @@ BluetoothRequestParent::DoRequest(const IsScoConnectedRequest& aRequest)
mService->IsScoConnected(mReplyRunnable.get());
return true;
}
bool
BluetoothRequestParent::DoRequest(const SendMetaDataRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TSendMetaDataRequest);
mService->SendMetaData(aRequest.title(),
aRequest.artist(),
aRequest.album(),
aRequest.mediaNumber(),
aRequest.totalMediaCount(),
aRequest.duration(),
mReplyRunnable.get());
return true;
}
bool
BluetoothRequestParent::DoRequest(const SendPlayStatusRequest& aRequest)
{
MOZ_ASSERT(mService);
MOZ_ASSERT(mRequestType == Request::TSendPlayStatusRequest);
mService->SendPlayStatus(aRequest.duration(),
aRequest.position(),
aRequest.playStatus(),
mReplyRunnable.get());
return true;
}

View File

@ -194,6 +194,12 @@ protected:
bool
DoRequest(const IsScoConnectedRequest& aRequest);
bool
DoRequest(const SendMetaDataRequest& aRequest);
bool
DoRequest(const SendPlayStatusRequest& aRequest);
};
END_BLUETOOTH_NAMESPACE

View File

@ -339,6 +339,32 @@ BluetoothServiceChildProcess::IsScoConnected(BluetoothReplyRunnable* aRunnable)
SendRequest(aRunnable, IsScoConnectedRequest());
}
void
BluetoothServiceChildProcess::SendMetaData(const nsAString& aTitle,
const nsAString& aArtist,
const nsAString& aAlbum,
uint32_t aMediaNumber,
uint32_t aTotalMediaCount,
uint32_t aDuration,
BluetoothReplyRunnable* aRunnable)
{
SendRequest(aRunnable,
SendMetaDataRequest(nsString(aTitle), nsString(aArtist),
nsString(aAlbum), aMediaNumber,
aTotalMediaCount, aDuration));
}
void
BluetoothServiceChildProcess::SendPlayStatus(uint32_t aDuration,
uint32_t aPosition,
const nsAString& aPlayStatus,
BluetoothReplyRunnable* aRunnable)
{
SendRequest(aRunnable,
SendPlayStatusRequest(aDuration, aPosition,
nsString(aPlayStatus)));
}
nsresult
BluetoothServiceChildProcess::HandleStartup()
{
@ -388,3 +414,4 @@ BluetoothServiceChildProcess::SendSinkMessage(const nsAString& aDeviceAddresses,
{
MOZ_CRASH("This should never be called!");
}

View File

@ -152,6 +152,21 @@ public:
virtual void
IsScoConnected(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual void
SendMetaData(const nsAString& aTitle,
const nsAString& aArtist,
const nsAString& aAlbum,
uint32_t aMediaNumber,
uint32_t aTotalMediaCount,
uint32_t aDuration,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual void
SendPlayStatus(uint32_t aDuration,
uint32_t aPosition,
const nsAString& aPlayStatus,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual nsresult
SendSinkMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage) MOZ_OVERRIDE;

View File

@ -141,6 +141,23 @@ struct IsScoConnectedRequest
{
};
struct SendMetaDataRequest
{
nsString title;
nsString artist;
nsString album;
uint32_t mediaNumber;
uint32_t totalMediaCount;
uint32_t duration;
};
struct SendPlayStatusRequest
{
uint32_t duration;
uint32_t position;
nsString playStatus;
};
union Request
{
DefaultAdapterPathRequest;
@ -167,6 +184,8 @@ union Request
ConnectScoRequest;
DisconnectScoRequest;
IsScoConnectedRequest;
SendMetaDataRequest;
SendPlayStatusRequest;
};
protocol PBluetooth

View File

@ -63,14 +63,16 @@ USING_BLUETOOTH_NAMESPACE
#define B2G_AGENT_CAPABILITIES "DisplayYesNo"
#define DBUS_MANAGER_IFACE BLUEZ_DBUS_BASE_IFC ".Manager"
#define DBUS_ADAPTER_IFACE BLUEZ_DBUS_BASE_IFC ".Adapter"
#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
#define DBUS_AGENT_IFACE BLUEZ_DBUS_BASE_IFC ".Agent"
#define DBUS_SINK_IFACE BLUEZ_DBUS_BASE_IFC ".AudioSink"
#define DBUS_DEVICE_IFACE BLUEZ_DBUS_BASE_IFC ".Device"
#define DBUS_AGENT_IFACE BLUEZ_DBUS_BASE_IFC ".Agent"
#define DBUS_SINK_IFACE BLUEZ_DBUS_BASE_IFC ".AudioSink"
#define DBUS_CTL_IFACE BLUEZ_DBUS_BASE_IFC ".Control"
#define BLUEZ_DBUS_BASE_PATH "/org/bluez"
#define BLUEZ_DBUS_BASE_IFC "org.bluez"
#define BLUEZ_ERROR_IFC "org.bluez.Error"
#define PROP_DEVICE_CONNECTED_TYPE "org.bluez.device.conn.type"
#define ERR_A2DP_IS_DISCONNECTED "A2dpIsDisconnected"
#define ERR_AVRCP_IS_DISCONNECTED "AvrcpIsDisconnected"
typedef struct {
const char* name;
@ -123,6 +125,10 @@ static Properties sSinkProperties[] = {
{"Playing", DBUS_TYPE_BOOLEAN}
};
static Properties sControlProperties[] = {
{"Connected", DBUS_TYPE_BOOLEAN}
};
static const char* sBluetoothDBusIfaces[] =
{
DBUS_MANAGER_IFACE,
@ -140,7 +146,8 @@ static const char* sBluetoothDBusSignals[] =
"type='signal',interface='org.bluez.Network'",
"type='signal',interface='org.bluez.NetworkServer'",
"type='signal',interface='org.bluez.HealthDevice'",
"type='signal',interface='org.bluez.AudioSink'"
"type='signal',interface='org.bluez.AudioSink'",
"type='signal',interface='org.bluez.Control'"
};
/**
@ -2802,3 +2809,134 @@ BluetoothDBusService::IsScoConnected(BluetoothReplyRunnable* aRunnable)
hfp->IsScoConnected(), EmptyString());
}
void
BluetoothDBusService::SendMetaData(const nsAString& aTitle,
const nsAString& aArtist,
const nsAString& aAlbum,
uint32_t aMediaNumber,
uint32_t aTotalMediaCount,
uint32_t aDuration,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
if (!IsReady()) {
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
return;
}
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
NS_ENSURE_TRUE_VOID(a2dp);
nsAutoString address;
a2dp->GetAddress(address);
nsString objectPath =
GetObjectPathFromAddress(sAdapterPath, address);
nsCString tempTitle = NS_ConvertUTF16toUTF8(aTitle);
nsCString tempArtist = NS_ConvertUTF16toUTF8(aArtist);
nsCString tempAlbum = NS_ConvertUTF16toUTF8(aAlbum);
nsCString tempMediaNumber, tempTotalMediaCount, tempDuration;
tempMediaNumber.AppendInt(aMediaNumber);
tempTotalMediaCount.AppendInt(aTotalMediaCount);
tempDuration.AppendInt(aDuration);
const char* title = tempTitle.get();
const char* album = tempAlbum.get();
const char* artist = tempArtist.get();
const char* mediaNumber = tempMediaNumber.get();
const char* totalMediaCount = tempTotalMediaCount.get();
const char* duration = tempDuration.get();
nsRefPtr<BluetoothReplyRunnable> runnable(aRunnable);
bool ret = dbus_func_args_async(mConnection,
-1,
GetVoidCallback,
(void*)runnable.get(),
NS_ConvertUTF16toUTF8(objectPath).get(),
DBUS_CTL_IFACE,
"UpdateMetaData",
DBUS_TYPE_STRING, &title,
DBUS_TYPE_STRING, &artist,
DBUS_TYPE_STRING, &album,
DBUS_TYPE_STRING, &mediaNumber,
DBUS_TYPE_STRING, &totalMediaCount,
DBUS_TYPE_STRING, &duration,
DBUS_TYPE_INVALID);
NS_ENSURE_TRUE_VOID(ret);
runnable.forget();
}
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;
}
void
BluetoothDBusService::SendPlayStatus(uint32_t aDuration,
uint32_t aPosition,
const nsAString& aPlayStatus,
BluetoothReplyRunnable* aRunnable)
{
MOZ_ASSERT(NS_IsMainThread());
if (!IsReady()) {
NS_NAMED_LITERAL_STRING(errorStr, "Bluetooth service is not ready yet!");
DispatchBluetoothReply(aRunnable, BluetoothValue(), errorStr);
return;
}
ControlPlayStatus playStatus =
PlayStatusStringToControlPlayStatus(aPlayStatus);
if (playStatus == ControlPlayStatus::PLAYSTATUS_UNKNOWN) {
DispatchBluetoothReply(aRunnable, BluetoothValue(),
NS_LITERAL_STRING("Invalid play status"));
return;
}
BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
NS_ENSURE_TRUE_VOID(a2dp);
nsAutoString address;
a2dp->GetAddress(address);
nsString objectPath =
GetObjectPathFromAddress(sAdapterPath, address);
nsRefPtr<BluetoothReplyRunnable> runnable(aRunnable);
uint32_t tempPlayStatus = playStatus;
bool ret = dbus_func_args_async(mConnection,
-1,
GetVoidCallback,
(void*)runnable.get(),
NS_ConvertUTF16toUTF8(objectPath).get(),
DBUS_CTL_IFACE,
"UpdatePlayStatus",
DBUS_TYPE_UINT32, &aDuration,
DBUS_TYPE_UINT32, &aPosition,
DBUS_TYPE_UINT32, &tempPlayStatus,
DBUS_TYPE_INVALID);
NS_ENSURE_TRUE_VOID(ret);
runnable.forget();
}

View File

@ -138,6 +138,21 @@ public:
virtual void
IsScoConnected(BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual void
SendMetaData(const nsAString& aTitle,
const nsAString& aArtist,
const nsAString& aAlbum,
uint32_t aMediaNumber,
uint32_t aTotalMediaCount,
uint32_t aDuration,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual void
SendPlayStatus(uint32_t aDuration,
uint32_t aPosition,
const nsAString& aPlayStatus,
BluetoothReplyRunnable* aRunnable) MOZ_OVERRIDE;
virtual nsresult
SendSinkMessage(const nsAString& aDeviceAddresses,
const nsAString& aMessage) MOZ_OVERRIDE;
@ -147,13 +162,16 @@ private:
const char* aInterface,
void (*aCB)(DBusMessage *, void *),
BluetoothReplyRunnable* aRunnable);
nsresult SendDiscoveryMessage(const char* aMessageName,
BluetoothReplyRunnable* aRunnable);
nsresult SendSetPropertyMessage(const char* aInterface,
const BluetoothNamedValue& aValue,
BluetoothReplyRunnable* aRunnable);
void DisconnectAllAcls(const nsAString& aAdapterPath);
};
END_BLUETOOTH_NAMESPACE

View File

@ -6,11 +6,44 @@
#include "nsIDOMEventTarget.idl"
/**
* MediaMetadata and MediaPlayStatus are used to keep data from Applications.
* Please see specification of AVRCP 1.3 for more details.
*
* @title: track title
* @artist: artist name
* @album: album name
* @mediaNumber: track number
* @totalMediaCount: number of tracks in the album
* @duration: playing time (ms)
*/
dictionary MediaMetaData
{
DOMString title;
DOMString artist;
DOMString album;
unsigned long mediaNumber;
unsigned long totalMediaCount;
unsigned long duration;
};
/**
* @duration: current track length (ms)
* @position: playing time (ms)
* @playStatus: STOPPED/PLAYING/PAUSED/FWD_SEEK/REV_SEEK/ERROR
*/
dictionary MediaPlayStatus
{
unsigned long duration;
unsigned long position;
DOMString playStatus;
};
interface nsIDOMDOMRequest;
interface nsIDOMBlob;
interface nsIDOMBluetoothDevice;
[scriptable, builtinclass, uuid(7058d214-3575-4913-99ad-0980296f617a)]
[scriptable, builtinclass, uuid(1a0c6c90-23e3-4f4c-8076-98e4341c2024)]
interface nsIDOMBluetoothAdapter : nsIDOMEventTarget
{
readonly attribute DOMString address;
@ -58,6 +91,10 @@ interface nsIDOMBluetoothAdapter : nsIDOMEventTarget
nsIDOMDOMRequest stopSendingFile(in DOMString aDeviceAddress);
nsIDOMDOMRequest confirmReceivingFile(in DOMString aDeviceAddress, in bool aConfirmation);
// AVRCP 1.3 methods
nsIDOMDOMRequest sendMediaMetaData(in jsval aOptions);
nsIDOMDOMRequest sendMediaPlayStatus(in jsval aOptions);
// Connect/Disconnect SCO (audio) connection
nsIDOMDOMRequest connectSco();
nsIDOMDOMRequest disconnectSco();

View File

@ -13,7 +13,9 @@ dictionaries = [
[ 'CameraSelector', 'nsIDOMCameraManager.idl' ],
[ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ],
[ 'SmsThreadListItem', 'nsIMobileMessageCallback.idl' ],
[ 'MmsAttachment', 'nsIDOMMozMmsMessage.idl' ]
[ 'MmsAttachment', 'nsIDOMMozMmsMessage.idl' ],
[ 'MediaMetaData', 'nsIDOMBluetoothAdapter.idl'],
[ 'MediaPlayStatus', 'nsIDOMBluetoothAdapter.idl']
]
# include file names