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