2011-12-07 02:58:56 -08:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/* vim: set sw=4 ts=8 et ft=cpp: */
|
2012-05-21 04:12:37 -07:00
|
|
|
/* 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/. */
|
2011-12-07 02:58:56 -08:00
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include <queue>
|
|
|
|
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/un.h>
|
|
|
|
#include <sys/select.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include "base/eintr_wrapper.h"
|
|
|
|
#include "base/message_loop.h"
|
|
|
|
#include "mozilla/FileUtils.h"
|
|
|
|
#include "mozilla/Monitor.h"
|
|
|
|
#include "mozilla/Util.h"
|
|
|
|
#include "nsAutoPtr.h"
|
|
|
|
#include "nsIThread.h"
|
|
|
|
#include "nsXULAppAPI.h"
|
|
|
|
#include "Ril.h"
|
|
|
|
|
2012-04-01 01:57:21 -07:00
|
|
|
#undef LOG
|
2011-12-07 02:58:56 -08:00
|
|
|
#if defined(MOZ_WIDGET_GONK)
|
|
|
|
#include <android/log.h>
|
|
|
|
#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
|
|
|
|
#else
|
|
|
|
#define LOG(args...) printf(args);
|
|
|
|
#endif
|
|
|
|
|
2011-12-12 12:39:17 -08:00
|
|
|
#define RIL_SOCKET_NAME "/dev/socket/rilproxy"
|
|
|
|
|
2011-12-07 02:58:56 -08:00
|
|
|
using namespace base;
|
|
|
|
using namespace std;
|
|
|
|
|
2011-12-08 23:12:32 -08:00
|
|
|
// Network port to connect to for adb forwarded sockets when doing
|
|
|
|
// desktop development.
|
|
|
|
const uint32_t RIL_TEST_PORT = 6200;
|
|
|
|
|
2011-12-07 02:58:56 -08:00
|
|
|
namespace mozilla {
|
|
|
|
namespace ipc {
|
|
|
|
|
|
|
|
struct RilClient : public RefCounted<RilClient>,
|
|
|
|
public MessageLoopForIO::Watcher
|
|
|
|
|
|
|
|
{
|
|
|
|
typedef queue<RilRawData*> RilRawDataQueue;
|
|
|
|
|
|
|
|
RilClient() : mSocket(-1)
|
|
|
|
, mMutex("RilClient.mMutex")
|
|
|
|
, mBlockedOnWrite(false)
|
2011-12-08 23:12:32 -08:00
|
|
|
, mIOLoop(MessageLoopForIO::current())
|
2012-04-01 01:57:21 -07:00
|
|
|
, mCurrentRilRawData(NULL)
|
2011-12-07 02:58:56 -08:00
|
|
|
{ }
|
|
|
|
virtual ~RilClient() { }
|
|
|
|
|
|
|
|
bool OpenSocket();
|
|
|
|
|
|
|
|
virtual void OnFileCanReadWithoutBlocking(int fd);
|
|
|
|
virtual void OnFileCanWriteWithoutBlocking(int fd);
|
|
|
|
|
|
|
|
ScopedClose mSocket;
|
|
|
|
MessageLoopForIO::FileDescriptorWatcher mReadWatcher;
|
|
|
|
MessageLoopForIO::FileDescriptorWatcher mWriteWatcher;
|
|
|
|
nsAutoPtr<RilRawData> mIncoming;
|
|
|
|
Mutex mMutex;
|
|
|
|
RilRawDataQueue mOutgoingQ;
|
|
|
|
bool mBlockedOnWrite;
|
|
|
|
MessageLoopForIO* mIOLoop;
|
|
|
|
nsAutoPtr<RilRawData> mCurrentRilRawData;
|
|
|
|
size_t mCurrentWriteOffset;
|
|
|
|
};
|
|
|
|
|
|
|
|
static RefPtr<RilClient> sClient;
|
|
|
|
static RefPtr<RilConsumer> sConsumer;
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// This code runs on the IO thread.
|
|
|
|
//
|
|
|
|
|
2012-04-01 01:57:21 -07:00
|
|
|
class RilReconnectTask : public CancelableTask {
|
|
|
|
RilReconnectTask() : mCanceled(false) { }
|
|
|
|
|
2011-12-08 23:12:32 -08:00
|
|
|
virtual void Run();
|
2012-04-01 01:57:21 -07:00
|
|
|
virtual void Cancel() { mCanceled = true; }
|
|
|
|
|
|
|
|
bool mCanceled;
|
|
|
|
|
|
|
|
public:
|
|
|
|
static void Enqueue(int aDelayMs = 0) {
|
|
|
|
MessageLoopForIO* ioLoop = MessageLoopForIO::current();
|
2012-09-30 22:03:43 -07:00
|
|
|
if (!ioLoop) {
|
|
|
|
NS_WARNING("No IOLoop to attach to, cancelling self!");
|
|
|
|
CancelIt();
|
|
|
|
return;
|
|
|
|
}
|
2012-04-01 01:57:21 -07:00
|
|
|
if (sTask) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sTask = new RilReconnectTask();
|
|
|
|
if (aDelayMs) {
|
|
|
|
ioLoop->PostDelayedTask(FROM_HERE, sTask, aDelayMs);
|
|
|
|
} else {
|
|
|
|
ioLoop->PostTask(FROM_HERE, sTask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void CancelIt() {
|
|
|
|
if (!sTask) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sTask->Cancel();
|
2012-07-30 07:20:58 -07:00
|
|
|
sTask = nullptr;
|
2012-04-01 01:57:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Can *ONLY* be touched by the IO thread. The event queue owns
|
|
|
|
// this memory when pointer is nonnull; do *NOT* free it manually.
|
|
|
|
static CancelableTask* sTask;
|
2011-12-08 23:12:32 -08:00
|
|
|
};
|
2012-04-01 01:57:21 -07:00
|
|
|
CancelableTask* RilReconnectTask::sTask;
|
2011-12-08 23:12:32 -08:00
|
|
|
|
|
|
|
void RilReconnectTask::Run() {
|
2012-04-01 01:57:21 -07:00
|
|
|
// NB: the order of these two statements is important! sTask must
|
|
|
|
// always run, whether we've been canceled or not, to avoid
|
|
|
|
// leading a dangling pointer in sTask.
|
2012-07-30 07:20:58 -07:00
|
|
|
sTask = nullptr;
|
2012-04-01 01:57:21 -07:00
|
|
|
if (mCanceled) {
|
2012-09-25 21:07:39 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sClient->OpenSocket()) {
|
|
|
|
return;
|
2011-12-08 23:12:32 -08:00
|
|
|
}
|
2012-04-01 01:57:21 -07:00
|
|
|
Enqueue(1000);
|
2011-12-08 23:12:32 -08:00
|
|
|
}
|
|
|
|
|
2012-09-25 21:07:39 -07:00
|
|
|
class RilWriteTask : public Task {
|
2011-12-07 02:58:56 -08:00
|
|
|
virtual void Run();
|
|
|
|
};
|
|
|
|
|
2012-09-25 21:07:39 -07:00
|
|
|
void RilWriteTask::Run() {
|
2012-09-30 22:03:43 -07:00
|
|
|
if(sClient->mSocket.get() < 0) {
|
|
|
|
NS_WARNING("Trying to write to non-open socket!");
|
|
|
|
return;
|
|
|
|
}
|
2012-04-12 03:21:24 -07:00
|
|
|
sClient->OnFileCanWriteWithoutBlocking(sClient->mSocket.rwget());
|
2011-12-07 02:58:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
ConnectToRil(Monitor* aMonitor, bool* aSuccess)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(!sClient);
|
|
|
|
|
|
|
|
sClient = new RilClient();
|
2012-04-01 01:57:21 -07:00
|
|
|
RilReconnectTask::Enqueue();
|
2011-12-08 23:12:32 -08:00
|
|
|
*aSuccess = true;
|
2011-12-07 02:58:56 -08:00
|
|
|
{
|
|
|
|
MonitorAutoLock lock(*aMonitor);
|
|
|
|
lock.Notify();
|
|
|
|
}
|
|
|
|
// aMonitor may have gone out of scope by now, don't touch it
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
RilClient::OpenSocket()
|
|
|
|
{
|
2012-09-30 22:03:43 -07:00
|
|
|
|
|
|
|
ScopedClose skt;
|
2011-12-07 02:58:56 -08:00
|
|
|
#if defined(MOZ_WIDGET_GONK)
|
|
|
|
// Using a network socket to test basic functionality
|
|
|
|
// before we see how this works on the phone.
|
|
|
|
struct sockaddr_un addr;
|
|
|
|
socklen_t alen;
|
|
|
|
size_t namelen;
|
|
|
|
int err;
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
2011-12-12 12:39:17 -08:00
|
|
|
strcpy(addr.sun_path, RIL_SOCKET_NAME);
|
2011-12-07 02:58:56 -08:00
|
|
|
addr.sun_family = AF_LOCAL;
|
2012-09-30 22:03:43 -07:00
|
|
|
skt.reset(socket(AF_LOCAL, SOCK_STREAM, 0));
|
2011-12-12 12:39:17 -08:00
|
|
|
alen = strlen(RIL_SOCKET_NAME) + offsetof(struct sockaddr_un, sun_path) + 1;
|
2011-12-07 02:58:56 -08:00
|
|
|
#else
|
|
|
|
struct hostent *hp;
|
|
|
|
struct sockaddr_in addr;
|
|
|
|
socklen_t alen;
|
|
|
|
|
|
|
|
hp = gethostbyname("localhost");
|
2011-12-08 23:12:32 -08:00
|
|
|
if (hp == 0) return false;
|
2011-12-07 02:58:56 -08:00
|
|
|
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
|
|
addr.sin_family = hp->h_addrtype;
|
2011-12-08 23:12:32 -08:00
|
|
|
addr.sin_port = htons(RIL_TEST_PORT);
|
2011-12-07 02:58:56 -08:00
|
|
|
memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
|
2012-09-30 22:03:43 -07:00
|
|
|
skt.reset(socket(hp->h_addrtype, SOCK_STREAM, 0));
|
2011-12-07 02:58:56 -08:00
|
|
|
alen = sizeof(addr);
|
|
|
|
#endif
|
|
|
|
|
2012-09-30 22:03:43 -07:00
|
|
|
if (skt.get() < 0) {
|
2011-12-07 02:58:56 -08:00
|
|
|
LOG("Cannot create socket for RIL!\n");
|
2011-12-08 23:12:32 -08:00
|
|
|
return false;
|
2011-12-07 02:58:56 -08:00
|
|
|
}
|
|
|
|
|
2012-09-30 22:03:43 -07:00
|
|
|
if (connect(skt.get(), (struct sockaddr *) &addr, alen) < 0) {
|
2012-04-11 10:05:35 -07:00
|
|
|
#if defined(MOZ_WIDGET_GONK)
|
2011-12-07 02:58:56 -08:00
|
|
|
LOG("Cannot open socket for RIL!\n");
|
2012-04-11 10:05:35 -07:00
|
|
|
#endif
|
2012-09-30 22:03:43 -07:00
|
|
|
skt.dispose();
|
2011-12-07 02:58:56 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set close-on-exec bit.
|
2012-09-30 22:03:43 -07:00
|
|
|
int flags = fcntl(skt.get(), F_GETFD);
|
2011-12-07 02:58:56 -08:00
|
|
|
if (-1 == flags) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags |= FD_CLOEXEC;
|
2012-09-30 22:03:43 -07:00
|
|
|
if (-1 == fcntl(skt.get(), F_SETFD, flags)) {
|
2011-12-07 02:58:56 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Select non-blocking IO.
|
2012-09-30 22:03:43 -07:00
|
|
|
if (-1 == fcntl(skt.get(), F_SETFL, O_NONBLOCK)) {
|
2011-12-07 02:58:56 -08:00
|
|
|
return false;
|
|
|
|
}
|
2012-09-30 22:03:43 -07:00
|
|
|
if (!mIOLoop->WatchFileDescriptor(skt.get(),
|
2011-12-07 02:58:56 -08:00
|
|
|
true,
|
|
|
|
MessageLoopForIO::WATCH_READ,
|
|
|
|
&mReadWatcher,
|
|
|
|
this)) {
|
|
|
|
return false;
|
|
|
|
}
|
2012-09-30 22:03:43 -07:00
|
|
|
mSocket = skt.forget();
|
2011-12-08 23:12:32 -08:00
|
|
|
LOG("Socket open for RIL\n");
|
2011-12-07 02:58:56 -08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RilClient::OnFileCanReadWithoutBlocking(int fd)
|
|
|
|
{
|
|
|
|
// Keep reading data until either
|
|
|
|
//
|
|
|
|
// - mIncoming is completely read
|
|
|
|
// If so, sConsumer->MessageReceived(mIncoming.forget())
|
|
|
|
//
|
|
|
|
// - mIncoming isn't completely read, but there's no more
|
|
|
|
// data available on the socket
|
|
|
|
// If so, break;
|
|
|
|
|
2012-04-12 03:21:24 -07:00
|
|
|
MOZ_ASSERT(fd == mSocket.get());
|
2011-12-07 02:58:56 -08:00
|
|
|
while (true) {
|
|
|
|
if (!mIncoming) {
|
|
|
|
mIncoming = new RilRawData();
|
2012-04-01 01:57:21 -07:00
|
|
|
ssize_t ret = read(fd, mIncoming->mData, RilRawData::MAX_DATA_SIZE);
|
2011-12-07 02:58:56 -08:00
|
|
|
if (ret <= 0) {
|
2012-04-06 19:58:31 -07:00
|
|
|
if (ret == -1) {
|
|
|
|
if (errno == EINTR) {
|
|
|
|
continue; // retry system call when interrupted
|
|
|
|
}
|
|
|
|
else if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
|
return; // no data available: return and re-poll
|
|
|
|
}
|
|
|
|
// else fall through to error handling on other errno's
|
|
|
|
}
|
2011-12-07 02:58:56 -08:00
|
|
|
LOG("Cannot read from network, error %d\n", ret);
|
2011-12-08 23:12:32 -08:00
|
|
|
// At this point, assume that we can't actually access
|
|
|
|
// the socket anymore, and start a reconnect loop.
|
|
|
|
mIncoming.forget();
|
|
|
|
mReadWatcher.StopWatchingFileDescriptor();
|
|
|
|
mWriteWatcher.StopWatchingFileDescriptor();
|
2012-09-30 22:03:43 -07:00
|
|
|
// ScopedClose will close our old socket on a reset.
|
|
|
|
// Setting to -1 means writes will fail with message.
|
|
|
|
mSocket.reset(-1);
|
2012-04-01 01:57:21 -07:00
|
|
|
RilReconnectTask::Enqueue();
|
2011-12-07 02:58:56 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
mIncoming->mSize = ret;
|
|
|
|
sConsumer->MessageReceived(mIncoming.forget());
|
2012-04-01 01:57:21 -07:00
|
|
|
if (ret < ssize_t(RilRawData::MAX_DATA_SIZE)) {
|
2011-12-07 02:58:56 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
RilClient::OnFileCanWriteWithoutBlocking(int fd)
|
|
|
|
{
|
|
|
|
// Try to write the bytes of mCurrentRilRawData. If all were written, continue.
|
|
|
|
//
|
|
|
|
// Otherwise, save the byte position of the next byte to write
|
|
|
|
// within mCurrentRilRawData, and request another write when the
|
|
|
|
// system won't block.
|
|
|
|
//
|
|
|
|
|
2012-04-12 03:21:24 -07:00
|
|
|
MOZ_ASSERT(fd == mSocket.get());
|
2011-12-07 02:58:56 -08:00
|
|
|
|
2012-05-19 08:29:45 -07:00
|
|
|
while (true) {
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(mMutex);
|
|
|
|
|
|
|
|
if (mOutgoingQ.empty() && !mCurrentRilRawData) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!mCurrentRilRawData) {
|
|
|
|
mCurrentRilRawData = mOutgoingQ.front();
|
|
|
|
mOutgoingQ.pop();
|
|
|
|
mCurrentWriteOffset = 0;
|
|
|
|
}
|
2011-12-07 02:58:56 -08:00
|
|
|
}
|
|
|
|
const uint8_t *toWrite;
|
|
|
|
|
|
|
|
toWrite = mCurrentRilRawData->mData;
|
|
|
|
|
|
|
|
while (mCurrentWriteOffset < mCurrentRilRawData->mSize) {
|
|
|
|
ssize_t write_amount = mCurrentRilRawData->mSize - mCurrentWriteOffset;
|
|
|
|
ssize_t written;
|
|
|
|
written = write (fd, toWrite + mCurrentWriteOffset,
|
|
|
|
write_amount);
|
|
|
|
if(written > 0) {
|
|
|
|
mCurrentWriteOffset += written;
|
|
|
|
}
|
|
|
|
if (written != write_amount) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(mCurrentWriteOffset != mCurrentRilRawData->mSize) {
|
|
|
|
MessageLoopForIO::current()->WatchFileDescriptor(
|
|
|
|
fd,
|
|
|
|
false,
|
|
|
|
MessageLoopForIO::WATCH_WRITE,
|
|
|
|
&mWriteWatcher,
|
|
|
|
this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mCurrentRilRawData = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
DisconnectFromRil(Monitor* aMonitor)
|
|
|
|
{
|
2012-04-01 01:57:21 -07:00
|
|
|
// Prevent stale reconnect tasks from being run after we've shut
|
|
|
|
// down.
|
|
|
|
RilReconnectTask::CancelIt();
|
2011-12-07 02:58:56 -08:00
|
|
|
// XXX This might "strand" messages in the outgoing queue. We'll
|
|
|
|
// assume that's OK for now.
|
2012-07-30 07:20:58 -07:00
|
|
|
sClient = nullptr;
|
2011-12-07 02:58:56 -08:00
|
|
|
{
|
|
|
|
MonitorAutoLock lock(*aMonitor);
|
|
|
|
lock.Notify();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// This code runs on any thread.
|
|
|
|
//
|
|
|
|
|
|
|
|
bool
|
|
|
|
StartRil(RilConsumer* aConsumer)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aConsumer);
|
|
|
|
sConsumer = aConsumer;
|
|
|
|
|
|
|
|
Monitor monitor("StartRil.monitor");
|
|
|
|
bool success;
|
|
|
|
{
|
|
|
|
MonitorAutoLock lock(monitor);
|
|
|
|
|
|
|
|
XRE_GetIOMessageLoop()->PostTask(
|
|
|
|
FROM_HERE,
|
|
|
|
NewRunnableFunction(ConnectToRil, &monitor, &success));
|
|
|
|
|
|
|
|
lock.Wait();
|
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
SendRilRawData(RilRawData** aMessage)
|
|
|
|
{
|
|
|
|
if (!sClient) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
RilRawData *msg = *aMessage;
|
2012-07-30 07:20:58 -07:00
|
|
|
*aMessage = nullptr;
|
2011-12-07 02:58:56 -08:00
|
|
|
|
|
|
|
{
|
|
|
|
MutexAutoLock lock(sClient->mMutex);
|
|
|
|
sClient->mOutgoingQ.push(msg);
|
|
|
|
}
|
|
|
|
sClient->mIOLoop->PostTask(FROM_HERE, new RilWriteTask());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StopRil()
|
|
|
|
{
|
|
|
|
Monitor monitor("StopRil.monitor");
|
|
|
|
{
|
|
|
|
MonitorAutoLock lock(monitor);
|
|
|
|
|
|
|
|
XRE_GetIOMessageLoop()->PostTask(
|
|
|
|
FROM_HERE,
|
|
|
|
NewRunnableFunction(DisconnectFromRil, &monitor));
|
|
|
|
|
|
|
|
lock.Wait();
|
|
|
|
}
|
|
|
|
|
2012-07-30 07:20:58 -07:00
|
|
|
sConsumer = nullptr;
|
2011-12-07 02:58:56 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace ipc
|
|
|
|
} // namespace mozilla
|