Bug 1180556 - Pack PBAP replies to OBEX response message and reply to remote device. r=btian
☠☠ backed out by 00b847293e70 ☠ ☠
authorJamin Liu <jaliu@mozilla.com>
Fri, 21 Aug 2015 09:52:04 +0800
changeset 291244 131251625ee858d5281b20b356cab8518037d599
parent 291243 5bdcc058e6d6ab6b29cf70754be30100dccb543e
child 291245 7236cfbf67088d87dbf4ba6a0aa3724f7a61e096
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
bugs1180556
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
Bug 1180556 - Pack PBAP replies to OBEX response message and reply to remote device. r=btian
dom/bluetooth/ObexBase.cpp
dom/bluetooth/ObexBase.h
dom/bluetooth/bluedroid/BluetoothPbapManager.cpp
dom/bluetooth/bluedroid/BluetoothPbapManager.h
--- a/dom/bluetooth/ObexBase.cpp
+++ b/dom/bluetooth/ObexBase.cpp
@@ -15,24 +15,24 @@ BEGIN_BLUETOOTH_NAMESPACE
 /**
  * Append byte array and length to header
  */
 int
 AppendHeader(uint8_t aHeaderId, uint8_t* aRetBuf, int aBufferSize,
              const uint8_t* aData, int aLength)
 {
   int headerLength = aLength + 3;
+  int writtenLength = (headerLength < aBufferSize) ? headerLength : aBufferSize;
 
   aRetBuf[0] = aHeaderId;
   aRetBuf[1] = (headerLength & 0xFF00) >> 8;
   aRetBuf[2] = headerLength & 0x00FF;
-  memcpy(&aRetBuf[3], aData, (aLength < aBufferSize - 3) ? aLength
-                                                         : aBufferSize - 3);
+  memcpy(&aRetBuf[3], aData, writtenLength - 3);
 
-  return headerLength;
+  return writtenLength;
 }
 
 /**
  * Append 4-byte integer to header
  */
 int
 AppendHeader(uint8_t aHeaderId, uint8_t* aRetBuf, int aValue)
 {
@@ -69,16 +69,44 @@ int
 AppendHeaderWho(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aWho,
                 int aLength)
 {
   return AppendHeader(ObexHeaderId::Who, aRetBuf, aBufferSize,
                       aWho, aLength);
 }
 
 int
+AppendHeaderAppParameters(uint8_t* aRetBuf, int aBufferSize,
+                          const uint8_t* aAppParameters, int aLength)
+{
+  return AppendHeader(ObexHeaderId::AppParameters, aRetBuf, aBufferSize,
+                      aAppParameters, aLength);
+}
+
+int
+AppendAppParameter(uint8_t* aRetBuf, int aBufferSize, const uint8_t aTagId,
+                   const uint8_t* aValue, int aLength)
+{
+  // An application parameter is a [tag]-[length]-[value] triplet. The [tag] and
+  // [length] fields are 1-byte length each.
+
+  if (aBufferSize < aLength + 2) {
+    // aBufferSize should be larger than size of AppParameter + header.
+    BT_WARNING("Return buffer size is too small for the AppParameter");
+    return 0;
+  }
+
+  aRetBuf[0] = aTagId;
+  aRetBuf[1] = aLength;
+  memcpy(&aRetBuf[2], aValue, aLength);
+
+  return aLength + 2;
+}
+
+int
 AppendHeaderLength(uint8_t* aRetBuf, int aObjectLength)
 {
   return AppendHeader(ObexHeaderId::Length, aRetBuf, aObjectLength);
 }
 
 int
 AppendHeaderConnectionId(uint8_t* aRetBuf, int aConnectionId)
 {
--- a/dom/bluetooth/ObexBase.h
+++ b/dom/bluetooth/ObexBase.h
@@ -10,16 +10,37 @@
 #include "BluetoothCommon.h"
 #include "nsAutoPtr.h"
 #include "nsTArray.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 const char FINAL_BIT = 0x80;
 
+/**
+ * Section 3.2 "Response format", IrOBEX ver 1.2
+ * The format of an OBEX response header is
+ * [response code:1][response length:2]
+ */
+static const uint32_t kObexRespHeaderSize = 3;
+
+/**
+ * Section 2.2.9 "Body, End-of-Body", IrOBEX ver 1.2
+ * The format of an OBEX Body header is
+ * [headerId:1][header length:2]
+ */
+static const uint32_t kObexBodyHeaderSize = 3;
+
+/**
+ * Section 3.3.1.4 "Minimum OBEX Packet Length", IrOBEX ver 1.2
+ * The minimum size of the OBEX Maximum packet length allowed for negotiation is
+ * 255 bytes.
+ */
+static const uint32_t kObexLeastMaxSize = 255;
+
 /*
  * Defined in section 2.1 "OBEX Headers", IrOBEX ver 1.2
  */
 enum ObexHeaderId {
   Count = 0xC0,
   Name = 0x01,
   Type = 0x42,
   Length = 0xC3,
@@ -246,17 +267,18 @@ public:
     }
 
     // According to OBEX spec., the value 0xFFFFFFFF is reserved and it's
     // considered invalid for Connection ID.
     return 0xFFFFFFFF;
   }
 
   /**
-   * Get a specified parameter from the 'Application Parameters' header.
+   * Get a specified parameter from the 'Application Parameters' header with
+   * big-endian byte ordering.
    *
    * @param aTagId      [in]  The tag ID of parameter which is defined by
    *                          applications or upper protocol layer.
    * @param aRetBuf     [out] The buffer which is used to return the parameter.
    * @param aBufferSize [in]  The size of the given buffer.
    *
    * @return a boolean value to indicate whether the given paramter exists.
    */
@@ -318,16 +340,20 @@ private:
 };
 
 int AppendHeaderName(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aName,
                      int aLength);
 int AppendHeaderBody(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aBody,
                      int aLength);
 int AppendHeaderWho(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aWho,
                     int aLength);
