Bug 1180554 - Dispatch events to PBAP event handlers when the PBAP requests comes. r=btian, r=mrbkap
☠☠ backed out by 00b847293e70 ☠ ☠
authorJamin Liu <jaliu@mozilla.com>
Fri, 21 Aug 2015 09:51:41 +0800
changeset 287085 f7e0cd74c082d5bee3493e39dea048a58af2dd59
parent 287084 c5f6995a464d7b07926520339acedfa0a355823f
child 287086 5bdcc058e6d6ab6b29cf70754be30100dccb543e
push id4651
push userdrno@ohlmeier.org
push dateFri, 21 Aug 2015 17:48:29 +0000
reviewersbtian, mrbkap
bugs1180554
milestone43.0a1
Bug 1180554 - Dispatch events to PBAP event handlers when the PBAP requests comes. r=btian, r=mrbkap
dom/base/nsGkAtomList.h
dom/bindings/Bindings.conf
dom/bluetooth/BluetoothCommon.h
dom/bluetooth/BluetoothPbapRequestHandle.cpp
dom/bluetooth/BluetoothPbapRequestHandle.h
dom/bluetooth/bluedroid/BluetoothPbapManager.cpp
dom/bluetooth/bluedroid/BluetoothPbapManager.h
dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
dom/bluetooth/bluetooth2/BluetoothAdapter.h
dom/bluetooth/bluetooth2/ipc/BluetoothTypes.ipdlh
dom/bluetooth/moz.build
dom/events/test/test_all_synthetic_events.html
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/BluetoothAdapter.webidl
dom/webidl/BluetoothPbapParameters.webidl
dom/webidl/BluetoothPbapRequestHandle.webidl
dom/webidl/BluetoothPhonebookPullingEvent.webidl
dom/webidl/BluetoothVCardListingEvent.webidl
dom/webidl/BluetoothVCardPullingEvent.webidl
dom/webidl/moz.build
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -847,16 +847,19 @@ GK_ATOM(onpaste, "onpaste")
 GK_ATOM(onpendingchange, "onpendingchange")
 GK_ATOM(onpichange, "onpichange")
 GK_ATOM(onpicture, "onpicture")
 GK_ATOM(onpopuphidden, "onpopuphidden")
 GK_ATOM(onpopuphiding, "onpopuphiding")
 GK_ATOM(onpopupshowing, "onpopupshowing")
 GK_ATOM(onpopupshown, "onpopupshown")
 GK_ATOM(onpreviewstatechange, "onpreviewstatechange")
+GK_ATOM(onpullphonebookreq, "onpullphonebookreq")
+GK_ATOM(onpullvcardentryreq, "onpullvcardentryreq")
+GK_ATOM(onpullvcardlistingreq, "onpullvcardlistingreq")
 GK_ATOM(onpush, "onpush")
 GK_ATOM(onpushsubscriptionchange, "onpushsubscriptionchange")
 GK_ATOM(onpschange, "onpschange")
 GK_ATOM(onptychange, "onptychange")
 GK_ATOM(onradiostatechange, "onradiostatechange")
 GK_ATOM(onrdsdisabled, "onrdsdisabled")
 GK_ATOM(onrdsenabled, "onrdsenabled")
 GK_ATOM(onreaderror, "onreaderror")
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -194,16 +194,20 @@ DOMInterfaces = {
     'nativeType': 'mozilla::dom::bluetooth::BluetoothPairingHandle',
 },
 
 'BluetoothPairingListener': {
     'nativeType':
       'mozilla::dom::bluetooth::BluetoothPairingListener',
 },
 
+'BluetoothPbapRequestHandle': {
+    'nativeType': 'mozilla::dom::bluetooth::BluetoothPbapRequestHandle',
+},
+
 'BoxObject': {
     'resultNotAddRefed': ['element'],
 },
 
 'Cache': {
     'implicitJSContext': [ 'add', 'addAll' ],
     'nativeType': 'mozilla::dom::cache::Cache',
 },
