зеркало из https://github.com/mozilla/gecko-dev.git
326 строки
7.0 KiB
C++
326 строки
7.0 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 "BluetoothSocketMessageWatcher.h"
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <sys/socket.h>
|
|
#include "BluetoothInterface.h"
|
|
#include "nsClassHashtable.h"
|
|
|
|
BEGIN_BLUETOOTH_NAMESPACE
|
|
|
|
//
|
|
// SocketMessageWatcherWrapper
|
|
//
|
|
|
|
/* |SocketMessageWatcherWrapper| wraps SocketMessageWatcher to keep it from
|
|
* being released by hash table's Remove() method.
|
|
*/
|
|
class SocketMessageWatcherWrapper
|
|
{
|
|
public:
|
|
SocketMessageWatcherWrapper(SocketMessageWatcher* aSocketMessageWatcher)
|
|
: mSocketMessageWatcher(aSocketMessageWatcher)
|
|
{
|
|
MOZ_ASSERT(mSocketMessageWatcher);
|
|
}
|
|
|
|
SocketMessageWatcher* GetSocketMessageWatcher()
|
|
{
|
|
return mSocketMessageWatcher;
|
|
}
|
|
|
|
private:
|
|
SocketMessageWatcher* mSocketMessageWatcher;
|
|
};
|
|
|
|
/* |sWatcherHashTable| maps result handlers to corresponding watchers */
|
|
static nsClassHashtable<nsRefPtrHashKey<BluetoothSocketResultHandler>,
|
|
SocketMessageWatcherWrapper>
|
|
sWatcherHashtable;
|
|
|
|
//
|
|
// SocketMessageWatcher
|
|
//
|
|
|
|
SocketMessageWatcher::SocketMessageWatcher(
|
|
int aFd, BluetoothSocketResultHandler* aRes)
|
|
: mFd(aFd)
|
|
, mClientFd(-1)
|
|
, mLen(0)
|
|
, mRes(aRes)
|
|
{
|
|
MOZ_ASSERT(mRes);
|
|
}
|
|
|
|
SocketMessageWatcher::~SocketMessageWatcher()
|
|
{ }
|
|
|
|
void
|
|
SocketMessageWatcher::OnFileCanReadWithoutBlocking(int aFd)
|
|
{
|
|
BluetoothStatus status;
|
|
|
|
switch (mLen) {
|
|
case 0:
|
|
status = RecvMsg1();
|
|
break;
|
|
case MSG1_SIZE:
|
|
status = RecvMsg2();
|
|
break;
|
|
default:
|
|
/* message-size error */
|
|
status = STATUS_FAIL;
|
|
break;
|
|
}
|
|
|
|
if (IsComplete() || status != STATUS_SUCCESS) {
|
|
StopWatching();
|
|
Proceed(status);
|
|
}
|
|
}
|
|
|
|
void
|
|
SocketMessageWatcher::OnFileCanWriteWithoutBlocking(int aFd)
|
|
{ }
|
|
|
|
void
|
|
SocketMessageWatcher::Watch()
|
|
{
|
|
// add this watcher and its result handler to hash table
|
|
sWatcherHashtable.Put(mRes, new SocketMessageWatcherWrapper(this));
|
|
|
|
MessageLoopForIO::current()->WatchFileDescriptor(
|
|
mFd,
|
|
true,
|
|
MessageLoopForIO::WATCH_READ,
|
|
&mWatcher,
|
|
this);
|
|
}
|
|
|
|
void
|
|
SocketMessageWatcher::StopWatching()
|
|
{
|
|
mWatcher.StopWatchingFileDescriptor();
|
|
|
|
// remove this watcher and its result handler from hash table
|
|
sWatcherHashtable.Remove(mRes);
|
|
}
|
|
|
|
bool
|
|
SocketMessageWatcher::IsComplete() const
|
|
{
|
|
return mLen == (MSG1_SIZE + MSG2_SIZE);
|
|
}
|
|
|
|
int
|
|
SocketMessageWatcher::GetFd() const
|
|
{
|
|
return mFd;
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::GetChannel1() const
|
|
{
|
|
return ReadInt32(OFF_CHANNEL1);
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::GetSize() const
|
|
{
|
|
return ReadInt16(OFF_SIZE);
|
|
}
|
|
|
|
nsString
|
|
SocketMessageWatcher::GetBdAddress() const
|
|
{
|
|
nsString bdAddress;
|
|
ReadBdAddress(OFF_BDADDRESS, bdAddress);
|
|
return bdAddress;
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::GetChannel2() const
|
|
{
|
|
return ReadInt32(OFF_CHANNEL2);
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::GetConnectionStatus() const
|
|
{
|
|
return ReadInt32(OFF_STATUS);
|
|
}
|
|
|
|
int
|
|
SocketMessageWatcher::GetClientFd() const
|
|
{
|
|
return mClientFd;
|
|
}
|
|
|
|
BluetoothSocketResultHandler*
|
|
SocketMessageWatcher::GetResultHandler() const
|
|
{
|
|
return mRes;
|
|
}
|
|
|
|
BluetoothStatus
|
|
SocketMessageWatcher::RecvMsg1()
|
|
{
|
|
struct iovec iv;
|
|
memset(&iv, 0, sizeof(iv));
|
|
iv.iov_base = mBuf;
|
|
iv.iov_len = MSG1_SIZE;
|
|
|
|
struct msghdr msg;
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_iov = &iv;
|
|
msg.msg_iovlen = 1;
|
|
|
|
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
|
|
if (res <= 0) {
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
mLen += res;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
#define CMSGHDR_CONTAINS_FD(_cmsghdr) \
|
|
( ((_cmsghdr)->cmsg_level == SOL_SOCKET) && \
|
|
((_cmsghdr)->cmsg_type == SCM_RIGHTS) )
|
|
|
|
BluetoothStatus
|
|
SocketMessageWatcher::RecvMsg2()
|
|
{
|
|
struct iovec iv;
|
|
memset(&iv, 0, sizeof(iv));
|
|
iv.iov_base = mBuf + MSG1_SIZE;
|
|
iv.iov_len = MSG2_SIZE;
|
|
|
|
struct msghdr msg;
|
|
struct cmsghdr cmsgbuf[CMSG_SPACE(sizeof(int))];
|
|
memset(&msg, 0, sizeof(msg));
|
|
msg.msg_iov = &iv;
|
|
msg.msg_iovlen = 1;
|
|
msg.msg_control = cmsgbuf;
|
|
msg.msg_controllen = sizeof(cmsgbuf);
|
|
|
|
ssize_t res = TEMP_FAILURE_RETRY(recvmsg(mFd, &msg, MSG_NOSIGNAL));
|
|
if (res <= 0) {
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
mLen += res;
|
|
|
|
if (msg.msg_flags & (MSG_CTRUNC | MSG_OOB | MSG_ERRQUEUE)) {
|
|
return STATUS_FAIL;
|
|
}
|
|
|
|
struct cmsghdr *cmsgptr = CMSG_FIRSTHDR(&msg);
|
|
|
|
// Extract client fd from message header
|
|
for (; cmsgptr; cmsgptr = CMSG_NXTHDR(&msg, cmsgptr)) {
|
|
if (CMSGHDR_CONTAINS_FD(cmsgptr)) {
|
|
// if multiple file descriptors have been sent, we close
|
|
// all but the final one.
|
|
if (mClientFd != -1) {
|
|
TEMP_FAILURE_RETRY(close(mClientFd));
|
|
}
|
|
// retrieve sent client fd
|
|
memcpy(&mClientFd, CMSG_DATA(cmsgptr), sizeof(mClientFd));
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
int16_t
|
|
SocketMessageWatcher::ReadInt16(unsigned long aOffset) const
|
|
{
|
|
/* little-endian buffer */
|
|
return (static_cast<int16_t>(mBuf[aOffset + 1]) << 8) |
|
|
static_cast<int16_t>(mBuf[aOffset]);
|
|
}
|
|
|
|
int32_t
|
|
SocketMessageWatcher::ReadInt32(unsigned long aOffset) const
|
|
{
|
|
/* little-endian buffer */
|
|
return (static_cast<int32_t>(mBuf[aOffset + 3]) << 24) |
|
|
(static_cast<int32_t>(mBuf[aOffset + 2]) << 16) |
|
|
(static_cast<int32_t>(mBuf[aOffset + 1]) << 8) |
|
|
static_cast<int32_t>(mBuf[aOffset]);
|
|
}
|
|
|
|
void
|
|
SocketMessageWatcher::ReadBdAddress(unsigned long aOffset,
|
|
nsAString& aBdAddress) const
|
|
{
|
|
char str[BLUETOOTH_ADDRESS_LENGTH + 1];
|
|
|
|
int res = snprintf(str, sizeof(str), "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
static_cast<int>(mBuf[aOffset + 0]),
|
|
static_cast<int>(mBuf[aOffset + 1]),
|
|
static_cast<int>(mBuf[aOffset + 2]),
|
|
static_cast<int>(mBuf[aOffset + 3]),
|
|
static_cast<int>(mBuf[aOffset + 4]),
|
|
static_cast<int>(mBuf[aOffset + 5]));
|
|
if (res < 0) {
|
|
aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
|
|
} else if ((size_t)res >= sizeof(str)) { /* string buffer too small */
|
|
aBdAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
|
|
} else {
|
|
aBdAddress = NS_ConvertUTF8toUTF16(str);
|
|
}
|
|
}
|
|
|
|
//
|
|
// SocketMessageWatcherTask
|
|
//
|
|
|
|
SocketMessageWatcherTask::SocketMessageWatcherTask(
|
|
SocketMessageWatcher* aWatcher)
|
|
: mWatcher(aWatcher)
|
|
{
|
|
MOZ_ASSERT(mWatcher);
|
|
}
|
|
|
|
void
|
|
SocketMessageWatcherTask::Run()
|
|
{
|
|
mWatcher->Watch();
|
|
}
|
|
|
|
//
|
|
// DeleteSocketMessageWatcherTask
|
|
//
|
|
|
|
DeleteSocketMessageWatcherTask::DeleteSocketMessageWatcherTask(
|
|
BluetoothSocketResultHandler* aRes)
|
|
: mRes(aRes)
|
|
{
|
|
MOZ_ASSERT(mRes);
|
|
}
|
|
|
|
void
|
|
DeleteSocketMessageWatcherTask::Run()
|
|
{
|
|
// look up hash table for the watcher corresponding to |mRes|
|
|
SocketMessageWatcherWrapper* wrapper = sWatcherHashtable.Get(mRes);
|
|
if (!wrapper) {
|
|
return;
|
|
}
|
|
|
|
// stop the watcher if it exists
|
|
SocketMessageWatcher* watcher = wrapper->GetSocketMessageWatcher();
|
|
watcher->StopWatching();
|
|
watcher->Proceed(STATUS_DONE);
|
|
}
|
|
|
|
END_BLUETOOTH_NAMESPACE
|