+int AppendHeaderAppParameters(uint8_t* aRetBuf, int aBufferSize,
+                              const uint8_t* aAppParameters, int aLength);
+int AppendAppParameter(uint8_t* aRetBuf, int aBufferSize, const uint8_t aTagId,
+                       const uint8_t* aValue, int aLength);
 int AppendHeaderLength(uint8_t* aRetBuf, int aObjectLength);
 int AppendHeaderConnectionId(uint8_t* aRetBuf, int aConnectionId);
 int AppendHeaderEndOfBody(uint8_t* aRetBuf);
 void SetObexPacketInfo(uint8_t* aRetBuf, uint8_t aOpcode, int aPacketLength);
 
 /**
  * @return true when the message was parsed without any error, false otherwise.
  */
--- a/dom/bluetooth/bluedroid/BluetoothPbapManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothPbapManager.cpp
@@ -12,16 +12,17 @@
 #include "BluetoothUuid.h"
 #include "ObexBase.h"
 
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "nsAutoPtr.h"
+#include "nsIInputStream.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 
 USING_BLUETOOTH_NAMESPACE
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
@@ -70,16 +71,18 @@ BluetoothPbapManager::HandleShutdown()
   MOZ_ASSERT(NS_IsMainThread());
 
   sInShutdown = true;
   Disconnect(nullptr);
   sPbapManager = nullptr;
 }
 
 BluetoothPbapManager::BluetoothPbapManager() : mConnected(false)
