Merge commit 'goog/master' into merge_master
diff --git a/Android.mk b/Android.mk
index 8b79ceb..fa2f6f8 100644
--- a/Android.mk
+++ b/Android.mk
@@ -21,6 +21,7 @@
   include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
 	      adb \
 	      libcutils \
+	      libsysutils \
 	      liblog \
 	      libnetutils \
 	      libpixelflinger \
diff --git a/adb/Android.mk b/adb/Android.mk
index 2296610..c8606cf 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -7,7 +7,6 @@
 
 # adb host tool
 # =========================================================
-ifneq ($(TARGET_SIMULATOR),true) # not 64 bit clean (also unused with the sim)
 include $(CLEAR_VARS)
 
 # Default to a virtual (sockets) usb interface
@@ -77,7 +76,6 @@
 $(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
 endif
 
-endif
 
 # adbd device daemon
 # =========================================================
diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c
index 95e20b5..850e10b 100644
--- a/fastboot/fastboot.c
+++ b/fastboot/fastboot.c
@@ -222,6 +222,7 @@
             "  -p <product>                             specify product name\n"
             "  -c <cmdline>                             override kernel commandline\n"
             "  -i <vendor id>                           specify a custom USB vendor id\n"
+            "  -b <base_addr>                           specify a custom kernel base address\n"
         );
     exit(1);
 }
diff --git a/include/sysutils/FrameworkCommand.h b/include/sysutils/FrameworkCommand.h
new file mode 100644
index 0000000..952e99a
--- /dev/null
+++ b/include/sysutils/FrameworkCommand.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __FRAMEWORK_CMD_HANDLER_H
+#define __FRAMEWORK_CMD_HANDLER_H
+
+#include "../../../frameworks/base/include/utils/List.h"
+
+
+class FrameworkCommand { 
+private:
+    const char *mCommand;
+
+public:
+
+    FrameworkCommand(const char *cmd);
+    virtual ~FrameworkCommand() { }
+
+    virtual int runCommand(char *data);
+
+    const char *getCommand() { return mCommand; }
+};
+
+typedef android::List<FrameworkCommand *> FrameworkCommandCollection;
+#endif
diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h
new file mode 100644
index 0000000..1454a6f
--- /dev/null
+++ b/include/sysutils/FrameworkListener.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _FRAMEWORKSOCKETLISTENER_H
+#define _FRAMEWORKSOCKETLISTENER_H
+
+#include "SocketListener.h"
+#include "FrameworkCommand.h"
+
+class FrameworkListener : public SocketListener {
+private:
+    FrameworkCommandCollection *mCommands;
+
+public:
+    FrameworkListener(const char *socketName);
+    virtual ~FrameworkListener() {}
+
+protected:
+    void registerCmd(FrameworkCommand *cmd);
+    virtual bool onDataAvailable(int socket);
+
+private:
+    void dispatchCommand(char *cmd);
+};
+#endif
diff --git a/include/sysutils/FrameworkManager.h b/include/sysutils/FrameworkManager.h
new file mode 100644
index 0000000..8a24d33
--- /dev/null
+++ b/include/sysutils/FrameworkManager.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _FRAMEWORKMANAGER_H
+#define _FRAMEWORKMANAGER_H
+
+#include <pthread.h>
+
+class FrameworkListener;
+
+class FrameworkManager {
+    int mDoorbell;        // Socket used to accept connections from framework
+    int mFwSock;          // Socket used to communicate with framework
+    const char *mSocketName;
+
+    FrameworkListener *mListener;
+    
+    pthread_mutex_t mWriteMutex;
+
+public:
+    FrameworkManager(FrameworkListener *Listener);
+    virtual ~FrameworkManager() {}
+
+    int run();
+    int sendMsg(char *msg);
+    int sendMsg(char *msg, char *data);
+};
+#endif
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h
new file mode 100644
index 0000000..95e83a3
--- /dev/null
+++ b/include/sysutils/NetlinkEvent.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _NETLINKEVENT_H
+#define _NETLINKEVENT_H
+
+#define NL_PARAMS_MAX 32
+
+class NetlinkEvent {
+    int  mSeq;
+    char *mPath;
+    int  mAction;
+    char *mSubsystem;
+    char *mParams[NL_PARAMS_MAX];
+
+public:
+    const static int NlActionUnknown;
+    const static int NlActionAdd;
+    const static int NlActionRemove;
+    const static int NlActionChange;
+
+    NetlinkEvent();
+    virtual ~NetlinkEvent();
+
+    bool decode(char *buffer, int size);
+    const char *findParam(const char *paramName);
+
+    const char *getSubsystem() { return mSubsystem; }
+    int getAction() { return mAction; }
+};
+
+#endif
diff --git a/include/sysutils/NetlinkListener.h b/include/sysutils/NetlinkListener.h
new file mode 100644
index 0000000..8ac811c
--- /dev/null
+++ b/include/sysutils/NetlinkListener.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _NETLINKLISTENER_H
+#define _NETLINKLISTENER_H
+
+#include "SocketListener.h"
+
+class NetlinkEvent;
+
+class NetlinkListener : public SocketListener {
+    char mBuffer[64 * 1024];
+
+public:
+    NetlinkListener(int socket);
+    virtual ~NetlinkListener() {}
+protected:
+    virtual bool onDataAvailable(int socket);
+};
+#endif
diff --git a/include/sysutils/SocketListener.h b/include/sysutils/SocketListener.h
new file mode 100644
index 0000000..f079dba
--- /dev/null
+++ b/include/sysutils/SocketListener.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _SOCKETLISTENER_H
+#define _SOCKETLISTENER_H
+
+class SocketListener {
+    int mSock;
+    int mCsock;
+    int mAcceptClients;
+    const char *mSocketName;
+
+public:
+    SocketListener(const char *socketName, bool acceptClients);
+    SocketListener(int socketFd, bool acceptClients);
+
+    virtual ~SocketListener() {}
+    virtual int run();
+
+protected:
+    virtual bool onDataAvailable(int socket);
+};
+#endif
diff --git a/init/devices.c b/init/devices.c
index e0b1f1f..c38b063 100644
--- a/init/devices.c
+++ b/init/devices.c
@@ -126,6 +126,7 @@
     { "/dev/msm_audpre",    0660,   AID_SYSTEM,     AID_AUDIO,      0 },
     { "/dev/htc-acoustic",  0660,   AID_SYSTEM,     AID_AUDIO,      0 },
     { "/dev/smd0",          0640,   AID_RADIO,      AID_RADIO,      0 },
+    { "/dev/qemu_trace",    0666,   AID_SYSTEM,     AID_SYSTEM,     0 },
     { "/dev/qmi",           0640,   AID_RADIO,      AID_RADIO,      0 },
     { "/dev/qmi0",          0640,   AID_RADIO,      AID_RADIO,      0 },
     { "/dev/qmi1",          0640,   AID_RADIO,      AID_RADIO,      0 },
diff --git a/init/init.c b/init/init.c
index 1630155..283608c 100644
--- a/init/init.c
+++ b/init/init.c
@@ -423,7 +423,7 @@
     if (svc) {
         service_stop(svc);
     } else {
-        ERROR("no such service '%s'\n");
+        ERROR("no such service '%s'\n", name);
     }
 }
 
diff --git a/init/init.h b/init/init.h
index b686869..c9363da 100644
--- a/init/init.h
+++ b/init/init.h
@@ -29,7 +29,8 @@
 void log_init(void);
 void log_set_level(int level);
 void log_close(void);
-void log_write(int level, const char *fmt, ...);
+void log_write(int level, const char *fmt, ...)
+    __attribute__ ((format(printf, 2, 3)));
 
 #define ERROR(x...)   log_write(3, "<3>init: " x)
 #define NOTICE(x...)  log_write(5, "<5>init: " x)
diff --git a/libcutils/strdup16to8.c b/libcutils/strdup16to8.c
index fadaabe..1a8ba86 100644
--- a/libcutils/strdup16to8.c
+++ b/libcutils/strdup16to8.c
@@ -15,6 +15,8 @@
 ** limitations under the License.
 */
 
+#include <limits.h>  /* for SIZE_MAX */
+
 #include <cutils/jstring.h>
 #include <assert.h>
 #include <stdlib.h>
@@ -26,19 +28,67 @@
  */
 extern size_t strnlen16to8(const char16_t* utf16Str, size_t len)
 {
-   size_t utf8Len = 0;
+    size_t utf8Len = 0;
 
-   while (len--) {
-       unsigned int uic = *utf16Str++;
+    /* A small note on integer overflow. The result can
+     * potentially be as big as 3*len, which will overflow
+     * for len > SIZE_MAX/3.
+     *
+     * Moreover, the result of a strnlen16to8 is typically used
+     * to allocate a destination buffer to strncpy16to8 which
+     * requires one more byte to terminate the UTF-8 copy, and
+     * this is generally done by careless users by incrementing
+     * the result without checking for integer overflows, e.g.:
+     *
+     *   dst = malloc(strnlen16to8(utf16,len)+1)
+     *
+     * Due to this, the following code will try to detect
+     * overflows, and never return more than (SIZE_MAX-1)
+     * when it detects one. A careless user will try to malloc
+     * SIZE_MAX bytes, which will return NULL which can at least
+     * be detected appropriately.
+     *
+     * As far as I know, this function is only used by strndup16(),
+     * but better be safe than sorry.
+     */
 
-       if (uic > 0x07ff)
-           utf8Len += 3;
-       else if (uic > 0x7f || uic == 0)
-           utf8Len += 2;
-       else
-           utf8Len++;
-   }
-   return utf8Len;
+    /* Fast path for the usual case where 3*len is < SIZE_MAX-1.
+     */
+    if (len < (SIZE_MAX-1)/3) {
+        while (len--) {
+            unsigned int uic = *utf16Str++;
+
+            if (uic > 0x07ff)
+                utf8Len += 3;
+            else if (uic > 0x7f || uic == 0)
+                utf8Len += 2;
+            else
+                utf8Len++;
+        }
+        return utf8Len;
+    }
+
+    /* The slower but paranoid version */
+    while (len--) {
+        unsigned int  uic     = *utf16Str++;
+        size_t        utf8Cur = utf8Len;
+
+        if (uic > 0x07ff)
+            utf8Len += 3;
+        else if (uic > 0x7f || uic == 0)
+            utf8Len += 2;
+        else
+            utf8Len++;
+
+        if (utf8Len < utf8Cur) /* overflow detected */
+            return SIZE_MAX-1;
+    }
+
+    /* don't return SIZE_MAX to avoid common user bug */
+    if (utf8Len == SIZE_MAX)
+        utf8Len = SIZE_MAX-1;
+
+    return utf8Len;
 }
 
 
