Bug 932192 - Refactor OPP manager for multiple file transfer, r=echou
authorBen Tian <btian@mozilla.com>
Fri, 01 Nov 2013 11:21:13 +0800
changeset 167917 c01c64954f59e24162cf9ee8ed365f7166130064
parent 167916 61cb34dc206084878f6736597799d441445d8a9b
child 167918 fab3ca5efd23e0fa49f08196064fec6a62df7b41
push id3224
push userlsblakk@mozilla.com
push dateTue, 04 Feb 2014 01:06:49 +0000
treeherdermozilla-beta@60c04d0987f1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersechou
bugs932192
milestone28.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 932192 - Refactor OPP manager for multiple file transfer, r=echou
dom/bluetooth/BluetoothOppManager.cpp
dom/bluetooth/BluetoothOppManager.h
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -3,17 +3,16 @@
 /* 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 "base/basictypes.h"
 #include "BluetoothOppManager.h"
 
 #include "BluetoothProfileController.h"
-#include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothSocket.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "ObexBase.h"
 
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/RefPtr.h"
@@ -47,16 +46,28 @@ static const uint32_t kUpdateProgressBas
  * [opcode:1][packet length:2][headerId:1][header length:2]
  */
 static const uint32_t kPutRequestHeaderSize = 6;
 
 StaticRefPtr<BluetoothOppManager> sBluetoothOppManager;
 static bool sInShutdown = false;
 }
 
+class mozilla::dom::bluetooth::SendFileBatch {
+public:
+  SendFileBatch(const nsAString& aDeviceAddress, BlobParent* aActor)
+    : mDeviceAddress(aDeviceAddress)
+  {
+    mBlobs.AppendElement(aActor->GetBlob().get());
+  }
+
+  nsString mDeviceAddress;
+  nsCOMArray<nsIDOMBlob> mBlobs;
+};
+
 NS_IMETHODIMP
 BluetoothOppManager::Observe(nsISupports* aSubject,
                              const char* aTopic,
                              const PRUnichar* aData)
 {
   MOZ_ASSERT(sBluetoothOppManager);
 
   if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
@@ -225,16 +236,54 @@ BluetoothOppManager::Get()
   BluetoothOppManager *manager = new BluetoothOppManager();
   NS_ENSURE_TRUE(manager->Init(), nullptr);
 
   sBluetoothOppManager = manager;
   return sBluetoothOppManager;
 }
 
 void
+BluetoothOppManager::ConnectInternal(const nsAString& aDeviceAddress)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Stop listening because currently we only support one connection at a time.
+  if (mRfcommSocket) {
+    mRfcommSocket->Disconnect();
+    mRfcommSocket = nullptr;
+  }
+
+  if (mL2capSocket) {
+    mL2capSocket->Disconnect();
+    mL2capSocket = nullptr;
+  }
+
+  mIsServer = false;
+
+  BluetoothService* bs = BluetoothService::Get();
+  if (!bs || sInShutdown || mSocket) {
+    OnSocketConnectError(mSocket);
+    return;
+  }
+
+  mNeedsUpdatingSdpRecords = true;
+
+  nsString uuid;
+  BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
+
+  if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) {
+    OnSocketConnectError(mSocket);
+    return;
+  }
+
+  mSocket =
+    new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
+}
+
+void
 BluetoothOppManager::Connect(const nsAString& aDeviceAddress,
                              BluetoothProfileController* aController)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aController && !mController);
 
   BluetoothService* bs = BluetoothService::Get();
   if (!bs || sInShutdown) {
@@ -246,40 +295,18 @@ BluetoothOppManager::Connect(const nsASt
     if (mConnectedDeviceAddress == aDeviceAddress) {
       aController->OnConnect(NS_LITERAL_STRING(ERR_ALREADY_CONNECTED));
     } else {
       aController->OnConnect(NS_LITERAL_STRING(ERR_REACHED_CONNECTION_LIMIT));
     }
     return;
   }
 
-  mNeedsUpdatingSdpRecords = true;
-
-  nsString uuid;
-  BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
-
-  if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) {
-    aController->OnConnect(NS_LITERAL_STRING(ERR_SERVICE_CHANNEL_NOT_FOUND));
-    return;
-  }
-
-  // Stop listening because currently we only support one connection at a time.
-  if (mRfcommSocket) {
-    mRfcommSocket->Disconnect();
-    mRfcommSocket = nullptr;
-  }
-
-  if (mL2capSocket) {
-    mL2capSocket->Disconnect();
-    mL2capSocket = nullptr;
-  }
-
   mController = aController;
