ipc/netd/Netd.cpp

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

mercurial