@@ -50,7 +100,7 @@
  *
  * Make sure you allocate "utf8Str" with the result of strlen16to8() + 1,
  * not just "len".
- * 
+ *
  * Please note, a terminated \0 is always added, so your result will always
  * be "strlen16to8() + 1" bytes long.
  */
@@ -58,6 +108,10 @@
 {
     char* utf8cur = utf8Str;
 
+    /* Note on overflows: We assume the user did check the result of
+     * strnlen16to8() properly or at a minimum checked the result of
+     * its malloc(SIZE_MAX) in case of overflow.
+     */
     while (len--) {
         unsigned int uic = *utf16Str++;
 
@@ -73,8 +127,8 @@
 
             if (uic == 0) {
                 break;
-            }           
-        }       
+            }
+        }
     }
 
    *utf8cur = '\0';
@@ -85,20 +139,30 @@
 /**
  * Convert a UTF-16 string to UTF-8.
  *
- * Make sure you allocate "dest" with the result of strblen16to8(),
- * not just "strlen16()".
  */
 char * strndup16to8 (const char16_t* s, size_t n)
 {
-    char *ret;
+    char*   ret;
+    size_t  len;
 
     if (s == NULL) {
         return NULL;
     }
 
-    ret = malloc(strnlen16to8(s, n) + 1);
+    len = strnlen16to8(s, n);
+
+    /* We are paranoid, and we check for SIZE_MAX-1
+     * too since it is an overflow value for our
+     * strnlen16to8 implementation.
+     */
+    if (len >= SIZE_MAX-1)
+        return NULL;
+
+    ret = malloc(len + 1);
+    if (ret == NULL)
+        return NULL;
 
     strncpy16to8 (ret, s, n);
-    
-    return ret;    
+
+    return ret;
 }
