1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/nfc/Nfc.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,356 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set sw=4 ts=8 et ft=cpp: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* Copyright © 2013, Deutsche Telekom, Inc. */ 1.11 + 1.12 +#include "mozilla/ipc/Nfc.h" 1.13 + 1.14 +#include <fcntl.h> 1.15 +#include <sys/socket.h> 1.16 +#include <sys/un.h> 1.17 + 1.18 +#undef CHROMIUM_LOG 1.19 +#if (defined(MOZ_WIDGET_GONK) && defined(DEBUG)) 1.20 +#include <android/log.h> 1.21 +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) 1.22 +#else 1.23 +#define CHROMIUM_LOG(args...) 1.24 +#endif 1.25 + 1.26 +#include "jsfriendapi.h" 1.27 +#include "mozilla/ArrayUtils.h" 1.28 +#include "nsThreadUtils.h" // For NS_IsMainThread. 1.29 + 1.30 +USING_WORKERS_NAMESPACE 1.31 +using namespace mozilla::ipc; 1.32 + 1.33 +namespace { 1.34 + 1.35 +const char* NFC_SOCKET_NAME = "/dev/socket/nfcd"; 1.36 + 1.37 +// Network port to connect to for adb forwarded sockets when doing 1.38 +// desktop development. 1.39 +const uint32_t NFC_TEST_PORT = 6400; 1.40 + 1.41 +nsRefPtr<mozilla::ipc::NfcConsumer> sNfcConsumer; 1.42 + 1.43 +class ConnectWorkerToNFC : public WorkerTask 1.44 +{ 1.45 +public: 1.46 + ConnectWorkerToNFC() 1.47 + { } 1.48 + 1.49 + virtual bool RunTask(JSContext* aCx); 1.50 +}; 1.51 + 1.52 +class SendNfcSocketDataTask : public nsRunnable 1.53 +{ 1.54 +public: 1.55 + SendNfcSocketDataTask(UnixSocketRawData* aRawData) 1.56 + : mRawData(aRawData) 1.57 + { } 1.58 + 1.59 + NS_IMETHOD Run() 1.60 + { 1.61 + MOZ_ASSERT(NS_IsMainThread()); 1.62 + 1.63 + if (!sNfcConsumer || 1.64 + sNfcConsumer->GetConnectionStatus() != SOCKET_CONNECTED) { 1.65 + // Probably shuting down. 1.66 + delete mRawData; 1.67 + return NS_OK; 1.68 + } 1.69 + 1.70 + sNfcConsumer->SendSocketData(mRawData); 1.71 + return NS_OK; 1.72 + } 1.73 + 1.74 +private: 1.75 + UnixSocketRawData* mRawData; 1.76 +}; 1.77 + 1.78 +bool 1.79 +PostToNFC(JSContext* aCx, 1.80 + unsigned aArgc, 1.81 + JS::Value* aVp) 1.82 +{ 1.83 + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); 1.84 + NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); 1.85 + 1.86 + if (args.length() != 1) { 1.87 + JS_ReportError(aCx, "Expecting one argument with the NFC message"); 1.88 + return false; 1.89 + } 1.90 + 1.91 + JS::Value v = args[0]; 1.92 + 1.93 + JSAutoByteString abs; 1.94 + void* data; 1.95 + size_t size; 1.96 + if (JSVAL_IS_STRING(v)) { 1.97 + JS::Rooted<JSString*> str(aCx, v.toString()); 1.98 + if (!abs.encodeUtf8(aCx, str)) { 1.99 + return false; 1.100 + } 1.101 + 1.102 + data = abs.ptr(); 1.103 + size = abs.length(); 1.104 + } else if (!JSVAL_IS_PRIMITIVE(v)) { 1.105 + JSObject* obj = JSVAL_TO_OBJECT(v); 1.106 + if (!JS_IsTypedArrayObject(obj)) { 1.107 + JS_ReportError(aCx, "Object passed in wasn't a typed array"); 1.108 + return false; 1.109 + } 1.110 + 1.111 + uint32_t type = JS_GetArrayBufferViewType(obj); 1.112 + if (type != js::ArrayBufferView::TYPE_INT8 && 1.113 + type != js::ArrayBufferView::TYPE_UINT8 && 1.114 + type != js::ArrayBufferView::TYPE_UINT8_CLAMPED) { 1.115 + JS_ReportError(aCx, "Typed array data is not octets"); 1.116 + return false; 1.117 + } 1.118 + 1.119 + size = JS_GetTypedArrayByteLength(obj); 1.120 + data = JS_GetArrayBufferViewData(obj); 1.121 + } else { 1.122 + JS_ReportError(aCx, 1.123 + "Incorrect argument. Expecting a string or a typed array"); 1.124 + return false; 1.125 + } 1.126 + 1.127 + UnixSocketRawData* raw = new UnixSocketRawData(data, size); 1.128 + 1.129 + nsRefPtr<SendNfcSocketDataTask> task = 1.130 + new SendNfcSocketDataTask(raw); 1.131 + NS_DispatchToMainThread(task); 1.132 + return true; 1.133 +} 1.134 + 1.135 +bool 1.136 +ConnectWorkerToNFC::RunTask(JSContext* aCx) 1.137 +{ 1.138 + // Set up the postNFCMessage on the function for worker -> NFC thread 1.139 + // communication. 1.140 + NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); 1.141 + NS_ASSERTION(!JS_IsRunning(aCx), "Are we being called somehow?"); 1.142 + JS::Rooted<JSObject*> workerGlobal(aCx, JS::CurrentGlobalOrNull(aCx)); 1.143 + 1.144 + return !!JS_DefineFunction(aCx, workerGlobal, 1.145 + "postNfcMessage", PostToNFC, 1, 0); 1.146 +} 1.147 + 1.148 +class DispatchNFCEvent : public WorkerTask 1.149 +{ 1.150 +public: 1.151 + DispatchNFCEvent(UnixSocketRawData* aMessage) 1.152 + : mMessage(aMessage) 1.153 + { } 1.154 + 1.155 + virtual bool RunTask(JSContext* aCx); 1.156 + 1.157 +private: 1.158 + nsAutoPtr<UnixSocketRawData> mMessage; 1.159 +}; 1.160 + 1.161 +bool 1.162 +DispatchNFCEvent::RunTask(JSContext* aCx) 1.163 +{ 1.164 + JS::Rooted<JSObject*> obj(aCx, JS::CurrentGlobalOrNull(aCx)); 1.165 + 1.166 + JSObject* array = JS_NewUint8Array(aCx, mMessage->mSize); 1.167 + if (!array) { 1.168 + return false; 1.169 + } 1.170 + JS::Rooted<JS::Value> arrayVal(aCx, JS::ObjectValue(*array)); 1.171 + 1.172 + memcpy(JS_GetArrayBufferViewData(array), mMessage->mData, mMessage->mSize); 1.173 + JS::Rooted<JS::Value> rval(aCx); 1.174 + return JS_CallFunctionName(aCx, obj, "onNfcMessage", arrayVal, &rval); 1.175 +} 1.176 + 1.177 +class NfcConnector : public mozilla::ipc::UnixSocketConnector 1.178 +{ 1.179 +public: 1.180 + NfcConnector() 1.181 + {} 1.182 + 1.183 + virtual ~NfcConnector() 1.184 + {} 1.185 + 1.186 + virtual int Create(); 1.187 + virtual bool CreateAddr(bool aIsServer, 1.188 + socklen_t& aAddrSize, 1.189 + sockaddr_any& aAddr, 1.190 + const char* aAddress); 1.191 + virtual bool SetUp(int aFd); 1.192 + virtual bool SetUpListenSocket(int aFd); 1.193 + virtual void GetSocketAddr(const sockaddr_any& aAddr, 1.194 + nsAString& aAddrStr); 1.195 +}; 1.196 + 1.197 +int 1.198 +NfcConnector::Create() 1.199 +{ 1.200 + MOZ_ASSERT(!NS_IsMainThread()); 1.201 + 1.202 + int fd = -1; 1.203 + 1.204 +#if defined(MOZ_WIDGET_GONK) 1.205 + fd = socket(AF_LOCAL, SOCK_STREAM, 0); 1.206 +#else 1.207 + // If we can't hit a local loopback, fail later in connect. 1.208 + fd = socket(AF_INET, SOCK_STREAM, 0); 1.209 +#endif 1.210 + 1.211 + if (fd < 0) { 1.212 + NS_WARNING("Could not open nfc socket!"); 1.213 + return -1; 1.214 + } 1.215 + 1.216 + if (!SetUp(fd)) { 1.217 + NS_WARNING("Could not set up socket!"); 1.218 + } 1.219 + return fd; 1.220 +} 1.221 + 1.222 +bool 1.223 +NfcConnector::CreateAddr(bool aIsServer, 1.224 + socklen_t& aAddrSize, 1.225 + sockaddr_any& aAddr, 1.226 + const char* aAddress) 1.227 +{ 1.228 + // We never open nfc socket as server. 1.229 + MOZ_ASSERT(!aIsServer); 1.230 + uint32_t af; 1.231 +#if defined(MOZ_WIDGET_GONK) 1.232 + af = AF_LOCAL; 1.233 +#else 1.234 + af = AF_INET; 1.235 +#endif 1.236 + switch (af) { 1.237 + case AF_LOCAL: 1.238 + aAddr.un.sun_family = af; 1.239 + if(strlen(aAddress) > sizeof(aAddr.un.sun_path)) { 1.240 + NS_WARNING("Address too long for socket struct!"); 1.241 + return false; 1.242 + } 1.243 + strcpy((char*)&aAddr.un.sun_path, aAddress); 1.244 + aAddrSize = strlen(aAddress) + offsetof(struct sockaddr_un, sun_path) + 1; 1.245 + break; 1.246 + case AF_INET: 1.247 + aAddr.in.sin_family = af; 1.248 + aAddr.in.sin_port = htons(NFC_TEST_PORT); 1.249 + aAddr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1.250 + aAddrSize = sizeof(sockaddr_in); 1.251 + break; 1.252 + default: 1.253 + NS_WARNING("Socket type not handled by connector!"); 1.254 + return false; 1.255 + } 1.256 + return true; 1.257 +} 1.258 + 1.259 +bool 1.260 +NfcConnector::SetUp(int aFd) 1.261 +{ 1.262 + // Nothing to do here. 1.263 + return true; 1.264 +} 1.265 + 1.266 +bool 1.267 +NfcConnector::SetUpListenSocket(int aFd) 1.268 +{ 1.269 + // Nothing to do here. 1.270 + return true; 1.271 +} 1.272 + 1.273 +void 1.274 +NfcConnector::GetSocketAddr(const sockaddr_any& aAddr, 1.275 + nsAString& aAddrStr) 1.276 +{ 1.277 + MOZ_CRASH("This should never be called!"); 1.278 +} 1.279 + 1.280 +} // anonymous namespace 1.281 + 1.282 +namespace mozilla { 1.283 +namespace ipc { 1.284 + 1.285 +NfcConsumer::NfcConsumer(WorkerCrossThreadDispatcher* aDispatcher) 1.286 + : mDispatcher(aDispatcher) 1.287 + , mShutdown(false) 1.288 +{ 1.289 + mAddress = NFC_SOCKET_NAME; 1.290 + 1.291 + ConnectSocket(new NfcConnector(), mAddress.get()); 1.292 +} 1.293 + 1.294 +nsresult 1.295 +NfcConsumer::Register(WorkerCrossThreadDispatcher* aDispatcher) 1.296 +{ 1.297 + MOZ_ASSERT(NS_IsMainThread()); 1.298 + 1.299 + if (sNfcConsumer) { 1.300 + return NS_ERROR_FAILURE; 1.301 + } 1.302 + 1.303 + nsRefPtr<ConnectWorkerToNFC> connection = new ConnectWorkerToNFC(); 1.304 + if (!aDispatcher->PostTask(connection)) { 1.305 + return NS_ERROR_UNEXPECTED; 1.306 + } 1.307 + 1.308 + // Now that we're set up, connect ourselves to the NFC thread. 1.309 + sNfcConsumer = new NfcConsumer(aDispatcher); 1.310 + return NS_OK; 1.311 +} 1.312 + 1.313 +void 1.314 +NfcConsumer::Shutdown() 1.315 +{ 1.316 + MOZ_ASSERT(NS_IsMainThread()); 1.317 + 1.318 + if (sNfcConsumer) { 1.319 + sNfcConsumer->mShutdown = true; 1.320 + sNfcConsumer->CloseSocket(); 1.321 + sNfcConsumer = nullptr; 1.322 + } 1.323 +} 1.324 + 1.325 +void 1.326 +NfcConsumer::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage) 1.327 +{ 1.328 + MOZ_ASSERT(NS_IsMainThread()); 1.329 + 1.330 + nsRefPtr<DispatchNFCEvent> dre(new DispatchNFCEvent(aMessage.forget())); 1.331 + mDispatcher->PostTask(dre); 1.332 +} 1.333 + 1.334 +void 1.335 +NfcConsumer::OnConnectSuccess() 1.336 +{ 1.337 + // Nothing to do here. 1.338 + CHROMIUM_LOG("NFC: %s\n", __FUNCTION__); 1.339 +} 1.340 + 1.341 +void 1.342 +NfcConsumer::OnConnectError() 1.343 +{ 1.344 + CHROMIUM_LOG("NFC: %s\n", __FUNCTION__); 1.345 + CloseSocket(); 1.346 +} 1.347 + 1.348 +void 1.349 +NfcConsumer::OnDisconnect() 1.350 +{ 1.351 + CHROMIUM_LOG("NFC: %s\n", __FUNCTION__); 1.352 + if (!mShutdown) { 1.353 + ConnectSocket(new NfcConnector(), mAddress.get(), 1.354 + GetSuggestedConnectDelayMs()); 1.355 + } 1.356 +} 1.357 + 1.358 +} // namespace ipc 1.359 +} // namespace mozilla