1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/ipc/netd/Netd.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,376 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "Netd.h" 1.9 +#include <android/log.h> 1.10 +#include <cutils/sockets.h> 1.11 +#include <fcntl.h> 1.12 +#include <sys/socket.h> 1.13 + 1.14 +#include "cutils/properties.h" 1.15 +#include "android/log.h" 1.16 + 1.17 +#include "nsWhitespaceTokenizer.h" 1.18 +#include "nsXULAppAPI.h" 1.19 +#include "nsAutoPtr.h" 1.20 +#include "nsString.h" 1.21 +#include "nsThreadUtils.h" 1.22 +#include "mozilla/RefPtr.h" 1.23 + 1.24 + 1.25 +#define CHROMIUM_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args) 1.26 +#define ICS_SYS_USB_RNDIS_MAC "/sys/class/android_usb/android0/f_rndis/ethaddr" 1.27 +#define INVALID_SOCKET -1 1.28 +#define MAX_RECONNECT_TIMES 10 1.29 + 1.30 +namespace { 1.31 + 1.32 +mozilla::RefPtr<mozilla::ipc::NetdClient> gNetdClient; 1.33 +mozilla::RefPtr<mozilla::ipc::NetdConsumer> gNetdConsumer; 1.34 + 1.35 +class StopNetdConsumer : public nsRunnable { 1.36 +public: 1.37 + NS_IMETHOD Run() 1.38 + { 1.39 + MOZ_ASSERT(NS_IsMainThread()); 1.40 + 1.41 + gNetdConsumer = nullptr; 1.42 + return NS_OK; 1.43 + } 1.44 +}; 1.45 + 1.46 +bool 1.47 +InitRndisAddress() 1.48 +{ 1.49 + char mac[20]; 1.50 + char serialno[] = "1234567890ABCDEF"; 1.51 + static const int kEthernetAddressLength = 6; 1.52 + char address[kEthernetAddressLength]; 1.53 + int i = 0; 1.54 + int ret = 0; 1.55 + int length = 0; 1.56 + mozilla::ScopedClose fd; 1.57 + 1.58 + fd.rwget() = open(ICS_SYS_USB_RNDIS_MAC, O_WRONLY); 1.59 + if (fd.rwget() == -1) { 1.60 + CHROMIUM_LOG("Unable to open file %s.", ICS_SYS_USB_RNDIS_MAC); 1.61 + return false; 1.62 + } 1.63 + 1.64 + property_get("ro.serialno", serialno, "1234567890ABCDEF"); 1.65 + 1.66 + memset(address, 0, sizeof(address)); 1.67 + // First byte is 0x02 to signify a locally administered address. 1.68 + address[0] = 0x02; 1.69 + length = strlen(serialno); 1.70 + for (i = 0; i < length; i++) { 1.71 + address[i % (kEthernetAddressLength - 1) + 1] ^= serialno[i]; 1.72 + } 1.73 + 1.74 + sprintf(mac, "%02x:%02x:%02x:%02x:%02x:%02x", 1.75 + address[0], address[1], address[2], 1.76 + address[3], address[4], address[5]); 1.77 + length = strlen(mac); 1.78 + ret = write(fd.get(), mac, length); 1.79 + if (ret != length) { 1.80 + CHROMIUM_LOG("Fail to write file %s.", ICS_SYS_USB_RNDIS_MAC); 1.81 + return false; 1.82 + } 1.83 + return true; 1.84 +} 1.85 + 1.86 +} // anonymous namespace 1.87 + 1.88 +namespace mozilla { 1.89 +namespace ipc { 1.90 + 1.91 +NetdClient::NetdClient() 1.92 + : LineWatcher('\0', MAX_COMMAND_SIZE) 1.93 + , mIOLoop(MessageLoopForIO::current()) 1.94 + , mSocket(INVALID_SOCKET) 1.95 + , mCurrentWriteOffset(0) 1.96 + , mReConnectTimes(0) 1.97 +{ 1.98 + MOZ_COUNT_CTOR(NetdClient); 1.99 +} 1.100 + 1.101 +NetdClient::~NetdClient() 1.102 +{ 1.103 + MOZ_COUNT_DTOR(NetdClient); 1.104 +} 1.105 + 1.106 +bool 1.107 +NetdClient::OpenSocket() 1.108 +{ 1.109 + mSocket.rwget() = socket_local_client("netd", 1.110 + ANDROID_SOCKET_NAMESPACE_RESERVED, 1.111 + SOCK_STREAM); 1.112 + if (mSocket.rwget() < 0) { 1.113 + CHROMIUM_LOG("Error connecting to : netd (%s) - will retry", strerror(errno)); 1.114 + return false; 1.115 + } 1.116 + // Add FD_CLOEXEC flag. 1.117 + int flags = fcntl(mSocket.get(), F_GETFD); 1.118 + if (flags == -1) { 1.119 + CHROMIUM_LOG("Error doing fcntl with F_GETFD command(%s)", strerror(errno)); 1.120 + return false; 1.121 + } 1.122 + flags |= FD_CLOEXEC; 1.123 + if (fcntl(mSocket.get(), F_SETFD, flags) == -1) { 1.124 + CHROMIUM_LOG("Error doing fcntl with F_SETFD command(%s)", strerror(errno)); 1.125 + return false; 1.126 + } 1.127 + // Set non-blocking. 1.128 + if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) { 1.129 + CHROMIUM_LOG("Error set non-blocking socket(%s)", strerror(errno)); 1.130 + return false; 1.131 + } 1.132 + if (!MessageLoopForIO::current()-> 1.133 + WatchFileDescriptor(mSocket.get(), 1.134 + true, 1.135 + MessageLoopForIO::WATCH_READ, 1.136 + &mReadWatcher, 1.137 + this)) { 1.138 + CHROMIUM_LOG("Error set socket read watcher(%s)", strerror(errno)); 1.139 + return false; 1.140 + } 1.141 + 1.142 + if (!mOutgoingQ.empty()) { 1.143 + MessageLoopForIO::current()-> 1.144 + WatchFileDescriptor(mSocket.get(), 1.145 + false, 1.146 + MessageLoopForIO::WATCH_WRITE, 1.147 + &mWriteWatcher, 1.148 + this); 1.149 + } 1.150 + 1.151 + CHROMIUM_LOG("Connected to netd"); 1.152 + return true; 1.153 +} 1.154 + 1.155 +void NetdClient::OnLineRead(int aFd, nsDependentCSubstring& aMessage) 1.156 +{ 1.157 + // Set errno to 0 first. For preventing to use the stale version of errno. 1.158 + errno = 0; 1.159 + // We found a line terminator. Each line is formatted as an 1.160 + // integer response code followed by the rest of the line. 1.161 + // Fish out the response code. 1.162 + int responseCode = strtol(aMessage.Data(), nullptr, 10); 1.163 + if (!errno) { 1.164 + NetdCommand* response = new NetdCommand(); 1.165 + // Passing all the response message, including the line terminator. 1.166 + response->mSize = aMessage.Length(); 1.167 + memcpy(response->mData, aMessage.Data(), aMessage.Length()); 1.168 + gNetdConsumer->MessageReceived(response); 1.169 + } 1.170 + 1.171 + if (!responseCode) { 1.172 + CHROMIUM_LOG("Can't parse netd's response"); 1.173 + } 1.174 +} 1.175 + 1.176 +void 1.177 +NetdClient::OnFileCanWriteWithoutBlocking(int aFd) 1.178 +{ 1.179 + MOZ_ASSERT(aFd == mSocket.get()); 1.180 + WriteNetdCommand(); 1.181 +} 1.182 + 1.183 +void 1.184 +NetdClient::OnError() 1.185 +{ 1.186 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.187 + 1.188 + mReadWatcher.StopWatchingFileDescriptor(); 1.189 + mWriteWatcher.StopWatchingFileDescriptor(); 1.190 + 1.191 + mSocket.dispose(); 1.192 + mCurrentWriteOffset = 0; 1.193 + mCurrentNetdCommand = nullptr; 1.194 + while (!mOutgoingQ.empty()) { 1.195 + delete mOutgoingQ.front(); 1.196 + mOutgoingQ.pop(); 1.197 + } 1.198 + Start(); 1.199 +} 1.200 + 1.201 +// static 1.202 +void 1.203 +NetdClient::Start() 1.204 +{ 1.205 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.206 + 1.207 + if (!gNetdClient) { 1.208 + CHROMIUM_LOG("Netd Client is not initialized"); 1.209 + return; 1.210 + } 1.211 + 1.212 + if (!gNetdClient->OpenSocket()) { 1.213 + // Socket open failed, try again in a second. 1.214 + CHROMIUM_LOG("Fail to connect to Netd"); 1.215 + if (++gNetdClient->mReConnectTimes > MAX_RECONNECT_TIMES) { 1.216 + CHROMIUM_LOG("Fail to connect to Netd after retry %d times", MAX_RECONNECT_TIMES); 1.217 + return; 1.218 + } 1.219 + 1.220 + MessageLoopForIO::current()-> 1.221 + PostDelayedTask(FROM_HERE, 1.222 + NewRunnableFunction(NetdClient::Start), 1.223 + 1000); 1.224 + return; 1.225 + } 1.226 + gNetdClient->mReConnectTimes = 0; 1.227 +} 1.228 + 1.229 +// static 1.230 +void 1.231 +NetdClient::SendNetdCommandIOThread(NetdCommand* aMessage) 1.232 +{ 1.233 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.234 + MOZ_ASSERT(aMessage); 1.235 + 1.236 + if (!gNetdClient) { 1.237 + CHROMIUM_LOG("Netd Client is not initialized"); 1.238 + return; 1.239 + } 1.240 + 1.241 + gNetdClient->mOutgoingQ.push(aMessage); 1.242 + 1.243 + if (gNetdClient->mSocket.get() == INVALID_SOCKET) { 1.244 + CHROMIUM_LOG("Netd connection is not established, push the message to queue"); 1.245 + return; 1.246 + } 1.247 + 1.248 + gNetdClient->WriteNetdCommand(); 1.249 +} 1.250 + 1.251 +void 1.252 +NetdClient::WriteNetdCommand() 1.253 +{ 1.254 + if (!mCurrentNetdCommand) { 1.255 + mCurrentWriteOffset = 0; 1.256 + mCurrentNetdCommand = mOutgoingQ.front(); 1.257 + mOutgoingQ.pop(); 1.258 + } 1.259 + 1.260 + while (mCurrentWriteOffset < mCurrentNetdCommand->mSize) { 1.261 + ssize_t write_amount = mCurrentNetdCommand->mSize - mCurrentWriteOffset; 1.262 + ssize_t written = write(mSocket.get(), 1.263 + mCurrentNetdCommand->mData + mCurrentWriteOffset, 1.264 + write_amount); 1.265 + if (written < 0) { 1.266 + CHROMIUM_LOG("Cannot write to network, error %d\n", (int) written); 1.267 + OnError(); 1.268 + return; 1.269 + } 1.270 + 1.271 + if (written > 0) { 1.272 + mCurrentWriteOffset += written; 1.273 + } 1.274 + 1.275 + if (written != write_amount) { 1.276 + CHROMIUM_LOG("WriteNetdCommand fail !!! Write is not completed"); 1.277 + break; 1.278 + } 1.279 + } 1.280 + 1.281 + if (mCurrentWriteOffset != mCurrentNetdCommand->mSize) { 1.282 + MessageLoopForIO::current()-> 1.283 + WatchFileDescriptor(mSocket.get(), 1.284 + false, 1.285 + MessageLoopForIO::WATCH_WRITE, 1.286 + &mWriteWatcher, 1.287 + this); 1.288 + return; 1.289 + } 1.290 + 1.291 + mCurrentNetdCommand = nullptr; 1.292 +} 1.293 + 1.294 +static void 1.295 +InitNetdIOThread() 1.296 +{ 1.297 + bool result; 1.298 + char propValue[PROPERTY_VALUE_MAX]; 1.299 + 1.300 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.301 + MOZ_ASSERT(!gNetdClient); 1.302 + 1.303 + property_get("ro.build.version.sdk", propValue, "0"); 1.304 + // Assign rndis address for usb tethering in ICS. 1.305 + if (atoi(propValue) >= 15) { 1.306 + result = InitRndisAddress(); 1.307 + // We don't return here because InitRnsisAddress() function is related to 1.308 + // usb tethering only. Others service such as wifi tethering still need 1.309 + // to use ipc to communicate with netd. 1.310 + if (!result) { 1.311 + CHROMIUM_LOG("fail to give rndis interface an address"); 1.312 + } 1.313 + } 1.314 + gNetdClient = new NetdClient(); 1.315 + gNetdClient->Start(); 1.316 +} 1.317 + 1.318 +static void 1.319 +ShutdownNetdIOThread() 1.320 +{ 1.321 + MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop()); 1.322 + nsCOMPtr<nsIRunnable> shutdownEvent = new StopNetdConsumer(); 1.323 + 1.324 + gNetdClient = nullptr; 1.325 + 1.326 + NS_DispatchToMainThread(shutdownEvent); 1.327 +} 1.328 + 1.329 +void 1.330 +StartNetd(NetdConsumer* aNetdConsumer) 1.331 +{ 1.332 + MOZ_ASSERT(NS_IsMainThread()); 1.333 + MOZ_ASSERT(aNetdConsumer); 1.334 + MOZ_ASSERT(gNetdConsumer == nullptr); 1.335 + 1.336 + gNetdConsumer = aNetdConsumer; 1.337 + XRE_GetIOMessageLoop()->PostTask( 1.338 + FROM_HERE, 1.339 + NewRunnableFunction(InitNetdIOThread)); 1.340 +} 1.341 + 1.342 +void 1.343 +StopNetd() 1.344 +{ 1.345 + MOZ_ASSERT(NS_IsMainThread()); 1.346 + 1.347 + nsIThread* currentThread = NS_GetCurrentThread(); 1.348 + NS_ASSERTION(currentThread, "This should never be null!"); 1.349 + 1.350 + XRE_GetIOMessageLoop()->PostTask( 1.351 + FROM_HERE, 1.352 + NewRunnableFunction(ShutdownNetdIOThread)); 1.353 + 1.354 + while (gNetdConsumer) { 1.355 + if (!NS_ProcessNextEvent(currentThread)) { 1.356 + NS_WARNING("Something bad happened!"); 1.357 + break; 1.358 + } 1.359 + } 1.360 +} 1.361 + 1.362 +/************************************************************************** 1.363 +* 1.364 +* This function runs in net worker Thread context. The net worker thread 1.365 +* is created in dom/system/gonk/NetworkManager.js 1.366 +* 1.367 +**************************************************************************/ 1.368 +void 1.369 +SendNetdCommand(NetdCommand* aMessage) 1.370 +{ 1.371 + MOZ_ASSERT(aMessage); 1.372 + 1.373 + XRE_GetIOMessageLoop()->PostTask( 1.374 + FROM_HERE, 1.375 + NewRunnableFunction(NetdClient::SendNetdCommandIOThread, aMessage)); 1.376 +} 1.377 + 1.378 +} // namespace ipc 1.379 +} // namespace mozilla