Bumping manifests a=b2g-bump
[gecko.git] / ipc / ril / Ril.cpp
blobce518a47494c6b25bf41d431a864c521df1ff5ef
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"
9 #include <fcntl.h>
10 #include <sys/socket.h>
11 #include <sys/un.h>
12 #include <netdb.h> // For gethostbyname.
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
22 #include "jsfriendapi.h"
23 #include "mozilla/ArrayUtils.h"
24 #include "nsTArray.h"
25 #include "nsThreadUtils.h" // For NS_IsMainThread.
27 USING_WORKERS_NAMESPACE
28 using namespace mozilla::ipc;
30 namespace {
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
42 public:
43 ConnectWorkerToRIL()
44 { }
46 virtual bool RunTask(JSContext *aCx);
49 class SendRilSocketDataTask : public nsRunnable
51 public:
52 SendRilSocketDataTask(unsigned long aClientId,
53 UnixSocketRawData *aRawData)
54 : mRawData(aRawData)
55 , mClientId(aClientId)
56 { }
58 NS_IMETHOD Run()
60 MOZ_ASSERT(NS_IsMainThread());
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;
70 sRilConsumers[mClientId]->SendSocketData(mRawData);
71 return NS_OK;
74 private:
75 UnixSocketRawData *mRawData;
76 unsigned long mClientId;
79 bool
80 PostToRIL(JSContext *aCx,
81 unsigned aArgc,
82 JS::Value *aVp)
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");
89 return false;
92 int clientId = args[0].toInt32();
93 JS::Value v = args[1];
95 JSAutoByteString abs;
96 void *data;
97 size_t size;
98 if (v.isString()) {
99 JS::Rooted<JSString*> str(aCx, v.toString());
100 if (!abs.encodeUtf8(aCx, str)) {
101 return false;
104 data = abs.ptr();
105 size = abs.length();
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");
110 return false;
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");
118 return false;
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;
129 UnixSocketRawData* raw = new UnixSocketRawData(data, size);
131 nsRefPtr<SendRilSocketDataTask> task =
132 new SendRilSocketDataTask(clientId, raw);
133 NS_DispatchToMainThread(task);
134 return true;
137 bool
138 ConnectWorkerToRIL::RunTask(JSContext *aCx)
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));
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;
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;
162 return !!JS_DefineFunction(aCx, workerGlobal,
163 "postRILMessage", PostToRIL, 2, 0);
166 class DispatchRILEvent : public WorkerTask
168 public:
169 DispatchRILEvent(unsigned long aClient,
170 UnixSocketRawData* aMessage)
171 : mClientId(aClient)
172 , mMessage(aMessage)
175 virtual bool RunTask(JSContext *aCx);
177 private:
178 unsigned long mClientId;
179 nsAutoPtr<UnixSocketRawData> mMessage;
182 bool
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));
188 if (!array) {
189 return false;
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
203 public:
204 RilConnector(unsigned long aClientId) : mClientId(aClientId)
207 virtual ~RilConnector()
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);
220 private:
221 unsigned long mClientId;
225 RilConnector::Create()
227 MOZ_ASSERT(!NS_IsMainThread());
229 int fd = -1;
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
238 if (fd < 0) {
239 NS_WARNING("Could not open ril socket!");
240 return -1;
243 if (!SetUp(fd)) {
244 NS_WARNING("Could not set up socket!");
246 return fd;
249 bool
250 RilConnector::CreateAddr(bool aIsServer,
251 socklen_t& aAddrSize,
252 sockaddr_any& aAddr,
253 const char* aAddress)
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;
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;
283 return true;
286 bool
287 RilConnector::SetUp(int aFd)
289 // Nothing to do here.
290 return true;
293 bool
294 RilConnector::SetUpListenSocket(int aFd)
296 // Nothing to do here.
297 return true;
300 void
301 RilConnector::GetSocketAddr(const sockaddr_any& aAddr,
302 nsAString& aAddrStr)
304 MOZ_CRASH("This should never be called!");
307 } // anonymous namespace
309 namespace mozilla {
310 namespace ipc {
312 RilConsumer::RilConsumer(unsigned long aClientId,
313 WorkerCrossThreadDispatcher* aDispatcher)
314 : mDispatcher(aDispatcher)
315 , mClientId(aClientId)
316 , mShutdown(false)
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;
329 ConnectSocket(new RilConnector(mClientId), mAddress.get());
332 nsresult
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);
353 return NS_OK;
356 void
357 RilConsumer::Shutdown()
359 MOZ_ASSERT(NS_IsMainThread());
361 for (unsigned long i = 0; i < sRilConsumers.Length(); i++) {
362 nsRefPtr<RilConsumer>& instance = sRilConsumers[i];
363 if (!instance) {
364 continue;
367 instance->mShutdown = true;
368 instance->CloseSocket();
369 instance = nullptr;
373 void
374 RilConsumer::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage)
376 MOZ_ASSERT(NS_IsMainThread());
378 nsRefPtr<DispatchRILEvent> dre(new DispatchRILEvent(mClientId, aMessage.forget()));
379 mDispatcher->PostTask(dre);
382 void
383 RilConsumer::OnConnectSuccess()
385 // Nothing to do here.
386 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__);
389 void
390 RilConsumer::OnConnectError()
392 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__);
393 CloseSocket();
396 void
397 RilConsumer::OnDisconnect()
399 CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__);
400 if (!mShutdown) {
401 ConnectSocket(new RilConnector(mClientId), mAddress.get(),
402 GetSuggestedConnectDelayMs());
406 } // namespace ipc
407 } // namespace mozilla