--- a/dom/bluetooth/BluetoothCommon.h
+++ b/dom/bluetooth/BluetoothCommon.h
@@ -209,16 +209,23 @@ extern bool gBluetoothDebugFlag;
 
 /**
  * When receiving a query about current play status from remote device, we'll
  * dispatch an event.
  */
 #define REQUEST_MEDIA_PLAYSTATUS_ID          "requestmediaplaystatus"
 
 /**
+ * When receiving a PBAP request from a remote device, we'll dispatch an event.
+ */
+#define PULL_PHONEBOOK_REQ_ID                "pullphonebookreq"
+#define PULL_VCARD_ENTRY_REQ_ID              "pullvcardentryreq"
+#define PULL_VCARD_LISTING_REQ_ID            "pullvcardlistingreq"
+
+/**
  * When the value of a characteristic of a remote BLE device changes, we'll
  * dispatch an event
  */
 #define GATT_CHARACTERISTIC_CHANGED_ID       "characteristicchanged"
 
 /**
  * When a remote BLE device gets connected / disconnected, we'll dispatch an
  * event.
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothPbapRequestHandle.cpp
@@ -0,0 +1,79 @@
+/* -*- 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 "BluetoothCommon.h"
+#include "BluetoothPbapRequestHandle.h"
+#include "BluetoothReplyRunnable.h"
+#include "BluetoothService.h"
+
+#include "mozilla/dom/BluetoothPbapRequestHandleBinding.h"
+
+using namespace mozilla;
+using namespace dom;
+
+USING_BLUETOOTH_NAMESPACE
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(BluetoothPbapRequestHandle, mOwner)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BluetoothPbapRequestHandle)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BluetoothPbapRequestHandle)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BluetoothPbapRequestHandle)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+BluetoothPbapRequestHandle::BluetoothPbapRequestHandle(nsPIDOMWindow* aOwner)
+  : mOwner(aOwner)
+{
+  MOZ_ASSERT(aOwner);
+}
+
+BluetoothPbapRequestHandle::~BluetoothPbapRequestHandle()
+{
+}
+
+already_AddRefed<BluetoothPbapRequestHandle>
+BluetoothPbapRequestHandle::Create(nsPIDOMWindow* aOwner)
+{
+  MOZ_ASSERT(aOwner);
+
+  nsRefPtr<BluetoothPbapRequestHandle> handle =
+    new BluetoothPbapRequestHandle(aOwner);
+
+  return handle.forget();
+}
+
+already_AddRefed<DOMRequest>
+BluetoothPbapRequestHandle::ReplyTovCardPulling(Blob& aBlob,
+                                                ErrorResult& aRv)
+{
+  // TODO: Implement this function (Bug 1180555)
+  return nullptr;
+}
+
+already_AddRefed<DOMRequest>
+BluetoothPbapRequestHandle::ReplyToPhonebookPulling(Blob& aBlob,
+                                                    uint16_t phonebookSize,
+                                                    ErrorResult& aRv)
+{
+  // TODO: Implement this function (Bug 1180555)
+  return nullptr;
+}
+
+already_AddRefed<DOMRequest>
+BluetoothPbapRequestHandle::ReplyTovCardListing(Blob& aBlob,
+                                                uint16_t phonebookSize,
+                                                ErrorResult& aRv)
+{
+  // TODO: Implement this function (Bug 1180555)
+  return nullptr;
+}
+
+JSObject*
+BluetoothPbapRequestHandle::WrapObject(JSContext* aCx,
+                                   JS::Handle<JSObject*> aGivenProto)
+{
+  return BluetoothPbapRequestHandleBinding::Wrap(aCx, this, aGivenProto);
+}
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothPbapRequestHandle.h
@@ -0,0 +1,62 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_bluetooth_bluetoothpbaprequesthandle_h
+#define mozilla_dom_bluetooth_bluetoothpbaprequesthandle_h
+
+#include "nsCOMPtr.h"
+#include "mozilla/dom/DOMRequest.h"
+#include "mozilla/dom/BlobSet.h"
+
+namespace mozilla {
+  class ErrorResult;
+  namespace dom {
+    class Blob;
+    class DOMRequest;
+  }
+}
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothPbapRequestHandle final : public nsISupports
+                                       , public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(BluetoothPbapRequestHandle)
+
+  static already_AddRefed<BluetoothPbapRequestHandle>
+    Create(nsPIDOMWindow* aOwner);
+
+  nsPIDOMWindow* GetParentObject() const
+  {
+    return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  already_AddRefed<DOMRequest> ReplyTovCardPulling(Blob& aBlob,
+                                                   ErrorResult& aRv);
+
+  already_AddRefed<DOMRequest> ReplyToPhonebookPulling(Blob& aBlob,
+                                                       uint16_t phonebookSize,
+                                                       ErrorResult& aRv);
+
+  already_AddRefed<DOMRequest> ReplyTovCardListing(Blob& aBlob,
+                                                   uint16_t phonebookSize,
+                                                   ErrorResult& aRv);
+
+private:
+  BluetoothPbapRequestHandle(nsPIDOMWindow* aOwner);
+  ~BluetoothPbapRequestHandle();
+
+  nsCOMPtr<nsPIDOMWindow> mOwner;
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif // mozilla_dom_bluetooth_bluetoothpbaprequesthandle_h
\ No newline at end of file
--- a/dom/bluetooth/bluedroid/BluetoothPbapManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothPbapManager.cpp
@@ -230,39 +230,69 @@ BluetoothPbapManager::ReceiveSocketData(
         ReplyError(ObexResponseCode::BadRequest);
         return;
       }
 
       ReplyToDisconnectOrAbort();
       AfterPbapDisconnected();
       break;
     case ObexRequestCode::SetPath: {
-        // Section 3.3.6 "SetPath", IrOBEX 1.2
-        // [opcode:1][length:2][flags:1][contants:1][Headers:var]
-        if (receivedLength < 5 ||
-            !ParseHeaders(&data[5], receivedLength - 5, &pktHeaders)) {
-          ReplyError(ObexResponseCode::BadRequest);
-          return;
-        }
+      // Section 3.3.6 "SetPath", IrOBEX 1.2
+      // [opcode:1][length:2][flags:1][contants:1][Headers:var]
+      if (receivedLength < 5 ||
+          !ParseHeaders(&data[5], receivedLength - 5, &pktHeaders)) {
+        ReplyError(ObexResponseCode::BadRequest);
+        return;
+      }
+
+      uint8_t response = SetPhoneBookPath(data[3], pktHeaders);
+      if (response != ObexResponseCode::Success) {
+        ReplyError(response);
+        return;
+      }
 
-        uint8_t response = SetPhoneBookPath(data[3], pktHeaders);
-        if (response != ObexResponseCode::Success) {
-          ReplyError(response);
-          return;
-        }
+      ReplyToSetPath();
+      break;
+    }
+    case ObexRequestCode::Get:
+    case ObexRequestCode::GetFinal: {
+      // [opcode:1][length:2][Headers:var]
+      if (receivedLength < 3 ||
+          !ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
+        ReplyError(ObexResponseCode::BadRequest);
+        return;
+      }
+
+      nsString type;
+      pktHeaders.GetContentType(type);
 
-        ReplyToSetPath();
+      uint8_t response;
+      if (type.EqualsLiteral("x-bt/vcard-listing")) {
+        response = PullvCardListing(pktHeaders);
+      } else if (type.EqualsLiteral("x-bt/vcard")) {
+        response = PullvCardEntry(pktHeaders);
+      } else if (type.EqualsLiteral("x-bt/phonebook")) {
+        response = PullPhonebook(pktHeaders);
+      } else {
+        response = ObexResponseCode::BadRequest;
+        BT_LOGR("Unknown PBAP request type: %s",
+                NS_ConvertUTF16toUTF8(type).get());
+      }
+
+      // The OBEX success response will be sent after Gaia replies the PBAP
+      // request.
+      if (response != ObexResponseCode::Success) {
+        ReplyError(response);
+        return;
       }
       break;
+    }
     case ObexRequestCode::Put:
     case ObexRequestCode::PutFinal:
-    case ObexRequestCode::Get:
-    case ObexRequestCode::GetFinal:
       ReplyError(ObexResponseCode::BadRequest);
-      BT_LOGR("Unsupported ObexRequestCode %x", opCode);
       break;
     default:
       ReplyError(ObexResponseCode::NotImplemented);
       BT_LOGR("Unrecognized ObexRequestCode %x", opCode);
       break;
   }
 }
 
@@ -342,16 +372,251 @@ BluetoothPbapManager::SetPhoneBookPath(u
   }
 
   mCurrentPath = newPath;
   BT_LOGR("current path [%s]", NS_ConvertUTF16toUTF8(mCurrentPath).get());
 
   return ObexResponseCode::Success;
 }
 
+uint8_t
+BluetoothPbapManager::PullPhonebook(const ObexHeaderSet& aHeader)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs) {
+    return ObexResponseCode::PreconditionFailed;
+  }
+
+  InfallibleTArray<BluetoothNamedValue> data;
+
+  nsString name;
+  aHeader.GetName(name);
+  BT_APPEND_NAMED_VALUE(data, "name", name);
+
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::Format);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::PropertySelector);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::MaxListCount);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::ListStartOffset);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::vCardSelector);
+
+  #ifdef MOZ_B2G_BT_API_V1
+    bs->DistributeSignal(
+      BluetoothSignal(NS_LITERAL_STRING(PULL_PHONEBOOK_REQ_ID),
+                      NS_LITERAL_STRING(KEY_ADAPTER),
+                      data));
+  #else
+    bs->DistributeSignal(NS_LITERAL_STRING(PULL_PHONEBOOK_REQ_ID),
+                         NS_LITERAL_STRING(KEY_ADAPTER),
+                         data);
+  #endif
+
+  return ObexResponseCode::Success;
+}
+
+uint8_t
+BluetoothPbapManager::PullvCardListing(const ObexHeaderSet& aHeader)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs) {
+    return ObexResponseCode::PreconditionFailed;
+  }
+
+  InfallibleTArray<BluetoothNamedValue> data;
+
+  nsString name;
+  aHeader.GetName(name);
+  BT_APPEND_NAMED_VALUE(data, "name", name);
+
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::Order);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::SearchValue);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::SearchProperty);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::MaxListCount);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::ListStartOffset);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::vCardSelector);
+
+  #ifdef MOZ_B2G_BT_API_V1
+    bs->DistributeSignal(
+      BluetoothSignal(NS_LITERAL_STRING(PULL_VCARD_LISTING_REQ_ID),
+                      NS_LITERAL_STRING(KEY_ADAPTER),
+                      data));
+  #else
+    bs->DistributeSignal(NS_LITERAL_STRING(PULL_VCARD_LISTING_REQ_ID),
+                         NS_LITERAL_STRING(KEY_ADAPTER),
+                         data);
+  #endif
+
+  return ObexResponseCode::Success;
+}
+
+uint8_t
+BluetoothPbapManager::PullvCardEntry(const ObexHeaderSet& aHeader)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs) {
+    return ObexResponseCode::PreconditionFailed;
+  }
+
+  InfallibleTArray<BluetoothNamedValue> data;
+
+  nsString name;
+  aHeader.GetName(name);
+  BT_APPEND_NAMED_VALUE(data, "name", name);
+
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::Format);
+  AppendBtNamedValueByTagId(aHeader, data, AppParameterTag::PropertySelector);
+
+  #ifdef MOZ_B2G_BT_API_V1
+    bs->DistributeSignal(
+      BluetoothSignal(NS_LITERAL_STRING(PULL_VCARD_ENTRY_REQ_ID),
+                      NS_LITERAL_STRING(KEY_ADAPTER),
+                      data));
+  #else
+    bs->DistributeSignal(NS_LITERAL_STRING(PULL_VCARD_ENTRY_REQ_ID),
+                         NS_LITERAL_STRING(KEY_ADAPTER),
+                         data);
+  #endif
+
+  return ObexResponseCode::Success;
+}
+
+void
+BluetoothPbapManager::AppendBtNamedValueByTagId(
+  const ObexHeaderSet& aHeader,
+  InfallibleTArray<BluetoothNamedValue>& aValues,
+  const AppParameterTag aTagId)
+{
+  uint8_t buf[64];
+
+  switch (aTagId) {
+    case AppParameterTag::Order: {
+      if (!aHeader.GetAppParameter(AppParameterTag::Order, buf, 64)) {
+        break;
+      }
+
+      static const nsString sOrderStr[] = {NS_LITERAL_STRING("alphanumeric"),
+                                           NS_LITERAL_STRING("indexed"),
+                                           NS_LITERAL_STRING("phonetical")};
+      uint8_t order = buf[0];
+      if (order < MOZ_ARRAY_LENGTH(sOrderStr)) {
+        BT_APPEND_NAMED_VALUE(aValues, "order", sOrderStr[order]);
+      } else {
+        BT_WARNING("%s: Unexpected value '%d' of 'Order'", __FUNCTION__, order);
+      }
+      break;
+    }
+    case AppParameterTag::SearchValue: {
+      if (!aHeader.GetAppParameter(AppParameterTag::SearchValue, buf, 64)) {
+        break;
+      }
+
+      // Section 5.3.4.3 "SearchValue {<text string>}", PBAP 1.2
+      // The UTF-8 character set shall be used for <text string>.
+
+      // Use nsCString to store UTF-8 string here to follow the suggestion of
+      // 'MDN:Internal_strings'.
+      nsCString text((char *) buf);
+
+      BT_APPEND_NAMED_VALUE(aValues, "searchText", text);
+      break;
+    }
+    case AppParameterTag::SearchProperty: {
+      if (!aHeader.GetAppParameter(AppParameterTag::SearchProperty, buf, 64)) {
+        break;
+      }
+
+      static const nsString sSearchKeyStr[] = {NS_LITERAL_STRING("name"),
+                                               NS_LITERAL_STRING("number"),
+                                               NS_LITERAL_STRING("sound")};
+      uint8_t searchKey = buf[0];
+      if (searchKey < MOZ_ARRAY_LENGTH(sSearchKeyStr)) {
+        BT_APPEND_NAMED_VALUE(aValues, "searchKey", sSearchKeyStr[searchKey]);
+      } else {
+        BT_WARNING("%s: Unexpected value '%d' of 'SearchProperty'",
+                   __FUNCTION__, searchKey);
+      }
+      break;
+    }
+    case AppParameterTag::MaxListCount: {
+      if (!aHeader.GetAppParameter(AppParameterTag::MaxListCount, buf, 64)) {
+        break;
+      }
+
+      uint16_t maxListCount = *((uint16_t *)buf);
+
+      // convert big endian to little endian
+      maxListCount = (maxListCount >> 8) | (maxListCount << 8);
+
+      BT_APPEND_NAMED_VALUE(aValues, "maxListCount", (uint32_t) maxListCount);
+      break;
+    }
+    case AppParameterTag::ListStartOffset: {
+      if (!aHeader.GetAppParameter(AppParameterTag::ListStartOffset, buf, 64)) {
+        break;
+      }
+
+      uint16_t listStartOffset = *((uint16_t *)buf);
+
+      // convert big endian to little endian
+      listStartOffset = (listStartOffset >> 8) | (listStartOffset << 8);
+
+      BT_APPEND_NAMED_VALUE(aValues, "listStartOffset",
+                           (uint32_t) listStartOffset);
+      break;
+    }
+    case AppParameterTag::PropertySelector: {
+      if (!aHeader.GetAppParameter(
+          AppParameterTag::PropertySelector, buf, 64)) {
+        break;
+      }
+
+      InfallibleTArray<uint32_t> props = PackPropertiesMask(buf, 64);
+
+      BT_APPEND_NAMED_VALUE(aValues, "propSelector", props);
+      break;
+    }
+    case AppParameterTag::Format: {
+      if (!aHeader.GetAppParameter(AppParameterTag::Format, buf, 64)) {
+        break;
+      }
+
+      bool usevCard3 = buf[0];
+      BT_APPEND_NAMED_VALUE(aValues, "format", usevCard3);
+      break;
+    }
+    case AppParameterTag::vCardSelector: {
+      if (!aHeader.GetAppParameter(AppParameterTag::vCardSelector, buf, 64)) {
+        break;
+      }
+
+      InfallibleTArray<uint32_t> props = PackPropertiesMask(buf, 64);
+
+      bool hasVCardSelectorOperator = aHeader.GetAppParameter(
+        AppParameterTag::vCardSelectorOperator, buf, 64);
+
+      if (hasVCardSelectorOperator && buf[0]) {
+        BT_APPEND_NAMED_VALUE(aValues, "vCardSelector_AND",
+                              BluetoothValue(props));
+      } else {
+        BT_APPEND_NAMED_VALUE(aValues, "vCardSelector_OR",
+                              BluetoothValue(props));
+      }
+      break;
+    }
+    default:
+      BT_LOGR("Unsupported AppParameterTag: %x", aTagId);
+      break;
+  }
+}
+
 bool
 BluetoothPbapManager::IsLegalPath(const nsAString& aPath)
 {
   static const char* sLegalPaths[] = {
     "", // root
     "/telecom",
     "/telecom/pb",
     "/telecom/ich",
@@ -455,16 +720,63 @@ BluetoothPbapManager::ReplyToSetPath()
   // Section 3.3.6 "SetPath", IrOBEX 1.2
   // [opcode:1][length:2][Headers:var]
   uint8_t req[255];
   int index = 3;
 
   SendObexData(req, ObexResponseCode::Success, index);
 }
 
+InfallibleTArray<uint32_t>
+BluetoothPbapManager::PackPropertiesMask(uint8_t* aData, int aSize)
+{
+  InfallibleTArray<uint32_t> propSelector;
+
+  // Table 5.1 "Property Mask", PBAP 1.2
+  // PropertyMask is a 64-bit mask that indicates the properties contained in
+  // the requested vCard objects. We only support bit 0~31 since the rest are
+  // reserved for future use or vendor specific properties.
+
+  // convert big endian to little endian
+  uint32_t x = (aData[7] << 0)  | (aData[6] << 8) |
+               (aData[5] << 16) | (aData[4] << 24);
+
+  uint32_t count = 0;
+  while (!x) {
+    if (x & 1) {
+      propSelector.AppendElement(count);
+    }
+
+    ++count;
+    x >>= 1;
+  }
+
+  return propSelector;
+}
+
+void
+BluetoothPbapManager::ReplyToPullPhonebook(Blob* aBlob, uint16_t aPhonebookSize)
+{
+  // TODO: Implement this function (Bug 1180556)
+}
+
+void
+BluetoothPbapManager::ReplyToPullvCardListing(
+  Blob* aBlob,
+  uint16_t aPhonebookSize)
+{
+  // TODO: Implement this function (Bug 1180556)
+}
+
+void
+BluetoothPbapManager::ReplyToPullvCardEntry(Blob* aBlob)
+{
+  // TODO: Implement this function (Bug 1180556)
+}
+
 void
 BluetoothPbapManager::ReplyError(uint8_t aError)
 {
   BT_LOGR("[0x%x]", aError);
 
   // Section 3.2 "Response Format", IrOBEX 1.2
   // [opcode:1][length:2][Headers:var]
   uint8_t req[255];
--- a/dom/bluetooth/bluedroid/BluetoothPbapManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothPbapManager.h
@@ -5,76 +5,96 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluedroid_BluetoothPbapManager_h
 #define mozilla_dom_bluetooth_bluedroid_BluetoothPbapManager_h
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
 #include "BluetoothSocketObserver.h"
+#include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/ipc/SocketBase.h"
 
+namespace mozilla {
+  namespace dom {
+    class Blob;
+  }
+}
+
 BEGIN_BLUETOOTH_NAMESPACE
 
 /*
  * Defined in section 6.2.1 "Application Parameters Header", PBAP ver 1.2
  */
 enum AppParameterTag {
   Order                   = 0x01,
   SearchValue             = 0x02,
   SearchProperty          = 0x03,
   MaxListCount            = 0x04,
   ListStartOffset         = 0x05,
   PropertySelector        = 0x06,
   Format                  = 0x07,
   PhonebookSize           = 0x08,
   NewMissedCalls          = 0x09,
+  // ----- enumerators below are supported since PBAP 1.2 ----- //
   PrimaryVersionCounter   = 0x0A,
   SecondaryVersionCounter = 0x0B,
   vCardSelector           = 0x0C,
   DatabaseIdentifier      = 0x0D,
   vCardSelectorOperator   = 0x0E,
   ResetNewMissedCalls     = 0x0F,
   PbapSupportedFeatures   = 0x10
 };
 
 class BluetoothSocket;
 class ObexHeaderSet;
 
 class BluetoothPbapManager : public BluetoothSocketObserver