+                                             , mRemoteMaxPacketLength(0)
+                                             , mRequirePhonebookSize(false)
 {
   mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
   mCurrentPath.AssignLiteral("");
 }
 
 BluetoothPbapManager::~BluetoothPbapManager()
 {
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
@@ -213,16 +216,27 @@ BluetoothPbapManager::ReceiveSocketData(
 
       // Section 6.4 "Establishing an OBEX Session", PBAP 1.2
       // The OBEX header target shall equal to kPbapObexTarget.
       if (!CompareHeaderTarget(pktHeaders)) {
         ReplyError(ObexResponseCode::BadRequest);
         return;
       }
 
+      // Save the max packet length from remote information
+      mRemoteMaxPacketLength = ((static_cast<int>(data[5]) << 8) | data[6]);
+
+      if (mRemoteMaxPacketLength < kObexLeastMaxSize) {
+        BT_LOGR("Remote maximum packet length %d is smaller than %d bytes",
+          mRemoteMaxPacketLength, kObexLeastMaxSize);
+        mRemoteMaxPacketLength = 0;
+        ReplyError(ObexResponseCode::BadRequest);
+        return;
+      }
+
       ReplyToConnect();
       AfterPbapConnected();
       break;
     case ObexRequestCode::Disconnect:
     case ObexRequestCode::Abort:
       // Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
       // The format of request packet of "Disconnect" and "Abort" are the same
       // [opcode:1][length:2][Headers:var]
@@ -249,17 +263,38 @@ BluetoothPbapManager::ReceiveSocketData(
         ReplyError(response);
         return;
       }
 
       ReplyToSetPath();
       break;
     }
     case ObexRequestCode::Get:
+      // Section 6.2.2 "OBEX Headers in Multi-Packet Responses", IrOBEX 1.2
+      // All OBEX request messages shall be sent as one OBEX packet containing
+      // all of the headers. I.e. OBEX GET with opcode 0x83 shall always be
+      // used. OBEX GET with opcode 0x03 shall never be used.
+      BT_WARNING("PBAP shall always uses OBEX GetFinal instead of Get.");
+
+      // no break. Treat 'Get' as 'GetFinal' for error tolerance.
     case ObexRequestCode::GetFinal: {
+      // As long as 'mVCardDataStream' requires multiple response packets to
+      // complete, the client should continue to issue GET requests until the
+      // final body information (in an End-of-Body header) arrives, along with
+      // the response code 0xA0 Success.
+      if (mVCardDataStream) {
+        if (!ReplyToGet(mVCardDataStream)) {
+          BT_WARNING("Failed to reply to PBAP GET request.");
+          ReplyError(ObexResponseCode::InternalServerError);
+        }
+        return;
+      }
+
+      // Section 3.1 "Request format", IrOBEX 1.2
+      // The format of an OBEX request is
       // [opcode:1][length:2][Headers:var]
       if (receivedLength < 3 ||
           !ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
         ReplyError(ObexResponseCode::BadRequest);
         return;
       }
 
       nsString type;
@@ -546,16 +581,21 @@ BluetoothPbapManager::AppendBtNamedValue
         break;
       }
 
       uint16_t maxListCount = *((uint16_t *)buf);
 
       // convert big endian to little endian
       maxListCount = (maxListCount >> 8) | (maxListCount << 8);
 
+      // Section 5 "Phone Book Access Profile Functions", PBAP 1.2
+      // Replying 'PhonebookSize' is mandatory if 'MaxListCount' parameter is
+      // present in the request with a value of 0, else it is excluded.
+      mRequirePhonebookSize = !maxListCount;
+
       BT_APPEND_NAMED_VALUE(aValues, "maxListCount", (uint32_t) maxListCount);
       break;
     }
     case AppParameterTag::ListStartOffset: {
       if (!aHeader.GetAppParameter(AppParameterTag::ListStartOffset, buf, 64)) {
         break;
       }
 
@@ -649,16 +689,24 @@ BluetoothPbapManager::AfterPbapConnected
   mCurrentPath.AssignLiteral("");
   mConnected = true;
 }
 
 void
 BluetoothPbapManager::AfterPbapDisconnected()
 {
   mConnected = false;
+
+  mRemoteMaxPacketLength = 0;
+  mRequirePhonebookSize = false;
+
+  if (mVCardDataStream) {
+    mVCardDataStream->Close();
+    mVCardDataStream = nullptr;
+  }
 }
 
 bool
 BluetoothPbapManager::IsConnected()
 {
   return mConnected;
 }
 
