1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/ril/Ril.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,407 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* vim: set sw=4 ts=8 et ft=cpp: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include "mozilla/ipc/Ril.h" 1.11 + 1.12 +#include <fcntl.h> 1.13 +#include <sys/socket.h> 1.14 +#include <sys/un.h> 1.15 +#include <netdb.h> // For gethostbyname. 1.16 + 1.17 +#undef CHROMIUM_LOG 1.18 +#if defined(MOZ_WIDGET_GONK) 1.19 +#include <android/log.h> 1.20 +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) 1.21 +#else 1.22 +#define CHROMIUM_LOG(args...) printf(args); 1.23 +#endif 1.24 + 1.25 +#include "jsfriendapi.h" 1.26 +#include "mozilla/ArrayUtils.h" 1.27 +#include "nsTArray.h" 1.28 +#include "nsThreadUtils.h" // For NS_IsMainThread. 1.29 + 1.30 +USING_WORKERS_NAMESPACE 1.31 +using namespace mozilla::ipc; 1.32 + 1.33 +namespace { 1.34 + 1.35 +const char* RIL_SOCKET_NAME = "/dev/socket/rilproxy"; 1.36 + 1.37 +// Network port to connect to for adb forwarded sockets when doing 1.38 +// desktop development. 1.39 +const uint32_t RIL_TEST_PORT = 6200; 1.40 + 1.41 +nsTArray<nsRefPtr<mozilla::ipc::RilConsumer> > sRilConsumers; 1.42 + 1.43 +class ConnectWorkerToRIL : public WorkerTask 1.44 +{ 1.45 +public: 1.46 + ConnectWorkerToRIL() 1.47 + { } 1.48 + 1.49 + virtual bool RunTask(JSContext *aCx); 1.50 +}; 1.51 + 1.52 +class SendRilSocketDataTask : public nsRunnable 1.53 +{ 1.54 +public: 1.55 + SendRilSocketDataTask(unsigned long aClientId, 1.56 + UnixSocketRawData *aRawData) 1.57 + : mRawData(aRawData) 1.58 + , mClientId(aClientId) 1.59 + { } 1.60 + 1.61 + NS_IMETHOD Run() 1.62 + { 1.63 + MOZ_ASSERT(NS_IsMainThread()); 1.64 + 1.65 + if (sRilConsumers.Length() <= mClientId || 1.66 + !sRilConsumers[mClientId] || 1.67 + sRilConsumers[mClientId]->GetConnectionStatus() != SOCKET_CONNECTED) { 1.68 + // Probably shuting down. 1.69 + delete mRawData; 1.70 + return NS_OK; 1.71 + } 1.72 + 1.73 + sRilConsumers[mClientId]->SendSocketData(mRawData); 1.74 + return NS_OK; 1.75 + } 1.76 + 1.77 +private: 1.78 + UnixSocketRawData *mRawData; 1.79 + unsigned long mClientId; 1.80 +}; 1.81 + 1.82 +bool 1.83 +PostToRIL(JSContext *aCx, 1.84 + unsigned aArgc, 1.85 + JS::Value *aVp) 1.86 +{ 1.87 + JS::CallArgs args = JS::CallArgsFromVp(aArgc, aVp); 1.88 + NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); 1.89 + 1.90 + if (args.length() != 2) { 1.91 + JS_ReportError(aCx, "Expecting two arguments with the RIL message"); 1.92 + return false; 1.93 + } 1.94 + 1.95 + int clientId = args[0].toInt32(); 1.96 + JS::Value v = args[1]; 1.97 + 1.98 + JSAutoByteString abs; 1.99 + void *data; 1.100 + size_t size; 1.101 + if (JSVAL_IS_STRING(v)) { 1.102 + JS::Rooted<JSString*> str(aCx, v.toString()); 1.103 + if (!abs.encodeUtf8(aCx, str)) { 1.104 + return false; 1.105 + } 1.106 + 1.107 + data = abs.ptr(); 1.108 + size = abs.length(); 1.109 + } else if (!JSVAL_IS_PRIMITIVE(v)) { 1.110 + JSObject *obj = JSVAL_TO_OBJECT(v); 1.111 + if (!JS_IsTypedArrayObject(obj)) { 1.112 + JS_ReportError(aCx, "Object passed in wasn't a typed array"); 1.113 + return false; 1.114 + } 1.115 + 1.116 + uint32_t type = JS_GetArrayBufferViewType(obj); 1.117 + if (type != js::ArrayBufferView::TYPE_INT8 && 1.118 + type != js::ArrayBufferView::TYPE_UINT8 && 1.119 + type != js::ArrayBufferView::TYPE_UINT8_CLAMPED) { 1.120 + JS_ReportError(aCx, "Typed array data is not octets"); 1.121 + return false; 1.122 + } 1.123 + 1.124 + size = JS_GetTypedArrayByteLength(obj); 1.125 + data = JS_GetArrayBufferViewData(obj); 1.126 + } else { 1.127 + JS_ReportError(aCx, 1.128 + "Incorrect argument. Expecting a string or a typed array"); 1.129 + return false; 1.130 + } 1.131 + 1.132 + UnixSocketRawData* raw = new UnixSocketRawData(data, size); 1.133 + 1.134 + nsRefPtr<SendRilSocketDataTask> task = 1.135 + new SendRilSocketDataTask(clientId, raw); 1.136 + NS_DispatchToMainThread(task); 1.137 + return true; 1.138 +} 1.139 + 1.140 +bool 1.141 +ConnectWorkerToRIL::RunTask(JSContext *aCx) 1.142 +{ 1.143 + // Set up the postRILMessage on the function for worker -> RIL thread 1.144 + // communication. 1.145 + NS_ASSERTION(!NS_IsMainThread(), "Expecting to be on the worker thread"); 1.146 + NS_ASSERTION(!JS_IsRunning(aCx), "Are we being called somehow?"); 1.147 + JS::Rooted<JSObject*> workerGlobal(aCx, JS::CurrentGlobalOrNull(aCx)); 1.148 + 1.149 + // Check whether |postRILMessage| has been defined. No one but this class 1.150 + // should ever define |postRILMessage| in a RIL worker, so we call to 1.151 + // |JS_LookupProperty| instead of |JS_GetProperty| here. 1.152 + JS::Rooted<JS::Value> val(aCx); 1.153 + if (!JS_LookupProperty(aCx, workerGlobal, "postRILMessage", &val)) { 1.154 + JS_ReportPendingException(aCx); 1.155 + return false; 1.156 + } 1.157 + 1.158 + // |JS_LookupProperty| could still return JS_TRUE with an "undefined" 1.159 + // |postRILMessage|, so we have to make sure that with an additional call 1.160 + // to |JS_TypeOfValue|. 1.161 + if (JSTYPE_FUNCTION == JS_TypeOfValue(aCx, val)) { 1.162 + return true; 1.163 + } 1.164 + 1.165 + return !!JS_DefineFunction(aCx, workerGlobal, 1.166 + "postRILMessage", PostToRIL, 2, 0); 1.167 +} 1.168 + 1.169 +class DispatchRILEvent : public WorkerTask 1.170 +{ 1.171 +public: 1.172 + DispatchRILEvent(unsigned long aClient, 1.173 + UnixSocketRawData* aMessage) 1.174 + : mClientId(aClient) 1.175 + , mMessage(aMessage) 1.176 + { } 1.177 + 1.178 + virtual bool RunTask(JSContext *aCx); 1.179 + 1.180 +private: 1.181 + unsigned long mClientId; 1.182 + nsAutoPtr<UnixSocketRawData> mMessage; 1.183 +}; 1.184 + 1.185 +bool 1.186 +DispatchRILEvent::RunTask(JSContext *aCx) 1.187 +{ 1.188 + JS::Rooted<JSObject*> obj(aCx, JS::CurrentGlobalOrNull(aCx)); 1.189 + 1.190 + JS::Rooted<JSObject*> array(aCx, JS_NewUint8Array(aCx, mMessage->mSize)); 1.191 + if (!array) { 1.192 + return false; 1.193 + } 1.194 + memcpy(JS_GetArrayBufferViewData(array), mMessage->mData, mMessage->mSize); 1.195 + 1.196 + JS::AutoValueArray<2> args(aCx); 1.197 + args[0].setNumber((uint32_t)mClientId); 1.198 + args[1].setObject(*array); 1.199 + 1.200 + JS::Rooted<JS::Value> rval(aCx); 1.201 + return JS_CallFunctionName(aCx, obj, "onRILMessage", args, &rval); 1.202 +} 1.203 + 1.204 +class RilConnector : public mozilla::ipc::UnixSocketConnector 1.205 +{ 1.206 +public: 1.207 + RilConnector(unsigned long aClientId) : mClientId(aClientId) 1.208 + {} 1.209 + 1.210 + virtual ~RilConnector() 1.211 + {} 1.212 + 1.213 + virtual int Create(); 1.214 + virtual bool CreateAddr(bool aIsServer, 1.215 + socklen_t& aAddrSize, 1.216 + sockaddr_any& aAddr, 1.217 + const char* aAddress); 1.218 + virtual bool SetUp(int aFd); 1.219 + virtual bool SetUpListenSocket(int aFd); 1.220 + virtual void GetSocketAddr(const sockaddr_any& aAddr, 1.221 + nsAString& aAddrStr); 1.222 + 1.223 +private: 1.224 + unsigned long mClientId; 1.225 +}; 1.226 + 1.227 +int 1.228 +RilConnector::Create() 1.229 +{ 1.230 + MOZ_ASSERT(!NS_IsMainThread()); 1.231 + 1.232 + int fd = -1; 1.233 + 1.234 +#if defined(MOZ_WIDGET_GONK) 1.235 + fd = socket(AF_LOCAL, SOCK_STREAM, 0); 1.236 +#else 1.237 + // If we can't hit a local loopback, fail later in connect. 1.238 + fd = socket(AF_INET, SOCK_STREAM, 0); 1.239 +#endif 1.240 + 1.241 + if (fd < 0) { 1.242 + NS_WARNING("Could not open ril socket!"); 1.243 + return -1; 1.244 + } 1.245 + 1.246 + if (!SetUp(fd)) { 1.247 + NS_WARNING("Could not set up socket!"); 1.248 + } 1.249 + return fd; 1.250 +} 1.251 + 1.252 +bool 1.253 +RilConnector::CreateAddr(bool aIsServer, 1.254 + socklen_t& aAddrSize, 1.255 + sockaddr_any& aAddr, 1.256 + const char* aAddress) 1.257 +{ 1.258 + // We never open ril socket as server. 1.259 + MOZ_ASSERT(!aIsServer); 1.260 + uint32_t af; 1.261 +#if defined(MOZ_WIDGET_GONK) 1.262 + af = AF_LOCAL; 1.263 +#else 1.264 + af = AF_INET; 1.265 +#endif 1.266 + switch (af) { 1.267 + case AF_LOCAL: 1.268 + aAddr.un.sun_family = af; 1.269 + if(strlen(aAddress) > sizeof(aAddr.un.sun_path)) { 1.270 + NS_WARNING("Address too long for socket struct!"); 1.271 + return false; 1.272 + } 1.273 + strcpy((char*)&aAddr.un.sun_path, aAddress); 1.274 + aAddrSize = strlen(aAddress) + offsetof(struct sockaddr_un, sun_path) + 1; 1.275 + break; 1.276 + case AF_INET: 1.277 + aAddr.in.sin_family = af; 1.278 + aAddr.in.sin_port = htons(RIL_TEST_PORT + mClientId); 1.279 + aAddr.in.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 1.280 + aAddrSize = sizeof(sockaddr_in); 1.281 + break; 1.282 + default: 1.283 + NS_WARNING("Socket type not handled by connector!"); 1.284 + return false; 1.285 + } 1.286 + return true; 1.287 +} 1.288 + 1.289 +bool 1.290 +RilConnector::SetUp(int aFd) 1.291 +{ 1.292 + // Nothing to do here. 1.293 + return true; 1.294 +} 1.295 + 1.296 +bool 1.297 +RilConnector::SetUpListenSocket(int aFd) 1.298 +{ 1.299 + // Nothing to do here. 1.300 + return true; 1.301 +} 1.302 + 1.303 +void 1.304 +RilConnector::GetSocketAddr(const sockaddr_any& aAddr, 1.305 + nsAString& aAddrStr) 1.306 +{ 1.307 + MOZ_CRASH("This should never be called!"); 1.308 +} 1.309 + 1.310 +} // anonymous namespace 1.311 + 1.312 +namespace mozilla { 1.313 +namespace ipc { 1.314 + 1.315 +RilConsumer::RilConsumer(unsigned long aClientId, 1.316 + WorkerCrossThreadDispatcher* aDispatcher) 1.317 + : mDispatcher(aDispatcher) 1.318 + , mClientId(aClientId) 1.319 + , mShutdown(false) 1.320 +{ 1.321 + // Only append client id after RIL_SOCKET_NAME when it's not connected to 1.322 + // the first(0) rilproxy for compatibility. 1.323 + if (!aClientId) { 1.324 + mAddress = RIL_SOCKET_NAME; 1.325 + } else { 1.326 + struct sockaddr_un addr_un; 1.327 + snprintf(addr_un.sun_path, sizeof addr_un.sun_path, "%s%lu", 1.328 + RIL_SOCKET_NAME, aClientId); 1.329 + mAddress = addr_un.sun_path; 1.330 + } 1.331 + 1.332 + ConnectSocket(new RilConnector(mClientId), mAddress.get()); 1.333 +} 1.334 + 1.335 +nsresult 1.336 +RilConsumer::Register(unsigned int aClientId, 1.337 + WorkerCrossThreadDispatcher* aDispatcher) 1.338 +{ 1.339 + MOZ_ASSERT(NS_IsMainThread()); 1.340 + 1.341 + sRilConsumers.EnsureLengthAtLeast(aClientId + 1); 1.342 + 1.343 + if (sRilConsumers[aClientId]) { 1.344 + NS_WARNING("RilConsumer already registered"); 1.345 + return NS_ERROR_FAILURE; 1.346 + } 1.347 + 1.348 + nsRefPtr<ConnectWorkerToRIL> connection = new ConnectWorkerToRIL(); 1.349 + if (!aDispatcher->PostTask(connection)) { 1.350 + NS_WARNING("Failed to connect worker to ril"); 1.351 + return NS_ERROR_UNEXPECTED; 1.352 + } 1.353 + 1.354 + // Now that we're set up, connect ourselves to the RIL thread. 1.355 + sRilConsumers[aClientId] = new RilConsumer(aClientId, aDispatcher); 1.356 + return NS_OK; 1.357 +} 1.358 + 1.359 +void 1.360 +RilConsumer::Shutdown() 1.361 +{ 1.362 + MOZ_ASSERT(NS_IsMainThread()); 1.363 + 1.364 + for (unsigned long i = 0; i < sRilConsumers.Length(); i++) { 1.365 + nsRefPtr<RilConsumer>& instance = sRilConsumers[i]; 1.366 + if (!instance) { 1.367 + continue; 1.368 + } 1.369 + 1.370 + instance->mShutdown = true; 1.371 + instance->CloseSocket(); 1.372 + instance = nullptr; 1.373 + } 1.374 +} 1.375 + 1.376 +void 1.377 +RilConsumer::ReceiveSocketData(nsAutoPtr<UnixSocketRawData>& aMessage) 1.378 +{ 1.379 + MOZ_ASSERT(NS_IsMainThread()); 1.380 + 1.381 + nsRefPtr<DispatchRILEvent> dre(new DispatchRILEvent(mClientId, aMessage.forget())); 1.382 + mDispatcher->PostTask(dre); 1.383 +} 1.384 + 1.385 +void 1.386 +RilConsumer::OnConnectSuccess() 1.387 +{ 1.388 + // Nothing to do here. 1.389 + CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__); 1.390 +} 1.391 + 1.392 +void 1.393 +RilConsumer::OnConnectError() 1.394 +{ 1.395 + CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__); 1.396 + CloseSocket(); 1.397 +} 1.398 + 1.399 +void 1.400 +RilConsumer::OnDisconnect() 1.401 +{ 1.402 + CHROMIUM_LOG("RIL[%lu]: %s\n", mClientId, __FUNCTION__); 1.403 + if (!mShutdown) { 1.404 + ConnectSocket(new RilConnector(mClientId), mAddress.get(), 1.405 + GetSuggestedConnectDelayMs()); 1.406 + } 1.407 +} 1.408 + 1.409 +} // namespace ipc 1.410 +} // namespace mozilla