-                          , public BluetoothProfileManagerBase
+                           , public BluetoothProfileManagerBase
 {
 public:
   BT_DECL_PROFILE_MGR_BASE
   BT_DECL_SOCKET_OBSERVER
   virtual void GetName(nsACString& aName)
   {
     aName.AssignLiteral("PBAP");
   }
 
   static const int MAX_PACKET_LENGTH = 0xFFFE;
 
   static BluetoothPbapManager* Get();
   bool Listen();
+  void ReplyToPullPhonebook(Blob* aBlob, uint16_t aPhonebookSize);
+  void ReplyToPullvCardListing(Blob* aBlob, uint16_t aPhonebookSize);
+  void ReplyToPullvCardEntry(Blob* aBlob);
 
 protected:
   virtual ~BluetoothPbapManager();
 
 private:
   BluetoothPbapManager();
   bool Init();
   void HandleShutdown();
 
   void ReplyToConnect();
   void ReplyToDisconnectOrAbort();
   void ReplyToSetPath();
   void ReplyError(uint8_t aError);
   void SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize);
 
   uint8_t SetPhoneBookPath(uint8_t flags, const ObexHeaderSet& aHeader);
+  uint8_t PullPhonebook(const ObexHeaderSet& aHeader);
+  uint8_t PullvCardListing(const ObexHeaderSet& aHeader);
+  uint8_t PullvCardEntry(const ObexHeaderSet& aHeader);
+  void AppendBtNamedValueByTagId(
+    const ObexHeaderSet& aHeader,
+    InfallibleTArray<BluetoothNamedValue>& aValues,
+    const AppParameterTag aTagId);
+
+  InfallibleTArray<uint32_t>  PackPropertiesMask(uint8_t* aData, int aSize);
   bool CompareHeaderTarget(const ObexHeaderSet& aHeader);
   bool IsLegalPath(const nsAString& aPath);
   void AfterPbapConnected();
   void AfterPbapDisconnected();
 
   /**
    * Current phonebook path
    */
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
@@ -11,17 +11,20 @@
 #include "nsIDocument.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsIPrincipal.h"
 #include "nsTArrayHelpers.h"
 
 #include "mozilla/dom/BluetoothAdapterBinding.h"
 #include "mozilla/dom/BluetoothAttributeEvent.h"
