Bug 795410 - patch 3: Get file content and send, r=qdot
authorEric Chou <echou@mozilla.com>
Fri, 28 Sep 2012 20:03:36 +0800
changeset 115024 5b799e7132346d8cf4cde4c1738fdfb4f5edb2d6
parent 115023 02747dcace863372fef293993a714720af1b8763
child 115025 d4355e045ea76e8bd1f473bdae0e52194e11a4f3
child 115031 aade44131d6c270a90faca358e1734709d51f74b
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersqdot
bugs795410
milestone18.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 795410 - patch 3: Get file content and send, r=qdot From 01ff8209cc5242bdf6c89432899ab4658e71e58a Mon Sep 17 00:00:00 2001
dom/bluetooth/BluetoothOppManager.cpp
dom/bluetooth/BluetoothOppManager.h
--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -8,26 +8,94 @@
 #include "BluetoothOppManager.h"
 
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothServiceUuid.h"
 #include "ObexBase.h"
 
 #include "mozilla/RefPtr.h"
-#include "nsIDOMFile.h"
+#include "nsIInputStream.h"
 
 USING_BLUETOOTH_NAMESPACE
 using namespace mozilla::ipc;
 
 static mozilla::RefPtr<BluetoothOppManager> sInstance;
+static nsCOMPtr<nsIInputStream> stream = nullptr;
+static uint64_t sSentFileSize = 0;
+
+class ReadFileTask : public nsRunnable
+{
+public:
+  ReadFileTask(nsIDOMBlob* aBlob) : mBlob(aBlob)
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+  }
+
+  NS_IMETHOD Run()
+  {
+    if (NS_IsMainThread()) {
+      NS_WARNING("Can't read file from main thread");
+      return NS_ERROR_FAILURE;
+    }
+
+    uint64_t fileSize;
+    nsresult rv = mBlob->GetSize(&fileSize);
+    if (NS_FAILED(rv)) {
+      NS_WARNING("Can't get file size");
+      return NS_ERROR_FAILURE;;
+    }
+
+    if (stream == nullptr) {
+      rv = mBlob->GetInternalStream(getter_AddRefs(stream));
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Can't get internal stream of blob");
+        return NS_ERROR_FAILURE;
+      }
+    }
+
+    /*
+     * 255 is the Minimum OBEX Packet Length (See section 3.3.1.4,
+     * IrOBEX ver 1.2)
+     */
+    char buf[255];
+    uint32_t numRead;
+    int offset = 0;
+
+    // function inputstream->Read() only works on non-main thread
+    rv = stream->Read(buf, sizeof(buf), &numRead);
+    if (NS_FAILED(rv)) {
+      // Needs error handling here
+      return NS_ERROR_FAILURE;
+    }
+
+    if (numRead > 0) {
+      if (sSentFileSize + numRead >= fileSize) {
+        sInstance->SendPutRequest((uint8_t*)buf, numRead, true);
+      } else {
+        sInstance->SendPutRequest((uint8_t*)buf, numRead, false);
+      }
+
+      sSentFileSize += numRead;
+    }
+
+    return NS_OK;
+  };
+
+private:
+  nsCOMPtr<nsIDOMBlob> mBlob;
+};
 
 BluetoothOppManager::BluetoothOppManager() : mConnected(false)
                                            , mConnectionId(1)
                                            , mLastCommand(0)
+                                           , mBlob(nullptr)
+                                           , mRemoteObexVersion(0)
+                                           , mRemoteConnectionFlags(0)
+                                           , mRemoteMaxPacketLength(0)
 {
 }
 
 BluetoothOppManager::~BluetoothOppManager()
 {
 }
 
 //static
