--- a/dom/bluetooth/BluetoothOppManager.cpp
+++ b/dom/bluetooth/BluetoothOppManager.cpp
@@ -225,16 +225,17 @@ BluetoothOppManager::BluetoothOppManager
, mBodySegmentLength(0)
, mReceivedDataBufferOffset(0)
, mAbortFlag(false)
, mNewFileFlag(false)
, mPutFinalFlag(false)
, mSendTransferCompleteFlag(false)
, mSuccessFlag(false)
, mWaitingForConfirmationFlag(false)
+ , mCurrentBlobIndex(-1)
{
mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
Listen();
}
BluetoothOppManager::~BluetoothOppManager()
{
}
@@ -270,20 +271,17 @@ BluetoothOppManager::Connect(const nsASt
}
if (mL2capSocket) {
mL2capSocket->Disconnect();
mL2capSocket = nullptr;
}
BluetoothService* bs = BluetoothService::Get();
- if (!bs) {
- NS_WARNING("BluetoothService not available!");
- return false;
- }
+ NS_ENSURE_TRUE(bs, false);
nsString uuid;
BluetoothUuidHelper::GetString(BluetoothServiceClass::OBJECT_PUSH, uuid);
mRunnable = aRunnable;
mSocket =
new BluetoothSocket(this, BluetoothSocketType::RFCOMM, true, true);
@@ -349,104 +347,81 @@ BluetoothOppManager::Listen()
mL2capSocket = nullptr;
return false;
}
}
return true;
}
-bool
-BluetoothOppManager::SendFile(BlobParent* aActor)
+void
+BluetoothOppManager::StartSendingNextFile()
{
- if (mBlob) {
- // Means there's a sending process. Reply error.
- return false;
- }
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!IsTransferring());
+ MOZ_ASSERT(mBlobs.Length() > mCurrentBlobIndex + 1);
+
+ mBlob = mBlobs[++mCurrentBlobIndex];
- /*
- * Process of sending a file:
- * - Keep blob because OPP connection has not been established yet.
- * - Try to retrieve file name from the blob or assign one if failed to get.
- * - Create an OPP connection by SendConnectRequest()
- * - After receiving the response, start to read file and send.
- */
- mBlob = aActor->GetBlob();
+ // Before sending content, we have to send a header including
+ // information such as file name, file length and content type.
+ ExtractBlobHeaders();
+ StartFileTransfer();
- sFileName.Truncate();
-
- nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
- if (file) {
- file->GetName(sFileName);
+ if (mCurrentBlobIndex == 0) {
+ // We may have more than one file waiting for transferring, but only one
+ // CONNECT request would be sent. Therefore check if this is the very first
+ // file at the head of queue.
+ SendConnectRequest();
+ } else {
+ SendPutHeaderRequest(sFileName, sFileLength);
+ AfterFirstPut();
}
- /**
- * We try our best to get the file extention to avoid interoperability issues.
- * However, once we found that we are unable to get suitable extension or
- * information about the content type, sending a pre-defined file name without
- * extension would be fine.
- */
- if (sFileName.IsEmpty()) {
- sFileName.AssignLiteral("Unknown");
- }
+ mTransferMode = false;
+}
- int32_t offset = sFileName.RFindChar('/');
- if (offset != kNotFound) {
- sFileName = Substring(sFileName, offset + 1);
+bool
+BluetoothOppManager::SendFile(const nsAString& aDeviceAddress,
+ BlobParent* aActor)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mCurrentBlobIndex >= 0) {
+ if (mConnectedDeviceAddress != aDeviceAddress) {
+ return false;
+ }
+
+ mBlobs.AppendElement(aActor->GetBlob().get());
+ return true;
}
- offset = sFileName.RFindChar('.');
- if (offset == kNotFound) {
- nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
-
- if (mimeSvc) {
- nsString mimeType;
- mBlob->GetType(mimeType);
-
- nsCString extension;
- nsresult rv =
- mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
- EmptyCString(),
- extension);
- if (NS_SUCCEEDED(rv)) {
- sFileName.AppendLiteral(".");
- AppendUTF8toUTF16(extension, sFileName);
- }
- }
- }
-
- SendConnectRequest();
- mTransferMode = false;
- StartFileTransfer();
-
+ mBlobs.AppendElement(aActor->GetBlob().get());
+ StartSendingNextFile();
return true;
}
bool
BluetoothOppManager::StopSendingFile()
{
mAbortFlag = true;
return true;
}
bool
BluetoothOppManager::ConfirmReceivingFile(bool aConfirm)
{
- if (!mConnected) return false;
+ NS_ENSURE_TRUE(mConnected, false);
+ NS_ENSURE_TRUE(mWaitingForConfirmationFlag, false);
- if (!mWaitingForConfirmationFlag) {
- NS_WARNING("We are not waiting for a confirmation now.");
- return false;
- }
+ MOZ_ASSERT(mPacketLeftLength == 0);
+
mWaitingForConfirmationFlag = false;
- NS_ASSERTION(mPacketLeftLength == 0,
- "Should not be in the middle of receiving a PUT packet.");
-
// For the first packet of first file
bool success = false;
if (aConfirm) {
StartFileTransfer();
if (CreateFile()) {
success = WriteToFile(mBodySegment.get(), mBodySegmentLength);
}
}
@@ -485,19 +460,20 @@ BluetoothOppManager::AfterOppConnected()
void
BluetoothOppManager::AfterOppDisconnected()
{
MOZ_ASSERT(NS_IsMainThread());
mConnected = false;
mLastCommand = 0;
- mBlob = nullptr;
mPacketLeftLength = 0;
+ 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;
}
@@ -532,21 +508,21 @@ BluetoothOppManager::DeleteReceivedFile(
}
f->Remove(false);
}
bool
BluetoothOppManager::CreateFile()
{
+ MOZ_ASSERT(mPacketLeftLength == 0);
+
nsString path;
path.AssignLiteral(TARGET_FOLDER);
- MOZ_ASSERT(mPacketLeftLength == 0);
-
nsCOMPtr<nsIFile> f;
nsresult rv;
rv = NS_NewLocalFile(path + sFileName, false, getter_AddRefs(f));
if (NS_FAILED(rv)) {
NS_WARNING("Couldn't new a local file");
return false;
}
@@ -595,27 +571,21 @@ BluetoothOppManager::CreateFile()
}
return true;
}
bool
BluetoothOppManager::WriteToFile(const uint8_t* aData, int aDataLength)
{
- if (!mOutputStream) {
- NS_WARNING("No available output stream");
- return false;
- }
+ NS_ENSURE_TRUE(mOutputStream, false);
uint32_t wrote = 0;
mOutputStream->Write((const char*)aData, aDataLength, &wrote);
- if (aDataLength != wrote) {
- NS_WARNING("Writing to the file failed");
- return false;
- }
+ NS_ENSURE_TRUE(aDataLength == wrote, false);
return true;
}
// Virtual function of class SocketConsumer
void
BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader)
{
@@ -639,16 +609,18 @@ BluetoothOppManager::ExtractPacketHeader
aHeader.GetBodyLength(&mBodySegmentLength);
}
}
bool
BluetoothOppManager::ExtractBlobHeaders()
{
+ RetrieveSentFileName();
+
nsresult rv = mBlob->GetType(sContentType);
if (NS_FAILED(rv)) {
NS_WARNING("Can't get content type");
SendDisconnectRequest();
return false;
}
uint64_t fileLength;
@@ -676,16 +648,62 @@ BluetoothOppManager::ExtractBlobHeaders(
NS_WARNING("Can't create thread");
SendDisconnectRequest();
return false;
}
return true;
}
+void
+BluetoothOppManager::RetrieveSentFileName()
+{
+ sFileName.Truncate();
+
+ nsCOMPtr<nsIDOMFile> file = do_QueryInterface(mBlob);
+ if (file) {
+ file->GetName(sFileName);
+ }
+
+ /**
+ * We try our best to get the file extention to avoid interoperability issues.
+ * However, once we found that we are unable to get suitable extension or
+ * information about the content type, sending a pre-defined file name without
+ * extension would be fine.
+ */
+ if (sFileName.IsEmpty()) {
+ sFileName.AssignLiteral("Unknown");
+ }
+
+ int32_t offset = sFileName.RFindChar('/');
+ if (offset != kNotFound) {
+ sFileName = Substring(sFileName, offset + 1);
+ }
+
+ offset = sFileName.RFindChar('.');
+ if (offset == kNotFound) {
+ nsCOMPtr<nsIMIMEService> mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID);
+
+ if (mimeSvc) {
+ nsString mimeType;
+ mBlob->GetType(mimeType);
+
+ nsCString extension;
+ nsresult rv =
+ mimeSvc->GetPrimaryExtension(NS_LossyConvertUTF16toASCII(mimeType),
+ EmptyCString(),
+ extension);
+ if (NS_SUCCEEDED(rv)) {
+ sFileName.AppendLiteral(".");
+ AppendUTF8toUTF16(extension, sFileName);
+ }
+ }
+ }
+}
+
bool
BluetoothOppManager::IsReservedChar(PRUnichar c)
{
return (c < 0x0020 ||
c == PRUnichar('?') || c == PRUnichar('|') || c == PRUnichar('<') ||
c == PRUnichar('>') || c == PRUnichar('"') || c == PRUnichar(':') ||
c == PRUnichar('/') || c == PRUnichar('*') || c == PRUnichar('\\'));
}
@@ -841,16 +859,27 @@ BluetoothOppManager::ServerDataHandler(U
FileTransferComplete();
}
} else {
NS_WARNING("Unhandled ObexRequestCode");
}
}
void
+BluetoothOppManager::ClearQueue()
+{
+ mCurrentBlobIndex = -1;
+ mBlob = nullptr;
+
+ while (!mBlobs.IsEmpty()) {
+ mBlobs.RemoveElement(mBlobs[0]);
+ }
+}
+
+void
BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
{
uint8_t opCode;
int packetLength;
if (mPacketLeftLength > 0) {
opCode = mPutFinalFlag ? ObexRequestCode::PutFinal : ObexRequestCode::Put;
packetLength = mPacketLeftLength;
@@ -879,17 +908,27 @@ BluetoothOppManager::ClientDataHandler(U
NS_WARNING(str.get());
FileTransferComplete();
return;
}
if (mLastCommand == ObexRequestCode::PutFinal) {
mSuccessFlag = true;
FileTransferComplete();
- SendDisconnectRequest();
+
+ if (mInputStream) {
+ mInputStream->Close();
+ mInputStream = nullptr;
+ }
+
+ if (mCurrentBlobIndex + 1 == mBlobs.Length()) {
+ SendDisconnectRequest();
+ } else {
+ StartSendingNextFile();
+ }
} else if (mLastCommand == ObexRequestCode::Abort) {
SendDisconnectRequest();
FileTransferComplete();
} else if (mLastCommand == ObexRequestCode::Disconnect) {
AfterOppDisconnected();
// Most devices will directly terminate connection after receiving
// Disconnect request, so we make a delay here. If the socket hasn't been
// disconnected, we will close it.
@@ -904,26 +943,18 @@ BluetoothOppManager::ClientDataHandler(U
AfterOppConnected();
// Keep remote information
mRemoteObexVersion = aMessage->mData[3];
mRemoteConnectionFlags = aMessage->mData[4];
mRemoteMaxPacketLength =
(((int)(aMessage->mData[5]) << 8) | aMessage->mData[6]);
- /*
- * Before sending content, we have to send a header including
- * information such as file name, file length and content type.
- */
- if (ExtractBlobHeaders()) {
- sInstance->SendPutHeaderRequest(sFileName, sFileLength);
- }
+ sInstance->SendPutHeaderRequest(sFileName, sFileLength);
} else if (mLastCommand == ObexRequestCode::Put) {
-
- // Send PutFinal packet when we get response
if (sWaitingToSendPutFinal) {
SendPutFinalRequest();
return;
}
if (mAbortFlag) {
SendAbortRequest();
return;
@@ -992,16 +1023,18 @@ BluetoothOppManager::SendConnectRequest(
memcpy(s->mData, req, s->mSize);
mSocket->SendSocketData(s);
}
void
BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
int aFileSize)
{
+ if (!mConnected) return;
+
uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
int len = aFileName.Length();
uint8_t* fileName = new uint8_t[(len + 1) * 2];
const PRUnichar* fileNamePtr = aFileName.BeginReading();
for (int i = 0; i < len; i++) {
fileName[i * 2] = (uint8_t)(fileNamePtr[i] >> 8);
@@ -1081,32 +1114,36 @@ BluetoothOppManager::SendPutFinalRequest
sWaitingToSendPutFinal = false;
delete [] req;
}
void
BluetoothOppManager::SendDisconnectRequest()
{
+ if (!mConnected) return;
+
// Section 3.3.2 "Disconnect", IrOBEX 1.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);
mSocket->SendSocketData(s);
}
void
BluetoothOppManager::SendAbortRequest()
{
+ if (!mConnected) return;
+
// Section 3.3.5 "Abort", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
SetObexPacketInfo(req, ObexRequestCode::Abort, index);
mLastCommand = ObexRequestCode::Abort;
@@ -1120,17 +1157,16 @@ BluetoothOppManager::IsTransferring()
{
return (mConnected && !mSendTransferCompleteFlag);
}
void
BluetoothOppManager::ReplyToConnect()
{
if (mConnected) return;
- mConnected = true;
// 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
@@ -1144,17 +1180,16 @@ BluetoothOppManager::ReplyToConnect()
memcpy(s->mData, req, s->mSize);
mSocket->SendSocketData(s);
}
void
BluetoothOppManager::ReplyToDisconnect()
{
if (!mConnected) return;
- mConnected = false;
// Section 3.3.2 "Disconnect", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
SetObexPacketInfo(req, ObexResponseCode::Success, index);
--- a/dom/bluetooth/BluetoothOppManager.h
+++ b/dom/bluetooth/BluetoothOppManager.h
@@ -4,19 +4,20 @@
* 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_bluetoothoppmanager_h__
#define mozilla_dom_bluetooth_bluetoothoppmanager_h__
#include "BluetoothCommon.h"
#include "BluetoothSocketObserver.h"
+#include "DeviceStorage.h"
#include "mozilla/dom/ipc/Blob.h"
#include "mozilla/ipc/UnixSocket.h"
-#include "DeviceStorage.h"
+#include "nsCOMArray.h"
class nsIOutputStream;
class nsIInputStream;
BEGIN_BLUETOOTH_NAMESPACE
class BluetoothReplyRunnable;
class BluetoothSocket;
@@ -49,17 +50,17 @@ public:
* either call Disconnect() to close RFCOMM connection or start another
* file-sending thread via calling SendFile() again.
*/
bool Connect(const nsAString& aDeviceObjectPath,
BluetoothReplyRunnable* aRunnable);
void Disconnect();
bool Listen();
- bool SendFile(BlobParent* aBlob);
+ bool SendFile(const nsAString& aDeviceAddress, BlobParent* aBlob);
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();
@@ -83,30 +84,33 @@ public:
virtual void OnDisconnect(BluetoothSocket* aSocket) MOZ_OVERRIDE;
void OnConnectSuccess() MOZ_OVERRIDE;
void OnConnectError() MOZ_OVERRIDE;
void OnDisconnect() MOZ_OVERRIDE;
private:
BluetoothOppManager();
void StartFileTransfer();
+ void StartSendingNextFile();
void FileTransferComplete();
void UpdateProgress();
void ReceivingFileConfirmation();
bool CreateFile();
bool WriteToFile(const uint8_t* aData, int aDataLength);
void DeleteReceivedFile();
void ReplyToConnect();
void ReplyToDisconnect();
void ReplyToPut(bool aFinal, bool aContinue);
void AfterOppConnected();
void AfterFirstPut();
void AfterOppDisconnected();
void ValidateFileName();
bool IsReservedChar(PRUnichar c);
+ void ClearQueue();
+ void RetrieveSentFileName();
/**
* OBEX session status.
* Set when OBEX session is established.
*/
bool mConnected;
nsString mConnectedDeviceAddress;
@@ -165,17 +169,19 @@ private:
* Set when receiving the first PUT packet and wait for
* ConfirmReceivingFile() to be called.
*/
bool mWaitingForConfirmationFlag;
nsAutoArrayPtr<uint8_t> mBodySegment;
nsAutoArrayPtr<uint8_t> mReceivedDataBuffer;
+ int mCurrentBlobIndex;
nsCOMPtr<nsIDOMBlob> mBlob;
+ nsCOMArray<nsIDOMBlob> mBlobs;
/**
* 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;