+#include "mozilla/dom/BluetoothPhonebookPullingEvent.h"
 #include "mozilla/dom/BluetoothStatusChangedEvent.h"
+#include "mozilla/dom/BluetoothVCardListingEvent.h"
+#include "mozilla/dom/BluetoothVCardPullingEvent.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/File.h"
 
 #include "mozilla/dom/bluetooth/BluetoothAdapter.h"
 #include "mozilla/dom/bluetooth/BluetoothClassOfDevice.h"
 #include "mozilla/dom/bluetooth/BluetoothDevice.h"
 #include "mozilla/dom/bluetooth/BluetoothDiscoveryHandle.h"
@@ -519,16 +522,22 @@ BluetoothAdapter::Notify(const Bluetooth
     init.mAddress = address;
     init.mStatus = status;
     nsRefPtr<BluetoothStatusChangedEvent> event =
       BluetoothStatusChangedEvent::Constructor(this, aData.name(), init);
     DispatchTrustedEvent(event);
   } else if (aData.name().EqualsLiteral(PAIRING_ABORTED_ID) ||
              aData.name().EqualsLiteral(REQUEST_MEDIA_PLAYSTATUS_ID)) {
     DispatchEmptyEvent(aData.name());
+  } else if (aData.name().EqualsLiteral(PULL_PHONEBOOK_REQ_ID)) {
+    HandlePullPhonebookReq(aData.value());
+  } else if (aData.name().EqualsLiteral(PULL_VCARD_ENTRY_REQ_ID)) {
+    HandlePullVCardEntryReq(aData.value());
+  } else if (aData.name().EqualsLiteral(PULL_VCARD_LISTING_REQ_ID)) {
+    HandlePullVCardListingReq(aData.value());
   } else {
     BT_WARNING("Not handling adapter signal: %s",
                NS_ConvertUTF16toUTF8(aData.name()).get());
   }
 }
 
 void
 BluetoothAdapter::SetDiscoveryHandleInUse(
@@ -1186,16 +1195,182 @@ BluetoothAdapter::HandleDeviceUnpaired(c
 
   // Notify application of unpaired device
   BluetoothDeviceEventInit init;
   init.mAddress = deviceAddress;
   DispatchDeviceEvent(NS_LITERAL_STRING(DEVICE_UNPAIRED_ID), init);
 }
 
 void
+BluetoothAdapter::HandlePullPhonebookReq(const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
+  const InfallibleTArray<BluetoothNamedValue>& arr =
+    aValue.get_ArrayOfBluetoothNamedValue();
+
+  MOZ_ASSERT(arr.Length() >= 1 &&
+             arr[0].value().type() == BluetoothValue::TnsString);
+
+  BluetoothPhonebookPullingEventInit init;
+
+  for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
+    const nsString& name = arr[i].name();
+    const BluetoothValue& value = arr[i].value();
+    if (name.EqualsLiteral("name")) {
+      init.mName = value.get_nsString();
+    } else if (name.EqualsLiteral("format")) {
+      init.mFormat = value.get_bool() ? vCardVersion::VCard30
+                                      : vCardVersion::VCard21;
+    } else if (name.EqualsLiteral("propSelector")) {
+      init.mPropSelector = getVCardProperties(value);
+    } else if (name.EqualsLiteral("maxListCount")) {
+      init.mMaxListCount = value.get_uint32_t();
+    } else if (name.EqualsLiteral("listStartOffset")) {
+      init.mListStartOffset = value.get_uint32_t();
+    } else if (name.EqualsLiteral("vCardSelector_AND")) {
+      init.mVcardSelector = getVCardProperties(value);
+      init.mVcardSelectorOperator = vCardSelectorOp::AND;
+    } else if (name.EqualsLiteral("vCardSelector_OR")) {
+      init.mVcardSelector = getVCardProperties(value);
+      init.mVcardSelectorOperator = vCardSelectorOp::OR;
+    }
+  }
+
+  nsRefPtr<BluetoothPhonebookPullingEvent> event =
+    BluetoothPhonebookPullingEvent::Constructor(this,
+      NS_LITERAL_STRING(PULL_PHONEBOOK_REQ_ID), init);
+  DispatchTrustedEvent(event);
+}
+
+void
+BluetoothAdapter::HandlePullVCardEntryReq(const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
+  const InfallibleTArray<BluetoothNamedValue>& arr =
+    aValue.get_ArrayOfBluetoothNamedValue();
+
+  MOZ_ASSERT(arr.Length() >= 1 &&
+             arr[0].value().type() == BluetoothValue::TnsString);
+
+  BluetoothVCardPullingEventInit init;
+  Sequence<vCardProperties> propSelector;
+
+  for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
+    const nsString& name = arr[i].name();
+    const BluetoothValue& value = arr[i].value();
+    if (name.EqualsLiteral("name")) {
+      init.mName = value.get_nsString();
+    } else if (name.EqualsLiteral("format")) {
+      init.mFormat = value.get_bool() ? vCardVersion::VCard30
+                                      : vCardVersion::VCard21;
+    } else if (name.EqualsLiteral("propSelector")) {
+      init.mPropSelector = getVCardProperties(value);
+    }
+  }
+
+  nsRefPtr<BluetoothVCardPullingEvent> event =
+    BluetoothVCardPullingEvent::Constructor(this,
+      NS_LITERAL_STRING(PULL_VCARD_ENTRY_REQ_ID), init);
+  DispatchTrustedEvent(event);
+}
+
+void
+BluetoothAdapter::HandlePullVCardListingReq(const BluetoothValue& aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
+  const InfallibleTArray<BluetoothNamedValue>& arr =
+    aValue.get_ArrayOfBluetoothNamedValue();
+
+  MOZ_ASSERT(arr.Length() >= 1 &&
+             arr[0].value().type() == BluetoothValue::TnsString);
+
+  BluetoothVCardListingEventInit init;
+
+  for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
+    const nsString& name = arr[i].name();
+    const BluetoothValue& value = arr[i].value();
+    if (name.EqualsLiteral("name")) {
+      init.mName = value.get_nsString();
+    } else if (name.EqualsLiteral("order")) {
+      init.mOrder = ConvertStringToVCardOrderType(value.get_nsString());
+    } else if (name.EqualsLiteral("searchText")) {
+      init.mSearchValue = value.get_nsString();
+    } else if (name.EqualsLiteral("searchKey")) {
+      init.mSearchKey = ConvertStringToVCardSearchKeyType(value.get_nsString());
+    } else if (name.EqualsLiteral("maxListCount")) {
+      init.mMaxListCount = value.get_uint32_t();
+    } else if (name.EqualsLiteral("listStartOffset")) {
+      init.mListStartOffset = value.get_uint32_t();
+    } else if (name.EqualsLiteral("vCardSelector_AND")) {
+      init.mVcardSelector = getVCardProperties(value);
+      init.mVcardSelectorOperator = vCardSelectorOp::AND;
+    } else if (name.EqualsLiteral("vCardSelector_OR")) {
+      init.mVcardSelector = getVCardProperties(value);
+      init.mVcardSelectorOperator = vCardSelectorOp::OR;
+    }
+  }
+
+  nsRefPtr<BluetoothVCardListingEvent> event =
+    BluetoothVCardListingEvent::Constructor(this,
+      NS_LITERAL_STRING(PULL_VCARD_LISTING_REQ_ID), init);
+  DispatchTrustedEvent(event);
+}
+
+Sequence<vCardProperties>
+BluetoothAdapter::getVCardProperties(const BluetoothValue &aValue)
+{
+  MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfuint32_t);
+
+  Sequence<vCardProperties> propSelector;
+
+  const InfallibleTArray<uint32_t>& propSelectorArr =
+    aValue.get_ArrayOfuint32_t();
+  for (uint32_t i = 0; i < propSelectorArr.Length(); ++i) {
+    propSelector.AppendElement(
+      static_cast<vCardProperties>(propSelectorArr[i]), mozilla::fallible);
+  }
+
+  return propSelector;
+}
+
+vCardOrderType
+BluetoothAdapter::ConvertStringToVCardOrderType(const nsAString& aString)
+{
+  using namespace mozilla::dom::vCardOrderTypeValues;
+
+  for (size_t index = 0; index < ArrayLength(strings) - 1; index++) {
+    if (aString.LowerCaseEqualsASCII(strings[index].value,
+                                     strings[index].length)) {
+      return static_cast<vCardOrderType>(index);
+    }
+  }
+
+  BT_WARNING("Treat the unexpected string '%s' as vCardOrderType::Indexed",
+    NS_ConvertUTF16toUTF8(aString).get());
+  return vCardOrderType::Indexed; // The default value is 'Indexed'.
+}
+
+vCardSearchKeyType
+BluetoothAdapter::ConvertStringToVCardSearchKeyType(const nsAString& aString)
+{
+  using namespace mozilla::dom::vCardSearchKeyTypeValues;
+
+  for (size_t index = 0; index < ArrayLength(strings) - 1; index++) {
+    if (aString.LowerCaseEqualsASCII(strings[index].value,
+                                     strings[index].length)) {
+      return static_cast<vCardSearchKeyType>(index);
+    }
+  }
+
+  BT_WARNING("Treat the unexpected string '%s' as vCardSearchKeyType::Name",
+    NS_ConvertUTF16toUTF8(aString).get());
+  return vCardSearchKeyType::Name; // The default value is 'Name'.
+}
+
+void
 BluetoothAdapter::DispatchAttributeEvent(const Sequence<nsString>& aTypes)
 {
   MOZ_ASSERT(!aTypes.IsEmpty());
 
   BluetoothAttributeEventInit init;
   init.mAttrs = aTypes;
 
   nsRefPtr<BluetoothAttributeEvent> event =
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.h
@@ -7,16 +7,17 @@
 #ifndef mozilla_dom_bluetooth_BluetoothAdapter_h
 #define mozilla_dom_bluetooth_BluetoothAdapter_h
 
 #include "BluetoothCommon.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/dom/BluetoothAdapterBinding.h"
 #include "mozilla/dom/BluetoothDeviceEvent.h"
+#include "mozilla/dom/BluetoothPbapParametersBinding.h"
 #include "mozilla/dom/Promise.h"
 #include "nsCOMPtr.h"
 
 namespace mozilla {
 namespace dom {
 class Blob;
 class DOMRequest;
 struct MediaMetaData;
@@ -84,16 +85,19 @@ public:
    * Event Handlers
    ***************************************************************************/
   IMPL_EVENT_HANDLER(attributechanged);
   IMPL_EVENT_HANDLER(devicepaired);
   IMPL_EVENT_HANDLER(deviceunpaired);
   IMPL_EVENT_HANDLER(pairingaborted);
   IMPL_EVENT_HANDLER(a2dpstatuschanged);
   IMPL_EVENT_HANDLER(hfpstatuschanged);
+  IMPL_EVENT_HANDLER(pullphonebookreq);
+  IMPL_EVENT_HANDLER(pullvcardentryreq);
+  IMPL_EVENT_HANDLER(pullvcardlistingreq);
   IMPL_EVENT_HANDLER(requestmediaplaystatus);
   IMPL_EVENT_HANDLER(scostatuschanged);
 
   /****************************************************************************
    * Methods (Web API Implementation)
    ***************************************************************************/
   already_AddRefed<Promise> Enable(ErrorResult& aRv);
   already_AddRefed<Promise> Disable(ErrorResult& aRv);
@@ -282,16 +286,83 @@ private:
   /**
    * Handle "LeDeviceFound" bluetooth signal.
    *
    * @param aValue [in] Properties array of the scanned device.
    */
   void HandleLeDeviceFound(const BluetoothValue& aValue);
 
   /**
+   * Handle PULL_PHONEBOOK_REQ_ID bluetooth signal.
+   *
+   * @param aValue [in] Properties array of the PBAP request.
+   *                    The array should contain few properties:
+   *                    - nsString   'name'
+   *                    - bool       'format'
+   *                    - uint32_t[] 'propSelector'
+   *                    - uint32_t   'maxListCount'
+   *                    - uint32_t   'listStartOffset'
+   *                    - uint32_t[] 'vCardSelector_AND'
+   *                    - uint32_t[] 'vCardSelector_AND'
+   */
+  void HandlePullPhonebookReq(const BluetoothValue& aValue);
+
+  /**
+   * Handle PULL_VCARD_ENTRY_REQ_ID bluetooth signal.
+   *
+   * @param aValue [in] Properties array of the PBAP request.
+   *                    The array should contain few properties:
+   *                    - nsString   'name'
+   *                    - bool       'format'
+   *                    - uint32_t[] 'propSelector'
+   */
+  void HandlePullVCardEntryReq(const BluetoothValue& aValue);
+
+  /**
+   * Handle PULL_VCARD_LISTING_REQ_ID bluetooth signal.
+   *
+   * @param aValue [in] Properties array of the PBAP request.
+   *                    The array should contain few properties:
+   *                    - nsString   'name'
+   *                    - nsString   'order'
+   *                    - nsString   'searchText'
+   *                    - nsString   'searchKey'
+   *                    - uint32_t   'maxListCount'
+   *                    - uint32_t   'listStartOffset'
+   *                    - uint32_t[] 'vCardSelector_AND'
+   *                    - uint32_t[] 'vCardSelector_AND'
+   */
+  void HandlePullVCardListingReq(const BluetoothValue& aValue);
+
+  /**
+   * Get a Sequence of vCard properies from a BluetoothValue. The name of
+   * BluetoothValue must be propSelector, vCardSelector_OR or vCardSelector_AND.
+   *
+   * @param aValue [in] a BluetoothValue with 'TArrayOfuint32_t' type
+   *                    The name of BluetoothValue must be 'propSelector',
+   *                    'vCardSelector_OR' or 'vCardSelector_AND'.
+   */
+  Sequence<vCardProperties> getVCardProperties(const BluetoothValue &aValue);
+
+  /**
+   * Convert string to vCardOrderType.
+   *
+   * @param aString [in] String to convert
+   */
+  vCardOrderType ConvertStringToVCardOrderType(const nsAString& aString);
+
+  /**
+   * Convert string to vCardSearchKeyType.
+   *
+   * @param aString [in] String to convert
+   */
+  vCardSearchKeyType ConvertStringToVCardSearchKeyType(
+    const nsAString& aString);
+
+  /**
    * Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
    *
    * @param aTypes [in] Array of changed attributes. Must be non-empty.
    */
   void DispatchAttributeEvent(const Sequence<nsString>& aTypes);
 
   /**
    * Fire BluetoothDeviceEvent to trigger
--- a/dom/bluetooth/bluetooth2/ipc/BluetoothTypes.ipdlh
+++ b/dom/bluetooth/bluetooth2/ipc/BluetoothTypes.ipdlh
@@ -25,20 +25,22 @@ namespace bluetooth {
  * Value structure for returns from bluetooth. Currently modeled after dbus
  * returns, which can be a 32-bit int, an UTF16 string, a bool, or an array of
  * UTF16 strings. Can also hold key-value pairs for dictionary-ish access.
  */
 union BluetoothValue
 {
   int32_t;
   uint32_t;
+  nsCString;
   nsString;
   bool;
   nsString[];
   uint8_t[];
+  uint32_t[];
   BluetoothNamedValue[];
   BluetoothGattId;
   BluetoothGattId[];
   BluetoothGattServiceId;
   BluetoothGattServiceId[];
   BluetoothGattCharAttribute[];
 };
 
--- a/dom/bluetooth/moz.build
+++ b/dom/bluetooth/moz.build
@@ -8,16 +8,17 @@ if CONFIG['MOZ_B2G_BT']:
 
     #
     # Generic code
     #
 
     SOURCES += [
         'BluetoothHidManager.cpp',
         'BluetoothInterface.cpp',
+        'BluetoothPbapRequestHandle.cpp',
         'BluetoothUtils.cpp',
         'BluetoothUuid.cpp',
         'ObexBase.cpp'
     ]
 
     if CONFIG['MOZ_B2G_RIL']:
         SOURCES += [
             'BluetoothRilListener.cpp'
@@ -139,17 +140,18 @@ EXPORTS.mozilla.dom.bluetooth += [
     'bluetooth2/BluetoothGattCharacteristic.h',
     'bluetooth2/BluetoothGattDescriptor.h',
     'bluetooth2/BluetoothGattServer.h',
     'bluetooth2/BluetoothGattService.h',
     'bluetooth2/BluetoothLeDeviceEvent.h',
     'bluetooth2/BluetoothManager.h',
     'bluetooth2/BluetoothPairingHandle.h',
     'bluetooth2/BluetoothPairingListener.h',
-    'BluetoothCommon.h'
+    'BluetoothCommon.h',
+    'BluetoothPbapRequestHandle.h',
 ]
 IPDL_SOURCES += [
     'bluetooth2/ipc/BluetoothTypes.ipdlh',
     'bluetooth2/ipc/PBluetooth.ipdl',
     'bluetooth2/ipc/PBluetoothRequest.ipdl',
 ]
 
 FAIL_ON_WARNINGS = True
--- a/dom/events/test/test_all_synthetic_events.html
+++ b/dom/events/test/test_all_synthetic_events.html
@@ -71,20 +71,32 @@ const kEventConstructors = {
   BluetoothLeDeviceEvent:                    { create: function (aName, aProps) {
                                                           return new BluetoothLeDeviceEvent(aName, aProps);
                                                        },
                                              },
   BluetoothPairingEvent:                     { create: function (aName, aProps) {
                                                           return new BluetoothPairingEvent(aName, aProps);
                                                        },
                                              },
+  BluetoothPhonebookPullingEvent:            { create: function (aName, aProps) {
+                                                          return new BluetoothPhonebookPullingEvent(aName, aProps);
+                                                       },
+                                             },
   BluetoothStatusChangedEvent:               { create: function (aName, aProps) {
                                                           return new BluetoothStatusChangedEvent(aName, aProps);
                                                        },
                                              },
+  BluetoothVCardListingEvent:                { create: function (aName, aProps) {
+                                                          return new BluetoothVCardListingEvent(aName, aProps);
+                                                       },
+                                             },
+  BluetoothVCardPullingEvent:                { create: function (aName, aProps) {
+                                                          return new BluetoothVCardPullingEvent(aName, aProps);
+                                                       },
+                                             },
   CallEvent:                                 { create: function (aName, aProps) {
                                                           return new CallEvent(aName, aProps);
                                                        },
                                              },
   CallGroupErrorEvent:                       { create: function (aName, aProps) {
                                                           return new CallGroupErrorEvent(aName, aProps);
                                                        },
                                              },
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -216,19 +216,25 @@ var interfaceNamesInGlobalScope =
     {name: "BluetoothLeDeviceEvent", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothManager", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothPairingEvent", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothPairingHandle", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "BluetoothPhonebookPullingEvent", b2g: true, permission: ["bluetooth"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothStatusChangedEvent", b2g: true,
      permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "BluetoothVCardListingEvent", b2g: true, permission: ["bluetooth"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "BluetoothVCardPullingEvent", b2g: true, permission: ["bluetooth"]},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BoxObject", xbl: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BrowserElementAudioChannel", b2g: true, permission: ["browser"] },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "BroadcastChannel",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Cache",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/BluetoothAdapter.webidl
+++ b/dom/webidl/BluetoothAdapter.webidl
@@ -64,16 +64,25 @@ interface BluetoothAdapter : EventTarget
            attribute EventHandler   onhfpstatuschanged;
 
   // Fired when sco connection status changed
            attribute EventHandler   onscostatuschanged;
 
   // Fired when remote devices query current media play status
            attribute EventHandler   onrequestmediaplaystatus;
 
+  // Fired when PBAP manager requests for 'pullphonebook'
+           attribute EventHandler   onpullphonebookreq;
+
+  // Fired when PBAP manager requests for 'pullvcardentry'
+           attribute EventHandler   onpullvcardentryreq;
+
+  // Fired when PBAP manager requests for 'pullvcardlisting'
+           attribute EventHandler   onpullvcardlistingreq;
+
   /**
    * Enable/Disable a local bluetooth adapter by asynchronus methods and return
    * its result through a Promise.
    *
    * Several onattributechanged events would be triggered during processing the
    * request, and the last one indicates adapter.state becomes enabled/disabled.
    */
   [NewObject, AvailableIn=CertifiedApps]
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothPbapParameters.webidl
@@ -0,0 +1,81 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+/**
+ * This enum holds the parameters to indicate the properties contained in the
+ * requested vCard objects.
+ */
+enum vCardProperties
+{
+  "version",
+  "fn",
+  "n",
+  "photo",
+  "bday",
+  "adr",
+  "label",
+  "tel",
+  "email",
+  "mailer",
+  "tz",
+  "geo",
+  "title",
+  "role",
+  "logo",
+  "agent",
+  "org",
+  "note",
+  "rev",
+  "sound",
+  "url",
+  "uid",
+  "key",
+  "nickname",
+  "categories",
+  "proid",
+  "class",
+  "sort-string",
+  "x-irmc-call-datetime",
+  "x-bt-speeddialkey",
+  "x-bt-uci",
+  "x-bt-uid"
+};
+
+/**
+ * This enum holds the parameters to indicate the sorting order of vCard
+ * objects.
+ */
+enum vCardOrderType {
+  "alphabetical",
+  "indexed",  // default
+  "phonetical"
+};
+
+/**
+ * This enum holds the parameters to indicate the search key of the search
+ * operation.
+ */
+enum vCardSearchKeyType {
+  "name",  // default
+  "number",
+  "sound"
+};
+
+/**
+ * This enum holds the parameters to indicate the vCard version.
+ */
+enum vCardVersion {
+  "vCard21", // default
+  "vCard30"
+};
+
+/**
+ * This enum holds the parameters to indicate the type of vCard selector.
+ */
+enum vCardSelectorOp {
+  "OR", // default
+  "AND"
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothPbapRequestHandle.webidl
@@ -0,0 +1,33 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+[CheckAnyPermissions="bluetooth"]
+interface BluetoothPbapRequestHandle
+{
+  /**
+   * Reply vCard object to the PBAP request. The DOMRequest will get onerror
+   * callback if the PBAP request type is not 'pullvcardentryreq' or operation
+   * fails.
+   */
+  [NewObject, Throws, AvailableIn=CertifiedApps]
+  DOMRequest replyTovCardPulling(Blob vcardObject);
+
+  /**
+   * Reply vCard object to the PBAP request. The DOMRequest will get onerror
+   * callback if the PBAP request type is not 'pullphonebookreq' or operation
+   * fails.
+   */
+  [NewObject, Throws, AvailableIn=CertifiedApps]
+  DOMRequest replyToPhonebookPulling(Blob vcardObject,
+                                     unsigned long long phonebookSize);
+  /**
+   * Reply vCard object to the PBAP request. The DOMRequest will get onerror
+   * callback if the PBAP request type is not 'pullvcardlistingreq' or operation
+   * fails.
+   */
+  [NewObject, Throws, AvailableIn=CertifiedApps]
+  DOMRequest replyTovCardListing(Blob vcardObject,
+                                 unsigned long long phonebookSize);
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothPhonebookPullingEvent.webidl
@@ -0,0 +1,36 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[CheckAnyPermissions="bluetooth",
+ Constructor(DOMString type,
+             optional BluetoothPhonebookPullingEventInit eventInitDict)]
+interface BluetoothPhonebookPullingEvent : Event
+{
+  readonly attribute DOMString                  name;
+  readonly attribute vCardVersion               format;
+  [Cached, Constant]
+  readonly attribute sequence<vCardProperties>  propSelector;
+  readonly attribute unsigned long              maxListCount;
+  readonly attribute unsigned long              listStartOffset;
+  [Cached, Constant]
+  readonly attribute sequence<vCardProperties>  vcardSelector;
+  readonly attribute vCardSelectorOp            vcardSelectorOperator;
+
+  readonly attribute BluetoothPbapRequestHandle? handle;
+};
+
+dictionary BluetoothPhonebookPullingEventInit : EventInit
+{
+  DOMString                 name = "";
+  vCardVersion              format = "vCard21";
+  sequence<vCardProperties> propSelector = [];
+  unsigned long             maxListCount = 0;
+  unsigned long             listStartOffset = 0;
+  sequence<vCardProperties> vcardSelector = [];
+  vCardSelectorOp           vcardSelectorOperator = "OR";
+
+  BluetoothPbapRequestHandle? handle = null;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothVCardListingEvent.webidl
@@ -0,0 +1,37 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[CheckAnyPermissions="bluetooth",
+ Constructor(DOMString type,
+             optional BluetoothVCardListingEventInit eventInitDict)]
+interface BluetoothVCardListingEvent : Event
+{
+  readonly attribute DOMString                 name;
+  readonly attribute vCardOrderType            order;
+  readonly attribute DOMString                 searchValue;
+  readonly attribute vCardSearchKeyType        searchKey;
+  readonly attribute unsigned long             maxListCount;
+  readonly attribute unsigned long             listStartOffset;
+  [Cached, Constant]
+  readonly attribute sequence<vCardProperties> vcardSelector;
+  readonly attribute vCardSelectorOp           vcardSelectorOperator;
+
+  readonly attribute BluetoothPbapRequestHandle? handle;
+};
+
+dictionary BluetoothVCardListingEventInit : EventInit
+{
+  DOMString                  name = "";
+  vCardOrderType             order = "indexed";
+  DOMString                  searchValue = "";
+  vCardSearchKeyType         searchKey = "name";
+  unsigned long              maxListCount = 0;
+  unsigned long              listStartOffset = 0;
+  sequence<vCardProperties>  vcardSelector = [];
+  vCardSelectorOp            vcardSelectorOperator = "OR";
+
+  BluetoothPbapRequestHandle? handle = null;
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/BluetoothVCardPullingEvent.webidl
@@ -0,0 +1,27 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/.
+ */
+
+[CheckAnyPermissions="bluetooth",
+ Constructor(DOMString type,
+             optional BluetoothVCardPullingEventInit eventInitDict)]
+interface BluetoothVCardPullingEvent : Event
+{
+  readonly attribute DOMString                 name;
+  readonly attribute vCardVersion              format;
+  [Cached, Constant]
+  readonly attribute sequence<vCardProperties> propSelector;
+
+  readonly attribute BluetoothPbapRequestHandle? handle;
+};
+
+dictionary BluetoothVCardPullingEventInit : EventInit
+{
+  DOMString                 name = "";
+  vCardVersion              format = "vCard21";
+  sequence<vCardProperties> propSelector = [];
+
+  BluetoothPbapRequestHandle? handle = null;
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -677,16 +677,18 @@ if CONFIG['MOZ_B2G_BT']:
         'BluetoothGattCharacteristic.webidl',
         'BluetoothGattDescriptor.webidl',
         'BluetoothGattServer.webidl',
         'BluetoothGattService.webidl',
         'BluetoothLeDeviceEvent.webidl',
         'BluetoothManager.webidl',
         'BluetoothPairingHandle.webidl',
         'BluetoothPairingListener.webidl',
+        'BluetoothPbapParameters.webidl',
+        'BluetoothPbapRequestHandle.webidl',
     ]
 
 if CONFIG['MOZ_SIMPLEPUSH']:
     WEBIDL_FILES += [
         'SimplePushManager.webidl'
     ]
 else:
     WEBIDL_FILES += [
@@ -823,17 +825,20 @@ if CONFIG['MOZ_GAMEPAD']:
 
 if CONFIG['MOZ_B2G_BT']:
     GENERATED_EVENTS_WEBIDL_FILES += [
         'BluetoothAdapterEvent.webidl',
         'BluetoothAttributeEvent.webidl',
         'BluetoothDeviceEvent.webidl',
         'BluetoothGattCharacteristicEvent.webidl',
         'BluetoothPairingEvent.webidl',
+        'BluetoothPhonebookPullingEvent.webidl',
         'BluetoothStatusChangedEvent.webidl',
+        'BluetoothVCardListingEvent.webidl',
+        'BluetoothVCardPullingEvent.webidl'
     ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     GENERATED_EVENTS_WEBIDL_FILES += [
         'MozWifiConnectionInfoEvent.webidl',
         'MozWifiP2pStatusChangeEvent.webidl',
         'MozWifiStationInfoEvent.webidl',
         'MozWifiStatusChangeEvent.webidl',