-  mSocket =
-    new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
+  OnConnect(EmptyString());
 }
 
 void
 BluetoothOppManager::Disconnect(BluetoothProfileController* aController)
 {
   if (!mSocket) {
     if (aController) {
       aController->OnDisconnect(NS_LITERAL_STRING(ERR_ALREADY_DISCONNECTED));
@@ -331,28 +358,31 @@ BluetoothOppManager::Listen()
       BT_WARNING("[OPP] Can't listen on L2CAP socket!");
       mRfcommSocket->Disconnect();
       mRfcommSocket = nullptr;
       mL2capSocket = nullptr;
       return false;
     }
   }
 
+  mIsServer = true;
+
   return true;
 }
 
 void
 BluetoothOppManager::StartSendingNextFile()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!IsConnected());
-  MOZ_ASSERT(mBlobs.Length() > mCurrentBlobIndex + 1);
 
-  mIsServer = false;
-  mBlob = mBlobs[++mCurrentBlobIndex];
+  MOZ_ASSERT(!IsConnected());
+  MOZ_ASSERT(!mBatches.IsEmpty());
+  MOZ_ASSERT(mBatches[0].mBlobs.Length() > mCurrentBlobIndex + 1);
+
+  mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
 
   // Before sending content, we have to send a header including
   // information such as file name, file length and content type.
   ExtractBlobHeaders();
   StartFileTransfer();
 
   if (mCurrentBlobIndex == 0) {
     // We may have more than one file waiting for transferring, but only one
@@ -366,28 +396,100 @@ BluetoothOppManager::StartSendingNextFil
 }
 
 bool
 BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
                               BlobParent* aActor)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  AppendBlobToSend(aDeviceAddress, aActor);
+  if (!mSocket) {
+    ProcessNextBatch();
+  }
+
+  return true;
+}
+
+void
+BluetoothOppManager::AppendBlobToSend(const nsAString& aDeviceAddress,
+                                      BlobParent* aActor)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  int indexTail = mBatches.Length() - 1;
+
+  /**
+   * Create a new batch if
+   * - mBatches is empty, or
+   * - aDeviceAddress differs from mDeviceAddress of the last batch
+   */
+  if (mBatches.IsEmpty() ||
+      aDeviceAddress != mBatches[indexTail].mDeviceAddress) {
+    SendFileBatch batch(aDeviceAddress, aActor);
+    mBatches.AppendElement(batch);
+  } else {
+    mBatches[indexTail].mBlobs.AppendElement(aActor->GetBlob().get());
+  }
+}
+
+void
+BluetoothOppManager::DiscardBlobsToSend()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(!mBatches.IsEmpty());
+  MOZ_ASSERT(!mIsServer);
+
+  int length = (int) mBatches[0].mBlobs.Length();
+  while (length > mCurrentBlobIndex + 1) {
+    mBlob = mBatches[0].mBlobs[++mCurrentBlobIndex];
+
+    BT_LOGR("%s: idx %d", __FUNCTION__, mCurrentBlobIndex);
+    ExtractBlobHeaders();
+    StartFileTransfer();
+    FileTransferComplete();
+  }
+}
+
+bool
+BluetoothOppManager::ProcessNextBatch()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // Remove the processed batch.
+  // A batch is processed if we've incremented mCurrentBlobIndex for it.
   if (mCurrentBlobIndex >= 0) {
-    if (mConnectedDeviceAddress != aDeviceAddress) {
-      return false;
-    }
+    ClearQueue();
+    mBatches.RemoveElementAt(0);
+    BT_LOGR("%s: REMOVE. %d remaining", __FUNCTION__, mBatches.Length());
+  }
 
-    mBlobs.AppendElement(aActor->GetBlob().get());
+  // Process the next batch
+  if (!mBatches.IsEmpty()) {
+    ConnectInternal(mBatches[0].mDeviceAddress);
     return true;
   }
 
-  mBlobs.AppendElement(aActor->GetBlob().get());
-  StartSendingNextFile();
-  return true;
+  // No more batch to process
+  return false;
+}
+
+void
+BluetoothOppManager::ClearQueue()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT(!mIsServer);
+  MOZ_ASSERT(!mBatches.IsEmpty());
+  MOZ_ASSERT(!mBatches[0].mBlobs.IsEmpty());
+
+  mCurrentBlobIndex = -1;
+  mBlob = nullptr;
+  mBatches[0].mBlobs.Clear();
 }
 
 bool
 BluetoothOppManager::StopSendingFile()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (mIsServer) {
@@ -429,17 +531,16 @@ BluetoothOppManager::ConfirmReceivingFil
 }
 
 void
 BluetoothOppManager::AfterFirstPut()
 {
   mUpdateProgressCounter = 1;
   mPutFinalFlag = false;
   mReceivedDataBufferOffset = 0;
-  mSendTransferCompleteFlag = false;
   mSentFileLength = 0;
   mWaitingToSendPutFinal = false;
   mSuccessFlag = false;
   mBodySegmentLength = 0;
 }
 
 void
 BluetoothOppManager::AfterOppConnected()
