Bug 1057337: Add Bluetooth A2DP and AVRCP notifications, r=shuang
authorThomas Zimmermann <tdz@users.sourceforge.net>
Mon, 01 Sep 2014 10:11:53 +0200
changeset 224433 13e870775e1b0868855b6568af6db56af718862c
parent 224432 0a5a5ebd748d5a41e3f99998f1fdf80e7b616330
child 224434 b0682f061278ce75c5ff58e3b5460654e922f01d
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshuang
bugs1057337
milestone34.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1057337: Add Bluetooth A2DP and AVRCP notifications, r=shuang This patch adds A2DP and AVRCP notifications and callbacks to Gecko's Bluedroid interface. Support for AVRCP depends on the version of the Android base system and is only available on versions 18, 19 or later.
dom/bluetooth/BluetoothCommon.h
dom/bluetooth/bluedroid/BluetoothInterface.cpp
dom/bluetooth/bluedroid/BluetoothInterface.h
--- a/dom/bluetooth/BluetoothCommon.h
+++ b/dom/bluetooth/BluetoothCommon.h
@@ -334,26 +334,49 @@ typedef mozilla::Observer<BluetoothSigna
 enum BluetoothObjectType {
   TYPE_MANAGER = 0,
   TYPE_ADAPTER = 1,
   TYPE_DEVICE = 2,
 
   TYPE_INVALID
 };
 
+enum BluetoothA2dpAudioState {
+  A2DP_AUDIO_STATE_REMOTE_SUSPEND,
+  A2DP_AUDIO_STATE_STOPPED,
+  A2DP_AUDIO_STATE_STARTED,
+};
+
+enum BluetoothA2dpConnectionState {
+  A2DP_CONNECTION_STATE_DISCONNECTED,
+  A2DP_CONNECTION_STATE_CONNECTING,
+  A2DP_CONNECTION_STATE_CONNECTED,
+  A2DP_CONNECTION_STATE_DISCONNECTING
+};
+
 enum ControlPlayStatus {
   PLAYSTATUS_STOPPED  = 0x00,
   PLAYSTATUS_PLAYING  = 0x01,
   PLAYSTATUS_PAUSED   = 0x02,
   PLAYSTATUS_FWD_SEEK = 0x03,
   PLAYSTATUS_REV_SEEK = 0x04,
   PLAYSTATUS_UNKNOWN,
   PLAYSTATUS_ERROR    = 0xFF,
 };
 
