summaryrefslogtreecommitdiffstats
path: root/ipc/netd/Netd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/netd/Netd.cpp')
-rw-r--r--ipc/netd/Netd.cpp374
1 files changed, 374 insertions, 0 deletions
diff --git a/ipc/netd/Netd.cpp b/ipc/netd/Netd.cpp
new file mode 100644
index 000000000..99350f250
--- /dev/null
+++ b/ipc/netd/Netd.cpp
@@ -0,0 +1,374 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "Netd.h"
+#include <android/log.h>
+#include <cutils/sockets.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include "android/log.h"
+#include "base/task.h"
+
+#include "nsWhitespaceTokenizer.h"
+#include "nsXULAppAPI.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Sprintf.h"
+#include "SystemProperty.h"
+
+#define NETD_LOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gonk", args)
+#define ICS_SYS_USB_RNDIS_MAC "/sys/class/android_usb/android0/f_rndis/ethaddr"
+#define INVALID_SOCKET -1
+#define MAX_RECONNECT_TIMES 10
+
+using mozilla::system::Property;
+
+namespace {
+
+RefPtr<mozilla::ipc::NetdClient> gNetdClient;
+RefPtr<mozilla::ipc::NetdConsumer> gNetdConsumer;
+
+class StopNetdConsumer : public mozilla::Runnable {
+public:
+ NS_IMETHOD Run() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ gNetdConsumer = nullptr;
+ return NS_OK;
+ }
+};
+
+bool
+InitRndisAddress()
+{
+ char mac[20];
+ char serialno[] = "1234567890ABCDEF";
+ static const int kEthernetAddressLength = 6;
+ char address[kEthernetAddressLength];
+ int i = 0;
+ int ret = 0;
+ int length = 0;
+ mozilla::ScopedClose fd;
+
+ fd.rwget() = open(ICS_SYS_USB_RNDIS_MAC, O_WRONLY);
+ if (fd.rwget() == -1) {
+ NETD_LOG("Unable to open file %s.", ICS_SYS_USB_RNDIS_MAC);
+ return false;
+ }
+
+ Property::Get("ro.serialno", serialno, "1234567890ABCDEF");
+
+ memset(address, 0, sizeof(address));
+ // First byte is 0x02 to signify a locally administered address.
+ address[0] = 0x02;
+ length = strlen(serialno);
+ for (i = 0; i < length; i++) {
+ address[i % (kEthernetAddressLength - 1) + 1] ^= serialno[i];
+ }
+
+ SprintfLiteral(mac, "%02x:%02x:%02x:%02x:%02x:%02x",
+ address[0], address[1], address[2],
+ address[3], address[4], address[5]);
+ length = strlen(mac);
+ ret = write(fd.get(), mac, length);
+ if (ret != length) {
+ NETD_LOG("Fail to write file %s.", ICS_SYS_USB_RNDIS_MAC);
+ return false;
+ }
+ return true;
+}
+
+} // namespace
+
+namespace mozilla {
+namespace ipc {
+
+NetdClient::NetdClient()
+ : LineWatcher('\0', MAX_COMMAND_SIZE)
+ , mIOLoop(MessageLoopForIO::current())
+ , mSocket(INVALID_SOCKET)
+ , mCurrentWriteOffset(0)
+ , mReConnectTimes(0)
+{
+ MOZ_COUNT_CTOR(NetdClient);
+}
+
+NetdClient::~NetdClient()
+{
+ MOZ_COUNT_DTOR(NetdClient);
+}
+
+bool
+NetdClient::OpenSocket()
+{
+ mSocket.rwget() = socket_local_client("netd",
+ ANDROID_SOCKET_NAMESPACE_RESERVED,
+ SOCK_STREAM);
+ if (mSocket.rwget() < 0) {
+ NETD_LOG("Error connecting to : netd (%s) - will retry", strerror(errno));
+ return false;
+ }
+ // Add FD_CLOEXEC flag.
+ int flags = fcntl(mSocket.get(), F_GETFD);
+ if (flags == -1) {
+ NETD_LOG("Error doing fcntl with F_GETFD command(%s)", strerror(errno));
+ return false;
+ }
+ flags |= FD_CLOEXEC;
+ if (fcntl(mSocket.get(), F_SETFD, flags) == -1) {
+ NETD_LOG("Error doing fcntl with F_SETFD command(%s)", strerror(errno));
+ return false;
+ }
+ // Set non-blocking.
+ if (fcntl(mSocket.get(), F_SETFL, O_NONBLOCK) == -1) {
+ NETD_LOG("Error set non-blocking socket(%s)", strerror(errno));
+ return false;
+ }
+ if (!MessageLoopForIO::current()->
+ WatchFileDescriptor(mSocket.get(),
+ true,
+ MessageLoopForIO::WATCH_READ,
+ &mReadWatcher,
+ this)) {
+ NETD_LOG("Error set socket read watcher(%s)", strerror(errno));
+ return false;
+ }
+
+ if (!mOutgoingQ.empty()) {
+ MessageLoopForIO::current()->
+ WatchFileDescriptor(mSocket.get(),
+ false,
+ MessageLoopForIO::WATCH_WRITE,
+ &mWriteWatcher,
+ this);
+ }
+
+ NETD_LOG("Connected to netd");
+ return true;
+}
+
+void NetdClient::OnLineRead(int aFd, nsDependentCSubstring& aMessage)
+{
+ // Set errno to 0 first. For preventing to use the stale version of errno.
+ errno = 0;
+ // We found a line terminator. Each line is formatted as an
+ // integer response code followed by the rest of the line.
+ // Fish out the response code.
+ int responseCode = strtol(aMessage.Data(), nullptr, 10);
+ if (!errno) {
+ NetdCommand* response = new NetdCommand();
+ // Passing all the response message, including the line terminator.
+ response->mSize = aMessage.Length();
+ memcpy(response->mData, aMessage.Data(), aMessage.Length());
+ gNetdConsumer->MessageReceived(response);
+ }
+
+ if (!responseCode) {
+ NETD_LOG("Can't parse netd's response");
+ }
+}
+
+void
+NetdClient::OnFileCanWriteWithoutBlocking(int aFd)
+{
+ MOZ_ASSERT(aFd == mSocket.get());
+ WriteNetdCommand();
+}
+
+void
+NetdClient::OnError()
+{
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+
+ mReadWatcher.StopWatchingFileDescriptor();
+ mWriteWatcher.StopWatchingFileDescriptor();
+
+ mSocket.dispose();
+ mCurrentWriteOffset = 0;
+ mCurrentNetdCommand = nullptr;
+ while (!mOutgoingQ.empty()) {
+ delete mOutgoingQ.front();
+ mOutgoingQ.pop();
+ }
+ Start();
+}
+
+// static
+void
+NetdClient::Start()
+{
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+
+ if (!gNetdClient) {
+ NETD_LOG("Netd Client is not initialized");
+ return;
+ }
+
+ if (!gNetdClient->OpenSocket()) {
+ // Socket open failed, try again in a second.
+ NETD_LOG("Fail to connect to Netd");
+ if (++gNetdClient->mReConnectTimes > MAX_RECONNECT_TIMES) {
+ NETD_LOG("Fail to connect to Netd after retry %d times", MAX_RECONNECT_TIMES);
+ return;
+ }
+
+ MessageLoopForIO::current()->
+ PostDelayedTask(NewRunnableFunction(NetdClient::Start),
+ 1000);
+ return;
+ }
+ gNetdClient->mReConnectTimes = 0;
+}
+
+// static
+void
+NetdClient::SendNetdCommandIOThread(NetdCommand* aMessage)
+{
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+ MOZ_ASSERT(aMessage);
+
+ if (!gNetdClient) {
+ NETD_LOG("Netd Client is not initialized");
+ return;
+ }
+
+ gNetdClient->mOutgoingQ.push(aMessage);
+
+ if (gNetdClient->mSocket.get() == INVALID_SOCKET) {
+ NETD_LOG("Netd connection is not established, push the message to queue");
+ return;
+ }
+
+ gNetdClient->WriteNetdCommand();
+}
+
+void
+NetdClient::WriteNetdCommand()
+{
+ if (!mCurrentNetdCommand) {
+ mCurrentWriteOffset = 0;
+ mCurrentNetdCommand = mOutgoingQ.front();
+ mOutgoingQ.pop();
+ }
+
+ while (mCurrentWriteOffset < mCurrentNetdCommand->mSize) {
+ ssize_t write_amount = mCurrentNetdCommand->mSize - mCurrentWriteOffset;
+ ssize_t written = write(mSocket.get(),
+ mCurrentNetdCommand->mData + mCurrentWriteOffset,
+ write_amount);
+ if (written < 0) {
+ NETD_LOG("Cannot write to network, error %d\n", (int) written);
+ OnError();
+ return;
+ }
+
+ if (written > 0) {
+ mCurrentWriteOffset += written;
+ }
+
+ if (written != write_amount) {
+ NETD_LOG("WriteNetdCommand fail !!! Write is not completed");
+ break;
+ }
+ }
+
+ if (mCurrentWriteOffset != mCurrentNetdCommand->mSize) {
+ MessageLoopForIO::current()->
+ WatchFileDescriptor(mSocket.get(),
+ false,
+ MessageLoopForIO::WATCH_WRITE,
+ &mWriteWatcher,
+ this);
+ return;
+ }
+
+ mCurrentNetdCommand = nullptr;
+}
+
+static void
+InitNetdIOThread()
+{
+ bool result;
+ char propValue[Property::VALUE_MAX_LENGTH];
+
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+ MOZ_ASSERT(!gNetdClient);
+
+ Property::Get("ro.build.version.sdk", propValue, "0");
+ // Assign rndis address for usb tethering in ICS.
+ if (atoi(propValue) >= 15) {
+ result = InitRndisAddress();
+ // We don't return here because InitRnsisAddress() function is related to
+ // usb tethering only. Others service such as wifi tethering still need
+ // to use ipc to communicate with netd.
+ if (!result) {
+ NETD_LOG("fail to give rndis interface an address");
+ }
+ }
+ gNetdClient = new NetdClient();
+ gNetdClient->Start();
+}
+
+static void
+ShutdownNetdIOThread()
+{
+ MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
+ nsCOMPtr<nsIRunnable> shutdownEvent = new StopNetdConsumer();
+
+ gNetdClient = nullptr;
+
+ NS_DispatchToMainThread(shutdownEvent);
+}
+
+void
+StartNetd(NetdConsumer* aNetdConsumer)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aNetdConsumer);
+ MOZ_ASSERT(gNetdConsumer == nullptr);
+
+ gNetdConsumer = aNetdConsumer;
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(InitNetdIOThread));
+}
+
+void
+StopNetd()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsIThread* currentThread = NS_GetCurrentThread();
+ NS_ASSERTION(currentThread, "This should never be null!");
+
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(ShutdownNetdIOThread));
+
+ while (gNetdConsumer) {
+ if (!NS_ProcessNextEvent(currentThread)) {
+ NS_WARNING("Something bad happened!");
+ break;
+ }
+ }
+}
+
+/**************************************************************************
+*
+* This function runs in net worker Thread context. The net worker thread
+* is created in dom/system/gonk/NetworkManager.js
+*
+**************************************************************************/
+void
+SendNetdCommand(NetdCommand* aMessage)
+{
+ MOZ_ASSERT(aMessage);
+
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(NetdClient::SendNetdCommandIOThread, aMessage));
+}
+
+} // namespace ipc
+} // namespace mozilla