|
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 #include "mozilla/ipc/Ril.h" |
|
8 |
|
9 #include <fcntl.h> |
|
10 #include <sys/socket.h> |
|
11 #include <sys/un.h> |
|
12 #include <netdb.h> // For gethostbyname. |
|
13 |
|
14 #undef CHROMIUM_LOG |
|
15 #if defined(MOZ_WIDGET_GONK) |
|
16 #include <android/log.h> |
|
17 #define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) |
|
18 #else |
|
19 #define CHROMIUM_LOG(args...) printf(args); |
|
20 #endif |
|
21 |
|
22 #include "jsfriendapi.h" |
|
23 #include "mozilla/ArrayUtils.h" |
|
24 #include "nsTArray.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* RIL_SOCKET_NAME = "/dev/socket/rilproxy"; |
|
33 |
|
34 // Network port to connect to for adb forwarded sockets when doing |
|
35 // desktop development. |
|
36 const uint32_t RIL_TEST_PORT = 6200; |
|
37 |
|
38 nsTArray<nsRefPtr<mozilla::ipc::RilConsumer> > sRilConsumers; |
|
39 |
|
40 class ConnectWorkerToRIL : public WorkerTask |
|
41 { |
|
42 public: |
|
43 ConnectWorkerToRIL() |
|
44 { } |
|
45 |
|
46 virtual bool RunTask(JSContext *aCx); |
|
47 }; |
|
48 |
|
49 class SendRilSocketDataTask : public nsRunnable |
|
50 { |
|
51 public: |
|
52 SendRilSocketDataTask(unsigned long aClientId, |
|
53 UnixSocketRawData *aRawData) |
|
54 : mRawData(aRawData) |
|
55 , mClientId(aClientId) |
|
56 { } |
|
57 |
|
58 NS_IMETHOD Run() |
|
59 { |
|
60 MOZ_ASSERT(NS_IsMainThread()); |
|
61 |
|
62 if (sRilConsumers.Length() <= mClientId || |
|
63 !sRilConsumers[mClientId] || |
|
64 sRilConsumers[mClientId]->GetConnectionStatus() != SOCKET_CONNECTED) { |
|
65 // Probably shuting down. |
|
66 delete mRawData; |
|
67 return NS_OK; |
|
68 } |
|
69 |
|
70 sRilConsumers[mClientId]->SendSocketData(mRawData); |
|
71 return NS_OK; |
|
72 } |
|
73 |
|
74 private: |
|
75 UnixSocketRawData *mRawData; |
|
76 unsigned long mClientId; |
|
77 }; |
|
78 |
|
79 bool |
|
80 PostToRIL(JSContext *aCx, |
|
81 unsigned aArgc, |
|
82 JS::Value *aVp) |
|
83 { |
|
84 JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); |
|
85 NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); |
|
86 |
|
87 if (args.length() != 2) { |
|
88 JS_ReportError(aCx, "Expecting two arguments with the RIL message"); |
|
89 return false; |
|
90 } |
|
91 |
|
92 int clientId = args[0].toInt32(); |
|
93 JS::Value v = args[1]; |
|
94 |
|
95 JSAutoByteString abs; |
|
96 void *data; |
|
97 size_t size; |
|
98 if (JSVAL_IS_STRING(v)) { |
|
99 JS::Rooted<JSString*> str(aCx, v.toString()); |
|
100 if (!abs.encodeUtf8(aCx, str)) { |
|
101 return false; |
|
102 } |
|
103 |
|
104 data = abs.ptr(); |
|
105 size = abs.length(); |
|
106 } else if (!JSVAL_IS_PRIMITIVE(v)) { |
|
107 JSObject *obj = JSVAL_TO_OBJECT(v); |
|
108 if (!JS_IsTypedArrayObject(obj)) { |
|
109 JS_ReportError(aCx, "Object passed in wasn't a typed array"); |
|
110 return false; |
|
111 } |
|
112 |
|
113 uint32_t type = JS_GetArrayBufferViewType(obj); |
|
114 if (type != js::ArrayBufferView::TYPE_INT8 && |
|
115 type != js::ArrayBufferView::TYPE_UINT8 && |
|
116 type != js::ArrayBufferView::TYPE_UINT8_CLAMPED) { |
|
117 JS_ReportError(aCx, "Typed array data is not octets"); |
|
118 return false; |
|
119 } |
|
120 |
|
121 size = JS_GetTypedArrayByteLength(obj); |
|
122 data = JS_GetArrayBufferViewData(obj); |
|
123 } else { |
|
124 JS_ReportError(aCx, |
|
125 "Incorrect argument. Expecting a string or a typed array"); |
|
126 return false; |
|
127 } |
|
128 |
|
129 UnixSocketRawData* raw = new UnixSocketRawData(data, size); |
|
130 |
|
131 nsRefPtr<SendRilSocketDataTask> task = |
|
132 new SendRilSocketDataTask(clientId, raw); |
|
133 NS_DispatchToMainThread(task); |
|
134 return true; |
|
135 } |
|
136 |
|
137 bool |
|
138 ConnectWorkerToRIL::RunTask(JSContext *aCx) |
|
139 { |
|
140 // Set up the postRILMessage on the function for worker -> RIL thread |
|
141 // communication. |
|
142 NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); |
|
143 NS_ASSERTION(!JS_IsRunning(aCx), "Are we being called somehow?"); |
|
144 JS::Rooted<JSObject*> workerGlobal(aCx, JS::CurrentGlobalOrNull(aCx)); |
|
145 |
|
146 // Check whether |postRILMessage| has been defined. No one but this class |
|
147 // should ever define |postRILMessage| in a RIL worker, so we call to |
|
148 // |JS_LookupProperty| instead of |JS_GetProperty| here. |
|
149 JS::Rooted<JS::Value> val(aCx); |
|
150 if (!JS_LookupProperty(aCx, workerGlobal, "postRILMessage", &val)) { |
|
151 JS_ReportPendingException(aCx); |
|
152 return false; |
|
153 } |
|
154 |
|
155 // |JS_LookupProperty| could still return JS_TRUE with an "undefined" |
|
156 // |postRILMessage|, so we have to make sure that with an additional call |
|
157 // to |JS_TypeOfValue|. |
|
158 if (JSTYPE_FUNCTION == JS_TypeOfValue(aCx, val)) { |
|
159 return true; |
|
160 } |
|
161 |
|
162 return !!JS_DefineFunction(aCx, workerGlobal, |
|
163 "postRILMessage", PostToRIL, 2, 0); |
|
164 } |
|
165 |
|
166 class DispatchRILEvent : public WorkerTask |
|
167 { |
|
168 public: |
|
169 DispatchRILEvent(unsigned long aClient, |
|
170 UnixSocketRawData* aMessage) |
|
171 : mClientId(aClient) |
|
172 , mMessage(aMessage) |
|
173 { } |
|
174 |
|
175 virtual bool RunTask(JSContext *aCx); |
|
176 |
|
177 private: |
|
178 unsigned long mClientId; |
|
179 nsAutoPtr<UnixSocketRawData> mMessage; |
|
180 }; |
|
181 |
|
182 bool |
|
183 DispatchRILEvent::RunTask(JSContext *aCx) |
|
184 { |
|
185 JS::Rooted<JSObject*> obj(aCx, JS::CurrentGlobalOrNull(aCx)); |
|
186 |
|
187 JS::Rooted<JSObject*> array(aCx, JS_NewUint8Array(aCx, mMessage->mSize)); |
|
188 if (!array) { |
|
189 return false; |
|
190 } |
|
191 memcpy(JS_GetArrayBufferViewData(array), mMessage->mData, mMessage->mSize); |
|
192 |
|
193 JS::AutoValueArray<2> args(aCx); |
|
194 args[0].setNumber((uint32_t)mClientId); |
|
195 args[1].setObject(*array); |
|
196 |
|
197 JS::Rooted<JS::Value> rval(aCx); |
|
198 return JS_CallFunctionName(aCx, obj, "onRILMessage", args, &rval); |
|
199 } |
|
200 |
|
201 class RilConnector : public mozilla::ipc::UnixSocketConnector |
|
202 { |
|
203 public: |
|
204 RilConnector(unsigned long aClientId) : mClientId(aClientId) |
|
205 {} |
|
206 |
|
207 virtual ~RilConnector() |
|
208 {} |
|
209 |
|
210 virtual int Create(); |
|
211 virtual bool CreateAddr(bool aIsServer, |
|
212 socklen_t& aAddrSize, |
|
213 sockaddr_any& aAddr, |
|
214 const char* aAddress); |
|
215 virtual bool SetUp(int aFd); |
|
216 virtual bool SetUpListenSocket(int aFd); |
|
217 virtual void GetSocketAddr(const sockaddr_any& aAddr, |
|
218 nsAString& aAddrStr); |
|
219 |
|
220 private: |
|
221 unsigned long mClientId; |
|
222 }; |
|
223 |
|
224 int |
|
225 RilConnector::Create() |
|
226 { |
|
227 MOZ_ASSERT(!NS_IsMainThread()); |
|
228 |
|
229 int fd = -1; |
|
230 |
|
231 #if defined(MOZ_WIDGET_GONK) |
|
232 fd = socket(AF_LOCAL, SOCK_STREAM, 0); |
|
233 #else |
|
234 // If we can't hit a local loopback, fail later in connect. |
|
235 fd = socket(AF_INET, SOCK_STREAM, 0); |
|
236 #endif |
|
237 |
|
238 if (fd < 0) { |
|
239 NS_WARNING("Could not open ril socket!"); |
|
240 return -1; |
|
241 } |
|
242 |
|
243 if (!SetUp(fd)) { |
|
244 NS_WARNING("Could not set up socket!"); |
|
245 } |
|
246 return fd; |
|
247 } |
|
248 |
|
249 bool |
|
250 RilConnector::CreateAddr(bool aIsServer, |
|
251 socklen_t& aAddrSize, |
|
252 sockaddr_any& aAddr, |
|
253 const char* aAddress) |
|
254 { |
|
255 // We never open ril socket as server. |
|
256 MOZ_ASSERT(!aIsServer); |
|
257 uint32_t af; |
|
258 #if defined(MOZ_WIDGET_GONK) |
|
259 af = AF_LOCAL; |
|
260 #else |
|
261 af = AF_INET; |
|
262 #endif |
|
263 switch (af) { |
|
264 case AF_LOCAL: |
|
265 aAddr.un.sun_family = af; |
|
266 if(strlen(aAddress) > sizeof(aAddr.un.sun_path)) { |
|
267 NS_WARNING("Address too long for socket struct!"); |
|
268 return false; |
|
269 } |
|
270 strcpy((char*)&aAddr.un.sun_path, aAddress); |
|
271 aAddrSize = strlen(aAddress) + offsetof(struct sockaddr_un, sun_path) + 1; |
|
272 break; |
|
273 case AF_INET: |
|
274 aAddr.in.sin_family = af; |
|
275 aAddr.in.sin_port = htons(RIL_TEST_PORT + mClientId); |
|
276 aAddr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
|
277 aAddrSize = sizeof(sockaddr_in); |
|
278 break; |
|
279 default: |
|
280 NS_WARNING("Socket type not handled by connector!"); |
|
281 return false; |
|
282 } |
|
283 return true; |
|
284 } |
|
285 |
|
286 bool |
|
287 RilConnector::SetUp(int aFd) |
|
288 { |
|
289 // Nothing to do here. |
|
290 return true; |
|
291 } |
|
292 |
|
293 bool |
|
294 RilConnector::SetUpListenSocket(int aFd) |
|
295 { |
|
296 // Nothing to do here. |
|
297 return true; |
|
298 } |
|
299 |
|
300 void |
|
301 RilConnector::GetSocketAddr(const sockaddr_any& aAddr, |
|
302 nsAString& aAddrStr) |
|
303 { |
|
304 MOZ_CRASH("This should never be called!"); |
|
305 } |
|
306 |
|
307 } // anonymous namespace |
|
308 |
|
309 namespace mozilla { |
|
310 namespace ipc { |
|
311 |
|
312 RilConsumer::RilConsumer(unsigned long aClientId, |
|
313 WorkerCrossThreadDispatcher* aDispatcher) |
|
314 : mDispatcher(aDispatcher) |
|
315 , mClientId(aClientId) |
|
316 , mShutdown(false) |
|
317 { |
|
318 // Only append client id after RIL_SOCKET_NAME when it's not connected to |
|
319 // the first(0) rilproxy for compatibility. |
|
320 if (!aClientId) { |
|
321 mAddress = RIL_SOCKET_NAME; |
|
322 } else { |
|
323 struct sockaddr_un addr_un; |
|
324 snprintf(addr_un.sun_path, sizeof addr_un.sun_path, "%s%lu", |
|
325 RIL_SOCKET_NAME, aClientId); |
|
326 mAddress = addr_un.sun_path; |
|
327 } |
|
328 |
|
329 ConnectSocket(new RilConnector(mClientId), mAddress.get()); |
|
330 } |
|
331 |
|
332 nsresult |
|
333 RilConsumer::Register(unsigned int aClientId, |
|
334 WorkerCrossThreadDispatcher* aDispatcher) |
|
335 { |
|
336 MOZ_ASSERT(NS_IsMainThread()); |
|
337 |
|
338 sRilConsumers.EnsureLengthAtLeast(aClientId + 1); |
|
339 |
|
340 if (sRilConsumers[aClientId]) { |
|
341 NS_WARNING("RilConsumer already registered"); |
|
342 return NS_ERROR_FAILURE; |
|
343 } |
|
344 |
|
345 nsRefPtr<ConnectWorkerToRIL> connection = new ConnectWorkerToRIL(); |
|
346 if (!aDispatcher->PostTask(connection)) { |
|
347 NS_WARNING("Failed to connect worker to ril"); |
|
348 return NS_ERROR_UNEXPECTED; |
|
349 } |
|
350 |
|
351 // Now that we're set up, connect ourselves to the RIL thread. |
|
352 sRilConsumers[aClientId] = new RilConsumer(aClientId, aDispatcher); |
|
353 return NS_OK; |
|
354 } |
|
355 |
|
356 void |
|
357 RilConsumer::Shutdown() |
|
358 { |
|
359 MOZ_ASSERT(NS_IsMainThread()); |
|
360 |
|
361 for (unsigned long i = 0; i < sRilConsumers.Length(); i++) { |
|
362 nsRefPtr<RilConsumer>& instance = sRilConsumers[i]; |
|
363 if (!instance) { |
|
364 continue; |
|
365 } |
|
366 |
|
367 instance->mShutdown = true; |
|
368 instance->CloseSocket(); |
|
369 instance = nullptr; |
|
370 } |
|
371 } |
|
372 |
|
373 void |
|
374 RilConsumer::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage) |
|
375 { |
|
376 MOZ_ASSERT(NS_IsMainThread()); |
|
377 |
|
378 nsRefPtr<DispatchRILEvent> dre(new DispatchRILEvent(mClientId, aMessage.forget())); |
|
379 mDispatcher->PostTask(dre); |
|
380 } |
|
381 |
|
382 void |
|
383 RilConsumer::OnConnectSuccess() |
|
384 { |
|
385 // Nothing to do here. |
|
386 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__); |
|
387 } |
|
388 |
|
389 void |
|
390 RilConsumer::OnConnectError() |
|
391 { |
|
392 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__); |
|
393 CloseSocket(); |
|
394 } |
|
395 |
|
396 void |
|
397 RilConsumer::OnDisconnect() |
|
398 { |
|
399 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__); |
|
400 if (!mShutdown) { |
|
401 ConnectSocket(new RilConnector(mClientId), mAddress.get(), |
|
402 GetSuggestedConnectDelayMs()); |
|
403 } |
|
404 } |
|
405 |
|
406 } // namespace ipc |
|
407 } // namespace mozilla |