+enum BluetoothAvrcpMediaAttribute {
+  AVRCP_MEDIA_ATTRIBUTE_TITLE,
+  AVRCP_MEDIA_ATTRIBUTE_ARTIST,
+  AVRCP_MEDIA_ATTRIBUTE_ALBUM,
+  AVRCP_MEDIA_ATTRIBUTE_TRACK_NUM,
+  AVRCP_MEDIA_ATTRIBUTE_NUM_TRACKS,
+  AVRCP_MEDIA_ATTRIBUTE_GENRE,
+  AVRCP_MEDIA_ATTRIBUTE_PLAYING_TIME
+};
+
 enum BluetoothAvrcpPlayerAttribute {
   AVRCP_PLAYER_ATTRIBUTE_EQUALIZER,
   AVRCP_PLAYER_ATTRIBUTE_REPEAT,
   AVRCP_PLAYER_ATTRIBUTE_SHUFFLE,
   AVRCP_PLAYER_ATTRIBUTE_SCAN
 };
 
 enum BluetoothAvrcpStatus {
@@ -373,25 +396,38 @@ enum BluetoothAvrcpEvent {
   AVRCP_EVENT_APP_SETTINGS_CHANGED
 };
 
 enum BluetoothAvrcpNotification {
   AVRCP_NTF_INTERIM,
   AVRCP_NTF_CHANGED
 };
 
+enum BluetoothAvrcpRemoteFeature {
+  AVRCP_REMOTE_FEATURE_NONE,
+  AVRCP_REMOTE_FEATURE_METADATA,
+  AVRCP_REMOTE_FEATURE_ABSOLUTE_VOLUME,
+  AVRCP_REMOTE_FEATURE_BROWSE
+};
+
 struct BluetoothAvrcpElementAttribute {
   uint32_t mId;
   nsString mValue;
 };
 
 struct BluetoothAvrcpNotificationParam {
   ControlPlayStatus mPlayStatus;
   uint8_t mTrack[8];
   uint32_t mSongPos;
   uint8_t mNumAttr;
   uint8_t mIds[256];
   uint8_t mValues[256];
 };
 
+struct BluetoothAvrcpPlayerSettings {
+  uint8_t mNumAttr;
+  uint8_t mIds[256];
+  uint8_t mValues[256];
+};
+
 END_BLUETOOTH_NAMESPACE
 
 #endif // mozilla_dom_bluetooth_bluetoothcommon_h__
--- a/dom/bluetooth/bluedroid/BluetoothInterface.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothInterface.cpp
@@ -431,28 +431,16 @@ Convert(bt_device_type_t aIn, BluetoothD
   };
   if (aIn >= MOZ_ARRAY_LENGTH(sDeviceType)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = sDeviceType[aIn];
   return NS_OK;
 }
 
-#if ANDROID_VERSION >= 18
-static nsresult
-Convert(const bt_remote_version_t& aIn, BluetoothRemoteInfo& aOut)
-{
-  aOut.mVerMajor = aIn.version;
-  aOut.mVerMinor = aIn.sub_ver;
-  aOut.mManufacturer = aIn.manufacturer;
-
-  return NS_OK;
-}
-#endif
-
 static nsresult
 Convert(const bt_service_record_t& aIn, BluetoothServiceRecord& aOut)
 {
   nsresult rv = Convert(aIn.uuid, aOut.mUuid);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
@@ -710,18 +698,63 @@ Convert(bthf_volume_type_t aIn, Bluetoot
   };
   if (aIn >= MOZ_ARRAY_LENGTH(sVolumeType)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = sVolumeType[aIn];
   return NS_OK;
 }
 
+static nsresult
+Convert(btav_connection_state_t aIn, BluetoothA2dpConnectionState& aOut)
+{
+  static const BluetoothA2dpConnectionState sConnectionState[] = {
+    CONVERT(BTAV_CONNECTION_STATE_DISCONNECTED,
+      A2DP_CONNECTION_STATE_DISCONNECTED),
+    CONVERT(BTAV_CONNECTION_STATE_CONNECTING,
+      A2DP_CONNECTION_STATE_CONNECTING),
+    CONVERT(BTAV_CONNECTION_STATE_CONNECTED,
+      A2DP_CONNECTION_STATE_CONNECTED),
+    CONVERT(BTAV_CONNECTION_STATE_DISCONNECTING,
+      A2DP_CONNECTION_STATE_DISCONNECTING),
+  };
+  if (aIn >= MOZ_ARRAY_LENGTH(sConnectionState)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sConnectionState[aIn];
+  return NS_OK;
+}
+
+static nsresult
+Convert(btav_audio_state_t aIn, BluetoothA2dpAudioState& aOut)
+{
+  static const BluetoothA2dpAudioState sAudioState[] = {
+    CONVERT(BTAV_AUDIO_STATE_REMOTE_SUSPEND, A2DP_AUDIO_STATE_REMOTE_SUSPEND),
+    CONVERT(BTAV_AUDIO_STATE_STOPPED, A2DP_AUDIO_STATE_STOPPED),
+    CONVERT(BTAV_AUDIO_STATE_STARTED, A2DP_AUDIO_STATE_STARTED),
+  };
+  if (aIn >= MOZ_ARRAY_LENGTH(sAudioState)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sAudioState[aIn];
+  return NS_OK;
+}
+
 #if ANDROID_VERSION >= 18
 static nsresult
+Convert(const bt_remote_version_t& aIn, BluetoothRemoteInfo& aOut)
+{
+  aOut.mVerMajor = aIn.version;
+  aOut.mVerMinor = aIn.sub_ver;
+  aOut.mManufacturer = aIn.manufacturer;
+
+  return NS_OK;
+}
+
+static nsresult
 Convert(ControlPlayStatus aIn, btrc_play_status_t& aOut)
 {
   static const btrc_play_status_t sPlayStatus[] = {
     CONVERT(PLAYSTATUS_STOPPED, BTRC_PLAYSTATE_STOPPED),
     CONVERT(PLAYSTATUS_PLAYING, BTRC_PLAYSTATE_PLAYING),
     CONVERT(PLAYSTATUS_PAUSED, BTRC_PLAYSTATE_PAUSED),
     CONVERT(PLAYSTATUS_FWD_SEEK, BTRC_PLAYSTATE_FWD_SEEK),
     CONVERT(PLAYSTATUS_REV_SEEK, BTRC_PLAYSTATE_REV_SEEK)
@@ -745,16 +778,33 @@ Convert(enum BluetoothAvrcpPlayerAttribu
   if (aIn >= MOZ_ARRAY_LENGTH(sPlayerAttr)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = sPlayerAttr[aIn];
   return NS_OK;
 }
 
 static nsresult
+Convert(btrc_player_attr_t aIn, enum BluetoothAvrcpPlayerAttribute& aOut)
+{
+  static const BluetoothAvrcpPlayerAttribute sPlayerAttr[] = {
+    CONVERT(0, static_cast<BluetoothAvrcpPlayerAttribute>(0)), // invalid, [0] required by gcc
+    CONVERT(BTRC_PLAYER_ATTR_EQUALIZER, AVRCP_PLAYER_ATTRIBUTE_EQUALIZER),
+    CONVERT(BTRC_PLAYER_ATTR_REPEAT, AVRCP_PLAYER_ATTRIBUTE_REPEAT),
+    CONVERT(BTRC_PLAYER_ATTR_SHUFFLE, AVRCP_PLAYER_ATTRIBUTE_SHUFFLE),
+    CONVERT(BTRC_PLAYER_ATTR_SCAN, AVRCP_PLAYER_ATTRIBUTE_SCAN)
+  };
+  if (!aIn || aIn >= MOZ_ARRAY_LENGTH(sPlayerAttr)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sPlayerAttr[aIn];
+  return NS_OK;
+}
+
+static nsresult
 Convert(enum BluetoothAvrcpStatus aIn, btrc_status_t& aOut)
 {
   static const btrc_status_t sStatus[] = {
     CONVERT(AVRCP_STATUS_BAD_COMMAND, BTRC_STS_BAD_CMD),
     CONVERT(AVRCP_STATUS_BAD_PARAMETER, BTRC_STS_BAD_PARAM),
     CONVERT(AVRCP_STATUS_NOT_FOUND, BTRC_STS_NOT_FOUND),
     CONVERT(AVRCP_STATUS_INTERNAL_ERROR, BTRC_STS_INTERNAL_ERR),
     CONVERT(AVRCP_STATUS_SUCCESS, BTRC_STS_NO_ERROR)
@@ -780,16 +830,57 @@ Convert(enum BluetoothAvrcpEvent aIn, bt
   if (aIn >= MOZ_ARRAY_LENGTH(sEventId)) {
     return NS_ERROR_ILLEGAL_VALUE;
   }
   aOut = sEventId[aIn];
   return NS_OK;
 }
 
 static nsresult
+Convert(btrc_event_id_t aIn, enum BluetoothAvrcpEvent& aOut)
+{
+  static const BluetoothAvrcpEvent sEventId[] = {
+    CONVERT(0, static_cast<BluetoothAvrcpEvent>(0)), // invalid, [0] required by gcc
+    CONVERT(BTRC_EVT_PLAY_STATUS_CHANGED, AVRCP_EVENT_PLAY_STATUS_CHANGED),
+    CONVERT(BTRC_EVT_TRACK_CHANGE, AVRCP_EVENT_TRACK_CHANGE),
+    CONVERT(BTRC_EVT_TRACK_REACHED_END, AVRCP_EVENT_TRACK_REACHED_END),
+    CONVERT(BTRC_EVT_TRACK_REACHED_START, AVRCP_EVENT_TRACK_REACHED_START),
+    CONVERT(BTRC_EVT_PLAY_POS_CHANGED, AVRCP_EVENT_PLAY_POS_CHANGED),
+    CONVERT(6, static_cast<BluetoothAvrcpEvent>(0)), // invalid, [6] required by gcc
+    CONVERT(7, static_cast<BluetoothAvrcpEvent>(0)), // invalid, [7] required by gcc
+    CONVERT(BTRC_EVT_APP_SETTINGS_CHANGED, AVRCP_EVENT_APP_SETTINGS_CHANGED)
+  };
+  if (!aIn || aIn >= MOZ_ARRAY_LENGTH(sEventId)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sEventId[aIn];
+  return NS_OK;
+}
+
+static nsresult
+Convert(btrc_media_attr_t aIn, enum BluetoothAvrcpMediaAttribute& aOut)
+{
+  static const BluetoothAvrcpMediaAttribute sEventId[] = {
+    CONVERT(0, static_cast<BluetoothAvrcpMediaAttribute>(0)), // invalid, [0] required by gcc
+    CONVERT(BTRC_MEDIA_ATTR_TITLE, AVRCP_MEDIA_ATTRIBUTE_TITLE),
+    CONVERT(BTRC_MEDIA_ATTR_ARTIST, AVRCP_MEDIA_ATTRIBUTE_ARTIST),
+    CONVERT(BTRC_MEDIA_ATTR_ALBUM, AVRCP_MEDIA_ATTRIBUTE_ALBUM),
+    CONVERT(BTRC_MEDIA_ATTR_TRACK_NUM, AVRCP_MEDIA_ATTRIBUTE_TRACK_NUM),
+    CONVERT(BTRC_MEDIA_ATTR_NUM_TRACKS, AVRCP_MEDIA_ATTRIBUTE_NUM_TRACKS),
+    CONVERT(BTRC_MEDIA_ATTR_GENRE, AVRCP_MEDIA_ATTRIBUTE_GENRE),
+    CONVERT(BTRC_MEDIA_ATTR_PLAYING_TIME, AVRCP_MEDIA_ATTRIBUTE_PLAYING_TIME)
+  };
+  if (!aIn || aIn >= MOZ_ARRAY_LENGTH(sEventId)) {
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+  aOut = sEventId[aIn];
+  return NS_OK;
+}
+
+static nsresult
 Convert(enum BluetoothAvrcpNotification aIn, btrc_notification_type_t& aOut)
 {
   static const btrc_notification_type_t sNotificationType[] = {
     CONVERT(AVRCP_NTF_INTERIM, BTRC_NOTIFICATION_TYPE_INTERIM),
     CONVERT(AVRCP_NTF_CHANGED, BTRC_NOTIFICATION_TYPE_CHANGED)
   };
   if (aIn >= MOZ_ARRAY_LENGTH(sNotificationType)) {
     return NS_ERROR_ILLEGAL_VALUE;
@@ -806,17 +897,38 @@ Convert(const BluetoothAvrcpElementAttri
 
   memcpy(aOut.text, value.get(), len);
   aOut.text[len] = '\0';
   aOut.attr_id = aIn.mId;
 
   return NS_OK;
 }
 
-#endif
+static nsresult
+Convert(const btrc_player_settings_t& aIn, BluetoothAvrcpPlayerSettings& aOut)
+{
+  aOut.mNumAttr = aIn.num_attr;
+  memcpy(aOut.mIds, aIn.attr_ids, aIn.num_attr);
+  memcpy(aOut.mValues, aIn.attr_values, aIn.num_attr);
+
+  return NS_OK;
+}
+#endif // ANDROID_VERSION >= 18
+
+#if ANDROID_VERSION >= 19
+static nsresult
+Convert(btrc_remote_features_t aIn, unsigned long& aOut)
+{
+  /* The input type's name is misleading. The converted value is
+   * actually a bitmask.
+   */
+  aOut = static_cast<unsigned long>(aIn);
+  return NS_OK;
+}
+#endif // ANDROID_VERSION >= 19
 
 /* |ConvertArray| is a helper for converting arrays. Pass an
  * instance of this structure as the first argument to |Convert|
  * to convert an array. The output type has to support the array
  * subscript operator.
  */
 template <typename T>
 struct ConvertArray
@@ -2711,16 +2823,79 @@ DispatchBluetoothA2dpResult(
   }
   nsresult rv = NS_DispatchToMainThread(runnable);
   if (NS_FAILED(rv)) {
     BT_WARNING("NS_DispatchToMainThread failed: %X", rv);
   }
   return rv;
 }
 
+// Notification handling
+//
+
+BluetoothA2dpNotificationHandler::~BluetoothA2dpNotificationHandler()
+{ }
+
+static BluetoothA2dpNotificationHandler* sA2dpNotificationHandler;
+
+struct BluetoothA2dpCallback
+{
+  class A2dpNotificationHandlerWrapper
+  {
+  public:
+    typedef BluetoothA2dpNotificationHandler ObjectType;
+
+    static ObjectType* GetInstance()
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+
+      return sA2dpNotificationHandler;
+    }
+  };
+
+  // Notifications
+
+  typedef BluetoothNotificationRunnable2<A2dpNotificationHandlerWrapper,
+                                         void,
+                                         BluetoothA2dpConnectionState,
+                                         nsString,
+                                         BluetoothA2dpConnectionState,
+                                         const nsAString&>
+    ConnectionStateNotification;
+
+  typedef BluetoothNotificationRunnable2<A2dpNotificationHandlerWrapper,
+                                         void,
+                                         BluetoothA2dpAudioState,
+                                         nsString,
+                                         BluetoothA2dpAudioState,
+                                         const nsAString&>
+    AudioStateNotification;
+
+  // Bluedroid A2DP callbacks
+
+  static void
+  ConnectionState(btav_connection_state_t aState, bt_bdaddr_t* aBdAddr)
+  {
+    ConnectionStateNotification::Dispatch(
+      &BluetoothA2dpNotificationHandler::ConnectionStateNotification,
+      aState, aBdAddr);
+  }
+
+  static void
+  AudioState(btav_audio_state_t aState, bt_bdaddr_t* aBdAddr)
+  {
+    AudioStateNotification::Dispatch(
+      &BluetoothA2dpNotificationHandler::AudioStateNotification,
+      aState, aBdAddr);
+  }
+};
+
+// Interface
+//
+
 BluetoothA2dpInterface::BluetoothA2dpInterface(
   const btav_interface_t* aInterface)
 : mInterface(aInterface)
 {
   MOZ_ASSERT(mInterface);
 }
 
 BluetoothA2dpInterface::~BluetoothA2dpInterface()
@@ -2829,16 +3004,209 @@ DispatchBluetoothAvrcpResult(
       &BluetoothAvrcpResultHandler::OnError, aStatus);
   }
   nsresult rv = NS_DispatchToMainThread(runnable);
   if (NS_FAILED(rv)) {
     BT_WARNING("NS_DispatchToMainThread failed: %X", rv);
   }
   return rv;
 }
+#endif
+
+// Notification handling
+//
+
+BluetoothAvrcpNotificationHandler::~BluetoothAvrcpNotificationHandler()
+{ }
+
+#if ANDROID_VERSION >= 18
+static BluetoothAvrcpNotificationHandler* sAvrcpNotificationHandler;
+
+struct BluetoothAvrcpCallback
+{
+  class AvrcpNotificationHandlerWrapper
+  {
+  public:
+    typedef BluetoothAvrcpNotificationHandler ObjectType;
+
+    static ObjectType* GetInstance()
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+
+      return sAvrcpNotificationHandler;
+    }
+  };
+
+  // Notifications
+
+  typedef BluetoothNotificationRunnable0<AvrcpNotificationHandlerWrapper,
+                                         void>
+    GetPlayStatusNotification;
+
+  typedef BluetoothNotificationRunnable0<AvrcpNotificationHandlerWrapper,
+                                         void>
+    ListPlayerAppAttrNotification;
+
+  typedef BluetoothNotificationRunnable1<AvrcpNotificationHandlerWrapper,
+                                         void,
+                                         BluetoothAvrcpPlayerAttribute>
+    ListPlayerAppValuesNotification;
+
+  typedef BluetoothNotificationRunnable2<AvrcpNotificationHandlerWrapper, void,
+    uint8_t, nsAutoArrayPtr<BluetoothAvrcpPlayerAttribute>,
+    uint8_t, const BluetoothAvrcpPlayerAttribute*>
+    GetPlayerAppValueNotification;
+
+  typedef BluetoothNotificationRunnable2<AvrcpNotificationHandlerWrapper, void,
+    uint8_t, nsAutoArrayPtr<BluetoothAvrcpPlayerAttribute>,
+    uint8_t, const BluetoothAvrcpPlayerAttribute*>
+    GetPlayerAppAttrsTextNotification;
+
+  typedef BluetoothNotificationRunnable3<AvrcpNotificationHandlerWrapper,
+                                         void,
+                                         uint8_t, uint8_t,
+                                         nsAutoArrayPtr<uint8_t>,
+                                         uint8_t, uint8_t, const uint8_t*>
+    GetPlayerAppValuesTextNotification;
+
+  typedef BluetoothNotificationRunnable1<AvrcpNotificationHandlerWrapper,
+                                         void,
+                                         BluetoothAvrcpPlayerSettings,
+                                         const BluetoothAvrcpPlayerSettings&>
+    SetPlayerAppValueNotification;
+
+  typedef BluetoothNotificationRunnable2<AvrcpNotificationHandlerWrapper, void,
+    uint8_t, nsAutoArrayPtr<BluetoothAvrcpMediaAttribute>,
+    uint8_t, const BluetoothAvrcpMediaAttribute*>
+    GetElementAttrNotification;
+
+  typedef BluetoothNotificationRunnable2<AvrcpNotificationHandlerWrapper,
+                                         void,
+                                         BluetoothAvrcpEvent, uint32_t>
+    RegisterNotificationNotification;
+
+#if ANDROID_VERSION >= 19
+  typedef BluetoothNotificationRunnable2<AvrcpNotificationHandlerWrapper,
+                                         void,
+                                         nsString, unsigned long,
+                                         const nsAString&>
+    RemoteFeatureNotification;
+
+  typedef BluetoothNotificationRunnable2<AvrcpNotificationHandlerWrapper,
+                                         void,
+                                         uint8_t, uint8_t>
+    VolumeChangeNotification;
+
+  typedef BluetoothNotificationRunnable2<AvrcpNotificationHandlerWrapper,
+                                         void,
+                                         int, int>
+    PassthroughCmdNotification;
+#endif // ANDROID_VERSION >= 19
+
+  // Bluedroid AVRCP callbacks
+
+  static void
+  GetPlayStatus()
+  {
+    GetPlayStatusNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::GetPlayStatusNotification);
+  }
+
+  static void
+  ListPlayerAppAttr()
+  {
+    ListPlayerAppAttrNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::ListPlayerAppAttrNotification);
+  }
+
+  static void
+  ListPlayerAppValues(btrc_player_attr_t aAttrId)
+  {
+    ListPlayerAppValuesNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::ListPlayerAppValuesNotification,
+      aAttrId);
+  }
+
+  static void
+  GetPlayerAppValue(uint8_t aNumAttrs, btrc_player_attr_t* aAttrs)
+  {
+    GetPlayerAppValueNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::GetPlayerAppValueNotification,
+      aNumAttrs, ConvertArray<btrc_player_attr_t>(aAttrs, aNumAttrs));
+  }
+
+  static void
+  GetPlayerAppAttrsText(uint8_t aNumAttrs, btrc_player_attr_t* aAttrs)
+  {
+    GetPlayerAppAttrsTextNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::GetPlayerAppAttrsTextNotification,
+      aNumAttrs, ConvertArray<btrc_player_attr_t>(aAttrs, aNumAttrs));
+  }
+
+  static void
+  GetPlayerAppValuesText(uint8_t aAttrId, uint8_t aNumVals, uint8_t* aVals)
+  {
+    GetPlayerAppValuesTextNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::GetPlayerAppValuesTextNotification,
+      aAttrId, aNumVals, ConvertArray<uint8_t>(aVals, aNumVals));
+  }
+
+  static void
+  SetPlayerAppValue(btrc_player_settings_t* aVals)
+  {
+    SetPlayerAppValueNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::SetPlayerAppValueNotification,
+      *aVals);
+  }
+
+  static void
+  GetElementAttr(uint8_t aNumAttrs, btrc_media_attr_t* aAttrs)
+  {
+    GetElementAttrNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::GetElementAttrNotification,
+      aNumAttrs, ConvertArray<btrc_media_attr_t>(aAttrs, aNumAttrs));
+  }
+
+  static void
+  RegisterNotification(btrc_event_id_t aEvent, uint32_t aParam)
+  {
+    RegisterNotificationNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::RegisterNotificationNotification,
+      aEvent, aParam);
+  }
+
+#if ANDROID_VERSION >= 19
+  static void
+  RemoteFeature(bt_bdaddr_t* aBdAddr, btrc_remote_features_t aFeatures)
+  {
+    RemoteFeatureNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::RemoteFeatureNotification,
+      aBdAddr, aFeatures);
+  }
+
+  static void
+  VolumeChange(uint8_t aVolume, uint8_t aCType)
+  {
+    VolumeChangeNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::VolumeChangeNotification,
+      aVolume, aCType);
+  }
+
+  static void
+  PassthroughCmd(int aId, int aKeyState)
+  {
+    PassthroughCmdNotification::Dispatch(
+      &BluetoothAvrcpNotificationHandler::PassthroughCmdNotification,
+      aId, aKeyState);
+  }
+#endif // ANDROID_VERSION >= 19
+};
+
+// Interface
+//
 
 BluetoothAvrcpInterface::BluetoothAvrcpInterface(
   const btrc_interface_t* aInterface)
 : mInterface(aInterface)
 {
   MOZ_ASSERT(mInterface);
 }
 
