1 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
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/. */
7 #include "mozilla/ipc/Ril.h"
10 #include <sys/socket.h>
12 #include <netdb.h> // For gethostbyname.
15 #if defined(MOZ_WIDGET_GONK)
16 #include <android/log.h>
17 #define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
19 #define CHROMIUM_LOG(args...) printf(args);
22 #include "jsfriendapi.h"
23 #include "mozilla/ArrayUtils.h"
24 #include "mozilla/ipc/UnixSocketConnector.h"
26 #include "nsThreadUtils.h" // For NS_IsMainThread.
28 USING_WORKERS_NAMESPACE
29 using namespace mozilla::ipc
;
33 static const char RIL_SOCKET_NAME
[] = "/dev/socket/rilproxy";
35 // Network port to connect to for adb forwarded sockets when doing
36 // desktop development.
37 static const uint32_t RIL_TEST_PORT
= 6200;
39 static nsTArray
<nsRefPtr
<mozilla::ipc::RilConsumer
> > sRilConsumers
;
41 class ConnectWorkerToRIL MOZ_FINAL
: public WorkerTask
44 bool RunTask(JSContext
* aCx
) MOZ_OVERRIDE
;
47 class SendRilSocketDataTask MOZ_FINAL
: public nsRunnable
50 SendRilSocketDataTask(unsigned long aClientId
,
51 UnixSocketRawData
* aRawData
)
53 , mClientId(aClientId
)
56 NS_IMETHOD
Run() MOZ_OVERRIDE
58 MOZ_ASSERT(NS_IsMainThread());
60 if (sRilConsumers
.Length() <= mClientId
||
61 !sRilConsumers
[mClientId
] ||
62 sRilConsumers
[mClientId
]->GetConnectionStatus() != SOCKET_CONNECTED
) {
63 // Probably shuting down.
68 sRilConsumers
[mClientId
]->SendSocketData(mRawData
);
73 UnixSocketRawData
* mRawData
;
74 unsigned long mClientId
;
78 PostToRIL(JSContext
* aCx
, unsigned aArgc
, JS::Value
* aVp
)
80 JS::CallArgs args
= JS::CallArgsFromVp(aArgc
, aVp
);
81 NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread");
83 if (args
.length() != 2) {
84 JS_ReportError(aCx
, "Expecting two arguments with the RIL message");
88 int clientId
= args
[0].toInt32();
89 JS::Value v
= args
[1];
91 UnixSocketRawData
* raw
= nullptr;
95 JS::Rooted
<JSString
*> str(aCx
, v
.toString());
96 if (!abs
.encodeUtf8(aCx
, str
)) {
100 raw
= new UnixSocketRawData(abs
.ptr(), abs
.length());
101 } else if (!v
.isPrimitive()) {
102 JSObject
* obj
= v
.toObjectOrNull();
103 if (!JS_IsTypedArrayObject(obj
)) {
104 JS_ReportError(aCx
, "Object passed in wasn't a typed array");
108 uint32_t type
= JS_GetArrayBufferViewType(obj
);
109 if (type
!= js::Scalar::Int8
&&
110 type
!= js::Scalar::Uint8
&&
111 type
!= js::Scalar::Uint8Clamped
) {
112 JS_ReportError(aCx
, "Typed array data is not octets");
116 JS::AutoCheckCannotGC nogc
;
117 size_t size
= JS_GetTypedArrayByteLength(obj
);
118 void* data
= JS_GetArrayBufferViewData(obj
, nogc
);
119 raw
= new UnixSocketRawData(data
, size
);
122 aCx
, "Incorrect argument. Expecting a string or a typed array");
127 JS_ReportError(aCx
, "Unable to post to RIL");
131 nsRefPtr
<SendRilSocketDataTask
> task
= new SendRilSocketDataTask(clientId
,
133 NS_DispatchToMainThread(task
);
138 ConnectWorkerToRIL::RunTask(JSContext
* aCx
)
140 // Set up the postRILMessage on the function for worker -> RIL thread
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
));
146 // Check whether |postRILMessage| has been defined. No one but this class
147 // should ever define |postRILMessage| in a RIL worker.
148 JS::Rooted
<JS::Value
> val(aCx
);
149 if (!JS_GetProperty(aCx
, workerGlobal
, "postRILMessage", &val
)) {
150 JS_ReportPendingException(aCx
);
154 // Make sure that |postRILMessage| is a function.
155 if (JSTYPE_FUNCTION
== JS_TypeOfValue(aCx
, val
)) {
159 return !!JS_DefineFunction(aCx
, workerGlobal
, "postRILMessage",
163 class DispatchRILEvent MOZ_FINAL
: public WorkerTask
166 DispatchRILEvent(unsigned long aClient
, UnixSocketRawData
* aMessage
)
171 bool RunTask(JSContext
* aCx
) MOZ_OVERRIDE
;
174 unsigned long mClientId
;
175 nsAutoPtr
<UnixSocketRawData
> mMessage
;
179 DispatchRILEvent::RunTask(JSContext
* aCx
)
181 JS::Rooted
<JSObject
*> obj(aCx
, JS::CurrentGlobalOrNull(aCx
));
183 JS::Rooted
<JSObject
*> array(aCx
,
184 JS_NewUint8Array(aCx
, mMessage
->GetSize()));
189 JS::AutoCheckCannotGC nogc
;
190 memcpy(JS_GetArrayBufferViewData(array
, nogc
),
191 mMessage
->GetData(), mMessage
->GetSize());
194 JS::AutoValueArray
<2> args(aCx
);
195 args
[0].setNumber((uint32_t)mClientId
);
196 args
[1].setObject(*array
);
198 JS::Rooted
<JS::Value
> rval(aCx
);
199 return JS_CallFunctionName(aCx
, obj
, "onRILMessage", args
, &rval
);
202 class RilConnector MOZ_FINAL
: public mozilla::ipc::UnixSocketConnector
205 RilConnector(unsigned long aClientId
)
206 : mClientId(aClientId
)
209 int Create() MOZ_OVERRIDE
;
210 bool CreateAddr(bool aIsServer
,
211 socklen_t
& aAddrSize
,
213 const char* aAddress
) MOZ_OVERRIDE
;
214 bool SetUp(int aFd
) MOZ_OVERRIDE
;
215 bool SetUpListenSocket(int aFd
) MOZ_OVERRIDE
;
216 void GetSocketAddr(const sockaddr_any
& aAddr
,
217 nsAString
& aAddrStr
) MOZ_OVERRIDE
;
220 unsigned long mClientId
;
224 RilConnector::Create()
226 MOZ_ASSERT(!NS_IsMainThread());
230 #if defined(MOZ_WIDGET_GONK)
231 fd
= socket(AF_LOCAL
, SOCK_STREAM
, 0);
233 // If we can't hit a local loopback, fail later in connect.
234 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
238 NS_WARNING("Could not open ril socket!");
243 NS_WARNING("Could not set up socket!");
249 RilConnector::CreateAddr(bool aIsServer
,
250 socklen_t
& aAddrSize
,
252 const char* aAddress
)
254 // We never open ril socket as server.
255 MOZ_ASSERT(!aIsServer
);
257 #if defined(MOZ_WIDGET_GONK)
264 aAddr
.un
.sun_family
= af
;
265 if(strlen(aAddress
) > sizeof(aAddr
.un
.sun_path
)) {
266 NS_WARNING("Address too long for socket struct!");
269 strcpy((char*)&aAddr
.un
.sun_path
, aAddress
);
270 aAddrSize
= strlen(aAddress
) + offsetof(struct sockaddr_un
, sun_path
) + 1;
273 aAddr
.in
.sin_family
= af
;
274 aAddr
.in
.sin_port
= htons(RIL_TEST_PORT
+ mClientId
);
275 aAddr
.in
.sin_addr
.s_addr
= htonl(INADDR_LOOPBACK
);
276 aAddrSize
= sizeof(sockaddr_in
);
279 NS_WARNING("Socket type not handled by connector!");
286 RilConnector::SetUp(int aFd
)
288 // Nothing to do here.
293 RilConnector::SetUpListenSocket(int aFd
)
295 // Nothing to do here.
300 RilConnector::GetSocketAddr(const sockaddr_any
& aAddr
, nsAString
& aAddrStr
)
302 MOZ_CRASH("This should never be called!");
305 } // anonymous namespace
310 RilConsumer::RilConsumer(unsigned long aClientId
,
311 WorkerCrossThreadDispatcher
* aDispatcher
)
312 : mDispatcher(aDispatcher
)
313 , mClientId(aClientId
)
316 // Only append client id after RIL_SOCKET_NAME when it's not connected to
317 // the first(0) rilproxy for compatibility.
319 mAddress
= RIL_SOCKET_NAME
;
321 struct sockaddr_un addr_un
;
322 snprintf(addr_un
.sun_path
, sizeof addr_un
.sun_path
, "%s%lu",
323 RIL_SOCKET_NAME
, aClientId
);
324 mAddress
= addr_un
.sun_path
;
327 Connect(new RilConnector(mClientId
), mAddress
.get());
331 RilConsumer::Register(unsigned int aClientId
,
332 WorkerCrossThreadDispatcher
* aDispatcher
)
334 MOZ_ASSERT(NS_IsMainThread());
336 sRilConsumers
.EnsureLengthAtLeast(aClientId
+ 1);
338 if (sRilConsumers
[aClientId
]) {
339 NS_WARNING("RilConsumer already registered");
340 return NS_ERROR_FAILURE
;
343 nsRefPtr
<ConnectWorkerToRIL
> connection
= new ConnectWorkerToRIL();
344 if (!aDispatcher
->PostTask(connection
)) {
345 NS_WARNING("Failed to connect worker to ril");
346 return NS_ERROR_UNEXPECTED
;
349 // Now that we're set up, connect ourselves to the RIL thread.
350 sRilConsumers
[aClientId
] = new RilConsumer(aClientId
, aDispatcher
);
355 RilConsumer::Shutdown()
357 MOZ_ASSERT(NS_IsMainThread());
359 for (unsigned long i
= 0; i
< sRilConsumers
.Length(); i
++) {
360 nsRefPtr
<RilConsumer
>& instance
= sRilConsumers
[i
];
365 instance
->mShutdown
= true;
372 RilConsumer::ReceiveSocketData(nsAutoPtr
<UnixSocketRawData
>& aMessage
)
374 MOZ_ASSERT(NS_IsMainThread());
376 nsRefPtr
<DispatchRILEvent
> dre(new DispatchRILEvent(mClientId
, aMessage
.forget()));
377 mDispatcher
->PostTask(dre
);
381 RilConsumer::OnConnectSuccess()
383 // Nothing to do here.
384 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId
, __FUNCTION__
);
388 RilConsumer::OnConnectError()
390 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId
, __FUNCTION__
);
395 RilConsumer::OnDisconnect()
397 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId
, __FUNCTION__
);
399 Connect(new RilConnector(mClientId
), mAddress
.get(),
400 GetSuggestedConnectDelayMs());
404 ConnectionOrientedSocketIO
*
407 return PrepareAccept(new RilConnector(mClientId
));
411 } // namespace mozilla