diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /ipc/ril | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'ipc/ril')
-rw-r--r-- | ipc/ril/Ril.cpp | 458 | ||||
-rw-r--r-- | ipc/ril/Ril.h | 54 | ||||
-rw-r--r-- | ipc/ril/RilConnector.cpp | 221 | ||||
-rw-r--r-- | ipc/ril/RilConnector.h | 63 | ||||
-rw-r--r-- | ipc/ril/RilSocket.cpp | 446 | ||||
-rw-r--r-- | ipc/ril/RilSocket.h | 110 | ||||
-rw-r--r-- | ipc/ril/RilSocketConsumer.cpp | 20 | ||||
-rw-r--r-- | ipc/ril/RilSocketConsumer.h | 64 | ||||
-rw-r--r-- | ipc/ril/moz.build | 22 |
9 files changed, 1458 insertions, 0 deletions
diff --git a/ipc/ril/Ril.cpp b/ipc/ril/Ril.cpp new file mode 100644 index 0000000000..90fb120522 --- /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 0000000000..206d3977ef --- /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 0000000000..6e10beed77 --- /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 0000000000..ef99af05d2 --- /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 0000000000..490a74ac0a --- /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 0000000000..a768b0eb77 --- /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 0000000000..a8a7418872 --- /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 0000000000..03007dc158 --- /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 0000000000..ca436ab43a --- /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' |