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: /* Copyright © 2013, Deutsche Telekom, Inc. */ michael@0: michael@0: #include "mozilla/ipc/Nfc.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #undef CHROMIUM_LOG michael@0: #if (defined(MOZ_WIDGET_GONK) && defined(DEBUG)) 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...) michael@0: #endif michael@0: michael@0: #include "jsfriendapi.h" michael@0: #include "mozilla/ArrayUtils.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* NFC_SOCKET_NAME = "/dev/socket/nfcd"; michael@0: michael@0: // Network port to connect to for adb forwarded sockets when doing michael@0: // desktop development. michael@0: const uint32_t NFC_TEST_PORT = 6400; michael@0: michael@0: nsRefPtr sNfcConsumer; michael@0: michael@0: class ConnectWorkerToNFC : public WorkerTask michael@0: { michael@0: public: michael@0: ConnectWorkerToNFC() michael@0: { } michael@0: michael@0: virtual bool RunTask(JSContext* aCx); michael@0: }; michael@0: michael@0: class SendNfcSocketDataTask : public nsRunnable michael@0: { michael@0: public: michael@0: SendNfcSocketDataTask(UnixSocketRawData* aRawData) michael@0: : mRawData(aRawData) michael@0: { } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!sNfcConsumer || michael@0: sNfcConsumer->GetConnectionStatus() != SOCKET_CONNECTED) { michael@0: // Probably shuting down. michael@0: delete mRawData; michael@0: return NS_OK; michael@0: } michael@0: michael@0: sNfcConsumer->SendSocketData(mRawData); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: UnixSocketRawData* mRawData; michael@0: }; michael@0: michael@0: bool michael@0: PostToNFC(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() != 1) { michael@0: JS_ReportError(aCx, "Expecting one argument with the NFC message"); michael@0: return false; michael@0: } michael@0: michael@0: JS::Value v = args[0]; 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 SendNfcSocketDataTask(raw); michael@0: NS_DispatchToMainThread(task); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ConnectWorkerToNFC::RunTask(JSContext* aCx) michael@0: { michael@0: // Set up the postNFCMessage on the function for worker -> NFC 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: return !!JS_DefineFunction(aCx, workerGlobal, michael@0: "postNfcMessage", PostToNFC, 1, 0); michael@0: } michael@0: michael@0: class DispatchNFCEvent : public WorkerTask michael@0: { michael@0: public: michael@0: DispatchNFCEvent(UnixSocketRawData* aMessage) michael@0: : mMessage(aMessage) michael@0: { } michael@0: michael@0: virtual bool RunTask(JSContext* aCx); michael@0: michael@0: private: michael@0: nsAutoPtr mMessage; michael@0: }; michael@0: michael@0: bool michael@0: DispatchNFCEvent::RunTask(JSContext* aCx) michael@0: { michael@0: JS::Rooted obj(aCx, JS::CurrentGlobalOrNull(aCx)); michael@0: michael@0: JSObject* array = JS_NewUint8Array(aCx, mMessage->mSize); michael@0: if (!array) { michael@0: return false; michael@0: } michael@0: JS::Rooted arrayVal(aCx, JS::ObjectValue(*array)); michael@0: michael@0: memcpy(JS_GetArrayBufferViewData(array), mMessage->mData, mMessage->mSize); michael@0: JS::Rooted rval(aCx); michael@0: return JS_CallFunctionName(aCx, obj, "onNfcMessage", arrayVal, &rval); michael@0: } michael@0: michael@0: class NfcConnector : public mozilla::ipc::UnixSocketConnector michael@0: { michael@0: public: michael@0: NfcConnector() michael@0: {} michael@0: michael@0: virtual ~NfcConnector() 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: michael@0: int michael@0: NfcConnector::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 nfc 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: NfcConnector::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 nfc 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(NFC_TEST_PORT); 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: NfcConnector::SetUp(int aFd) michael@0: { michael@0: // Nothing to do here. michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: NfcConnector::SetUpListenSocket(int aFd) michael@0: { michael@0: // Nothing to do here. michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: NfcConnector::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: NfcConsumer::NfcConsumer(WorkerCrossThreadDispatcher* aDispatcher) michael@0: : mDispatcher(aDispatcher) michael@0: , mShutdown(false) michael@0: { michael@0: mAddress = NFC_SOCKET_NAME; michael@0: michael@0: ConnectSocket(new NfcConnector(), mAddress.get()); michael@0: } michael@0: michael@0: nsresult michael@0: NfcConsumer::Register(WorkerCrossThreadDispatcher* aDispatcher) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (sNfcConsumer) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsRefPtr connection = new ConnectWorkerToNFC(); michael@0: if (!aDispatcher->PostTask(connection)) { michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: // Now that we're set up, connect ourselves to the NFC thread. michael@0: sNfcConsumer = new NfcConsumer(aDispatcher); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: NfcConsumer::Shutdown() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (sNfcConsumer) { michael@0: sNfcConsumer->mShutdown = true; michael@0: sNfcConsumer->CloseSocket(); michael@0: sNfcConsumer = nullptr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: NfcConsumer::ReceiveSocketData(nsAutoPtr& aMessage) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr dre(new DispatchNFCEvent(aMessage.forget())); michael@0: mDispatcher->PostTask(dre); michael@0: } michael@0: michael@0: void michael@0: NfcConsumer::OnConnectSuccess() michael@0: { michael@0: // Nothing to do here. michael@0: CHROMIUM_LOG("NFC: %s\n", __FUNCTION__); michael@0: } michael@0: michael@0: void michael@0: NfcConsumer::OnConnectError() michael@0: { michael@0: CHROMIUM_LOG("NFC: %s\n", __FUNCTION__); michael@0: CloseSocket(); michael@0: } michael@0: michael@0: void michael@0: NfcConsumer::OnDisconnect() michael@0: { michael@0: CHROMIUM_LOG("NFC: %s\n", __FUNCTION__); michael@0: if (!mShutdown) { michael@0: ConnectSocket(new NfcConnector(), mAddress.get(), michael@0: GetSuggestedConnectDelayMs()); michael@0: } michael@0: } michael@0: michael@0: } // namespace ipc michael@0: } // namespace mozilla