@@ -673,62 +721,62 @@ BluetoothPbapManager::ReplyToConnect()
 {
   if (mConnected) {
     return;
   }
 
   // Section 3.3.1 "Connect", IrOBEX 1.2
   // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
   // [Headers:var]
-  uint8_t req[255];
+  uint8_t res[kObexLeastMaxSize];
   int index = 7;
 
-  req[3] = 0x10; // version=1.0
-  req[4] = 0x00; // flag=0x00
-  req[5] = BluetoothPbapManager::MAX_PACKET_LENGTH >> 8;
-  req[6] = (uint8_t)BluetoothPbapManager::MAX_PACKET_LENGTH;
+  res[3] = 0x10; // version=1.0
+  res[4] = 0x00; // flag=0x00
+  res[5] = BluetoothPbapManager::MAX_PACKET_LENGTH >> 8;
+  res[6] = (uint8_t)BluetoothPbapManager::MAX_PACKET_LENGTH;
 
   // Section 6.4 "Establishing an OBEX Session", PBAP 1.2
   // Headers: [Who:16][Connection ID]
-  index += AppendHeaderWho(&req[index], 255, kPbapObexTarget.mUuid,
-                           sizeof(BluetoothUuid));
-  index += AppendHeaderConnectionId(&req[index], 0x01);
+  index += AppendHeaderWho(&res[index], kObexLeastMaxSize,
+                           kPbapObexTarget.mUuid, sizeof(BluetoothUuid));
+  index += AppendHeaderConnectionId(&res[index], 0x01);
 
-  SendObexData(req, ObexResponseCode::Success, index);
+  SendObexData(res, ObexResponseCode::Success, index);
 }
 
 void
 BluetoothPbapManager::ReplyToDisconnectOrAbort()
 {
   if (!mConnected) {
     return;
   }
 
   // Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
   // The format of response packet of "Disconnect" and "Abort" are the same
   // [opcode:1][length:2][Headers:var]
-  uint8_t req[255];
-  int index = 3;
+  uint8_t res[kObexLeastMaxSize];
+  int index = kObexRespHeaderSize;
 
-  SendObexData(req, ObexResponseCode::Success, index);
+  SendObexData(res, ObexResponseCode::Success, index);
 }
 
 void
 BluetoothPbapManager::ReplyToSetPath()
 {
   if (!mConnected) {
     return;
   }
 
   // Section 3.3.6 "SetPath", IrOBEX 1.2
   // [opcode:1][length:2][Headers:var]
-  uint8_t req[255];
-  int index = 3;
+  uint8_t res[kObexLeastMaxSize];
+  int index = kObexRespHeaderSize;
 
-  SendObexData(req, ObexResponseCode::Success, index);
+  SendObexData(res, 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
@@ -748,75 +796,204 @@ BluetoothPbapManager::PackPropertiesMask
 
     ++count;
     x >>= 1;
   }
 
   return propSelector;
 }
 
-void
+bool
 BluetoothPbapManager::ReplyToPullPhonebook(BlobParent* aActor,
                                            uint16_t aPhonebookSize)
 {
   nsRefPtr<BlobImpl> impl = aActor->GetBlobImpl();
   nsRefPtr<Blob> blob = Blob::Create(nullptr, impl);
 
-  ReplyToPullPhonebook(blob.get(), aPhonebookSize);
+  return ReplyToPullPhonebook(blob.get(), aPhonebookSize);
 }
 
-void
+bool
 BluetoothPbapManager::ReplyToPullPhonebook(Blob* aBlob, uint16_t aPhonebookSize)
 {
-  // TODO: Implement this function (Bug 1180556)
+  if (!mConnected) {
+    return false;
+  }
+
+  if (!GetInputStreamFromBlob(mVCardDataStream, aBlob)) {
+    ReplyError(ObexResponseCode::InternalServerError);
+    return false;
+  }
+
+  return ReplyToGet(mVCardDataStream, aPhonebookSize);
 }
 
