Bug 817972 - Make Gecko Bluetooth capable of queueing file-sending requests, r=gyeh

This commit is contained in:
Eric Chou 2013-04-26 18:09:00 +08:00
parent d31b7f65ff
commit 90c597dd73
3 changed files with 137 additions and 96 deletions

View File

@ -230,6 +230,7 @@ BluetoothOppManager::BluetoothOppManager() : mConnected(false)
, mSendTransferCompleteFlag(false)
, mSuccessFlag(false)
, mWaitingForConfirmationFlag(false)
, mCurrentBlobIndex(-1)
{
mConnectedDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
Listen();
@ -275,10 +276,7 @@ BluetoothOppManager::Connect(const nsAString& aDeviceObjectPath,
}
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);
@ -354,69 +352,50 @@ BluetoothOppManager::Listen()
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);
/*
* 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();
mBlob = mBlobs[++mCurrentBlobIndex];
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);
}
}
}
SendConnectRequest();
mTransferMode = false;
// 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
// 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();
}
mTransferMode = false;
}
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;
}
mBlobs.AppendElement(aActor->GetBlob().get());
StartSendingNextFile();
return true;
}
@ -431,17 +410,13 @@ BluetoothOppManager::StopSendingFile()
bool
BluetoothOppManager::ConfirmReceivingFile(bool aConfirm)
{
if (!mConnected) return false;
NS_ENSURE_TRUE(mConnected, false);
NS_ENSURE_TRUE(mWaitingForConfirmationFlag, false);
MOZ_ASSERT(mPacketLeftLength == 0);
if (!mWaitingForConfirmationFlag) {
NS_WARNING("We are not waiting for a confirmation now.");
return false;
}
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) {
@ -490,9 +465,10 @@ BluetoothOppManager::AfterOppDisconnected()
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;
@ -537,11 +513,11 @@ BluetoothOppManager::DeleteReceivedFile()
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));
@ -600,17 +576,11 @@ BluetoothOppManager::CreateFile()
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;
}
@ -644,6 +614,8 @@ BluetoothOppManager::ExtractPacketHeaders(const ObexHeaderSet& aHeader)
bool
BluetoothOppManager::ExtractBlobHeaders()
{
RetrieveSentFileName();
nsresult rv = mBlob->GetType(sContentType);
if (NS_FAILED(rv)) {
NS_WARNING("Can't get content type");
@ -681,6 +653,52 @@ BluetoothOppManager::ExtractBlobHeaders()
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)
{
@ -845,6 +863,17 @@ BluetoothOppManager::ServerDataHandler(UnixSocketRawData* aMessage)
}
}
void
BluetoothOppManager::ClearQueue()
{
mCurrentBlobIndex = -1;
mBlob = nullptr;
while (!mBlobs.IsEmpty()) {
mBlobs.RemoveElement(mBlobs[0]);
}
}
void
BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
{
@ -884,7 +913,17 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
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();
@ -909,16 +948,8 @@ BluetoothOppManager::ClientDataHandler(UnixSocketRawData* aMessage)
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;
@ -997,6 +1028,8 @@ void
BluetoothOppManager::SendPutHeaderRequest(const nsAString& aFileName,
int aFileSize)
{
if (!mConnected) return;
uint8_t* req = new uint8_t[mRemoteMaxPacketLength];
int len = aFileName.Length();
@ -1086,6 +1119,8 @@ BluetoothOppManager::SendPutFinalRequest()
void
BluetoothOppManager::SendDisconnectRequest()
{
if (!mConnected) return;
// Section 3.3.2 "Disconnect", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
@ -1102,6 +1137,8 @@ BluetoothOppManager::SendDisconnectRequest()
void
BluetoothOppManager::SendAbortRequest()
{
if (!mConnected) return;
// Section 3.3.5 "Abort", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
@ -1125,7 +1162,6 @@ 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]
@ -1149,7 +1185,6 @@ void
BluetoothOppManager::ReplyToDisconnect()
{
if (!mConnected) return;
mConnected = false;
// Section 3.3.2 "Disconnect", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]

View File

@ -9,9 +9,10 @@
#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;
@ -54,7 +55,7 @@ public:
void Disconnect();
bool Listen();
bool SendFile(BlobParent* aBlob);
bool SendFile(const nsAString& aDeviceAddress, BlobParent* aBlob);
bool StopSendingFile();
bool ConfirmReceivingFile(bool aConfirm);
@ -88,6 +89,7 @@ public:
private:
BluetoothOppManager();
void StartFileTransfer();
void StartSendingNextFile();
void FileTransferComplete();
void UpdateProgress();
void ReceivingFileConfirmation();
@ -102,6 +104,8 @@ private:
void AfterOppDisconnected();
void ValidateFileName();
bool IsReservedChar(PRUnichar c);
void ClearQueue();
void RetrieveSentFileName();
/**
* OBEX session status.
@ -170,7 +174,9 @@ private:
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

View File

@ -2719,7 +2719,7 @@ BluetoothDBusService::SendFile(const nsAString& aDeviceAddress,
BluetoothValue v = true;
nsAutoString errorStr;
if (!opp->SendFile(aBlobParent)) {
if (!opp->SendFile(aDeviceAddress, aBlobParent)) {
errorStr.AssignLiteral("Calling SendFile() failed");
}