--- a/dom/bluetooth/bluedroid/BluetoothInterface.h
+++ b/dom/bluetooth/bluedroid/BluetoothInterface.h
@@ -260,16 +260,36 @@ protected:
 private:
   const bthf_interface_t* mInterface;
 };
 
 //
 // Bluetooth Advanced Audio Interface
 //
 
+class BluetoothA2dpNotificationHandler
+{
+public:
+  virtual ~BluetoothA2dpNotificationHandler();
+
+  virtual void
+  ConnectionStateNotification(BluetoothA2dpConnectionState aState,
+                              const nsAString& aBdAddr)
+  { }
+
+  virtual void
+  AudioStateNotification(BluetoothA2dpAudioState aState,
+                         const nsAString& aBdAddr)
+  { }
+
+protected:
+  BluetoothA2dpNotificationHandler()
+  { }
+};
+
 class BluetoothA2dpResultHandler
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothA2dpResultHandler)
 
   virtual ~BluetoothA2dpResultHandler() { }
 
   virtual void OnError(BluetoothStatus aStatus)
@@ -304,16 +324,79 @@ protected:
 private:
   const btav_interface_t* mInterface;
 };
 
 //
 // Bluetooth AVRCP Interface
 //
 
+class BluetoothAvrcpNotificationHandler
+{
+public:
+  virtual ~BluetoothAvrcpNotificationHandler();
+
+  virtual void
+  GetPlayStatusNotification()
+  { }
+
+  virtual void
+  ListPlayerAppAttrNotification()
+  { }
+
+  virtual void
+  ListPlayerAppValuesNotification(BluetoothAvrcpPlayerAttribute aAttrId)
+  { }
+
+  virtual void
+  GetPlayerAppValueNotification(uint8_t aNumAttrs,
+                                const BluetoothAvrcpPlayerAttribute* aAttrs)
+  { }
+
+  virtual void
+  GetPlayerAppAttrsTextNotification(uint8_t aNumAttrs,
+                                    const BluetoothAvrcpPlayerAttribute* aAttrs)
+  { }
+
+  virtual void
+  GetPlayerAppValuesTextNotification(uint8_t aAttrId, uint8_t aNumVals,
+                                     const uint8_t* aValues)
+  { }
+
+  virtual void
+  SetPlayerAppValueNotification(const BluetoothAvrcpPlayerSettings& aSettings)
+  { }
+
+  virtual void
+  GetElementAttrNotification(uint8_t aNumAttrs,
+                             const BluetoothAvrcpMediaAttribute* aAttrs)
+  { }
+
+  virtual void
+  RegisterNotificationNotification(BluetoothAvrcpEvent aEvent,
+                                   uint32_t aParam)
+  { }
+
+  virtual void
+  RemoteFeatureNotification(const nsAString& aBdAddr, unsigned long aFeatures)
+  { }
+
+  virtual void
+  VolumeChangeNotification(uint8_t aVolume, uint8_t aCType)
+  { }
+
+  virtual void
+  PassthroughCmdNotification(int aId, int aKeyState)
+  { }
+
+protected:
+  BluetoothAvrcpNotificationHandler()
+  { }
+};
+
 class BluetoothAvrcpResultHandler
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BluetoothAvrcpResultHandler)
 
   virtual ~BluetoothAvrcpResultHandler() { }
 
   virtual void OnError(BluetoothStatus aStatus)