-void
+bool
 BluetoothPbapManager::ReplyToPullvCardListing(BlobParent* aActor,
                                               uint16_t aPhonebookSize)
 {
   nsRefPtr<BlobImpl> impl = aActor->GetBlobImpl();
   nsRefPtr<Blob> blob = Blob::Create(nullptr, impl);
 
-  ReplyToPullvCardListing(blob.get(), aPhonebookSize);
+  return ReplyToPullvCardListing(blob.get(), aPhonebookSize);
 }
 
-void
+bool
 BluetoothPbapManager::ReplyToPullvCardListing(Blob* aBlob,
                                               uint16_t aPhonebookSize)
 {
-  // TODO: Implement this function (Bug 1180556)
+  if (!mConnected) {
+    return false;
+  }
+
+  if (!GetInputStreamFromBlob(mVCardDataStream, aBlob)) {
+    ReplyError(ObexResponseCode::InternalServerError);
+    return false;
+  }
+
+  return ReplyToGet(mVCardDataStream, aPhonebookSize);
 }
 
-void
+bool
 BluetoothPbapManager::ReplyToPullvCardEntry(BlobParent* aActor)
 {
   nsRefPtr<BlobImpl> impl = aActor->GetBlobImpl();
   nsRefPtr<Blob> blob = Blob::Create(nullptr, impl);
 
-  ReplyToPullvCardEntry(blob.get());
+  return ReplyToPullvCardEntry(blob.get());
+}
+
+bool
+BluetoothPbapManager::ReplyToPullvCardEntry(Blob* aBlob)
+{
+  if (!mConnected) {
+    return false;
+  }
+
+  if (!GetInputStreamFromBlob(mVCardDataStream, aBlob)) {
+    ReplyError(ObexResponseCode::InternalServerError);
+    return false;
+  }
+
+  return ReplyToGet(mVCardDataStream);
 }
 
