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/. */
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"
25 #include "nsThreadUtils.h" // For NS_IsMainThread.
27 USING_WORKERS_NAMESPACE
28 using namespace mozilla::ipc
;
32 const char* RIL_SOCKET_NAME
= "/dev/socket/rilproxy";
34 // Network port to connect to for adb forwarded sockets when doing
35 // desktop development.
36 const uint32_t RIL_TEST_PORT
= 6200;
38 nsTArray
<nsRefPtr
<mozilla::ipc::RilConsumer
> > sRilConsumers
;
40 class ConnectWorkerToRIL
: public WorkerTask
46 virtual bool RunTask(JSContext
*aCx
);
49 class SendRilSocketDataTask
: public nsRunnable
52 SendRilSocketDataTask(unsigned long aClientId
,
53 UnixSocketRawData
*aRawData
)
55 , mClientId(aClientId
)
60 MOZ_ASSERT(NS_IsMainThread());
62 if (sRilConsumers
.Length() <= mClientId
||
63 !sRilConsumers
[mClientId
] ||
64 sRilConsumers
[mClientId
]->GetConnectionStatus() != SOCKET_CONNECTED
) {
65 // Probably shuting down.
70 sRilConsumers
[mClientId
]->SendSocketData(mRawData
);
75 UnixSocketRawData
*mRawData
;
76 unsigned long mClientId
;
80 PostToRIL(JSContext
*aCx
,
84 JS::CallArgs args
= JS::CallArgsFromVp(aArgc
, aVp
);
85 NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread");
87 if (args
.length() != 2) {
88 JS_ReportError(aCx
, "Expecting two arguments with the RIL message");
92 int clientId
= args
[0].toInt32();
93 JS::Value v
= args
[1];
99 JS::Rooted
<JSString
*> str(aCx
, v
.toString());
100 if (!abs
.encodeUtf8(aCx
, str
)) {
106 } else if (!v
.isPrimitive()) {
107 JSObject
*obj
= v
.toObjectOrNull();
108 if (!JS_IsTypedArrayObject(obj
)) {
109 JS_ReportError(aCx
, "Object passed in wasn't a typed array");
113 uint32_t type
= JS_GetArrayBufferViewType(obj
);
114 if (type
!= js::Scalar::Int8
&&
115 type
!= js::Scalar::Uint8
&&
116 type
!= js::Scalar::Uint8Clamped
) {
117 JS_ReportError(aCx
, "Typed array data is not octets");
121 size
= JS_GetTypedArrayByteLength(obj
);
122 data
= JS_GetArrayBufferViewData(obj
);
125 "Incorrect argument. Expecting a string or a typed array");
129 UnixSocketRawData
* raw
= new UnixSocketRawData(data
, size
);
131 nsRefPtr
<SendRilSocketDataTask
> task
=
132 new SendRilSocketDataTask(clientId
, raw
);
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, 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
);
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
)) {
162 return !!JS_DefineFunction(aCx
, workerGlobal
,
163 "postRILMessage", PostToRIL
, 2, 0);
166 class DispatchRILEvent
: public WorkerTask
169 DispatchRILEvent(unsigned long aClient
,
170 UnixSocketRawData
* aMessage
)
175 virtual bool RunTask(JSContext
*aCx
);
178 unsigned long mClientId
;
179 nsAutoPtr
<UnixSocketRawData
> mMessage
;
183 DispatchRILEvent::RunTask(JSContext
*aCx
)
185 JS::Rooted
<JSObject
*> obj(aCx
, JS::CurrentGlobalOrNull(aCx
));
187 JS::Rooted
<JSObject
*> array(aCx
, JS_NewUint8Array(aCx
, mMessage
->mSize
));
191 memcpy(JS_GetArrayBufferViewData(array
), mMessage
->mData
, mMessage
->mSize
);
193 JS::AutoValueArray
<2> args(aCx
);
194 args
[0].setNumber((uint32_t)mClientId
);
195 args
[1].setObject(*array
);
197 JS::Rooted
<JS::Value
> rval(aCx
);
198 return JS_CallFunctionName(aCx
, obj
, "onRILMessage", args
, &rval
);
201 class RilConnector
: public mozilla::ipc::UnixSocketConnector
204 RilConnector(unsigned long aClientId
) : mClientId(aClientId
)
207 virtual ~RilConnector()
210 virtual int Create();
211 virtual bool CreateAddr(bool aIsServer
,
212 socklen_t
& aAddrSize
,
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
);
221 unsigned long mClientId
;
225 RilConnector::Create()
227 MOZ_ASSERT(!NS_IsMainThread());
231 #if defined(MOZ_WIDGET_GONK)
232 fd
= socket(AF_LOCAL
, SOCK_STREAM
, 0);
234 // If we can't hit a local loopback, fail later in connect.
235 fd
= socket(AF_INET
, SOCK_STREAM
, 0);
239 NS_WARNING("Could not open ril socket!");
244 NS_WARNING("Could not set up socket!");
250 RilConnector::CreateAddr(bool aIsServer
,
251 socklen_t
& aAddrSize
,
253 const char* aAddress
)
255 // We never open ril socket as server.
256 MOZ_ASSERT(!aIsServer
);
258 #if defined(MOZ_WIDGET_GONK)
265 aAddr
.un
.sun_family
= af
;
266 if(strlen(aAddress
) > sizeof(aAddr
.un
.sun_path
)) {
267 NS_WARNING("Address too long for socket struct!");
270 strcpy((char*)&aAddr
.un
.sun_path
, aAddress
);
271 aAddrSize
= strlen(aAddress
) + offsetof(struct sockaddr_un
, sun_path
) + 1;
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
);
280 NS_WARNING("Socket type not handled by connector!");
287 RilConnector::SetUp(int aFd
)
289 // Nothing to do here.
294 RilConnector::SetUpListenSocket(int aFd
)
296 // Nothing to do here.
301 RilConnector::GetSocketAddr(const sockaddr_any
& aAddr
,
304 MOZ_CRASH("This should never be called!");
307 } // anonymous namespace
312 RilConsumer::RilConsumer(unsigned long aClientId
,
313 WorkerCrossThreadDispatcher
* aDispatcher
)
314 : mDispatcher(aDispatcher
)
315 , mClientId(aClientId
)
318 // Only append client id after RIL_SOCKET_NAME when it's not connected to
319 // the first(0) rilproxy for compatibility.
321 mAddress
= RIL_SOCKET_NAME
;
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
;
329 ConnectSocket(new RilConnector(mClientId
), mAddress
.get());
333 RilConsumer::Register(unsigned int aClientId
,
334 WorkerCrossThreadDispatcher
* aDispatcher
)
336 MOZ_ASSERT(NS_IsMainThread());
338 sRilConsumers
.EnsureLengthAtLeast(aClientId
+ 1);
340 if (sRilConsumers
[aClientId
]) {
341 NS_WARNING("RilConsumer already registered");
342 return NS_ERROR_FAILURE
;
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
;
351 // Now that we're set up, connect ourselves to the RIL thread.
352 sRilConsumers
[aClientId
] = new RilConsumer(aClientId
, aDispatcher
);
357 RilConsumer::Shutdown()
359 MOZ_ASSERT(NS_IsMainThread());
361 for (unsigned long i
= 0; i
< sRilConsumers
.Length(); i
++) {
362 nsRefPtr
<RilConsumer
>& instance
= sRilConsumers
[i
];
367 instance
->mShutdown
= true;
368 instance
->CloseSocket();
374 RilConsumer::ReceiveSocketData(nsAutoPtr
<UnixSocketRawData
>& aMessage
)
376 MOZ_ASSERT(NS_IsMainThread());
378 nsRefPtr
<DispatchRILEvent
> dre(new DispatchRILEvent(mClientId
, aMessage
.forget()));
379 mDispatcher
->PostTask(dre
);
383 RilConsumer::OnConnectSuccess()
385 // Nothing to do here.
386 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId
, __FUNCTION__
);
390 RilConsumer::OnConnectError()
392 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId
, __FUNCTION__
);
397 RilConsumer::OnDisconnect()
399 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId
, __FUNCTION__
);
401 ConnectSocket(new RilConnector(mClientId
), mAddress
.get(),
402 GetSuggestedConnectDelayMs());
407 } // namespace mozilla