|
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 |