ipc/netd/Netd.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "Netd.h"
michael@0 6 #include <android/log.h>
michael@0 7 #include <cutils/sockets.h>
michael@0 8 #include <fcntl.h>
michael@0 9 #include <sys/socket.h>
michael@0 10
michael@0 11 #include "cutils/properties.h"
michael@0 12 #include "android/log.h"
michael@0 13
michael@0 14 #include "nsWhitespaceTokenizer.h"
michael@0 15 #include "nsXULAppAPI.h"
michael@0 16 #include "nsAutoPtr.h"
michael@0 17 #include "nsString.h"
michael@0 18 #include "nsThreadUtils.h"
michael@0 19 #include "mozilla/RefPtr.h"
michael@0 20
michael@0 21
michael@0 22 #define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
michael@0 23 #define ICS_SYS_USB_RNDIS_MAC "/sys/class/android_usb/android0/f_rndis/ethaddr"
michael@0 24 #define INVALID_SOCKET -1
michael@0 25 #define MAX_RECONNECT_TIMES 10
michael@0 26
michael@0 27 namespace {
michael@0 28
michael@0 29 mozilla::RefPtr<mozilla::ipc::NetdClient> gNetdClient;
michael@0 30 mozilla::RefPtr<mozilla::ipc::NetdConsumer> gNetdConsumer;
michael@0 31
michael@0 32 class StopNetdConsumer : public nsRunnable {
michael@0 33 public:
michael@0 34 NS_IMETHOD Run()
michael@0 35 {
michael@0 36 MOZ_ASSERT(NS_IsMainThread());
michael@0 37
michael@0 38 gNetdConsumer = nullptr;
michael@0 39 return NS_OK;
michael@0 40 }
michael@0 41 };
michael@0 42
michael@0 43 bool
michael@0 44 InitRndisAddress()
michael@0 45 {
michael@0 46 char mac[20];
michael@0 47 char serialno[] = "1234567890ABCDEF";
michael@0 48 static const int kEthernetAddressLength = 6;
michael@0 49 char address[kEthernetAddressLength];
michael@0 50 int i = 0;
michael@0 51 int ret = 0;
michael@0 52 int length = 0;
michael@0 53 mozilla::ScopedClose fd;
michael@0 54
michael@0 55 fd.rwget() = open(ICS_SYS_USB_RNDIS_MAC, O_WRONLY);
michael@0 56 if (fd.rwget() == -1) {
michael@0 57 CHROMIUM_LOG("Unable to open file %s.", ICS_SYS_USB_RNDIS_MAC);
michael@0 58 return false;
michael@0 59 }
michael@0 60
michael@0 61 property_get("ro.serialno", serialno, "1234567890ABCDEF");
michael@0 62
michael@0 63 memset(address, 0, sizeof(address));
michael@0 64 // First byte is 0x02 to signify a locally administered address.
michael@0 65 address[0] = 0x02;
michael@0 66 length = strlen(serialno);
michael@0 67 for (i = 0; i < length; i++) {
michael@0 68 address[i % (kEthernetAddressLength - 1) + 1] ^= serialno[i];
michael@0 69 }
michael@0 70
michael@0 71 sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x",
michael@0 72 address[0], address[1], address[2],
michael@0 73 address[3], address[4], address[5]);
michael@0 74 length = strlen(mac);
michael@0 75 ret = write(fd.get(), mac, length);
michael@0 76 if (ret != length) {
michael@0 77 CHROMIUM_LOG("Fail to write file %s.", ICS_SYS_USB_RNDIS_MAC);
michael@0 78 return false;
michael@0 79 }
michael@0 80 return true;
michael@0 81 }
michael@0 82
michael@0 83 } // anonymous namespace
michael@0 84
michael@0 85 namespace mozilla {
michael@0 86 namespace ipc {
michael@0 87
michael@0 88 NetdClient::NetdClient()
michael@0 89 : LineWatcher('\0', MAX_COMMAND_SIZE)
michael@0 90 , mIOLoop(MessageLoopForIO::current())
michael@0 91 , mSocket(INVALID_SOCKET)
michael@0 92 , mCurrentWriteOffset(0)
michael@0 93 , mReConnectTimes(0)
michael@0 94 {
michael@0 95 MOZ_COUNT_CTOR(NetdClient);
michael@0 96 }
michael@0 97
michael@0 98 NetdClient::~NetdClient()
michael@0 99 {
michael@0 100 MOZ_COUNT_DTOR(NetdClient);
michael@0 101 }
michael@0 102
michael@0 103 bool
michael@0 104 NetdClient::OpenSocket()
michael@0 105 {
michael@0 106 mSocket.rwget() = socket_local_client("netd",
michael@0 107 ANDROID_SOCKET_NAMESPACE_RESERVED,
michael@0 108 SOCK_STREAM);
michael@0 109 if (mSocket.rwget() < 0) {
michael@0 110 CHROMIUM_LOG("Error connecting to : netd (%s) - will retry", strerror(errno));
michael@0 111 return false;
michael@0 112 }
michael@0 113 // Add FD_CLOEXEC flag.
michael@0 114 int flags = fcntl(mSocket.get(), F_GETFD);
michael@0 115 if (flags == -1) {
michael@0 116 CHROMIUM_LOG("Error doing fcntl with F_GETFD command(%s)", strerror(errno));
michael@0 117 return false;
michael@0 118 }
michael@0 119 flags |= FD_CLOEXEC;
michael@0 120 if (fcntl(mSocket.get(), F_SETFD, flags) == -1) {
michael@0 121 CHROMIUM_LOG("Error doing fcntl with F_SETFD command(%s)", strerror(errno));
michael@0 122 return false;
michael@0 123 }
michael@0 124 // Set non-blocking.
michael@0 125 if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) {
michael@0 126 CHROMIUM_LOG("Error set non-blocking socket(%s)", strerror(errno));
michael@0 127 return false;
michael@0 128 }
michael@0 129 if (!MessageLoopForIO::current()->
michael@0 130 WatchFileDescriptor(mSocket.get(),
michael@0 131 true,
michael@0 132 MessageLoopForIO::WATCH_READ,
michael@0 133 &mReadWatcher,
michael@0 134 this)) {
michael@0 135 CHROMIUM_LOG("Error set socket read watcher(%s)", strerror(errno));
michael@0 136 return false;
michael@0 137 }
michael@0 138
michael@0 139 if (!mOutgoingQ.empty()) {
michael@0 140 MessageLoopForIO::current()->
michael@0 141 WatchFileDescriptor(mSocket.get(),
michael@0 142 false,
michael@0 143 MessageLoopForIO::WATCH_WRITE,
michael@0 144 &mWriteWatcher,
michael@0 145 this);
michael@0 146 }
michael@0 147
michael@0 148 CHROMIUM_LOG("Connected to netd");
michael@0 149 return true;
michael@0 150 }
michael@0 151
michael@0 152 void NetdClient::OnLineRead(int aFd, nsDependentCSubstring& aMessage)
michael@0 153 {
michael@0 154 // Set errno to 0 first. For preventing to use the stale version of errno.
michael@0 155 errno = 0;
michael@0 156 // We found a line terminator. Each line is formatted as an
michael@0 157 // integer response code followed by the rest of the line.
michael@0 158 // Fish out the response code.
michael@0 159 int responseCode = strtol(aMessage.Data(), nullptr, 10);
michael@0 160 if (!errno) {
michael@0 161 NetdCommand* response = new NetdCommand();
michael@0 162 // Passing all the response message, including the line terminator.
michael@0 163 response->mSize = aMessage.Length();
michael@0 164 memcpy(response->mData, aMessage.Data(), aMessage.Length());
michael@0 165 gNetdConsumer->MessageReceived(response);
michael@0 166 }
michael@0 167
michael@0 168 if (!responseCode) {
michael@0 169 CHROMIUM_LOG("Can't parse netd's response");
michael@0 170 }
michael@0 171 }
michael@0 172
michael@0 173 void
michael@0 174 NetdClient::OnFileCanWriteWithoutBlocking(int aFd)
michael@0 175 {
michael@0 176 MOZ_ASSERT(aFd == mSocket.get());
michael@0 177 WriteNetdCommand();
michael@0 178 }
michael@0 179
michael@0 180 void
michael@0 181 NetdClient::OnError()
michael@0 182 {
michael@0 183 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 184
michael@0 185 mReadWatcher.StopWatchingFileDescriptor();
michael@0 186 mWriteWatcher.StopWatchingFileDescriptor();
michael@0 187
michael@0 188 mSocket.dispose();
michael@0 189 mCurrentWriteOffset = 0;
michael@0 190 mCurrentNetdCommand = nullptr;
michael@0 191 while (!mOutgoingQ.empty()) {
michael@0 192 delete mOutgoingQ.front();
michael@0 193 mOutgoingQ.pop();
michael@0 194 }
michael@0 195 Start();
michael@0 196 }
michael@0 197
michael@0 198 // static
michael@0 199 void
michael@0 200 NetdClient::Start()
michael@0 201 {
michael@0 202 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 203
michael@0 204 if (!gNetdClient) {
michael@0 205 CHROMIUM_LOG("Netd Client is not initialized");
michael@0 206 return;
michael@0 207 }
michael@0 208
michael@0 209 if (!gNetdClient->OpenSocket()) {
michael@0 210 // Socket open failed, try again in a second.
michael@0 211 CHROMIUM_LOG("Fail to connect to Netd");
michael@0 212 if (++gNetdClient->mReConnectTimes > MAX_RECONNECT_TIMES) {
michael@0 213 CHROMIUM_LOG("Fail to connect to Netd after retry %d times", MAX_RECONNECT_TIMES);
michael@0 214 return;
michael@0 215 }
michael@0 216
michael@0 217 MessageLoopForIO::current()->
michael@0 218 PostDelayedTask(FROM_HERE,
michael@0 219 NewRunnableFunction(NetdClient::Start),
michael@0 220 1000);
michael@0 221 return;
michael@0 222 }
michael@0 223 gNetdClient->mReConnectTimes = 0;
michael@0 224 }
michael@0 225
michael@0 226 // static
michael@0 227 void
michael@0 228 NetdClient::SendNetdCommandIOThread(NetdCommand* aMessage)
michael@0 229 {
michael@0 230 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 231 MOZ_ASSERT(aMessage);
michael@0 232
michael@0 233 if (!gNetdClient) {
michael@0 234 CHROMIUM_LOG("Netd Client is not initialized");
michael@0 235 return;
michael@0 236 }
michael@0 237
michael@0 238 gNetdClient->mOutgoingQ.push(aMessage);
michael@0 239
michael@0 240 if (gNetdClient->mSocket.get() == INVALID_SOCKET) {
michael@0 241 CHROMIUM_LOG("Netd connection is not established, push the message to queue");
michael@0 242 return;
michael@0 243 }
michael@0 244
michael@0 245 gNetdClient->WriteNetdCommand();
michael@0 246 }
michael@0 247
michael@0 248 void
michael@0 249 NetdClient::WriteNetdCommand()
michael@0 250 {
michael@0 251 if (!mCurrentNetdCommand) {
michael@0 252 mCurrentWriteOffset = 0;
michael@0 253 mCurrentNetdCommand = mOutgoingQ.front();
michael@0 254 mOutgoingQ.pop();
michael@0 255 }
michael@0 256
michael@0 257 while (mCurrentWriteOffset < mCurrentNetdCommand->mSize) {
michael@0 258 ssize_t write_amount = mCurrentNetdCommand->mSize - mCurrentWriteOffset;
michael@0 259 ssize_t written = write(mSocket.get(),
michael@0 260 mCurrentNetdCommand->mData + mCurrentWriteOffset,
michael@0 261 write_amount);
michael@0 262 if (written < 0) {
michael@0 263 CHROMIUM_LOG("Cannot write to network, error %d\n", (int) written);
michael@0 264 OnError();
michael@0 265 return;
michael@0 266 }
michael@0 267
michael@0 268 if (written > 0) {
michael@0 269 mCurrentWriteOffset += written;
michael@0 270 }
michael@0 271
michael@0 272 if (written != write_amount) {
michael@0 273 CHROMIUM_LOG("WriteNetdCommand fail !!! Write is not completed");
michael@0 274 break;
michael@0 275 }
michael@0 276 }
michael@0 277
michael@0 278 if (mCurrentWriteOffset != mCurrentNetdCommand->mSize) {
michael@0 279 MessageLoopForIO::current()->
michael@0 280 WatchFileDescriptor(mSocket.get(),
michael@0 281 false,
michael@0 282 MessageLoopForIO::WATCH_WRITE,
michael@0 283 &mWriteWatcher,
michael@0 284 this);
michael@0 285 return;
michael@0 286 }
michael@0 287
michael@0 288 mCurrentNetdCommand = nullptr;
michael@0 289 }
michael@0 290
michael@0 291 static void
michael@0 292 InitNetdIOThread()
michael@0 293 {
michael@0 294 bool result;
michael@0 295 char propValue[PROPERTY_VALUE_MAX];
michael@0 296
michael@0 297 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 298 MOZ_ASSERT(!gNetdClient);
michael@0 299
michael@0 300 property_get("ro.build.version.sdk", propValue, "0");
michael@0 301 // Assign rndis address for usb tethering in ICS.
michael@0 302 if (atoi(propValue) >= 15) {
michael@0 303 result = InitRndisAddress();
michael@0 304 // We don't return here because InitRnsisAddress() function is related to
michael@0 305 // usb tethering only. Others service such as wifi tethering still need
michael@0 306 // to use ipc to communicate with netd.
michael@0 307 if (!result) {
michael@0 308 CHROMIUM_LOG("fail to give rndis interface an address");
michael@0 309 }
michael@0 310 }
michael@0 311 gNetdClient = new NetdClient();
michael@0 312 gNetdClient->Start();
michael@0 313 }
michael@0 314
michael@0 315 static void
michael@0 316 ShutdownNetdIOThread()
michael@0 317 {
michael@0 318 MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
michael@0 319 nsCOMPtr<nsIRunnable> shutdownEvent = new StopNetdConsumer();
michael@0 320
michael@0 321 gNetdClient = nullptr;
michael@0 322
michael@0 323 NS_DispatchToMainThread(shutdownEvent);
michael@0 324 }
michael@0 325
michael@0 326 void
michael@0 327 StartNetd(NetdConsumer* aNetdConsumer)
michael@0 328 {
michael@0 329 MOZ_ASSERT(NS_IsMainThread());
michael@0 330 MOZ_ASSERT(aNetdConsumer);
michael@0 331 MOZ_ASSERT(gNetdConsumer == nullptr);
michael@0 332
michael@0 333 gNetdConsumer = aNetdConsumer;
michael@0 334 XRE_GetIOMessageLoop()->PostTask(
michael@0 335 FROM_HERE,
michael@0 336 NewRunnableFunction(InitNetdIOThread));
michael@0 337 }
michael@0 338
michael@0 339 void
michael@0 340 StopNetd()
michael@0 341 {
michael@0 342 MOZ_ASSERT(NS_IsMainThread());
michael@0 343
michael@0 344 nsIThread* currentThread = NS_GetCurrentThread();
michael@0 345 NS_ASSERTION(currentThread, "This should never be null!");
michael@0 346
michael@0 347 XRE_GetIOMessageLoop()->PostTask(
michael@0 348 FROM_HERE,
michael@0 349 NewRunnableFunction(ShutdownNetdIOThread));
michael@0 350
michael@0 351 while (gNetdConsumer) {
michael@0 352 if (!NS_ProcessNextEvent(currentThread)) {
michael@0 353 NS_WARNING("Something bad happened!");
michael@0 354 break;
michael@0 355 }
michael@0 356 }
michael@0 357 }
michael@0 358
michael@0 359 /**************************************************************************
michael@0 360 *
michael@0 361 * This function runs in net worker Thread context. The net worker thread
michael@0 362 * is created in dom/system/gonk/NetworkManager.js
michael@0 363 *
michael@0 364 **************************************************************************/
michael@0 365 void
michael@0 366 SendNetdCommand(NetdCommand* aMessage)
michael@0 367 {
michael@0 368 MOZ_ASSERT(aMessage);
michael@0 369
michael@0 370 XRE_GetIOMessageLoop()->PostTask(
michael@0 371 FROM_HERE,
michael@0 372 NewRunnableFunction(NetdClient::SendNetdCommandIOThread, aMessage));
michael@0 373 }
michael@0 374
michael@0 375 } // namespace ipc
michael@0 376 } // namespace mozilla

mercurial