Bug 699222 - Part 2: IPC RIL implementation. r=cjones
authorKyle Machulis <kmachulis@mozilla.com>
Wed, 07 Dec 2011 18:58:56 +0800
changeset 82162 c160a02d2d2a87266944c390fc70f2504e1a612d
parent 82161 e487df6617edf52b63cb50376fb44ca6b3063b47
child 82163 4f9095c87c188adc4127229f6344445a06cdd887
push id21584
push usermbrubeck@mozilla.com
push dateWed, 07 Dec 2011 20:15:44 +0000
treeherdermozilla-central@7ab478082ca7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs699222
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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