summaryrefslogtreecommitdiffstats
path: root/ipc/ril
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/ril')
-rw-r--r--ipc/ril/Ril.cpp458
-rw-r--r--ipc/ril/Ril.h54
-rw-r--r--ipc/ril/RilConnector.cpp221
-rw-r--r--ipc/ril/RilConnector.h63
-rw-r--r--ipc/ril/RilSocket.cpp446
-rw-r--r--ipc/ril/RilSocket.h110
-rw-r--r--ipc/ril/RilSocketConsumer.cpp20
-rw-r--r--ipc/ril/RilSocketConsumer.h64
-rw-r--r--ipc/ril/moz.build22
9 files changed, 1458 insertions, 0 deletions
diff --git a/ipc/ril/Ril.cpp b/ipc/ril/Ril.cpp
new file mode 100644
index 000000000..90fb12052
--- /dev/null
+++ b/ipc/ril/Ril.cpp
@@ -0,0 +1,458 @@
+/* -*- 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 "mozilla/ipc/Ril.h"
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "jsfriendapi.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/workers/Workers.h"
+#include "mozilla/ipc/RilSocket.h"
+#include "mozilla/ipc/RilSocketConsumer.h"
+#include "nsThreadUtils.h" // For NS_IsMainThread.
+#include "RilConnector.h"
+
+#ifdef CHROMIUM_LOG
+#undef CHROMIUM_LOG
+#endif
+
+#if defined(MOZ_WIDGET_GONK)
+#include <android/log.h>
+#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
+#else
+#define CHROMIUM_LOG(args...) printf(args);
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+USING_WORKERS_NAMESPACE;
+using namespace JS;
+
+class RilConsumer;
+
+static const char RIL_SOCKET_NAME[] = "/dev/socket/rilproxy";
+
+static nsTArray<UniquePtr<RilConsumer>> sRilConsumers;
+
+//
+// RilConsumer
+//
+
+class RilConsumer final : public RilSocketConsumer
+{
+public:
+ RilConsumer();
+
+ nsresult ConnectWorkerToRIL(JSContext* aCx);
+
+ nsresult Register(unsigned long aClientId,
+ WorkerCrossThreadDispatcher* aDispatcher);
+ void Unregister();
+
+ // Methods for |RilSocketConsumer|
+ //
+
+ void ReceiveSocketData(JSContext* aCx,
+ int aIndex,
+ UniquePtr<UnixSocketBuffer>& aBuffer) override;
+ void OnConnectSuccess(int aIndex) override;
+ void OnConnectError(int aIndex) override;
+ void OnDisconnect(int aIndex) override;
+
+protected:
+ static bool PostRILMessage(JSContext* aCx, unsigned aArgc, Value* aVp);
+
+ nsresult Send(JSContext* aCx, const CallArgs& aArgs);
+ nsresult Receive(JSContext* aCx,
+ uint32_t aClientId,
+ const UnixSocketBuffer* aBuffer);
+ void Close();
+
+private:
+ RefPtr<RilSocket> mSocket;
+ nsCString mAddress;
+ bool mShutdown;
+};
+
+RilConsumer::RilConsumer()
+ : mShutdown(false)
+{ }
+
+nsresult
+RilConsumer::ConnectWorkerToRIL(JSContext* aCx)
+{
+ // Set up the postRILMessage on the function for worker -> RIL thread
+ // communication.
+ Rooted<JSObject*> workerGlobal(aCx, CurrentGlobalOrNull(aCx));
+
+ // Check whether |postRILMessage| has been defined. No one but this class
+ // should ever define |postRILMessage| in a RIL worker.
+ Rooted<Value> val(aCx);
+ if (!JS_GetProperty(aCx, workerGlobal, "postRILMessage", &val)) {
+ // Just returning failure here will cause the exception on the JSContext to
+ // be reported as needed.
+ return NS_ERROR_FAILURE;
+ }
+
+ // Make sure that |postRILMessage| is a function.
+ if (JSTYPE_FUNCTION == JS_TypeOfValue(aCx, val)) {
+ return NS_OK;
+ }
+
+ JSFunction* postRILMessage = JS_DefineFunction(aCx, workerGlobal,
+ "postRILMessage",
+ PostRILMessage, 2, 0);
+ if (NS_WARN_IF(!postRILMessage)) {
+ // Just returning failure here will cause the exception on the JSContext to
+ // be reported as needed.
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+nsresult
+RilConsumer::Register(unsigned long aClientId,
+ WorkerCrossThreadDispatcher* aDispatcher)
+{
+ // Only append client id after RIL_SOCKET_NAME when it's not connected to
+ // the first(0) rilproxy for compatibility.
+ if (!aClientId) {
+ mAddress = RIL_SOCKET_NAME;
+ } else {
+ struct sockaddr_un addr_un;
+ snprintf(addr_un.sun_path, sizeof addr_un.sun_path, "%s%lu",
+ RIL_SOCKET_NAME, aClientId);
+ mAddress = addr_un.sun_path;
+ }
+
+ mSocket = new RilSocket(aDispatcher, this, aClientId);
+
+ nsresult rv = mSocket->Connect(new RilConnector(mAddress, aClientId));
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void
+RilConsumer::Unregister()
+{
+ mShutdown = true;
+ Close();
+}
+
+bool
+RilConsumer::PostRILMessage(JSContext* aCx, unsigned aArgc, Value* aVp)
+{
+ CallArgs args = CallArgsFromVp(aArgc, aVp);
+
+ if (args.length() != 2) {
+ JS_ReportErrorASCII(aCx, "Expecting two arguments with the RIL message");
+ return false;
+ }
+
+ int clientId = args[0].toInt32();
+
+ if ((ssize_t)sRilConsumers.Length() <= clientId || !sRilConsumers[clientId]) {
+ // Probably shutting down.
+ return true;
+ }
+
+ nsresult rv = sRilConsumers[clientId]->Send(aCx, args);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ return true;
+}
+
+nsresult
+RilConsumer::Send(JSContext* aCx, const CallArgs& aArgs)
+{
+ if (NS_WARN_IF(!mSocket) ||
+ NS_WARN_IF(mSocket->GetConnectionStatus() == SOCKET_DISCONNECTED)) {
+ // Probably shutting down.
+ return NS_OK;
+ }
+
+ UniquePtr<UnixSocketRawData> raw;
+
+ Value v = aArgs[1];
+
+ if (v.isString()) {
+ JSAutoByteString abs;
+ Rooted<JSString*> str(aCx, v.toString());
+ if (!abs.encodeUtf8(aCx, str)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ raw = MakeUnique<UnixSocketRawData>(abs.ptr(), abs.length());
+ } else if (!v.isPrimitive()) {
+ JSObject* obj = v.toObjectOrNull();
+ if (!JS_IsTypedArrayObject(obj)) {
+ JS_ReportErrorASCII(aCx, "Object passed in wasn't a typed array");
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t type = JS_GetArrayBufferViewType(obj);
+ if (type != js::Scalar::Int8 &&
+ type != js::Scalar::Uint8 &&
+ type != js::Scalar::Uint8Clamped) {
+ JS_ReportErrorASCII(aCx, "Typed array data is not octets");
+ return NS_ERROR_FAILURE;
+ }
+
+ size_t size = JS_GetTypedArrayByteLength(obj);
+ bool isShared;
+ void* data;
+ {
+ AutoCheckCannotGC nogc;
+ data = JS_GetArrayBufferViewData(obj, &isShared, nogc);
+ }
+ if (isShared) {
+ JS_ReportErrorASCII(
+ aCx, "Incorrect argument. Shared memory not supported");
+ return NS_ERROR_FAILURE;
+ }
+ raw = MakeUnique<UnixSocketRawData>(data, size);
+ } else {
+ JS_ReportErrorASCII(
+ aCx, "Incorrect argument. Expecting a string or a typed array");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!raw) {
+ JS_ReportErrorASCII(aCx, "Unable to post to RIL");
+ return NS_ERROR_FAILURE;
+ }
+
+ mSocket->SendSocketData(raw.release());
+
+ return NS_OK;
+}
+
+nsresult
+RilConsumer::Receive(JSContext* aCx,
+ uint32_t aClientId,
+ const UnixSocketBuffer* aBuffer)
+{
+ MOZ_ASSERT(aBuffer);
+
+ Rooted<JSObject*> obj(aCx, CurrentGlobalOrNull(aCx));
+
+ Rooted<JSObject*> array(aCx, JS_NewUint8Array(aCx, aBuffer->GetSize()));
+ if (NS_WARN_IF(!array)) {
+ // Just suppress the exception, since our callers don't have a way to
+ // indicate they failed.
+ JS_ClearPendingException(aCx);
+ return NS_ERROR_FAILURE;
+ }
+ {
+ AutoCheckCannotGC nogc;
+ bool isShared;
+ memcpy(JS_GetArrayBufferViewData(array, &isShared, nogc),
+ aBuffer->GetData(), aBuffer->GetSize());
+ MOZ_ASSERT(!isShared); // Array was constructed above.
+ }
+
+ AutoValueArray<2> args(aCx);
+ args[0].setNumber(aClientId);
+ args[1].setObject(*array);
+
+ Rooted<Value> rval(aCx);
+ JS_CallFunctionName(aCx, obj, "onRILMessage", args, &rval);
+ // Just suppress the exception, since our callers don't have a way to
+ // indicate they failed.
+ JS_ClearPendingException(aCx);
+
+ return NS_OK;
+}
+
+void
+RilConsumer::Close()
+{
+ if (mSocket) {
+ mSocket->Close();
+ mSocket = nullptr;
+ }
+}
+
+// |RilSocketConnector|
+
+void
+RilConsumer::ReceiveSocketData(JSContext* aCx,
+ int aIndex,
+ UniquePtr<UnixSocketBuffer>& aBuffer)
+{
+ Receive(aCx, (uint32_t)aIndex, aBuffer.get());
+}
+
+void
+RilConsumer::OnConnectSuccess(int aIndex)
+{
+ // Nothing to do here.
+ CHROMIUM_LOG("RIL[%d]: %s\n", aIndex, __FUNCTION__);
+}
+
+void
+RilConsumer::OnConnectError(int aIndex)
+{
+ CHROMIUM_LOG("RIL[%d]: %s\n", aIndex, __FUNCTION__);
+ Close();
+}
+
+void
+RilConsumer::OnDisconnect(int aIndex)
+{
+ CHROMIUM_LOG("RIL[%d]: %s\n", aIndex, __FUNCTION__);
+ if (mShutdown) {
+ return;
+ }
+ mSocket->Connect(new RilConnector(mAddress, aIndex),
+ mSocket->GetSuggestedConnectDelayMs());
+}
+
+//
+// RilWorker
+//
+
+nsTArray<UniquePtr<RilWorker>> RilWorker::sRilWorkers;
+
+nsresult
+RilWorker::Register(unsigned int aClientId,
+ WorkerCrossThreadDispatcher* aDispatcher)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ sRilWorkers.EnsureLengthAtLeast(aClientId + 1);
+
+ if (sRilWorkers[aClientId]) {
+ NS_WARNING("RilWorkers already registered");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Now that we're set up, connect ourselves to the RIL thread.
+ sRilWorkers[aClientId] = MakeUnique<RilWorker>(aDispatcher);
+
+ nsresult rv = sRilWorkers[aClientId]->RegisterConsumer(aClientId);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+void
+RilWorker::Shutdown()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ for (size_t i = 0; i < sRilWorkers.Length(); ++i) {
+ if (!sRilWorkers[i]) {
+ continue;
+ }
+ sRilWorkers[i]->UnregisterConsumer(i);
+ sRilWorkers[i] = nullptr;
+ }
+}
+
+RilWorker::RilWorker(WorkerCrossThreadDispatcher* aDispatcher)
+ : mDispatcher(aDispatcher)
+{
+ MOZ_ASSERT(mDispatcher);
+}
+
+class RilWorker::RegisterConsumerTask : public WorkerTask
+{
+public:
+ RegisterConsumerTask(unsigned int aClientId,
+ WorkerCrossThreadDispatcher* aDispatcher)
+ : mClientId(aClientId)
+ , mDispatcher(aDispatcher)
+ {
+ MOZ_ASSERT(mDispatcher);
+ }
+
+ bool RunTask(JSContext* aCx) override
+ {
+ sRilConsumers.EnsureLengthAtLeast(mClientId + 1);
+
+ MOZ_ASSERT(!sRilConsumers[mClientId]);
+
+ auto rilConsumer = MakeUnique<RilConsumer>();
+
+ nsresult rv = rilConsumer->ConnectWorkerToRIL(aCx);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ rv = rilConsumer->Register(mClientId, mDispatcher);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ sRilConsumers[mClientId] = Move(rilConsumer);
+
+ return true;
+ }
+
+private:
+ unsigned int mClientId;
+ RefPtr<WorkerCrossThreadDispatcher> mDispatcher;
+};
+
+nsresult
+RilWorker::RegisterConsumer(unsigned int aClientId)
+{
+ RefPtr<RegisterConsumerTask> task = new RegisterConsumerTask(aClientId,
+ mDispatcher);
+ if (!mDispatcher->PostTask(task)) {
+ NS_WARNING("Failed to post register-consumer task.");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+class RilWorker::UnregisterConsumerTask : public WorkerTask
+{
+public:
+ UnregisterConsumerTask(unsigned int aClientId)
+ : mClientId(aClientId)
+ { }
+
+ bool RunTask(JSContext* aCx) override
+ {
+ MOZ_ASSERT(mClientId < sRilConsumers.Length());
+ MOZ_ASSERT(sRilConsumers[mClientId]);
+
+ sRilConsumers[mClientId]->Unregister();
+ sRilConsumers[mClientId] = nullptr;
+
+ return true;
+ }
+
+private:
+ unsigned int mClientId;
+};
+
+void
+RilWorker::UnregisterConsumer(unsigned int aClientId)
+{
+ RefPtr<UnregisterConsumerTask> task =
+ new UnregisterConsumerTask(aClientId);
+
+ if (!mDispatcher->PostTask(task)) {
+ NS_WARNING("Failed to post unregister-consumer task.");
+ return;
+ }
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/ril/Ril.h b/ipc/ril/Ril.h
new file mode 100644
index 000000000..206d3977e
--- /dev/null
+++ b/ipc/ril/Ril.h
@@ -0,0 +1,54 @@
+/* -*- 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/. */
+
+#ifndef mozilla_ipc_Ril_h
+#define mozilla_ipc_Ril_h 1
+
+#include "nsError.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+
+namespace dom {
+namespace workers {
+
+class WorkerCrossThreadDispatcher;
+
+} // namespace workers
+} // namespace dom
+
+namespace ipc {
+
+class RilConsumer;
+
+class RilWorker final
+{
+public:
+ static nsresult Register(
+ unsigned int aClientId,
+ mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher);
+
+ static void Shutdown();
+
+ // Public for |MakeUnique| Call |Register| instead.
+ RilWorker(mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher);
+
+private:
+ class RegisterConsumerTask;
+ class UnregisterConsumerTask;
+
+ nsresult RegisterConsumer(unsigned int aClientId);
+ void UnregisterConsumer(unsigned int aClientId);
+
+ static nsTArray<UniquePtr<RilWorker>> sRilWorkers;
+
+ RefPtr<mozilla::dom::workers::WorkerCrossThreadDispatcher> mDispatcher;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_Ril_h
diff --git a/ipc/ril/RilConnector.cpp b/ipc/ril/RilConnector.cpp
new file mode 100644
index 000000000..6e10beed7
--- /dev/null
+++ b/ipc/ril/RilConnector.cpp
@@ -0,0 +1,221 @@
+/* -*- 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 "RilConnector.h"
+#include <fcntl.h>
+#include <sys/socket.h>
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
+#include "nsThreadUtils.h" // For NS_IsMainThread.
+
+#ifdef AF_INET
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#endif
+#ifdef AF_UNIX
+#include <sys/un.h>
+#endif
+
+namespace mozilla {
+namespace ipc {
+
+static const uint16_t RIL_TEST_PORT = 6200;
+
+RilConnector::RilConnector(const nsACString& aAddressString,
+ unsigned long aClientId)
+ : mAddressString(aAddressString)
+ , mClientId(aClientId)
+{
+ MOZ_COUNT_CTOR_INHERITED(RilConnector, UnixSocketConnector);
+}
+
+RilConnector::~RilConnector()
+{
+ MOZ_COUNT_DTOR_INHERITED(RilConnector, UnixSocketConnector);
+}
+
+nsresult
+RilConnector::CreateSocket(int aDomain, int& aFd) const
+{
+ aFd = socket(aDomain, SOCK_STREAM, 0);
+ if (aFd < 0) {
+ NS_WARNING("Could not open RIL socket!");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+RilConnector::SetSocketFlags(int aFd) const
+{
+ static const int sReuseAddress = 1;
+
+ // Set close-on-exec bit.
+ int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFD));
+ if (flags < 0) {
+ return NS_ERROR_FAILURE;
+ }
+ flags |= FD_CLOEXEC;
+ int res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFD, flags));
+ if (res < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set non-blocking status flag.
+ flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
+ if (flags < 0) {
+ return NS_ERROR_FAILURE;
+ }
+ flags |= O_NONBLOCK;
+ res = TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags));
+ if (res < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set socket addr to be reused even if kernel is still waiting to close.
+ res = setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &sReuseAddress,
+ sizeof(sReuseAddress));
+ if (res < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+RilConnector::CreateAddress(int aDomain,
+ struct sockaddr& aAddress,
+ socklen_t& aAddressLength) const
+{
+ switch (aDomain) {
+#ifdef AF_UNIX
+ case AF_UNIX: {
+ struct sockaddr_un* address =
+ reinterpret_cast<struct sockaddr_un*>(&aAddress);
+ address->sun_family = aDomain;
+ size_t siz = mAddressString.Length() + 1;
+ if (siz > sizeof(address->sun_path)) {
+ NS_WARNING("Address too long for socket struct!");
+ return NS_ERROR_FAILURE;
+ }
+ memcpy(address->sun_path, mAddressString.get(), siz);
+ aAddressLength = offsetof(struct sockaddr_un, sun_path) + siz;
+ }
+ break;
+#endif
+#ifdef AF_INET
+ case AF_INET: {
+ struct sockaddr_in* address =
+ reinterpret_cast<struct sockaddr_in*>(&aAddress);
+ address->sin_family = aDomain;
+ address->sin_port = htons(RIL_TEST_PORT + mClientId);
+ address->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ aAddressLength = sizeof(*address);
+ }
+ break;
+#endif
+ default:
+ NS_WARNING("Address family not handled by connector!");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+// |UnixSocketConnector|
+
+nsresult
+RilConnector::ConvertAddressToString(const struct sockaddr& aAddress,
+ socklen_t aAddressLength,
+ nsACString& aAddressString)
+{
+#ifdef AF_UNIX
+ if (aAddress.sa_family == AF_UNIX) {
+ const struct sockaddr_un* un =
+ reinterpret_cast<const struct sockaddr_un*>(&aAddress);
+
+ size_t len = aAddressLength - offsetof(struct sockaddr_un, sun_path);
+
+ aAddressString.Assign(un->sun_path, len);
+ } else
+#endif
+#ifdef AF_INET
+ if (aAddress.sa_family == AF_INET) {
+ const struct sockaddr_in* in =
+ reinterpret_cast<const struct sockaddr_in*>(&aAddress);
+
+ aAddressString.Assign(nsDependentCString(inet_ntoa(in->sin_addr)));
+ } else
+#endif
+ {
+ NS_WARNING("Address family not handled by connector!");
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+RilConnector::CreateListenSocket(struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aListenFd)
+{
+ MOZ_CRASH("|RilConnector| does not support listening sockets.");
+}
+
+nsresult
+RilConnector::AcceptStreamSocket(int aListenFd,
+ struct sockaddr* aAddress,
+ socklen_t* aAddressLen,
+ int& aStreamFd)
+{
+ MOZ_CRASH("|RilConnector| does not support accepting sockets.");
+}
+
+nsresult
+RilConnector::CreateStreamSocket(struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aStreamFd)
+{
+#ifdef MOZ_WIDGET_GONK
+ static const int sDomain = AF_UNIX;
+#else
+ static const int sDomain = AF_INET;
+#endif
+
+ ScopedClose fd;
+
+ nsresult rv = CreateSocket(sDomain, fd.rwget());
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ rv = SetSocketFlags(fd);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ if (aAddress && aAddressLength) {
+ rv = CreateAddress(sDomain, *aAddress, *aAddressLength);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ aStreamFd = fd.forget();
+
+ return NS_OK;
+}
+
+nsresult
+RilConnector::Duplicate(UnixSocketConnector*& aConnector)
+{
+ aConnector = new RilConnector(*this);
+
+ return NS_OK;
+}
+
+}
+}
diff --git a/ipc/ril/RilConnector.h b/ipc/ril/RilConnector.h
new file mode 100644
index 000000000..ef99af05d
--- /dev/null
+++ b/ipc/ril/RilConnector.h
@@ -0,0 +1,63 @@
+/* -*- 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/. */
+
+#ifndef mozilla_ipc_RilConnector_h
+#define mozilla_ipc_RilConnector_h
+
+#include "mozilla/ipc/UnixSocketConnector.h"
+
+namespace mozilla {
+namespace ipc {
+
+/**
+ * |RilConnector| creates sockets for connecting to rild.
+ */
+class RilConnector final : public UnixSocketConnector
+{
+public:
+ RilConnector(const nsACString& aAddressString,
+ unsigned long aClientId);
+ ~RilConnector();
+
+ // Methods for |UnixSocketConnector|
+ //
+
+ nsresult ConvertAddressToString(const struct sockaddr& aAddress,
+ socklen_t aAddressLength,
+ nsACString& aAddressString) override;
+
+ nsresult CreateListenSocket(struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aListenFd) override;
+
+ nsresult AcceptStreamSocket(int aListenFd,
+ struct sockaddr* aAddress,
+ socklen_t* aAddressLen,
+ int& aStreamFd) override;
+
+ nsresult CreateStreamSocket(struct sockaddr* aAddress,
+ socklen_t* aAddressLength,
+ int& aStreamFd) override;
+
+ nsresult Duplicate(UnixSocketConnector*& aConnector) override;
+
+
+private:
+ nsresult CreateSocket(int aDomain, int& aFd) const;
+ nsresult SetSocketFlags(int aFd) const;
+ nsresult CreateAddress(int aDomain,
+ struct sockaddr& aAddress,
+ socklen_t& aAddressLength) const;
+
+ nsCString mAddressString;
+ unsigned long mClientId;
+};
+
+}
+}
+
+#endif
diff --git a/ipc/ril/RilSocket.cpp b/ipc/ril/RilSocket.cpp
new file mode 100644
index 000000000..490a74ac0
--- /dev/null
+++ b/ipc/ril/RilSocket.cpp
@@ -0,0 +1,446 @@
+/* -*- 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 "RilSocket.h"
+#include <fcntl.h>
+#include "mozilla/dom/workers/Workers.h"
+#include "mozilla/ipc/UnixSocketConnector.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h" // for MOZ_COUNT_CTOR, MOZ_COUNT_DTOR
+#include "nsXULAppAPI.h"
+#include "RilSocketConsumer.h"
+#include "mozilla/Unused.h"
+
+static const size_t MAX_READ_SIZE = 1 << 16;
+
+namespace mozilla {
+namespace ipc {
+
+USING_WORKERS_NAMESPACE
+
+//
+// RilSocketIO
+//
+
+class RilSocketIO final : public ConnectionOrientedSocketIO
+{
+public:
+ class ConnectTask;
+ class DelayedConnectTask;
+ class ReceiveTask;
+
+ RilSocketIO(WorkerCrossThreadDispatcher* aDispatcher,
+ MessageLoop* aConsumerLoop,
+ MessageLoop* aIOLoop,
+ RilSocket* aRilSocket,
+ UnixSocketConnector* aConnector);
+ ~RilSocketIO();
+
+ RilSocket* GetRilSocket();
+ DataSocket* GetDataSocket();
+
+ // Delayed-task handling
+ //
+
+ void SetDelayedConnectTask(CancelableRunnable* aTask);
+ void ClearDelayedConnectTask();
+ void CancelDelayedConnectTask();
+
+ // Methods for |DataSocket|
+ //
+
+ nsresult QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer) override;
+ void ConsumeBuffer() override;
+ void DiscardBuffer() override;
+
+ // Methods for |SocketIOBase|
+ //
+
+ SocketBase* GetSocketBase() override;
+
+ bool IsShutdownOnConsumerThread() const override;
+ bool IsShutdownOnIOThread() const override;
+
+ void ShutdownOnConsumerThread() override;
+ void ShutdownOnIOThread() override;
+
+private:
+ /**
+ * Cross-thread dispatcher for the RIL worker
+ */
+ RefPtr<WorkerCrossThreadDispatcher> mDispatcher;
+
+ /**
+ * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
+ * directly from consumer thread. All non-consumer-thread accesses should
+ * happen with mIO as container.
+ */
+ RefPtr<RilSocket> mRilSocket;
+
+ /**
+ * If true, do not requeue whatever task we're running
+ */
+ bool mShuttingDownOnIOThread;
+
+ /**
+ * Task member for delayed connect task. Should only be access on consumer
+ * thread.
+ */
+ CancelableRunnable* mDelayedConnectTask;
+
+ /**
+ * I/O buffer for received data
+ */
+ UniquePtr<UnixSocketRawData> mBuffer;
+};
+
+RilSocketIO::RilSocketIO(WorkerCrossThreadDispatcher* aDispatcher,
+ MessageLoop* aConsumerLoop,
+ MessageLoop* aIOLoop,
+ RilSocket* aRilSocket,
+ UnixSocketConnector* aConnector)
+ : ConnectionOrientedSocketIO(aConsumerLoop, aIOLoop, aConnector)
+ , mDispatcher(aDispatcher)
+ , mRilSocket(aRilSocket)
+ , mShuttingDownOnIOThread(false)
+ , mDelayedConnectTask(nullptr)
+{
+ MOZ_ASSERT(mDispatcher);
+ MOZ_ASSERT(mRilSocket);
+
+ MOZ_COUNT_CTOR_INHERITED(RilSocketIO, ConnectionOrientedSocketIO);
+}
+
+RilSocketIO::~RilSocketIO()
+{
+ MOZ_ASSERT(IsConsumerThread());
+ MOZ_ASSERT(IsShutdownOnConsumerThread());
+
+ MOZ_COUNT_DTOR_INHERITED(RilSocketIO, ConnectionOrientedSocketIO);
+}
+
+RilSocket*
+RilSocketIO::GetRilSocket()
+{
+ return mRilSocket.get();
+}
+
+DataSocket*
+RilSocketIO::GetDataSocket()
+{
+ return mRilSocket.get();
+}
+
+void
+RilSocketIO::SetDelayedConnectTask(CancelableRunnable* aTask)
+{
+ MOZ_ASSERT(IsConsumerThread());
+
+ mDelayedConnectTask = aTask;
+}
+
+void
+RilSocketIO::ClearDelayedConnectTask()
+{
+ MOZ_ASSERT(IsConsumerThread());
+
+ mDelayedConnectTask = nullptr;
+}
+
+void
+RilSocketIO::CancelDelayedConnectTask()
+{
+ MOZ_ASSERT(IsConsumerThread());
+
+ if (!mDelayedConnectTask) {
+ return;
+ }
+
+ mDelayedConnectTask->Cancel();
+ ClearDelayedConnectTask();
+}
+
+// |DataSocketIO|
+
+nsresult
+RilSocketIO::QueryReceiveBuffer(UnixSocketIOBuffer** aBuffer)
+{
+ MOZ_ASSERT(aBuffer);
+
+ if (!mBuffer) {
+ mBuffer = MakeUnique<UnixSocketRawData>(MAX_READ_SIZE);
+ }
+ *aBuffer = mBuffer.get();
+
+ return NS_OK;
+}
+
+/**
+ * |ReceiveTask| transfers data received on the I/O thread
+ * to an instance of |RilSocket| on the consumer thread.
+ */
+class RilSocketIO::ReceiveTask final : public WorkerTask
+{
+public:
+ ReceiveTask(RilSocketIO* aIO, UnixSocketBuffer* aBuffer)
+ : mIO(aIO)
+ , mBuffer(aBuffer)
+ {
+ MOZ_ASSERT(mIO);
+ }
+
+ bool RunTask(JSContext* aCx) override
+ {
+ // Dispatched via WCTD, but still needs to run on the consumer thread
+ MOZ_ASSERT(mIO->IsConsumerThread());
+
+ if (NS_WARN_IF(mIO->IsShutdownOnConsumerThread())) {
+ // Since we've already explicitly closed and the close
+ // happened before this, this isn't really an error.
+ return true;
+ }
+
+ RilSocket* rilSocket = mIO->GetRilSocket();
+ MOZ_ASSERT(rilSocket);
+
+ rilSocket->ReceiveSocketData(aCx, mBuffer);
+
+ return true;
+ }
+
+private:
+ RilSocketIO* mIO;
+ UniquePtr<UnixSocketBuffer> mBuffer;
+};
+
+void
+RilSocketIO::ConsumeBuffer()
+{
+ RefPtr<ReceiveTask> task = new ReceiveTask(this, mBuffer.release());
+ Unused << NS_WARN_IF(!mDispatcher->PostTask(task));
+}
+
+void
+RilSocketIO::DiscardBuffer()
+{
+ // Nothing to do.
+}
+
+// |SocketIOBase|
+
+SocketBase*
+RilSocketIO::GetSocketBase()
+{
+ return GetDataSocket();
+}
+
+bool
+RilSocketIO::IsShutdownOnConsumerThread() const
+{
+ MOZ_ASSERT(IsConsumerThread());
+
+ return mRilSocket == nullptr;
+}
+
+bool
+RilSocketIO::IsShutdownOnIOThread() const
+{
+ return mShuttingDownOnIOThread;
+}
+
+void
+RilSocketIO::ShutdownOnConsumerThread()
+{
+ MOZ_ASSERT(IsConsumerThread());
+ MOZ_ASSERT(!IsShutdownOnConsumerThread());
+
+ mRilSocket = nullptr;
+}
+
+void
+RilSocketIO::ShutdownOnIOThread()
+{
+ MOZ_ASSERT(!IsConsumerThread());
+ MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+ Close(); // will also remove fd from I/O loop
+ mShuttingDownOnIOThread = true;
+}
+
+//
+// Socket tasks
+//
+
+class RilSocketIO::ConnectTask final
+ : public SocketIOTask<RilSocketIO>
+{
+public:
+ ConnectTask(RilSocketIO* aIO)
+ : SocketIOTask<RilSocketIO>(aIO)
+ { }
+
+ NS_IMETHOD Run() override
+ {
+ MOZ_ASSERT(!GetIO()->IsConsumerThread());
+ MOZ_ASSERT(!IsCanceled());
+
+ GetIO()->Connect();
+
+ return NS_OK;
+ }
+};
+
+class RilSocketIO::DelayedConnectTask final
+ : public SocketIOTask<RilSocketIO>
+{
+public:
+ DelayedConnectTask(RilSocketIO* aIO)
+ : SocketIOTask<RilSocketIO>(aIO)
+ { }
+
+ NS_IMETHOD Run() override
+ {
+ MOZ_ASSERT(GetIO()->IsConsumerThread());
+
+ if (IsCanceled()) {
+ return NS_OK;
+ }
+
+ RilSocketIO* io = GetIO();
+ if (io->IsShutdownOnConsumerThread()) {
+ return NS_OK;
+ }
+
+ io->ClearDelayedConnectTask();
+ io->GetIOLoop()->PostTask(MakeAndAddRef<ConnectTask>(io));
+
+ return NS_OK;
+ }
+};
+
+//
+// RilSocket
+//
+
+RilSocket::RilSocket(WorkerCrossThreadDispatcher* aDispatcher,
+ RilSocketConsumer* aConsumer, int aIndex)
+ : mIO(nullptr)
+ , mDispatcher(aDispatcher)
+ , mConsumer(aConsumer)
+ , mIndex(aIndex)
+{
+ MOZ_ASSERT(mDispatcher);
+ MOZ_ASSERT(mConsumer);
+
+ MOZ_COUNT_CTOR_INHERITED(RilSocket, ConnectionOrientedSocket);
+}
+
+RilSocket::~RilSocket()
+{
+ MOZ_ASSERT(!mIO);
+
+ MOZ_COUNT_DTOR_INHERITED(RilSocket, ConnectionOrientedSocket);
+}
+
+void
+RilSocket::ReceiveSocketData(JSContext* aCx,
+ UniquePtr<UnixSocketBuffer>& aBuffer)
+{
+ mConsumer->ReceiveSocketData(aCx, mIndex, aBuffer);
+}
+
+nsresult
+RilSocket::Connect(UnixSocketConnector* aConnector, int aDelayMs,
+ MessageLoop* aConsumerLoop, MessageLoop* aIOLoop)
+{
+ MOZ_ASSERT(!mIO);
+
+ mIO = new RilSocketIO(mDispatcher, aConsumerLoop, aIOLoop, this, aConnector);
+ SetConnectionStatus(SOCKET_CONNECTING);
+
+ if (aDelayMs > 0) {
+ RefPtr<RilSocketIO::DelayedConnectTask> connectTask =
+ MakeAndAddRef<RilSocketIO::DelayedConnectTask>(mIO);
+ mIO->SetDelayedConnectTask(connectTask);
+ MessageLoop::current()->PostDelayedTask(connectTask.forget(), aDelayMs);
+ } else {
+ aIOLoop->PostTask(MakeAndAddRef<RilSocketIO::ConnectTask>(mIO));
+ }
+
+ return NS_OK;
+}
+
+nsresult
+RilSocket::Connect(UnixSocketConnector* aConnector, int aDelayMs)
+{
+ return Connect(aConnector, aDelayMs,
+ MessageLoop::current(), XRE_GetIOMessageLoop());
+}
+
+// |ConnectionOrientedSocket|
+
+nsresult
+RilSocket::PrepareAccept(UnixSocketConnector* aConnector,
+ MessageLoop* aConsumerLoop,
+ MessageLoop* aIOLoop,
+ ConnectionOrientedSocketIO*& aIO)
+{
+ MOZ_CRASH("|RilSocket| does not support accepting connections.");
+}
+
+// |DataSocket|
+
+void
+RilSocket::SendSocketData(UnixSocketIOBuffer* aBuffer)
+{
+ MOZ_ASSERT(mIO);
+ MOZ_ASSERT(mIO->IsConsumerThread());
+ MOZ_ASSERT(!mIO->IsShutdownOnConsumerThread());
+
+ mIO->GetIOLoop()->PostTask(
+ MakeAndAddRef<SocketIOSendTask<RilSocketIO, UnixSocketIOBuffer>>(mIO, aBuffer));
+}
+
+// |SocketBase|
+
+void
+RilSocket::Close()
+{
+ MOZ_ASSERT(mIO);
+ MOZ_ASSERT(mIO->IsConsumerThread());
+
+ mIO->CancelDelayedConnectTask();
+
+ // From this point on, we consider |mIO| as being deleted. We sever
+ // the relationship here so any future calls to |Connect| will create
+ // a new I/O object.
+ mIO->ShutdownOnConsumerThread();
+ mIO->GetIOLoop()->PostTask(MakeAndAddRef<SocketIOShutdownTask>(mIO));
+ mIO = nullptr;
+
+ NotifyDisconnect();
+}
+
+void
+RilSocket::OnConnectSuccess()
+{
+ mConsumer->OnConnectSuccess(mIndex);
+}
+
+void
+RilSocket::OnConnectError()
+{
+ mConsumer->OnConnectError(mIndex);
+}
+
+void
+RilSocket::OnDisconnect()
+{
+ mConsumer->OnDisconnect(mIndex);
+}
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/ipc/ril/RilSocket.h b/ipc/ril/RilSocket.h
new file mode 100644
index 000000000..a768b0eb7
--- /dev/null
+++ b/ipc/ril/RilSocket.h
@@ -0,0 +1,110 @@
+/* -*- 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/. */
+
+#ifndef mozilla_ipc_RilSocket_h
+#define mozilla_ipc_RilSocket_h
+
+#include "mozilla/ipc/ConnectionOrientedSocket.h"
+
+class JSContext;
+class MessageLoop;
+
+namespace mozilla {
+namespace dom {
+namespace workers {
+
+class WorkerCrossThreadDispatcher;
+
+} // namespace workers
+} // namespace dom
+} // namespace mozilla
+
+namespace mozilla {
+namespace ipc {
+
+class RilSocketConsumer;
+class RilSocketIO;
+class UnixSocketConnector;
+
+class RilSocket final : public ConnectionOrientedSocket
+{
+public:
+ /**
+ * Constructs an instance of |RilSocket|.
+ *
+ * @param aDispatcher The dispatcher class for the received messages.
+ * @param aConsumer The consumer for the socket.
+ * @param aIndex An arbitrary index.
+ */
+ RilSocket(mozilla::dom::workers::WorkerCrossThreadDispatcher* aDispatcher,
+ RilSocketConsumer* aConsumer, int aIndex);
+
+ /**
+ * Method to be called whenever data is received. RIL-worker only.
+ *
+ * @param aCx The RIL worker's JS context.
+ * @param aBuffer Data received from the socket.
+ */
+ void ReceiveSocketData(JSContext* aCx, UniquePtr<UnixSocketBuffer>& aBuffer);
+
+ /**
+ * Starts a task on the socket that will try to connect to a socket in a
+ * non-blocking manner.
+ *
+ * @param aConnector Connector object for socket type specific functions
+ * @param aDelayMs Time delay in milliseconds.
+ * @param aConsumerLoop The socket's consumer thread.
+ * @param aIOLoop The socket's I/O thread.
+ * @return NS_OK on success, or an XPCOM error code otherwise.
+ */
+ nsresult Connect(UnixSocketConnector* aConnector, int aDelayMs,
+ MessageLoop* aConsumerLoop, MessageLoop* aIOLoop);
+
+ /**
+ * Starts a task on the socket that will try to connect to a socket in a
+ * non-blocking manner.
+ *
+ * @param aConnector Connector object for socket type specific functions
+ * @param aDelayMs Time delay in milliseconds.
+ * @return NS_OK on success, or an XPCOM error code otherwise.
+ */
+ nsresult Connect(UnixSocketConnector* aConnector, int aDelayMs = 0);
+
+ // Methods for |ConnectionOrientedSocket|
+ //
+
+ nsresult PrepareAccept(UnixSocketConnector* aConnector,
+ MessageLoop* aConsumerLoop,
+ MessageLoop* aIOLoop,
+ ConnectionOrientedSocketIO*& aIO) override;
+
+ // Methods for |DataSocket|
+ //
+
+ void SendSocketData(UnixSocketIOBuffer* aBuffer) override;
+
+ // Methods for |SocketBase|
+ //
+
+ void Close() override;
+ void OnConnectSuccess() override;
+ void OnConnectError() override;
+ void OnDisconnect() override;
+
+protected:
+ virtual ~RilSocket();
+
+private:
+ RilSocketIO* mIO;
+ RefPtr<mozilla::dom::workers::WorkerCrossThreadDispatcher> mDispatcher;
+ RilSocketConsumer* mConsumer;
+ int mIndex;
+};
+
+} // namespace ipc
+} // namepsace mozilla
+
+#endif // mozilla_ipc_RilSocket_h
diff --git a/ipc/ril/RilSocketConsumer.cpp b/ipc/ril/RilSocketConsumer.cpp
new file mode 100644
index 000000000..a8a741887
--- /dev/null
+++ b/ipc/ril/RilSocketConsumer.cpp
@@ -0,0 +1,20 @@
+/* -*- 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 "RilSocketConsumer.h"
+
+namespace mozilla {
+namespace ipc {
+
+//
+// RilSocketConsumer
+//
+
+RilSocketConsumer::~RilSocketConsumer()
+{ }
+
+}
+}
diff --git a/ipc/ril/RilSocketConsumer.h b/ipc/ril/RilSocketConsumer.h
new file mode 100644
index 000000000..03007dc15
--- /dev/null
+++ b/ipc/ril/RilSocketConsumer.h
@@ -0,0 +1,64 @@
+/* -*- 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/. */
+
+#ifndef mozilla_ipc_RilSocketConsumer_h
+#define mozilla_ipc_RilSocketConsumer_h
+
+#include "mozilla/UniquePtr.h"
+
+class JSContext;
+
+namespace mozilla {
+namespace ipc {
+
+class UnixSocketBuffer;
+
+/**
+ * |RilSocketConsumer| handles socket events and received data.
+ */
+class RilSocketConsumer
+{
+public:
+ /**
+ * Method to be called whenever data is received. RIL-worker only.
+ *
+ * @param aCx The RIL worker's JS context.
+ * @param aIndex The index that has been given to the stream socket.
+ * @param aBuffer Data received from the socket.
+ */
+ virtual void ReceiveSocketData(JSContext* aCx,
+ int aIndex,
+ UniquePtr<UnixSocketBuffer>& aBuffer) = 0;
+
+ /**
+ * Callback for socket success. Consumer-thread only.
+ *
+ * @param aIndex The index that has been given to the stream socket.
+ */
+ virtual void OnConnectSuccess(int aIndex) = 0;
+
+ /**
+ * Callback for socket errors. Consumer-thread only.
+ *
+ * @param aIndex The index that has been given to the stream socket.
+ */
+ virtual void OnConnectError(int aIndex) = 0;
+
+ /**
+ * Callback for socket disconnect. Consumer-thread only.
+ *
+ * @param aIndex The index that has been given to the stream socket.
+ */
+ virtual void OnDisconnect(int aIndex) = 0;
+
+protected:
+ virtual ~RilSocketConsumer();
+};
+
+}
+}
+
+#endif
diff --git a/ipc/ril/moz.build b/ipc/ril/moz.build
new file mode 100644
index 000000000..ca436ab43
--- /dev/null
+++ b/ipc/ril/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.ipc += [
+ 'Ril.h',
+ 'RilSocket.h',
+ 'RilSocketConsumer.h'
+]
+
+SOURCES += [
+ 'Ril.cpp',
+ 'RilConnector.cpp',
+ 'RilSocket.cpp',
+ 'RilSocketConsumer.cpp'
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'