@@ -462,23 +563,20 @@ BluetoothOppManager::AfterOppConnected()
 }
 
 void
 BluetoothOppManager::AfterOppDisconnected()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mConnected = false;
-  mIsServer = true;
   mLastCommand = 0;
   mPacketLeftLength = 0;
   mDsFile = nullptr;
 
-  ClearQueue();
-
   // We can't reset mSuccessFlag here since this function may be called
   // before we send system message of transfer complete
   // mSuccessFlag = false;
 
   if (mInputStream) {
     mInputStream->Close();
     mInputStream = nullptr;
   }
@@ -543,17 +641,17 @@ BluetoothOppManager::CreateFile()
 
 bool
 BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
 {
   NS_ENSURE_TRUE(mOutputStream, false);
 
   uint32_t wrote = 0;
   mOutputStream->Write((const char*)aData, aDataLength, &wrote);
-  NS_ENSURE_TRUE(aDataLength == wrote, false);
+  NS_ENSURE_TRUE(aDataLength == (int) wrote, false);
 
   return true;
 }
 
 // Virtual function of class SocketConsumer
 void
 BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader)
 {
@@ -687,16 +785,18 @@ BluetoothOppManager::ValidateFileName()
       mFileName.Replace(i, 1, PRUnichar('_'));
     }
   }
 }
 
 void
 BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   uint8_t opCode;
   int packetLength;
   int receivedLength = aMessage->mSize;
 
   if (mPacketLeftLength > 0) {
     opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
     packetLength = mPacketLeftLength;
   } else {
@@ -710,18 +810,16 @@ BluetoothOppManager::ServerDataHandler(U
          opCode == ObexRequestCode::PutFinal)) {
       mNewFileFlag = true;
       AfterFirstPut();
     }
   }
 
   ObexHeaderSet pktHeaders(opCode);
   if (opCode == ObexRequestCode::Connect) {
-    mIsServer = true;
-
     // Section 3.3.1 "Connect", IrOBEX 1.2
     // [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
     // [Headers:var]
     ParseHeaders(&aMessage->mData[7],
                  receivedLength - 7,
                  &pktHeaders);
     ReplyToConnect();
     AfterOppConnected();
@@ -838,29 +936,20 @@ BluetoothOppManager::ServerDataHandler(U
     BT_WARNING("Unsupported ObexRequestCode");
   } else {
     ReplyError(ObexResponseCode::NotImplemented);
     BT_WARNING("Unrecognized ObexRequestCode");
   }
 }
 
 void
