mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
7676cacec8
From 01ff8209cc5242bdf6c89432899ab4658e71e58a Mon Sep 17 00:00:00 2001
362 lines
9.6 KiB
C++
362 lines
9.6 KiB
C++
/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
/* 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 "BluetoothReplyRunnable.h"
|
|
#include "BluetoothService.h"
|
|
#include "BluetoothServiceUuid.h"
|
|
#include "ObexBase.h"
|
|
|
|
#include "mozilla/RefPtr.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
|
|
BluetoothOppManager*
|
|
BluetoothOppManager::Get()
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
if (sInstance == nullptr) {
|
|
sInstance = new BluetoothOppManager();
|
|
}
|
|
|
|
return sInstance;
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::Connect(const nsAString& aDeviceObjectPath,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
BluetoothService* bs = BluetoothService::Get();
|
|
if (!bs) {
|
|
NS_WARNING("BluetoothService not available!");
|
|
return false;
|
|
}
|
|
|
|
nsString serviceUuidStr =
|
|
NS_ConvertUTF8toUTF16(mozilla::dom::bluetooth::BluetoothServiceUuidStr::ObjectPush);
|
|
|
|
nsRefPtr<BluetoothReplyRunnable> runnable = aRunnable;
|
|
|
|
nsresult rv = bs->GetSocketViaService(aDeviceObjectPath,
|
|
serviceUuidStr,
|
|
BluetoothSocketType::RFCOMM,
|
|
true,
|
|
false,
|
|
this,
|
|
runnable);
|
|
|
|
runnable.forget();
|
|
return NS_FAILED(rv) ? false : true;
|
|
}
|
|
|
|
void
|
|
BluetoothOppManager::Disconnect()
|
|
{
|
|
CloseSocket();
|
|
}
|
|
|
|
bool
|
|
BluetoothOppManager::SendFile(BlobParent* aActor,
|
|
BluetoothReplyRunnable* aRunnable)
|
|
{
|
|
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) {
|
|
// 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::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);
|
|
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);
|
|
}
|