diff --git a/libsysutils/Android.mk b/libsysutils/Android.mk
new file mode 100644
index 0000000..0b15c12
--- /dev/null
+++ b/libsysutils/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                             \
+                  src/FrameworkManager.cpp    \
+                  src/SocketListener.cpp      \
+                  src/FrameworkListener.cpp   \
+                  src/NetlinkListener.cpp     \
+                  src/NetlinkEvent.cpp        \
+                  src/FrameworkCommand.cpp    \
+
+LOCAL_MODULE:= libsysutils
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS) 
+
+LOCAL_CFLAGS := 
+
+LOCAL_SHARED_LIBRARIES := libcutils
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp
new file mode 100644
index 0000000..0444de5
--- /dev/null
+++ b/libsysutils/src/FrameworkCommand.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+
+#define LOG_TAG "FrameworkCommand"
+
+#include <cutils/log.h>
+
+#include <sysutils/FrameworkCommand.h>
+
+FrameworkCommand::FrameworkCommand(const char *cmd) {
+    mCommand = cmd;
+}
+
+int FrameworkCommand::runCommand(char *data) {
+    LOGW("Command %s has no run handler!", getCommand());
+    errno = ENOSYS;
+    return -1;
+}
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
new file mode 100644
index 0000000..b920215
--- /dev/null
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <string.h>
+
+#define LOG_TAG "FrameworkListener"
+
+#include <cutils/log.h>
+
+#include <sysutils/FrameworkListener.h>
+#include <sysutils/FrameworkCommand.h>
+
+FrameworkListener::FrameworkListener(const char *socketName) :
+                            SocketListener(socketName, true) {
+    mCommands = new FrameworkCommandCollection();
+}
+
+bool FrameworkListener::onDataAvailable(int socket) {
+    char buffer[101];
+    int len;
+
+    if ((len = read(socket, buffer, sizeof(buffer) -1)) < 0) {
+        LOGE("read() failed (%s)", strerror(errno));
+        return errno;
+    } else if (!len) {
+        LOGW("Lost connection to client");
+        return false;
+    }
+
+    int start = 0;
+    int i;
+
+    buffer[len] = '\0';
+
+    for (i = 0; i < len; i++) {
+        if (buffer[i] == '\0') {
+            dispatchCommand(buffer + start);
+            start = i + 1;
+        }
+    }
+    return true;
+}
+
+void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
+    mCommands->push_back(cmd);
+}
+
+void FrameworkListener::dispatchCommand(char *cmd) {
+    FrameworkCommandCollection::iterator i;
+
+    for (i = mCommands->begin(); i != mCommands->end(); ++i) {
+        FrameworkCommand *c = *i;
+
+        if (!strncmp(cmd, c->getCommand(), strlen(c->getCommand()))) {
+            if (c->runCommand(cmd)) {
+                LOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
+            }
+            return;
+        }
+    }
+
+    LOGE("No cmd handlers defined for '%s'", cmd);
+}
+
diff --git a/libsysutils/src/FrameworkManager.cpp b/libsysutils/src/FrameworkManager.cpp
new file mode 100644
index 0000000..5dceb9f
--- /dev/null
+++ b/libsysutils/src/FrameworkManager.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <cutils/config_utils.h>
+#include <cutils/cpu_info.h>
+#include <cutils/properties.h>
+#include <cutils/sockets.h>
+
+#define LOG_TAG "FrameworkManager"
+#include <cutils/log.h>
+
+#include <sysutils/FrameworkManager.h>
+#include <sysutils/FrameworkListener.h>
+
+FrameworkManager::FrameworkManager(FrameworkListener *Listener) {
+    mDoorbell = -1;
+    mFwSock = -1;
+    mListener = Listener;
+
+    pthread_mutex_init(&mWriteMutex, NULL);
+}
+
+int FrameworkManager::run() {
+
+    if (mListener->run()) {
+        LOGE("Error running listener (%s)", strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+/* ========
+ * Privates
+ * ========
+ */
+
+int FrameworkManager::sendMsg(char *msg) {
+    LOGD("FrameworkManager::sendMsg(%s)", msg);
+    if (mFwSock < 0) {
+        errno = EHOSTUNREACH;
+        return -1;
+    }
+
+    pthread_mutex_lock(&mWriteMutex);
+    if (write(mFwSock, msg, strlen(msg) +1) < 0) {
+        LOGW("Unable to send msg '%s' (%s)", msg, strerror(errno));
+    }
+    pthread_mutex_unlock(&mWriteMutex);
+    return 0;
+}
+
+int FrameworkManager::sendMsg(char *msg, char *data) {
+    char *buffer = (char *) alloca(strlen(msg) + strlen(data) + 1);
+    if (!buffer) {
+        errno = -ENOMEM;
+        return -1;
+    }
+    strcpy(buffer, msg);
+    strcat(buffer, data);
+    return sendMsg(buffer);
+}
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
new file mode 100644
index 0000000..1e94ed1
--- /dev/null
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "NetlinkEvent"
+#include <cutils/log.h>
+
+#include <sysutils/NetlinkEvent.h>
+
+const int NetlinkEvent::NlActionUnknown = 0;
+const int NetlinkEvent::NlActionAdd = 1;
+const int NetlinkEvent::NlActionRemove = 2;
+const int NetlinkEvent::NlActionChange = 3;
+
+NetlinkEvent::NetlinkEvent() {
+    mAction = NlActionUnknown;
+}
+
+NetlinkEvent::~NetlinkEvent() {
+    int i;
+    if (mPath)
+        free(mPath);
+    if (mSubsystem)
+        free(mSubsystem);
+    for (i = 0; i < NL_PARAMS_MAX; i++) {
+        if (!mParams[i])
+            break;
+        free(mParams[i]);
+    }
+}
+
+bool NetlinkEvent::decode(char *buffer, int size) {
+    char *s = buffer;
+    char *end;
+    int param_idx = 0;
+    int i;
+    int first = 1;
+
+    end = s + size;
+    while (s < end) {
+        if (first) {
+            char *p;
+            for (p = s; *p != '@'; p++);
+            p++;
+            mPath = strdup(p);
+            first = 0;
+        } else {
+            if (!strncmp(s, "ACTION=", strlen("ACTION="))) {
+                char *a = s + strlen("ACTION=");
+                if (!strcmp(a, "add"))
+                    mAction = NlActionAdd;
+                else if (!strcmp(a, "remove"))
+                    mAction = NlActionRemove;
+                else if (!strcmp(a, "change"))
+                    mAction = NlActionChange;
+            } else if (!strncmp(s, "SEQNUM=", strlen("SEQNUM="))) 
+                mSeq = atoi(s + strlen("SEQNUM="));
+            else if (!strncmp(s, "SUBSYSTEM=", strlen("SUBSYSTEM="))) 
+                mSubsystem = strdup(s + strlen("SUBSYSTEM="));
+            else
+                mParams[param_idx++] = strdup(s);
+        }
+        s+= strlen(s) + 1;
+    }
+    return true;
+}
+
+const char *NetlinkEvent::findParam(const char *paramName) {
+    int i;
+
+    for (i = 0; i < NL_PARAMS_MAX; i++) {
+        if (!mParams[i])
+            break;
+        if (!strncmp(mParams[i], paramName, strlen(paramName)))
+            return &mParams[i][strlen(paramName) + 1];
+    }
+
+    LOGE("NetlinkEvent::FindParam(): Parameter '%s' not found", paramName);
+    return NULL;
+}
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
new file mode 100644
index 0000000..96a616d
--- /dev/null
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <string.h>
+
+#define LOG_TAG "NetlinkListener"
+#include <cutils/log.h>
+
+#include <sysutils/NetlinkListener.h>
+#include <sysutils/NetlinkEvent.h>
+
+NetlinkListener::NetlinkListener(int socket) :
+                            SocketListener(socket, false) {
+}
+
+bool NetlinkListener::onDataAvailable(int socket)
+{
+    LOGD("NetlinkListener::onDataAvailable()");
+
+    int count;
+
+    if ((count = recv(socket, mBuffer, sizeof(mBuffer), 0)) < 0) {
+        LOGE("recv failed (%s)", strerror(errno));
+        return false;
+    }
+
+    NetlinkEvent *evt = new NetlinkEvent();
+    if (!evt->decode(mBuffer, count)) {
+        LOGE("Error decoding NetlinkEvent");
+        goto out;
+    }
+
+    LOGD("Ignoring '%s' netlink event", evt->getSubsystem());
+
+out:
+    delete evt;
+    return true;
+}
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
new file mode 100644
index 0000000..f92e30d
--- /dev/null
+++ b/libsysutils/src/SocketListener.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#define LOG_TAG "SocketListener"
+#include <cutils/log.h>
+
+#include <cutils/sockets.h>
+
+#include <sysutils/SocketListener.h>
+
+SocketListener::SocketListener(const char *socketName, bool acceptClients) {
+    mAcceptClients = acceptClients;
+    mCsock = -1;
+    mSocketName = socketName;
+    mSock = -1;
+}
+
+SocketListener::SocketListener(int socketFd, bool acceptClients) {
+    mAcceptClients = acceptClients;
+    mCsock = -1;
+    mSocketName = NULL;
+    mSock = socketFd;
+}
+
+int SocketListener::run() {
+
+    if (!mSocketName && mSock == -1) {
+        errno = EINVAL;
+        return -1;
+    } else if (mSocketName) {
+        if ((mSock = android_get_control_socket(mSocketName)) < 0) {
+            LOGE("Obtaining file descriptor socket '%s' failed: %s",
+                 mSocketName, strerror(errno));
+            return -1;
+        }
+    }
+
+    if (mAcceptClients) {
+        if (listen(mSock, 4) < 0) {
+            LOGE("Unable to listen on socket (%s)", strerror(errno));
+            return -1;
+        }
+    }
+
+    while(1) {
+        fd_set read_fds;
+        struct timeval to;
+        int max = 0;
+        int rc = 0;
+
+        to.tv_sec = 60 * 60;
+        to.tv_usec = 0;
+
+        FD_ZERO(&read_fds);
+
+        if ((mAcceptClients == false) ||
+            (mAcceptClients == true && mCsock == -1)) {
+            FD_SET(mSock, &read_fds);
+            max = mSock;
+        } else if (mCsock != -1) {
+            FD_SET(mCsock, &read_fds);
+            max = mCsock;
+        }
+
+        if ((rc = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) {
+            LOGE("select failed (%s)", strerror(errno));
+            return -errno;
+        } else if (!rc)
+            continue;
+        else if (FD_ISSET(mSock, &read_fds)) {
+            /*
+             * If we're accepting client connections then 
+             * accept and gobble the event. Otherwise
+             * pass it on to the handlers.
+             */
+            if (mAcceptClients) {
+                struct sockaddr addr;
+                socklen_t alen = sizeof(addr);
+
+                if ((mCsock = accept(mSock, &addr, &alen)) < 0) {
+                    LOGE("accept failed (%s)", strerror(errno));
+                    return -errno;
+                }
+                LOGD("SocketListener client connection accepted");
+            } else if (!onDataAvailable(mSock)) {
+                LOGW("SocketListener closing listening socket (Will shut down)");
+                close(mSock);
+                return -ESHUTDOWN;
+            }
+        } else if ((FD_ISSET(mCsock, &read_fds)) &&
+                   !onDataAvailable(mCsock)) {
+                /*
+                 * Once mCsock == -1, we'll start
+                 * accepting connections on mSock again.
+                 */
+                LOGD("SocketListener closing client socket");
+                close(mCsock);
+                mCsock = -1;
+            }
+    }
+    return 0;
+}
+
+bool SocketListener::onDataAvailable(int socket) {
+    return false;
+}
diff --git a/nexus/Android.mk b/nexus/Android.mk
new file mode 100644
index 0000000..e76cb27
--- /dev/null
+++ b/nexus/Android.mk
@@ -0,0 +1,47 @@
+BUILD_NEXUS := false
+ifeq ($(BUILD_NEXUS),true)
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                          \
+                  main.cpp                 \
+                  NetworkManager.cpp       \
+                  CommandListener.cpp      \
+                  Controller.cpp           \
+                  WifiController.cpp       \
+                  LoopController.cpp       \
+                  NexusCommand.cpp         \
+                  TiwlanWifiController.cpp \
+                  Supplicant.cpp           \
+                  SupplicantEvent.cpp      \
+                  SupplicantListener.cpp   \
+                  VpnController.cpp        \
+                  ScanResult.cpp           \
+
+LOCAL_MODULE:= nexus
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+
+LOCAL_CFLAGS := 
+
+LOCAL_SHARED_LIBRARIES := libsysutils libwpa_client libutils
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES:=          \
+                  nexctl.c \
+
+LOCAL_MODULE:= nexctl
+
+LOCAL_C_INCLUDES := $(KERNEL_HEADERS)
+
+LOCAL_CFLAGS := 
+
+LOCAL_SHARED_LIBRARIES := libutils
+
+include $(BUILD_EXECUTABLE)
+
+endif # ifeq ($(BUILD_NEXUS),true)
diff --git a/nexus/CommandListener.cpp b/nexus/CommandListener.cpp
new file mode 100644
index 0000000..cdb1db5
--- /dev/null
+++ b/nexus/CommandListener.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <errno.h>
+
+#define LOG_TAG "CommandListener"
+#include <cutils/log.h>
+
+#include "CommandListener.h"
+#include "Controller.h"
+#include "NetworkManager.h"
+#include "WifiController.h"
+
+CommandListener::CommandListener(NetworkManager *netman) :
+                 FrameworkListener("nexus") {
+    mNetman = netman;
+
+    registerCmd(new WifiEnableCmd(netman));
+    registerCmd(new WifiDisableCmd(netman));
+    registerCmd(new WifiScanCmd(netman));
+
+    registerCmd(new VpnEnableCmd(netman));
+    registerCmd(new VpnDisableCmd(netman));
+}
+ 
+/* -------------
+ * Wifi Commands
+ * ------------ */
+
+CommandListener::WifiEnableCmd::WifiEnableCmd(NetworkManager *netman) :
+                 NexusCommand("wifi_enable", netman) {
+} 
+               
+int CommandListener::WifiEnableCmd::runCommand(char *data) {
+    Controller *c = mNetman->findController("WIFI");
+    char buffer[32];
+
+    sprintf(buffer, "WIFI_ENABLE:%d", (c->enable() ? errno : 0));
+    mNetman->getFrameworkManager()->sendMsg(buffer);
+    return 0;
+}
+
+CommandListener::WifiDisableCmd::WifiDisableCmd(NetworkManager *netman) :
+                 NexusCommand("wifi_disable", netman) {
+} 
+               
+int CommandListener::WifiDisableCmd::runCommand(char *data) {
+    Controller *c = mNetman->findController("WIFI");
+    char buffer[32];
+
+    sprintf(buffer, "WIFI_DISABLE:%d", (c->disable() ? errno : 0));
+    mNetman->getFrameworkManager()->sendMsg(buffer);
+    return 0;
+}
+
+CommandListener::WifiScanCmd::WifiScanCmd(NetworkManager *netman) :
+                 NexusCommand("wifi_scan", netman) {
+} 
+
+int CommandListener::WifiScanCmd::runCommand(char *data) {
+    LOGD("WifiScanCmd(%s)", data);
+    WifiController *wc = (WifiController *) mNetman->findController("WIFI");
+    char buffer[32];
+    int mode = 0;
+    char *bword, *last;
+
+    if (!(bword = strtok_r(data, ":", &last))) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    if (!(bword = strtok_r(NULL, ":", &last))) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    mode = atoi(bword);
+
+    sprintf(buffer, "WIFI_SCAN:%d", (wc->setScanMode(mode) ? errno : 0));
+    mNetman->getFrameworkManager()->sendMsg(buffer);
+    return 0;
+}
+
+/* ------------
+ * Vpn Commands
+ * ------------ */
+CommandListener::VpnEnableCmd::VpnEnableCmd(NetworkManager *netman) :
+                 NexusCommand("vpn_enable", netman) {
+} 
+               
+int CommandListener::VpnEnableCmd::runCommand(char *data) {
+    Controller *c = mNetman->findController("VPN");
+    char buffer[32];
+
+    sprintf(buffer, "VPN_ENABLE:%d", (c->enable() ? errno : 0));
+    mNetman->getFrameworkManager()->sendMsg(buffer);
+    return 0;
+}
+
+CommandListener::VpnDisableCmd::VpnDisableCmd(NetworkManager *netman) :
+                 NexusCommand("vpn_disable", netman) {
+} 
+               
+int CommandListener::VpnDisableCmd::runCommand(char *data) {
+    Controller *c = mNetman->findController("VPN");
+    char buffer[32];
+
+    sprintf(buffer, "VPN_DISABLE:%d", (c->disable() ? errno : 0));
+    mNetman->getFrameworkManager()->sendMsg(buffer);
+    return 0;
+}
diff --git a/nexus/CommandListener.h b/nexus/CommandListener.h
new file mode 100644
index 0000000..261c093
--- /dev/null
+++ b/nexus/CommandListener.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _COMMANDLISTENER_H__
+#define _COMMANDLISTENER_H__
+
+#include <sysutils/FrameworkListener.h>
+#include "NexusCommand.h"
+
+class NetworkManager;
+
+class CommandListener : public FrameworkListener {
+protected:
+    NetworkManager *mNetman;
+
+public:
+    CommandListener(NetworkManager *netman);
+    virtual ~CommandListener() {}
+
+private:
+    class WifiEnableCmd : public NexusCommand {
+    public:
+        WifiEnableCmd(NetworkManager *);
+        virtual ~WifiEnableCmd() {}
+        int runCommand(char *data);
+    };
+
+    class WifiDisableCmd : public NexusCommand {
+    public:
+        WifiDisableCmd(NetworkManager *);
+        virtual ~WifiDisableCmd() {}
+        int runCommand(char *data);
+    };
+
+    class WifiScanCmd : public NexusCommand {
+    public:
+        WifiScanCmd(NetworkManager *);
+        virtual ~WifiScanCmd() {}
+        int runCommand(char *data);
+    };
+
+    class VpnEnableCmd : public NexusCommand {
+    public:
+        VpnEnableCmd(NetworkManager *);
+        virtual ~VpnEnableCmd() {}
+        int runCommand(char *data);
+    };
+
+    class VpnDisableCmd : public NexusCommand {
+    public:
+        VpnDisableCmd(NetworkManager *);
+        virtual ~VpnDisableCmd() {}
+        int runCommand(char *data);
+    };
+
+};
+
+#endif
diff --git a/nexus/Controller.cpp b/nexus/Controller.cpp
new file mode 100644
index 0000000..cc1a187
--- /dev/null
+++ b/nexus/Controller.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define LOG_TAG "Controller"
+
+#include <cutils/log.h>
+
+#include "Controller.h"
+
+extern "C" int init_module(void *, unsigned int, const char *);
+extern "C" int delete_module(const char *, unsigned int);
+
+Controller::Controller(const char *name) {
+    mName = name;
+}
+
+int Controller::start() {
+    return 0;
+}
+
+int Controller::stop() {
+    return 0;
+}
+
+int Controller::loadKernelModule(char *modpath, const char *args) {
+    void *module;
+    unsigned int size;
+
+    LOGD("loadKernelModule(%s, %s)", modpath, args);
+
+    module = loadFile(modpath, &size);
+    if (!module) {
+        errno = -EIO;
+        return -1;
+    }
+
+    int rc = init_module(module, size, args);
+    free (module);
+    return rc;
+}
+
+int Controller::unloadKernelModule(const char *modtag) {
+    int rc = -1;
+    int retries = 10;
+
+    LOGD("unloadKernelModule(%s)", modtag);
+    while (retries--) {
+        rc = delete_module(modtag, O_NONBLOCK | O_EXCL);
+        if (rc < 0 && errno == EAGAIN)
+            usleep(1000*500);
+        else
+            break;
+    }
+
+    if (rc != 0) {
+        LOGW("Unable to unload kernel driver '%s' (%s)", modtag, 
+             strerror(errno));
+    }
+    return rc;
+}
+
+bool Controller::isKernelModuleLoaded(const char *modtag) {
+    FILE *fp = fopen("/proc/modules", "r");
+
+    if (!fp) {
+        LOGE("Unable to open /proc/modules (%s)", strerror(errno));
+        return false;
+    }
+
+    char line[255];
+    while(fgets(line, sizeof(line), fp)) {
+        char *endTag = strchr(line, ' ');
+
+        if (!endTag) {
+            LOGW("Unable to find tag for line '%s'", line);
+            continue;
+        }
+        if (!strncmp(line, modtag, (endTag - line))) {
+            fclose(fp);
+            return true;
+        }
+    }
+
+    fclose(fp);
+    return false;
+}
+
+
+void *Controller::loadFile(char *filename, unsigned int *_size)
+{
+	int ret, fd;
+	struct stat sb;
+	ssize_t size;
+	void *buffer = NULL;
+
+	/* open the file */
+	fd = open(filename, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	/* find out how big it is */
+	if (fstat(fd, &sb) < 0)
+		goto bail;
+	size = sb.st_size;
+
+	/* allocate memory for it to be read into */
+	buffer = malloc(size);
+	if (!buffer)
+		goto bail;
+
+	/* slurp it into our buffer */
+	ret = read(fd, buffer, size);
+	if (ret != size)
+		goto bail;
+
+	/* let the caller know how big it is */
+	*_size = size;
+
+bail:
+	close(fd);
+	return buffer;
+}
diff --git a/nexus/Controller.h b/nexus/Controller.h
new file mode 100644
index 0000000..8e69978
--- /dev/null
+++ b/nexus/Controller.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _CONTROLLER_H
+#define _CONTROLLER_H
+
+#include "../../../frameworks/base/include/utils/List.h"
+
+class Controller {
+private:
+    const char *mName;
+    
+public:
+    Controller(const char *name);
+    virtual ~Controller() {}
+
+    virtual int start();
+    virtual int stop();
+
+    virtual int enable() = 0;
+    virtual int disable() = 0;
+
+    virtual const char *getName() { return mName; }
+
+protected:
+    int loadKernelModule(char *modpath, const char *args);
+    bool isKernelModuleLoaded(const char *modtag);
+    int unloadKernelModule(const char *modtag);
+
+private:
+    void *loadFile(char *filename, unsigned int *_size);
+};
+
+typedef android::List<Controller *> ControllerCollection;
+#endif
diff --git a/nexus/LoopController.cpp b/nexus/LoopController.cpp
new file mode 100644
index 0000000..38037ac
--- /dev/null
+++ b/nexus/LoopController.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+
+#include "LoopController.h"
+
+LoopController::LoopController() :
+                Controller("LOOP") {
+}
+
+int LoopController::enable() {
+    errno = ENOSYS;
+    return -1;
+}
+
+int LoopController::disable() {
+    errno = ENOSYS;
+    return -1;
+}
diff --git a/nexus/LoopController.h b/nexus/LoopController.h
new file mode 100644
index 0000000..8b89977
--- /dev/null
+++ b/nexus/LoopController.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _LOOP_CONTROLLER_H
+#define _LOOP_CONTROLLER_H
+
+#include "Controller.h"
+
+class LoopController : public Controller {
+public:
+    LoopController();
+    virtual ~LoopController() {}
+
+    int enable();
+    int disable();
+};
+
+#endif
diff --git a/nexus/NetworkManager.cpp b/nexus/NetworkManager.cpp
new file mode 100644
index 0000000..9c64945
--- /dev/null
+++ b/nexus/NetworkManager.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <errno.h>
+
+#define LOG_TAG "Nexus"
+
+#include <cutils/log.h>
+
+#include "NetworkManager.h"
+#include "CommandListener.h"
+#include "LoopController.h"
+#include "VpnController.h"
+
+#include "TiwlanWifiController.h"
+
+NetworkManager::NetworkManager() {
+    mListener = new CommandListener(this);
+    mFm = new FrameworkManager(mListener);
+    mControllers = new ControllerCollection();
+}
+
+int NetworkManager::run() {
+    LOGD("NetworkManager::start()");
+
+    // XXX: Factory needed
+    addController(new LoopController());
+    addController(new TiwlanWifiController("/system/lib/modules/wlan.ko", "wlan", ""));
+    addController(new VpnController());
+    //addController(new GenericController("rmnet0"));
+
+    if (startControllers()) {
+        LOGW("Unable to start all controllers (%s)", strerror(errno));
+    }
+    mFm->run();
+    return 0;
+}
+
+void NetworkManager::addController(Controller *c) {
+    mControllers->push_back(c);
+}
+
+int NetworkManager::startControllers() {
+    int rc = 0;
+    ControllerCollection::iterator i;
+
+    for (i = mControllers->begin(); i != mControllers->end(); ++i) {
+        int irc = (*i)->start();
+        LOGD("Controller '%s' start rc = %d", (*i)->getName(), irc);
+        if (irc && !rc) 
+            rc = irc;
+    }
+    return rc;
+}
+
+int NetworkManager::stopControllers() {
+    int rc = 0;
+    ControllerCollection::iterator i;
+
+    for (i = mControllers->begin(); i != mControllers->end(); ++i) {
+        int irc = (*i)->stop();
+        LOGD("Controller '%s' stop rc = %d", (*i)->getName(), irc);
+        if (irc && !rc) 
+            rc = irc;
+    }
+    return rc;
+}
+
+Controller *NetworkManager::findController(const char *name) {
+    ControllerCollection::iterator i;
+    for (i = mControllers->begin(); i != mControllers->end(); ++i) {
+        if (!strcmp((*i)->getName(), name))
+            return *i;
+    }
+    LOGW("Controller '%s' not found", name);
+    return NULL;
+}
+
+int NetworkManager::onInterfaceCreated(Controller *c, char *name) {
+    LOGD("Interface %s created by controller %s", name, c->getName());
+    return 0;
+}
+
+int NetworkManager::onInterfaceDestroyed(Controller *c, char *name) {
+    LOGD("Interface %s destroyed by controller %s", name, c->getName());
+    return 0;
+}
diff --git a/nexus/NetworkManager.h b/nexus/NetworkManager.h
new file mode 100644
index 0000000..8f362a9
--- /dev/null
+++ b/nexus/NetworkManager.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _NETWORKMANAGER_H
+#define _NETWORKMANAGER_H
+
+#include "Controller.h"
+
+#include <sysutils/FrameworkManager.h>
+
+class NetworkManager {
+private:
+    FrameworkListener    *mListener;
+    FrameworkManager     *mFm;
+    ControllerCollection *mControllers;
+
+public:
+    NetworkManager();
+    virtual ~NetworkManager() {}
+
+    int run();
+
+private:
+    void addController(Controller *c);
+    int startControllers();
+    int stopControllers();
+
+public:
+    Controller *findController(const char *name);
+    ControllerCollection *getControllers() { return mControllers; }
+    FrameworkManager *getFrameworkManager() { return mFm; }
+
+public:
+// XXX: Extract these into an interface
+    int onInterfaceCreated(Controller *c, char *name);
+    int onInterfaceDestroyed(Controller *c, char *name);
+
+};
+#endif
diff --git a/nexus/NexusCommand.cpp b/nexus/NexusCommand.cpp
new file mode 100644
index 0000000..090113c
--- /dev/null
+++ b/nexus/NexusCommand.cpp
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "NexusCommand.h"
+
+NexusCommand::NexusCommand(const char *cmd, NetworkManager *netman) :
+              FrameworkCommand(cmd)  {
+    mNetman = netman;
+}
diff --git a/nexus/NexusCommand.h b/nexus/NexusCommand.h
new file mode 100644
index 0000000..204541c
--- /dev/null
+++ b/nexus/NexusCommand.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _NEXUS_COMMAND_H
+#define _NEXUS_COMMAND_H
+
+#include <sysutils/FrameworkCommand.h>
+
+class NetworkManager;
+
+class NexusCommand : public FrameworkCommand {
+protected:
+    NetworkManager *mNetman;
+
+public:
+    NexusCommand(const char *cmd, NetworkManager *netman);
+    virtual ~NexusCommand() {}
+};
+
+#endif
diff --git a/nexus/ScanResult.cpp b/nexus/ScanResult.cpp
new file mode 100644
index 0000000..b7237b5
--- /dev/null
+++ b/nexus/ScanResult.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+
+#define LOG_TAG "ScanResult"
+#include <cutils/log.h>
+
+#include "ScanResult.h"
+
+ScanResult::ScanResult() {
+}
+
+ScanResult::ScanResult(char *rawResult) {
+    char *tok, *next = NULL;
+
+    if (!(tok = strtok_r(rawResult, "\t", &next)))
+        goto out_bad;
+    mBssid = strdup(tok);
+
+    if (!(tok = strtok_r(NULL, "\t", &next)))
+        goto out_bad;
+    mFreq = atoi(tok);
+
+    if (!(tok = strtok_r(NULL, "\t", &next)))
+        goto out_bad;
+    mLevel = atoi(tok);
+
+    if (!(tok = strtok_r(rawResult, "\t", &next)))
+        goto out_bad;
+    mFlags = strdup(tok);
+
+    if (!(tok = strtok_r(rawResult, "\t", &next)))
+        goto out_bad;
+    mSsid = strdup(tok);
+
+    return;
+
+ out_bad:
+    LOGW("Malformatted scan result (%s)", rawResult);
+}
+
+ScanResult::~ScanResult() {
+    if (mBssid)
+        free(mBssid);
+    if (mFlags)
+        free(mFlags);
+    if (mSsid)
+        free(mSsid);
+}
+
+ScanResult *ScanResult::clone() {
+    ScanResult *r = new ScanResult();
+
+    r->mBssid = strdup(mBssid);
+    r->mFreq = mFreq;
+    r->mLevel = mLevel;
+    r->mFlags = strdup(mFlags);
+    r->mSsid = strdup(mSsid);
+
+    return r;
+}
diff --git a/nexus/ScanResult.h b/nexus/ScanResult.h
new file mode 100644
index 0000000..79b2b65
--- /dev/null
+++ b/nexus/ScanResult.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _SCAN_RESULT_H
+#define _SCAN_RESULT_H
+
+#include <sys/types.h>
+
+#include "../../../frameworks/base/include/utils/List.h"
+
+class ScanResult {
+    char     *mBssid;
+    uint32_t mFreq;
+    int      mLevel;
+    char     *mFlags;
+    char     *mSsid;
+
+private:
+    ScanResult();
+
+public:
+    ScanResult(char *rawResult);
+    virtual ~ScanResult();
+
+    ScanResult *clone();
+
+    const char *getBssid() { return mBssid; }
+    uint32_t getFreq() { return mFreq; }
+    const char *getFlags() { return mFlags; }
+    const char *getSsid() { return mSsid; }
+};
+
+typedef android::List<ScanResult *> ScanResultCollection;
+
+#endif
diff --git a/nexus/Supplicant.cpp b/nexus/Supplicant.cpp
new file mode 100644
index 0000000..215a8b3
--- /dev/null
+++ b/nexus/Supplicant.cpp
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+
+#define LOG_TAG "Supplicant"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#undef HAVE_LIBC_SYSTEM_PROPERTIES
+
+#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
+#endif
+
+#include "Supplicant.h"
+#include "SupplicantListener.h"
+#include "SupplicantState.h"
+#include "SupplicantEvent.h"
+#include "ScanResult.h"
+
+#include "libwpa_client/wpa_ctrl.h"
+
+#define IFACE_DIR        "/data/system/wpa_supplicant"
+#define DRIVER_PROP_NAME "wlan.driver.status"
+#define SUPPLICANT_NAME  "wpa_supplicant"
+#define SUPP_PROP_NAME   "init.svc.wpa_supplicant"
+
+Supplicant::Supplicant() {
+    mCtrl = NULL;
+    mMonitor = NULL;
+    mListener = NULL;
+
+    mState = SupplicantState::UNKNOWN;
+
+    mLatestScanResults = new ScanResultCollection();
+
+    pthread_mutex_init(&mLatestScanResultsLock, NULL);
+}
+
+int Supplicant::start() {
+    LOGD("start():");
+    // XXX: Validate supplicant config file
+    
+    char status[PROPERTY_VALUE_MAX] = {'\0'};
+    int count = 200;
+#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
+    const prop_info *pi;
+    unsigned int serial = 0;
+#endif
+
+    if (property_get(SUPP_PROP_NAME, status, NULL) &&
+        strcmp(status, "running") == 0) {
+        return 0;
+    }
+
+    wpa_ctrl_cleanup();
+#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
+    pi = __system_property_find(SUPP_PROP_NAME);
+    if (pi != NULL)
+        serial = pi->serial;
+#endif
+
+    property_set("ctl.start", SUPPLICANT_NAME);
+    sched_yield();
+    while (count--) {
+#ifdef HAVE_LIBC_SYSTEM_PROPERTIES
+        if (!pi)
+            pi = __system_property_find(SUPP_PROP_NAME);
+        if (pi) {
+            __system_property_read(pi, NULL, status);
+            if (strcmp(status, "running") == 0)
+                return 0;
+            else if (pi->serial != serial &&
+                    strcmp(status, "stopped") == 0) {
+                errno = EIO;
+                return -1;
+            }
+        }
+#else
+        if (property_get(SUPP_PROP_NAME, status, NULL)) {
+            if (strcmp(status, "running") == 0)
+                break;
+        }
+#endif
+        usleep(100000);
+    }
+
+    if (!count) {
+        errno = ETIMEDOUT;
+        return -1;
+    }
+  
+    if (connectToSupplicant()) {
+        LOGE("Error connecting to supplicant (%s)\n", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+int Supplicant::stop() {
+    LOGD("stop()");
+
+    char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+    int count = 50; 
+
+    if (mListener->stopListener()) {
+        LOGW("Unable to stop supplicant listener (%s)", strerror(errno));
+        return -1;
+    }
+
+    if (property_get(SUPP_PROP_NAME, supp_status, NULL)
+        && strcmp(supp_status, "stopped") == 0) {
+        return 0;
+    }
+
+    property_set("ctl.stop", SUPPLICANT_NAME);
+    sched_yield();
+
+    while (count-- > 0) {
+        if (property_get(SUPP_PROP_NAME, supp_status, NULL)) {
+            if (strcmp(supp_status, "stopped") == 0)
+                break;
+        }
+        usleep(100000);
+    }
+
+    if (mCtrl) {
+        wpa_ctrl_close(mCtrl);
+        mCtrl = NULL;
+    }
+    if (mMonitor) {
+        wpa_ctrl_close(mMonitor);
+        mMonitor = NULL;
+    }
+
+    if (!count) {
+        LOGD("Timed out waiting for supplicant to stop");
+        errno = ETIMEDOUT;
+        return -1;
+    }
+
+    LOGD("Stopped OK");
+
+    return 0;
+}
+
+bool Supplicant::isStarted() {
+    char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+    if (!property_get(SUPP_PROP_NAME, supp_status, NULL) ||
+        !strcmp(supp_status, "running")) {
+        return false;
+    }
+    return true;
+}
+
+int Supplicant::connectToSupplicant() {
+    char ifname[256];
+    char supp_status[PROPERTY_VALUE_MAX] = {'\0'};
+
+    if (!property_get(SUPP_PROP_NAME, supp_status, NULL)
+            || strcmp(supp_status, "running") != 0) {
+        LOGE("Supplicant not running, cannot connect");
+        return -1;
+    }
+
+    mCtrl = wpa_ctrl_open("tiwlan0");
+    if (mCtrl == NULL) {
+        LOGE("Unable to open connection to supplicant on \"%s\": %s",
+             "tiwlan0", strerror(errno));
+        return -1;
+    }
+    mMonitor = wpa_ctrl_open("tiwlan0");
+    if (mMonitor == NULL) {
+        wpa_ctrl_close(mCtrl);
+        mCtrl = NULL;
+        return -1;
+    }
+    if (wpa_ctrl_attach(mMonitor) != 0) {
+        wpa_ctrl_close(mMonitor);
+        wpa_ctrl_close(mCtrl);
+        mCtrl = mMonitor = NULL;
+        return -1;
+    }
+
+    mListener = new SupplicantListener(this, mMonitor);
+    
+    if (mListener->startListener()) {
+        LOGE("Error - unable to start supplicant listener");
+        stop();
+        return -1;
+    }
+    return 0;
+}
+
+int Supplicant::sendCommand(const char *cmd, char *reply, size_t *reply_len)
+{
+    if (!mCtrl) {
+        errno = ENOTCONN;
+        return -1;
+    }
+
+    LOGD("sendCommand(): -> '%s'", cmd);
+
+    int rc;
+    if ((rc = wpa_ctrl_request(mCtrl, cmd, strlen(cmd), reply, reply_len, NULL)) == -2)  {
+        errno = ETIMEDOUT;
+        return -1;
+    } else if (rc < 0 || !strncmp(reply, "FAIL", 4)) {
+        errno = EIO;
+        return -1;
+    }
+
+    if (!strncmp(cmd, "PING", 4) ||
+        !strncmp(cmd, "SCAN_RESULTS", 12)) 
+        reply[*reply_len] = '\0';
+
+    LOGD("sendCommand(): <- '%s'", reply);
+    return 0;
+}
+
+int Supplicant::triggerScan(bool active) {
+    char reply[255];
+    size_t len = sizeof(reply);
+
+    if (sendCommand((active ? "DRIVER SCAN-ACTIVE" : "DRIVER SCAN-PASSIVE"),
+                     reply, &len)) {
+        LOGW("triggerScan(%d): Error setting scan mode (%s)", active,
+             strerror(errno));
+        return -1;
+    }
+    len = sizeof(reply);
+
+    if (sendCommand("SCAN", reply, &len)) {
+        LOGW("triggerScan(%d): Error initiating scan", active);
+        return -1;
+    }
+    return 0;
+}
+
+int Supplicant::onConnectedEvent(SupplicantEvent *evt) {
+    LOGD("onConnectedEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onDisconnectedEvent(SupplicantEvent *evt) {
+    LOGD("onDisconnectedEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onTerminatingEvent(SupplicantEvent *evt) {
+    LOGD("onTerminatingEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onPasswordChangedEvent(SupplicantEvent *evt) {
+    LOGD("onPasswordChangedEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onEapNotificationEvent(SupplicantEvent *evt) {
+    LOGD("onEapNotificationEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onEapStartedEvent(SupplicantEvent *evt) {
+    LOGD("onEapStartedEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onEapMethodEvent(SupplicantEvent *evt) {
+    LOGD("onEapMethodEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onEapSuccessEvent(SupplicantEvent *evt) {
+    LOGD("onEapSuccessEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onEapFailureEvent(SupplicantEvent *evt) {
+    LOGD("onEapFailureEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onScanResultsEvent(SupplicantEvent *evt) {
+    LOGD("onScanResultsEvent(%s)", evt->getEvent());
+
+    if (!strcmp(evt->getEvent(), "Ready")) {
+        char *reply;
+
+        if (!(reply = (char *) malloc(4096))) {
+            errno = -ENOMEM;
+            return -1;
+        }
+
+        size_t len = 4096;
+ 
+        if (sendCommand("SCAN_RESULTS", reply, &len)) {
+            LOGW("onScanResultsEvent(%s): Error getting scan results (%s)",
+                  evt->getEvent(), strerror(errno));
+            free(reply);
+            return -1;
+        }
+
+        pthread_mutex_lock(&mLatestScanResultsLock);
+        if (!mLatestScanResults->empty()) {
+            ScanResultCollection::iterator i;
+
+            for (i = mLatestScanResults->begin();
+                 i !=mLatestScanResults->end(); ++i) {
+                delete *i;
+            }
+            mLatestScanResults->clear();
+        }
+
+        char *linep;
+        char *linep_next = NULL;
+
+        if (!strtok_r(reply, "\n", &linep_next)) {
+            free(reply);
+            return 0;;
+        }
+
+        while((linep = strtok_r(NULL, "\n", &linep_next)))
+            mLatestScanResults->push_back(new ScanResult(linep));
+
+        pthread_mutex_unlock(&mLatestScanResultsLock);
+        free(reply);
+    } else {
+        LOGW("Unknown SCAN_RESULTS event (%s)", evt->getEvent());
+    }
+    return 0;
+}
+
+int Supplicant::onStateChangeEvent(SupplicantEvent *evt) {
+    LOGD("onStateChangeEvent(%s)", evt->getEvent());
+    // XXX: Update mState
+    return 0;
+}
+
+int Supplicant::onLinkSpeedEvent(SupplicantEvent *evt) {
+    LOGD("onLinkSpeedEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+int Supplicant::onDriverStateEvent(SupplicantEvent *evt) {
+    LOGD("onDriverStateEvent(%s)", evt->getEvent());
+    return 0;
+}
+
+// XXX: Use a cursor + smartptr instead
+const ScanResultCollection *Supplicant::getLatestScanResults() {
+    ScanResultCollection *d = new ScanResultCollection();
+    ScanResultCollection::iterator i;
+
+    pthread_mutex_lock(&mLatestScanResultsLock);
+    for (i = mLatestScanResults->begin(); i != mLatestScanResults->end(); ++i) {
+        d->push_back((*i)->clone());
+    }
+
+    pthread_mutex_unlock(&mLatestScanResultsLock);
+    return d;
+};
diff --git a/nexus/Supplicant.h b/nexus/Supplicant.h
new file mode 100644
index 0000000..46a9e86
--- /dev/null
+++ b/nexus/Supplicant.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _SUPPLICANT_H
+#define _SUPPLICANT_H
+
+struct wpa_ctrl;
+class SupplicantListener;
+class SupplicantEvent;
+
+#include <pthread.h>
+
+#include "ScanResult.h"
+
+class Supplicant {
+private:
+    struct wpa_ctrl      *mCtrl;
+    struct wpa_ctrl      *mMonitor;
+    SupplicantListener   *mListener;
+    int                  mState;
+
+    ScanResultCollection *mLatestScanResults;
+    pthread_mutex_t      mLatestScanResultsLock;
+  
+public:
+    Supplicant();
+    virtual ~Supplicant() {}
+
+    virtual int start();
+    virtual int stop();
+    virtual bool isStarted();
+
+    virtual int triggerScan(bool active);
+
+    int getState() { return mState; }
+
+    const ScanResultCollection *getLatestScanResults();
+
+// XXX: Extract these into an interface
+public:
+    virtual int onConnectedEvent(SupplicantEvent *evt);
+    virtual int onDisconnectedEvent(SupplicantEvent *evt);
+    virtual int onTerminatingEvent(SupplicantEvent *evt);
+    virtual int onPasswordChangedEvent(SupplicantEvent *evt);
+    virtual int onEapNotificationEvent(SupplicantEvent *evt);
+    virtual int onEapStartedEvent(SupplicantEvent *evt);
+    virtual int onEapMethodEvent(SupplicantEvent *evt);
+    virtual int onEapSuccessEvent(SupplicantEvent *evt);
+    virtual int onEapFailureEvent(SupplicantEvent *evt);
+    virtual int onScanResultsEvent(SupplicantEvent *evt);
+    virtual int onStateChangeEvent(SupplicantEvent *evt);
+    virtual int onLinkSpeedEvent(SupplicantEvent *evt);
+    virtual int onDriverStateEvent(SupplicantEvent *evt);
+
+private:
+    int connectToSupplicant();
+    int sendCommand(const char *cmd, char *reply, size_t *reply_len);
+};
+
+#endif
diff --git a/nexus/SupplicantEvent.cpp b/nexus/SupplicantEvent.cpp
new file mode 100644
index 0000000..5c0944a
--- /dev/null
+++ b/nexus/SupplicantEvent.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+
+#define LOG_TAG "SupplicantEvent"
+#include <cutils/log.h>
+
+#include "SupplicantEvent.h"
+
+#include "libwpa_client/wpa_ctrl.h"
+
+SupplicantEvent::SupplicantEvent(char *event, size_t len) {
+
+    if (event[0] == '<') {
+        char *match = strchr(event, '>');
+        if (match) {
+            char tmp[16];
+
+            strncpy(tmp, &event[1], (match - event));
+            mLevel = atoi(tmp);
+            event += (match - event) + 1;
+        } else
+            LOGW("Unclosed level brace in event");
+    } else
+        LOGW("No level specified in event");
+
+    /*
+     * <N>CTRL-EVENT-XXX
+     *    ^
+     *    +---- event
+     */
+
+    if (!strncmp(event, WPA_EVENT_CONNECTED, strlen(WPA_EVENT_CONNECTED)))
+        mType = SupplicantEvent::EVENT_CONNECTED;
+    else if (!strncmp(event, WPA_EVENT_DISCONNECTED, strlen(WPA_EVENT_DISCONNECTED)))
+        mType = SupplicantEvent::EVENT_DISCONNECTED;
+    else if (!strncmp(event, WPA_EVENT_TERMINATING, strlen(WPA_EVENT_TERMINATING)))
+        mType = SupplicantEvent::EVENT_TERMINATING;
+    else if (!strncmp(event, WPA_EVENT_PASSWORD_CHANGED, strlen(WPA_EVENT_PASSWORD_CHANGED)))
+        mType = SupplicantEvent::EVENT_PASSWORD_CHANGED;
+    else if (!strncmp(event, WPA_EVENT_EAP_NOTIFICATION, strlen(WPA_EVENT_EAP_NOTIFICATION)))
+        mType = SupplicantEvent::EVENT_EAP_NOTIFICATION;
+    else if (!strncmp(event, WPA_EVENT_EAP_STARTED, strlen(WPA_EVENT_EAP_STARTED)))
+        mType = SupplicantEvent::EVENT_EAP_STARTED;
+    else if (!strncmp(event, WPA_EVENT_EAP_METHOD, strlen(WPA_EVENT_EAP_METHOD)))
+        mType = SupplicantEvent::EVENT_EAP_METHOD;
+    else if (!strncmp(event, WPA_EVENT_EAP_SUCCESS, strlen(WPA_EVENT_EAP_SUCCESS)))
+        mType = SupplicantEvent::EVENT_EAP_SUCCESS;
+    else if (!strncmp(event, WPA_EVENT_EAP_FAILURE, strlen(WPA_EVENT_EAP_FAILURE)))
+        mType = SupplicantEvent::EVENT_EAP_FAILURE;
+    else if (!strncmp(event, WPA_EVENT_SCAN_RESULTS, strlen(WPA_EVENT_SCAN_RESULTS)))
+        mType = SupplicantEvent::EVENT_SCAN_RESULTS;
+    else if (!strncmp(event, WPA_EVENT_STATE_CHANGE, strlen(WPA_EVENT_STATE_CHANGE)))
+        mType = SupplicantEvent::EVENT_STATE_CHANGE;
+    else if (!strncmp(event, WPA_EVENT_LINK_SPEED, strlen(WPA_EVENT_LINK_SPEED)))
+        mType = SupplicantEvent::EVENT_LINK_SPEED;
+    else if (!strncmp(event, WPA_EVENT_DRIVER_STATE, strlen(WPA_EVENT_DRIVER_STATE)))
+        mType = SupplicantEvent::EVENT_DRIVER_STATE;
+    else {
+        LOGW("Unknown supplicant event '%s'", event);
+        mType = SupplicantEvent::EVENT_UNKNOWN;
+    }
+
+    for (event; *event != ' '; event++);
+    event++;
+
+    /*
+     * <N>CTRL-EVENT-XXX YYYY
+     *                   ^
+     *                   +---- event
+     */
+
+    for (event; *event == ' '; event++);
+
+    mEvent = strdup(event);
+    mLen = len;
+}
+
+SupplicantEvent::~SupplicantEvent() {
+    if (mEvent)
+        free(mEvent);
+}
diff --git a/nexus/SupplicantEvent.h b/nexus/SupplicantEvent.h
new file mode 100644
index 0000000..86ce8cb
--- /dev/null
+++ b/nexus/SupplicantEvent.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _SUPPLICANT_EVENT_H
+#define _SUPPLICANT_EVENT_H
+
+#include <sys/types.h>
+
+class SupplicantEvent {
+private:
+    int mType;
+    char *mEvent;
+    size_t mLen;
+    int mLevel;
+
+public:
+    static const int EVENT_UNKNOWN          = 0;
+    static const int EVENT_CONNECTED        = 1;
+    static const int EVENT_DISCONNECTED     = 2;
+    static const int EVENT_TERMINATING      = 3;
+    static const int EVENT_PASSWORD_CHANGED = 4;
+    static const int EVENT_EAP_NOTIFICATION = 5;
+    static const int EVENT_EAP_STARTED      = 6;
+    static const int EVENT_EAP_METHOD       = 7;
+    static const int EVENT_EAP_SUCCESS      = 8;
+    static const int EVENT_EAP_FAILURE      = 9;
+    static const int EVENT_SCAN_RESULTS     = 10;
+    static const int EVENT_STATE_CHANGE     = 11;
+    static const int EVENT_LINK_SPEED       = 12;
+    static const int EVENT_DRIVER_STATE     = 13;
+
+public:
+    SupplicantEvent(char *event, size_t len);
+    virtual ~SupplicantEvent();
+
+    int getType() { return mType; }
+    const char *getEvent() { return mEvent; }
+    int getLen() { return mLen; }
+    int getLevel() { return mLevel; }
+};
+
+#endif
diff --git a/nexus/SupplicantListener.cpp b/nexus/SupplicantListener.cpp
new file mode 100644
index 0000000..16306b5
--- /dev/null
+++ b/nexus/SupplicantListener.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <sys/types.h>
+#include <pthread.h>
+
+#define LOG_TAG "SupplicantListener"
+#include <cutils/log.h>
+
+#include "libwpa_client/wpa_ctrl.h"
+
+#include "Supplicant.h"
+#include "SupplicantListener.h"
+#include "SupplicantEvent.h"
+
+SupplicantListener::SupplicantListener(Supplicant *supplicant, struct wpa_ctrl *monitor) :
+                    SocketListener(wpa_ctrl_get_fd(monitor), false) {
+    mSupplicant = supplicant;
+    mMonitor = monitor;
+    mThread = NULL;
+}
+
+int SupplicantListener::startListener() {
+    LOGD("startListener()");
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+
+    return pthread_create(&mThread, &attr, &SupplicantListener::threadStart, this);
+}
+
+int SupplicantListener::stopListener() {
+    errno = -ENOSYS;
+    return -1;
+}
+
+void *SupplicantListener::threadStart(void *obj) {
+    LOGD("threadStart(): Worker thread started");
+    reinterpret_cast<SupplicantListener *>(obj)->run();
+    LOGD("threadStart(): Worker thread exited");
+    return NULL;
+}
+
+bool SupplicantListener::onDataAvailable(int socket) {
+    char buf[255];
+    size_t buflen = sizeof(buf);
+    int rc;
+    size_t nread = buflen - 1;
+
+    if ((rc = wpa_ctrl_recv(mMonitor, buf, &nread))) {
+        LOGE("wpa_ctrl_recv failed (%s)", strerror(errno));
+        return -errno;
+    }
+
+    buf[nread] = '\0';
+    if (!rc && !nread) {
+        LOGD("Received EOF on supplicant socket\n");
+        strncpy(buf, WPA_EVENT_TERMINATING " - signal 0 received", buflen-1);
+        buf[buflen-1] = '\0';
+        return false;
+    }
+
+    SupplicantEvent *evt = new SupplicantEvent(buf, nread);
+
+    // XXX: Make this a factory
+    // XXX: Instead of calling Supplicant directly
+    // extract an Interface and use that instead
+    if (evt->getType() == SupplicantEvent::EVENT_CONNECTED)
+        rc = mSupplicant->onConnectedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_DISCONNECTED)
+        rc = mSupplicant->onDisconnectedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_TERMINATING)
+        rc = mSupplicant->onTerminatingEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_PASSWORD_CHANGED)
+        rc = mSupplicant->onPasswordChangedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_NOTIFICATION)
+        rc = mSupplicant->onEapNotificationEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_STARTED)
+        rc = mSupplicant->onEapStartedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_SUCCESS)
+        rc = mSupplicant->onEapSuccessEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_FAILURE)
+        rc = mSupplicant->onEapFailureEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_SCAN_RESULTS)
+        rc = mSupplicant->onScanResultsEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_STATE_CHANGE)
+        rc = mSupplicant->onStateChangeEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_LINK_SPEED)
+        rc = mSupplicant->onLinkSpeedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_DRIVER_STATE)
+        rc = mSupplicant->onDriverStateEvent(evt);
+    else {
+        LOGW("Ignoring unknown event");
+    }
+
+    delete evt;
+    
+    if (rc)
+        return false;
+    return true;
+}
diff --git a/nexus/SupplicantListener.h b/nexus/SupplicantListener.h
new file mode 100644
index 0000000..3d27a68
--- /dev/null
+++ b/nexus/SupplicantListener.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _SUPPLICANTLISTENER_H__
+#define _SUPPLICANTLISTENER_H__
+
+#include <pthread.h>
+
+#include <sysutils/SocketListener.h>
+
+struct wpa_ctrl;
+class Supplicant;
+
+class SupplicantListener: public SocketListener {
+private:
+    struct wpa_ctrl *mMonitor;
+    Supplicant      *mSupplicant;
+    pthread_t       mThread;
+
+public:
+    SupplicantListener(Supplicant *supplicant, struct wpa_ctrl *monitor);
+    virtual ~SupplicantListener() {}
+    int startListener();
+    int stopListener();
+
+    struct wpa_ctrl *getMonitor() { return mMonitor; }
+    Supplicant *getSupplicant() { return mSupplicant; }
+
+protected:
+    virtual bool onDataAvailable(int socket);
+
+private:
+    static void *threadStart(void *obj);
+};
+
+#endif
diff --git a/nexus/SupplicantState.h b/nexus/SupplicantState.h
new file mode 100644
index 0000000..f2cf603
--- /dev/null
+++ b/nexus/SupplicantState.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _SUPPLICANT_STATE_H
+#define _SUPPLICANT_STATE_H
+
+class SupplicantState {
+public:
+    static const int UNKNOWN           = 0;
+    static const int DISCONNECTED      = 1;
+    static const int INACTIVE          = 2;
+    static const int SCANNING          = 3;
+    static const int ASSOCIATING       = 4;
+    static const int ASSOCIATED        = 5;
+    static const int FOURWAY_HANDSHAKE = 6;
+    static const int GROUP_HANDSHAKE   = 7;
+    static const int COMPLETED         = 8;
+    static const int IDLE              = 9;
+};
+
+#endif
diff --git a/nexus/TiwlanWifiController.cpp b/nexus/TiwlanWifiController.cpp
new file mode 100644
index 0000000..ec83825
--- /dev/null
+++ b/nexus/TiwlanWifiController.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+
+#include <cutils/properties.h>
+#define LOG_TAG "TiwlanWifiController"
+#include <cutils/log.h>
+
+#include "TiwlanWifiController.h"
+
+#define DRIVER_PROP_NAME "wlan.driver.status"
+
+extern "C" int sched_yield(void);
+
+TiwlanWifiController::TiwlanWifiController(char *modpath, char *modname, char *modargs) :
+                      WifiController(modpath, modname, modargs) {
+}
+
+int TiwlanWifiController::powerUp() {
+    return 0; // Powerup is currently done when the driver is loaded
+}
+
+int TiwlanWifiController::powerDown() {
+    return 0; // Powerdown is currently done when the driver is unloaded
+}
+
+bool TiwlanWifiController::isPoweredUp() {
+    return isKernelModuleLoaded(getModuleName());
+}
+
+int TiwlanWifiController::loadFirmware() {
+    char driver_status[PROPERTY_VALUE_MAX];
+    int count = 100;
+
+    LOGD("loadFirmware()");
+    property_set("ctl.start", "wlan_loader");
+    sched_yield();
+
+    // Wait for driver to be ready
+    while (count-- > 0) {
+        if (property_get(DRIVER_PROP_NAME, driver_status, NULL)) {
+            if (strcmp(driver_status, "ok") == 0)
+                return 0;
+            else if (strcmp(DRIVER_PROP_NAME, "failed") == 0)
+                return -1;
+        }
+        usleep(200000);
+    }
+    property_set(DRIVER_PROP_NAME, "timeout");
+    return -1;
+}
diff --git a/nexus/TiwlanWifiController.h b/nexus/TiwlanWifiController.h
new file mode 100644
index 0000000..a93d610
--- /dev/null
+++ b/nexus/TiwlanWifiController.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _TIWLAN_WIFI_CONTROLLER_H
+#define _TIWLAN_WIFI_CONTROLLER_H
+
+#include "WifiController.h"
+
+class TiwlanWifiController : public WifiController {
+public:
+    TiwlanWifiController(char *modpath, char *modname, char *modargs);
+    virtual ~TiwlanWifiController() {}
+
+    virtual int powerUp();
+    virtual int powerDown();
+    virtual bool isPoweredUp();
+    virtual int loadFirmware();
+};
+#endif
diff --git a/nexus/VpnController.cpp b/nexus/VpnController.cpp
new file mode 100644
index 0000000..2d3db85
--- /dev/null
+++ b/nexus/VpnController.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include "VpnController.h"
+
+VpnController::VpnController() :
+               Controller("VPN") {
+}
+
+int VpnController::start() {
+    errno = -ENOSYS;
+    return -1;
+}
+
+int VpnController::stop() {
+    errno = -ENOSYS;
+    return -1;
+}
+
+int VpnController::enable() {
+
+    // Load modules
+    // Start daemons
+    errno = -ENOSYS;
+    return -1;
+}
+
+int VpnController::disable() {
+    errno = -ENOSYS;
+    return -1;
+}
diff --git a/nexus/VpnController.h b/nexus/VpnController.h
new file mode 100644
index 0000000..f792ce3
--- /dev/null
+++ b/nexus/VpnController.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _VPN_CONTROLLER_H
+#define _VPN_CONTROLLER_H
+
+#include "Controller.h"
+
+class VpnController : public Controller {
+
+public:
+    VpnController();
+    virtual ~VpnController() {}
+
+    virtual int start();
+    virtual int stop();
+
+    virtual int enable();
+    virtual int disable();
+
+protected:
+
+private:
+};
+
+#endif
diff --git a/nexus/WifiController.cpp b/nexus/WifiController.cpp
new file mode 100644
index 0000000..8a7e33f
--- /dev/null
+++ b/nexus/WifiController.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <string.h>
+#include <errno.h>
+
+#define LOG_TAG "WifiController"
+#include <cutils/log.h>
+
+#include "Supplicant.h"
+#include "WifiController.h"
+
+WifiController::WifiController(char *modpath, char *modname, char *modargs) :
+                Controller("WIFI") {
+    strncpy(mModulePath, modpath, sizeof(mModulePath));
+    strncpy(mModuleName, modname, sizeof(mModuleName));
+    strncpy(mModuleArgs, modargs, sizeof(mModuleArgs));
+
+    mSupplicant = new Supplicant();
+    mCurrentScanMode = 0;
+}
+
+int WifiController::start() {
+    return 0;
+}
+
+int WifiController::stop() {
+    errno = ENOSYS;
+    return -1;
+}
+
+int WifiController::enable() {
+    if (!isPoweredUp() && powerUp()) {
+        LOGE("Powerup failed (%s)", strerror(errno));
+        return -1;
+    }
+   
+    if (mModuleName[0] != '\0' && !isKernelModuleLoaded(mModuleName)) {
+        if (loadKernelModule(mModulePath, mModuleArgs)) {
+            LOGE("Kernel module load failed (%s)", strerror(errno));
+            goto out_powerdown;
+        }
+    }
+
+    if (loadFirmware()) {
+        LOGE("Firmware load failed (%s)", strerror(errno));
+        goto out_powerdown;
+    }
+
+    if (!mSupplicant->isStarted() && mSupplicant->start()) {
+        LOGE("Supplicant start failed (%s)", strerror(errno));
+        goto out_unloadmodule;
+    }
+
+    return 0;
+
+out_unloadmodule:
+    if (mModuleName[0] != '\0' && !isKernelModuleLoaded(mModuleName)) {
+        if (unloadKernelModule(mModuleName)) {
+            LOGE("Unable to unload module after failure!");
+        }
+    }
+
+out_powerdown:
+    if (powerDown()) {
+        LOGE("Unable to powerdown after failure!");
+    }
+    return -1;
+}
+
+int WifiController::disable() {
+    LOGD("disable()");
+
+    if (mSupplicant->isStarted() && mSupplicant->stop()) {
+        LOGE("Supplicant stop failed (%s)", strerror(errno));
+        return -1;
+    }
+
+    if (mModuleName[0] != '\0' && isKernelModuleLoaded(mModuleName)) {
+        if (unloadKernelModule(mModuleName)) {
+            LOGE("Unable to unload module (%s)", strerror(errno));
+            return -1;
+        }
+    }
+
+    if (isPoweredUp() && powerDown()) {
+        LOGE("Powerdown failed (%s)", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+int WifiController::loadFirmware() {
+    return 0;
+}
+
+int WifiController::setScanMode(int mode) {
+    int rc = 0;
+
+    if (mCurrentScanMode == mode)
+        return 0;
+
+    if (!(mode & SCAN_ENABLE_MASK)) {
+        if (mCurrentScanMode & SCAN_REPEAT_MASK)
+            stopPeriodicScan();
+    } else if (mode & SCAN_REPEAT_MASK)
+        rc = startPeriodicScan();
+    else
+        rc = mSupplicant->triggerScan(mode & SCAN_ACTIVE_MASK);
+    
+    return rc;
+}
+
+int WifiController::startPeriodicScan() {
+    errno = -ENOSYS;
+    return -1;
+}
+
+int WifiController::stopPeriodicScan() {
+    errno = -ENOSYS;
+    return -1;
+}
diff --git a/nexus/WifiController.h b/nexus/WifiController.h
new file mode 100644
index 0000000..6d00513
--- /dev/null
+++ b/nexus/WifiController.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _WIFI_CONTROLLER_H
+#define _WIFI_CONTROLLER_H
+
+#include <sys/types.h>
+
+#include "Controller.h"
+
+class NetInterface;
+class Supplicant;
+
+class WifiController : public Controller {
+public:
+    static const uint32_t SCAN_ENABLE_MASK       = 0x01;
+    static const uint32_t SCAN_ACTIVE_MASK       = 0x02;
+    static const uint32_t SCAN_REPEAT_MASK       = 0x04;
+
+    static const uint32_t SCANMODE_NONE               = 0;
+    static const uint32_t SCANMODE_PASSIVE_ONESHOT    = SCAN_ENABLE_MASK;
+    static const uint32_t SCANMODE_PASSIVE_CONTINUOUS = SCAN_ENABLE_MASK | SCAN_REPEAT_MASK;
+    static const uint32_t SCANMODE_ACTIVE_ONESHOT     = SCAN_ENABLE_MASK | SCAN_ACTIVE_MASK;
+    static const uint32_t SCANMODE_ACTIVE_CONTINUOUS  = SCAN_ENABLE_MASK | SCAN_ACTIVE_MASK | SCAN_REPEAT_MASK;
+
+private:
+    Supplicant *mSupplicant;
+    char        mModulePath[255];
+    char        mModuleName[64];
+    char        mModuleArgs[255];
+    int         mCurrentScanMode;
+
+
+public:
+    WifiController(char *modpath, char *modname, char *modargs);
+    virtual ~WifiController() {}
+
+    int start();
+    int stop();
+
+    int enable();
+    int disable();
+
+    int getType();
+
+    char *getModulePath() { return mModulePath; }
+    char *getModuleName() { return mModuleName; }
+    char *getModuleArgs() { return mModuleArgs; }
+
+    Supplicant *getSupplicant() { return mSupplicant; }
+
+    int getScanMode() { return mCurrentScanMode; }
+    int setScanMode(int mode);
+
+protected:
+    virtual int powerUp() = 0;
+    virtual int powerDown() = 0;
+    virtual int loadFirmware();
+    virtual bool isPoweredUp() = 0;
+
+private:
+    int startPeriodicScan();
+    int stopPeriodicScan();
+};
+
+#endif
diff --git a/nexus/main.cpp b/nexus/main.cpp
new file mode 100644
index 0000000..a26a14d
--- /dev/null
+++ b/nexus/main.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <errno.h>
+
+#define LOG_TAG "Nexus"
+
+#include "cutils/log.h"
+#include "NetworkManager.h"
+
+int main() {
+    NetworkManager    *nm;
+
+    LOGI("Nexus version 0.1 firing up");
+
+    if (!(nm = new NetworkManager())) {
+        LOGE("Unable to create NetworkManager");
+        exit (-1);
+    };
+
+    if (nm->run()) {
+        LOGE("Unable to Run NetworkManager (%s)", strerror(errno));
+        exit (1);
+    }
+
+    LOGI("Nexus exiting");
+    exit(0);
+}
diff --git a/nexus/nexctl.c b/nexus/nexctl.c
new file mode 100644
index 0000000..6d117c7
--- /dev/null
+++ b/nexus/nexctl.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <cutils/sockets.h>
+
+#include <private/android_filesystem_config.h>
+
+static void signal_handler(int sig) {
+    fprintf(stdout, "{ interrupt! }\n");
+}
+
+int main(int argc, char **argv) {
+    int sock;
+
+    if ((sock = socket_local_client("nexus",
+                                     ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                     SOCK_STREAM)) < 0) {
+        fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
+        exit(1);
+    }
+
+    printf("Connected to nexus\n");
+
+    while(1) {
+        fd_set read_fds;
+        struct timeval to;
+        int rc = 0;
+
+        signal(SIGINT, SIG_DFL);
+
+        printf("-> ");
+        fflush(stdout);
+
+        char buffer[255];
+        if (!fgets(buffer, sizeof(buffer) -1, stdin)) {
+            printf("Exiting...\n");
+            exit(0);
+        }
+
+        buffer[strlen(buffer) -1] = 0;
+
+        printf("sending '%s'\n", buffer);
+        if (write(sock, buffer, strlen(buffer) +1) < 0) {
+            fprintf(stderr, "Error writing data (%s)\n", strerror(errno));
+            exit(2);
+        }
+
+wait:
+        to.tv_sec = 5;
+        to.tv_usec = 0;
+        FD_ZERO(&read_fds);
+        FD_SET(sock, &read_fds);
+    
+        signal(SIGINT, signal_handler);
+     
+        if ((rc = select(sock +1, &read_fds, NULL, NULL, &to)) < 0) {
+            if (errno == EINTR)
+                continue;
+            fprintf(stderr, "Error in select (%s)\n", strerror(errno));
+            exit(2);
+        } else if (!rc) {
+            printf("{response timeout}\n");
+            continue;
+        } else if (FD_ISSET(sock, &read_fds)) {
+printf("got data!\n");
+             if ((rc = read(sock, buffer, sizeof(buffer)-1)) < 0) {
+                 fprintf(stderr, "Error reading response (%s)\n", strerror(errno));
+                 exit(2);
+             }
+            printf(" |%s|\n", buffer);
+            goto wait;
+        }
+    }
+
+
+    exit(0);
+
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index e1f1e8a..c20eeaa 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -217,6 +217,10 @@
 service vold /system/bin/vold
     socket vold stream 0660 root mount
 
+service nexus /system/bin/nexus
+    socket nexus stream 0660 root system
+    disabled
+
 #service mountd /system/bin/mountd
 #    socket mountd stream 0660 root mount