-BluetoothOppManager::ClearQueue()
-{
-  mCurrentBlobIndex = -1;
-  mBlob = nullptr;
-
-  while (!mBlobs.IsEmpty()) {
-    mBlobs.RemoveElement(mBlobs[0]);
-  }
-}
-
-void
 BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   uint8_t opCode;
   int packetLength;
 
   if (mPacketLeftLength > 0) {
     opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
     packetLength = mPacketLeftLength;
   } else {
     opCode = aMessage->mData[0];
@@ -893,17 +982,17 @@ BluetoothOppManager::ClientDataHandler(U
     mSuccessFlag = true;
     FileTransferComplete();
 
     if (mInputStream) {
       mInputStream->Close();
       mInputStream = nullptr;
     }
 
-    if (mCurrentBlobIndex + 1 == mBlobs.Length()) {
+    if (mCurrentBlobIndex + 1 == (int) mBatches[0].mBlobs.Length()) {
       SendDisconnectRequest();
     } else {
       StartSendingNextFile();
     }
   } else if (mLastCommand == ObexRequestCode::Abort) {
     SendDisconnectRequest();
     FileTransferComplete();
   } else if (mLastCommand == ObexRequestCode::Disconnect) {
@@ -922,17 +1011,17 @@ BluetoothOppManager::ClientDataHandler(U
     AfterOppConnected();
 
     // Keep remote information
     mRemoteObexVersion = aMessage->mData[3];
     mRemoteConnectionFlags = aMessage->mData[4];
     mRemoteMaxPacketLength =
       (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
 
-    sBluetoothOppManager->SendPutHeaderRequest(mFileName, mFileLength);
+    SendPutHeaderRequest(mFileName, mFileLength);
   } else if (mLastCommand == ObexRequestCode::Put) {
     if (mWaitingToSendPutFinal) {
       SendPutFinalRequest();
       return;
     }
 
     if (kUpdateProgressBase * mUpdateProgressCounter < mSentFileLength) {
       UpdateProgress();
@@ -1256,16 +1345,18 @@ BluetoothOppManager::StartFileTransfer()
   name.AssignLiteral("contentType");
   v = mContentType;
   parameters.AppendElement(BluetoothNamedValue(name, v));
 
   if (!BroadcastSystemMessage(type, parameters)) {
     BT_WARNING("Failed to broadcast [bluetooth-opp-transfer-start]");
     return;
   }
+
+  mSendTransferCompleteFlag = false;
 }
 
 void
 BluetoothOppManager::UpdateProgress()
 {
   nsString type, name;
   BluetoothValue v;
   InfallibleTArray<BluetoothNamedValue> parameters;
@@ -1333,16 +1424,17 @@ BluetoothOppManager::NotifyAboutFileChan
   NS_ENSURE_TRUE_VOID(obs);
 
   obs->NotifyObservers(mDsFile, "file-watcher-notify", data.get());
 }
 
 void
 BluetoothOppManager::OnSocketConnectSuccess(BluetoothSocket* aSocket)
 {
+  BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer)? "server" : "client");
   MOZ_ASSERT(aSocket);
 
   /**
    * If the created connection is an inbound connection, close another server
    * socket because currently only one file-transfer session is allowed. After
    * that, we need to make sure that both server socket would be nulled out.
    * As for outbound connections, we just notify the controller that it's done.
    */
@@ -1359,31 +1451,46 @@ BluetoothOppManager::OnSocketConnectSucc
     mRfcommSocket->Disconnect();
     mRfcommSocket = nullptr;
   }
 
   // Cache device address since we can't get socket address when a remote
   // device disconnect with us.
   mSocket->GetAddress(mConnectedDeviceAddress);
 
-  OnConnect(EmptyString());
+  // Start sending file if we connect as a client
+  if (!mIsServer) {
+    StartSendingNextFile();
+  }
 }
 
 void
 BluetoothOppManager::OnSocketConnectError(BluetoothSocket* aSocket)
 {
+  BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer)? "server" : "client");
+
   mRfcommSocket = nullptr;
   mL2capSocket = nullptr;
+  mSocket = nullptr;
 
-  OnConnect(NS_LITERAL_STRING("SocketConnectionError"));
+  if (!mIsServer) {
+    // Inform gaia of remaining blobs' sending failure
+    DiscardBlobsToSend();
+  }
+
+  // Listen as a server if there's no more batch to process
+  if (!ProcessNextBatch()) {
+    Listen();
+  }
 }
 
 void
 BluetoothOppManager::OnSocketDisconnect(BluetoothSocket* aSocket)
 {
+  BT_LOGR("%s: [%s]", __FUNCTION__, (mIsServer)? "server" : "client");
   MOZ_ASSERT(aSocket);
 
   if (aSocket != mSocket) {
     // Do nothing when a listening server socket is closed.
     return;
   }
 
   /**
@@ -1392,24 +1499,33 @@ BluetoothOppManager::OnSocketDisconnect(
    * delete the broken file when we failed to receive a file from the remote,
    * and notify the transfer has been completed (but failed). We also call
    * AfterOppDisconnected here to ensure all variables will be cleaned.
    */
   if (!mSuccessFlag) {
     if (mIsServer) {
       DeleteReceivedFile();
     }
+
     FileTransferComplete();
+    if (!mIsServer) {
+      // Inform gaia of remaining blobs' sending failure
+      DiscardBlobsToSend();
+    }
   }
 
   AfterOppDisconnected();
   mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
   mSuccessFlag = false;
 
-  OnDisconnect(EmptyString());
+  mSocket = nullptr;
+  // Listen as a server if there's no more batch to process
+  if (!ProcessNextBatch()) {
+    Listen();
+  }
 }
 
 void
 BluetoothOppManager::OnGetServiceChannel(const nsAString& aDeviceAddress,
                                          const nsAString& aServiceUuid,
                                          int aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -1418,41 +1534,41 @@ BluetoothOppManager::OnGetServiceChannel
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   if (aChannel < 0) {
     if (mNeedsUpdatingSdpRecords) {
       mNeedsUpdatingSdpRecords = false;
       bs->UpdateSdpRecords(aDeviceAddress, this);
     } else {
-      OnConnect(NS_LITERAL_STRING(ERR_SERVICE_CHANNEL_NOT_FOUND));
+      OnSocketConnectError(mSocket);
     }
 
     return;
   }
 
   if (!mSocket->Connect(NS_ConvertUTF16toUTF8(aDeviceAddress), aChannel)) {
-    OnConnect(NS_LITERAL_STRING("SocketConnectionError"));
+    OnSocketConnectError(mSocket);
   }
 }
 
 void
 BluetoothOppManager::OnUpdateSdpRecords(const nsAString& aDeviceAddress)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
 
   BluetoothService* bs = BluetoothService::Get();
   NS_ENSURE_TRUE_VOID(bs);
 
   nsString uuid;
   BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
 
   if (NS_FAILED(bs->GetServiceChannel(aDeviceAddress, uuid, this))) {
-    OnConnect(NS_LITERAL_STRING(ERR_SERVICE_CHANNEL_NOT_FOUND));
+    OnSocketConnectError(mSocket);
   }
 }
 
 NS_IMPL_ISUPPORTS1(BluetoothOppManager, nsIObserver)
 
 bool
 BluetoothOppManager::AcquireSdcardMountLock()
 {
@@ -1486,19 +1602,16 @@ BluetoothOppManager::OnConnect(const nsA
   controller->OnConnect(aErrorStr);
 }
 
 void
 BluetoothOppManager::OnDisconnect(const nsAString& aErrorStr)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  mSocket = nullptr;
-  Listen();
-
   /**
    * On the one hand, notify the controller that we've done for outbound
    * connections. On the other hand, we do nothing for inbound connections.
    */
   NS_ENSURE_TRUE_VOID(mController);
 
   nsRefPtr<BluetoothProfileController> controller = mController.forget();
   controller->OnDisconnect(aErrorStr);
--- a/dom/bluetooth/BluetoothOppManager.h
+++ b/dom/bluetooth/BluetoothOppManager.h
@@ -18,16 +18,17 @@
 class nsIOutputStream;
 class nsIInputStream;
 class nsIVolumeMountLock;
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocket;
 class ObexHeaderSet;
+class SendFileBatch;
 
 class BluetoothOppManager : public BluetoothSocketObserver
                           , public BluetoothProfileManagerBase
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
@@ -41,17 +42,17 @@ public:
 
   ~BluetoothOppManager();
   static BluetoothOppManager* Get();
   void ClientDataHandler(mozilla::ipc::UnixSocketRawData* aMessage);
   void ServerDataHandler(mozilla::ipc::UnixSocketRawData* aMessage);
 
   bool Listen();
 
-  bool SendFile(const nsAString& aDeviceAddress, BlobParent* aBlob);
+  bool SendFile(const nsAString& aDeviceAddress, BlobParent* aActor);
   bool StopSendingFile();
   bool ConfirmReceivingFile(bool aConfirm);
 
   void SendConnectRequest();
   void SendPutHeaderRequest(const nsAString& aFileName, int aFileSize);
   void SendPutRequest(uint8_t* aFileBody, int aFileBodyLength);
   void SendPutFinalRequest();
   void SendDisconnectRequest();
@@ -120,16 +121,20 @@ private:
   void AfterOppDisconnected();
   void ValidateFileName();
   bool IsReservedChar(PRUnichar c);
   void ClearQueue();
   void RetrieveSentFileName();
   void NotifyAboutFileChange();
   bool AcquireSdcardMountLock();
   void SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize);
+  void AppendBlobToSend(const nsAString& aDeviceAddress, BlobParent* aActor);
+  void DiscardBlobsToSend();
+  bool ProcessNextBatch();
+  void ConnectInternal(const nsAString& aDeviceAddress);
 
   /**
    * OBEX session status.
    * Set when OBEX session is established.
    */
   bool mConnected;
   nsString mConnectedDeviceAddress;
 
@@ -202,17 +207,17 @@ private:
   uint32_t mSentFileLength;
   bool mWaitingToSendPutFinal;
 
   nsAutoArrayPtr<uint8_t> mBodySegment;
   nsAutoArrayPtr<uint8_t> mReceivedDataBuffer;
 
   int mCurrentBlobIndex;
   nsCOMPtr<nsIDOMBlob> mBlob;
-  nsCOMArray<nsIDOMBlob> mBlobs;
+  nsTArray<SendFileBatch> mBatches;
 
   /**
    * A seperate member thread is required because our read calls can block
    * execution, which is not allowed to happen on the IOThread.
    */
   nsCOMPtr<nsIThread> mReadFileThread;
   nsCOMPtr<nsIOutputStream> mOutputStream;
   nsCOMPtr<nsIInputStream> mInputStream;