-void
-BluetoothPbapManager::ReplyToPullvCardEntry(Blob* aBlob)
+bool
+BluetoothPbapManager::ReplyToGet(nsIInputStream* aStream,
+                                 uint16_t aPhonebookSize)
 {
-  // TODO: Implement this function (Bug 1180556)
+  MOZ_ASSERT(aStream);
+  MOZ_ASSERT(mRemoteMaxPacketLength >= kObexLeastMaxSize);
+
+  // This response will be composed by these four parts.
+  // Part 1: [response code:1][length:2]
+  // Part 2: [headerId:1][length:2][PhonebookSize:4]  (optional)
+  // Part 3: [headerId:1][length:2][Body:var]
+  // Part 4: [headerId:1][length:2][EndOfBody:0]      (optional)
+
+  uint8_t* res = new uint8_t[mRemoteMaxPacketLength];
+
+  // ---- Part 1, move index for [response code:1][length:2] ---- //
+  // res[0~2] will be set in SendObexData()
+  unsigned int index = kObexRespHeaderSize;
+
+  // ---- Part 2, add [response code:1][length:2] to response ---- //
+  if (mRequirePhonebookSize) {
+    // convert little endian to big endian
+    uint8_t phonebookSize[2];
+    phonebookSize[0] = (aPhonebookSize & 0xFF00) >> 8;
+    phonebookSize[1] = aPhonebookSize & 0x00FF;
+
+    // Section 6.2.1 "Application Parameters Header", PBAP 1.2
+    // appParameters: [headerId:1][length:2][PhonebookSize:4], where
+    //                [PhonebookSize:4] = [tagId:1][length:1][value:2]
+    uint8_t appParameters[4];
+    AppendAppParameter(appParameters,
+                       sizeof(appParameters),
+                       (uint8_t) AppParameterTag::PhonebookSize,
+                       phonebookSize,
+                       sizeof(phonebookSize));
+
+    index += AppendHeaderAppParameters(&res[index],
+                                       mRemoteMaxPacketLength,
+                                       appParameters,
+                                       sizeof(appParameters));
+    mRequirePhonebookSize = false;
+  }
+
+  // ---- Part 3, add [headerId:1][length:2][Body:var] to response ---- //
+  // Remaining packet size to append Body, excluding Body's header
+  uint32_t remainingPacketSize = mRemoteMaxPacketLength - kObexBodyHeaderSize
+                                                        - index;
+
+  // Read vCard data from input stream
+  uint32_t numRead = 0;
+  nsAutoArrayPtr<char> buffer(new char[remainingPacketSize]);
+  nsresult rv = aStream->Read(buffer, remainingPacketSize, &numRead);
+  if (NS_FAILED(rv)) {
+    BT_WARNING("Failed to read from input stream.");
+    return false;
+  }
+
+  if (numRead) {
+    index += AppendHeaderBody(&res[index],
+                              remainingPacketSize,
+                              (uint8_t*) buffer.forget(),
+                              numRead);
+  }
+
+  // More GET requests are required if remaining packet size isn't
+  // enough for 1) number of bytes read and 2) one EndOfBody's header
+  uint8_t opcode;
+  if (numRead + kObexBodyHeaderSize > remainingPacketSize) {
+    opcode = ObexResponseCode::Continue;
+  } else {
+    // ---- Part 4, add [headerId:1][length:2][EndOfBody:var] to response --- //
+    opcode = ObexResponseCode::Success;
+    index += AppendHeaderEndOfBody(&res[index]);
+
+    aStream->Close();
+    aStream = nullptr;
+  }
+
+  SendObexData(res, opcode, index);
+  delete [] res;
+
+  return true;
+}
+
+bool
+BluetoothPbapManager::GetInputStreamFromBlob(nsIInputStream* aStream,
+                                             Blob* aBlob)
+{
+  // PBAP can only handle one OBEX BODY transfer at the same time.
+  if (mVCardDataStream) {
+    BT_WARNING("Shouldn't handle multiple PBAP responses at the same time");
+    mVCardDataStream->Close();
+    mVCardDataStream = nullptr;
+  }
+
+  ErrorResult rv;
+  aBlob->GetInternalStream(getter_AddRefs(mVCardDataStream), rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return false;
+  }
+
+  return true;
 }
 
 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];
-  int index = 3;
-
-  SendObexData(req, aError, index);
+  // [response code:1][length:2][data:var]
+  uint8_t res[kObexLeastMaxSize];
+  SendObexData(res, aError, kObexBodyHeaderSize);
 }
 
 void
 BluetoothPbapManager::SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize)
 {
   SetObexPacketInfo(aData, aOpcode, aSize);
   mSocket->SendSocketData(new UnixSocketRawData(aData, aSize));
 }
--- a/dom/bluetooth/bluedroid/BluetoothPbapManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothPbapManager.h
@@ -8,16 +8,18 @@
 #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"
 
