Merge commit 'goog/master' into merge_master
diff --git a/adb/services.c b/adb/services.c
index b99cca1..0a5edcf 100644
--- a/adb/services.c
+++ b/adb/services.c
@@ -33,10 +33,6 @@
 #  endif
 #endif
 
-#ifndef HAVE_WIN32_PROC
-#include <sys/poll.h>
-#endif
-
 typedef struct stinfo stinfo;
 
 struct stinfo {
@@ -203,8 +199,8 @@
 static int create_subprocess(const char *cmd, const char *arg0, const char *arg1)
 {
 #ifdef HAVE_WIN32_PROC
-    fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
-    return -1;
+	fprintf(stderr, "error: create_subprocess not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
+	return -1;
 #else /* !HAVE_WIN32_PROC */
     char *devname;
     int ptm;
@@ -271,54 +267,6 @@
 #define SHELL_COMMAND "/system/bin/sh"
 #endif
 
-static void shell_service(int s, void *command)
-{
-    char    buffer[MAX_PAYLOAD];
-    int     fd, ret = 0;
-    unsigned count = 0;
-
-    fd = create_subprocess(SHELL_COMMAND, "-c", (char *)command);
-
-    while (1) {
-        while (count < sizeof(buffer)) {
-#ifndef HAVE_WIN32_PROC
-            /* add a 200ms timeout so we don't block indefinitely with our
-               buffer partially filled.
-            */
-            if (count > 0) {
-                struct pollfd pollfd;
-
-                pollfd.fd = fd;
-                pollfd.events = POLLIN;
-                ret = poll(&pollfd, 1, 200);
-                if (ret <= 0) {
-                    D("poll returned %d\n", ret);
-                    // file has closed or we timed out
-                    // set ret to 1 so we don't exit the outer loop
-                    ret = 1;
-                    break;
-                }
-            }
-#endif
-            ret = adb_read(fd, buffer + count, sizeof(buffer) - count);
-            D("ret: %d, count: %d\n", ret, count);
-            if (ret > 0)
-                count += ret;
-            else
-                break;
-        }
-
-        D("writing: %d\n", count);
-        adb_write(s, buffer, count);
-        count = 0;
-        if (ret <= 0)
-            break;
-    }
-
-    adb_close(fd);
-    adb_close(s);
-}
-
 int service_to_fd(const char *name)
 {
     int ret = -1;
@@ -372,7 +320,7 @@
 #endif
     } else if(!HOST && !strncmp(name, "shell:", 6)) {
         if(name[6]) {
-            ret = create_service_thread(shell_service, (void *)(name + 6));
+            ret = create_subprocess(SHELL_COMMAND, "-c", name + 6);
         } else {
             ret = create_subprocess(SHELL_COMMAND, "-", 0);
         }
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index a4351ac..1a14842 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -50,6 +50,7 @@
 #define AID_DHCP          1014  /* dhcp client */
 #define AID_SDCARD_RW     1015  /* external storage write access */
 #define AID_VPN           1016  /* vpn system */
+#define AID_KEYSTORE      1017  /* keystore subsystem */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -97,7 +98,8 @@
     { "net_bt",    AID_NET_BT, },
     { "sdcard_rw", AID_SDCARD_RW, },
     { "vpn",       AID_VPN, },
-    { "inet",      AID_INET, }, 
+    { "keystore",  AID_KEYSTORE, },
+    { "inet",      AID_INET, },
     { "net_raw",   AID_NET_RAW, },
     { "misc",      AID_MISC, },
     { "nobody",    AID_NOBODY, },
@@ -155,7 +157,7 @@
     { 00550, AID_ROOT,      AID_SHELL,     "system/etc/init.gprs-pppd" },
     { 00550, AID_DHCP,      AID_SHELL,     "system/etc/dhcpcd/dhcpcd-run-hooks" },
     { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/dbus.conf" },
-    { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/hcid.conf" },
+    { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/main.conf" },
     { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/input.conf" },
     { 00440, AID_BLUETOOTH, AID_BLUETOOTH, "system/etc/bluez/audio.conf" },
     { 00440, AID_RADIO,     AID_AUDIO,     "/system/etc/AudioPara4.csv" },
diff --git a/include/private/pixelflinger/ggl_context.h b/include/private/pixelflinger/ggl_context.h
index 3a030c5..8a36fa9 100644
--- a/include/private/pixelflinger/ggl_context.h
+++ b/include/private/pixelflinger/ggl_context.h
@@ -21,8 +21,8 @@
 #include <stddef.h>
 #include <string.h>
 #include <sys/types.h>
+#include <endian.h>
 
-#include <utils/Endian.h>
 #include <pixelflinger/pixelflinger.h>
 #include <private/pixelflinger/ggl_fixed.h>
 
diff --git a/include/sysutils/FrameworkCommand.h b/include/sysutils/FrameworkCommand.h
index 5b50247..6c1fca6 100644
--- a/include/sysutils/FrameworkCommand.h
+++ b/include/sysutils/FrameworkCommand.h
@@ -29,7 +29,7 @@
     FrameworkCommand(const char *cmd);
     virtual ~FrameworkCommand() { }
 
-    virtual int runCommand(SocketClient *c, char *data) = 0;
+    virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
 
     const char *getCommand() { return mCommand; }
 };
diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h
index 8a83c33..4e3d396 100644
--- a/include/sysutils/FrameworkListener.h
+++ b/include/sysutils/FrameworkListener.h
@@ -22,6 +22,8 @@
 class SocketClient;
 
 class FrameworkListener : public SocketListener {
+public:
+    static const int CMD_ARGS_MAX = 8;
 private:
     FrameworkCommandCollection *mCommands;
 
@@ -34,6 +36,6 @@
     virtual bool onDataAvailable(SocketClient *c);
 
 private:
-    void dispatchCommand(SocketClient *c, char *cmd);
+    void dispatchCommand(SocketClient *c, char *data);
 };
 #endif
diff --git a/libacc/Android.mk b/libacc/Android.mk
index 77c71c6..f77e2b3 100644
--- a/libacc/Android.mk
+++ b/libacc/Android.mk
@@ -1,9 +1,8 @@
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
-#
-# Shared library
-#
+# Shared library for target
+# ========================================================
 
 LOCAL_MODULE:= libacc
 LOCAL_SRC_FILES := acc.cpp
@@ -12,8 +11,29 @@
 LOCAL_SRC_FILES += disassem.cpp
 endif
 
-LOCAL_SHARED_LIBRARIES := libdl
+LOCAL_SHARED_LIBRARIES := libdl libcutils
 
 include $(BUILD_SHARED_LIBRARY)
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+# Shared library for host
+# ========================================================
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libacc
+LOCAL_SRC_FILES := acc.cpp
+
+LOCAL_CFLAGS := -O0 -g
+
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_SRC_FILES += disassem.cpp
+endif
+
+LOCAL_STATIC_LIBRARIES := libcutils
+LOCAL_LDLIBS := -ldl
+
+include $(BUILD_HOST_SHARED_LIBRARY)
+
+# Build children
+# ========================================================
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/libacc/acc.cpp b/libacc/acc.cpp
index de36ce5..7d14e3f 100644
--- a/libacc/acc.cpp
+++ b/libacc/acc.cpp
@@ -10,12 +10,17 @@
 
 #include <ctype.h>
 #include <dlfcn.h>
-#include <setjmp.h>
+#include <errno.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <cutils/hashmap.h>
+
+#if defined(__i386__)
+#include <sys/mman.h>
+#endif
 
 #if defined(__arm__)
 #include <unistd.h>
@@ -43,6 +48,8 @@
 // #define LOG_API(...) fprintf (stderr, __VA_ARGS__)
 // #define ENABLE_ARM_DISASSEMBLY
 
+// #define PROVIDE_TRACE_CODEGEN
+
 namespace acc {
 
 class ErrorSink {
@@ -63,6 +70,7 @@
         char* pProgramBase;
         ErrorSink* mErrorSink;
         int mSize;
+        bool mOverflowed;
 
         void release() {
             if (pProgramBase != 0) {
@@ -71,13 +79,16 @@
             }
         }
 
-        void check(int n) {
+        bool check(int n) {
             int newSize = ind - pProgramBase + n;
-            if (newSize > mSize) {
+            bool overflow = newSize > mSize;
+            if (overflow && !mOverflowed) {
+                mOverflowed = true;
                 if (mErrorSink) {
                     mErrorSink->error("Code too large: %d bytes", newSize);
                 }
             }
+            return overflow;
         }
 
     public:
@@ -86,6 +97,7 @@
             ind = 0;
             mErrorSink = 0;
             mSize = 0;
+            mOverflowed = false;
         }
 
         ~CodeBuf() {
@@ -104,7 +116,9 @@
         }
 
         int o4(int n) {
-            check(4);
+            if(check(4)) {
+                return 0;
+            }
             intptr_t result = (intptr_t) ind;
             * (int*) ind = n;
             ind += 4;
@@ -115,7 +129,9 @@
          * Output a byte. Handles all values, 0..ff.
          */
         void ob(int n) {
-            check(1);
+            if(check(1)) {
+                return;
+            }
             *ind++ = n;
         }
 
@@ -166,7 +182,7 @@
             pCodeBuf->setErrorSink(mErrorSink);
         }
 
-        void setErrorSink(ErrorSink* pErrorSink) {
+        virtual void setErrorSink(ErrorSink* pErrorSink) {
             mErrorSink = pErrorSink;
             if (pCodeBuf) {
                 pCodeBuf->setErrorSink(mErrorSink);
@@ -988,7 +1004,14 @@
         }
 
         virtual int finishCompile() {
-            return 0;
+            size_t pagesize = 4096;
+            size_t base = (size_t) getBase() & ~ (pagesize - 1);
+            size_t top =  ((size_t) getPC() + pagesize - 1) & ~ (pagesize - 1);
+            int err = mprotect((void*) base, top - base, PROT_READ | PROT_WRITE | PROT_EXEC);
+            if (err) {
+               error("mprotect() failed: %d", errno);
+            }
+            return err;
         }
 
     private:
@@ -1024,18 +1047,184 @@
         int decodeOp(int op) {
             if (op < 0 || op > OP_COUNT) {
                 error("Out-of-range operator: %d\n", op);
+                op = 0;
             }
             return operatorHelper[op];
         }
 
         void gmov(int l, int t) {
             o(l + 0x83);
-            oad((t < LOCAL) << 7 | 5, t);
+            oad((t > -LOCAL && t < LOCAL) << 7 | 5, t);
         }
     };
 
 #endif // PROVIDE_X86_CODEGEN
 
+#ifdef PROVIDE_TRACE_CODEGEN
+    class TraceCodeGenerator : public CodeGenerator {
+    private:
+        CodeGenerator* mpBase;
+
+    public:
+        TraceCodeGenerator(CodeGenerator* pBase) {
+            mpBase = pBase;
+        }
+
+        virtual ~TraceCodeGenerator() {
+            delete mpBase;
+        }
+
+        virtual void init(CodeBuf* pCodeBuf) {
+            mpBase->init(pCodeBuf);
+        }
+
+        void setErrorSink(ErrorSink* pErrorSink) {
+            mpBase->setErrorSink(pErrorSink);
+        }
+
+        /* returns address to patch with local variable size
+        */
+        virtual int functionEntry(int argCount) {
+            int result = mpBase->functionEntry(argCount);
+            fprintf(stderr, "functionEntry(%d) -> %d\n", argCount, result);
+            return result;
+        }
+
+        virtual void functionExit(int argCount, int localVariableAddress, int localVariableSize) {
+            fprintf(stderr, "functionExit(%d, %d, %d)\n",
+                    argCount, localVariableAddress, localVariableSize);
+            mpBase->functionExit(argCount, localVariableAddress, localVariableSize);
+        }
+
+        /* load immediate value */
+        virtual void li(int t) {
+            fprintf(stderr, "li(%d)\n", t);
+            mpBase->li(t);
+        }
+
+        virtual int gjmp(int t) {
+            int result = mpBase->gjmp(t);
+            fprintf(stderr, "gjmp(%d) = %d\n", t, result);
+            return result;
+        }
+
+        /* l = 0: je, l == 1: jne */
+        virtual int gtst(bool l, int t) {
+            int result = mpBase->gtst(l, t);
+            fprintf(stderr, "gtst(%d,%d) = %d\n", l, t, result);
+            return result;
+        }
+
+        virtual void gcmp(int op) {
+            fprintf(stderr, "gcmp(%d)\n", op);
+            mpBase->gcmp(op);
+        }
+
+        virtual void genOp(int op) {
+            fprintf(stderr, "genOp(%d)\n", op);
+            mpBase->genOp(op);
+        }
+
+        virtual void clearR1() {
+            fprintf(stderr, "clearR1()\n");
+            mpBase->clearR1();
+        }
+
+        virtual void pushR0() {
+            fprintf(stderr, "pushR0()\n");
+            mpBase->pushR0();
+        }
+
+        virtual void popR1() {
+            fprintf(stderr, "popR1()\n");
+            mpBase->popR1();
+        }
+
+        virtual void storeR0ToR1(bool isInt) {
+            fprintf(stderr, "storeR0ToR1(%d)\n", isInt);
+            mpBase->storeR0ToR1(isInt);
+        }
+
+        virtual void loadR0FromR0(bool isInt) {
+            fprintf(stderr, "loadR0FromR0(%d)\n", isInt);
+            mpBase->loadR0FromR0(isInt);
+        }
+
+        virtual void leaR0(int ea) {
+            fprintf(stderr, "leaR0(%d)\n", ea);
+            mpBase->leaR0(ea);
+        }
+
+        virtual void storeR0(int ea) {
+            fprintf(stderr, "storeR0(%d)\n", ea);
+            mpBase->storeR0(ea);
+        }
+
+        virtual void loadR0(int ea, bool isIncDec, int op) {
+            fprintf(stderr, "loadR0(%d, %d, %d)\n", ea, isIncDec, op);
+            mpBase->loadR0(ea, isIncDec, op);
+        }
+
+        virtual int beginFunctionCallArguments() {
+            int result = mpBase->beginFunctionCallArguments();
+            fprintf(stderr, "beginFunctionCallArguments() = %d\n", result);
+            return result;
+        }
+
+        virtual void storeR0ToArg(int l) {
+            fprintf(stderr, "storeR0ToArg(%d)\n", l);
+            mpBase->storeR0ToArg(l);
+        }
+
+        virtual void endFunctionCallArguments(int a, int l) {
+            fprintf(stderr, "endFunctionCallArguments(%d, %d)\n", a, l);
+            mpBase->endFunctionCallArguments(a, l);
+        }
+
+        virtual int callForward(int symbol) {
+            int result = mpBase->callForward(symbol);
+            fprintf(stderr, "callForward(%d) = %d\n", symbol, result);
+            return result;
+        }
+
+        virtual void callRelative(int t) {
+            fprintf(stderr, "callRelative(%d)\n", t);
+            mpBase->callRelative(t);
+        }
+
+        virtual void callIndirect(int l) {
+            fprintf(stderr, "callIndirect(%d)\n", l);
+            mpBase->callIndirect(l);
+        }
+
+        virtual void adjustStackAfterCall(int l, bool isIndirect) {
+            fprintf(stderr, "adjustStackAfterCall(%d, %d)\n", l, isIndirect);
+            mpBase->adjustStackAfterCall(l, isIndirect);
+        }
+
+        virtual int jumpOffset() {
+            return mpBase->jumpOffset();
+        }
+
+        virtual int disassemble(FILE* out) {
+            return mpBase->disassemble(out);
+        }
+
+        /* output a symbol and patch all calls to it */
+        virtual void gsym(int t) {
+            fprintf(stderr, "gsym(%d)\n", t);
+            mpBase->gsym(t);
+        }
+
+        virtual int finishCompile() {
+            int result = mpBase->finishCompile();
+            fprintf(stderr, "finishCompile() = %d\n", result);
+            return result;
+        }
+    };
+
+#endif // PROVIDE_TRACE_CODEGEN
+
     class InputStream {
     public:
         int getChar() {
@@ -1086,27 +1275,6 @@
         size_t mPosition;
     };
 
-    int ch; // Current input character, or EOF
-    intptr_t tok;     // token
-    intptr_t tokc;    // token extra info
-    int tokl;         // token operator level
-    intptr_t rsym; // return symbol
-    intptr_t loc; // local variable index
-    char* glo;  // global variable index
-    char* sym_stk;
-    char* dstk; // Define stack
-    char* dptr; // Macro state: Points to macro text during macro playback.
-    int dch;    // Macro state: Saves old value of ch during a macro playback.
-    char* last_id;
-    void* pSymbolBase;
-    char* pGlobalBase;
-    char* pVarsBase; // Value of variables
-
-    InputStream* file;
-
-    CodeBuf codeBuf;
-    CodeGenerator* pGen;
-
     class String {
     public:
         String() {
@@ -1115,18 +1283,57 @@
             mSize = 0;
         }
 
+        String(const char* item, int len, bool adopt) {
+            if (len < 0) {
+                len = strlen(item);
+            }
+            if (adopt) {
+                mpBase = (char*) item;
+                mUsed = len;
+                mSize = len + 1;
+            } else {
+                mpBase = 0;
+                mUsed = 0;
+                mSize = 0;
+                appendBytes(item, len);
+            }
+        }
+
+        String(const String& other) {
+            mpBase = 0;
+            mUsed = 0;
+            mSize = 0;
+            appendBytes(other.getUnwrapped(), other.len());
+        }
+
         ~String() {
             if (mpBase) {
                 free(mpBase);
             }
         }
 
-        char* getUnwrapped() {
+        String& operator=(const String& other) {
+            clear();
+            appendBytes(other.getUnwrapped(), other.len());
+            return *this;
+        }
+
+        inline char* getUnwrapped() const {
             return mpBase;
         }
 
+        void clear() {
+            mUsed = 0;
+            if (mSize > 0) {
+                mpBase[0] = 0;
+            }
+        }
+
         void appendCStr(const char* s) {
-            int n = strlen(s);
+            appendBytes(s, strlen(s));
+        }
+
+        void appendBytes(const char* s, int n) {
             memcpy(ensure(n), s, n + 1);
         }
 
@@ -1134,6 +1341,14 @@
             * ensure(1) = c;
         }
 
+        char* orphan() {
+            char* result = mpBase;
+            mpBase = 0;
+            mUsed = 0;
+            mSize = 0;
+            return result;
+        }
+
         void printf(const char* fmt,...) {
             va_list ap;
             va_start(ap, fmt);
@@ -1148,7 +1363,7 @@
             free(temp);
         }
 
-        size_t len() {
+        inline size_t len() const {
             return mUsed;
         }
 
@@ -1174,33 +1389,433 @@
         size_t mSize;
     };
 
-    String mErrorBuf;
+    /**
+     * Wrap an externally allocated string for use as a hash key.
+     */
+    class FakeString : public String {
+    public:
+        FakeString(const char* string, size_t length) :
+            String((char*) string, length, true) {}
 
-    jmp_buf mErrorRecoveryJumpBuf;
+        ~FakeString() {
+            orphan();
+        }
+    };
+
+    template<class V> class StringTable {
+    public:
+        StringTable() {
+            init(10);
+        }
+
+        StringTable(size_t initialCapacity) {
+            init(initialCapacity);
+        }
+
+        ~StringTable() {
+            clear();
+            hashmapFree(mpMap);
+        }
+
+        void clear() {
+            hashmapForEach(mpMap, freeKeyValue, this);
+        }
+
+        bool contains(String* pKey) {
+            bool result = hashmapContainsKey(mpMap, pKey);
+            return result;
+        }
+
+        V* get(String* pKey) {
+            V* result = (V*) hashmapGet(mpMap, pKey);
+            return result;
+        }
+
+        V* remove(String* pKey) {
+            V* result = (V*) hashmapRemove(mpMap, pKey);
+            return result;
+        }
+
+        V* put(String* pKey, V* value) {
+            V* result = (V*) hashmapPut(mpMap, pKey, value);
+            if (result) {
+                // The key was not adopted by the map, so delete it here.
+                delete pKey;
+            }
+            return result;
+        }
+
+        void forEach(bool (*callback)(String* key, V* value, void* context),
+                     void* context) {
+            hashmapForEach(mpMap, (bool (*)(void*, void*, void*)) callback,
+                           context);
+        }
+
+    protected:
+
+        void init(size_t initialCapacity) {
+            mpMap = hashmapCreate(initialCapacity, hashFn, equalsFn);
+        }
+
+        static int hashFn(void* pKey) {
+            String* pString = (String*) pKey;
+            return hashmapHash(pString->getUnwrapped(), pString->len());
+        }
+
+        static bool equalsFn(void* keyA, void* keyB) {
+            String* pStringA = (String*) keyA;
+            String* pStringB = (String*) keyB;
+            return pStringA->len() == pStringB->len()
+                && strcmp(pStringA->getUnwrapped(), pStringB->getUnwrapped())
+                    == 0;
+        }
+
+        static bool freeKeyValue(void* key, void* value, void* context) {
+            delete (String*) key;
+            delete (V*) value;
+            return true;
+        }
+
+        Hashmap* mpMap;
+    };
+
+    class MacroTable : public StringTable<String> {
+    public:
+        MacroTable() : StringTable<String>(10) {}
+    };
+
+    class KeywordTable {
+    public:
+
+        KeywordTable(){
+            mpMap = hashmapCreate(40, hashFn, equalsFn);
+            put("int", TOK_INT);
+            put("char", TOK_CHAR);
+            put("void", TOK_VOID);
+            put("if", TOK_IF);
+            put("else", TOK_ELSE);
+            put("while", TOK_WHILE);
+            put("break", TOK_BREAK);
+            put("return", TOK_RETURN);
+            put("for", TOK_FOR);
+            // TODO: remove these preprocessor-specific keywords. You should
+            // be able to have symbols named pragma or define.
+            put("pragma", TOK_PRAGMA);
+            put("define", TOK_DEFINE);
+
+            const char* unsupported[] = {
+                "auto",
+                "case",
+                "const",
+                "continue",
+                "default",
+                "do",
+                "double",
+                "enum",
+                "extern",
+                "float",
+                "goto",
+                "long",
+                "register",
+                "short",
+                "signed",
+                "sizeof",
+                "static",
+                "struct",
+                "switch",
+                "typedef",
+                "union",
+                "unsigned",
+                "volatile",
+                "_Bool",
+                "_Complex",
+                "_Imaginary",
+                "inline",
+                "restrict",
+                0};
+
+            for(int i = 0; unsupported[i]; i++) {
+                put(unsupported[i], TOK_UNSUPPORTED_KEYWORD);
+            }
+        }
+
+        ~KeywordTable() {
+            hashmapFree(mpMap);
+        }
+
+        int get(String* key) {
+            return (int) hashmapGet(mpMap, key->getUnwrapped());
+        }
+
+        const char* lookupKeyFor(int value) {
+            FindValContext context;
+            context.key = 0;
+            hashmapForEach(mpMap, findKeyFn, &context);
+            return context.key;
+        }
+
+    private:
+        void put(const char* kw, int val) {
+            hashmapPut(mpMap, (void*) kw, (void*) val);
+        }
+
+        static int hashFn(void* pKey) {
+            char* pString = (char*) pKey;
+            return hashmapHash(pString, strlen(pString));
+        }
+
+        static bool equalsFn(void* keyA, void* keyB) {
+            const char* pStringA = (const char*) keyA;
+            const char* pStringB = (const char*) keyB;
+            return strcmp(pStringA, pStringB)  == 0;
+        }
+
+        struct FindValContext {
+            char* key;
+            int value;
+        };
+
+        static bool findKeyFn(void* key, void* value, void* context) {
+            FindValContext* pContext = (FindValContext*) context;
+            if ((int) value == pContext->value) {
+                pContext->key = (char*) key;
+                return false;
+            }
+            return true;
+        }
+
+        Hashmap* mpMap;
+    };
+
+    template<class E> class Array {
+        public:
+        Array() {
+            mpBase = 0;
+            mUsed = 0;
+            mSize = 0;
+        }
+
+        ~Array() {
+            if (mpBase) {
+                free(mpBase);
+            }
+        }
+
+        E get(int i) {
+            if (i < 0 || i > (int) mUsed) {
+                // error("internal error: Index out of range");
+                return E();
+            }
+            return mpBase[i];
+        }
+
+        void set(int i, E val) {
+            mpBase[i] =  val;
+        }
+
+        void pop() {
+            if (mUsed > 0) {
+                mUsed -= 1;
+            } else {
+                // error("internal error: Popped empty stack.");
+            }
+        }
+
+        void push(E item) {
+            * ensure(1) = item;
+        }
+
+        size_t len() {
+            return mUsed;
+        }
+
+    private:
+        E* ensure(int n) {
+            size_t newUsed = mUsed + n;
+            if (newUsed > mSize) {
+                size_t newSize = mSize * 2 + 10;
+                if (newSize < newUsed) {
+                    newSize = newUsed;
+                }
+                mpBase = (E*) realloc(mpBase, sizeof(E) * newSize);
+                mSize = newSize;
+            }
+            E* result = mpBase + mUsed;
+            mUsed = newUsed;
+            return result;
+        }
+
+        E* mpBase;
+        size_t mUsed;
+        size_t mSize;
+    };
+
+    struct InputState {
+        InputStream* pStream;
+        int oldCh;
+    };
+
+    struct VariableInfo {
+        VariableInfo() {
+            pAddress = 0;
+            pForward = 0;
+        }
+        void* pAddress;
+        void* pForward; // For a forward direction, linked list of data to fix up
+    };
+
+    typedef StringTable<VariableInfo> SymbolTable;
+
+    class SymbolStack {
+    public:
+        SymbolStack() {
+            mLevel = 0;
+            addEntry();
+        }
+
+        void pushLevel() {
+            mLevel++;
+        }
+
+        void popLevel() {
+            mLevel--;
+            Entry e = mStack.get(mStack.len()-1);
+            if (mLevel < e.level) {
+                mStack.pop();
+                delete e.pTable;
+            }
+        }
+
+        VariableInfo* get(String* pName) {
+            int len = mStack.len();
+            VariableInfo* v = NULL;
+            int level = -1;
+            for (int i = len - 1; i >= 0; i--) {
+                Entry e = mStack.get(i);
+                v = e.pTable->get(pName);
+                if (v) {
+                    level = e.level;
+                    break;
+                }
+            }
+#if 0
+            fprintf(stderr, "Lookup %s %08x level %d\n", pName->getUnwrapped(), v, level);
+            if (v) {
+                fprintf(stderr, "  %08x %08x\n", v->pAddress, v->pForward);
+            }
+#endif
+            return v;
+        }
+
+        VariableInfo* addLocal(String* pName) {
+            int len = mStack.len();
+            if (mStack.get(len-1).level != mLevel) {
+                addEntry();
+                len++;
+            }
+            return addImp(len-1, pName);
+        }
+
+        VariableInfo* addGlobal(String* pName) {
+            return addImp(0, pName);
+        }
+
+        void forEachGlobal(
+            bool (*callback)(String* key, VariableInfo* value, void* context),
+            void* context) {
+            mStack.get(0).pTable->forEach(callback, context);
+        }
+
+    private:
+        VariableInfo* addImp(int entryIndex, String* pName) {
+            Entry e = mStack.get(entryIndex);
+            SymbolTable* pTable = e.pTable;
+            if (pTable->contains(pName)) {
+                return NULL;
+            }
+            VariableInfo* v = new VariableInfo();
+
+            delete pTable->put(pName, v);
+#if 0
+            fprintf(stderr, "Add \"%s\" %08x level %d\n", pName->getUnwrapped(), v, e.level);
+#endif
+            return v;
+        }
+
+        void addEntry() {
+            Entry e;
+            e.level = mLevel;
+            e.pTable = new SymbolTable();
+            mStack.push(e);
+        }
+
+        struct Entry {
+            Entry() {
+                level = 0;
+                pTable = NULL;
+            }
+            int level;
+            SymbolTable* pTable;
+        };
+
+        int mLevel;
+        Array<Entry> mStack;
+    };
+
+    int ch; // Current input character, or EOF
+    intptr_t tok;     // token
+    intptr_t tokc;    // token extra info
+    int tokl;         // token operator level
+    intptr_t rsym; // return symbol
+    intptr_t loc; // local variable index
+    char* glo;  // global variable index
+    String mTokenString;
+    char* dptr; // Macro state: Points to macro text during macro playback.
+    int dch;    // Macro state: Saves old value of ch during a macro playback.
+    char* pGlobalBase;
+    KeywordTable mKeywords;
+    SymbolStack mSymbolTable;
+    InputStream* file;
+
+    CodeBuf codeBuf;
+    CodeGenerator* pGen;
+
+    MacroTable mMacros;
+    Array<InputState> mInputStateStack;
+
+    String mErrorBuf;
 
     String mPragmas;
     int mPragmaStringCount;
 
     static const int ALLOC_SIZE = 99999;
 
-    // Indentifiers start at 0x100 and increase by # (chars + 1) * 8
-    static const int TOK_IDENT = 0x100;
-    static const int TOK_INT = 0x100;
-    static const int TOK_CHAR = TOK_INT + 4*8;
-    static const int TOK_VOID = TOK_CHAR + 5*8;
-    static const int TOK_IF = TOK_VOID + 5*8;
-    static const int TOK_ELSE = TOK_IF + 3*8;
-    static const int TOK_WHILE = TOK_ELSE + 5*8;
-    static const int TOK_BREAK = TOK_WHILE + 6*8;
-    static const int TOK_RETURN = TOK_BREAK + 6*8;
-    static const int TOK_FOR = TOK_RETURN + 7*8;
-    static const int TOK_PRAGMA = TOK_FOR + 4*8;
-    static const int TOK_DEFINE = TOK_PRAGMA + 7*8;
-    static const int TOK_MAIN = TOK_DEFINE + 7*8;
-
     static const int TOK_DUMMY = 1;
     static const int TOK_NUM = 2;
 
+    // 3..255 are character and/or operators
+
+    // Keywords start at 0x100 and increase by 1
+    static const int TOK_KEYWORD = 0x100;
+    static const int TOK_INT = TOK_KEYWORD + 0;
+    static const int TOK_CHAR = TOK_KEYWORD + 1;
+    static const int TOK_VOID = TOK_KEYWORD + 2;
+    static const int TOK_IF = TOK_KEYWORD + 3;
+    static const int TOK_ELSE = TOK_KEYWORD + 4;
+    static const int TOK_WHILE = TOK_KEYWORD + 5;
+    static const int TOK_BREAK = TOK_KEYWORD + 6;
+    static const int TOK_RETURN = TOK_KEYWORD + 7;
+    static const int TOK_FOR = TOK_KEYWORD + 8;
+    static const int TOK_PRAGMA = TOK_KEYWORD + 9;
+    static const int TOK_DEFINE = TOK_KEYWORD + 10;
+    static const int TOK_UNSUPPORTED_KEYWORD = TOK_KEYWORD + 0xff;
+
+    static const int TOK_UNDEFINED_SYMBOL = 0x200;
+
+    // Symbols start at 0x300, but are really pointers to VariableInfo structs.
+    static const int TOK_SYMBOL = 0x300;
+
+
     static const int LOCAL = 0x200;
 
     static const int SYM_FORWARD = 0;
@@ -1208,7 +1823,6 @@
 
     /* tokens in string heap */
     static const int TAG_TOK = ' ';
-    static const int TAG_MACRO = 2;
 
     static const int OP_INCREMENT = 0;
     static const int OP_DECREMENT = 1;
@@ -1242,16 +1856,13 @@
     static const char operatorLevel[];
 
     void pdef(int t) {
-        if (dstk - sym_stk >= ALLOC_SIZE) {
-            error("Symbol table exhausted");
-        }
-        *dstk++ = t;
+        mTokenString.append(t);
     }
 
     void inp() {
         if (dptr) {
             ch = *dptr++;
-            if (ch == TAG_MACRO) {
+            if (ch == 0) {
                 dptr = 0;
                 ch = dch;
             }
@@ -1266,13 +1877,92 @@
         return isalnum(ch) | (ch == '_');
     }
 
-    /* read a character constant */
-    void getq() {
+    /* read a character constant, advances ch to after end of constant */
+    int getq() {
+        int val = ch;
         if (ch == '\\') {
             inp();
-            if (ch == 'n')
-                ch = '\n';
+            if (isoctal(ch)) {
+                // 1 to 3 octal characters.
+                val = 0;
+                for(int i = 0; i < 3; i++) {
+                    if (isoctal(ch)) {
+                        val = (val << 3) + ch - '0';
+                        inp();
+                    }
+                }
+                return val;
+            } else if (ch == 'x' || ch == 'X') {
+                // N hex chars
+                inp();
+                if (! isxdigit(ch)) {
+                    error("'x' character escape requires at least one digit.");
+                } else {
+                    val = 0;
+                    while (isxdigit(ch)) {
+                        int d = ch;
+                        if (isdigit(d)) {
+                            d -= '0';
+                        } else if (d <= 'F') {
+                            d = d - 'A' + 10;
+                        } else {
+                            d = d - 'a' + 10;
+                        }
+                        val = (val << 4) + d;
+                        inp();
+                    }
+                }
+            } else {
+                int val = ch;
+                switch (ch) {
+                    case 'a':
+                        val = '\a';
+                        break;
+                    case 'b':
+                        val = '\b';
+                        break;
+                    case 'f':
+                        val = '\f';
+                        break;
+                    case 'n':
+                        val = '\n';
+                        break;
+                    case 'r':
+                        val = '\r';
+                        break;
+                    case 't':
+                        val = '\t';
+                        break;
+                    case 'v':
+                        val = '\v';
+                        break;
+                    case '\\':
+                        val = '\\';
+                        break;
+                    case '\'':
+                        val = '\'';
+                        break;
+                    case '"':
+                        val = '"';
+                        break;
+                    case '?':
+                        val = '?';
+                        break;
+                    default:
+                        error("Undefined character escape %c", ch);
+                        break;
+                }
+                inp();
+                return val;
+            }
+        } else {
+            inp();
         }
+        return val;
+    }
+
+    static bool isoctal(int ch) {
+        return ch >= '0' && ch <= '7';
     }
 
     void next() {
@@ -1283,22 +1973,13 @@
                 inp();
                 next();
                 if (tok == TOK_DEFINE) {
-                    next();
-                    pdef(TAG_TOK); /* fill last ident tag */
-                    *(int *) tok = SYM_DEFINE;
-                    *(char* *) (tok + 4) = dstk; /* define stack */
-                    while (ch != '\n') {
-                        pdef(ch);
-                        inp();
-                    }
-                    pdef(ch);
-                    pdef(TAG_MACRO);
+                    doDefine();
                 } else if (tok == TOK_PRAGMA) {
                     doPragma();
                 } else {
-                    error("Unsupported preprocessor directive \"%s\"", last_id);
+                    error("Unsupported preprocessor directive \"%s\"",
+                          mTokenString.getUnwrapped());
                 }
-
             }
             inp();
         }
@@ -1306,37 +1987,35 @@
         tok = ch;
         /* encode identifiers & numbers */
         if (isid()) {
-            pdef(TAG_TOK);
-            last_id = dstk;
+            mTokenString.clear();
             while (isid()) {
                 pdef(ch);
                 inp();
             }
             if (isdigit(tok)) {
-                tokc = strtol(last_id, 0, 0);
+                tokc = strtol(mTokenString.getUnwrapped(), 0, 0);
                 tok = TOK_NUM;
             } else {
-                if (dstk - sym_stk + 1 > ALLOC_SIZE) {
-                    error("symbol stack overflow");
-                }
-                * dstk = TAG_TOK; /* no need to mark end of string (we
-                 suppose data is initialized to zero by calloc) */
-                tok = (intptr_t) (strstr(sym_stk, (last_id - 1))
-                        - sym_stk);
-                * dstk = 0; /* mark real end of ident for dlsym() */
-                tok = tok * 8 + TOK_IDENT;
-                if (tok > TOK_DEFINE) {
-                    if (tok + 8 > ALLOC_SIZE) {
-                        error("Variable Table overflow.");
-                    }
-                    tok = (intptr_t) (pVarsBase + tok);
-                    /*        printf("tok=%s %x\n", last_id, tok); */
-                    /* define handling */
-                    if (*(int *) tok == SYM_DEFINE) {
-                        dptr = *(char* *) (tok + 4);
-                        dch = ch;
-                        inp();
-                        next();
+                // Is this a macro?
+                String* pValue = mMacros.get(&mTokenString);
+                if (pValue) {
+                    // Yes, it is a macro
+                    dptr = pValue->getUnwrapped();
+                    dch = ch;
+                    inp();
+                    next();
+                } else {
+                    // Is this a keyword?
+                    int kwtok = mKeywords.get(&mTokenString);
+                    if (kwtok) {
+                        tok = kwtok;
+                        // fprintf(stderr, "tok= keyword %s %x\n", last_id, tok);
+                    } else {
+                        tok = (intptr_t) mSymbolTable.get(&mTokenString);
+                        if (!tok) {
+                            tok = TOK_UNDEFINED_SYMBOL;
+                        }
+                        // fprintf(stderr, "tok= symbol %s %x\n", last_id, tok);
                     }
                 }
             }
@@ -1344,24 +2023,29 @@
             inp();
             if (tok == '\'') {
                 tok = TOK_NUM;
-                getq();
-                tokc = ch;
-                inp();
-                inp();
+                tokc = getq();
+                if (ch != '\'') {
+                    error("Expected a ' character, got %c", ch);
+                } else {
+                  inp();
+                }
             } else if ((tok == '/') & (ch == '*')) {
                 inp();
-                while (ch) {
-                    while (ch != '*')
+                while (ch && ch != EOF) {
+                    while (ch != '*' && ch != EOF)
                         inp();
                     inp();
                     if (ch == '/')
                         ch = 0;
                 }
+                if (ch == EOF) {
+                    error("End of file inside comment.");
+                }
                 inp();
                 next();
             } else if ((tok == '/') & (ch == '/')) {
                 inp();
-                while (ch && (ch != '\n')) {
+                while (ch && (ch != '\n') && (ch != EOF)) {
                     inp();
                 }
                 inp();
@@ -1394,15 +2078,19 @@
         }
 #if 0
         {
-            char* p;
+            const char* p;
 
             printf("tok=0x%x ", tok);
-            if (tok >= TOK_IDENT) {
+            if (tok >= TOK_KEYWORD) {
                 printf("'");
-                if (tok> TOK_DEFINE)
-                p = sym_stk + 1 + ((char*) tok - pVarsBase - TOK_IDENT) / 8;
-                else
-                p = sym_stk + 1 + (tok - TOK_IDENT) / 8;
+                if (tok>= TOK_SYMBOL)
+                    p = sym_stk + 1 + ((char*) tok - (char*) pVarsBase) / 8;
+                else {
+                    p = mKeywords.lookupKeyFor(tok);
+                    if (!p) {
+                        p = "unknown keyword";
+                    }
+                }
                 while (*p != TAG_TOK && *p)
                 printf("%c", *p++);
                 printf("'\n");
@@ -1415,6 +2103,31 @@
 #endif
     }
 
+    void doDefine() {
+        String* pName = new String();
+        while (isspace(ch)) {
+            inp();
+        }
+        while (isid()) {
+            pName->append(ch);
+            inp();
+        }
+        if (ch == '(') {
+            delete pName;
+            error("Defines with arguments not supported");
+            return;
+        }
+        while (isspace(ch)) {
+            inp();
+        }
+        String* pValue = new String();
+        while (ch != '\n' && ch != EOF) {
+            pValue->append(ch);
+            inp();
+        }
+        delete mMacros.put(pName, pValue);
+    }
+
     void doPragma() {
         // # pragma name(val)
         int state = 0;
@@ -1463,7 +2176,6 @@
         mErrorBuf.printf("%ld: ", file->getLine());
         mErrorBuf.vprintf(fmt, ap);
         mErrorBuf.printf("\n");
-        longjmp(mErrorRecoveryJumpBuf, 1);
     }
 
     void skip(intptr_t c) {
@@ -1477,15 +2189,16 @@
     void unary(intptr_t l) {
         intptr_t n, t, a;
         int c;
+        String tString;
         t = 0;
-        n = 1; /* type of expression 0 = forward, 1 = value, other =
-         lvalue */
+        n = 1; /* type of expression 0 = forward, 1 = value, other = lvalue */
         if (tok == '\"') {
             pGen->li((int) glo);
-            while (ch != '\"') {
-                getq();
-                *allocGlobalSpace(1) = ch;
-                inp();
+            while (ch != '\"' && ch != EOF) {
+                *allocGlobalSpace(1) = getq();
+            }
+            if (ch != '\"') {
+                error("Unterminated string constant.");
             }
             *glo = 0;
             /* align heap */
@@ -1496,6 +2209,7 @@
             c = tokl;
             a = tokc;
             t = tok;
+            tString = mTokenString;
             next();
             if (t == TOK_NUM) {
                 pGen->li(a);
@@ -1538,11 +2252,23 @@
             } else if (t == '&') {
                 pGen->leaR0(*(int *) tok);
                 next();
+            } else if (t == EOF ) {
+                error("Unexpected EOF.");
+            } else if (!checkSymbol(t, &tString)) {
+                // Don't have to do anything special here, the error
+                // message was printed by checkSymbol() above.
             } else {
-                n = *(int *) t;
+                if (t == TOK_UNDEFINED_SYMBOL) {
+                    t = (intptr_t) mSymbolTable.addGlobal(
+                        new String(tString));
+                }
+
+                n = (intptr_t) ((VariableInfo*) t)->pAddress;
                 /* forward reference: try dlsym */
                 if (!n) {
-                    n = (intptr_t) dlsym(RTLD_DEFAULT, last_id);
+                    n = (intptr_t) dlsym(RTLD_DEFAULT,
+                                         tString.getUnwrapped());
+                    ((VariableInfo*) t)->pAddress = (void*) n;
                 }
                 if ((tok == '=') & l) {
                     /* assignment */
@@ -1551,6 +2277,9 @@
                     pGen->storeR0(n);
                 } else if (tok != '(') {
                     /* variable */
+                    if (!n) {
+                        error("Undefined variable %s", tString.getUnwrapped());
+                    }
                     pGen->loadR0(n, tokl == 11, tokc);
                     if (tokl == 11) {
                         next();
@@ -1568,7 +2297,7 @@
             a = pGen->beginFunctionCallArguments();
             next();
             l = 0;
-            while (tok != ')') {
+            while (tok != ')' && tok != EOF) {
                 expr();
                 pGen->storeR0ToArg(l);
                 if (tok == ',')
@@ -1576,7 +2305,7 @@
                 l = l + 4;
             }
             pGen->endFunctionCallArguments(a, l);
-            next();
+            skip(')');
             if (!n) {
                 /* forward reference */
                 t = t + 4;
@@ -1639,20 +2368,23 @@
         return pGen->gtst(0, 0);
     }
 
-    void block(intptr_t l) {
+    void block(intptr_t l, bool outermostFunctionBlock) {
         intptr_t a, n, t;
 
-        if (tok == TOK_IF) {
+        if (tok == TOK_INT || tok == TOK_CHAR) {
+            /* declarations */
+            localDeclarations();
+        } else if (tok == TOK_IF) {
             next();
             skip('(');
             a = test_expr();
             skip(')');
-            block(l);
+            block(l, false);
             if (tok == TOK_ELSE) {
                 next();
                 n = pGen->gjmp(0); /* jmp */
                 pGen->gsym(a);
-                block(l);
+                block(l, false);
                 pGen->gsym(n); /* patch else jmp */
             } else {
                 pGen->gsym(a); /* patch if test */
@@ -1682,16 +2414,20 @@
                 }
             }
             skip(')');
-            block((intptr_t) &a);
+            block((intptr_t) &a, false);
             pGen->gjmp(n - codeBuf.getPC() - pGen->jumpOffset()); /* jmp */
             pGen->gsym(a);
         } else if (tok == '{') {
+            if (! outermostFunctionBlock) {
+                mSymbolTable.pushLevel();
+            }
             next();
-            /* declarations */
-            localDeclarations();
-            while (tok != '}')
-                block(l);
-            next();
+            while (tok != '}' && tok != EOF)
+                block(l, false);
+            skip('}');
+            if (! outermostFunctionBlock) {
+                mSymbolTable.popLevel();
+            }
         } else {
             if (tok == TOK_RETURN) {
                 next();
@@ -1765,24 +2501,47 @@
         }
     }
 
-    void checkSymbol() {
-        if (tok <= TOK_DEFINE) {
-            error("Expected a symbol");
+    void addGlobalSymbol() {
+        tok = (intptr_t) mSymbolTable.addGlobal(
+            new String(mTokenString));
+        reportIfDuplicate();
+    }
+
+    void reportIfDuplicate() {
+        if (!tok) {
+            error("Duplicate definition of %s", mTokenString.getUnwrapped());
         }
     }
 
+    void addLocalSymbol() {
+        tok = (intptr_t) mSymbolTable.addLocal(
+                new String(mTokenString));
+        reportIfDuplicate();
+    }
+
     void localDeclarations() {
         intptr_t a;
         Type base;
 
         while (acceptType(base)) {
-            while (tok != ';') {
+            while (tok != ';' && tok != EOF) {
                 Type t = acceptPointerDeclaration(t);
-                checkSymbol();
-                loc = loc + 4;
-                *(int *) tok = -loc;
-
+                int variableAddress = 0;
+                if (checkSymbol()) {
+                    addLocalSymbol();
+                    if (tok) {
+                        loc = loc + 4;
+                        variableAddress = -loc;
+                        ((VariableInfo*) tok)->pAddress = (void*) variableAddress;
+                    }
+                }
                 next();
+                if (tok == '=') {
+                    /* assignment */
+                    next();
+                    expr();
+                    pGen->storeR0(variableAddress);
+                }
                 if (tok == ',')
                     next();
             }
@@ -1790,56 +2549,112 @@
         }
     }
 
+    bool checkSymbol() {
+        return checkSymbol(tok, &mTokenString);
+    }
+
+    bool checkSymbol(int token, String* pText) {
+        bool result = token < EOF || token >= TOK_UNDEFINED_SYMBOL;
+        if (!result) {
+            String temp;
+            if (token == EOF ) {
+                temp.printf("EOF");
+            } else if (token == TOK_NUM) {
+                temp.printf("numeric constant");
+            } else if (token >= 0 && token < 256) {
+                temp.printf("char \'%c\'", token);
+            } else if (token >= TOK_KEYWORD && token < TOK_UNSUPPORTED_KEYWORD) {
+                temp.printf("keyword \"%s\"", pText->getUnwrapped());
+            } else {
+                temp.printf("reserved keyword \"%s\"",
+                            pText->getUnwrapped());
+            }
+            error("Expected symbol. Got %s", temp.getUnwrapped());
+        }
+        return result;
+    }
+
     void globalDeclarations() {
         while (tok != EOF) {
             Type base;
             expectType(base);
             Type t = acceptPointerDeclaration(t);
-            checkSymbol();
-            int name = tok;
+            if (tok >=  0 && tok < TOK_UNDEFINED_SYMBOL) {
+                error("Unexpected token %d", tok);
+                break;
+            }
+            if (tok == TOK_UNDEFINED_SYMBOL) {
+                addGlobalSymbol();
+            }
+            VariableInfo* name = (VariableInfo*) tok;
+            if (name && name->pAddress) {
+                error("Already defined global %s",
+                      mTokenString.getUnwrapped());
+            }
             next();
-            if (tok == ',' || tok == ';') {
+            if (tok == ',' || tok == ';' || tok == '=') {
                 // it's a variable declaration
                 for(;;) {
-                    *(int* *) name = (int*) allocGlobalSpace(4);
+                    if (name) {
+                        name->pAddress = (int*) allocGlobalSpace(4);
+                    }
+                    if (tok == '=') {
+                        next();
+                        if (tok == TOK_NUM) {
+                            if (name) {
+                                * (int*) name->pAddress = tokc;
+                            }
+                            next();
+                        } else {
+                            error("Expected an integer constant");
+                        }
+                    }
                     if (tok != ',') {
                         break;
                     }
-                    next();
+                    skip(',');
                     t = acceptPointerDeclaration(t);
-                    checkSymbol();
-                    name = tok;
+                    addGlobalSymbol();
+                    name = (VariableInfo*) tok;
                     next();
                 }
                 skip(';');
             } else {
-                /* patch forward references (XXX: does not work for function
-                 pointers) */
-                pGen->gsym(*(int *) (name + 4));
-                /* put function address */
-                *(int *) name = codeBuf.getPC();
+                if (name) {
+                    /* patch forward references (XXX: does not work for function
+                     pointers) */
+                    pGen->gsym((int) name->pForward);
+                    /* put function address */
+                    name->pAddress = (void*) codeBuf.getPC();
+                }
                 skip('(');
+                mSymbolTable.pushLevel();
                 intptr_t a = 8;
                 int argCount = 0;
-                while (tok != ')') {
+                while (tok != ')' && tok != EOF) {
                     Type aType;
                     expectType(aType);
                     aType = acceptPointerDeclaration(aType);
-                    checkSymbol();
-                    /* read param name and compute offset */
-                    *(int *) tok = a;
-                    a = a + 4;
+                    if (checkSymbol()) {
+                        addLocalSymbol();
+                        if (tok) {
+                            /* read param name and compute offset */
+                            *(int *) tok = a;
+                            a = a + 4;
+                        }
+                    }
                     next();
                     if (tok == ',')
                         next();
                     argCount++;
                 }
-                skip(')'); /* skip ')' */
+                skip(')');
                 rsym = loc = 0;
                 a = pGen->functionEntry(argCount);
-                block(0);
+                block(0, true);
                 pGen->gsym(rsym);
                 pGen->functionExit(argCount, a, loc);
+                mSymbolTable.popLevel();
             }
         }
     }
@@ -1847,6 +2662,7 @@
     char* allocGlobalSpace(int bytes) {
         if (glo - pGlobalBase + bytes > ALLOC_SIZE) {
             error("Global space exhausted");
+            return NULL;
         }
         char* result = glo;
         glo += bytes;
@@ -1854,18 +2670,10 @@
     }
 
     void cleanup() {
-        if (sym_stk != 0) {
-            free(sym_stk);
-            sym_stk = 0;
-        }
         if (pGlobalBase != 0) {
             free(pGlobalBase);
             pGlobalBase = 0;
         }
-        if (pVarsBase != 0) {
-            free(pVarsBase);
-            pVarsBase = 0;
-        }
         if (pGen) {
             delete pGen;
             pGen = 0;
@@ -1881,18 +2689,13 @@
         tokc = 0;
         tokl = 0;
         ch = 0;
-        pVarsBase = 0;
         rsym = 0;
         loc = 0;
         glo = 0;
-        sym_stk = 0;
-        dstk = 0;
         dptr = 0;
         dch = 0;
-        last_id = 0;
         file = 0;
         pGlobalBase = 0;
-        pVarsBase = 0;
         pGen = 0;
         mPragmaStringCount = 0;
     }
@@ -1926,8 +2729,9 @@
         }
         if (pGen == NULL) {
             error("No code generator defined.");
+        } else {
+            pGen->setErrorSink(this);
         }
-        pGen->setErrorSink(this);
     }
 
 public:
@@ -1948,42 +2752,54 @@
 
     int compile(const char* text, size_t textLength) {
         int result;
-        if (! (result = setjmp(mErrorRecoveryJumpBuf))) {
-            cleanup();
-            clear();
-            codeBuf.init(ALLOC_SIZE);
-            setArchitecture(NULL);
-            if (!pGen) {
-                return -1;
+
+        cleanup();
+        clear();
+        codeBuf.init(ALLOC_SIZE);
+        setArchitecture(NULL);
+        if (!pGen) {
+            return -1;
+        }
+#ifdef PROVIDE_TRACE_CODEGEN
+            pGen = new TraceCodeGenerator(pGen);
+#endif
+            pGen->setErrorSink(this);
+        pGen->init(&codeBuf);
+        file = new TextInputStream(text, textLength);
+        pGlobalBase = (char*) calloc(1, ALLOC_SIZE);
+        glo = pGlobalBase;
+        inp();
+        next();
+        globalDeclarations();
+        checkForUndefinedForwardReferences();
+        result = pGen->finishCompile();
+        if (result == 0) {
+            if (mErrorBuf.len()) {
+                result = -2;
             }
-            pGen->init(&codeBuf);
-            file = new TextInputStream(text, textLength);
-            sym_stk = (char*) calloc(1, ALLOC_SIZE);
-            static const char* predefinedSymbols =
-                " int char void"
-                " if else while break return for"
-                " pragma define main ";
-            dstk = strcpy(sym_stk, predefinedSymbols)
-                    + strlen(predefinedSymbols);
-            pGlobalBase = (char*) calloc(1, ALLOC_SIZE);
-            glo = pGlobalBase;
-            pVarsBase = (char*) calloc(1, ALLOC_SIZE);
-            inp();
-            next();
-            globalDeclarations();
-            pGen->finishCompile();
         }
         return result;
     }
 
-    int run(int argc, char** argv) {
-        typedef int (*mainPtr)(int argc, char** argv);
-        mainPtr aMain = (mainPtr) *(int*) (pVarsBase + TOK_MAIN);
-        if (!aMain) {
-            fprintf(stderr, "Could not find function \"main\".\n");
-            return -1;
+    void checkForUndefinedForwardReferences() {
+        mSymbolTable.forEachGlobal(static_ufrcFn, this);
+    }
+
+    static bool static_ufrcFn(String* key, VariableInfo* value,
+                                                 void* context) {
+        Compiler* pCompiler = (Compiler*) context;
+        return pCompiler->undefinedForwardReferenceCheck(key, value);
+    }
+
+    bool undefinedForwardReferenceCheck(String* key, VariableInfo* value) {
+#if 0
+        fprintf(stderr, "%s 0x%8x 0x%08x\n", key->getUnwrapped(),
+                value->pAddress, value->pForward);
+#endif
+        if (!value->pAddress && value->pForward) {
+            error("Undefined forward reference: %s", key->getUnwrapped());
         }
-        return aMain(argc, argv);
+        return true;
     }
 
     int dump(FILE* out) {
@@ -1999,30 +2815,10 @@
      * If found, return its value.
      */
     void* lookup(const char* name) {
-        if (!sym_stk) {
-            return NULL;
-        }
-        size_t nameLen = strlen(name);
-        char* pSym = sym_stk;
-        char c;
-        for(;;) {
-            c = *pSym++;
-            if (c == 0) {
-                break;
-            }
-            if (c == TAG_TOK) {
-                if (memcmp(pSym, name, nameLen) == 0
-                        && pSym[nameLen] == TAG_TOK) {
-                    int tok = pSym - 1 - sym_stk;
-                    tok = tok * 8 + TOK_IDENT;
-                    if (tok <= TOK_DEFINE) {
-                        return 0;
-                    } else {
-                        tok = (intptr_t) (pVarsBase + tok);
-                        return * (void**) tok;
-                    }
-                }
-            }
+        String string(name, -1, false);
+        VariableInfo* pVariableInfo = mSymbolTable.get(&string);
+        if (pVariableInfo) {
+            return pVariableInfo->pAddress;
         }
         return NULL;
     }
diff --git a/libacc/tests/Android.mk b/libacc/tests/Android.mk
index 2cff9d3..f8907b4 100644
--- a/libacc/tests/Android.mk
+++ b/libacc/tests/Android.mk
@@ -1,5 +1,9 @@
 LOCAL_PATH:= $(call my-dir)
+
+# Executable for host
+# ========================================================
 include $(CLEAR_VARS)
+LOCAL_MODULE:= acc
 
 LOCAL_SRC_FILES:= \
 	main.cpp
@@ -7,9 +11,23 @@
 LOCAL_SHARED_LIBRARIES := \
     libacc
 
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_HOST_EXECUTABLE)
+
+# Executable for target
+# ========================================================
+include $(CLEAR_VARS)
 LOCAL_MODULE:= acc
 
+LOCAL_SRC_FILES:= \
+	main.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libacc
+
+LOCAL_CFLAGS := -O0 -g 
+
 LOCAL_MODULE_TAGS := tests
 
 include $(BUILD_EXECUTABLE)
-
diff --git a/libacc/tests/data/constants.c b/libacc/tests/data/constants.c
new file mode 100644
index 0000000..230109a
--- /dev/null
+++ b/libacc/tests/data/constants.c
@@ -0,0 +1,30 @@
+#define FOO 0x10
+
+int main() {
+    printf("0 = %d\n", 0);
+    printf("010 = %d\n", 010);
+    printf("0x10 = %d\n", FOO);
+    printf("'\\a' = %d\n", '\a');
+    printf("'\\b' = %d\n", '\b');
+    printf("'\\f' = %d\n", '\f');
+    printf("'\\n' = %d\n", '\n');
+    printf("'\\r' = %d\n", '\r');
+    printf("'\\t' = %d\n", '\t');
+    printf("'\\v' = %d\n", '\v');
+    // Undefined
+    // printf("'\\z' = %d\n", '\z');
+    printf("'\\\\' = %d\n", '\\');
+    printf("'\\'' = %d\n", '\'');
+    printf("'\\\"' = %d\n", '\"');
+    printf("'\\?' = %d\n", '\?');
+    printf("'\\0' = %d\n", '\0');
+    printf("'\\1' = %d\n", '\1');
+    printf("'\\12' = %d\n", '\12');
+    printf("'\\123' = %d\n", '\123');
+    printf("'\\x0' = %d\n", '\x0');
+    printf("'\\x1' = %d\n", '\x1');
+    printf("'\\x12' = %d\n", '\x12');
+    printf("'\\x123' = %d\n", '\x123');
+    printf("'\\x1f' = %d\n", '\x1f');
+    printf("'\\x1F' = %d\n", '\x1F');
+}
diff --git a/libacc/tests/data/expr-ansi.c b/libacc/tests/data/expr-ansi.c
new file mode 100644
index 0000000..d463659
--- /dev/null
+++ b/libacc/tests/data/expr-ansi.c
@@ -0,0 +1,60 @@
+/* Test operators */
+
+void testInc() { int a, b; a = 3; b = a++; printf("3++ = %d %d\n", b, a); }
+void testDec() { int a, b; a = 3; b = a--; printf("3-- = %d %d\n", b, a); }
+void testTimes(){ printf("%d * %d = %d\n", 10, 4, 10 * 4); }
+void testDiv(){ printf("%d / %d = %d\n", 11, 4, 11 / 4); }
+void testMod(){ printf("%d %% %d = %d\n", 11, 4, 11 % 4); }
+void testPlus(){ printf("%d + %d = %d\n", 10, 4, 10 + 4); }
+void testMinus(){ printf("%d - %d = %d\n", 10, 4, 10 - 4); }
+void testShiftLeft(){ printf("%d << %d = %d\n", 10, 4, 10 << 4); }
+void testShiftRight(){ printf("%d >> %d = %d\n", 100, 4, 100 >> 4); }
+void testLess(){ printf("%d < %d = %d\n", 10, 4, 10 < 4); }
+void testLesEqual(){ printf("%d <= %d = %d\n", 10, 4, 10 <= 4); }
+void testGreater(){ printf("%d > %d = %d\n", 10, 4, 10 > 4); }
+void testGreaterEqual(){ printf("%d >= %d = %d\n", 10, 4, 10 >= 4); }
+void testEqualTo(){ printf("%d == %d = %d\n", 10, 4, 10 == 4); }
+void testNotEqualTo(){ printf("%d != %d = %d\n", 10, 4, 10 != 4); }
+void testBitAnd(){ printf("%d & %d = %d\n", 10, 7, 10 & 7); }
+void testBitXor(){ printf("%d ^ %d = %d\n", 10, 7, 10 ^ 7); }
+void testBitOr(){ printf("%d | %d = %d\n", 10, 4, 10 | 4); }
+void testAssignment(){ int a, b; a = 3; b = a; printf("b == %d\n", b); }
+void testLogicalAnd(){ printf("%d && %d = %d\n", 10, 4, 10 && 4); }
+void testLogicalOr(){ printf("%d || %d = %d\n", 10, 4, 10 || 4); }
+void testAddressOf(){ int a; printf("&a is %d\n", &a); }
+void testPointerIndirection(){ int a, b; a = &b; b = 17; printf("*%d  = %d =?= %d\n", a, * (int*) a, b); }
+void testNegation(){ printf("-%d = %d\n", 10, -10); }
+void testUnaryPlus(){ printf("+%d = %d\n", 10, +10); }
+void testUnaryNot(){ printf("!%d = %d\n", 10, !10); }
+void testBitNot(){ printf("~%d = %d\n", 10, ~10); }
+
+int main(int a, char** b) {
+    testInc();
+    testDec();
+    testTimes();
+    testDiv();
+    testMod();
+    testPlus();
+    testMinus();
+    testShiftLeft();
+    testShiftRight();
+    testLess();
+    testLesEqual();
+    testGreater();
+    testGreaterEqual();
+    testEqualTo();
+    testNotEqualTo();
+    testBitAnd();
+    testBinXor();
+    testBitOr();
+    testAssignment();
+    testLogicalAnd();
+    testLogicalOr();
+    testAddressOf();
+    testPointerIndirection();
+    testNegation();
+    testUnaryPlus();
+    testUnaryNot();
+    testBitNot();
+    return 0;
+}
diff --git a/libacc/tests/data/expr2.c b/libacc/tests/data/expr2.c
new file mode 100644
index 0000000..04b6a38
--- /dev/null
+++ b/libacc/tests/data/expr2.c
@@ -0,0 +1,6 @@
+/* Test operators */
+
+main() {
+    int a;
+    a = a++;
+}
diff --git a/libacc/tests/data/locals.c b/libacc/tests/data/locals.c
new file mode 100644
index 0000000..f1ef363
--- /dev/null
+++ b/libacc/tests/data/locals.c
@@ -0,0 +1,71 @@
+int a;
+
+int f() {
+    int a;
+    // Undefined variable b
+    // printf("f 0: a = %d b = %d\n", a, b);
+    printf("f 0: a = %d\n", a);
+    a = 2;
+    printf("f 1: a = %d\n", a);
+}
+
+int g(int a) {
+    printf("g 0: a = %d\n", a);
+    a = 3;
+    printf("g 1: a = %d\n", a);
+}
+
+int h(int a) {
+    // int a; // gcc 4.3 says error: 'a' redeclared as different kind of symbol
+
+    printf("h 0: a = %d\n", a);
+    a = 4;
+    printf("h 1: a = %d\n", a);
+}
+
+// Already defined global 
+// int h() {}
+int globCheck() {
+    fprintf(stdout, "globCheck()\n");
+}
+
+int fwdCheck() {
+    b();
+    // Undefined forward reference
+    // c();
+}
+
+int b() {
+    printf("b()\n");
+}
+
+int nested() {
+    int a;
+    printf("nested 0: a = %d\n", a);
+    a = 50;
+    printf("nested 1: a = %d\n", a);
+    {
+        int a;
+        printf("nested 2: a = %d\n", a);
+        a = 51;
+        printf("nested 3: a = %d\n", a);
+    }
+    printf("nested 4: a = %d\n", a);
+}
+
+int main() {
+    globCheck();
+    fwdCheck();
+    printf("main 0: a = %d\n", a);
+    a = 5;
+    printf("main 1: a = %d\n", a);
+    f();
+    printf("main 2: a = %d\n", a);
+    g(77);
+    printf("main 3: a = %d\n", a);
+    h(30);
+    printf("main 4: a = %d\n", a);
+    nested();
+    printf("main 5: a = %d\n", a);
+    return 0;
+}
diff --git a/libacc/tests/data/otcc-ansi.c b/libacc/tests/data/otcc-ansi.c
index 069514b..72580e9 100644
--- a/libacc/tests/data/otcc-ansi.c
+++ b/libacc/tests/data/otcc-ansi.c
@@ -50,7 +50,7 @@
         o();
     }
     C = 0;
-    d = h;
+    d = h;	
     if (X()) {
         E(32);
         M = D;
@@ -162,7 +162,7 @@
 
 void N(int j, int e) {
     ae(j + 131);
-    s((e < 512) << 7 | 5, e);
+    s((e > -512 && e < 512) << 7 | 5, e);
 }
 
 void T (int j) {
@@ -404,7 +404,7 @@
                     v=v +4;
                 }
                 ad();
-                if( d == 44)ad();
+                if( d == 44)ad()	;
             }
             ad();
         }
@@ -432,11 +432,20 @@
     }
 }
 
+int run(int g, int e) {
+    return (*(int(*)()) *(int*) (P + 592))(g, e);
+}
+
 int main(int g, int e) {
+    int result;
     Q = stdin;
     if (g-- > 1) {
         e = e + 4;
         Q = fopen(*(int*) e, "r");
+        if (!Q) {
+            fprintf(stderr, "otcc-ansi.c: could not open file %s\n", *(int*) e);
+            return -2;
+        }
     }
     D = strcpy(R = calloc(1, 99999), " int if else while break return for define main ") + 48;
     v = calloc(1, 99999);
@@ -445,5 +454,13 @@
     o();
     ad();
     ab(0);
-    return (*(int(*)()) *(int*) (P + 592))(g, e);
+    if (mprotect(ac & (~ 4095), (99999 + 4095) & (~ 4095), 7)) {
+        printf("Mprotect failed. %d\n", errno);
+        return -1;
+    }
+    fprintf(stderr, "otcc-ansi.c: About to execute compiled code:\n");
+    result = run(g, e);
+    fprintf(stderr, "atcc-ansi.c: result: %d\n", result);
+    return result;
 }
+
diff --git a/libacc/tests/data/otcc.c b/libacc/tests/data/otcc.c
index 577fcf3..433ae2e 100644
--- a/libacc/tests/data/otcc.c
+++ b/libacc/tests/data/otcc.c
@@ -441,6 +441,8 @@
 o f;
 c;
 ab(0);
+mprotect(ac & (~ 4095), (99999 + 4095) & (~ 4095), 7);
+fprintf(stderr, "otcc.c: about to execute compiled code.\n");
 J(*(int(*)f)k(P+592))(g,n;
 }
 
diff --git a/libacc/tests/data/returnval-ansi.c b/libacc/tests/data/returnval-ansi.c
index 42802c5..6b53fd5 100644
--- a/libacc/tests/data/returnval-ansi.c
+++ b/libacc/tests/data/returnval-ansi.c
@@ -1,7 +1,8 @@
+
 int main(int argc, char** argv) {
   return f();
 }
 
 int f() {
-    return 10;
+    return 42;
 }
diff --git a/libacc/tests/data/returnval.c b/libacc/tests/data/returnval.c
index 1b9dd81..1cf5bae 100644
--- a/libacc/tests/data/returnval.c
+++ b/libacc/tests/data/returnval.c
@@ -1,6 +1,3 @@
-#pragma foo3(bar) //sdfsfd
-#pragma a(b)
-
 main() {
   return 42;
 }
diff --git a/libacc/tests/main.cpp b/libacc/tests/main.cpp
index acee09d..4f8a65d 100644
--- a/libacc/tests/main.cpp
+++ b/libacc/tests/main.cpp
@@ -32,6 +32,7 @@
 int main(int argc, char** argv) {
     const char* inFile = NULL;
     bool printListing;
+    bool runResults = false;
     FILE* in = stdin;
     int i;
     for (i = 1; i < argc; i++) {
@@ -41,6 +42,9 @@
                 case 'S':
                     printListing = true;
                     break;
+                case 'R':
+                    runResults = true;
+                    break;
             default:
                 fprintf(stderr, "Unrecognized flag %s\n", arg);
                 return 3;
@@ -105,16 +109,20 @@
         }
     }
 
-    accGetScriptLabel(script, "main", (ACCvoid**) & mainPointer);
+    if (runResults) {
+        accGetScriptLabel(script, "main", (ACCvoid**) & mainPointer);
 
-    result = accGetError(script);
-    if (result == ACC_NO_ERROR) {
-        fprintf(stderr, "Executing compiled code:\n");
-        int codeArgc = argc - i + 1;
-        char** codeArgv = argv + i - 1;
-        codeArgv[0] = (char*) (inFile ? inFile : "stdin");
-        result = run(mainPointer, codeArgc, codeArgv);
-        fprintf(stderr, "result: %d\n", result);
+        result = accGetError(script);
+        if (result != ACC_NO_ERROR) {
+            fprintf(stderr, "Could not find main: %d\n", result);
+        } else {
+            fprintf(stderr, "Executing compiled code:\n");
+            int codeArgc = argc - i + 1;
+            char** codeArgv = argv + i - 1;
+            codeArgv[0] = (char*) (inFile ? inFile : "stdin");
+            result = run(mainPointer, codeArgc, codeArgv);
+            fprintf(stderr, "result: %d\n", result);
+        }
     }
 
 exit:
diff --git a/libacc/tests/testarm b/libacc/tests/testarm
index 24fbc42..1d4b866 100755
--- a/libacc/tests/testarm
+++ b/libacc/tests/testarm
@@ -6,4 +6,4 @@
 mm -j8
 cd tests
 adb sync
-adb shell /system/bin/acc -S /system/bin/returnval-ansi.c
+adb shell /system/bin/acc -R -S /system/bin/returnval-ansi.c
diff --git a/libacc/tests/testlocal b/libacc/tests/testlocal
index ccabf7d..1a0b4c5 100755
--- a/libacc/tests/testlocal
+++ b/libacc/tests/testlocal
@@ -1,17 +1,23 @@
-#!/bin/sh
-rm -f test-acc
-cd ..
-g++ -I../include acc.cpp disassem.cpp tests/main.cpp -g -ldl -o tests/test-acc
-cd tests
-if [ -x "test-acc" ]; then
-  ./test-acc -S data/returnval-ansi.c
+#!/bin/bash
 
-  if [ "$(uname)" = "Linux" ]; then
-    if [ "$(uname -m)" = "i686" ]; then
-      echo "Linux i686. Testing otcc-ansi.c"
-      ./test-acc data/otcc-ansi.c data/returnval.c
-      echo "Linux i686. Testing otcc-ansi.c data/otcc.c"
-      ./test-acc data/otcc-ansi.c data/otcc.c data/returnval.c
-    fi
-  fi
+SCRIPT_DIR=`dirname $BASH_SOURCE`
+DATA=$SCRIPT_DIR/data
+ACC=`which acc`
+
+echo "Compiling returnval-ansi.c"
+$ACC -S $DATA/returnval-ansi.c
+
+echo "Compiling whole compiler."
+$ACC -S "$DATA/otcc-ansi.c"
+
+if file $ACC | grep -q "ELF 32-bit LSB executable, Intel 80386"; then
+	echo "Linux 32bit Intel."
+	echo "TESTING returnval-ansi.c:"
+        $ACC -R $DATA/returnval-ansi.c
+        echo TESTING otcc-ansi.c returnval-ansi.c
+	$ACC -R "$DATA/otcc-ansi.c" "$DATA/returnval.c"
+	echo TESTING otcc-ansi.c otcc.c returnval-ansi.c
+	$ACC -R $DATA/otcc-ansi.c $DATA/otcc.c $DATA/returnval.c
 fi
+
+echo "Done with tests."
diff --git a/libsysutils/src/FrameworkCommand.cpp b/libsysutils/src/FrameworkCommand.cpp
index 94e7426..c52eac7 100644
--- a/libsysutils/src/FrameworkCommand.cpp
+++ b/libsysutils/src/FrameworkCommand.cpp
@@ -25,7 +25,7 @@
     mCommand = cmd;
 }
 
-int FrameworkCommand::runCommand(SocketClient *c, char *data) {
+int FrameworkCommand::runCommand(SocketClient *c, int argc, char **argv) {
     LOGW("Command %s has no run handler!", getCommand());
     errno = ENOSYS;
     return -1;
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index e8ae847..e9ca897 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -36,17 +36,14 @@
     if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) {
         LOGE("read() failed (%s)", strerror(errno));
         return errno;
-    } else if (!len) {
-        LOGW("Lost connection to client");
+    } else if (!len)
         return false;
-    }
 
     int offset = 0;
     int i;
 
     for (i = 0; i < len; i++) {
-        if (buffer[i] == '\n') {
-            buffer[i] = '\0';
+        if (buffer[i] == '\0') {
             dispatchCommand(c, buffer + offset);
             offset = i + 1;
         }
@@ -58,13 +55,20 @@
     mCommands->push_back(cmd);
 }
 
-void FrameworkListener::dispatchCommand(SocketClient *cli, char *cmd) {
-    char *next = cmd;
-    char *cm;
-    char *arg;
+void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
+    int argc;
+    char *argv[FrameworkListener::CMD_ARGS_MAX];
 
-    if (!(cm = strsep(&next, ":"))) {
-        cli->sendMsg(500, "Malformatted message", false);
+    if (!index(data, '"')) {
+        char *next = data;
+        char *field;
+        int i;
+
+        for (i = 0; (i < FrameworkListener::CMD_ARGS_MAX) &&
+                    (argv[i] = strsep(&next, " ")); i++);
+        argc = i+1;
+    } else {
+        LOGD("blehhh not supported");
         return;
     }
 
@@ -73,8 +77,8 @@
     for (i = mCommands->begin(); i != mCommands->end(); ++i) {
         FrameworkCommand *c = *i;
 
-        if (!strcmp(cm, c->getCommand())) {
-            if (c->runCommand(cli, next)) {
+        if (!strcmp(argv[0], c->getCommand())) {
+            if (c->runCommand(cli, argc, argv)) {
                 LOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
             }
             return;
diff --git a/libsysutils/src/SocketClient.cpp b/libsysutils/src/SocketClient.cpp
index f0e846f..857ed4d 100644
--- a/libsysutils/src/SocketClient.cpp
+++ b/libsysutils/src/SocketClient.cpp
@@ -33,19 +33,10 @@
         return -1;
     }
 
-    char *tmp;
-    const char *bp = msg;
-
-    if (msg[strlen(msg)] != '\n') {
-        tmp = (char *) alloca(strlen(msg) + 1);
-        strcpy(tmp, msg);
-        strcat(tmp, "\n");
-        bp = tmp;
-    }
-
+    // Send the message including null character
     int rc = 0;
-    const char *p = bp;
-    int brtw = strlen(bp);
+    const char *p = msg;
+    int brtw = strlen(msg) + 1;
 
     pthread_mutex_lock(&mWriteMutex);
     while(brtw) {
diff --git a/libsysutils/src/SocketListener.cpp b/libsysutils/src/SocketListener.cpp
index 1f80121..1a937c2 100644
--- a/libsysutils/src/SocketListener.cpp
+++ b/libsysutils/src/SocketListener.cpp
@@ -157,7 +157,6 @@
                 if (FD_ISSET(fd, &read_fds)) {
                     pthread_mutex_unlock(&mClientsLock);
                     if (!onDataAvailable(*it)) {
-                        LOGD("SocketListener closing client socket");
                         close(fd);
                         pthread_mutex_lock(&mClientsLock);
                         delete *it;
diff --git a/logcat/event-log-tags b/logcat/event-log-tags
index f9355ec..13f7488 100644
--- a/logcat/event-log-tags
+++ b/logcat/event-log-tags
@@ -350,3 +350,9 @@
 # browser stats for diary study
 70101 browser_zoom_level_change (start level|1|5),(end level|1|5),(time|2|3)
 70102 browser_double_tap_duration (duration|1|3),(time|2|3)
+
+# aggregation service
+70200 aggregation (aggregation time|2|3)
+
+# NOTE - the range 1000000-2000000 is reserved for partners and others who
+# want to define their own log tags without conflicting with the core platform.
diff --git a/nexus/Android.mk b/nexus/Android.mk
index bd4e3d4..6172e62 100644
--- a/nexus/Android.mk
+++ b/nexus/Android.mk
@@ -5,26 +5,35 @@
 
 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           \
-                  WifiScanner.cpp          \
-                  WifiNetwork.cpp          \
-                  OpenVpnController.cpp    \
-                  InterfaceConfig.cpp      \
-                  PropertyManager.cpp      \
-                  SupplicantState.cpp 
+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                       \
+                  WifiScanner.cpp                      \
+                  WifiNetwork.cpp                      \
+                  OpenVpnController.cpp                \
+                  InterfaceConfig.cpp                  \
+                  PropertyManager.cpp                  \
+                  SupplicantState.cpp                  \
+                  SupplicantEventFactory.cpp           \
+                  SupplicantConnectedEvent.cpp         \
+                  SupplicantAssociatingEvent.cpp       \
+                  SupplicantAssociatedEvent.cpp        \
+                  SupplicantStateChangeEvent.cpp       \
+                  SupplicantScanResultsEvent.cpp       \
+                  SupplicantConnectionTimeoutEvent.cpp \
+                  SupplicantDisconnectedEvent.cpp      \
+                  SupplicantStatus.cpp
 
 LOCAL_MODULE:= nexus
 
diff --git a/nexus/CommandListener.cpp b/nexus/CommandListener.cpp
index e8de7f5..8eb378b 100644
--- a/nexus/CommandListener.cpp
+++ b/nexus/CommandListener.cpp
@@ -53,7 +53,8 @@
                  NexusCommand("wifi_create_network") {
 }
 
-int CommandListener::WifiCreateNetworkCmd::runCommand(SocketClient *cli, char *data) {
+int CommandListener::WifiCreateNetworkCmd::runCommand(SocketClient *cli,
+                                                      int argc, char **argv) {
     NetworkManager *nm = NetworkManager::Instance();
     WifiController *wc = (WifiController *) nm->findController("WIFI");
     WifiNetwork *wn;
@@ -72,11 +73,12 @@
                  NexusCommand("wifi_remove_network") {
 }
 
-int CommandListener::WifiRemoveNetworkCmd::runCommand(SocketClient *cli, char *data) {
+int CommandListener::WifiRemoveNetworkCmd::runCommand(SocketClient *cli,
+                                                      int argc, char **argv) {
     NetworkManager *nm = NetworkManager::Instance();
     WifiController *wc = (WifiController *) nm->findController("WIFI");
 
-    if (wc->removeNetwork(atoi(data)))
+    if (wc->removeNetwork(atoi(argv[1])))
         cli->sendMsg(ErrorCode::OperationFailed, "Failed to remove network", true);
     else {
         cli->sendMsg(ErrorCode::CommandOkay, "Network removed.", false);
@@ -88,7 +90,8 @@
                  NexusCommand("wifi_scan_results") {
 }
 
-int CommandListener::WifiScanResultsCmd::runCommand(SocketClient *cli, char *data) {
+int CommandListener::WifiScanResultsCmd::runCommand(SocketClient *cli,
+                                                    int argc, char **argv) {
     NetworkManager *nm = NetworkManager::Instance();
     WifiController *wc = (WifiController *) nm->findController("WIFI");
 
@@ -114,7 +117,8 @@
                  NexusCommand("wifi_list_networks") {
 }
 
-int CommandListener::WifiListNetworksCmd::runCommand(SocketClient *cli, char *data) {
+int CommandListener::WifiListNetworksCmd::runCommand(SocketClient *cli,
+                                                     int argc, char **argv) {
     NetworkManager *nm = NetworkManager::Instance();
     WifiController *wc = (WifiController *) nm->findController("WIFI");
 
@@ -144,23 +148,19 @@
                  NexusCommand("get") {
 }
 
-int CommandListener::GetCmd::runCommand(SocketClient *cli, char *data) {
-    char *next = data;
-    char *propname;
+int CommandListener::GetCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+    char val[Property::ValueMaxSize];
 
-    if (!(propname = strsep(&next, ":")))
-        goto out_inval;
-
-    char pb[Property::NameMaxSize + 6];
-    snprintf(pb, sizeof(pb), "%s:", propname);
-
-    if (!NetworkManager::Instance()->getPropMngr()->get(propname,
-                                                        &pb[strlen(pb)],
-                                                        sizeof(pb) - strlen(pb))) {
+    if (!NetworkManager::Instance()->getPropMngr()->get(argv[1],
+                                                        val,
+                                                        sizeof(val))) {
         goto out_inval;
     }
 
-    cli->sendMsg(ErrorCode::PropertyRead, pb, false);
+    char *tmp;
+    asprintf(&tmp, "%s %s", argv[1], val);
+    cli->sendMsg(ErrorCode::PropertyRead, tmp, false);
+    free(tmp);
 
     cli->sendMsg(ErrorCode::CommandOkay, "Property read.", false);
     return 0;
@@ -174,23 +174,9 @@
                  NexusCommand("set") {
 }
 
-int CommandListener::SetCmd::runCommand(SocketClient *cli, char *data) {
-    char *bword;
-    char *last;
-    char propname[Property::NameMaxSize];
-    char propval[Property::ValueMaxSize];
-
-    if (!(bword = strtok_r(data, ":", &last)))
-        goto out_inval;
-
-    strncpy(propname, bword, sizeof(propname));
-
-    if (!(bword = strtok_r(NULL, ":", &last)))
-        goto out_inval;
-
-    strncpy(propval, bword, sizeof(propval));
-
-    if (NetworkManager::Instance()->getPropMngr()->set(propname, propval))
+int CommandListener::SetCmd::runCommand(SocketClient *cli, int argc,
+                                        char **argv) {
+    if (NetworkManager::Instance()->getPropMngr()->set(argv[1], argv[2]))
         goto out_inval;
 
     cli->sendMsg(ErrorCode::CommandOkay, "Property set.", false);
@@ -206,7 +192,7 @@
                  NexusCommand("list") {
 }
 
-int CommandListener::ListCmd::runCommand(SocketClient *cli, char *data) {
+int CommandListener::ListCmd::runCommand(SocketClient *cli, int argc, char **argv) {
     android::List<char *> *pc;
 
     if (!(pc = NetworkManager::Instance()->getPropMngr()->createPropertyList())) {
@@ -227,7 +213,7 @@
         }
 
         char *buf;
-        if (asprintf(&buf, "%s:%s", (*it), p_v) < 0) {
+        if (asprintf(&buf, "%s %s", (*it), p_v) < 0) {
             LOGE("Failed to allocate memory");
             free((*it));
             continue;
diff --git a/nexus/CommandListener.h b/nexus/CommandListener.h
index b57c25f..30c6dc0 100644
--- a/nexus/CommandListener.h
+++ b/nexus/CommandListener.h
@@ -31,56 +31,56 @@
     public:
         WifiScanCmd();
         virtual ~WifiScanCmd() {}
-        int runCommand(SocketClient *c, char *data);
+        int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
     class WifiScanResultsCmd : public NexusCommand {
     public:
         WifiScanResultsCmd();
         virtual ~WifiScanResultsCmd() {}
-        int runCommand(SocketClient *c, char *data);
+        int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
     class WifiCreateNetworkCmd : public NexusCommand {
     public:
         WifiCreateNetworkCmd();
         virtual ~WifiCreateNetworkCmd() {}
-        int runCommand(SocketClient *c, char *data);
+        int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
     class WifiRemoveNetworkCmd : public NexusCommand {
     public:
         WifiRemoveNetworkCmd();
         virtual ~WifiRemoveNetworkCmd() {}
-        int runCommand(SocketClient *c, char *data);
+        int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
     class WifiListNetworksCmd : public NexusCommand {
     public:
         WifiListNetworksCmd();
         virtual ~WifiListNetworksCmd() {}
-        int runCommand(SocketClient *c, char *data);
+        int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
     class SetCmd : public NexusCommand {
     public:
         SetCmd();
         virtual ~SetCmd() {}
-        int runCommand(SocketClient *c, char *data);
+        int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
     class GetCmd : public NexusCommand {
     public:
         GetCmd();
         virtual ~GetCmd() {}
-        int runCommand(SocketClient *c, char *data);
+        int runCommand(SocketClient *c, int argc, char ** argv);
     };
 
     class ListCmd : public NexusCommand {
     public:
         ListCmd();
         virtual ~ListCmd() {}
-        int runCommand(SocketClient *c, char *data);
+        int runCommand(SocketClient *c, int argc, char ** argv);
     };
 };
 
diff --git a/nexus/Controller.cpp b/nexus/Controller.cpp
index 9d4ff3c..17fb519 100644
--- a/nexus/Controller.cpp
+++ b/nexus/Controller.cpp
@@ -34,9 +34,11 @@
 extern "C" int init_module(void *, unsigned int, const char *);
 extern "C" int delete_module(const char *, unsigned int);
 
-Controller::Controller(const char *name, PropertyManager *propMngr) {
+Controller::Controller(const char *name, PropertyManager *propMngr,
+                       IControllerHandler *handlers) {
     mPropMngr = propMngr;
     mName = strdup(name);
+    mHandlers = handlers;
     mBoundInterface = NULL;
 }
 
diff --git a/nexus/Controller.h b/nexus/Controller.h
index 9137f9a..af03d2e 100644
--- a/nexus/Controller.h
+++ b/nexus/Controller.h
@@ -23,12 +23,12 @@
 #include <utils/List.h>
 
 class PropertyManager;
+class IControllerHandler;
 
 #include "PropertyManager.h"
 #include "IPropertyProvider.h"
 
 class Controller : public IPropertyProvider {
-private:
     /*
      * Name of this controller - WIFI/VPN/USBNET/BTNET/BTDUN/LOOP/etc
      */
@@ -42,9 +42,11 @@
 
 protected:
     PropertyManager *mPropMngr;
+    IControllerHandler *mHandlers;
     
 public:
-    Controller(const char *name, PropertyManager *propMngr);
+    Controller(const char *name, PropertyManager *propMngr,
+               IControllerHandler *handlers);
     virtual ~Controller();
 
     virtual int start();
diff --git a/nexus/IControllerHandler.h b/nexus/IControllerHandler.h
new file mode 100644
index 0000000..92d015f
--- /dev/null
+++ b/nexus/IControllerHandler.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 _ICONTROLLER_HANDLER_H
+#define _ICONTROLLER_HANDLER_H
+
+class Controller;
+class InterfaceConfig;
+
+class IControllerHandler {
+public:
+    virtual void onInterfaceStarted(Controller *c, const InterfaceConfig *cfg) = 0;
+    virtual void onInterfaceStopping(Controller *c, const char *name) = 0;
+};
+
+#endif
+
diff --git a/nexus/ISupplicantEventHandler.h b/nexus/ISupplicantEventHandler.h
index 7e1bd5a..b7fd17b 100644
--- a/nexus/ISupplicantEventHandler.h
+++ b/nexus/ISupplicantEventHandler.h
@@ -17,21 +17,34 @@
 #ifndef _ISUPPLICANT_EVENT_HANDLER_H
 #define _ISUPPLICANT_EVENT_HANDLER_H
 
+class SupplicantAssociatingEvent;
+class SupplicantAssociatedEvent;
+class SupplicantConnectedEvent;
+class SupplicantScanResultsEvent;
+class SupplicantStateChangeEvent;
+class SupplicantConnectionTimeoutEvent;
+class SupplicantDisconnectedEvent;
+
 class ISupplicantEventHandler {
 public:
-    virtual int onConnectedEvent(SupplicantEvent *evt) = 0;
-    virtual int onDisconnectedEvent(SupplicantEvent *evt) = 0;
-    virtual int onTerminatingEvent(SupplicantEvent *evt) = 0;
-    virtual int onPasswordChangedEvent(SupplicantEvent *evt) = 0;
-    virtual int onEapNotificationEvent(SupplicantEvent *evt) = 0;
-    virtual int onEapStartedEvent(SupplicantEvent *evt) = 0;
-    virtual int onEapMethodEvent(SupplicantEvent *evt) = 0;
-    virtual int onEapSuccessEvent(SupplicantEvent *evt) = 0;
-    virtual int onEapFailureEvent(SupplicantEvent *evt) = 0;
-    virtual int onScanResultsEvent(SupplicantEvent *evt) = 0;
-    virtual int onStateChangeEvent(SupplicantEvent *evt) = 0;
-    virtual int onLinkSpeedEvent(SupplicantEvent *evt) = 0;
-    virtual int onDriverStateEvent(SupplicantEvent *evt) = 0;
+    virtual void onAssociatingEvent(SupplicantAssociatingEvent *evt) = 0;
+    virtual void onAssociatedEvent(SupplicantAssociatedEvent *evt) = 0;
+    virtual void onConnectedEvent(SupplicantConnectedEvent *evt) = 0;
+    virtual void onScanResultsEvent(SupplicantScanResultsEvent *evt) = 0;
+    virtual void onStateChangeEvent(SupplicantStateChangeEvent *evt) = 0;
+    virtual void onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt) = 0;
+    virtual void onDisconnectedEvent(SupplicantDisconnectedEvent *evt) = 0;
+#if 0
+    virtual void onTerminatingEvent(SupplicantEvent *evt) = 0;
+    virtual void onPasswordChangedEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapNotificationEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapStartedEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapMethodEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapSuccessEvent(SupplicantEvent *evt) = 0;
+    virtual void onEapFailureEvent(SupplicantEvent *evt) = 0;
+    virtual void onLinkSpeedEvent(SupplicantEvent *evt) = 0;
+    virtual void onDriverStateEvent(SupplicantEvent *evt) = 0;
+#endif
 };
 
 #endif
diff --git a/nexus/LoopController.cpp b/nexus/LoopController.cpp
index a86202a..5cfb1fe 100644
--- a/nexus/LoopController.cpp
+++ b/nexus/LoopController.cpp
@@ -19,8 +19,9 @@
 #include "LoopController.h"
 #include "PropertyManager.h"
 
-LoopController::LoopController(PropertyManager *propmngr) :
-                Controller("LOOP", propmngr) {
+LoopController::LoopController(PropertyManager *propmngr,
+                               IControllerHandler *handlers) :
+                Controller("LOOP", propmngr, handlers) {
 }
 
 int LoopController::set(const char *name, const char *value) {
diff --git a/nexus/LoopController.h b/nexus/LoopController.h
index bb8314f..53d16f1 100644
--- a/nexus/LoopController.h
+++ b/nexus/LoopController.h
@@ -19,10 +19,11 @@
 
 #include "Controller.h"
 
+class ControllerHandler;
 
 class LoopController : public Controller {
 public:
-    LoopController(PropertyManager *propmngr);
+    LoopController(PropertyManager *propmngr, IControllerHandler *h);
     virtual ~LoopController() {}
 
     int set(const char *name, const char *value);
diff --git a/nexus/NetworkManager.cpp b/nexus/NetworkManager.cpp
index f4ae88f..2f13a40 100644
--- a/nexus/NetworkManager.cpp
+++ b/nexus/NetworkManager.cpp
@@ -89,23 +89,26 @@
     return NULL;
 }
 
-int NetworkManager::onInterfaceStart(Controller *c, const InterfaceConfig *cfg) {
+void NetworkManager::onInterfaceStarted(Controller *c, const InterfaceConfig *cfg) {
     LOGD("Interface %s started by controller %s", c->getBoundInterface(), c->getName());
 
     // Look up the interface
 
     if (0) { // already started?
-        errno = EADDRINUSE;
-        return -1;
     }
 
-    if (cfg->getUseDhcp()) {
+    if (cfg) {
+        if (cfg->getUseDhcp()) {
+            // Launch DHCP thread
+        } else {
+            // Static configuration
+        }
     } else {
+        LOGD("No InterfaceConfig for %s:%s - assuming self-managed",
+            c->getName(), c->getBoundInterface());
     }
-    return 0;
 }
 
-int NetworkManager::onInterfaceStop(Controller *c, const char *name) {
+void NetworkManager::onInterfaceStopping(Controller *c, const char *name) {
     LOGD("Interface %s stopped by controller %s", name, c->getName());
-    return 0;
 }
diff --git a/nexus/NetworkManager.h b/nexus/NetworkManager.h
index e75382d..edc80c9 100644
--- a/nexus/NetworkManager.h
+++ b/nexus/NetworkManager.h
@@ -20,12 +20,12 @@
 #include <sysutils/SocketListener.h>
 
 #include "Controller.h"
-
 #include "PropertyManager.h"
+#include "IControllerHandler.h"
 
 class InterfaceConfig;
 
-class NetworkManager {
+class NetworkManager : public IControllerHandler {
 private:
     static NetworkManager *sInstance;
 
@@ -55,16 +55,7 @@
 
     NetworkManager(PropertyManager *propMngr);
 
-public:
-    /*
-     * Called from a controller when an interface is available/ready for use.
-     * 'cfg' contains information on how this interface should be configured.
-     */
-    int onInterfaceStart(Controller *c, const InterfaceConfig *cfg);
-
-    /*
-     * Called from a controller when an interface should be shut down
-     */
-    int onInterfaceStop(Controller *c, const char *name);
+    void onInterfaceStarted(Controller *c, const InterfaceConfig *cfg);
+    void onInterfaceStopping(Controller *c, const char *name);
 };
 #endif
diff --git a/nexus/OpenVpnController.cpp b/nexus/OpenVpnController.cpp
index 4c144a4..f1ea510 100644
--- a/nexus/OpenVpnController.cpp
+++ b/nexus/OpenVpnController.cpp
@@ -30,8 +30,9 @@
 #define DAEMON_PROP_NAME "vpn.openvpn.status"
 #define DAEMON_CONFIG_FILE "/data/misc/openvpn/openvpn.conf"
 
-OpenVpnController::OpenVpnController(PropertyManager *propmngr) :
-                   VpnController(propmngr) {
+OpenVpnController::OpenVpnController(PropertyManager *propmngr,
+                                     IControllerHandler *handlers) :
+                   VpnController(propmngr, handlers) {
     mServiceManager = new ServiceManager();
 }
 
@@ -40,11 +41,11 @@
 }
 
 int OpenVpnController::start() {
-    return 0;
+    return VpnController::start();
 }
 
 int OpenVpnController::stop() {
-    return 0;
+    return VpnController::stop();
 }
 
 int OpenVpnController::enable() {
diff --git a/nexus/OpenVpnController.h b/nexus/OpenVpnController.h
index 323c44c..529aab5 100644
--- a/nexus/OpenVpnController.h
+++ b/nexus/OpenVpnController.h
@@ -21,13 +21,14 @@
 #include "VpnController.h"
 
 class ServiceManager;
+class IControllerHandler;
 
 class OpenVpnController : public VpnController {
 private:
     ServiceManager *mServiceManager;
 
 public:
-    OpenVpnController(PropertyManager *propmngr);
+    OpenVpnController(PropertyManager *propmngr, IControllerHandler *handlers);
     virtual ~OpenVpnController();
 
     int start();
diff --git a/nexus/PropertyManager.cpp b/nexus/PropertyManager.cpp
index 3366bab..6faf9b8 100644
--- a/nexus/PropertyManager.cpp
+++ b/nexus/PropertyManager.cpp
@@ -37,7 +37,8 @@
     for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it) {
         if (!strcmp(name, (*it)->getName())) {
             errno = EADDRINUSE;
-            LOGE("Failed to register property (%s)", strerror(errno));
+            LOGE("Failed to register property %s (%s)",
+                 name, strerror(errno));
             pthread_mutex_unlock(&mLock);
             return -1;
         }
diff --git a/nexus/Supplicant.cpp b/nexus/Supplicant.cpp
index e69f2c0..9bb6bf2 100644
--- a/nexus/Supplicant.cpp
+++ b/nexus/Supplicant.cpp
@@ -29,13 +29,10 @@
 
 #include "Supplicant.h"
 #include "SupplicantListener.h"
-#include "SupplicantState.h"
-#include "SupplicantEvent.h"
-#include "ScanResult.h"
-#include "PropertyManager.h"
 #include "NetworkManager.h"
 #include "ErrorCode.h"
 #include "WifiController.h"
+#include "SupplicantStatus.h"
 
 #include "libwpa_client/wpa_ctrl.h"
 
@@ -45,21 +42,16 @@
 #define SUPP_CONFIG_TEMPLATE "/system/etc/wifi/wpa_supplicant.conf"
 #define SUPP_CONFIG_FILE "/data/misc/wifi/wpa_supplicant.conf"
 
-Supplicant::Supplicant(WifiController *wc, PropertyManager *propmngr) {
+Supplicant::Supplicant(WifiController *wc, ISupplicantEventHandler *handlers) {
+    mHandlers = handlers;
     mController = wc;
-    mPropMngr = propmngr;
     mInterfaceName = NULL;
     mCtrl = NULL;
     mMonitor = NULL;
     mListener = NULL;
    
-    mState = SupplicantState::UNKNOWN;
-
     mServiceManager = new ServiceManager();
 
-    mLatestScanResults = new ScanResultCollection();
-    pthread_mutex_init(&mLatestScanResultsLock, NULL);
-
     mNetworks = new WifiNetworkCollection();
     pthread_mutex_init(&mNetworksLock, NULL);
 }
@@ -92,14 +84,11 @@
         return -1;
     }
 
-    mPropMngr->registerProperty("wifi.supplicant.state", this);
     return 0;
 }
 
 int Supplicant::stop() {
 
-    mPropMngr->unregisterProperty("wifi.supplicant.state");
-
     if (mListener->stopListener()) {
         LOGW("Unable to stop supplicant listener (%s)", strerror(errno));
         return -1;
@@ -125,6 +114,30 @@
     return mServiceManager->isRunning(SUPPLICANT_SERVICE_NAME);
 }
 
+SupplicantStatus *Supplicant::getStatus() {
+    char *reply;
+    size_t len = 4096;
+
+    if (!(reply = (char *) malloc(len))) {
+        errno = ENOMEM;
+        return NULL;
+    }
+
+    if (sendCommand("STATUS", reply, &len)) {
+        free(reply);
+        return NULL;
+    }
+
+    SupplicantStatus *ss = SupplicantStatus::createStatus(reply, len);
+  
+    free (reply);
+    return ss;
+}
+
+/*
+ * Retrieves the list of networks from Supplicant
+ * and merge them into our current list
+ */
 int Supplicant::refreshNetworkList() {
     char *reply;
     size_t len = 4096;
@@ -144,27 +157,59 @@
 
     if (!strtok_r(reply, "\n", &linep_next)) {
         LOGW("Malformatted network list\n");
-    } else {
-        pthread_mutex_lock(&mNetworksLock);
-        if (!mNetworks->empty()) {
-            WifiNetworkCollection::iterator i;
-
-            for (i = mNetworks->begin(); i !=mNetworks->end(); ++i)
-                delete *i;
-            mNetworks->clear();
-        }
-
-        while((linep = strtok_r(NULL, "\n", &linep_next))) {
-            WifiNetwork *wn = new WifiNetwork(mController, this, linep);
-            mNetworks->push_back(wn);
-            if (wn->refresh())
-                LOGW("Unable to refresh network id %d", wn->getNetworkId());
-        }
-
-        LOGD("Loaded %d networks\n", mNetworks->size());
-        pthread_mutex_unlock(&mNetworksLock);
+        free(reply);
+        errno = EIO;
+        return -1;
     }
 
+    pthread_mutex_lock(&mNetworksLock);
+
+    int num_added = 0;
+    int num_refreshed = 0;
+    int num_removed = 0;
+    while((linep = strtok_r(NULL, "\n", &linep_next))) {
+        // TODO: Move the decode into a static method so we
+        // don't create new_wn when we don't have to.
+        WifiNetwork *new_wn = new WifiNetwork(mController, this, linep);
+        WifiNetwork *merge_wn;
+
+        if ((merge_wn = this->lookupNetwork_UNLOCKED(new_wn->getNetworkId()))) {
+            num_refreshed++;
+            if (merge_wn->refresh()) {
+                LOGW("Error refreshing network %d (%s)",
+                     merge_wn->getNetworkId(), strerror(errno));
+                }
+            delete new_wn;
+        } else {
+            num_added++;
+            new_wn->registerProperties();
+            mNetworks->push_back(new_wn);
+            if (new_wn->refresh()) {
+                LOGW("Unable to refresh network id %d (%s)",
+                    new_wn->getNetworkId(), strerror(errno));
+            }
+        }
+    }
+
+    if (!mNetworks->empty()) {
+        // TODO: Add support for detecting removed networks
+        WifiNetworkCollection::iterator i;
+
+        for (i = mNetworks->begin(); i != mNetworks->end(); ++i) {
+            if (0) {
+                num_removed++;
+                (*i)->unregisterProperties();
+                delete (*i);
+                i = mNetworks->erase(i);
+            }
+        }
+    }
+
+
+    LOGD("Networks added %d, refreshed %d, removed %d\n", 
+         num_added, num_refreshed, num_removed);
+    pthread_mutex_unlock(&mNetworksLock);
+
     free(reply);
     return 0;
 }
@@ -192,7 +237,7 @@
         return -1;
     }
 
-    mListener = new SupplicantListener(this, mMonitor);
+    mListener = new SupplicantListener(mHandlers, mMonitor);
 
     if (mListener->startListener()) {
         LOGE("Error - unable to start supplicant listener");
@@ -245,165 +290,6 @@
     return 0;
 }
 
-int Supplicant::set(const char *name, const char *value) {
-    const char *n = name + strlen("wifi.supplicant.");
-
-    errno = -EROFS;
-    return -1;
-}
-
-const char *Supplicant::get(const char *name, char *buffer, size_t max) {
-    const char *n = name + strlen("wifi.supplicant.");
-
-    if (!strcasecmp(n, "state"))
-        return SupplicantState::toString(mState, buffer, max);
-    errno = ENOENT;
-    return NULL;
-}
-
-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) {
-    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);
-            pthread_mutex_unlock(&mLatestScanResultsLock);
-            return 0;
-        }
-
-        while((linep = strtok_r(NULL, "\n", &linep_next)))
-            mLatestScanResults->push_back(new ScanResult(linep));
-
-        char tmp[128];
-        sprintf(tmp, "Scan results ready (%d)", mLatestScanResults->size());
-        NetworkManager::Instance()->getBroadcaster()->
-                                    sendBroadcast(ErrorCode::UnsolicitedInformational, tmp, false);
-        pthread_mutex_unlock(&mLatestScanResultsLock);
-        free(reply);
-    } else {
-        LOGW("Unknown SCAN_RESULTS event (%s)", evt->getEvent());
-    }
-    return 0;
-}
-
-int Supplicant::onStateChangeEvent(SupplicantEvent *evt) {
-    char *bword, *last;
-    char *tmp = strdup(evt->getEvent());
-
-    if (!(bword = strtok_r(tmp, " ", &last))) {
-        LOGE("Malformatted state update (%s)", evt->getEvent());
-        free(tmp);
-        return 0;
-    }
-
-    if (!(bword = strtok_r(NULL, " ", &last))) {
-        LOGE("Malformatted state update (%s)", evt->getEvent());
-        free(tmp);
-        return 0;
-    }
-
-    mState = atoi(&bword[strlen("state=")]);
-    LOGD("State changed to %d", mState);
-    free(tmp);
-    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
-ScanResultCollection *Supplicant::createLatestScanResults() {
-    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;
-}
-
 WifiNetwork *Supplicant::createNetwork() {
     char reply[255];
     size_t len = sizeof(reply) -1;
@@ -445,14 +331,18 @@
 
 WifiNetwork *Supplicant::lookupNetwork(int networkId) {
     pthread_mutex_lock(&mNetworksLock);
+    WifiNetwork *wn = lookupNetwork_UNLOCKED(networkId);
+    pthread_mutex_unlock(&mNetworksLock);
+    return wn;
+}
+
+WifiNetwork *Supplicant::lookupNetwork_UNLOCKED(int networkId) {
     WifiNetworkCollection::iterator it;
     for (it = mNetworks->begin(); it != mNetworks->end(); ++it) {
         if ((*it)->getNetworkId() == networkId) {
-            pthread_mutex_unlock(&mNetworksLock);
             return *it;
         }
     }
-    pthread_mutex_unlock(&mNetworksLock);
     errno = ENOENT;
     return NULL;
 }
@@ -528,6 +418,12 @@
         return -1;
     }
     free(tmp);
+
+    len = sizeof(reply) -1;
+    if (sendCommand("SAVE_CONFIG", reply, &len)) {
+        LOGE("Error saving config after %s = %s", var, val);
+        return -1;
+    }
     return 0;
 }
 
diff --git a/nexus/Supplicant.h b/nexus/Supplicant.h
index 42f2f79..3efbe4c 100644
--- a/nexus/Supplicant.h
+++ b/nexus/Supplicant.h
@@ -19,38 +19,31 @@
 
 struct wpa_ctrl;
 class SupplicantListener;
-class SupplicantEvent;
 class ServiceManager;
-class PropertyManager;
 class Controller;
 class WifiController;
+class SupplicantStatus;
 
 #include <pthread.h>
 
-#include "ScanResult.h"
 #include "WifiNetwork.h"
-#include "IPropertyProvider.h"
 #include "ISupplicantEventHandler.h"
 
-class Supplicant : public IPropertyProvider, public ISupplicantEventHandler {
+class Supplicant {
 private:
     struct wpa_ctrl      *mCtrl;
     struct wpa_ctrl      *mMonitor;
     SupplicantListener   *mListener;
-    int                  mState;
     ServiceManager       *mServiceManager;
-    PropertyManager      *mPropMngr;
     WifiController       *mController;
     char                 *mInterfaceName;
 
-    ScanResultCollection *mLatestScanResults;
-    pthread_mutex_t      mLatestScanResultsLock;
-
-    WifiNetworkCollection *mNetworks;
-    pthread_mutex_t        mNetworksLock;
+    WifiNetworkCollection   *mNetworks;
+    pthread_mutex_t         mNetworksLock;
+    ISupplicantEventHandler *mHandlers;
  
 public:
-    Supplicant(WifiController *wc, PropertyManager *propmngr);
+    Supplicant(WifiController *wc, ISupplicantEventHandler *handlers);
     virtual ~Supplicant();
 
     int start();
@@ -58,7 +51,6 @@
     bool isStarted();
 
     int triggerScan(bool active);
-    ScanResultCollection *createLatestScanResults();
 
     WifiNetwork *createNetwork();
     WifiNetwork *lookupNetwork(int networkId);
@@ -71,33 +63,18 @@
                               size_t max);
     int enableNetwork(int networkId, bool enabled);
 
-    int getState() { return mState; }
+    SupplicantStatus *getStatus();
+
     Controller *getController() { return (Controller *) mController; }
     const char *getInterfaceName() { return mInterfaceName; }
 
-    int set(const char *name, const char *value);
-    const char *get(const char *name, char *buffer, size_t max);
+    int sendCommand(const char *cmd, char *reply, size_t *reply_len);
 
 private:
     int connectToSupplicant();
-    int sendCommand(const char *cmd, char *reply, size_t *reply_len);
     int setupConfig();
     int retrieveInterfaceName();
-
-    // ISupplicantEventHandler methods
-    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);
+    WifiNetwork *lookupNetwork_UNLOCKED(int networkId);
 };
 
 #endif
diff --git a/nexus/SupplicantAssociatedEvent.cpp b/nexus/SupplicantAssociatedEvent.cpp
new file mode 100644
index 0000000..e40411e
--- /dev/null
+++ b/nexus/SupplicantAssociatedEvent.cpp
@@ -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.
+ */
+
+#include <stdlib.h>
+
+#define LOG_TAG "SupplicantAssociatedEvent"
+#include <cutils/log.h>
+
+#include "SupplicantAssociatedEvent.h"
+
+SupplicantAssociatedEvent::SupplicantAssociatedEvent(int level, char *event,
+                                                     size_t len) :
+                           SupplicantEvent(SupplicantEvent::EVENT_ASSOCIATED,
+                                           level) {
+    char *p = event;
+
+    // "00:13:46:40:40:aa"
+    mBssid = (char *) malloc(18);
+    strncpy(mBssid, p, 17);
+    mBssid[17] = '\0';
+}
+
+SupplicantAssociatedEvent::~SupplicantAssociatedEvent() {
+    if (mBssid)
+        free(mBssid);
+}
+
diff --git a/nexus/SupplicantAssociatedEvent.h b/nexus/SupplicantAssociatedEvent.h
new file mode 100644
index 0000000..aa33c59
--- /dev/null
+++ b/nexus/SupplicantAssociatedEvent.h
@@ -0,0 +1,34 @@
+/*
+ * 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 _SupplicantAssociatedEvent_H
+#define _SupplicantAssociatedEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantAssociatedEvent : public SupplicantEvent {
+    char *mBssid;
+    char *mSsid;
+    int  mFreq;
+
+public:
+    SupplicantAssociatedEvent(int level, char *event, size_t len);
+    virtual ~SupplicantAssociatedEvent();
+
+    const char *getBssid() { return mBssid; }
+};
+
+#endif
diff --git a/nexus/SupplicantAssociatingEvent.cpp b/nexus/SupplicantAssociatingEvent.cpp
new file mode 100644
index 0000000..00a85b6
--- /dev/null
+++ b/nexus/SupplicantAssociatingEvent.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 "SupplicantAssociatingEvent"
+#include <cutils/log.h>
+
+#include "SupplicantAssociatingEvent.h"
+
+SupplicantAssociatingEvent::SupplicantAssociatingEvent(int level, char *event,
+                                                     size_t len) :
+                           SupplicantEvent(SupplicantEvent::EVENT_ASSOCIATING,
+                                           level) {
+    char *p = event;
+
+    mBssid = NULL;
+    mSsid = NULL;
+
+    // SSID 'default' 
+    // OR
+    // "00:13:46:40:40:aa (SSID='default' freq=2437 MHz)"
+
+    if (strncmp(event, "SSID", 4)) {
+        mBssid = (char *) malloc(18);
+        strncpy(mBssid, p, 17);
+        mBssid[17] = '\0';
+        p += 25;
+
+        // "00:13:46:40:40:aa (SSID='default' freq=2437 MHz)"
+        //                           ^
+        //                           p
+        char *q = index(p, '\'');
+        if (!q) {
+            LOGE("Unable to decode SSID (p = {%s})\n", p);
+            return;
+        }
+        mSsid = (char *) malloc((q - p) +1);
+        strncpy(mSsid, p, q-p);
+        mSsid[q-p] = '\0';
+
+        p = q + 7;
+    
+        // "00:13:46:40:40:aa (SSID='default' freq=2437 MHz)"
+        //                                         ^
+        //                                         p
+        if (!(q = index(p, ' '))) {
+            LOGE("Unable to decode frequency\n");
+            return;
+        }
+        *q = '\0';
+        mFreq = atoi(p);
+    } else {
+        p+= 6;
+
+        // SSID 'default' 
+        //       ^
+        //       p
+
+        char *q = index(p, '\'');
+        if (!q) {
+            LOGE("Unable to decode SSID (p = {%s})\n", p);
+            return;
+        }
+        mSsid = (char *) malloc((q - p) +1);
+        strncpy(mSsid, p, q-p);
+        mSsid[q-p] = '\0';
+    }
+}
+
+SupplicantAssociatingEvent::SupplicantAssociatingEvent(const char *bssid, 
+                                                     const char *ssid,
+                                                     int freq) :
+                           SupplicantEvent(SupplicantEvent::EVENT_ASSOCIATING, -1) {
+    mBssid = strdup(bssid);
+    mSsid= strdup(ssid);
+    mFreq = freq;
+}
+
+SupplicantAssociatingEvent::~SupplicantAssociatingEvent() {
+    if (mBssid)
+        free(mBssid);
+    if (mSsid)
+        free(mSsid);
+}
+
diff --git a/nexus/SupplicantAssociatingEvent.h b/nexus/SupplicantAssociatingEvent.h
new file mode 100644
index 0000000..d3a4d5c
--- /dev/null
+++ b/nexus/SupplicantAssociatingEvent.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 _SupplicantAssociatingEvent_H
+#define _SupplicantAssociatingEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantAssociatingEvent : public SupplicantEvent {
+    char *mBssid;
+    char *mSsid;
+    int  mFreq;
+
+public:
+    SupplicantAssociatingEvent(int level, char *event, size_t len);
+    SupplicantAssociatingEvent(const char *bssid, const char *ssid, int freq);
+    virtual ~SupplicantAssociatingEvent();
+
+    const char *getBssid() { return mBssid; }
+    const char *getSsid() { return mSsid; }
+    int getFreq() { return mFreq;}
+};
+
+#endif
diff --git a/nexus/SupplicantConnectedEvent.cpp b/nexus/SupplicantConnectedEvent.cpp
new file mode 100644
index 0000000..e58bab2
--- /dev/null
+++ b/nexus/SupplicantConnectedEvent.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SupplicantConnectedEvent"
+#include <cutils/log.h>
+
+#include "SupplicantConnectedEvent.h"
+
+SupplicantConnectedEvent::SupplicantConnectedEvent(int level, char *event,
+                                                   size_t len) :
+                          SupplicantEvent(SupplicantEvent::EVENT_CONNECTED,
+                                          level) {
+    char *p;
+
+    //  "- Connection to 00:13:46:40:40:aa completed (auth) [id=1 id_str=], 89"
+    
+    if ((p = index(event + 2, ' ')) && (++p = index(p, ' '))) {
+        mBssid = (char *) malloc(18);
+        strncpy(mBssid, ++p, 17);
+        mBssid[17] = '\0';
+
+        //  "- Connection to 00:13:46:40:40:aa completed (auth) [id=1 id_str=], 89"
+        //                   ^
+        //                   p
+        
+        if ((p = index(p, ' ')) && ((++p = index(p, ' ')))) {
+            if (!strncmp(++p, "(auth)", 6))
+                mReassociated = false;
+            else
+                mReassociated = true;
+        } else
+            LOGE("Unable to decode re-assocation");
+    } else
+        LOGE("Unable to decode event");
+}
+
+SupplicantConnectedEvent::SupplicantConnectedEvent(const char *bssid, 
+                                                   bool reassocated) :
+                          SupplicantEvent(SupplicantEvent::EVENT_CONNECTED, -1) {
+    mBssid = strdup(bssid);
+    mReassociated = reassocated;
+}
+
+SupplicantConnectedEvent::~SupplicantConnectedEvent() {
+    if (mBssid)
+        free(mBssid);
+}
+
diff --git a/nexus/SupplicantConnectedEvent.h b/nexus/SupplicantConnectedEvent.h
new file mode 100644
index 0000000..03e9842
--- /dev/null
+++ b/nexus/SupplicantConnectedEvent.h
@@ -0,0 +1,36 @@
+/*
+ * 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 _SupplicantConnectedEvent_H
+#define _SupplicantConnectedEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantConnectedEvent : public SupplicantEvent {
+private:
+    char *mBssid;
+    bool mReassociated;
+
+public:
+    SupplicantConnectedEvent(int level, char *event, size_t len);
+    SupplicantConnectedEvent(const char *bssid, bool reassicated);
+    virtual ~SupplicantConnectedEvent();
+
+    const char *getBssid() { return mBssid; }
+    bool getReassociated() { return mReassociated; }
+};
+
+#endif
diff --git a/nexus/SupplicantConnectionTimeoutEvent.cpp b/nexus/SupplicantConnectionTimeoutEvent.cpp
new file mode 100644
index 0000000..8b8a7d7
--- /dev/null
+++ b/nexus/SupplicantConnectionTimeoutEvent.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SupplicantConnectionTimeoutEvent"
+#include <cutils/log.h>
+
+#include "SupplicantConnectionTimeoutEvent.h"
+
+SupplicantConnectionTimeoutEvent::SupplicantConnectionTimeoutEvent(int level, char *event,
+                                                   size_t len) :
+                          SupplicantEvent(SupplicantEvent::EVENT_CONNECTIONTIMEOUT,
+                                          level) {
+    // 00:13:46:40:40:aa timed out.'
+    mBssid = (char *) malloc(18);
+    strncpy(mBssid, event, 17);
+    mBssid[17] = '\0';
+}
+
+SupplicantConnectionTimeoutEvent::~SupplicantConnectionTimeoutEvent() {
+    if (mBssid)
+        free(mBssid);
+}
+
diff --git a/nexus/SupplicantConnectionTimeoutEvent.h b/nexus/SupplicantConnectionTimeoutEvent.h
new file mode 100644
index 0000000..0d2606d
--- /dev/null
+++ b/nexus/SupplicantConnectionTimeoutEvent.h
@@ -0,0 +1,34 @@
+/*
+ * 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 _SupplicantConnectionTimeoutEvent_H
+#define _SupplicantConnectionTimeoutEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantConnectionTimeoutEvent : public SupplicantEvent {
+private:
+    char *mBssid;
+    bool mReassociated;
+
+public:
+    SupplicantConnectionTimeoutEvent(int level, char *event, size_t len);
+    virtual ~SupplicantConnectionTimeoutEvent();
+
+    const char *getBssid() { return mBssid; }
+};
+
+#endif
diff --git a/nexus/SupplicantDisconnectedEvent.cpp b/nexus/SupplicantDisconnectedEvent.cpp
new file mode 100644
index 0000000..11e499d
--- /dev/null
+++ b/nexus/SupplicantDisconnectedEvent.cpp
@@ -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.
+ */
+
+#define LOG_TAG "SupplicantDisconnectedEvent"
+#include <cutils/log.h>
+
+#include "SupplicantDisconnectedEvent.h"
+
+SupplicantDisconnectedEvent::SupplicantDisconnectedEvent(int level, char *event,
+                                                       size_t len) :
+                            SupplicantEvent(SupplicantEvent::EVENT_DISCONNECTED,
+                                            level) {
+}
+
+SupplicantDisconnectedEvent::SupplicantDisconnectedEvent() :
+                            SupplicantEvent(SupplicantEvent::EVENT_DISCONNECTED, -1) {
+}
+
+SupplicantDisconnectedEvent::~SupplicantDisconnectedEvent() {
+}
diff --git a/nexus/SupplicantDisconnectedEvent.h b/nexus/SupplicantDisconnectedEvent.h
new file mode 100644
index 0000000..c09ec62
--- /dev/null
+++ b/nexus/SupplicantDisconnectedEvent.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 _SupplicantDisconnectedEvent_H
+#define _SupplicantDisconnectedEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantDisconnectedEvent : public SupplicantEvent {
+
+public:
+    SupplicantDisconnectedEvent(int level, char *event, size_t len);
+    SupplicantDisconnectedEvent();
+    virtual ~SupplicantDisconnectedEvent();
+};
+
+#endif
diff --git a/nexus/SupplicantEvent.cpp b/nexus/SupplicantEvent.cpp
index 2e6d665..faf7b45 100644
--- a/nexus/SupplicantEvent.cpp
+++ b/nexus/SupplicantEvent.cpp
@@ -23,74 +23,7 @@
 
 #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);
+SupplicantEvent::SupplicantEvent(int type, int level) {
+    mType = type;
+    mLevel = level;
 }
diff --git a/nexus/SupplicantEvent.h b/nexus/SupplicantEvent.h
index 2dc6722..9d7cbd9 100644
--- a/nexus/SupplicantEvent.h
+++ b/nexus/SupplicantEvent.h
@@ -22,33 +22,32 @@
 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;
+    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;
+    static const int EVENT_ASSOCIATING       = 14;
+    static const int EVENT_ASSOCIATED        = 15;
+    static const int EVENT_CONNECTIONTIMEOUT = 16;
 
 public:
-    SupplicantEvent(char *event, size_t len);
-    virtual ~SupplicantEvent();
+    SupplicantEvent(int type, int level);
+    virtual ~SupplicantEvent() {}
 
     int getType() { return mType; }
-    const char *getEvent() { return mEvent; }
-    int getLen() { return mLen; }
     int getLevel() { return mLevel; }
 };
 
diff --git a/nexus/SupplicantEventFactory.cpp b/nexus/SupplicantEventFactory.cpp
new file mode 100644
index 0000000..8695aca
--- /dev/null
+++ b/nexus/SupplicantEventFactory.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "SupplicantEventFactory"
+#include <cutils/log.h>
+
+#include "SupplicantEvent.h"
+#include "SupplicantEventFactory.h"
+#include "SupplicantAssociatingEvent.h"
+#include "SupplicantAssociatedEvent.h"
+#include "SupplicantConnectedEvent.h"
+#include "SupplicantStateChangeEvent.h"
+#include "SupplicantScanResultsEvent.h"
+#include "SupplicantConnectionTimeoutEvent.h"
+#include "SupplicantDisconnectedEvent.h"
+#if 0
+#include "SupplicantTerminatingEvent.h"
+#include "SupplicantPasswordChangedEvent.h"
+#include "SupplicantEapNotificationEvent.h"
+#include "SupplicantEapStartedEvent.h"
+#include "SupplicantEapMethodEvent.h"
+#include "SupplicantEapSuccessEvent.h"
+#include "SupplicantEapFailureEvent.h"
+#include "SupplicantLinkSpeedEvent.h"
+#include "SupplicantDriverStateEvent.h"
+#endif
+
+#include "libwpa_client/wpa_ctrl.h"
+
+SupplicantEventFactory::SupplicantEventFactory() {
+}
+
+SupplicantEvent *SupplicantEventFactory::createEvent(char *event, size_t len) {
+    int level = 0;
+
+    if (event[0] == '<') {
+        char *match = strchr(event, '>');
+        if (match) {
+            char tmp[16];
+
+            strncpy(tmp, &event[1], (match - event));
+            level = 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, "Authentication with ", 20)) {
+        if (!strcmp(event + strlen(event) - strlen(" timed out."),
+                    " timed out.")) {
+            return new SupplicantConnectionTimeoutEvent(level,
+                                                        event + 20,
+                                                        len);
+        } else
+            return NULL;
+        
+    } else if (!strncmp(event, "Associated with ", 16))
+        return new SupplicantAssociatedEvent(level, event + 16, len);
+    else if (!strncmp(event, "Trying to associate with ", 25))
+        return new SupplicantAssociatingEvent(level, event + 25, len);
+    else if (!strncmp(event, WPA_EVENT_CONNECTED, strlen(WPA_EVENT_CONNECTED))) {
+        return new SupplicantConnectedEvent(level,
+                                            event + strlen(WPA_EVENT_CONNECTED),
+                                            len);
+    } else if (!strncmp(event, WPA_EVENT_SCAN_RESULTS, strlen(WPA_EVENT_SCAN_RESULTS))) {
+        return new SupplicantScanResultsEvent(level,
+                                              event + strlen(WPA_EVENT_SCAN_RESULTS),
+                                              len);
+    } else if (!strncmp(event, WPA_EVENT_STATE_CHANGE, strlen(WPA_EVENT_STATE_CHANGE))) {
+        return new SupplicantStateChangeEvent(level,
+                                              event + strlen(WPA_EVENT_STATE_CHANGE),
+                                              len);
+    }
+    else if (!strncmp(event, WPA_EVENT_DISCONNECTED, strlen(WPA_EVENT_DISCONNECTED)))
+        return new SupplicantDisconnectedEvent(level, event, len);
+#if 0
+    else if (!strncmp(event, WPA_EVENT_TERMINATING, strlen(WPA_EVENT_TERMINATING)))
+        return new SupplicantTerminatingEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_PASSWORD_CHANGED, strlen(WPA_EVENT_PASSWORD_CHANGED)))
+        return new SupplicantPasswordChangedEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_NOTIFICATION, strlen(WPA_EVENT_EAP_NOTIFICATION)))
+        return new SupplicantEapNotificationEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_STARTED, strlen(WPA_EVENT_EAP_STARTED)))
+        return new SupplicantEapStartedEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_METHOD, strlen(WPA_EVENT_EAP_METHOD)))
+        return new SupplicantEapMethodEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_SUCCESS, strlen(WPA_EVENT_EAP_SUCCESS)))
+        return new SupplicantEapSuccessEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_EAP_FAILURE, strlen(WPA_EVENT_EAP_FAILURE)))
+        return new SupplicantEapFailureEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_LINK_SPEED, strlen(WPA_EVENT_LINK_SPEED)))
+        return new SupplicantLinkSpeedEvent(event, len);
+    else if (!strncmp(event, WPA_EVENT_DRIVER_STATE, strlen(WPA_EVENT_DRIVER_STATE)))
+         return new SupplicantDriverStateEvent(event, len);
+#endif
+    return NULL;
+}
diff --git a/nexus/SupplicantEventFactory.h b/nexus/SupplicantEventFactory.h
new file mode 100644
index 0000000..22e5707
--- /dev/null
+++ b/nexus/SupplicantEventFactory.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 _SupplicantEventFactory_H
+#define _SupplicantEventFactory_H
+
+class SupplicantEvent;
+
+class SupplicantEventFactory {
+public:
+    SupplicantEventFactory();
+    virtual ~SupplicantEventFactory() {}
+
+    SupplicantEvent *createEvent(char *event, size_t len);
+};
+
+#endif
diff --git a/nexus/SupplicantListener.cpp b/nexus/SupplicantListener.cpp
index b94648b..b91fc02 100644
--- a/nexus/SupplicantListener.cpp
+++ b/nexus/SupplicantListener.cpp
@@ -24,14 +24,21 @@
 #include "libwpa_client/wpa_ctrl.h"
 
 #include "SupplicantListener.h"
-#include "SupplicantEvent.h"
 #include "ISupplicantEventHandler.h"
+#include "SupplicantEventFactory.h"
+#include "SupplicantEvent.h"
+#include "SupplicantAssociatingEvent.h"
+#include "SupplicantAssociatedEvent.h"
+#include "SupplicantConnectedEvent.h"
+#include "SupplicantScanResultsEvent.h"
+#include "SupplicantStateChangeEvent.h"
 
 SupplicantListener::SupplicantListener(ISupplicantEventHandler *handlers, 
                                        struct wpa_ctrl *monitor) :
                     SocketListener(wpa_ctrl_get_fd(monitor), false) {
     mHandlers = handlers;
     mMonitor = monitor;
+    mFactory = new SupplicantEventFactory();
 }
 
 bool SupplicantListener::onDataAvailable(SocketClient *cli) {
@@ -53,44 +60,50 @@
         return false;
     }
 
-    SupplicantEvent *evt = new SupplicantEvent(buf, nread);
+    SupplicantEvent *evt = mFactory->createEvent(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 = mHandlers->onConnectedEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_DISCONNECTED)
-        rc = mHandlers->onDisconnectedEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_TERMINATING)
-        rc = mHandlers->onTerminatingEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_PASSWORD_CHANGED)
-        rc = mHandlers->onPasswordChangedEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_EAP_NOTIFICATION)
-        rc = mHandlers->onEapNotificationEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_EAP_STARTED)
-        rc = mHandlers->onEapStartedEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_EAP_SUCCESS)
-        rc = mHandlers->onEapSuccessEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_EAP_FAILURE)
-        rc = mHandlers->onEapFailureEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_SCAN_RESULTS)
-        rc = mHandlers->onScanResultsEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_STATE_CHANGE)
-        rc = mHandlers->onStateChangeEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_LINK_SPEED)
-        rc = mHandlers->onLinkSpeedEvent(evt);
-    else if (evt->getType() == SupplicantEvent::EVENT_DRIVER_STATE)
-        rc = mHandlers->onDriverStateEvent(evt);
-    else {
-        LOGW("Ignoring unknown event");
+    if (!evt) {
+        LOGW("Dropping unknown supplicant event '%s'", buf);
+        return true;
     }
 
+    // Call the appropriate handler
+    if (evt->getType() == SupplicantEvent::EVENT_ASSOCIATING)
+        mHandlers->onAssociatingEvent((SupplicantAssociatingEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_ASSOCIATED)
+        mHandlers->onAssociatedEvent((SupplicantAssociatedEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_CONNECTED)
+        mHandlers->onConnectedEvent((SupplicantConnectedEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_SCAN_RESULTS)
+        mHandlers->onScanResultsEvent((SupplicantScanResultsEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_STATE_CHANGE)
+        mHandlers->onStateChangeEvent((SupplicantStateChangeEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_CONNECTIONTIMEOUT)
+        mHandlers->onConnectionTimeoutEvent((SupplicantConnectionTimeoutEvent *) evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_DISCONNECTED)
+        mHandlers->onDisconnectedEvent((SupplicantDisconnectedEvent *) evt);
+    else
+        LOGW("Whoops - no handler available for event '%s'\n", buf);
+#if 0
+    else if (evt->getType() == SupplicantEvent::EVENT_TERMINATING)
+        mHandlers->onTerminatingEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_PASSWORD_CHANGED)
+        mHandlers->onPasswordChangedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_NOTIFICATION)
+        mHandlers->onEapNotificationEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_STARTED)
+        mHandlers->onEapStartedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_SUCCESS)
+        mHandlers->onEapSuccessEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_EAP_FAILURE)
+        mHandlers->onEapFailureEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_LINK_SPEED)
+        mHandlers->onLinkSpeedEvent(evt);
+    else if (evt->getType() == SupplicantEvent::EVENT_DRIVER_STATE)
+        mHandlers->onDriverStateEvent(evt);
+#endif
+
     delete evt;
 
-    if (rc) {
-        LOGW("Handler %d (%s) error: %s", evt->getType(), evt->getEvent(), strerror(errno));
-        return false;
-    }
     return true;
 }
diff --git a/nexus/SupplicantListener.h b/nexus/SupplicantListener.h
index 3d186ad..a1da773 100644
--- a/nexus/SupplicantListener.h
+++ b/nexus/SupplicantListener.h
@@ -23,12 +23,13 @@
 class Supplicant;
 class SocketClient;
 class ISupplicantEventHandler;
+class SupplicantEventFactory;
 
 class SupplicantListener: public SocketListener {
-private:
     struct wpa_ctrl         *mMonitor;
     ISupplicantEventHandler *mHandlers;
-
+    SupplicantEventFactory  *mFactory;
+    
 public:
     SupplicantListener(ISupplicantEventHandler *handlers,
                        struct wpa_ctrl *monitor);
diff --git a/nexus/SupplicantScanResultsEvent.cpp b/nexus/SupplicantScanResultsEvent.cpp
new file mode 100644
index 0000000..c53adad
--- /dev/null
+++ b/nexus/SupplicantScanResultsEvent.cpp
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "SupplicantScanResultsEvent"
+#include <cutils/log.h>
+
+#include "SupplicantScanResultsEvent.h"
+
+SupplicantScanResultsEvent::SupplicantScanResultsEvent(int level, char *event,
+                                                       size_t len) :
+                            SupplicantEvent(SupplicantEvent::EVENT_SCAN_RESULTS,
+                                            level) {
+}
+
+SupplicantScanResultsEvent::SupplicantScanResultsEvent() :
+                            SupplicantEvent(SupplicantEvent::EVENT_SCAN_RESULTS, -1) {
+}
+
+SupplicantScanResultsEvent::~SupplicantScanResultsEvent() {
+}
+
diff --git a/nexus/SupplicantScanResultsEvent.h b/nexus/SupplicantScanResultsEvent.h
new file mode 100644
index 0000000..5f82041
--- /dev/null
+++ b/nexus/SupplicantScanResultsEvent.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 _SupplicantScanResultsEvent_H
+#define _SupplicantScanResultsEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantScanResultsEvent : public SupplicantEvent {
+
+public:
+    SupplicantScanResultsEvent(int level, char *event, size_t len);
+    SupplicantScanResultsEvent();
+    virtual ~SupplicantScanResultsEvent();
+};
+
+#endif
diff --git a/nexus/SupplicantStateChangeEvent.cpp b/nexus/SupplicantStateChangeEvent.cpp
new file mode 100644
index 0000000..cf0b9da
--- /dev/null
+++ b/nexus/SupplicantStateChangeEvent.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "SupplicantStateChangeEvent"
+#include <cutils/log.h>
+
+#include "SupplicantStateChangeEvent.h"
+
+SupplicantStateChangeEvent::SupplicantStateChangeEvent(int level, char *event,
+                                                       size_t len) :
+                            SupplicantEvent(SupplicantEvent::EVENT_STATE_CHANGE,
+                                            level) {
+    // XXX: move this stuff into a static creation method
+    char *p = index(event, ' ');
+    if (!p) {
+        LOGW("Bad event '%s'\n", event);
+        return;
+    }
+
+    mState = atoi(p + strlen("state=") + 1);
+}
+
+SupplicantStateChangeEvent::SupplicantStateChangeEvent(int state) :
+                            SupplicantEvent(SupplicantEvent::EVENT_STATE_CHANGE, -1) {
+    mState = state;
+}
+
+SupplicantStateChangeEvent::~SupplicantStateChangeEvent() {
+}
+
diff --git a/nexus/SupplicantStateChangeEvent.h b/nexus/SupplicantStateChangeEvent.h
new file mode 100644
index 0000000..77bff65
--- /dev/null
+++ b/nexus/SupplicantStateChangeEvent.h
@@ -0,0 +1,34 @@
+/*
+ * 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 _SupplicantStateChangeEvent_H
+#define _SupplicantStateChangeEvent_H
+
+#include "SupplicantEvent.h"
+
+class SupplicantStateChangeEvent : public SupplicantEvent {
+private:
+    int mState;
+
+public:
+    SupplicantStateChangeEvent(int level, char *event, size_t len);
+    SupplicantStateChangeEvent(int state);
+    virtual ~SupplicantStateChangeEvent();
+
+    int getState() { return mState; }
+};
+
+#endif
diff --git a/nexus/SupplicantStatus.cpp b/nexus/SupplicantStatus.cpp
new file mode 100644
index 0000000..87f6c98
--- /dev/null
+++ b/nexus/SupplicantStatus.cpp
@@ -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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define LOG_TAG "SupplicantState"
+#include <cutils/log.h>
+
+#include "SupplicantStatus.h"
+#include "SupplicantState.h"
+
+SupplicantStatus::SupplicantStatus() {
+    mWpaState = SupplicantState::UNKNOWN;
+    mId = -1;
+    mBssid = NULL;
+    mSsid = NULL;
+}
+
+SupplicantStatus::SupplicantStatus(int state, int id, char *bssid, char *ssid) :
+                  mWpaState(state), mId(id), mBssid(bssid), mSsid(ssid) {
+
+LOGD("state %d, id %d, bssid %p, ssid %p\n", mWpaState, mId, mBssid, mSsid);
+}
+
+SupplicantStatus::~SupplicantStatus() {
+    if (mBssid)
+        free(mBssid);
+    if (mSsid)
+        free(mSsid);
+}
+
+SupplicantStatus *SupplicantStatus::createStatus(char *data, int len) {
+    char *bssid = NULL;
+    char *ssid = NULL;
+    int id = -1;
+    int state = SupplicantState::UNKNOWN;
+
+    char *next = data;
+    char *line;
+    while((line = strsep(&next, "\n"))) {
+        char *token = strsep(&next, "=");
+        char *value = strsep(&next, "=");
+
+        if (!strcmp(token, "bssid"))
+            bssid = strdup(value);
+        else if (!strcmp(token, "ssid"))
+            ssid = strdup(value);
+        else if (!strcmp(token, "id"))
+            id = atoi(value);
+        else if (!strcmp(token, "wpa_state"))
+            state = atoi(value);
+        else
+            LOGD("Ignoring unsupported status token '%s'", token);
+    }
+
+    return new SupplicantStatus(state, id, bssid, ssid);
+    
+}
diff --git a/nexus/SupplicantStatus.h b/nexus/SupplicantStatus.h
new file mode 100644
index 0000000..ef01841
--- /dev/null
+++ b/nexus/SupplicantStatus.h
@@ -0,0 +1,42 @@
+/*
+ * 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 _SupplicantStatus_H
+#define _SupplicantStatus_H
+
+class SupplicantStatus {
+private:
+    int mWpaState;
+    int mId;
+    char *mBssid;
+    char *mSsid;
+
+private:
+    SupplicantStatus();
+    SupplicantStatus(int state, int id, char *bssid, char *ssid);
+
+public:
+    virtual ~SupplicantStatus();
+    static SupplicantStatus *createStatus(char *data, int len);
+
+    int getWpaState() { return mWpaState; }
+    int getId() { return mId; }
+    const char *getBssid() { return mBssid; }
+    const char *getSsid() { return mSsid; }
+   
+};
+
+#endif
diff --git a/nexus/TiwlanWifiController.cpp b/nexus/TiwlanWifiController.cpp
index 307c48c..6945e3e 100644
--- a/nexus/TiwlanWifiController.cpp
+++ b/nexus/TiwlanWifiController.cpp
@@ -30,8 +30,12 @@
 
 extern "C" int sched_yield(void);
 
-TiwlanWifiController::TiwlanWifiController(PropertyManager *propmngr, char *modpath, char *modname, char *modargs) :
-                      WifiController(propmngr, modpath, modname, modargs) {
+TiwlanWifiController::TiwlanWifiController(PropertyManager *propmngr,
+                                           IControllerHandler *handlers,
+                                           char *modpath, char *modname,
+                                           char *modargs) :
+                      WifiController(propmngr, handlers, modpath, modname,
+                                     modargs) {
 }
 
 int TiwlanWifiController::powerUp() {
diff --git a/nexus/TiwlanWifiController.h b/nexus/TiwlanWifiController.h
index d3ebe88..852a288 100644
--- a/nexus/TiwlanWifiController.h
+++ b/nexus/TiwlanWifiController.h
@@ -20,9 +20,11 @@
 #include "PropertyManager.h"
 #include "WifiController.h"
 
+class IControllerHandler;
+
 class TiwlanWifiController : public WifiController {
 public:
-    TiwlanWifiController(PropertyManager *propmngr, char *modpath, char *modname, char *modargs);
+    TiwlanWifiController(PropertyManager *propmngr, IControllerHandler *handlers, char *modpath, char *modname, char *modargs);
     virtual ~TiwlanWifiController() {}
 
     virtual int powerUp();
diff --git a/nexus/VpnController.cpp b/nexus/VpnController.cpp
index 1246703..add4dc3 100644
--- a/nexus/VpnController.cpp
+++ b/nexus/VpnController.cpp
@@ -25,18 +25,19 @@
 #include "PropertyManager.h"
 #include "VpnController.h"
 
-VpnController::VpnController(PropertyManager *propmngr) :
-               Controller("VPN", propmngr) {
+VpnController::VpnController(PropertyManager *propmngr,
+                             IControllerHandler *handlers) :
+               Controller("VPN", propmngr, handlers) {
     mEnabled = false;
-    propmngr->registerProperty("vpn.enabled", this);
-    propmngr->registerProperty("vpn.gateway", this);
 }
 
 int VpnController::start() {
+    mPropMngr->registerProperty("vpn.enabled", this);
     return 0;
 }
 
 int VpnController::stop() {
+    mPropMngr->unregisterProperty("vpn.enabled");
     return 0;
 }
 
@@ -49,8 +50,13 @@
             return 0;
         rc = (en ? enable() : disable());
 
-        if (!rc)
+        if (!rc) {
             mEnabled = en;
+            if (en) 
+                mPropMngr->unregisterProperty("vpn.gateway");
+            else
+                mPropMngr->unregisterProperty("vpn.gateway");
+        }
         return rc;
     } if (!strcmp(name, "vpn.gateway")) {
         if (!inet_aton(value, &mVpnGateway)) {
diff --git a/nexus/VpnController.h b/nexus/VpnController.h
index b36856f..1af4d9f 100644
--- a/nexus/VpnController.h
+++ b/nexus/VpnController.h
@@ -21,6 +21,8 @@
 
 #include "Controller.h"
 
+class IControllerHandler;
+
 class VpnController : public Controller {
     bool           mEnabled;
     /*
@@ -29,7 +31,7 @@
     struct in_addr mVpnGateway;
 
 public:
-    VpnController(PropertyManager *propmngr);
+    VpnController(PropertyManager *propmngr, IControllerHandler *handlers);
     virtual ~VpnController() {}
 
     virtual int start();
diff --git a/nexus/WifiController.cpp b/nexus/WifiController.cpp
index 3d06806..5c5db1a 100644
--- a/nexus/WifiController.cpp
+++ b/nexus/WifiController.cpp
@@ -27,34 +27,50 @@
 #include "NetworkManager.h"
 #include "ErrorCode.h"
 #include "WifiNetwork.h"
+#include "ISupplicantEventHandler.h"
+#include "SupplicantState.h"
+#include "SupplicantStatus.h"
+#include "SupplicantAssociatingEvent.h"
+#include "SupplicantAssociatedEvent.h"
+#include "SupplicantConnectedEvent.h"
+#include "SupplicantScanResultsEvent.h"
+#include "SupplicantStateChangeEvent.h"
+#include "SupplicantConnectionTimeoutEvent.h"
+#include "SupplicantDisconnectedEvent.h"
 
-WifiController::WifiController(PropertyManager *propmngr, char *modpath, char *modname, char *modargs) :
-                Controller("WIFI", propmngr) {
+WifiController::WifiController(PropertyManager *mPropMngr,
+                               IControllerHandler *handlers,
+                               char *modpath, char *modname, char *modargs) :
+                Controller("WIFI", mPropMngr, handlers) {
     strncpy(mModulePath, modpath, sizeof(mModulePath));
     strncpy(mModuleName, modname, sizeof(mModuleName));
     strncpy(mModuleArgs, modargs, sizeof(mModuleArgs));
 
-    mSupplicant = new Supplicant(this, propmngr);
+    mLatestScanResults = new ScanResultCollection();
+    pthread_mutex_init(&mLatestScanResultsLock, NULL);
+
+    mSupplicant = new Supplicant(this, this);
     mScanner = new WifiScanner(mSupplicant, 10);
     mCurrentScanMode = 0;
 
     mEnabled = false;
 
-    propmngr->registerProperty("wifi.enabled", this);
+    mSupplicantState = SupplicantState::UNKNOWN;
 }
 
 int WifiController::start() {
+    mPropMngr->registerProperty("wifi.enabled", this);
     return 0;
 }
 
 int WifiController::stop() {
-    errno = ENOSYS;
-    return -1;
+    mPropMngr->unregisterProperty("wifi.enabled");
+    return 0;
 }
 
 int WifiController::enable() {
     if (!isPoweredUp()) {
-        sendStatusBroadcast("POWERING_UP");
+        sendStatusBroadcast("Powering up WiFi hardware");
         if (powerUp()) {
             LOGE("Powerup failed (%s)", strerror(errno));
             return -1;
@@ -62,7 +78,7 @@
     }
 
     if (mModuleName[0] != '\0' && !isKernelModuleLoaded(mModuleName)) {
-        sendStatusBroadcast("LOADING_DRIVER");
+        sendStatusBroadcast("Loading WiFi driver");
         if (loadKernelModule(mModulePath, mModuleArgs)) {
             LOGE("Kernel module load failed (%s)", strerror(errno));
             goto out_powerdown;
@@ -70,7 +86,7 @@
     }
 
     if (!isFirmwareLoaded()) {
-        sendStatusBroadcast("LOADING_FIRMWARE");
+        sendStatusBroadcast("Loading WiFI firmware");
         if (loadFirmware()) {
             LOGE("Firmware load failed (%s)", strerror(errno));
             goto out_powerdown;
@@ -78,7 +94,7 @@
     }
 
     if (!mSupplicant->isStarted()) {
-        sendStatusBroadcast("STARTING_SUPPLICANT");
+        sendStatusBroadcast("Starting WPA Supplicant");
         if (mSupplicant->start()) {
             LOGE("Supplicant start failed (%s)", strerror(errno));
             goto out_unloadmodule;
@@ -93,6 +109,7 @@
     if (mSupplicant->refreshNetworkList())
         LOGW("Error getting list of networks (%s)", strerror(errno));
 
+    mPropMngr->registerProperty("wifi.supplicant.state", this);
     mPropMngr->registerProperty("wifi.scanmode", this);
     mPropMngr->registerProperty("wifi.interface", this);
 
@@ -121,8 +138,11 @@
 int WifiController::disable() {
 
     mPropMngr->unregisterProperty("wifi.scanmode");
+    mPropMngr->unregisterProperty("wifi.supplicant.state");
+    mPropMngr->unregisterProperty("wifi.scanmode");
+
     if (mSupplicant->isStarted()) {
-        sendStatusBroadcast("STOPPING_SUPPLICANT");
+        sendStatusBroadcast("Stopping WPA Supplicant");
         if (mSupplicant->stop()) {
             LOGE("Supplicant stop failed (%s)", strerror(errno));
             return -1;
@@ -131,7 +151,7 @@
         LOGW("disable(): Supplicant not running?");
 
     if (mModuleName[0] != '\0' && isKernelModuleLoaded(mModuleName)) {
-        sendStatusBroadcast("UNLOADING_DRIVER");
+        sendStatusBroadcast("Unloading WiFi driver");
         if (unloadKernelModule(mModuleName)) {
             LOGE("Unable to unload module (%s)", strerror(errno));
             return -1;
@@ -139,7 +159,7 @@
     }
 
     if (isPoweredUp()) {
-        sendStatusBroadcast("POWERING_DOWN");
+        sendStatusBroadcast("Powering down WiFi hardware");
         if (powerDown()) {
             LOGE("Powerdown failed (%s)", strerror(errno));
             return -1;
@@ -184,7 +204,15 @@
 }
 
 ScanResultCollection *WifiController::createScanResults() {
-    return mSupplicant->createLatestScanResults();
+    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;
 }
 
 WifiNetworkCollection *WifiController::createNetworkList() {
@@ -207,7 +235,10 @@
         return -1;
     } else if (!strcmp(name, "wifi.scanmode"))
         return setScanMode((uint32_t) strtoul(value, NULL, 0));
-    else
+    else if (!strcmp(name, "wifi.supplicant.state")) {
+        errno = EROFS;
+        return -1;
+    } else
         return Controller::set(name, value);
     return rc;
 }
@@ -221,9 +252,158 @@
                  (getBoundInterface() ? getBoundInterface() : "none"));
     } else if (!strcmp(name, "wifi.scanmode"))
         snprintf(buffer, maxsize, "0x%.8x", mCurrentScanMode);
+    else if (!strcmp(name, "wifi.supplicant.state"))
+        return SupplicantState::toString(mSupplicantState, buffer, maxsize);
     else
         return Controller::get(name, buffer, maxsize);
 
     return buffer;
 }
 
+void WifiController::onAssociatingEvent(SupplicantAssociatingEvent *evt) {
+    LOGD("onAssociatingEvent(%s, %s, %d)",
+         (evt->getBssid() ? evt->getBssid() : "n/a"),
+         (evt->getSsid() ? evt->getSsid() : "n/a"),
+         evt->getFreq());
+}
+
+void WifiController::onAssociatedEvent(SupplicantAssociatedEvent *evt) {
+    LOGD("onAssociatedEvent(%s)", evt->getBssid());
+}
+
+void WifiController::onConnectedEvent(SupplicantConnectedEvent *evt) {
+    LOGD("onConnectedEvent(%s, %d)", evt->getBssid(), evt->getReassociated());
+    if (!evt->getReassociated()) {
+        SupplicantStatus *ss = mSupplicant->getStatus();
+        WifiNetwork *wn;
+
+        if (ss->getWpaState() != SupplicantState::COMPLETED) {
+            char tmp[32];
+
+            LOGW("onConnected() with SupplicantState = %s!",
+                 SupplicantState::toString(ss->getWpaState(), tmp,
+                 sizeof(tmp)));
+            return;
+        }
+
+        if (ss->getId() == -1) {
+            LOGW("onConnected() with id = -1!");
+            return;
+        }
+        
+        if (!(wn = mSupplicant->lookupNetwork(ss->getId()))) {
+            LOGW("Error looking up connected network id %d (%s)",
+                 ss->getId(), strerror(errno));
+            return;
+        }
+      
+        delete ss;
+        mHandlers->onInterfaceStarted(this, wn->getIfaceCfg());
+    }
+}
+
+void WifiController::onScanResultsEvent(SupplicantScanResultsEvent *evt) {
+    char *reply;
+
+    if (!(reply = (char *) malloc(4096))) {
+        LOGE("Out of memory");
+        return;
+    }
+
+    size_t len = 4096;
+
+    if (mSupplicant->sendCommand("SCAN_RESULTS", reply, &len)) {
+        LOGW("onScanResultsEvent: Error getting scan results (%s)",
+             strerror(errno));
+        free(reply);
+        return;
+    }
+
+    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);
+        pthread_mutex_unlock(&mLatestScanResultsLock);
+        return;
+    }
+
+    while((linep = strtok_r(NULL, "\n", &linep_next)))
+        mLatestScanResults->push_back(new ScanResult(linep));
+
+    char *tmp;
+    asprintf(&tmp, "Scan results ready (%d)", mLatestScanResults->size());
+    NetworkManager::Instance()->getBroadcaster()->
+                                sendBroadcast(ErrorCode::UnsolicitedInformational, tmp, false);
+    free(tmp);
+    pthread_mutex_unlock(&mLatestScanResultsLock);
+    free(reply);
+}
+
+void WifiController::onStateChangeEvent(SupplicantStateChangeEvent *evt) {
+    char tmp[32];
+    char tmp2[32];
+    
+    LOGD("onStateChangeEvent(%s -> %s)", 
+         SupplicantState::toString(mSupplicantState, tmp, sizeof(tmp)),
+         SupplicantState::toString(evt->getState(), tmp2, sizeof(tmp2)));
+
+    mSupplicantState = evt->getState();
+}
+
+void WifiController::onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt) {
+    LOGD("onConnectionTimeoutEvent(%s)", evt->getBssid());
+}
+
+void WifiController::onDisconnectedEvent(SupplicantDisconnectedEvent *evt) {
+    LOGD("onDisconnectedEvent()");
+}
+
+#if 0
+void WifiController::onTerminatingEvent(SupplicantEvent *evt) {
+    LOGD("onTerminatingEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onPasswordChangedEvent(SupplicantEvent *evt) {
+    LOGD("onPasswordChangedEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapNotificationEvent(SupplicantEvent *evt) {
+    LOGD("onEapNotificationEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapStartedEvent(SupplicantEvent *evt) {
+    LOGD("onEapStartedEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapMethodEvent(SupplicantEvent *evt) {
+    LOGD("onEapMethodEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapSuccessEvent(SupplicantEvent *evt) {
+    LOGD("onEapSuccessEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onEapFailureEvent(SupplicantEvent *evt) {
+    LOGD("onEapFailureEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onLinkSpeedEvent(SupplicantEvent *evt) {
+    LOGD("onLinkSpeedEvent(%s)", evt->getEvent());
+}
+
+void WifiController::onDriverStateEvent(SupplicantEvent *evt) {
+    LOGD("onDriverStateEvent(%s)", evt->getEvent());
+}
+#endif
diff --git a/nexus/WifiController.h b/nexus/WifiController.h
index b2f4530..c61d97a 100644
--- a/nexus/WifiController.h
+++ b/nexus/WifiController.h
@@ -20,15 +20,21 @@
 #include <sys/types.h>
 
 #include "Controller.h"
+#include "ScanResult.h"
+#include "WifiNetwork.h"
+#include "ISupplicantEventHandler.h"
 
 class NetInterface;
 class Supplicant;
 class WifiScanner;
+class SupplicantAssociatingEvent;
+class SupplicantAssociatedEvent;
+class SupplicantConnectedEvent;
+class SupplicantScanResultsEvent;
+class SupplicantStateChangeEvent;
+class SupplicantDisconnectedEvent;
 
-#include "ScanResult.h"
-#include "WifiNetwork.h"
-
-class WifiController : public Controller {
+class WifiController : public Controller, public ISupplicantEventHandler {
 public:
     static const uint32_t SCAN_ENABLE_MASK       = 0x01;
     static const uint32_t SCAN_ACTIVE_MASK       = 0x02;
@@ -45,12 +51,18 @@
     char        mModulePath[255];
     char        mModuleName[64];
     char        mModuleArgs[255];
+
     uint32_t    mCurrentScanMode;
     WifiScanner *mScanner;
+    int         mSupplicantState;
+
+    ScanResultCollection *mLatestScanResults;
+    pthread_mutex_t      mLatestScanResultsLock;
+
     bool        mEnabled;
 
 public:
-    WifiController(PropertyManager *propmngr, char *modpath, char *modname, char *modargs);
+    WifiController(PropertyManager *propmngr, IControllerHandler *handlers, char *modpath, char *modname, char *modargs);
     virtual ~WifiController() {}
 
     int start();
@@ -85,6 +97,27 @@
     int setScanMode(uint32_t mode);
     int enable();
     int disable();
+
+    // ISupplicantEventHandler methods
+    virtual void onAssociatingEvent(SupplicantAssociatingEvent *evt);
+    virtual void onAssociatedEvent(SupplicantAssociatedEvent *evt);
+    virtual void onConnectedEvent(SupplicantConnectedEvent *evt);
+    virtual void onScanResultsEvent(SupplicantScanResultsEvent *evt);
+    virtual void onStateChangeEvent(SupplicantStateChangeEvent *evt);
+    virtual void onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt);
+    virtual void onDisconnectedEvent(SupplicantDisconnectedEvent *evt);
+#if 0
+    virtual void onTerminatingEvent(SupplicantEvent *evt);
+    virtual void onPasswordChangedEvent(SupplicantEvent *evt);
+    virtual void onEapNotificationEvent(SupplicantEvent *evt);
+    virtual void onEapStartedEvent(SupplicantEvent *evt);
+    virtual void onEapMethodEvent(SupplicantEvent *evt);
+    virtual void onEapSuccessEvent(SupplicantEvent *evt);
+    virtual void onEapFailureEvent(SupplicantEvent *evt);
+    virtual void onLinkSpeedEvent(SupplicantEvent *evt);
+    virtual void onDriverStateEvent(SupplicantEvent *evt);
+#endif
+
 };
 
 #endif
diff --git a/nexus/WifiNetwork.cpp b/nexus/WifiNetwork.cpp
index 818b91d..7059bd0 100644
--- a/nexus/WifiNetwork.cpp
+++ b/nexus/WifiNetwork.cpp
@@ -76,7 +76,7 @@
     mDefaultKeyIndex = -1;
     mPriority = -1;
     mHiddenSsid = NULL;
-    mAllowedKeyManagement = 0;
+    mAllowedKeyManagement = KeyManagementMask::UNKNOWN;
     mAllowedProtocols = 0;
     mAllowedAuthAlgorithms = 0;
     mAllowedPairwiseCiphers = 0;
@@ -94,8 +94,6 @@
     asprintf(&tmp2, "wifi.net.%d", mNetid);
     mIfaceCfg = new InterfaceConfig(tmp2);
     free(tmp2);
-
-    registerProperties();
     free(tmp);
 }
 
@@ -121,8 +119,6 @@
     asprintf(&tmp2, "wifi.net.%d", mNetid);
     mIfaceCfg = new InterfaceConfig(tmp2);
     free(tmp2);
-
-    registerProperties();
 }
 
 WifiNetwork *WifiNetwork::clone() {
@@ -153,7 +149,6 @@
 }
 
 WifiNetwork::~WifiNetwork() {
-    unregisterProperties();
     if (mSsid)
         free(mSsid);
     if (mBssid)
@@ -203,7 +198,26 @@
 
     len = sizeof(buffer);
     if (mSuppl->getNetworkVar(mNetid, "key_mgmt", buffer, len)) {
-        // TODO
+        if (!strcmp(buffer, "NONE"))
+            setAllowedKeyManagement(KeyManagementMask::NONE);
+        else if (index(buffer, ' ')) {
+            char *next = buffer;
+            char *token;
+            uint32_t mask = 0;
+
+            while((token = strsep(&next, " "))) {
+                if (!strcmp(token, "WPA-PSK"))
+                    mask |= KeyManagementMask::WPA_PSK;
+                else if (!strcmp(token, "WPA-EAP"))
+                    mask |= KeyManagementMask::WPA_EAP;
+                else if (!strcmp(token, "IEE8021X"))
+                    mask |= KeyManagementMask::IEEE8021X;
+                else
+                    LOGW("Unsupported key management scheme '%s'" , token);
+            }
+            setAllowedKeyManagement(mask);
+        } else
+            LOGE("Unsupported key management '%s'", buffer);
     }
 
     len = sizeof(buffer);
@@ -273,7 +287,7 @@
 
         while((v_token = strsep(&v_next, " "))) {
             if (!strcasecmp(v_token, "NONE")) {
-                mask = 0;
+                mask = KeyManagementMask::NONE;
                 none = true;
             } else if (!none) {
                 if (!strcasecmp(v_token, "WPA_PSK"))
@@ -363,7 +377,29 @@
         snprintf(buffer, maxsize, "%d", getDefaultKeyIndex());
     else if (!strcasecmp(fc, "pri"))
         snprintf(buffer, maxsize, "%d", getPriority());
-    else if (!strcasecmp(fc, "hiddenssid")) {
+    else if (!strcasecmp(fc, "AllowedKeyManagement")) {
+        if (getAllowedKeyManagement() == KeyManagementMask::NONE) 
+            strncpy(buffer, "NONE", maxsize);
+        else {
+            char tmp[80] = { '\0' };
+
+            if (getAllowedKeyManagement() & KeyManagementMask::WPA_PSK)
+                strcat(tmp, "WPA_PSK ");
+            if (getAllowedKeyManagement() & KeyManagementMask::WPA_EAP)
+                strcat(tmp, "WPA_EAP ");
+            if (getAllowedKeyManagement() & KeyManagementMask::IEEE8021X)
+                strcat(tmp, "IEEE8021X");
+            if (tmp[0] == '\0') {
+                strncpy(buffer, "(internal error)", maxsize);
+                errno = ENOENT;
+                return NULL;
+            }
+            if (tmp[strlen(tmp)] == ' ')
+                tmp[strlen(tmp)] = '\0';
+
+            strncpy(buffer, tmp, maxsize);
+        }
+    } else if (!strcasecmp(fc, "hiddenssid")) {
         strncpy(buffer,
                 getHiddenSsid() ? getHiddenSsid() : "none",
                 maxsize);
diff --git a/nexus/WifiNetwork.h b/nexus/WifiNetwork.h
index 360ccc2..c2f5d23 100644
--- a/nexus/WifiNetwork.h
+++ b/nexus/WifiNetwork.h
@@ -157,8 +157,6 @@
 
 private:
     WifiNetwork();
-    int registerProperties();
-    int unregisterProperties();
 
 public:
     WifiNetwork(WifiController *c, Supplicant *suppl, int networkId);
@@ -167,6 +165,8 @@
     virtual ~WifiNetwork();
 
     WifiNetwork *clone();
+    int registerProperties();
+    int unregisterProperties();
 
     int getNetworkId() { return mNetid; }
     const char *getSsid() { return mSsid; }
@@ -187,7 +187,7 @@
     int set(const char *name, const char *value);
     const char *get(const char *name, char *buffer, size_t maxsize);
 
-//    InterfaceConfig *getIfaceCfg() { return mIfaceCfg; }
+    InterfaceConfig *getIfaceCfg() { return mIfaceCfg; }
 
     int setEnabled(bool enabled);
     int setSsid(const char *ssid);
diff --git a/nexus/main.cpp b/nexus/main.cpp
index e460d42..936d33f 100644
--- a/nexus/main.cpp
+++ b/nexus/main.cpp
@@ -40,10 +40,10 @@
 
     nm->setBroadcaster((SocketListener *) cl);
 
-    nm->attachController(new LoopController(nm->getPropMngr()));
-    nm->attachController(new TiwlanWifiController(nm->getPropMngr(), "/system/lib/modules/wlan.ko", "wlan", ""));
-//    nm->attachController(new AndroidL2TPVpnController());
-    nm->attachController(new OpenVpnController(nm->getPropMngr()));
+    nm->attachController(new LoopController(nm->getPropMngr(), nm));
+    nm->attachController(new TiwlanWifiController(nm->getPropMngr(), nm, "/system/lib/modules/wlan.ko", "wlan", ""));
+//    nm->attachController(new AndroidL2TPVpnController(nm->getPropMngr(), nm));
+    nm->attachController(new OpenVpnController(nm->getPropMngr(), nm));
 
 
     if (NetworkManager::Instance()->run()) {
diff --git a/nexus/nexctl.c b/nexus/nexctl.c
index cfebbf0..8e1d90c 100644
--- a/nexus/nexctl.c
+++ b/nexus/nexctl.c
@@ -29,25 +29,59 @@
 #include <sys/un.h>
 
 #include <cutils/sockets.h>
-
 #include <private/android_filesystem_config.h>
 
+static void usage(char *progname);
+static int do_monitor(int sock, int stop_after_cmd);
+static int do_cmd(int sock, int argc, char **argv);
+
 int main(int argc, char **argv) {
     int sock;
 
+    if (argc < 2)
+        usage(argv[0]);
+
     if ((sock = socket_local_client("nexus",
                                      ANDROID_SOCKET_NAMESPACE_RESERVED,
                                      SOCK_STREAM)) < 0) {
         fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
-        exit(1);
+        exit(4);
     }
 
-    printf("Connected to nexus\n");
+    if (!strcmp(argv[1], "monitor"))
+        exit(do_monitor(sock, 0));
+    exit(do_cmd(sock, argc, argv));
+}
 
-    char line[255];
+static int do_cmd(int sock, int argc, char **argv) {
+    char final_cmd[255] = { '\0' };
+    int i;
+
+    for (i = 1; i < argc; i++) {
+        char *cmp;
+
+        if (!index(argv[i], ' '))
+            asprintf(&cmp, "%s%s", argv[i], (i == (argc -1)) ? "" : " ");
+        else
+            asprintf(&cmp, "\"%s\"%s", argv[i], (i == (argc -1)) ? "" : " ");
+
+        strcat(final_cmd, cmp);
+        free(cmp);
+    }
+
+    if (write(sock, final_cmd, strlen(final_cmd) + 1) < 0) {
+        perror("write");
+        return errno;
+    }
+
+    return do_monitor(sock, 1);
+}
+
+static int do_monitor(int sock, int stop_after_cmd) {
     char *buffer = malloc(4096);
-    int cursor = 0;
-    int col = 0;
+
+    if (!stop_after_cmd)
+        printf("[Connected to Nexus]\n");
 
     while(1) {
         fd_set read_fds;
@@ -59,66 +93,56 @@
 
         FD_ZERO(&read_fds);
         FD_SET(sock, &read_fds);
-        FD_SET(0, &read_fds);
 
-        if (col == 0) {
-            fprintf(stdout, "-> ");
-            fflush(stdout);
-            col = 3;
-        }
-    
         if ((rc = select(sock +1, &read_fds, NULL, NULL, &to)) < 0) {
             fprintf(stderr, "Error in select (%s)\n", strerror(errno));
-            exit(2);
+            free(buffer);
+            return errno;
         } else if (!rc) {
             continue;
+            fprintf(stderr, "[TIMEOUT]\n");
+            return ETIMEDOUT;
         } else if (FD_ISSET(sock, &read_fds)) {
             memset(buffer, 0, 4096);
             if ((rc = read(sock, buffer, 4096)) <= 0) {
-                 fprintf(stderr, "Error reading response (%s)\n", strerror(errno));
-                 exit(2);
+                if (rc == 0)
+                    fprintf(stderr, "Lost connection to Nexus - did it crash?\n");
+                else
+                    fprintf(stderr, "Error reading data (%s)\n", strerror(errno));
+                free(buffer);
+                if (rc == 0)
+                    return ECONNRESET;
+                return errno;
             }
-            int i;
-            for (i = 0; i < col; i++) {
-                fprintf(stdout, "%c", 8);
-            }
+            
+            int offset = 0;
+            int i = 0;
 
-            printf("%s", buffer);
-            printf("-> ");
-            for (i = 0; i < cursor; i++) {
-                fprintf(stdout, "%c", line[i]);
-            }
-            fflush(stdout);
-        } else if (FD_ISSET(0, &read_fds)) {
-            char c;
+            for (i = 0; i < rc; i++) {
+                if (buffer[i] == '\0') {
+                    int code;
+                    char tmp[4];
 
-            if ((rc = read(0, &c, 1)) < 0) {
-                fprintf(stderr, "Error reading from terminal (%s)\n", strerror(errno));
-                exit(2);
-            } else if (!rc) {
-                fprintf(stderr, "0 length read from terminal\n");
-                exit(2);
-            }
+                    strncpy(tmp, buffer + offset, 3);
+                    tmp[3] = '\0';
+                    code = atoi(tmp);
 
-            fprintf(stdout, "%c", c);
-            fflush(stdout);
-
-            line[cursor] = c;
-
-            if (c == '\n') {
-                if ((rc = write(sock, line, strlen(line))) < 0) {
-                    fprintf(stderr, "Error writing to nexus (%s)\n", strerror(errno));
-                    exit(2);
+                    printf("%s\n", buffer + offset);
+                    if (stop_after_cmd) {
+                        if (code >= 200 && code < 600)
+                            return 0;
+                    }
+                    offset = i + 1;
                 }
-                memset(line, 0, sizeof(line));
-                cursor = 0;
-                col = 0;
-            } else {
-                cursor++;
-                col++;
             }
         }
     }
-
-    exit(0);
+    free(buffer);
+    return 0;
 }
+
+static void usage(char *progname) {
+    fprintf(stderr, "Usage: %s <monitor>|<cmd> [arg1] [arg2...]\n", progname);
+    exit(1);
+}
+
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index b2fe8cf..44e343c 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -6,13 +6,15 @@
 copy_from := \
 	etc/dbus.conf \
 	etc/init.goldfish.sh \
+	etc/ppp/ip-up \
+	etc/ppp/ip-down \
+	etc/ppp/peers/common \
+	etc/racoon/racoon.conf \
 	etc/hosts
 
 dont_copy := \
 	etc/init.gprs-pppd \
-	etc/ppp/chap-secrets \
-	etc/ppp/ip-down \
-	etc/ppp/ip-up
+	etc/ppp/chap-secrets
 
 copy_to := $(addprefix $(TARGET_OUT)/,$(copy_from))
 copy_from := $(addprefix $(LOCAL_PATH)/,$(copy_from))
diff --git a/rootdir/etc/ppp/ip-down b/rootdir/etc/ppp/ip-down
index 672fa1e..58d21e5 100755
--- a/rootdir/etc/ppp/ip-down
+++ b/rootdir/etc/ppp/ip-down
@@ -1,14 +1 @@
 #!/system/bin/sh
-case $1 in
-    ppp1)
-	echo 0 > /proc/sys/net/ipv4/ip_forward;
-	;;
-esac
-
-# Use interface name if linkname is not available
-NAME=${LINKNAME:-"$1"}
-
-/system/bin/setprop "net.$NAME.dns1" "$DNS1"
-/system/bin/setprop "net.$NAME.dns2" "$DNS2" 
-/system/bin/setprop "net.$NAME.local-ip" "$IPLOCAL" 
-/system/bin/setprop "net.$NAME.remote-ip" "$IPREMOTE" 
diff --git a/rootdir/etc/ppp/ip-up b/rootdir/etc/ppp/ip-up
index cb2d577..8c8f12c 100755
--- a/rootdir/etc/ppp/ip-up
+++ b/rootdir/etc/ppp/ip-up
@@ -1,24 +1,33 @@
 #!/system/bin/sh
-case $1 in
-    ppp1)
-	/android/bin/iptables --flush;
-	/android/bin/iptables --table nat --flush;
-	/android/bin/iptables --delete-chain;
-	/android/bin/iptables --table nat --append POSTROUTING --out-interface ppp0 -j MASQUERADE;
-	/android/bin/iptables --append FORWARD --in-interface ppp1 -j ACCEPT;
-	echo 0 > /proc/sys/net/ipv4/ip_forward;
-	echo 1 > /proc/sys/net/ipv4/ip_forward;
-	;;
-    ppp0)
-        /system/bin/setprop "net.interfaces.defaultroute" "gprs"
-        ;;
-esac
 
 # Use interface name if linkname is not available
-NAME=${LINKNAME:-"$1"}
 
-/system/bin/setprop "net.$NAME.dns1" "$DNS1"
-/system/bin/setprop "net.$NAME.dns2" "$DNS2" 
-/system/bin/setprop "net.$NAME.local-ip" "$IPLOCAL" 
-/system/bin/setprop "net.$NAME.remote-ip" "$IPREMOTE" 
+/system/bin/setprop "net.dns1" "$DNS1"
+/system/bin/setprop "net.dns2" "$DNS2"
+
+# Retrieve the default gateway from /proc/net/route
+RTAB=`cat /proc/net/route`
+flag=-1; i=0;
+for l in $RTAB; do
+    if (exp flag==1) then DGW=$l; flag=0; fi;
+    if (exp i%11 == 1) then
+        if (exp $l=="00000000") then flag=1; fi;
+    fi;
+    i=`exp i+1`;
+done
+FH=${DGW%????}
+LH=${DGW#????}
+A=`exp 0x${LH#??}`
+B=`exp 0x${LH%??}`
+C=`exp 0x${FH#??}`
+D=`exp 0x${FH%??}`
+GATEWAY="$A.$B.$C.$D"
+VPNSERVER=`getprop "net.vpn.server_ip"`
+
+# Protect the route to vpn server
+/system/bin/route add -net $VPNSERVER netmask 255.255.255.255 gw $GATEWAY
+
+# Route all traffic to vpn connection
+/system/bin/route add -net 0.0.0.0 netmask 128.0.0.0 gw $IPREMOTE
+/system/bin/route add -net 128.0.0.0 netmask 128.0.0.0 gw $IPREMOTE
 
diff --git a/rootdir/etc/ppp/peers/common b/rootdir/etc/ppp/peers/common
new file mode 100755
index 0000000..4183841
--- /dev/null
+++ b/rootdir/etc/ppp/peers/common
@@ -0,0 +1,10 @@
+ipcp-accept-local
+ipcp-accept-remote
+refuse-eap
+noccp
+noauth
+idle 1800
+mtu 1400
+mru 1400
+nodefaultroute
+usepeerdns
diff --git a/rootdir/etc/racoon/racoon.conf b/rootdir/etc/racoon/racoon.conf
new file mode 100644
index 0000000..eb50a2d
--- /dev/null
+++ b/rootdir/etc/racoon/racoon.conf
@@ -0,0 +1,35 @@
+#path certificate "";
+path certificate "/";
+
+sainfo anonymous {
+   encryption_algorithm aes, 3des;
+   authentication_algorithm hmac_sha1, hmac_md5;
+   compression_algorithm deflate;
+   lifetime time 3600 sec;
+}
+
+remote anonymous {
+   exchange_mode main;
+   doi ipsec_doi;
+   situation identity_only;
+   ike_frag on;
+   generate_policy on;
+   my_identifier asn1dn;
+   nat_traversal on; # always use NAT-T
+   ca_type x509 "ca.crt";
+   certificate_type x509 "user.crt" "user.key";
+   verify_identifier off;
+   verify_cert on;
+   nonce_size 16;
+   initial_contact on;
+   proposal_check obey;
+
+   proposal {
+      authentication_method rsasig;
+      hash_algorithm sha1;
+      encryption_algorithm 3des;
+      lifetime time 3600 sec;
+      dh_group 2;
+   }
+}
+
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9853cc6..cf7d885 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -23,6 +23,7 @@
     mkdir /system
     mkdir /data 0771 system system
     mkdir /cache 0770 system cache
+    mkdir /config 0500 root root
     mkdir /sqlite_stmt_journals 01777 root root
     mount tmpfs tmpfs /sqlite_stmt_journals size=4m
 
@@ -50,11 +51,11 @@
     mkdir /dev/cpuctl/bg_non_interactive
     chown system system /dev/cpuctl/bg_non_interactive/tasks
     chmod 0777 /dev/cpuctl/bg_non_interactive/tasks
-    write /dev/cpuctl/bg_non_interactive/cpu.shares 1024
+    write /dev/cpuctl/bg_non_interactive/cpu.shares 1
 
 # mount mtd partitions
     # Mount /system rw first to give the filesystem a chance to save a checkpoint
-    mount yaffs2 mtd@system /system 
+    mount yaffs2 mtd@system /system
     mount yaffs2 mtd@system /system ro remount
 
     # We chown/chmod /data again so because mount is run as root + defaults
@@ -73,7 +74,10 @@
 
 # create basic filesystem structure
     mkdir /data/misc 01771 system misc
-    mkdir /data/misc/hcid 0770 bluetooth bluetooth
+    mkdir /data/misc/bluetoothd 0770 bluetooth bluetooth
+    mkdir /data/misc/keystore 0770 keystore keystore
+    mkdir /data/misc/vpn 0770 system system
+    mkdir /data/misc/vpn/profiles 0770 system system
     mkdir /data/local 0771 shell shell
     mkdir /data/local/tmp 0771 shell shell
     mkdir /data/data 0771 system system
@@ -105,14 +109,12 @@
 # set RLIMIT_NICE to allow priorities from 19 to -20
     setrlimit 13 40 40
 
-# Set timeout value for rmnet stats.
-    write /sys/devices/virtual/net/rmnet0/timeout_suspend 5000000
-
 # Define the oom_adj values for the classes of processes that can be
 # killed by the kernel.  These are used in ActivityManagerService.
     setprop ro.FOREGROUND_APP_ADJ 0
     setprop ro.VISIBLE_APP_ADJ 1
     setprop ro.SECONDARY_SERVER_ADJ 2
+    setprop ro.BACKUP_APP_ADJ 2
     setprop ro.HOME_APP_ADJ 4
     setprop ro.HIDDEN_APP_MIN_ADJ 7
     setprop ro.CONTENT_PROVIDER_ADJ 14
@@ -123,6 +125,7 @@
     setprop ro.FOREGROUND_APP_MEM 1536
     setprop ro.VISIBLE_APP_MEM 2048
     setprop ro.SECONDARY_SERVER_MEM 4096
+    setprop ro.BACKUP_APP_MEM 4096
     setprop ro.HOME_APP_MEM 4096
     setprop ro.HIDDEN_APP_MEM 5120
     setprop ro.CONTENT_PROVIDER_MEM 5632
@@ -252,16 +255,18 @@
     disabled
     oneshot
 
-service dbus /system/bin/dbus-daemon --system --nofork
+#STOPSHIP: Remove logwrapper before production
+service dbus /system/bin/logwrapper /system/bin/dbus-daemon --system --nofork
     socket dbus stream 660 bluetooth bluetooth
     user bluetooth
     group bluetooth net_bt_admin
 
-service hcid /system/bin/hcid -s -n -f /etc/bluez/hcid.conf
+#STOPSHIP: Remove logwrapper and -d option before production
+service bluetoothd /system/bin/logwrapper /system/bin/bluetoothd -d -n
     socket bluetooth stream 660 bluetooth bluetooth
     socket dbus_bluetooth stream 660 bluetooth bluetooth
     # init.rc does not yet support applying capabilities, so run as root and
-    # let hcid drop uid to bluetooth with the right linux capabilities
+    # let bluetoothd drop uid to bluetooth with the right linux capabilities
     group bluetooth net_bt_admin misc
     disabled
 
@@ -282,3 +287,19 @@
 
 service flash_recovery /system/bin/flash_image recovery /system/recovery.img
     oneshot
+
+service racoon /system/bin/racoon -F -f /etc/racoon/racoon.conf
+    socket racoon stream 600 system system
+    disabled
+    oneshot
+
+service mtpd /system/bin/mtpd
+    socket mtpd stream 600 system system
+    disabled
+    oneshot
+
+service keystore /system/bin/keystore
+    user keystore
+    group keystore
+    socket keystore stream 666
+
diff --git a/vold/format.c b/vold/format.c
index 3383949..a1faf7a 100755
--- a/vold/format.c
+++ b/vold/format.c
@@ -44,7 +44,7 @@
         args[3] = "-O android";
         args[4] = devpath;
         args[5] = NULL;
-        rc = logwrap(5, args);
+        rc = logwrap(5, args, 1);
     } else {
         char *args[7];
         args[0] = MKE2FS_PATH;
@@ -54,7 +54,7 @@
         args[4] = "-v";
         args[5] = devpath;
         args[6] = NULL;
-        rc = logwrap(6, args);
+        rc = logwrap(6, args, 1);
     }
  
     free(devpath);
diff --git a/vold/logwrapper.c b/vold/logwrapper.c
index 2900f2e..46f6ed3 100644
--- a/vold/logwrapper.c
+++ b/vold/logwrapper.c
@@ -100,7 +100,7 @@
     }
 }
 
-int logwrap(int argc, char* argv[], pid_t *childPid)
+int logwrap(int argc, char* argv[], pid_t *childPid, int background)
 {
     pid_t pid;
 
@@ -138,6 +138,25 @@
         dup2(child_ptty, 2);
         close(child_ptty);
 
+        if (background) {
+            int fd = open("/dev/cpuctl/bg_non_interactive/tasks", O_WRONLY);
+      
+            if (fd >=0 ) {
+                char text[64];
+
+                sprintf(text, "%d", getpid());
+                if (write(fd, text, strlen(text)) < 0) {
+                    LOG(LOG_WARN, "logwrapper",
+                        "Unable to background process (%s)", strerror(errno));
+                    close(fd);
+                }
+                close(fd);
+            } else {
+                LOG(LOG_WARN, "logwrapper",
+                    "Unable to background process (%s)", strerror(errno));
+            }
+        }
+
         child(argc, argv);
     } else {
         return parent(argv[0], parent_ptty);
diff --git a/vold/logwrapper.h b/vold/logwrapper.h
index 602e24c..bf28aae 100644
--- a/vold/logwrapper.h
+++ b/vold/logwrapper.h
@@ -19,5 +19,5 @@
 #define _LOGWRAPPER_H
 
 #include <stdlib.h>
-int logwrap(int argc, char* argv[]);
+int logwrap(int argc, char* argv[], int background);
 #endif
diff --git a/vold/volmgr_ext3.c b/vold/volmgr_ext3.c
index 680be21..fe3b2bb 100644
--- a/vold/volmgr_ext3.c
+++ b/vold/volmgr_ext3.c
@@ -107,7 +107,7 @@
     args[3] = devpath;
     args[4] = NULL;
 
-    int rc = logwrap(4, args);
+    int rc = logwrap(4, args, 1);
 
     if (rc == 0) {
         LOG_VOL("filesystem '%s' had no errors", devpath);
diff --git a/vold/volmgr_vfat.c b/vold/volmgr_vfat.c
index 2b0e1fa..7833222 100644
--- a/vold/volmgr_vfat.c
+++ b/vold/volmgr_vfat.c
@@ -62,13 +62,13 @@
             args[3] = "-p";
             args[4] = blkdev_get_devpath(dev);
             args[5] = NULL;
-            rc = logwrap(5, args);
+            rc = logwrap(5, args, 1);
             free(args[4]);
         } else {
             args[2] = "-n";
             args[3] = blkdev_get_devpath(dev);
             args[4] = NULL;
-            rc = logwrap(4, args);
+            rc = logwrap(4, args, 1);
             free(args[3]);
         }