Bug 699222 - Part 2: IPC RIL implementation. r=cjones
authorKyle Machulis <kmachulis@mozilla.com>
Wed, 07 Dec 2011 18:58:56 +0800
changeset 82961 c160a02d2d2a87266944c390fc70f2504e1a612d
parent 82960 e487df6617edf52b63cb50376fb44ca6b3063b47
child 82962 4f9095c87c188adc4127229f6344445a06cdd887
push idunknown
push userunknown
push dateunknown
reviewerscjones
bugs699222
milestone11.0a1
Bug 699222 - Part 2: IPC RIL implementation. r=cjones
dom/system/b2g/RadioManager.cpp
ipc/Makefile.in
ipc/ril/Makefile.in
ipc/ril/Ril.cpp
ipc/ril/Ril.h
toolkit/library/Makefile.in
--- a/dom/system/b2g/RadioManager.cpp
+++ b/dom/system/b2g/RadioManager.cpp
@@ -83,17 +83,17 @@ PostToRIL(JSContext *cx, uintN argc, jsv
 
   if (argc != 1) {
     JS_ReportError(cx, "Expecting a single argument with the RIL message");
     return false;
   }
 
   jsval v = JS_ARGV(cx, vp)[0];
 
-  nsAutoPtr<RilMessage> rm(new RilMessage());
+  nsAutoPtr<RilRawData> rm(new RilRawData());
   JSAutoByteString abs;
   void *data;
   size_t size;
   if (JSVAL_IS_STRING(v)) {
     JSString *str = JSVAL_TO_STRING(v);
     if (!abs.encode(cx, str)) {
       return false;
     }
@@ -118,26 +118,26 @@ PostToRIL(JSContext *cx, uintN argc, jsv
     size = JS_GetTypedArrayByteLength(obj);
     data = JS_GetTypedArrayData(obj);
   } else {
     JS_ReportError(cx,
                    "Incorrect argument. Expecting a string or a typed array");
     return false;
   }
 
-  if (size > RilMessage::DATA_SIZE) {
+  if (size > RilRawData::MAX_DATA_SIZE) {
     JS_ReportError(cx, "Passed-in data is too large");
     return false;
   }
 
   rm->mSize = size;
   memcpy(rm->mData, data, size);
 
-  RilMessage *tosend = rm.forget();
-  JS_ALWAYS_TRUE(SendRilMessage(&tosend));
+  RilRawData *tosend = rm.forget();
+  JS_ALWAYS_TRUE(SendRilRawData(&tosend));
   return true;
 }
 
 bool
 ConnectWorkerToRIL::RunTask(JSContext *aCx)
 {
   // Set up the postRILMessage on the function for worker -> RIL thread
   // communication.
@@ -147,32 +147,32 @@ ConnectWorkerToRIL::RunTask(JSContext *a
 
   return JS_DefineFunction(aCx, workerGlobal, "postRILMessage", PostToRIL, 1, 0);
 }
 
 class RILReceiver : public RilConsumer
 {
   class DispatchRILEvent : public WorkerTask {
   public:
-    DispatchRILEvent(RilMessage *aMessage)
+    DispatchRILEvent(RilRawData *aMessage)
       : mMessage(aMessage)
     { }
 
     virtual bool RunTask(JSContext *aCx);
 
   private:
-    nsAutoPtr<RilMessage> mMessage;
+    nsAutoPtr<RilRawData> mMessage;
   };
 
 public:
   RILReceiver(WorkerCrossThreadDispatcher *aDispatcher)
     : mDispatcher(aDispatcher)
   { }
 
-  virtual void MessageReceived(RilMessage *aMessage) {
+  virtual void MessageReceived(RilRawData *aMessage) {
     nsRefPtr<DispatchRILEvent> dre(new DispatchRILEvent(aMessage));
     mDispatcher->PostTask(dre);
   }
 
 private:
   nsRefPtr<WorkerCrossThreadDispatcher> mDispatcher;
 };
 
--- a/ipc/Makefile.in
+++ b/ipc/Makefile.in
@@ -38,11 +38,15 @@ DEPTH = ..
 topsrcdir = @top_srcdir@
 srcdir = @srcdir@
 VPATH = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS += chromium glue ipdl testshell
 
+ifdef MOZ_B2G_RIL #{
+DIRS += ril
+endif #}
+
 TOOL_DIRS = app
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/ipc/ril/Makefile.in
@@ -0,0 +1,65 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla IPC.
+#
+# The Initial Developer of the Original Code is
+#   The Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2009
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Chris Jones <jones.chris.g@gmail.com>
+#   Kyle Machulis <kyle@nonpolynomial.com>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE = ipc
+LIBRARY_NAME = mozril_s
+FORCE_STATIC_LIB = 1
+LIBXUL_LIBRARY = 1
+EXPORT_LIBRARY = 1
+
+EXPORTS_NAMESPACES = mozilla/ipc
+
+EXPORTS_mozilla/ipc = \
+  Ril.h \
+  $(NULL)
+
+CPPSRCS += \
+  Ril.cpp \
+  $(NULL)
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/ipc/ril/Ril.cpp
@@ -0,0 +1,359 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et ft=cpp: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>
+ *   Kyle Machulis <kyle@nonpolynomial.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#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"
+
+#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
+
+using namespace base;
+using namespace std;
+
+namespace mozilla {
+namespace ipc {
+
+struct RilClient : public RefCounted<RilClient>,
+                   public MessageLoopForIO::Watcher
+
+{
+    typedef queue<RilRawData*> RilRawDataQueue;
+
+    RilClient() : mSocket(-1)
+                , mMutex("RilClient.mMutex")
+                , mBlockedOnWrite(false)
+                , mCurrentRilRawData(NULL)
+    { }
+    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.
+//
+
+class RilWriteTask : public Task {
+    virtual void Run();
+};
+
+void RilWriteTask::Run() {
+    sClient->OnFileCanWriteWithoutBlocking(sClient->mSocket.mFd);
+}
+
+static void
+ConnectToRil(Monitor* aMonitor, bool* aSuccess)
+{
+    MOZ_ASSERT(!sClient);
+
+    sClient = new RilClient();
+    if (!(*aSuccess = sClient->OpenSocket())) {
+        sClient = nsnull;
+    }
+
+    {
+        MonitorAutoLock lock(*aMonitor);
+        lock.Notify();
+    }
+    // aMonitor may have gone out of scope by now, don't touch it
+}
+
+bool
+RilClient::OpenSocket()
+{
+#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));
+    strcpy(addr.sun_path, "/dev/socket/rilb2g");
+    addr.sun_family = AF_LOCAL;
+    mSocket.mFd = socket(AF_LOCAL, SOCK_STREAM, 0);
+    alen = strlen("/dev/socket/rilb2g") + offsetof(struct sockaddr_un, sun_path) + 1;
+#else
+    struct hostent *hp;
+    struct sockaddr_in addr;
+    socklen_t alen;
+
+    hp = gethostbyname("localhost");
+    if (hp == 0) return -1;
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = hp->h_addrtype;
+    addr.sin_port = htons(6200);
+    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
+    mSocket.mFd = socket(hp->h_addrtype, SOCK_STREAM, 0);
+    alen = sizeof(addr);
+#endif
+
+    if (mSocket.mFd < 0) {
+        LOG("Cannot create socket for RIL!\n");
+        return -1;
+    }
+
+    if (connect(mSocket.mFd, (struct sockaddr *) &addr, alen) < 0) {
+        LOG("Cannot open socket for RIL!\n");
+        close(mSocket.mFd);
+        return false;
+    }
+    LOG("Socket open for RIL\n");
+
+    // Set close-on-exec bit.
+    int flags = fcntl(mSocket.mFd, F_GETFD);
+    if (-1 == flags) {
+        return false;
+    }
+
+    flags |= FD_CLOEXEC;
+    if (-1 == fcntl(mSocket.mFd, F_SETFD, flags)) {
+        return false;
+    }
+
+    // Select non-blocking IO.
+    if (-1 == fcntl(mSocket.mFd, F_SETFL, O_NONBLOCK)) {
+        return false;
+    }
+    mIOLoop = MessageLoopForIO::current();
+    if (!mIOLoop->WatchFileDescriptor(mSocket.mFd,
+                                      true,
+                                      MessageLoopForIO::WATCH_READ,
+                                      &mReadWatcher,
+                                      this)) {
+        return false;
+    }
+    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;
+
+    MOZ_ASSERT(fd == mSocket.mFd);
+    while (true) {
+        if (!mIncoming) {
+            mIncoming = new RilRawData();
+            int ret = read(fd, mIncoming->mData, 1024);
+            if (ret <= 0) {
+                LOG("Cannot read from network, error %d\n", ret);
+                return;
+            }
+            mIncoming->mSize = ret;
+            sConsumer->MessageReceived(mIncoming.forget());
+            if (ret < 1024) {
+                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.
+    //
+
+    MOZ_ASSERT(fd == mSocket.mFd);
+
+    while (!mOutgoingQ.empty() || mCurrentRilRawData != NULL) {
+        if(!mCurrentRilRawData) {
+            mCurrentRilRawData = mOutgoingQ.front();
+            mOutgoingQ.pop();
+            mCurrentWriteOffset = 0;
+        }
+        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)
+{
+    // XXX This might "strand" messages in the outgoing queue.  We'll
+    // assume that's OK for now.
+    sClient = nsnull;
+    {
+        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;
+    *aMessage = nsnull;
+
+    {
+        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();
+    }
+
+    sConsumer = nsnull;
+}
+
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/ipc/ril/Ril.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et ft=cpp: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at:
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Code.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Chris Jones <jones.chris.g@gmail.com>
+ *   Kyle Machulis <kyle@nonpolynomial.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_ipc_Ril_h
+#define mozilla_ipc_Ril_h 1
+
+#include "mozilla/RefPtr.h"
+
+namespace base {
+class MessageLoop;
+}
+
+class nsIThread;
+
+namespace mozilla {
+namespace ipc {
+
+
+/*
+ * Represents raw data going to or coming from the RIL socket. Can
+ * actually contain multiple RIL parcels in the data block, and may
+ * also contain incomplete parcels on the front or back. Actual parcel
+ * construction is handled in the worker thread.
+ */
+struct RilRawData
+{
+    static const size_t MAX_DATA_SIZE = 1024;
+    uint8_t mData[MAX_DATA_SIZE];
+
+    // Number of octets in mData.
+    size_t mSize;
+};
+
+class RilConsumer : public RefCounted<RilConsumer>
+{
+public:
+    virtual ~RilConsumer() { }
+    virtual void MessageReceived(RilRawData* aMessage) { }
+};
+
+bool StartRil(RilConsumer* aConsumer);
+
+bool SendRilRawData(RilRawData** aMessage);
+
+void StopRil();
+
+} // namespace ipc
+} // namepsace mozilla
+
+#endif // mozilla_ipc_Ril_h
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -142,16 +142,20 @@ STATIC_LIBS += \
   mozipc_s \
   mozipdlgen_s \
   ipcshell_s \
   gfx2d \
   gfxipc_s \
   hal_s \
   $(NULL)
 
+ifdef MOZ_B2G_RIL #{
+STATIC_LIBS += mozril_s
+endif #}
+
 ifdef MOZ_IPDL_TESTS
 STATIC_LIBS += ipdlunittest_s
 endif
 
 ifeq (Linux,$(OS_ARCH))
 ifneq (Android,$(OS_TARGET))
 OS_LIBS += -lrt
 endif