+class nsIInputStream;
+
 namespace mozilla {
   namespace dom {
     class Blob;
     class BlobParent;
   }
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
@@ -64,70 +66,90 @@ public:
   static BluetoothPbapManager* Get();
   bool Listen();
 
   /**
    * Reply vCard object to the *IPC* 'pullphonebook' request.
    *
    * @param aActor [in]          a blob actor containing the vCard objects
    * @param aPhonebookSize [in]  the number of vCard indexes in the blob
+   *
+   * @return true if the response packet has been packed correctly and started
+   *         to be sent to the remote device; false otherwise.
    */
-  void ReplyToPullPhonebook(BlobParent* aActor, uint16_t aPhonebookSize);
+  bool ReplyToPullPhonebook(BlobParent* aActor, uint16_t aPhonebookSize);
 
   /**
    * Reply vCard object to the *in-process* 'pullphonebook' request.
    *
    * @param aBlob [in]           a blob contained the vCard objects
    * @param aPhonebookSize [in]  the number of vCard indexes in the blob
+   *
+   * @return true if the response packet has been packed correctly and started
+   *         to be sent to the remote device; false otherwise.
    */
-  void ReplyToPullPhonebook(Blob* aBlob, uint16_t aPhonebookSize);
+  bool ReplyToPullPhonebook(Blob* aBlob, uint16_t aPhonebookSize);
 
   /**
    * Reply vCard object to the *IPC* 'pullvcardlisting' request.
    *
    * @param aActor [in]          a blob actor containing the vCard objects
    * @param aPhonebookSize [in]  the number of vCard indexes in the blob
+   *
+   * @return true if the response packet has been packed correctly and started
+   *         to be sent to the remote device; false otherwise.
    */
-  void ReplyToPullvCardListing(BlobParent* aActor, uint16_t aPhonebookSize);
+  bool ReplyToPullvCardListing(BlobParent* aActor, uint16_t aPhonebookSize);
 
   /**
    * Reply vCard object to the *in-process* 'pullvcardlisting' request.
    *
    * @param aBlob [in]           a blob contained the vCard objects
    * @param aPhonebookSize [in]  the number of vCard indexes in the blob
+   *
+   * @return true if the response packet has been packed correctly and started
+   *         to be sent to the remote device; false otherwise.
    */
-  void ReplyToPullvCardListing(Blob* aBlob, uint16_t aPhonebookSize);
+  bool ReplyToPullvCardListing(Blob* aBlob, uint16_t aPhonebookSize);
 
   /**
    * Reply vCard object to the *IPC* 'pullvcardentry' request.
    *
    * @param aActor [in]  a blob actor containing the vCard objects
+   *
+   * @return true if the response packet has been packed correctly and started
+   *         to be sent to the remote device; false otherwise.
    */
-  void ReplyToPullvCardEntry(BlobParent* aActor);
+  bool ReplyToPullvCardEntry(BlobParent* aActor);
 
   /**
    * Reply vCard object to the *in-process* 'pullvcardentry' request.
    *
    * @param aBlob [in]  a blob contained the vCard objects
+   *
+   * @return true if the response packet has been packed correctly and started
+   *         to be sent to the remote device; false otherwise.
    */
-  void ReplyToPullvCardEntry(Blob* aBlob);
+  bool 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);
+  bool ReplyToGet(nsIInputStream* aStream, uint16_t aPhonebookSize = 0);
+  bool GetInputStreamFromBlob(nsIInputStream* aStream, Blob* aBlob);
 
   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,
@@ -145,22 +167,38 @@ private:
   nsString mCurrentPath;
 
   /**
    * OBEX session status. Set when OBEX session is established.
    */
   bool mConnected;
   nsString mDeviceAddress;
 
+  /**
+   * Maximum packet length that remote device can receive
+   */
+  unsigned int mRemoteMaxPacketLength;
+
   // If a connection has been established, mSocket will be the socket
   // communicating with the remote socket. We maintain the invariant that if
   // mSocket is non-null, mServerSocket must be null (and vice versa).
   nsRefPtr<BluetoothSocket> mSocket;
 
   // Server socket. Once an inbound connection is established, it will hand
   // over the ownership to mSocket, and get a new server socket while Listen()
   // is called.
   nsRefPtr<BluetoothSocket> mServerSocket;
+
+  /**
+   * The data stream of vCards which is used in current processing response.
+   */
+  nsCOMPtr<nsIInputStream> mVCardDataStream;
+
+  /**
+   * A flag to indicate whether 'PhonebookSize' is mandatory for next OBEX
+   * response
+   */
+  bool mRequirePhonebookSize;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif // mozilla_dom_bluetooth_bluedroid_BluetoothPbapManager_h