@@ -77,58 +145,217 @@ BluetoothOppManager::Disconnect()
 {
   CloseSocket();
 }
 
 bool
 BluetoothOppManager::SendFile(BlobParent* aActor,
                               BluetoothReplyRunnable* aRunnable)
 {
-  // will implement in another patch.
+  if (mBlob) {
+    // Means there's a sending process. Reply error.
+    return false;
+  }
+
+  /*
+   * Process of sending a file:
+   *  - Keep blob because OPP connection has not been established yet.
+   *  - Create an OPP connection by SendConnectRequest()
+   *  - After receiving the response, start to read file and send.
+   */
+  mBlob = aActor->GetBlob();
+
+  SendConnectRequest();
+
   return true;
 }
 
 bool
 BluetoothOppManager::StopSendingFile(BluetoothReplyRunnable* aRunnable)
 {
   // will implement in another patch.
   return true;
 }
 
 // Virtual function of class SocketConsumer
 void
 BluetoothOppManager::ReceiveSocketData(UnixSocketRawData* aMessage)
 {
   uint8_t responseCode = aMessage->mData[0];
+  int packetLength = (((int)aMessage->mData[1]) << 8) | aMessage->mData[2];
+  int receivedLength = aMessage->mSize;
 
   if (mLastCommand == ObexRequestCode::Connect) {
     if (responseCode == ObexResponseCode::Success) {
       mConnected = true;
+
+      // Keep remote information
+      mRemoteObexVersion = aMessage->mData[3];
+      mRemoteConnectionFlags = aMessage->mData[4];
+      mRemoteMaxPacketLength =
+        (((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
+
+      if (mBlob) {
+        /*
+         * Before sending content, we have to send a header including
+         * information such as file name, file length and content type.
+         */
+        nsresult rv;
+        nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
+        nsString fileName;
+        if (file) {
+          rv = file->GetName(fileName);
+        }
+
+        if (!file || fileName.IsEmpty()) {
+          fileName.AssignLiteral("Unknown");
+        }
+
+        uint64_t fileSize;
+        rv = mBlob->GetSize(&fileSize);
+        if (NS_FAILED(rv)) {
+          NS_WARNING("Can't get file size");
+          return;
+        }
+
+        sSentFileSize = 0;
+        sInstance->SendPutHeaderRequest(fileName, fileSize);
+      }
     }
   } else if (mLastCommand == ObexRequestCode::Disconnect) {
-    if (responseCode == ObexResponseCode::Success) {
+    if (responseCode != ObexResponseCode::Success) {
+      // FIXME: Needs error handling here
+      NS_WARNING("[OPP] Disconnect failed");
+    } else {
       mConnected = false;
+      mBlob = nullptr;
+    }
+  } else if (mLastCommand == ObexRequestCode::Put) {
+    if (responseCode != ObexResponseCode::Continue) {
+      // FIXME: Needs error handling here
+      NS_WARNING("[OPP] Put failed");
+    } else {
+      nsCOMPtr<nsIThread> t;
+      NS_NewThread(getter_AddRefs(t));
+
+      nsRefPtr<ReadFileTask> task = new ReadFileTask(mBlob);
+
+      if (NS_FAILED(t->Dispatch(task, NS_DISPATCH_NORMAL))) {
+        NS_WARNING("Cannot dispatch ring task!");
+      }
+    }
+  } else if (mLastCommand == ObexRequestCode::PutFinal) {
+    if (responseCode != ObexResponseCode::Success) {
+      // FIXME: Needs error handling here
+      NS_WARNING("[OPP] PutFinal failed");
+    } else {
+      SendDisconnectRequest();
     }
   }
 }
 
 void
-BluetoothOppManager::SendConnectReqeust()
+BluetoothOppManager::SendConnectRequest()
 {
+  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];
   int index = 7;
 
   req[3] = 0x10; // version=1.0
   req[4] = 0x00; // flag=0x00
   req[5] = BluetoothOppManager::MAX_PACKET_LENGTH >> 8;
   req[6] = BluetoothOppManager::MAX_PACKET_LENGTH;
 
-  index += AppendHeaderConnectionId(&req[index], mConnectionId++);
+  index += AppendHeaderConnectionId(&req[index], mConnectionId);
   SetObexPacketInfo(req, ObexRequestCode::Connect, index);
   mLastCommand = ObexRequestCode::Connect;
 
   UnixSocketRawData* s = new UnixSocketRawData(index);
   memcpy(s->mData, req, s->mSize);
   SendSocketData(s);
 }
 
+void
+BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName, int aFileSize)
+{
+  uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
+
+  const PRUnichar* fileNamePtr = aFileName.BeginReading();
+  uint32_t len = aFileName.Length();
+  uint8_t* fileName = new uint8_t[(len + 1) * 2];
+  for (int i = 0; i < len; i++) {
+    fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8);
+    fileName[i * 2 + 1] = (uint8_t)fileNamePtr[i];
+  }
+
+  fileName[len * 2] = 0x00;
+  fileName[len * 2 + 1] = 0x00;
+
+  int index = 3;
+  index += AppendHeaderConnectionId(&req[index], mConnectionId);
+  index += AppendHeaderName(&req[index], (char*)fileName, (len + 1) * 2);
+  index += AppendHeaderLength(&req[index], aFileSize);
+
+  SetObexPacketInfo(req, ObexRequestCode::Put, index);
+  mLastCommand = ObexRequestCode::Put;
+
+  UnixSocketRawData* s = new UnixSocketRawData(index);
+  memcpy(s->mData, req, s->mSize);
+  SendSocketData(s);
+
+  delete [] fileName;
+  delete [] req;
+}
+
+void
+BluetoothOppManager::SendPutRequest(uint8_t* aFileBody,
+                                    int aFileBodyLength,
+                                    bool aFinal)
+{
+  int sentFileBodyLength = 0;
+  int index = 3;
+  int packetLeftSpace = mRemoteMaxPacketLength - index - 3;
+
+  if (!mConnected) return;
+  if (aFileBodyLength > packetLeftSpace) {
+    NS_WARNING("Not allowed such a small MaxPacketLength value");
+    return;
+  }
+
+  // IrOBEX 1.2 3.3.3
+  // [opcode:1][length:2][Headers:var]
+  uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
+
+  index += AppendHeaderBody(&req[index], aFileBody, aFileBodyLength);
+
+  if (aFinal) {
+    SetObexPacketInfo(req, ObexRequestCode::PutFinal, index);
+    mLastCommand = ObexRequestCode::PutFinal;
+  } else {
+    SetObexPacketInfo(req, ObexRequestCode::Put, index);
+    mLastCommand = ObexRequestCode::Put;
+  }
+
+  UnixSocketRawData* s = new UnixSocketRawData(index);
+  memcpy(s->mData, req, s->mSize);
+  SendSocketData(s);
+
+  delete [] req;
+}
+
+void
+BluetoothOppManager::SendDisconnectRequest()
+{
+  // IrOBEX 1.2 3.3.2
+  // [opcode:1][length:2][Headers:var]
+  uint8_t req[255];
+  int index = 3;
+
+  SetObexPacketInfo(req, ObexRequestCode::Disconnect, index);
+  mLastCommand = ObexRequestCode::Disconnect;
+
+  UnixSocketRawData* s = new UnixSocketRawData(index);
+  memcpy(s->mData, req, s->mSize);
+  SendSocketData(s);
+}
--- a/dom/bluetooth/BluetoothOppManager.h
+++ b/dom/bluetooth/BluetoothOppManager.h
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
 #define mozilla_dom_bluetooth_bluetoothoppmanager_h__
 
 #include "BluetoothCommon.h"
 #include "mozilla/dom/ipc/Blob.h"
 #include "mozilla/ipc/UnixSocket.h"
+#include "nsIDOMFile.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothReplyRunnable;
 
 class BluetoothOppManager : public mozilla::ipc::UnixSocketConsumer
 {
 public:
@@ -45,20 +46,31 @@ public:
                BluetoothReplyRunnable* aRunnable);
   void Disconnect();
 
   bool SendFile(BlobParent* aBlob,
                 BluetoothReplyRunnable* aRunnable);
 
   bool StopSendingFile(BluetoothReplyRunnable* aRunnable);
 
+  // xxx For runnable use
+  void SendConnectRequest();
+  void SendPutHeaderRequest(const nsAString& aFileName, int aFileSize);
+  void SendPutRequest(uint8_t* aFileBody, int aFileBodyLength,
+                      bool aFinal);
+  void SendDisconnectRequest();
+
 private:
   BluetoothOppManager();
-  void SendConnectReqeust();
 
   bool mConnected;
   int mConnectionId;
   int mLastCommand;
+  uint8_t mRemoteObexVersion;
+  uint8_t mRemoteConnectionFlags;
+  int mRemoteMaxPacketLength;
+
+  nsCOMPtr<nsIDOMBlob> mBlob;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif