michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set sw=4 ts=8 et ft=cpp: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ipc/Ril.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include // For gethostbyname. michael@0: michael@0: #undef CHROMIUM_LOG michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: #include michael@0: #define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) michael@0: #else michael@0: #define CHROMIUM_LOG(args...) printf(args); michael@0: #endif michael@0: michael@0: #include "jsfriendapi.h" michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "nsTArray.h" michael@0: #include "nsThreadUtils.h" // For NS_IsMainThread. michael@0: michael@0: USING_WORKERS_NAMESPACE michael@0: using namespace mozilla::ipc; michael@0: michael@0: namespace { michael@0: michael@0: const char* RIL_SOCKET_NAME = "/dev/socket/rilproxy"; michael@0: michael@0: // Network port to connect to for adb forwarded sockets when doing michael@0: // desktop development. michael@0: const uint32_t RIL_TEST_PORT = 6200; michael@0: michael@0: nsTArray > sRilConsumers; michael@0: michael@0: class ConnectWorkerToRIL : public WorkerTask michael@0: { michael@0: public: michael@0: ConnectWorkerToRIL() michael@0: { } michael@0: michael@0: virtual bool RunTask(JSContext *aCx); michael@0: }; michael@0: michael@0: class SendRilSocketDataTask : public nsRunnable michael@0: { michael@0: public: michael@0: SendRilSocketDataTask(unsigned long aClientId, michael@0: UnixSocketRawData *aRawData) michael@0: : mRawData(aRawData) michael@0: , mClientId(aClientId) michael@0: { } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (sRilConsumers.Length() <= mClientId || michael@0: !sRilConsumers[mClientId] || michael@0: sRilConsumers[mClientId]->GetConnectionStatus() != SOCKET_CONNECTED) { michael@0: // Probably shuting down. michael@0: delete mRawData; michael@0: return NS_OK; michael@0: } michael@0: michael@0: sRilConsumers[mClientId]->SendSocketData(mRawData); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: UnixSocketRawData *mRawData; michael@0: unsigned long mClientId; michael@0: }; michael@0: michael@0: bool michael@0: PostToRIL(JSContext *aCx, michael@0: unsigned aArgc, michael@0: JS::Value *aVp) michael@0: { michael@0: JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); michael@0: NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); michael@0: michael@0: if (args.length() != 2) { michael@0: JS_ReportError(aCx, "Expecting two arguments with the RIL message"); michael@0: return false; michael@0: } michael@0: michael@0: int clientId = args[0].toInt32(); michael@0: JS::Value v = args[1]; michael@0: michael@0: JSAutoByteString abs; michael@0: void *data; michael@0: size_t size; michael@0: if (JSVAL_IS_STRING(v)) { michael@0: JS::Rooted str(aCx, v.toString()); michael@0: if (!abs.encodeUtf8(aCx, str)) { michael@0: return false; michael@0: } michael@0: michael@0: data = abs.ptr(); michael@0: size = abs.length(); michael@0: } else if (!JSVAL_IS_PRIMITIVE(v)) { michael@0: JSObject *obj = JSVAL_TO_OBJECT(v); michael@0: if (!JS_IsTypedArrayObject(obj)) { michael@0: JS_ReportError(aCx, "Object passed in wasn't a typed array"); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t type = JS_GetArrayBufferViewType(obj); michael@0: if (type != js::ArrayBufferView::TYPE_INT8 && michael@0: type != js::ArrayBufferView::TYPE_UINT8 && michael@0: type != js::ArrayBufferView::TYPE_UINT8_CLAMPED) { michael@0: JS_ReportError(aCx, "Typed array data is not octets"); michael@0: return false; michael@0: } michael@0: michael@0: size = JS_GetTypedArrayByteLength(obj); michael@0: data = JS_GetArrayBufferViewData(obj); michael@0: } else { michael@0: JS_ReportError(aCx, michael@0: "Incorrect argument. Expecting a string or a typed array"); michael@0: return false; michael@0: } michael@0: michael@0: UnixSocketRawData* raw = new UnixSocketRawData(data, size); michael@0: michael@0: nsRefPtr task = michael@0: new SendRilSocketDataTask(clientId, raw); michael@0: NS_DispatchToMainThread(task); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ConnectWorkerToRIL::RunTask(JSContext *aCx) michael@0: { michael@0: // Set up the postRILMessage on the function for worker -> RIL thread michael@0: // communication. michael@0: NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); michael@0: NS_ASSERTION(!JS_IsRunning(aCx), "Are we being called somehow?"); michael@0: JS::Rooted workerGlobal(aCx, JS::CurrentGlobalOrNull(aCx)); michael@0: michael@0: // Check whether |postRILMessage| has been defined. No one but this class michael@0: // should ever define |postRILMessage| in a RIL worker, so we call to michael@0: // |JS_LookupProperty| instead of |JS_GetProperty| here. michael@0: JS::Rooted val(aCx); michael@0: if (!JS_LookupProperty(aCx, workerGlobal, "postRILMessage", &val)) { michael@0: JS_ReportPendingException(aCx); michael@0: return false; michael@0: } michael@0: michael@0: // |JS_LookupProperty| could still return JS_TRUE with an "undefined" michael@0: // |postRILMessage|, so we have to make sure that with an additional call michael@0: // to |JS_TypeOfValue|. michael@0: if (JSTYPE_FUNCTION == JS_TypeOfValue(aCx, val)) { michael@0: return true; michael@0: } michael@0: michael@0: return !!JS_DefineFunction(aCx, workerGlobal, michael@0: "postRILMessage", PostToRIL, 2, 0); michael@0: } michael@0: michael@0: class DispatchRILEvent : public WorkerTask michael@0: { michael@0: public: michael@0: DispatchRILEvent(unsigned long aClient, michael@0: UnixSocketRawData* aMessage) michael@0: : mClientId(aClient) michael@0: , mMessage(aMessage) michael@0: { } michael@0: michael@0: virtual bool RunTask(JSContext *aCx); michael@0: michael@0: private: michael@0: unsigned long mClientId; michael@0: nsAutoPtr mMessage; michael@0: }; michael@0: michael@0: bool michael@0: DispatchRILEvent::RunTask(JSContext *aCx) michael@0: { michael@0: JS::Rooted obj(aCx, JS::CurrentGlobalOrNull(aCx)); michael@0: michael@0: JS::Rooted array(aCx, JS_NewUint8Array(aCx, mMessage->mSize)); michael@0: if (!array) { michael@0: return false; michael@0: } michael@0: memcpy(JS_GetArrayBufferViewData(array), mMessage->mData, mMessage->mSize); michael@0: michael@0: JS::AutoValueArray<2> args(aCx); michael@0: args[0].setNumber((uint32_t)mClientId); michael@0: args[1].setObject(*array); michael@0: michael@0: JS::Rooted rval(aCx); michael@0: return JS_CallFunctionName(aCx, obj, "onRILMessage", args, &rval); michael@0: } michael@0: michael@0: class RilConnector : public mozilla::ipc::UnixSocketConnector michael@0: { michael@0: public: michael@0: RilConnector(unsigned long aClientId) : mClientId(aClientId) michael@0: {} michael@0: michael@0: virtual ~RilConnector() michael@0: {} michael@0: michael@0: virtual int Create(); michael@0: virtual bool CreateAddr(bool aIsServer, michael@0: socklen_t& aAddrSize, michael@0: sockaddr_any& aAddr, michael@0: const char* aAddress); michael@0: virtual bool SetUp(int aFd); michael@0: virtual bool SetUpListenSocket(int aFd); michael@0: virtual void GetSocketAddr(const sockaddr_any& aAddr, michael@0: nsAString& aAddrStr); michael@0: michael@0: private: michael@0: unsigned long mClientId; michael@0: }; michael@0: michael@0: int michael@0: RilConnector::Create() michael@0: { michael@0: MOZ_ASSERT(!NS_IsMainThread()); michael@0: michael@0: int fd = -1; michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: fd = socket(AF_LOCAL, SOCK_STREAM, 0); michael@0: #else michael@0: // If we can't hit a local loopback, fail later in connect. michael@0: fd = socket(AF_INET, SOCK_STREAM, 0); michael@0: #endif michael@0: michael@0: if (fd < 0) { michael@0: NS_WARNING("Could not open ril socket!"); michael@0: return -1; michael@0: } michael@0: michael@0: if (!SetUp(fd)) { michael@0: NS_WARNING("Could not set up socket!"); michael@0: } michael@0: return fd; michael@0: } michael@0: michael@0: bool michael@0: RilConnector::CreateAddr(bool aIsServer, michael@0: socklen_t& aAddrSize, michael@0: sockaddr_any& aAddr, michael@0: const char* aAddress) michael@0: { michael@0: // We never open ril socket as server. michael@0: MOZ_ASSERT(!aIsServer); michael@0: uint32_t af; michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: af = AF_LOCAL; michael@0: #else michael@0: af = AF_INET; michael@0: #endif michael@0: switch (af) { michael@0: case AF_LOCAL: michael@0: aAddr.un.sun_family = af; michael@0: if(strlen(aAddress) > sizeof(aAddr.un.sun_path)) { michael@0: NS_WARNING("Address too long for socket struct!"); michael@0: return false; michael@0: } michael@0: strcpy((char*)&aAddr.un.sun_path, aAddress); michael@0: aAddrSize = strlen(aAddress) + offsetof(struct sockaddr_un, sun_path) + 1; michael@0: break; michael@0: case AF_INET: michael@0: aAddr.in.sin_family = af; michael@0: aAddr.in.sin_port = htons(RIL_TEST_PORT + mClientId); michael@0: aAddr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); michael@0: aAddrSize = sizeof(sockaddr_in); michael@0: break; michael@0: default: michael@0: NS_WARNING("Socket type not handled by connector!"); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: RilConnector::SetUp(int aFd) michael@0: { michael@0: // Nothing to do here. michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: RilConnector::SetUpListenSocket(int aFd) michael@0: { michael@0: // Nothing to do here. michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: RilConnector::GetSocketAddr(const sockaddr_any& aAddr, michael@0: nsAString& aAddrStr) michael@0: { michael@0: MOZ_CRASH("This should never be called!"); michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: namespace mozilla { michael@0: namespace ipc { michael@0: michael@0: RilConsumer::RilConsumer(unsigned long aClientId, michael@0: WorkerCrossThreadDispatcher* aDispatcher) michael@0: : mDispatcher(aDispatcher) michael@0: , mClientId(aClientId) michael@0: , mShutdown(false) michael@0: { michael@0: // Only append client id after RIL_SOCKET_NAME when it's not connected to michael@0: // the first(0) rilproxy for compatibility. michael@0: if (!aClientId) { michael@0: mAddress = RIL_SOCKET_NAME; michael@0: } else { michael@0: struct sockaddr_un addr_un; michael@0: snprintf(addr_un.sun_path, sizeof addr_un.sun_path, "%s%lu", michael@0: RIL_SOCKET_NAME, aClientId); michael@0: mAddress = addr_un.sun_path; michael@0: } michael@0: michael@0: ConnectSocket(new RilConnector(mClientId), mAddress.get()); michael@0: } michael@0: michael@0: nsresult michael@0: RilConsumer::Register(unsigned int aClientId, michael@0: WorkerCrossThreadDispatcher* aDispatcher) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: sRilConsumers.EnsureLengthAtLeast(aClientId + 1); michael@0: michael@0: if (sRilConsumers[aClientId]) { michael@0: NS_WARNING("RilConsumer already registered"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr connection = new ConnectWorkerToRIL(); michael@0: if (!aDispatcher->PostTask(connection)) { michael@0: NS_WARNING("Failed to connect worker to ril"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: // Now that we're set up, connect ourselves to the RIL thread. michael@0: sRilConsumers[aClientId] = new RilConsumer(aClientId, aDispatcher); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: RilConsumer::Shutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: for (unsigned long i = 0; i < sRilConsumers.Length(); i++) { michael@0: nsRefPtr& instance = sRilConsumers[i]; michael@0: if (!instance) { michael@0: continue; michael@0: } michael@0: michael@0: instance->mShutdown = true; michael@0: instance->CloseSocket(); michael@0: instance = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: RilConsumer::ReceiveSocketData(nsAutoPtr& aMessage) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr dre(new DispatchRILEvent(mClientId, aMessage.forget())); michael@0: mDispatcher->PostTask(dre); michael@0: } michael@0: michael@0: void michael@0: RilConsumer::OnConnectSuccess() michael@0: { michael@0: // Nothing to do here. michael@0: CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__); michael@0: } michael@0: michael@0: void michael@0: RilConsumer::OnConnectError() michael@0: { michael@0: CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__); michael@0: CloseSocket(); michael@0: } michael@0: michael@0: void michael@0: RilConsumer::OnDisconnect() michael@0: { michael@0: CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__); michael@0: if (!mShutdown) { michael@0: ConnectSocket(new RilConnector(mClientId), mAddress.get(), michael@0: GetSuggestedConnectDelayMs()); michael@0: } michael@0: } michael@0: michael@0: } // namespace ipc michael@0: } // namespace mozilla