ipc/nfc/Nfc.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:8f8abdda38ae
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set sw=4 ts=8 et ft=cpp: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /* Copyright © 2013, Deutsche Telekom, Inc. */
8
9 #include "mozilla/ipc/Nfc.h"
10
11 #include <fcntl.h>
12 #include <sys/socket.h>
13 #include <sys/un.h>
14
15 #undef CHROMIUM_LOG
16 #if (defined(MOZ_WIDGET_GONK) && defined(DEBUG))
17 #include <android/log.h>
18 #define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
19 #else
20 #define CHROMIUM_LOG(args...)
21 #endif
22
23 #include "jsfriendapi.h"
24 #include "mozilla/ArrayUtils.h"
25 #include "nsThreadUtils.h" // For NS_IsMainThread.
26
27 USING_WORKERS_NAMESPACE
28 using namespace mozilla::ipc;
29
30 namespace {
31
32 const char* NFC_SOCKET_NAME = "/dev/socket/nfcd";
33
34 // Network port to connect to for adb forwarded sockets when doing
35 // desktop development.
36 const uint32_t NFC_TEST_PORT = 6400;
37
38 nsRefPtr<mozilla::ipc::NfcConsumer> sNfcConsumer;
39
40 class ConnectWorkerToNFC : public WorkerTask
41 {
42 public:
43 ConnectWorkerToNFC()
44 { }
45
46 virtual bool RunTask(JSContext* aCx);
47 };
48
49 class SendNfcSocketDataTask : public nsRunnable
50 {
51 public:
52 SendNfcSocketDataTask(UnixSocketRawData* aRawData)
53 : mRawData(aRawData)
54 { }
55
56 NS_IMETHOD Run()
57 {
58 MOZ_ASSERT(NS_IsMainThread());
59
60 if (!sNfcConsumer ||
61 sNfcConsumer->GetConnectionStatus() != SOCKET_CONNECTED) {
62 // Probably shuting down.
63 delete mRawData;
64 return NS_OK;
65 }
66
67 sNfcConsumer->SendSocketData(mRawData);
68 return NS_OK;
69 }
70
71 private:
72 UnixSocketRawData* mRawData;
73 };
74
75 bool
76 PostToNFC(JSContext* aCx,
77 unsigned aArgc,
78 JS::Value* aVp)
79 {
80 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp);
81 NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread");
82
83 if (args.length() != 1) {
84 JS_ReportError(aCx, "Expecting one argument with the NFC message");
85 return false;
86 }
87
88 JS::Value v = args[0];
89
90 JSAutoByteString abs;
91 void* data;
92 size_t size;
93 if (JSVAL_IS_STRING(v)) {
94 JS::Rooted<JSString*> str(aCx, v.toString());
95 if (!abs.encodeUtf8(aCx, str)) {
96 return false;
97 }
98
99 data = abs.ptr();
100 size = abs.length();
101 } else if (!JSVAL_IS_PRIMITIVE(v)) {
102 JSObject* obj = JSVAL_TO_OBJECT(v);
103 if (!JS_IsTypedArrayObject(obj)) {
104 JS_ReportError(aCx, "Object passed in wasn't a typed array");
105 return false;
106 }
107
108 uint32_t type = JS_GetArrayBufferViewType(obj);
109 if (type != js::ArrayBufferView::TYPE_INT8 &&
110 type != js::ArrayBufferView::TYPE_UINT8 &&
111 type != js::ArrayBufferView::TYPE_UINT8_CLAMPED) {
112 JS_ReportError(aCx, "Typed array data is not octets");
113 return false;
114 }
115
116 size = JS_GetTypedArrayByteLength(obj);
117 data = JS_GetArrayBufferViewData(obj);
118 } else {
119 JS_ReportError(aCx,
120 "Incorrect argument. Expecting a string or a typed array");
121 return false;
122 }
123
124 UnixSocketRawData* raw = new UnixSocketRawData(data, size);
125
126 nsRefPtr<SendNfcSocketDataTask> task =
127 new SendNfcSocketDataTask(raw);
128 NS_DispatchToMainThread(task);
129 return true;
130 }
131
132 bool
133 ConnectWorkerToNFC::RunTask(JSContext* aCx)
134 {
135 // Set up the postNFCMessage on the function for worker -> NFC thread
136 // communication.
137 NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread");
138 NS_ASSERTION(!JS_IsRunning(aCx), "Are we being called somehow?");
139 JS::Rooted<JSObject*> workerGlobal(aCx, JS::CurrentGlobalOrNull(aCx));
140
141 return !!JS_DefineFunction(aCx, workerGlobal,
142 "postNfcMessage", PostToNFC, 1, 0);
143 }
144
145 class DispatchNFCEvent : public WorkerTask
146 {
147 public:
148 DispatchNFCEvent(UnixSocketRawData* aMessage)
149 : mMessage(aMessage)
150 { }
151
152 virtual bool RunTask(JSContext* aCx);
153
154 private:
155 nsAutoPtr<UnixSocketRawData> mMessage;
156 };
157
158 bool
159 DispatchNFCEvent::RunTask(JSContext* aCx)
160 {
161 JS::Rooted<JSObject*> obj(aCx, JS::CurrentGlobalOrNull(aCx));
162
163 JSObject* array = JS_NewUint8Array(aCx, mMessage->mSize);
164 if (!array) {
165 return false;
166 }
167 JS::Rooted<JS::Value> arrayVal(aCx, JS::ObjectValue(*array));
168
169 memcpy(JS_GetArrayBufferViewData(array), mMessage->mData, mMessage->mSize);
170 JS::Rooted<JS::Value> rval(aCx);
171 return JS_CallFunctionName(aCx, obj, "onNfcMessage", arrayVal, &rval);
172 }
173
174 class NfcConnector : public mozilla::ipc::UnixSocketConnector
175 {
176 public:
177 NfcConnector()
178 {}
179
180 virtual ~NfcConnector()
181 {}
182
183 virtual int Create();
184 virtual bool CreateAddr(bool aIsServer,
185 socklen_t& aAddrSize,
186 sockaddr_any& aAddr,
187 const char* aAddress);
188 virtual bool SetUp(int aFd);
189 virtual bool SetUpListenSocket(int aFd);
190 virtual void GetSocketAddr(const sockaddr_any& aAddr,
191 nsAString& aAddrStr);
192 };
193
194 int
195 NfcConnector::Create()
196 {
197 MOZ_ASSERT(!NS_IsMainThread());
198
199 int fd = -1;
200
201 #if defined(MOZ_WIDGET_GONK)
202 fd = socket(AF_LOCAL, SOCK_STREAM, 0);
203 #else
204 // If we can't hit a local loopback, fail later in connect.
205 fd = socket(AF_INET, SOCK_STREAM, 0);
206 #endif
207
208 if (fd < 0) {
209 NS_WARNING("Could not open nfc socket!");
210 return -1;
211 }
212
213 if (!SetUp(fd)) {
214 NS_WARNING("Could not set up socket!");
215 }
216 return fd;
217 }
218
219 bool
220 NfcConnector::CreateAddr(bool aIsServer,
221 socklen_t& aAddrSize,
222 sockaddr_any& aAddr,
223 const char* aAddress)
224 {
225 // We never open nfc socket as server.
226 MOZ_ASSERT(!aIsServer);
227 uint32_t af;
228 #if defined(MOZ_WIDGET_GONK)
229 af = AF_LOCAL;
230 #else
231 af = AF_INET;
232 #endif
233 switch (af) {
234 case AF_LOCAL:
235 aAddr.un.sun_family = af;
236 if(strlen(aAddress) > sizeof(aAddr.un.sun_path)) {
237 NS_WARNING("Address too long for socket struct!");
238 return false;
239 }
240 strcpy((char*)&aAddr.un.sun_path, aAddress);
241 aAddrSize = strlen(aAddress) + offsetof(struct sockaddr_un, sun_path) + 1;
242 break;
243 case AF_INET:
244 aAddr.in.sin_family = af;
245 aAddr.in.sin_port = htons(NFC_TEST_PORT);
246 aAddr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
247 aAddrSize = sizeof(sockaddr_in);
248 break;
249 default:
250 NS_WARNING("Socket type not handled by connector!");
251 return false;
252 }
253 return true;
254 }
255
256 bool
257 NfcConnector::SetUp(int aFd)
258 {
259 // Nothing to do here.
260 return true;
261 }
262
263 bool
264 NfcConnector::SetUpListenSocket(int aFd)
265 {
266 // Nothing to do here.
267 return true;
268 }
269
270 void
271 NfcConnector::GetSocketAddr(const sockaddr_any& aAddr,
272 nsAString& aAddrStr)
273 {
274 MOZ_CRASH("This should never be called!");
275 }
276
277 } // anonymous namespace
278
279 namespace mozilla {
280 namespace ipc {
281
282 NfcConsumer::NfcConsumer(WorkerCrossThreadDispatcher* aDispatcher)
283 : mDispatcher(aDispatcher)
284 , mShutdown(false)
285 {
286 mAddress = NFC_SOCKET_NAME;
287
288 ConnectSocket(new NfcConnector(), mAddress.get());
289 }
290
291 nsresult
292 NfcConsumer::Register(WorkerCrossThreadDispatcher* aDispatcher)
293 {
294 MOZ_ASSERT(NS_IsMainThread());
295
296 if (sNfcConsumer) {
297 return NS_ERROR_FAILURE;
298 }
299
300 nsRefPtr<ConnectWorkerToNFC> connection = new ConnectWorkerToNFC();
301 if (!aDispatcher->PostTask(connection)) {
302 return NS_ERROR_UNEXPECTED;
303 }
304
305 // Now that we're set up, connect ourselves to the NFC thread.
306 sNfcConsumer = new NfcConsumer(aDispatcher);
307 return NS_OK;
308 }
309
310 void
311 NfcConsumer::Shutdown()
312 {
313 MOZ_ASSERT(NS_IsMainThread());
314
315 if (sNfcConsumer) {
316 sNfcConsumer->mShutdown = true;
317 sNfcConsumer->CloseSocket();
318 sNfcConsumer = nullptr;
319 }
320 }
321
322 void
323 NfcConsumer::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
324 {
325 MOZ_ASSERT(NS_IsMainThread());
326
327 nsRefPtr<DispatchNFCEvent> dre(new DispatchNFCEvent(aMessage.forget()));
328 mDispatcher->PostTask(dre);
329 }
330
331 void
332 NfcConsumer::OnConnectSuccess()
333 {
334 // Nothing to do here.
335 CHROMIUM_LOG("NFC: %s\n", __FUNCTION__);
336 }
337
338 void
339 NfcConsumer::OnConnectError()
340 {
341 CHROMIUM_LOG("NFC: %s\n", __FUNCTION__);
342 CloseSocket();
343 }
344
345 void
346 NfcConsumer::OnDisconnect()
347 {
348 CHROMIUM_LOG("NFC: %s\n", __FUNCTION__);
349 if (!mShutdown) {
350 ConnectSocket(new NfcConnector(), mAddress.get(),
351 GetSuggestedConnectDelayMs());
352 }
353 }
354
355 } // namespace ipc
356 } // namespace mozilla

mercurial