CLOSED TREE Bug 1180554 - Dispatch events to PBAP event handlers when the PBAP requests comes. r=btian, r=mrbkap
authorJamin Liu <jaliu@mozilla.com>
Mon, 24 Aug 2015 10:29:56 +0800
changeset 291542 092034d8c3b1946e70b4bb875251a39604855571
parent 291540 4ccdd06e51d7209ba429196df7cab97bf66962db
child 291543 3c598d855e334b740dadba1b20d4fd52c3e56549
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbtian, mrbkap
bugs1180554
milestone43.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
CLOSED TREE 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
@@ -212,23 +212,31 @@ var interfaceNamesInGlobalScope =
     {name: "BluetoothGattServer", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "BluetoothGattService", b2g: true, permission: ["bluetooth"]},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {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: "BluetoothPbapRequestHandle", 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
@@ -679,16 +679,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 += [
@@ -825,17 +827,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',