nexus: Rollup update for nexus

nexus: Change field separator from : to ' '

Signed-off-by: San Mehat <san@google.com>

nexus: Add some prototypes for stuff to come

Signed-off-by: San Mehat <san@google.com>

nexus: Add some TODOs

Signed-off-by: San Mehat <san@google.com>

libsysutils: Put a proper token parser into the FrameworkListener which
supports minimal \ escapes and quotes

Signed-off-by: San Mehat <san@google.com>

nexus: Fix a lot of bugs

Signed-off-by: San Mehat <san@google.com>

libsysutils: Remove some debugging
Signed-off-by: San Mehat <san@google.com>

nexus: Send broadcasts for supplicant state changes

Signed-off-by: San Mehat <san@google.com>

nexus: Plumb DHCP listener state changes to NetworkManager

Signed-off-by: San Mehat <san@google.com>

nexus: Make the SupplicantState strings more parsable

Signed-off-by: San Mehat <san@google.com>

nexus: Broadcast a message when dhcp state changes.

Signed-off-by: San Mehat <san@google.com>

nexus: Add a few new response codes

Signed-off-by: San Mehat <san@google.com>

nexus: Rename ErrorCode -> ResponseCode

Signed-off-by: San Mehat <san@google.com>

nexus: Add DHCP event broadcasting. Also adds the framework for
tracking supplicant 'searching-for-AP' state

Signed-off-by: San Mehat <san@google.com>

nexus: REmove WifiScanner

Signed-off-by: San Mehat <san@google.com>

nexus: Change the way scanning works. scanmode can now be selected
independantly of triggering a scan. Also adds rxfilter support

Signed-off-by: San Mehat <san@google.com>

nexus: Add support for configuring bluetooth coexistence scanning and modes

Signed-off-by: San Mehat <san@google.com>

nexus: use case insensitive match for property names

Signed-off-by: San Mehat <san@google.com>

nexus: Rollup of a bunch of stuff:
    - 'list' command now takes an argument to match against
    - InterfaceConfig has been moved into the Controller base (for now)
    - DhcpClient now has some rudimentry locking
    - process 'ADDRINFO' messages from dhcpcd
    - Drop tertiary dns

Signed-off-by: San Mehat <san@google.com>

nexus: Clean up some of the supplicant variable parsing and add 'wifi.current'

Signed-off-by: San Mehat <san@google.com>

nexus: Add driver-stop/start, initial suspend support

Signed-off-by: San Mehat <san@google.com>

nexus: Add Controller suspend/resume callbacks, as well as locking

Signed-off-by: San Mehat <san@google.com>

nexus: Make ARP probing configurable for DhcpClient

Signed-off-by: San Mehat <san@google.com>

nexus: Add linkspeed / rssi retrieval

Signed-off-by: San Mehat <san@google.com>

nexus: Add WifiStatusPoller to track RSSI/linkspeed when associated

Signed-off-by: San Mehat <san@google.com>

nexus: Disable some debugging and add 'wifi.netcount' property

Signed-off-by: San Mehat <san@google.com>

nexus: Replace the hackish property system with something more flexible with namespaces

Signed-off-by: San Mehat <san@google.com>

libsysutils: Fix a few bugs in SocketListener

Signed-off-by: San Mehat <san@google.com>

nexus: PropertyManager: Add array support

Signed-off-by: San Mehat <san@google.com>

nexus: Clean up properties
Signed-off-by: San Mehat <san@google.com>

nexus: WifiController: Change name of 'CurrentNetwork' property

Signed-off-by: San Mehat <san@google.com>
diff --git a/nexus/Android.mk b/nexus/Android.mk
index cd477ef..f9f7110 100644
--- a/nexus/Android.mk
+++ b/nexus/Android.mk
@@ -7,23 +7,22 @@
 
 LOCAL_SRC_FILES:=                                      \
                   main.cpp                             \
-                  NetworkManager.cpp                   \
+                  NexusCommand.cpp                     \
                   CommandListener.cpp                  \
+                  Property.cpp                         \
+                  PropertyManager.cpp                  \
+                  InterfaceConfig.cpp                  \
+                  NetworkManager.cpp                   \
                   Controller.cpp                       \
                   WifiController.cpp                   \
-                  LoopController.cpp                   \
-                  NexusCommand.cpp                     \
                   TiwlanWifiController.cpp             \
+                  TiwlanEventListener.cpp              \
+                  WifiNetwork.cpp                      \
+                  WifiStatusPoller.cpp                 \
+                  ScanResult.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         \
@@ -34,8 +33,11 @@
                   SupplicantConnectionTimeoutEvent.cpp \
                   SupplicantDisconnectedEvent.cpp      \
                   SupplicantStatus.cpp                 \
-                  TiwlanEventListener.cpp              \
+                  OpenVpnController.cpp                \
+                  VpnController.cpp                    \
+                  LoopController.cpp                   \
                   DhcpClient.cpp DhcpListener.cpp      \
+                  DhcpState.cpp DhcpEvent.cpp          \
 
 LOCAL_MODULE:= nexus
 
diff --git a/nexus/CommandListener.cpp b/nexus/CommandListener.cpp
index 8eb378b..5973ff5 100644
--- a/nexus/CommandListener.cpp
+++ b/nexus/CommandListener.cpp
@@ -31,7 +31,7 @@
 #include "NetworkManager.h"
 #include "WifiController.h"
 #include "VpnController.h"
-#include "ErrorCode.h"
+#include "ResponseCode.h"
 
 CommandListener::CommandListener() :
                  FrameworkListener("nexus") {
@@ -60,11 +60,11 @@
     WifiNetwork *wn;
 
     if (!(wn = wc->createNetwork()))
-        cli->sendMsg(ErrorCode::OperationFailed, "Failed to create network", true);
+        cli->sendMsg(ResponseCode::OperationFailed, "Failed to create network", true);
     else {
         char tmp[128];
         sprintf(tmp, "Created network id %d.", wn->getNetworkId());
-        cli->sendMsg(ErrorCode::CommandOkay, tmp, false);
+        cli->sendMsg(ResponseCode::CommandOkay, tmp, false);
     }
     return 0;
 }
@@ -79,9 +79,9 @@
     WifiController *wc = (WifiController *) nm->findController("WIFI");
 
     if (wc->removeNetwork(atoi(argv[1])))
-        cli->sendMsg(ErrorCode::OperationFailed, "Failed to remove network", true);
+        cli->sendMsg(ResponseCode::OperationFailed, "Failed to remove network", true);
     else {
-        cli->sendMsg(ErrorCode::CommandOkay, "Network removed.", false);
+        cli->sendMsg(ResponseCode::CommandOkay, "Network removed.", false);
     }
     return 0;
 }
@@ -100,16 +100,16 @@
     char buffer[256];
 
     for(it = src->begin(); it != src->end(); ++it) {
-        sprintf(buffer, "%s:%u:%d:%s:%s",
+        sprintf(buffer, "%s %u %d %s %s",
                 (*it)->getBssid(), (*it)->getFreq(), (*it)->getLevel(),
                 (*it)->getFlags(), (*it)->getSsid());
-        cli->sendMsg(ErrorCode::WifiScanResult, buffer, false);
+        cli->sendMsg(ResponseCode::WifiScanResult, buffer, false);
         delete (*it);
         it = src->erase(it);
     }
 
     delete src;
-    cli->sendMsg(ErrorCode::CommandOkay, "Scan results complete.", false);
+    cli->sendMsg(ResponseCode::CommandOkay, "Scan results complete.", false);
     return 0;
 }
 
@@ -128,12 +128,12 @@
 
     for(it = src->begin(); it != src->end(); ++it) {
         sprintf(buffer, "%d:%s", (*it)->getNetworkId(), (*it)->getSsid());
-        cli->sendMsg(ErrorCode::WifiNetworkList, buffer, false);
+        cli->sendMsg(ResponseCode::WifiNetworkList, buffer, false);
         delete (*it);
     }
 
     delete src;
-    cli->sendMsg(ErrorCode::CommandOkay, "Network listing complete.", false);
+    cli->sendMsg(ResponseCode::CommandOkay, "Network listing complete.", false);
     return 0;
 }
 
@@ -159,14 +159,14 @@
 
     char *tmp;
     asprintf(&tmp, "%s %s", argv[1], val);
-    cli->sendMsg(ErrorCode::PropertyRead, tmp, false);
+    cli->sendMsg(ResponseCode::PropertyRead, tmp, false);
     free(tmp);
 
-    cli->sendMsg(ErrorCode::CommandOkay, "Property read.", false);
+    cli->sendMsg(ResponseCode::CommandOkay, "Property read.", false);
     return 0;
 out_inval:
     errno = EINVAL;
-    cli->sendMsg(ErrorCode::CommandParameterError, "Failed to read property.", true);
+    cli->sendMsg(ResponseCode::CommandParameterError, "Failed to read property.", true);
     return 0;
 }
 
@@ -179,12 +179,12 @@
     if (NetworkManager::Instance()->getPropMngr()->set(argv[1], argv[2]))
         goto out_inval;
 
-    cli->sendMsg(ErrorCode::CommandOkay, "Property set.", false);
+    cli->sendMsg(ResponseCode::CommandOkay, "Property set.", false);
     return 0;
 
 out_inval:
     errno = EINVAL;
-    cli->sendMsg(ErrorCode::CommandParameterError, "Failed to set property.", true);
+    cli->sendMsg(ResponseCode::CommandParameterError, "Failed to set property.", true);
     return 0;
 }
 
@@ -194,10 +194,14 @@
 
 int CommandListener::ListCmd::runCommand(SocketClient *cli, int argc, char **argv) {
     android::List<char *> *pc;
+    char *prefix = NULL;
 
-    if (!(pc = NetworkManager::Instance()->getPropMngr()->createPropertyList())) {
+    if (argc > 1)
+        prefix = argv[1];
+
+    if (!(pc = NetworkManager::Instance()->getPropMngr()->createPropertyList(prefix))) {
         errno = ENODATA;
-        cli->sendMsg(ErrorCode::CommandParameterError, "Failed to list properties.", true);
+        cli->sendMsg(ResponseCode::CommandParameterError, "Failed to list properties.", true);
         return 0;
     }
 
@@ -218,7 +222,7 @@
             free((*it));
             continue;
         }
-        cli->sendMsg(ErrorCode::PropertyList, buf, false);
+        cli->sendMsg(ResponseCode::PropertyList, buf, false);
         free(buf);
 
         free((*it));
@@ -226,6 +230,6 @@
 
     delete pc;
 
-    cli->sendMsg(ErrorCode::CommandOkay, "Properties list complete.", false);
+    cli->sendMsg(ResponseCode::CommandOkay, "Properties list complete.", false);
     return 0;
 }
diff --git a/nexus/Controller.cpp b/nexus/Controller.cpp
index 17fb519..f6a2436 100644
--- a/nexus/Controller.cpp
+++ b/nexus/Controller.cpp
@@ -30,6 +30,7 @@
 #include <cutils/log.h>
 
 #include "Controller.h"
+#include "InterfaceConfig.h"
 
 extern "C" int init_module(void *, unsigned int, const char *);
 extern "C" int delete_module(const char *, unsigned int);
@@ -57,16 +58,6 @@
     return 0;
 }
 
-int Controller::set(const char *name, const char *value) {
-    errno = ENOENT;
-    return -1;
-}
-
-const char *Controller::get(const char *name, char *buffer, size_t maxsize) {
-    errno = ENOENT;
-    return NULL;
-}
-
 int Controller::loadKernelModule(char *modpath, const char *args) {
     void *module;
     unsigned int size;
@@ -164,13 +155,11 @@
 
 int Controller::bindInterface(const char *ifname) {
     mBoundInterface = strdup(ifname);
-    LOGD("Controller %s bound to %s", mName, ifname);
     return 0;
 }
 
 int Controller::unbindInterface(const char *ifname) {
     free(mBoundInterface);
     mBoundInterface = NULL;
-    LOGD("Controller %s unbound from %s", mName, ifname);
     return 0;
 }
diff --git a/nexus/Controller.h b/nexus/Controller.h
index af03d2e..e7e17c5 100644
--- a/nexus/Controller.h
+++ b/nexus/Controller.h
@@ -26,9 +26,8 @@
 class IControllerHandler;
 
 #include "PropertyManager.h"
-#include "IPropertyProvider.h"
 
-class Controller : public IPropertyProvider {
+class Controller {
     /*
      * Name of this controller - WIFI/VPN/USBNET/BTNET/BTDUN/LOOP/etc
      */
@@ -54,11 +53,7 @@
 
     const char *getName() { return mName; }
     const char *getBoundInterface() { return mBoundInterface; }
-
-    /* IPropertyProvider methods */
-    virtual int set(const char *name, const char *value);
-    virtual const char *get(const char *name, char *buffer, size_t maxsize);
-
+    
 protected:
     int loadKernelModule(char *modpath, const char *args);
     bool isKernelModuleLoaded(const char *modtag);
diff --git a/nexus/DhcpClient.cpp b/nexus/DhcpClient.cpp
index 2bd9c68..a5654d2 100644
--- a/nexus/DhcpClient.cpp
+++ b/nexus/DhcpClient.cpp
@@ -17,7 +17,9 @@
 #include <stdio.h>
 #include <errno.h>
 #include <sys/types.h>
+#include <sys/socket.h>
 #include <arpa/inet.h>
+#include <pthread.h>
 
 #define LOG_TAG "DhcpClient"
 #include <cutils/log.h>
@@ -29,6 +31,7 @@
 #include "DhcpState.h"
 #include "DhcpListener.h"
 #include "IDhcpEventHandlers.h"
+#include "Controller.h"
 
 extern "C" {
 int ifc_disable(const char *ifname);
@@ -54,9 +57,13 @@
 }
 
 DhcpClient::DhcpClient(IDhcpEventHandlers *handlers) :
-            mState(DhcpState::STOPPED), mHandlers(handlers) {
+            mState(DhcpState::INIT), mHandlers(handlers) {
     mServiceManager = new ServiceManager();
     mListener = NULL;
+    mListenerSocket = NULL;
+    mController = NULL;
+    mDoArpProbe = false;
+    pthread_mutex_init(&mLock, NULL);
 }
 
 DhcpClient::~DhcpClient() {
@@ -65,41 +72,89 @@
         delete mListener;
 }
 
-int DhcpClient::start(const char *interface) {
-
+int DhcpClient::start(Controller *c) {
+    LOGD("Starting DHCP service (arp probe = %d)", mDoArpProbe);
     char svc[PROPERTY_VALUE_MAX];
-    snprintf(svc, sizeof(svc), "dhcpcd_ng:%s", interface);
+    snprintf(svc,
+             sizeof(svc),
+             "dhcpcd:%s%s",
+             (!mDoArpProbe ? "-A " : ""),
+             c->getBoundInterface());
 
-    if (mServiceManager->start(svc)) {
-        LOGE("Failed to start dhcp service");
+    pthread_mutex_lock(&mLock);
+
+    if (mController) {
+        pthread_mutex_unlock(&mLock);
+        errno = EBUSY;
+        return -1;
+    }
+    mController = c;
+
+    sockaddr_in addr;
+    if ((mListenerSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+        LOGE("Failed to create DHCP listener socket");
+        pthread_mutex_unlock(&mLock);
+        return -1;
+    }
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+    addr.sin_port = htons(DhcpClient::STATUS_MONITOR_PORT);
+
+    if (bind(mListenerSocket, (struct sockaddr *) &addr, sizeof(addr))) {
+        LOGE("Failed to bind DHCP listener socket");
+        close(mListenerSocket);
+        mListenerSocket = -1;
+        pthread_mutex_unlock(&mLock);
         return -1;
     }
 
-    mListener = new DhcpListener(mHandlers);
+    if (mServiceManager->start(svc)) {
+        LOGE("Failed to start dhcp service");
+        pthread_mutex_unlock(&mLock);
+        return -1;
+    }
+
+    mListener = new DhcpListener(mController, mListenerSocket, mHandlers);
     if (mListener->startListener()) {
         LOGE("Failed to start listener");
 #if 0
-        mServiceManager->stop("dhcpcd_ng");
+        mServiceManager->stop("dhcpcd");
         return -1;
 #endif
         delete mListener;
         mListener = NULL;
+        pthread_mutex_unlock(&mLock);
     }
 
-    mState = DhcpState::STARTED;
-
+    pthread_mutex_unlock(&mLock);
     return 0;
 }
 
 int DhcpClient::stop() {
+    pthread_mutex_lock(&mLock);
+    if (!mController) {
+        pthread_mutex_unlock(&mLock);
+        return 0;
+    }
+
     if (mListener) {
         mListener->stopListener();
         delete mListener;
         mListener = NULL;
     }
+    close(mListenerSocket);
 
-    if (mServiceManager->stop("dhcpcd_ng")) 
+    if (mServiceManager->stop("dhcpcd")) {
         LOGW("Failed to stop DHCP service (%s)", strerror(errno));
-    mState = DhcpState::STOPPED;
+        // XXX: Kill it the hard way.. but its gotta go!
+    }
+
+    mController = NULL;
+    pthread_mutex_unlock(&mLock);
     return 0;
 }
+
+void DhcpClient::setDoArpProbe(bool probe) {
+    mDoArpProbe = probe;
+}
diff --git a/nexus/DhcpClient.h b/nexus/DhcpClient.h
index e0a2784..42bfda0 100644
--- a/nexus/DhcpClient.h
+++ b/nexus/DhcpClient.h
@@ -18,23 +18,36 @@
 #ifndef _DhcpClient_H
 #define _DhcpClient_H
 
+#include <pthread.h>
+
 class IDhcpEventHandlers;
 class ServiceManager;
 class DhcpListener;
+class Controller;
 
 class DhcpClient {
+public:
+    static const int STATUS_MONITOR_PORT = 6666;
+
+private:
     int                mState;
     IDhcpEventHandlers *mHandlers;
     ServiceManager     *mServiceManager;
     DhcpListener       *mListener;
+    int                mListenerSocket;
+    pthread_mutex_t    mLock;
+    Controller         *mController;
+    bool               mDoArpProbe;
 
 public:
     DhcpClient(IDhcpEventHandlers *handlers);
     virtual ~DhcpClient();
 
     int getState() { return mState; }
+    bool getDoArpProbe() { return mDoArpProbe; }
+    void setDoArpProbe(bool probe);
 
-    int start(const char *interface);
+    int start(Controller *c);
     int stop();
 };
 
diff --git a/nexus/DhcpEvent.cpp b/nexus/DhcpEvent.cpp
new file mode 100644
index 0000000..58893f4
--- /dev/null
+++ b/nexus/DhcpEvent.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#define LOG_TAG "DhcpEvent"
+#include <cutils/log.h>
+
+#include "DhcpEvent.h"
+
+char *DhcpEvent::toString(int val, char *buffer, int max) {
+    if (val == DhcpEvent::UNKNOWN)
+        strncpy(buffer, "UNKNOWN", max);
+    else if (val == DhcpEvent::STOP)
+        strncpy(buffer, "STOP", max);
+    else if (val == DhcpEvent::RENEW)
+        strncpy(buffer, "RENEW", max);
+    else if (val == DhcpEvent::RELEASE)
+        strncpy(buffer, "RELEASE", max);
+    else if (val == DhcpEvent::TIMEOUT)
+        strncpy(buffer, "TIMEOUT", max);
+    else
+        strncpy(buffer, "(internal error)", max);
+
+    return buffer;
+}
+
+int DhcpEvent::parseString(const char *buffer) {
+    if (!strcasecmp(buffer, "UNKNOWN"))
+        return DhcpEvent::UNKNOWN;
+    else if (!strcasecmp(buffer, "STOP"))
+        return DhcpEvent::STOP;
+    else if (!strcasecmp(buffer, "RENEW"))
+        return DhcpEvent::RENEW;
+    else if (!strcasecmp(buffer, "RELEASE"))
+        return DhcpEvent::RELEASE;
+    else if (!strcasecmp(buffer, "TIMEOUT"))
+        return DhcpEvent::TIMEOUT;
+    else {
+        LOGW("Bad event '%s'", buffer);
+        return -1;
+    }
+}
diff --git a/nexus/IPropertyProvider.h b/nexus/DhcpEvent.h
similarity index 63%
rename from nexus/IPropertyProvider.h
rename to nexus/DhcpEvent.h
index 17ad5f8..f77834d 100644
--- a/nexus/IPropertyProvider.h
+++ b/nexus/DhcpEvent.h
@@ -14,20 +14,20 @@
  * limitations under the License.
  */
 
-#ifndef _IPROPERTY_PROVIDER_H
-#define _IPROPERTY_PROVIDER_H
+#ifndef _DHCP_EVENT_H
+#define _DHCP_EVENT_H
 
-#include <unistd.h>
-#include <sys/types.h>
-
-#include <utils/List.h>
-
-class IPropertyProvider {
+class DhcpEvent {
 public:
-    virtual int set(const char *name, const char *value) = 0;
-    virtual const char *get(const char *name, char *buffer, size_t max) = 0;
-};
+    static const int UNKNOWN = 0;
+    static const int STOP    = 1;
+    static const int RENEW   = 2;
+    static const int RELEASE = 3;
+    static const int TIMEOUT = 4;
 
-typedef android::List<IPropertyProvider *> IPropertyProviderCollection;
+    static char *toString(int val, char *buffer, int max);
+
+    static int parseString(const char *buffer);
+};
 
 #endif
diff --git a/nexus/DhcpListener.cpp b/nexus/DhcpListener.cpp
index 2d07ee8..afa68eb 100644
--- a/nexus/DhcpListener.cpp
+++ b/nexus/DhcpListener.cpp
@@ -14,21 +14,95 @@
  * limitations under the License.
  */
 
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #define LOG_TAG "DhcpListener"
 #include <cutils/log.h>
 
 #include <DhcpListener.h>
 #include "IDhcpEventHandlers.h"
+#include "DhcpState.h"
+#include "DhcpEvent.h"
+#include "Controller.h"
 
-DhcpListener::DhcpListener(IDhcpEventHandlers *handlers) :
-              SocketListener("dhcp_ng", false) {
+DhcpListener::DhcpListener(Controller *c, int socket, IDhcpEventHandlers *handlers) :
+              SocketListener(socket, false) {
     mHandlers = handlers;
+    mController = c;
 }
 
 DhcpListener::~DhcpListener() {
 }
 
 bool DhcpListener::onDataAvailable(SocketClient *cli) {
-    LOGD("onDataAvailable()");
+    char buffer[255];
+    int rc;
+
+    if ((rc = read(cli->getSocket(), buffer, sizeof(buffer))) < 0) {
+        LOGW("Error reading dhcp status msg (%s)", strerror(errno));
+        return true;
+    }
+
+    if (!strncmp(buffer, "STATE:", 6)) {
+        char *next = buffer;
+        char *tmp;
+        int i;
+
+        for (i = 0; i < 2; i++) {
+            if (!(tmp = strsep(&next, ":"))) {
+                LOGW("Error parsing state '%s'", buffer);
+                return true;
+            }
+        }
+
+        int st = DhcpState::parseString(tmp);
+        mHandlers->onDhcpStateChanged(mController, st);
+    } else if (!strncmp(buffer, "ADDRINFO:", 9)) {
+        char *next = buffer + 9;
+	struct in_addr ipaddr, netmask, gateway, broadcast, dns1, dns2;
+
+        if (!inet_aton(strsep(&next, ":"), &ipaddr)) {
+            LOGW("Malformatted IP specified");
+        }
+        if (!inet_aton(strsep(&next, ":"), &netmask)) {
+            LOGW("Malformatted netmask specified");
+        }
+        if (!inet_aton(strsep(&next, ":"), &broadcast)) {
+            LOGW("Malformatted broadcast specified");
+        }
+        if (!inet_aton(strsep(&next, ":"), &gateway)) {
+            LOGW("Malformatted gateway specified");
+        }
+        if (!inet_aton(strsep(&next, ":"), &dns1)) {
+            LOGW("Malformatted dns1 specified");
+        }
+        if (!inet_aton(strsep(&next, ":"), &dns2)) {
+            LOGW("Malformatted dns2 specified");
+        }
+        mHandlers->onDhcpLeaseUpdated(mController, &ipaddr, &netmask,
+                                      &broadcast, &gateway, &dns1, &dns2);
+ 
+    } else if (!strncmp(buffer, "EVENT:", 6)) {
+        char *next = buffer;
+        char *tmp;
+        int i;
+
+        for (i = 0; i < 2; i++) {
+            if (!(tmp = strsep(&next, ":"))) {
+                LOGW("Error parsing event '%s'", buffer);
+                return true;
+            }
+        }
+
+        int ev = DhcpEvent::parseString(tmp);
+        mHandlers->onDhcpEvent(mController, ev);
+  
+    } else {
+        LOGW("Unknown DHCP monitor msg '%s'", buffer);
+    }
+
     return true;
 }
diff --git a/nexus/DhcpListener.h b/nexus/DhcpListener.h
index bfc5a37..ca6fe37 100644
--- a/nexus/DhcpListener.h
+++ b/nexus/DhcpListener.h
@@ -20,13 +20,15 @@
 #include <sysutils/SocketListener.h>
 
 class IDhcpEventHandlers;
+class Controller;
 
 class DhcpListener : public SocketListener {
     IDhcpEventHandlers *mHandlers;
+    Controller         *mController;
 
 public:
 
-    DhcpListener(IDhcpEventHandlers *handlers);
+    DhcpListener(Controller *c, int socket, IDhcpEventHandlers *handlers);
     virtual ~DhcpListener();
 
 private:
diff --git a/nexus/DhcpState.cpp b/nexus/DhcpState.cpp
new file mode 100644
index 0000000..c9d5135
--- /dev/null
+++ b/nexus/DhcpState.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#define LOG_TAG "DhcpState"
+#include <cutils/log.h>
+
+#include "DhcpState.h"
+
+char *DhcpState::toString(int val, char *buffer, int max) {
+    if (val == DhcpState::INIT)
+        strncpy(buffer, "INIT", max);
+    else if (val == DhcpState::DISCOVERING)
+        strncpy(buffer, "DISCOVERING", max);
+    else if (val == DhcpState::REQUESTING)
+        strncpy(buffer, "REQUESTING", max);
+    else if (val == DhcpState::BOUND)
+        strncpy(buffer, "BOUND", max);
+    else if (val == DhcpState::RENEWING)
+        strncpy(buffer, "RENEWING", max);
+    else if (val == DhcpState::REBINDING)
+        strncpy(buffer, "REBINDING", max);
+    else if (val == DhcpState::REBOOT)
+        strncpy(buffer, "REBOOT", max);
+    else if (val == DhcpState::RENEW_REQUESTED)
+        strncpy(buffer, "RENEW_REQUESTED", max);
+    else if (val == DhcpState::INIT_IPV4LL)
+        strncpy(buffer, "INIT_IPV4LL", max);
+    else if (val == DhcpState::PROBING)
+        strncpy(buffer, "PROBING", max);
+    else if (val == DhcpState::ANNOUNCING)
+        strncpy(buffer, "ANNOUNCING", max);
+    else
+        strncpy(buffer, "(internal error)", max);
+
+    return buffer;
+}
+
+int DhcpState::parseString(const char *buffer) {
+    if (!strcasecmp(buffer, "INIT"))
+        return DhcpState::INIT;
+    else if (!strcasecmp(buffer, "DISCOVERING"))
+        return DhcpState::DISCOVERING;
+    else if (!strcasecmp(buffer, "REQUESTING"))
+        return DhcpState::REQUESTING;
+    else if (!strcasecmp(buffer, "BOUND"))
+        return DhcpState::BOUND;
+    else if (!strcasecmp(buffer, "RENEWING"))
+        return DhcpState::RENEWING;
+    else if (!strcasecmp(buffer, "REBINDING"))
+        return DhcpState::REBINDING;
+    else if (!strcasecmp(buffer, "REBOOT"))
+        return DhcpState::REBOOT;
+    else if (!strcasecmp(buffer, "RENEW_REQUESTED"))
+        return DhcpState::INIT_IPV4LL;
+    else if (!strcasecmp(buffer, "INIT_IPV4LL"))
+        return DhcpState::INIT_IPV4LL;
+    else if (!strcasecmp(buffer, "PROBING"))
+        return DhcpState::PROBING;
+    else if (!strcasecmp(buffer, "ANNOUNCING"))
+        return DhcpState::ANNOUNCING;
+    else {
+        LOGW("Bad state '%s'", buffer);
+        return -1;
+    }
+}
diff --git a/nexus/DhcpState.h b/nexus/DhcpState.h
index 27d7c7e..041c5d2 100644
--- a/nexus/DhcpState.h
+++ b/nexus/DhcpState.h
@@ -14,14 +14,26 @@
  * limitations under the License.
  */
 
-#ifndef _DhcpState_H
-#define _DhcpState_H
+#ifndef _DHCP_STATE_H
+#define _DHCP_STATE_H
 
 class DhcpState {
 public:
-    static const int UNKNOWN = 0;
-    static const int STOPPED = 1;
-    static const int STARTED = 2;
+    static const int INIT            = 0;
+    static const int DISCOVERING     = 1;
+    static const int REQUESTING      = 2;
+    static const int BOUND           = 3;
+    static const int RENEWING        = 4;
+    static const int REBINDING       = 5;
+    static const int REBOOT          = 6;
+    static const int RENEW_REQUESTED = 7;
+    static const int INIT_IPV4LL     = 8;
+    static const int PROBING         = 9;
+    static const int ANNOUNCING      = 10;
+
+    static char *toString(int val, char *buffer, int max);
+
+    static int parseString(const char *buffer);
 };
 
 #endif
diff --git a/nexus/IControllerHandler.h b/nexus/IControllerHandler.h
index f7be39c..3151587 100644
--- a/nexus/IControllerHandler.h
+++ b/nexus/IControllerHandler.h
@@ -22,8 +22,11 @@
 
 class IControllerHandler {
 public:
-    virtual void onInterfaceConnected(Controller *c, const InterfaceConfig *cfg) = 0;
-    virtual void onInterfaceDisconnected(Controller *c, const char *name) = 0;
+    virtual ~IControllerHandler() {}
+    virtual void onInterfaceConnected(Controller *c) = 0;
+    virtual void onInterfaceDisconnected(Controller *c) = 0;
+    virtual void onControllerSuspending(Controller *c) = 0;
+    virtual void onControllerResumed(Controller *c) = 0;
 };
 
 #endif
diff --git a/nexus/IDhcpEventHandlers.h b/nexus/IDhcpEventHandlers.h
index 3f51f64..2568f09 100644
--- a/nexus/IDhcpEventHandlers.h
+++ b/nexus/IDhcpEventHandlers.h
@@ -20,6 +20,14 @@
 
 class IDhcpEventHandlers {
 public:
+    virtual ~IDhcpEventHandlers() {}
+    virtual void onDhcpStateChanged(Controller *c, int state) = 0;
+    virtual void onDhcpEvent(Controller *c, int event) = 0;
+    virtual void onDhcpLeaseUpdated(Controller *c,
+                                    struct in_addr *addr, struct in_addr *net,
+                                    struct in_addr *brd,
+                                    struct in_addr *gw, struct in_addr *dns1,
+                                    struct in_addr *dns2) = 0;
 };
 
 #endif
diff --git a/nexus/ISupplicantEventHandler.h b/nexus/ISupplicantEventHandler.h
index b7fd17b..1a3aa07 100644
--- a/nexus/ISupplicantEventHandler.h
+++ b/nexus/ISupplicantEventHandler.h
@@ -27,6 +27,7 @@
 
 class ISupplicantEventHandler {
 public:
+    virtual ~ISupplicantEventHandler(){}
     virtual void onAssociatingEvent(SupplicantAssociatingEvent *evt) = 0;
     virtual void onAssociatedEvent(SupplicantAssociatedEvent *evt) = 0;
     virtual void onConnectedEvent(SupplicantConnectedEvent *evt) = 0;
diff --git a/nexus/IPropertyProvider.h b/nexus/IWifiStatusPollerHandler.h
similarity index 63%
copy from nexus/IPropertyProvider.h
copy to nexus/IWifiStatusPollerHandler.h
index 17ad5f8..9b94945 100644
--- a/nexus/IPropertyProvider.h
+++ b/nexus/IWifiStatusPollerHandler.h
@@ -14,20 +14,13 @@
  * limitations under the License.
  */
 
-#ifndef _IPROPERTY_PROVIDER_H
-#define _IPROPERTY_PROVIDER_H
+#ifndef _IWIFISTATUSPOLLER_HANDLER_H
+#define _IWIFISTATUSPOLLER_HANDLER_H
 
-#include <unistd.h>
-#include <sys/types.h>
-
-#include <utils/List.h>
-
-class IPropertyProvider {
+class IWifiStatusPollerHandler {
 public:
-    virtual int set(const char *name, const char *value) = 0;
-    virtual const char *get(const char *name, char *buffer, size_t max) = 0;
+    virtual ~IWifiStatusPollerHandler() {}
+    virtual void onStatusPollInterval() = 0;
 };
 
-typedef android::List<IPropertyProvider *> IPropertyProviderCollection;
-
 #endif
diff --git a/nexus/InterfaceConfig.cpp b/nexus/InterfaceConfig.cpp
index 66861d0..832cbca 100644
--- a/nexus/InterfaceConfig.cpp
+++ b/nexus/InterfaceConfig.cpp
@@ -23,147 +23,72 @@
 #include "InterfaceConfig.h"
 #include "NetworkManager.h"
 
-const char *InterfaceConfig::PropertyNames[] = { "dhcp", "ip",
-                                                 "netmask",
-                                                 "gateway", "dns1", "dns2",
-                                                 "dns3", '\0' };
-
-InterfaceConfig::InterfaceConfig(const char *prop_prefix) {
-    mPropPrefix = strdup(prop_prefix);
-    mUseDhcp = true;
-    registerProperties();
+InterfaceConfig::InterfaceConfig(bool propertiesReadOnly) {
+    mStaticProperties.propIp = new IPV4AddressPropertyHelper("Addr", propertiesReadOnly, &mIp);
+    mStaticProperties.propNetmask = new IPV4AddressPropertyHelper("Netmask", propertiesReadOnly, &mNetmask);
+    mStaticProperties.propGateway = new IPV4AddressPropertyHelper("Gateway", propertiesReadOnly, &mGateway);
+    mStaticProperties.propBroadcast = new IPV4AddressPropertyHelper("Broadcast", propertiesReadOnly, &mBroadcast);
+    mStaticProperties.propDns = new InterfaceDnsProperty(this, propertiesReadOnly);
 }
 
 InterfaceConfig::~InterfaceConfig() {
-    unregisterProperties();
-    free(mPropPrefix);
+    delete mStaticProperties.propIp;
+    delete mStaticProperties.propNetmask;
+    delete mStaticProperties.propGateway;
+    delete mStaticProperties.propBroadcast;
+    delete mStaticProperties.propDns;
 }
 
-InterfaceConfig::InterfaceConfig(const char *prop_prefix,
-                    const char *ip, const char *nm,
-                    const char *gw, const char *dns1, const char *dns2,
-                    const char *dns3) {
-    mPropPrefix = strdup(prop_prefix);
-    mUseDhcp = false;
-
-    if (!inet_aton(ip, &mIp))
-        LOGW("Unable to parse ip (%s)", ip);
-    if (!inet_aton(nm, &mNetmask))
-        LOGW("Unable to parse netmask (%s)", nm);
-    if (!inet_aton(gw, &mGateway))
-        LOGW("Unable to parse gateway (%s)", gw);
-    if (!inet_aton(dns1, &mDns1))
-        LOGW("Unable to parse dns1 (%s)", dns1);
-    if (!inet_aton(dns2, &mDns2))
-        LOGW("Unable to parse dns2 (%s)", dns2);
-    if (!inet_aton(dns3, &mDns3))
-        LOGW("Unable to parse dns3 (%s)", dns3);
-    registerProperties();
+void InterfaceConfig::setIp(struct in_addr *addr) {
+    memcpy(&mIp, addr, sizeof(struct in_addr));
 }
 
-InterfaceConfig::InterfaceConfig(const char *prop_prefix,
-                    const struct in_addr *ip,
-                    const struct in_addr *nm, const struct in_addr *gw,
-                    const struct in_addr *dns1, const struct in_addr *dns2,
-                    const struct in_addr *dns3) {
-    mPropPrefix = strdup(prop_prefix);
-    mUseDhcp = false;
-
-    memcpy(&mIp, ip, sizeof(struct in_addr));
-    memcpy(&mNetmask, nm, sizeof(struct in_addr));
-    memcpy(&mGateway, gw, sizeof(struct in_addr));
-    memcpy(&mDns1, dns1, sizeof(struct in_addr));
-    memcpy(&mDns2, dns2, sizeof(struct in_addr));
-    memcpy(&mDns3, dns3, sizeof(struct in_addr));
-    registerProperties();
+void InterfaceConfig::setNetmask(struct in_addr *addr) {
+    memcpy(&mNetmask, addr, sizeof(struct in_addr));
 }
 
-int InterfaceConfig::registerProperties() {
-    for (const char **p = InterfaceConfig::PropertyNames; *p != '\0'; p++) {
-        char *tmp;
-        asprintf(&tmp, "%s.if.%s", mPropPrefix, *p);
+void InterfaceConfig::setGateway(struct in_addr *addr) {
+    memcpy(&mGateway, addr, sizeof(struct in_addr));
+}
 
-        if (NetworkManager::Instance()->getPropMngr()->registerProperty(tmp,
-                                                                        this)) {
-            free(tmp);
-            return -1;
-        }
-        free(tmp);
-    }
+void InterfaceConfig::setBroadcast(struct in_addr *addr) {
+    memcpy(&mBroadcast, addr, sizeof(struct in_addr));
+}
+
+void InterfaceConfig::setDns(int idx, struct in_addr *addr) {
+    memcpy(&mDns[idx], addr, sizeof(struct in_addr));
+}
+
+int InterfaceConfig::attachProperties(PropertyManager *pm, const char *nsName) {
+    pm->attachProperty(nsName, mStaticProperties.propIp);
+    pm->attachProperty(nsName, mStaticProperties.propNetmask);
+    pm->attachProperty(nsName, mStaticProperties.propGateway);
+    pm->attachProperty(nsName, mStaticProperties.propBroadcast);
+    pm->attachProperty(nsName, mStaticProperties.propDns);
     return 0;
 }
 
-int InterfaceConfig::unregisterProperties() {
-    for (const char **p = InterfaceConfig::PropertyNames; *p != '\0'; p++) {
-        char *tmp;
-        asprintf(&tmp, "%s.if.%s", mPropPrefix, *p);
-
-        if (NetworkManager::Instance()->getPropMngr()->unregisterProperty(tmp))
-            LOGW("Unable to remove property '%s' (%s)", tmp, strerror(errno));
-        free(tmp);
-    }
+int InterfaceConfig::detachProperties(PropertyManager *pm, const char *nsName) {
+    pm->detachProperty(nsName, mStaticProperties.propIp);
+    pm->detachProperty(nsName, mStaticProperties.propNetmask);
+    pm->detachProperty(nsName, mStaticProperties.propGateway);
+    pm->detachProperty(nsName, mStaticProperties.propBroadcast);
+    pm->detachProperty(nsName, mStaticProperties.propDns);
     return 0;
 }
 
-int InterfaceConfig::set(const char *name, const char *value) {
-    const char *n;
+InterfaceConfig::InterfaceDnsProperty::InterfaceDnsProperty(InterfaceConfig *c,
+                                                            bool ro) :
+                 IPV4AddressProperty("Dns", ro, 2) {
+    mCfg = c;
+}
 
-    for (n = &name[strlen(name)]; *n != '.'; n--);
-    n++;
-
-    if (!strcasecmp(n, "name")) {
-        errno = EROFS;
-        return -1;
-    } else if (!strcasecmp(n, "ip") && !inet_aton(value, &mIp))
-        goto out_inval;
-    else if (!strcasecmp(n, "dhcp"))
-        mUseDhcp = (atoi(value) == 0 ? false : true);
-    else if (!strcasecmp(n, "netmask") && !inet_aton(value, &mNetmask))
-        goto out_inval;
-    else if (!strcasecmp(n, "gateway") && !inet_aton(value, &mGateway))
-        goto out_inval;
-    else if (!strcasecmp(n, "dns1") && !inet_aton(value, &mDns1))
-        goto out_inval;
-    else if (!strcasecmp(n, "dns2") && !inet_aton(value, &mDns2))
-        goto out_inval;
-    else if (!strcasecmp(n, "dns3") && !inet_aton(value, &mDns3))
-        goto out_inval;
-    else {
-        errno = ENOENT;
-        return -1;
-    }
-
+int InterfaceConfig::InterfaceDnsProperty::set(int idx, struct in_addr *value) {
+    memcpy(&mCfg->mDns[idx], value, sizeof(struct in_addr));
     return 0;
-
-out_inval:
-    errno = EINVAL;
-    return -1;
+}
+int InterfaceConfig::InterfaceDnsProperty::get(int idx, struct in_addr *buf) {
+    memcpy(buf, &mCfg->mDns[idx], sizeof(struct in_addr));
+    return 0;
 }
 
-const char *InterfaceConfig::get(const char *name, char *buffer, size_t max) {
-    const char *n;
-
-    for (n = &name[strlen(name)]; *n != '.'; n--);
-    n++;
-
-    if (!strcasecmp(n, "ip"))
-        strncpy(buffer, inet_ntoa(mIp), max);
-    else if (!strcasecmp(n, "dhcp"))
-        snprintf(buffer, max, "%d", mUseDhcp);
-    else if (!strcasecmp(n, "netmask"))
-        strncpy(buffer, inet_ntoa(mNetmask), max);
-    else if (!strcasecmp(n, "gateway"))
-        strncpy(buffer, inet_ntoa(mGateway), max);
-    else if (!strcasecmp(n, "dns1"))
-        strncpy(buffer, inet_ntoa(mDns1), max);
-    else if (!strcasecmp(n, "dns2"))
-        strncpy(buffer, inet_ntoa(mDns2), max);
-    else if (!strcasecmp(n, "dns3"))
-        strncpy(buffer, inet_ntoa(mDns3), max);
-    else {
-        strncpy(buffer, "(internal error)", max);
-        errno = ENOENT;
-        return NULL;
-    }
-    return buffer;
-}
diff --git a/nexus/InterfaceConfig.h b/nexus/InterfaceConfig.h
index d97f20b..3fc7390 100644
--- a/nexus/InterfaceConfig.h
+++ b/nexus/InterfaceConfig.h
@@ -22,53 +22,58 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#include "IPropertyProvider.h"
-
+#include "Property.h"
 class PropertyManager;
 
-class InterfaceConfig : public IPropertyProvider {
-public:
-    static const char *PropertyNames[];
+class InterfaceConfig {
+    class InterfaceDnsProperty;
+    friend class InterfaceConfig::InterfaceDnsProperty;
 
-private:
-    char *mPropPrefix;
-    bool mUseDhcp;
+    struct {
+        IPV4AddressPropertyHelper *propIp;
+        IPV4AddressPropertyHelper *propNetmask;
+        IPV4AddressPropertyHelper *propGateway;
+        IPV4AddressPropertyHelper *propBroadcast;
+        InterfaceDnsProperty      *propDns;
+    } mStaticProperties;
+
     struct in_addr mIp;
     struct in_addr mNetmask;
     struct in_addr mGateway;
-    struct in_addr mDns1;
-    struct in_addr mDns2;
-    struct in_addr mDns3;
+    struct in_addr mBroadcast;
+    struct in_addr mDns[2];
 
 public:
-    InterfaceConfig(const char *prop_prefix);
-    InterfaceConfig(const char *prop_prefix,
-                    const char *ip, const char *nm,
-                    const char *gw, const char *dns1, const char *dns2,
-                    const char *dns3);
-
-    InterfaceConfig(const char *prop_prefix,
-                    const struct in_addr *ip,
-                    const struct in_addr *nm, const struct in_addr *gw,
-                    const struct in_addr *dns1, const struct in_addr *dns2,
-                    const struct in_addr *dns3);
-
+    InterfaceConfig(bool propertiesReadOnly);
     virtual ~InterfaceConfig();
     
     int set(const char *name, const char *value);
     const char *get(const char *name, char *buffer, size_t maxsize);
 
-    bool            getUseDhcp() const { return mUseDhcp; }
     const struct in_addr &getIp() const { return mIp; }
     const struct in_addr &getNetmask() const { return mNetmask; }
     const struct in_addr &getGateway() const { return mGateway; }
-    const struct in_addr &getDns1() const { return mDns1; }
-    const struct in_addr &getDns2() const { return mDns2; }
-    const struct in_addr &getDns3() const { return mDns3; }
+    const struct in_addr &getBroadcast() const { return mBroadcast; }
+    const struct in_addr &getDns(int idx) const { return mDns[idx]; }
+
+    void setIp(struct in_addr *addr);
+    void setNetmask(struct in_addr *addr);
+    void setGateway(struct in_addr *addr);
+    void setBroadcast(struct in_addr *addr);
+    void setDns(int idx, struct in_addr *addr);
+
+    int attachProperties(PropertyManager *pm, const char *nsName);
+    int detachProperties(PropertyManager *pm, const char *nsName);
 
 private:
-    int registerProperties();
-    int unregisterProperties();
+
+    class InterfaceDnsProperty : public IPV4AddressProperty {
+        InterfaceConfig *mCfg;
+    public:
+        InterfaceDnsProperty(InterfaceConfig *cfg, bool ro);
+        int set(int idx, struct in_addr *value);
+        int get(int idx, struct in_addr *buffer);
+    };
 };
 
 
diff --git a/nexus/LoopController.cpp b/nexus/LoopController.cpp
index 5cfb1fe..f8e01d7 100644
--- a/nexus/LoopController.cpp
+++ b/nexus/LoopController.cpp
@@ -21,14 +21,5 @@
 
 LoopController::LoopController(PropertyManager *propmngr,
                                IControllerHandler *handlers) :
-                Controller("LOOP", propmngr, handlers) {
+                Controller("loop", propmngr, handlers) {
 }
-
-int LoopController::set(const char *name, const char *value) {
-    return Controller::set(name, value);
-}
-
-const char *LoopController::get(const char *name, char *buffer, size_t maxsize) {
-    return Controller::get(name, buffer, maxsize);
-}
-
diff --git a/nexus/NetworkManager.cpp b/nexus/NetworkManager.cpp
index 7450b95..e0df409 100644
--- a/nexus/NetworkManager.cpp
+++ b/nexus/NetworkManager.cpp
@@ -24,6 +24,9 @@
 #include "NetworkManager.h"
 #include "InterfaceConfig.h"
 #include "DhcpClient.h"
+#include "DhcpState.h"
+#include "DhcpEvent.h"
+#include "ResponseCode.h"
 
 NetworkManager *NetworkManager::sInstance = NULL;
 
@@ -35,8 +38,9 @@
 
 NetworkManager::NetworkManager(PropertyManager *propMngr) {
     mBroadcaster = NULL;
-    mControllers = new ControllerCollection();
+    mControllerBindings = new ControllerBindingCollection();
     mPropMngr = propMngr;
+    mLastDhcpState = DhcpState::INIT;
     mDhcp = new DhcpClient(this);
 }
 
@@ -51,17 +55,17 @@
 }
 
 int NetworkManager::attachController(Controller *c) {
-    mControllers->push_back(c);
+    ControllerBinding *cb = new ControllerBinding(c);
+    mControllerBindings->push_back(cb);
     return 0;
 }
 
 int NetworkManager::startControllers() {
     int rc = 0;
-    ControllerCollection::iterator i;
+    ControllerBindingCollection::iterator it;
 
-    for (i = mControllers->begin(); i != mControllers->end(); ++i) {
-        int irc = (*i)->start();
-        LOGD("Controller '%s' start rc = %d", (*i)->getName(), irc);
+    for (it = mControllerBindings->begin(); it != mControllerBindings->end(); ++it) {
+        int irc = (*it)->getController()->start();
         if (irc && !rc)
             rc = irc;
     }
@@ -70,52 +74,132 @@
 
 int NetworkManager::stopControllers() {
     int rc = 0;
-    ControllerCollection::iterator i;
+    ControllerBindingCollection::iterator it;
 
-    for (i = mControllers->begin(); i != mControllers->end(); ++i) {
-        int irc = (*i)->stop();
-        LOGD("Controller '%s' stop rc = %d", (*i)->getName(), irc);
+    for (it = mControllerBindings->begin(); it != mControllerBindings->end(); ++it) {
+        int irc = (*it)->getController()->stop();
         if (irc && !rc)
             rc = irc;
     }
     return rc;
 }
 
-Controller *NetworkManager::findController(const char *name) {
-    ControllerCollection::iterator i;
-    for (i = mControllers->begin(); i != mControllers->end(); ++i) {
-        if (!strcmp((*i)->getName(), name))
-            return *i;
+NetworkManager::ControllerBinding *NetworkManager::lookupBinding(Controller *c) {
+    ControllerBindingCollection::iterator it;
+
+    for (it = mControllerBindings->begin(); it != mControllerBindings->end(); ++it) {
+        if ((*it)->getController() == c)
+            return (*it);
     }
-    LOGW("Controller '%s' not found", name);
+    errno = ENOENT;
     return NULL;
 }
 
-void NetworkManager::onInterfaceConnected(Controller *c, const InterfaceConfig *cfg) {
+Controller *NetworkManager::findController(const char *name) {
+    ControllerBindingCollection::iterator it;
+
+    for (it = mControllerBindings->begin(); it != mControllerBindings->end(); ++it) {
+        if (!strcasecmp((*it)->getController()->getName(), name))
+            return (*it)->getController();
+    }
+    errno = ENOENT;
+    return NULL;
+}
+
+void NetworkManager::onInterfaceConnected(Controller *c) {
     LOGD("Controller %s interface %s connected", c->getName(), c->getBoundInterface());
 
-    // Look up the interface
-
-    if (0) { // already started?
-    }
-
-    if (cfg) {
-        if (cfg->getUseDhcp() && mDhcp->start(c->getBoundInterface())) {
-            LOGE("DHCP start failed");
-        } else if (!cfg->getUseDhcp()) {
-            // Static configuration
-        }
-    } else {
-        LOGD("No InterfaceConfig for %s:%s - assuming self-managed",
-            c->getName(), c->getBoundInterface());
+    if (mDhcp->start(c)) {
+        LOGE("Failed to start DHCP (%s)", strerror(errno));
+        return;
     }
 }
 
-void NetworkManager::onInterfaceDisconnected(Controller *c, const char *name) {
-    LOGD("Controller %s interface %s disconnected", c->getName(), name);
+void NetworkManager::onInterfaceDisconnected(Controller *c) {
+    LOGD("Controller %s interface %s disconnected", c->getName(),
+         c->getBoundInterface());
 
-    // If we have a DHCP request out on this interface then stop it
-    if (1) {
-        mDhcp->stop();
-    }
+    mDhcp->stop();
 }
+
+void NetworkManager::onControllerSuspending(Controller *c) {
+    LOGD("Controller %s interface %s suspending", c->getName(),
+         c->getBoundInterface());
+    mDhcp->stop();
+}
+
+void NetworkManager::onControllerResumed(Controller *c) {
+    LOGD("Controller %s interface %s resumed", c->getName(),
+         c->getBoundInterface());
+}
+
+void NetworkManager::onDhcpStateChanged(Controller *c, int state) {
+    char tmp[255];
+    char tmp2[255];
+
+    LOGD("onDhcpStateChanged(%s -> %s)",
+         DhcpState::toString(mLastDhcpState, tmp, sizeof(tmp)),
+         DhcpState::toString(state, tmp2, sizeof(tmp2)));
+
+    switch(state) {
+        case DhcpState::BOUND:
+            // Refresh the 'net.xxx' for the controller
+            break;
+        case DhcpState::RENEWING:
+            break;
+        default:
+            break;
+    }
+
+    char *tmp3;
+    asprintf(&tmp3,
+             "DHCP state changed from %d (%s) -> %d (%s)", 
+             mLastDhcpState,
+             DhcpState::toString(mLastDhcpState, tmp, sizeof(tmp)),
+             state,
+             DhcpState::toString(state, tmp2, sizeof(tmp2)));
+
+    getBroadcaster()->sendBroadcast(ResponseCode::DhcpStateChange,
+                                    tmp3,
+                                    false);
+    free(tmp3);
+                          
+    mLastDhcpState = state;
+}
+
+void NetworkManager::onDhcpEvent(Controller *c, int evt) {
+    char tmp[64];
+    LOGD("onDhcpEvent(%s)", DhcpEvent::toString(evt, tmp, sizeof(tmp)));
+}
+
+void NetworkManager::onDhcpLeaseUpdated(Controller *c, struct in_addr *addr,
+                                        struct in_addr *net,
+                                        struct in_addr *brd,
+                                        struct in_addr *gw,
+                                        struct in_addr *dns1,
+                                        struct in_addr *dns2) {
+    ControllerBinding *bind = lookupBinding(c);
+
+    if (!bind->getCurrentCfg())
+        bind->setCurrentCfg(new InterfaceConfig(true));
+
+    bind->getCurrentCfg()->setIp(addr);
+    bind->getCurrentCfg()->setNetmask(net);
+    bind->getCurrentCfg()->setGateway(gw);
+    bind->getCurrentCfg()->setBroadcast(brd);
+    bind->getCurrentCfg()->setDns(0, dns1);
+    bind->getCurrentCfg()->setDns(1, dns2);
+}
+
+NetworkManager::ControllerBinding::ControllerBinding(Controller *c) :
+                mController(c) {
+}
+
+void NetworkManager::ControllerBinding::setCurrentCfg(InterfaceConfig *c) {
+    mCurrentCfg = c;
+}
+
+void NetworkManager::ControllerBinding::setBoundCfg(InterfaceConfig *c) {
+    mBoundCfg = c;
+}
+
diff --git a/nexus/NetworkManager.h b/nexus/NetworkManager.h
index 44f3417..93702ce 100644
--- a/nexus/NetworkManager.h
+++ b/nexus/NetworkManager.h
@@ -17,6 +17,7 @@
 #ifndef _NETWORKMANAGER_H
 #define _NETWORKMANAGER_H
 
+#include <utils/List.h>
 #include <sysutils/SocketListener.h>
 
 #include "Controller.h"
@@ -28,14 +29,33 @@
 class DhcpClient;
 
 class NetworkManager : public IControllerHandler, public IDhcpEventHandlers {
-private:
     static NetworkManager *sInstance;
 
+    class ControllerBinding {
+        Controller      *mController;
+        InterfaceConfig *mCurrentCfg;
+        InterfaceConfig *mBoundCfg;
+
+    public:
+        ControllerBinding(Controller *c);
+        virtual ~ControllerBinding() {}
+
+        InterfaceConfig *getCurrentCfg() { return mCurrentCfg; }
+        InterfaceConfig *getBoundCfg() { return mCurrentCfg; }
+        Controller *getController() { return mController; }
+
+        void setCurrentCfg(InterfaceConfig *cfg);
+        void setBoundCfg(InterfaceConfig *cfg);
+    };
+
+    typedef android::List<ControllerBinding *> ControllerBindingCollection;
+
 private:
-    ControllerCollection *mControllers;
-    SocketListener       *mBroadcaster;
-    PropertyManager      *mPropMngr;
-    DhcpClient           *mDhcp;
+    ControllerBindingCollection *mControllerBindings;
+    SocketListener              *mBroadcaster;
+    PropertyManager             *mPropMngr;
+    DhcpClient                  *mDhcp;
+    int                         mLastDhcpState;
 
 public:
     virtual ~NetworkManager();
@@ -57,8 +77,19 @@
     int stopControllers();
 
     NetworkManager(PropertyManager *propMngr);
+    ControllerBinding *lookupBinding(Controller *c);
 
-    void onInterfaceConnected(Controller *c, const InterfaceConfig *cfg);
-    void onInterfaceDisconnected(Controller *c, const char *name);
+    void onInterfaceConnected(Controller *c);
+    void onInterfaceDisconnected(Controller *c);
+    void onControllerSuspending(Controller *c);
+    void onControllerResumed(Controller *c);
+
+    void onDhcpStateChanged(Controller *c, int state);
+    void onDhcpEvent(Controller *c, int event);
+    void onDhcpLeaseUpdated(Controller *c,
+                            struct in_addr *addr, struct in_addr *net,
+                            struct in_addr *brd,
+                            struct in_addr *gw, struct in_addr *dns1,
+                            struct in_addr *dns2);
 };
 #endif
diff --git a/nexus/Property.cpp b/nexus/Property.cpp
new file mode 100644
index 0000000..d02769d
--- /dev/null
+++ b/nexus/Property.cpp
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.h>
+#include <netinet/in.h>
+
+#define LOG_TAG "Property"
+
+#include <cutils/log.h>
+
+#include "Property.h"
+
+Property::Property(const char *name, bool readOnly,
+                   int type, int numElements) :
+          mName(name), mReadOnly(readOnly), mType(type),
+          mNumElements(numElements) {
+    if (index(name, '.')) {
+        LOGW("Property name %s violates namespace rules", name);
+    }
+}
+
+StringProperty::StringProperty(const char *name, bool ro, int elements) :
+                Property(name, ro, Property::Type_STRING, elements) {
+}
+int StringProperty::set(int idx, int value) {
+    LOGE("Integer 'set' called on string property!");
+    errno = EINVAL;
+    return -1;
+}
+int StringProperty::set(int idx, struct in_addr *value) {
+    LOGE("IpAddr 'set' called on string property!");
+    errno = EINVAL;
+    return -1;
+}
+int StringProperty::get(int idx, int *buffer) {
+    LOGE("Integer 'get' called on string property!");
+    errno = EINVAL;
+    return -1;
+}
+int StringProperty::get(int idx, struct in_addr *buffer) {
+    LOGE("IpAddr 'get' called on string property!");
+    errno = EINVAL;
+    return -1;
+}
+
+StringPropertyHelper::StringPropertyHelper(const char *name, bool ro,
+                                           char *buffer, size_t max) :
+                      StringProperty(name, ro, 1) {
+    mBuffer = buffer;
+    mMax = max;
+}
+
+int StringPropertyHelper::set(int idx, const char *value) {
+    if (idx != 0) {
+        LOGW("Attempt to use array index on StringPropertyHelper::set");
+        errno = EINVAL;
+        return -1;
+    }
+    strncpy(mBuffer, value, mMax);
+    return 0;
+}
+
+int StringPropertyHelper::get(int idx, char *buffer, size_t max) {
+    if (idx != 0) {
+        LOGW("Attempt to use array index on StringPropertyHelper::get");
+        errno = EINVAL;
+        return -1;
+    }
+    strncpy(buffer, mBuffer, max);
+    return 0;
+}
+ 
+IntegerProperty::IntegerProperty(const char *name, bool ro, int elements) :
+                Property(name, ro, Property::Type_INTEGER, elements) {
+}
+
+int IntegerProperty::set(int idx, const char *value) {
+    LOGE("String 'set' called on integer property!");
+    errno = EINVAL;
+    return -1;
+}
+int IntegerProperty::set(int idx, struct in_addr *value) {
+    LOGE("IpAddr 'set' called on integer property!");
+    errno = EINVAL;
+    return -1;
+}
+int IntegerProperty::get(int idx, char *buffer, size_t max) {
+    LOGE("String 'get' called on integer property!");
+    errno = EINVAL;
+    return -1;
+}
+int IntegerProperty::get(int idx, struct in_addr *buffer) {
+    LOGE("IpAddr 'get' called on integer property!");
+    errno = EINVAL;
+    return -1;
+}
+
+IntegerPropertyHelper::IntegerPropertyHelper(const char *name, bool ro,
+                                             int *buffer) :
+                       IntegerProperty(name, ro, 1) {
+    mBuffer = buffer;
+}
+
+int IntegerPropertyHelper::set(int idx, int value) {
+    if (idx != 0) {
+        LOGW("Attempt to use array index on IntegerPropertyHelper::set");
+        errno = EINVAL;
+        return -1;
+    }
+    *mBuffer = value;
+    return 0;
+}
+
+int IntegerPropertyHelper::get(int idx, int *buffer) {
+    if (idx != 0) {
+        LOGW("Attempt to use array index on IntegerPropertyHelper::get");
+        errno = EINVAL;
+        return -1;
+    }
+    *buffer = *mBuffer;
+    return 0;
+}
+
+IPV4AddressProperty::IPV4AddressProperty(const char *name, bool ro, int elements) :
+                Property(name, ro, Property::Type_IPV4, elements) {
+}
+
+int IPV4AddressProperty::set(int idx, const char *value) {
+    LOGE("String 'set' called on ipv4 property!");
+    errno = EINVAL;
+    return -1;
+}
+int IPV4AddressProperty::set(int idx, int value) {
+    LOGE("Integer 'set' called on ipv4 property!");
+    errno = EINVAL;
+    return -1;
+}
+int IPV4AddressProperty::get(int idx, char *buffer, size_t max) {
+    LOGE("String 'get' called on ipv4 property!");
+    errno = EINVAL;
+    return -1;
+}
+int IPV4AddressProperty::get(int idx, int *buffer) {
+    LOGE("Integer 'get' called on ipv4 property!");
+    errno = EINVAL;
+    return -1;
+}
+
+IPV4AddressPropertyHelper::IPV4AddressPropertyHelper(const char *name, bool ro,
+                                                     struct in_addr *buffer) :
+                       IPV4AddressProperty(name, ro, 1) {
+    mBuffer = buffer;
+}
+
+int IPV4AddressPropertyHelper::set(int idx, struct in_addr *value) {
+    if (idx != 0) {
+        LOGW("Attempt to use array index on IPV4AddressPropertyHelper::set");
+        errno = EINVAL;
+        return -1;
+    }
+    memcpy(mBuffer, value, sizeof(struct in_addr));
+    return 0;
+}
+
+int IPV4AddressPropertyHelper::get(int idx, struct in_addr *buffer) {
+    if (idx != 0) {
+        LOGW("Attempt to use array index on IPV4AddressPropertyHelper::get");
+        errno = EINVAL;
+        return -1;
+    }
+    memcpy(buffer, mBuffer, sizeof(struct in_addr));
+    return 0;
+}
+
+PropertyNamespace::PropertyNamespace(const char *name) {
+    mName = strdup(name);
+    mProperties = new PropertyCollection();
+}
+
+PropertyNamespace::~PropertyNamespace() {
+    PropertyCollection::iterator it;
+    for (it = mProperties->begin(); it != mProperties->end();) {
+        delete (*it);
+        it = mProperties->erase(it);
+    }
+    delete mProperties;
+    free(mName);
+}
diff --git a/nexus/Property.h b/nexus/Property.h
index 198f722..ceea2d3 100644
--- a/nexus/Property.h
+++ b/nexus/Property.h
@@ -14,8 +14,124 @@
  * limitations under the License.
  */
 
+#ifndef _PROPERTY_H
+#define _PROPERTY_H
+
+#include <netinet/in.h>
+#include <utils/List.h>
+
 class Property {
+    const char *mName;
+    bool       mReadOnly;
+    int        mType;
+    int        mNumElements;
+
 public:
-    static const int NameMaxSize = 128;
+    static const int NameMaxSize   = 128;
     static const int ValueMaxSize  = 255;
+
+    static const int Type_STRING  = 1;
+    static const int Type_INTEGER = 2;
+    static const int Type_IPV4    = 3;
+
+    Property(const char *name, bool ro, int type, int elements);
+    virtual ~Property() {}
+
+    virtual int set(int idx, const char *value) = 0;
+    virtual int set(int idx, int value) = 0;
+    virtual int set(int idx, struct in_addr *value) = 0;
+
+    virtual int get(int idx, char *buffer, size_t max) = 0;
+    virtual int get(int idx, int *buffer) = 0;
+    virtual int get(int idx, struct in_addr *buffer) = 0;
+
+    int getType() { return mType; }
+    bool getReadOnly() { return mReadOnly; }
+    int getNumElements() { return mNumElements; }
+    const char *getName() { return mName; }
 };
+
+class StringProperty : public Property {
+public:
+    StringProperty(const char *name, bool ro, int elements);
+    virtual ~StringProperty() {}
+ 
+    virtual int set(int idx, const char *value) = 0;
+    int set(int idx, int value);
+    int set(int idx, struct in_addr *value);
+
+    virtual int get(int idx, char *buffer, size_t max) = 0;
+    int get(int idx, int *buffer);
+    int get(int idx, struct in_addr *buffer);
+};
+
+class StringPropertyHelper : public StringProperty {
+    char *mBuffer;
+    size_t mMax;
+public:
+    StringPropertyHelper(const char *name, bool ro,
+                         char *buffer, size_t max);
+    int set(int idx, const char *value);
+    int get(int idx, char *buffer, size_t max);
+};
+
+class IntegerProperty : public Property {
+public:
+    IntegerProperty(const char *name, bool ro, int elements);
+    virtual ~IntegerProperty() {}
+ 
+    int set(int idx, const char *value);
+    virtual int set(int idx, int value) = 0;
+    int set(int idx, struct in_addr *value);
+
+    int get(int idx, char *buffer, size_t max);
+    virtual int get(int idx, int *buffer) = 0;
+    int get(int idx, struct in_addr *buffer);
+};
+
+class IntegerPropertyHelper : public IntegerProperty {
+    int *mBuffer;
+public:
+    IntegerPropertyHelper(const char *name, bool ro, int *buffer);
+    int set(int idx, int value);
+    int get(int idx, int *buffer);
+};
+
+class IPV4AddressProperty : public Property {
+public:
+    IPV4AddressProperty(const char *name, bool ro, int elements);
+    virtual ~IPV4AddressProperty() {}
+ 
+    int set(int idx, const char *value);
+    int set(int idx, int value);
+    virtual int set(int idx, struct in_addr *value) = 0;
+
+    int get(int idx, char *buffer, size_t max);
+    int get(int idx, int *buffer);
+    virtual int get(int idx, struct in_addr *buffer) = 0;
+};
+
+class IPV4AddressPropertyHelper : public IPV4AddressProperty {
+    struct in_addr *mBuffer;
+public:
+    IPV4AddressPropertyHelper(const char *name, bool ro, struct in_addr *buf);
+    int set(int idx, struct in_addr *value);
+    int get(int idx, struct in_addr *buffer);
+};
+
+typedef android::List<Property *> PropertyCollection;
+
+class PropertyNamespace {
+    char         *mName;
+    PropertyCollection *mProperties;
+
+public:
+    PropertyNamespace(const char *name);
+    virtual ~PropertyNamespace();
+
+    const char *getName() { return mName; }
+    PropertyCollection *getProperties() { return mProperties; }
+};
+
+typedef android::List<PropertyNamespace *> PropertyNamespaceCollection;
+#endif
diff --git a/nexus/PropertyManager.cpp b/nexus/PropertyManager.cpp
index 6faf9b8..704b223 100644
--- a/nexus/PropertyManager.cpp
+++ b/nexus/PropertyManager.cpp
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
 #define LOG_TAG "PropertyManager"
 
 #include <cutils/log.h>
@@ -21,103 +26,255 @@
 #include "PropertyManager.h"
 
 PropertyManager::PropertyManager() {
-    mPropertyPairs = new PropertyPairCollection();
+    mNamespaces = new PropertyNamespaceCollection();
     pthread_mutex_init(&mLock, NULL);
 }
 
 PropertyManager::~PropertyManager() {
-    delete mPropertyPairs;
+    PropertyNamespaceCollection::iterator it;
+
+    for (it = mNamespaces->begin(); it != mNamespaces->end();) {
+        delete (*it);
+        it = mNamespaces->erase(it);
+    }
+    delete mNamespaces;
 }
 
-int PropertyManager::registerProperty(const char *name, IPropertyProvider *pp) {
-    PropertyPairCollection::iterator it;
+PropertyNamespace *PropertyManager::lookupNamespace_UNLOCKED(const char *ns) {
+    PropertyNamespaceCollection::iterator ns_it;
 
-//    LOGD("registerProperty(%s)", name);
-    pthread_mutex_lock(&mLock);
-    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it) {
-        if (!strcmp(name, (*it)->getName())) {
-            errno = EADDRINUSE;
-            LOGE("Failed to register property %s (%s)",
-                 name, strerror(errno));
-            pthread_mutex_unlock(&mLock);
-            return -1;
-        }
+    for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) {
+        if (!strcasecmp(ns, (*ns_it)->getName()))
+            return (*ns_it);
     }
-    mPropertyPairs->push_back(new PropertyPair(name, pp));
+    errno = ENOENT;
+    return NULL;
+}
+
+Property *PropertyManager::lookupProperty_UNLOCKED(PropertyNamespace *ns, const char *name) {
+    PropertyCollection::iterator it;
+
+    for (it = ns->getProperties()->begin();
+         it != ns->getProperties()->end(); ++it) {
+        if (!strcasecmp(name, (*it)->getName()))
+            return (*it);
+    }
+    errno = ENOENT;
+    return NULL;
+}
+
+int PropertyManager::attachProperty(const char *ns_name, Property *p) {
+    PropertyNamespace *ns;
+
+    LOGD("Attaching property %s to namespace %s", p->getName(), ns_name);
+    pthread_mutex_lock(&mLock);
+    if (!(ns = lookupNamespace_UNLOCKED(ns_name))) {
+        LOGD("Creating namespace %s", ns_name);
+        ns = new PropertyNamespace(ns_name);
+        mNamespaces->push_back(ns);
+    }
+
+    if (lookupProperty_UNLOCKED(ns, p->getName())) {
+        errno = EADDRINUSE;
+        pthread_mutex_unlock(&mLock);
+        LOGE("Failed to register property %s.%s (%s)",
+            ns_name, p->getName(), strerror(errno));
+        return -1;
+    }
+
+    ns->getProperties()->push_back(p);
     pthread_mutex_unlock(&mLock);
     return 0;
 }
 
-int PropertyManager::unregisterProperty(const char *name) {
-    PropertyPairCollection::iterator it;
+int PropertyManager::detachProperty(const char *ns_name, Property *p) {
+    PropertyNamespace *ns;
 
-//    LOGD("unregisterProperty(%s)", name);
+    LOGD("Detaching property %s from namespace %s", p->getName(), ns_name);
     pthread_mutex_lock(&mLock);
-    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it) {
-        if (!strcmp(name, (*it)->getName())) {
+    if (!(ns = lookupNamespace_UNLOCKED(ns_name))) {
+        pthread_mutex_unlock(&mLock);
+        LOGE("Namespace '%s' not found", ns_name);
+        return -1;
+    }
+
+    PropertyCollection::iterator it;
+
+    for (it = ns->getProperties()->begin();
+         it != ns->getProperties()->end(); ++it) {
+        if (!strcasecmp(p->getName(), (*it)->getName())) {
             delete ((*it));
-            mPropertyPairs->erase(it);
+            ns->getProperties()->erase(it);
             pthread_mutex_unlock(&mLock);
             return 0;
         }
     }
+
+    LOGE("Property %s.%s not found", ns_name, p->getName());
     pthread_mutex_unlock(&mLock);
     errno = ENOENT;
     return -1;
 }
 
+int PropertyManager::doSet(Property *p, int idx, const char *value) {
+
+    if (p->getReadOnly()) {
+        errno = EROFS;
+        return -1;
+    }
+
+    if (p->getType() == Property::Type_STRING) {
+        return p->set(idx, value);
+    } else if (p->getType() == Property::Type_INTEGER) {
+        int tmp;
+        errno = 0;
+        tmp = strtol(value, (char **) NULL, 10);
+        if (errno) {
+            LOGE("Failed to convert '%s' to int", value);
+            errno = EINVAL;
+            return -1;
+        }
+        return p->set(idx, tmp);
+    } else if (p->getType() == Property::Type_IPV4) {
+        struct in_addr tmp;
+        if (!inet_aton(value, &tmp)) {
+            LOGE("Failed to convert '%s' to ipv4", value);
+            errno = EINVAL;
+            return -1;
+        }
+        return p->set(idx, &tmp);
+    } else {
+        LOGE("Property '%s' has an unknown type (%d)", p->getName(),
+             p->getType());
+        errno = EINVAL;
+        return -1;
+    }
+    errno = ENOENT;
+    return -1;
+}
+
+int PropertyManager::doGet(Property *p, int idx, char *buffer, size_t max) {
+
+    if (p->getType() == Property::Type_STRING) {
+        if (p->get(idx, buffer, max)) {
+            LOGW("String property %s get failed (%s)", p->getName(),
+                 strerror(errno));
+            return -1;
+        }
+    }
+    else if (p->getType() == Property::Type_INTEGER) {
+        int tmp;
+        if (p->get(idx, &tmp)) {
+            LOGW("Integer property %s get failed (%s)", p->getName(),
+                 strerror(errno));
+            return -1;
+        }
+        snprintf(buffer, max, "%d", tmp);
+    } else if (p->getType() == Property::Type_IPV4) {
+        struct in_addr tmp;
+        if (p->get(idx, &tmp)) {
+            LOGW("IPV4 property %s get failed (%s)", p->getName(),
+                 strerror(errno));
+            return -1;
+        }
+        strncpy(buffer, inet_ntoa(tmp), max);
+    } else {
+        LOGE("Property '%s' has an unknown type (%d)", p->getName(),
+             p->getType());
+        errno = EINVAL;
+        return -1;
+    }
+    return 0;
+}
+
 /*
  * IPropertyManager methods
  */
 
 int PropertyManager::set(const char *name, const char *value) {
-    PropertyPairCollection::iterator it;
 
+    LOGD("set %s = '%s'", name, value);
     pthread_mutex_lock(&mLock);
-    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it) {
-        if (!strcmp(name, (*it)->getName())) {
-            pthread_mutex_unlock(&mLock);
-            return (*it)->getProvider()->set(name, value);
+    PropertyNamespaceCollection::iterator ns_it;
+    for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) {
+        PropertyCollection::iterator p_it;
+        for (p_it = (*ns_it)->getProperties()->begin();
+             p_it != (*ns_it)->getProperties()->end(); ++p_it) {
+            for (int i = 0; i < (*p_it)->getNumElements(); i++) {
+                char fqn[255];
+                char tmp[8];
+                sprintf(tmp, "_%d", i);
+                snprintf(fqn, sizeof(fqn), "%s.%s%s",
+                         (*ns_it)->getName(), (*p_it)->getName(),
+                         ((*p_it)->getNumElements() > 1 ? tmp : ""));
+                if (!strcasecmp(name, fqn)) {
+                    pthread_mutex_unlock(&mLock);
+                    return doSet((*p_it), i, value);
+                }
+            }
         }
     }
+
+    LOGE("Property %s not found", name);
     pthread_mutex_unlock(&mLock);
     errno = ENOENT;
     return -1;
 }
 
 const char *PropertyManager::get(const char *name, char *buffer, size_t max) {
-    PropertyPairCollection::iterator it;
-
-    memset(buffer, 0, max);
     pthread_mutex_lock(&mLock);
-    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it) {
-        if (!strcmp(name, (*it)->getName())) {
-            pthread_mutex_unlock(&mLock);
-            return (*it)->getProvider()->get(name, buffer, max);
+    PropertyNamespaceCollection::iterator ns_it;
+    for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) {
+        PropertyCollection::iterator p_it;
+        for (p_it = (*ns_it)->getProperties()->begin();
+             p_it != (*ns_it)->getProperties()->end(); ++p_it) {
+
+            for (int i = 0; i < (*p_it)->getNumElements(); i++) {
+                char fqn[255];
+                char tmp[8];
+                sprintf(tmp, "_%d", i);
+                snprintf(fqn, sizeof(fqn), "%s.%s%s",
+                         (*ns_it)->getName(), (*p_it)->getName(),
+                         ((*p_it)->getNumElements() > 1 ? tmp : ""));
+                if (!strcasecmp(name, fqn)) {
+                    pthread_mutex_unlock(&mLock);
+                    if (doGet((*p_it), i, buffer, max))
+                        return NULL;
+                    return buffer;
+                }
             }
+        }
     }
+
+    LOGE("Property %s not found", name);
     pthread_mutex_unlock(&mLock);
     errno = ENOENT;
     return NULL;
 }
 
-android::List<char *> *PropertyManager::createPropertyList() {
+android::List<char *> *PropertyManager::createPropertyList(const char *prefix) {
     android::List<char *> *c = new android::List<char *>();
 
-    PropertyPairCollection::iterator it;
-
     pthread_mutex_lock(&mLock);
-    for (it = mPropertyPairs->begin(); it != mPropertyPairs->end(); ++it)
-         c->push_back(strdup((*it)->getName()));
+    PropertyNamespaceCollection::iterator ns_it;
+    for (ns_it = mNamespaces->begin(); ns_it != mNamespaces->end(); ++ns_it) {
+        PropertyCollection::iterator p_it;
+        for (p_it = (*ns_it)->getProperties()->begin();
+             p_it != (*ns_it)->getProperties()->end(); ++p_it) {
+            for (int i = 0; i < (*p_it)->getNumElements(); i++) {
+                char fqn[255];
+                char tmp[8];
+                sprintf(tmp, "_%d", i);
+                snprintf(fqn, sizeof(fqn), "%s.%s%s",
+                         (*ns_it)->getName(), (*p_it)->getName(),
+                         ((*p_it)->getNumElements() > 1 ? tmp : ""));
+                if (!prefix ||
+                    (prefix && !strncasecmp(fqn, prefix, strlen(prefix)))) {
+                    c->push_back(strdup(fqn));
+                }
+            }
+        }
+    }
     pthread_mutex_unlock(&mLock);
     return c;
 }
-
-PropertyPair::PropertyPair(const char *name, IPropertyProvider *pp) {
-    mName = strdup(name);
-    mPp = pp;
-}
-
-PropertyPair::~PropertyPair() {
-    free(mName);
-}
diff --git a/nexus/PropertyManager.h b/nexus/PropertyManager.h
index 10d0b2a..af56a9c 100644
--- a/nexus/PropertyManager.h
+++ b/nexus/PropertyManager.h
@@ -22,36 +22,28 @@
 
 #include <utils/List.h>
 
-#include "IPropertyProvider.h"
-
-class PropertyPair {
-private:
-    char *mName;
-    IPropertyProvider *mPp;
- 
-public:
-    PropertyPair(const char *name, IPropertyProvider *pp);
-    virtual ~PropertyPair();
-
-    const char *getName() { return mName; }
-    IPropertyProvider *getProvider() { return mPp; }
-};
-
-typedef android::List<PropertyPair *> PropertyPairCollection;
+#include "Property.h"
 
 class PropertyManager {
-    PropertyPairCollection *mPropertyPairs;
-    pthread_mutex_t         mLock;
+    PropertyNamespaceCollection *mNamespaces;
+    pthread_mutex_t    mLock;
 
 public:
     PropertyManager();
-    virtual ~PropertyManager();   
-    int registerProperty(const char *name, IPropertyProvider *pp);
-    int unregisterProperty(const char *name);
-    android::List<char *> *createPropertyList();
+    virtual ~PropertyManager();
+    int attachProperty(const char *ns, Property *p);
+    int detachProperty(const char *ns, Property *p);
+
+    android::List<char *> *createPropertyList(const char *prefix);
 
     int set(const char *name, const char *value);
     const char *get(const char *name, char *buffer, size_t max);
+
+private:
+    PropertyNamespace *lookupNamespace_UNLOCKED(const char *ns);
+    Property *lookupProperty_UNLOCKED(PropertyNamespace *ns, const char *name);
+    int doSet(Property *p, int idx, const char *value);
+    int doGet(Property *p, int idx, char *buffer, size_t max);
 };
 
 #endif
diff --git a/nexus/ErrorCode.h b/nexus/ResponseCode.h
similarity index 84%
rename from nexus/ErrorCode.h
rename to nexus/ResponseCode.h
index 414dd2c..4b6cac8 100644
--- a/nexus/ErrorCode.h
+++ b/nexus/ResponseCode.h
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-#ifndef _ERRORCODE_H
-#define _ERRORCODE_H
+#ifndef _RESPONSECODE_H
+#define _RESPONSECODE_H
 
-class ErrorCode {
+class ResponseCode {
 public:
     // 100 series - Requestion action was initiated; expect another reply
     // before proceeding with a new command.
@@ -44,5 +44,10 @@
 
     // 600 series - Unsolicited broadcasts
     static const int UnsolicitedInformational = 600;
+    static const int DhcpStateChange = 605;
+    static const int SupplicantStateChange = 610;
+    static const int ScanResultsReady = 615;
+    static const int LinkSpeedChange = 620;
+    static const int RssiChange = 625;
 };
 #endif
diff --git a/nexus/Supplicant.cpp b/nexus/Supplicant.cpp
index 9bb6bf2..6aa36e8 100644
--- a/nexus/Supplicant.cpp
+++ b/nexus/Supplicant.cpp
@@ -30,7 +30,6 @@
 #include "Supplicant.h"
 #include "SupplicantListener.h"
 #include "NetworkManager.h"
-#include "ErrorCode.h"
 #include "WifiController.h"
 #include "SupplicantStatus.h"
 
@@ -114,6 +113,29 @@
     return mServiceManager->isRunning(SUPPLICANT_SERVICE_NAME);
 }
 
+int Supplicant::sendCommand(const char *cmd, char *reply, size_t *reply_len) {
+
+    if (!mCtrl) {
+        errno = ENOTCONN;
+        return -1;
+    }
+
+//    LOGD("sendCommand(): -> '%s'", cmd);
+
+    int rc;
+    memset(reply, 0, *reply_len);
+    if ((rc = wpa_ctrl_request(mCtrl, cmd, strlen(cmd), reply, reply_len, NULL)) == -2)  {
+        errno = ETIMEDOUT;
+        return -1;
+    } else if (rc < 0 || !strncmp(reply, "FAIL", 4)) {
+        strcpy(reply, "FAIL");
+        errno = EIO;
+        return -1;
+    }
+
+ //   LOGD("sendCommand(): <- '%s'", reply);
+    return 0;
+}
 SupplicantStatus *Supplicant::getStatus() {
     char *reply;
     size_t len = 4096;
@@ -162,6 +184,7 @@
         return -1;
     }
 
+    PropertyManager *pm = NetworkManager::Instance()->getPropMngr();
     pthread_mutex_lock(&mNetworksLock);
 
     int num_added = 0;
@@ -182,7 +205,9 @@
             delete new_wn;
         } else {
             num_added++;
-            new_wn->registerProperties();
+            char new_ns[20];
+            snprintf(new_ns, sizeof(new_ns), "wifi.net.%d", new_wn->getNetworkId());
+            new_wn->attachProperties(pm, new_ns);
             mNetworks->push_back(new_wn);
             if (new_wn->refresh()) {
                 LOGW("Unable to refresh network id %d (%s)",
@@ -198,7 +223,9 @@
         for (i = mNetworks->begin(); i != mNetworks->end(); ++i) {
             if (0) {
                 num_removed++;
-                (*i)->unregisterProperties();
+                char del_ns[20];
+                snprintf(del_ns, sizeof(del_ns), "wifi.net.%d", (*i)->getNetworkId());
+                (*i)->detachProperties(pm, del_ns);
                 delete (*i);
                 i = mNetworks->erase(i);
             }
@@ -247,31 +274,7 @@
     return 0;
 }
 
-int Supplicant::sendCommand(const char *cmd, char *reply, size_t *reply_len)
-{
-    if (!mCtrl) {
-        errno = ENOTCONN;
-        return -1;
-    }
-
-//    LOGD("sendCommand(): -> '%s'", cmd);
-
-    int rc;
-    memset(reply, 0, *reply_len);
-    if ((rc = wpa_ctrl_request(mCtrl, cmd, strlen(cmd), reply, reply_len, NULL)) == -2)  {
-        errno = ETIMEDOUT;
-        return -1;
-    } else if (rc < 0 || !strncmp(reply, "FAIL", 4)) {
-        strcpy(reply, "FAIL");
-        errno = EIO;
-        return -1;
-    }
-
-//   LOGD("sendCommand(): <- '%s'", reply);
-    return 0;
-}
-
-int Supplicant::triggerScan(bool active) {
+int Supplicant::setScanMode(bool active) {
     char reply[255];
     size_t len = sizeof(reply);
 
@@ -281,10 +284,88 @@
              strerror(errno));
         return -1;
     }
-    len = sizeof(reply);
+    return 0;
+}
+
+int Supplicant::triggerScan() {
+    char reply[255];
+    size_t len = sizeof(reply);
 
     if (sendCommand("SCAN", reply, &len)) {
-        LOGW("triggerScan(%d): Error initiating scan", active);
+        LOGW("triggerScan(): Error initiating scan");
+        return -1;
+    }
+    return 0;
+}
+
+int Supplicant::getRssi(int *buffer) {
+    char reply[64];
+    size_t len = sizeof(reply);
+
+    if (sendCommand("DRIVER RSSI", reply, &len)) {
+        LOGW("Failed to get RSSI (%s)", strerror(errno));
+        return -1;
+    }
+
+    char *next = reply;
+    char *s;
+    for (int i = 0; i < 3; i++) {
+        if (!(s = strsep(&next, " "))) {
+            LOGE("Error parsing RSSI");
+            errno = EIO;
+            return -1;
+        }
+    }
+    *buffer = atoi(s);
+    return 0;
+}
+
+int Supplicant::getLinkSpeed() {
+    char reply[64];
+    size_t len = sizeof(reply);
+
+    if (sendCommand("DRIVER LINKSPEED", reply, &len)) {
+        LOGW("Failed to get LINKSPEED (%s)", strerror(errno));
+        return -1;
+    }
+
+    char *next = reply;
+    char *s;
+
+    if (!(s = strsep(&next, " "))) {
+        LOGE("Error parsing LINKSPEED");
+        errno = EIO;
+        return -1;
+    }
+
+    if (!(s = strsep(&next, " "))) {
+        LOGE("Error parsing LINKSPEED");
+        errno = EIO;
+        return -1;
+    }
+    return atoi(s);
+}
+
+int Supplicant::stopDriver() {
+    char reply[64];
+    size_t len = sizeof(reply);
+
+    LOGD("stopDriver()");
+
+    if (sendCommand("DRIVER STOP", reply, &len)) {
+        LOGW("Failed to stop driver (%s)", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+int Supplicant::startDriver() {
+    char reply[64];
+    size_t len = sizeof(reply);
+
+    LOGD("startDriver()");
+    if (sendCommand("DRIVER START", reply, &len)) {
+        LOGW("Failed to start driver (%s)", strerror(errno));
         return -1;
     }
     return 0;
@@ -301,7 +382,11 @@
         reply[strlen(reply) -1] = '\0';
 
     WifiNetwork *wn = new WifiNetwork(mController, this, atoi(reply));
+    PropertyManager *pm = NetworkManager::Instance()->getPropMngr();
     pthread_mutex_lock(&mNetworksLock);
+    char new_ns[20];
+    snprintf(new_ns, sizeof(new_ns), "wifi.net.%d", wn->getNetworkId());
+    wn->attachProperties(pm, new_ns);
     mNetworks->push_back(wn);
     pthread_mutex_unlock(&mNetworksLock);
     return wn;
@@ -411,8 +496,9 @@
     char reply[255];
     size_t len = sizeof(reply) -1;
 
+    LOGD("netid %d, var '%s' = '%s'", networkId, var, val);
     char *tmp;
-    asprintf(&tmp, "SET_NETWORK %d %s \"%s\"", networkId, var, val);
+    asprintf(&tmp, "SET_NETWORK %d %s %s", networkId, var, val);
     if (sendCommand(tmp, reply, &len)) {
         free(tmp);
         return -1;
@@ -457,6 +543,95 @@
     return 0;
 }
 
+int Supplicant::enablePacketFilter() {
+    char req[128];
+    char reply[16];
+    size_t len;
+    int i;
+    
+    for (i = 0; i <=3; i++) {
+        snprintf(req, sizeof(req), "DRIVER RXFILTER-ADD %d", i);
+        len = sizeof(reply);
+        if (sendCommand(req, reply, &len))
+            return -1;
+    }
+
+    len = sizeof(reply);
+    if (sendCommand("DRIVER RXFILTER-START", reply, &len))
+        return -1;
+    return 0;
+}
+  
+int Supplicant::disablePacketFilter() {
+    char req[128];
+    char reply[16];
+    size_t len;
+    int i;
+    
+    len = sizeof(reply);
+    if (sendCommand("DRIVER RXFILTER-STOP", reply, &len))
+        return -1;
+
+    for (i = 3; i >=0; i--) {
+        snprintf(req, sizeof(req), "DRIVER RXFILTER-REMOVE %d", i);
+        len = sizeof(reply);
+        if (sendCommand(req, reply, &len))
+            return -1;
+    }
+    return 0;
+}
+
+int Supplicant::enableBluetoothCoexistenceScan() {
+    char req[128];
+    char reply[16];
+    size_t len;
+    int i;
+    
+    len = sizeof(reply);
+    if (sendCommand("DRIVER BTCOEXSCAN-START", reply, &len))
+        return -1;
+    return 0;
+}
+
+int Supplicant::disableBluetoothCoexistenceScan() {
+    char req[128];
+    char reply[16];
+    size_t len;
+    int i;
+    
+    len = sizeof(reply);
+    if (sendCommand("DRIVER BTCOEXSCAN-STOP", reply, &len))
+        return -1;
+    return 0;
+}
+
+int Supplicant::setBluetoothCoexistenceMode(int mode) {
+    char req[64];
+
+    sprintf(req, "DRIVER BTCOEXMODE %d", mode);
+
+    char reply[16];
+    size_t len = sizeof(reply) -1;
+
+    if (sendCommand(req, reply, &len))
+        return -1;
+    return 0;
+}
+
+int Supplicant::setApScanMode(int mode) {
+    char req[64];
+
+//    LOGD("setApScanMode(%d)", mode);
+    sprintf(req, "AP_SCAN %d", mode);
+
+    char reply[16];
+    size_t len = sizeof(reply) -1;
+
+    if (sendCommand(req, reply, &len))
+        return -1;
+    return 0;
+}
+
 
 int Supplicant::retrieveInterfaceName() {
     char reply[255];
@@ -469,3 +644,34 @@
     mInterfaceName = strdup(reply);
     return 0;
 }
+
+int Supplicant::reconnect() {
+    char req[128];
+    char reply[16];
+    size_t len;
+    int i;
+    
+    len = sizeof(reply);
+    if (sendCommand("RECONNECT", reply, &len))
+        return -1;
+    return 0;
+}
+
+int Supplicant::disconnect() {
+    char req[128];
+    char reply[16];
+    size_t len;
+    int i;
+    
+    len = sizeof(reply);
+    if (sendCommand("DISCONNECT", reply, &len))
+        return -1;
+    return 0;
+}
+
+int Supplicant::getNetworkCount() {
+    pthread_mutex_lock(&mNetworksLock);
+    int cnt = mNetworks->size();
+    pthread_mutex_unlock(&mNetworksLock);
+    return cnt;
+}
diff --git a/nexus/Supplicant.h b/nexus/Supplicant.h
index 3efbe4c..3900f4f 100644
--- a/nexus/Supplicant.h
+++ b/nexus/Supplicant.h
@@ -30,7 +30,6 @@
 #include "ISupplicantEventHandler.h"
 
 class Supplicant {
-private:
     struct wpa_ctrl      *mCtrl;
     struct wpa_ctrl      *mMonitor;
     SupplicantListener   *mListener;
@@ -50,7 +49,8 @@
     int stop();
     bool isStarted();
 
-    int triggerScan(bool active);
+    int setScanMode(bool active);
+    int triggerScan();
 
     WifiNetwork *createNetwork();
     WifiNetwork *lookupNetwork(int networkId);
@@ -63,6 +63,21 @@
                               size_t max);
     int enableNetwork(int networkId, bool enabled);
 
+    int disconnect();
+    int reconnect();
+    int reassociate();
+    int setApScanMode(int mode);
+    int enablePacketFilter();
+    int disablePacketFilter();
+    int setBluetoothCoexistenceMode(int mode);
+    int enableBluetoothCoexistenceScan();
+    int disableBluetoothCoexistenceScan();
+    int stopDriver();
+    int startDriver();
+    int getRssi(int *buffer);
+    int getLinkSpeed();
+    int getNetworkCount();
+
     SupplicantStatus *getStatus();
 
     Controller *getController() { return (Controller *) mController; }
diff --git a/nexus/SupplicantState.cpp b/nexus/SupplicantState.cpp
index a16d370..2815430 100644
--- a/nexus/SupplicantState.cpp
+++ b/nexus/SupplicantState.cpp
@@ -23,25 +23,25 @@
 
 char *SupplicantState::toString(int val, char *buffer, int max) {
     if (val == SupplicantState::UNKNOWN)
-        strncpy(buffer, "Unknown", max);
+        strncpy(buffer, "UNKNOWN", max);
     else if (val == SupplicantState::DISCONNECTED)
-        strncpy(buffer, "Disconnected", max);
+        strncpy(buffer, "DISCONNECTED", max);
     else if (val == SupplicantState::INACTIVE)
-        strncpy(buffer, "Inactive", max);
+        strncpy(buffer, "INACTIVE", max);
     else if (val == SupplicantState::SCANNING)
-        strncpy(buffer, "Scanning", max);
+        strncpy(buffer, "SCANNING", max);
     else if (val == SupplicantState::ASSOCIATING)
-        strncpy(buffer, "Associating", max);
+        strncpy(buffer, "ASSOCIATING", max);
     else if (val == SupplicantState::ASSOCIATED)
-        strncpy(buffer, "Associated", max);
+        strncpy(buffer, "ASSOCIATED", max);
     else if (val == SupplicantState::FOURWAY_HANDSHAKE)
-        strncpy(buffer, "Fourway Handshake", max);
+        strncpy(buffer, "FOURWAY_HANDSHAKE", max);
     else if (val == SupplicantState::GROUP_HANDSHAKE)
-        strncpy(buffer, "Group Handshake", max);
+        strncpy(buffer, "GROUP_HANDSHAKE", max);
     else if (val == SupplicantState::COMPLETED)
-        strncpy(buffer, "Completed", max);
+        strncpy(buffer, "COMPLETED", max);
     else if (val == SupplicantState::IDLE)
-        strncpy(buffer, "Idle", max);
+        strncpy(buffer, "IDLE", max);
     else
         strncpy(buffer, "(internal error)", max);
 
diff --git a/nexus/TiwlanWifiController.cpp b/nexus/TiwlanWifiController.cpp
index 61535c3..016c790 100644
--- a/nexus/TiwlanWifiController.cpp
+++ b/nexus/TiwlanWifiController.cpp
@@ -51,6 +51,7 @@
 int TiwlanWifiController::powerDown() {
     if (mEventListener) {
         delete mEventListener;
+        shutdown(mListenerSock, SHUT_RDWR);
         close(mListenerSock);
         mListenerSock = -1;
         mEventListener = NULL;
@@ -77,7 +78,8 @@
                 LOGD("Firmware loaded OK");
 
                 if (startDriverEventListener()) {
-                    LOGW("Failed to start driver event listener");
+                    LOGW("Failed to start driver event listener (%s)",
+                         strerror(errno));
                 }
 
                 return 0;
@@ -95,32 +97,48 @@
 
 int TiwlanWifiController::startDriverEventListener() {
     struct sockaddr_in addr;
-    int s;
 
-    if ((s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+    if (mListenerSock != -1) {
+        LOGE("Listener already started!");
+        errno = EBUSY;
         return -1;
+    }
+
+    if ((mListenerSock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+        LOGE("socket failed (%s)", strerror(errno));
+        return -1;
+    }
 
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
     addr.sin_addr.s_addr = htonl(INADDR_ANY);
     addr.sin_port = htons(TI_DRIVER_MSG_PORT);
 
-    if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-        close(s);
-        return -1;
+    if (bind(mListenerSock,
+             (struct sockaddr *) &addr,
+             sizeof(addr)) < 0) {
+        LOGE("bind failed (%s)", strerror(errno));
+        goto out_err;
     }
 
-    mEventListener = new TiwlanEventListener(s);
+    mEventListener = new TiwlanEventListener(mListenerSock);
 
     if (mEventListener->startListener()) {
         LOGE("Error starting driver listener (%s)", strerror(errno));
+        goto out_err;
+    }
+    return 0;
+out_err:
+    if (mEventListener) {
         delete mEventListener;
         mEventListener = NULL;
-        close(s);
-        return -1;
     }
-    mListenerSock = s;
-    return 0;
+    if (mListenerSock != -1) {
+        shutdown(mListenerSock, SHUT_RDWR);
+        close(mListenerSock);
+        mListenerSock = -1;
+    }
+    return -1;
 }
 
 bool TiwlanWifiController::isFirmwareLoaded() {
diff --git a/nexus/VpnController.cpp b/nexus/VpnController.cpp
index add4dc3..015710f 100644
--- a/nexus/VpnController.cpp
+++ b/nexus/VpnController.cpp
@@ -27,56 +27,66 @@
 
 VpnController::VpnController(PropertyManager *propmngr,
                              IControllerHandler *handlers) :
-               Controller("VPN", propmngr, handlers) {
+               Controller("vpn", propmngr, handlers) {
     mEnabled = false;
+
+    mStaticProperties.propEnabled = new VpnEnabledProperty(this);
+    mDynamicProperties.propGateway = new IPV4AddressPropertyHelper("Gateway",
+                                                                   false,
+                                                                   &mGateway);
 }
 
 int VpnController::start() {
-    mPropMngr->registerProperty("vpn.enabled", this);
+    mPropMngr->attachProperty("vpn", mStaticProperties.propEnabled);
     return 0;
 }
 
 int VpnController::stop() {
-    mPropMngr->unregisterProperty("vpn.enabled");
+    mPropMngr->detachProperty("vpn", mStaticProperties.propEnabled);
     return 0;
 }
 
-int VpnController::set(const char *name, const char *value) {
-    if (!strcmp(name, "vpn.enabled")) {
-        int en = atoi(value);
-        int rc;
-
-        if (en == mEnabled)
-            return 0;
-        rc = (en ? enable() : disable());
-
-        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)) {
-            errno = EINVAL;
-            return -1;
-        }
-        return 0;
-    }
-
-    return Controller::set(name, value);
+VpnController::VpnIntegerProperty::VpnIntegerProperty(VpnController *c,
+                                                      const char *name,
+                                                      bool ro,
+                                                      int elements) :
+                IntegerProperty(name, ro, elements) {
+    mVc = c;
 }
 
-const char *VpnController::get(const char *name, char *buffer, size_t maxsize) {
-    if (!strcmp(name, "vpn.enabled")) {
-        snprintf(buffer, maxsize, "%d", mEnabled);
-        return buffer;
-    } if (!strcmp(name, "vpn.gateway")) {
-        snprintf(buffer, maxsize, "%s", inet_ntoa(mVpnGateway));
-        return buffer;
-    }
+VpnController::VpnStringProperty::VpnStringProperty(VpnController *c,
+                                                    const char *name,
+                                                    bool ro, int elements) :
+                StringProperty(name, ro, elements) {
+    mVc = c;
+}
 
-    return Controller::get(name, buffer, maxsize);
+VpnController::VpnIPV4AddressProperty::VpnIPV4AddressProperty(VpnController *c,
+                                                              const char *name,
+                                                              bool ro, int elements) :
+                IPV4AddressProperty(name, ro, elements) {
+    mVc = c;
+}
+
+VpnController::VpnEnabledProperty::VpnEnabledProperty(VpnController *c) :
+                VpnIntegerProperty(c, "Enabled", false, 1) {
+}
+int VpnController::VpnEnabledProperty::get(int idx, int *buffer) {
+    *buffer = mVc->mEnabled;
+    return 0;
+}
+int VpnController::VpnEnabledProperty::set(int idx, int value) {
+    int rc;
+    if (!value) {
+        mVc->mPropMngr->detachProperty("vpn", mVc->mDynamicProperties.propGateway);
+        rc = mVc->disable();
+    } else {
+        rc = mVc->enable();
+        if (!rc) {
+            mVc->mPropMngr->attachProperty("vpn", mVc->mDynamicProperties.propGateway);
+        }
+    }
+    if (!rc)
+        mVc->mEnabled = value;
+    return rc;
 }
diff --git a/nexus/VpnController.h b/nexus/VpnController.h
index 1af4d9f..4bd86b5 100644
--- a/nexus/VpnController.h
+++ b/nexus/VpnController.h
@@ -24,11 +24,63 @@
 class IControllerHandler;
 
 class VpnController : public Controller {
+    class VpnIntegerProperty : public IntegerProperty {
+    protected:
+        VpnController *mVc;
+    public:
+        VpnIntegerProperty(VpnController *c, const char *name, bool ro,
+                            int elements);
+        virtual ~VpnIntegerProperty() {}
+        virtual int set(int idx, int value) = 0;
+        virtual int get(int idx, int *buffer) = 0;
+    };
+    friend class VpnController::VpnIntegerProperty;
+
+    class VpnStringProperty : public StringProperty {
+    protected:
+        VpnController *mVc;
+    public:
+        VpnStringProperty(VpnController *c, const char *name, bool ro,
+                            int elements);
+        virtual ~VpnStringProperty() {}
+        virtual int set(int idx, const char *value) = 0;
+        virtual int get(int idx, char *buffer, size_t max) = 0;
+    };
+    friend class VpnController::VpnStringProperty;
+
+    class VpnIPV4AddressProperty : public IPV4AddressProperty {
+    protected:
+        VpnController *mVc;
+    public:
+        VpnIPV4AddressProperty(VpnController *c, const char *name, bool ro,
+                          int elements);
+        virtual ~VpnIPV4AddressProperty() {}
+        virtual int set(int idx, struct in_addr *value) = 0;
+        virtual int get(int idx, struct in_addr *buffer) = 0;
+    };
+    friend class VpnController::VpnIPV4AddressProperty;
+
+    class VpnEnabledProperty : public VpnIntegerProperty {
+    public:
+        VpnEnabledProperty(VpnController *c);
+        virtual ~VpnEnabledProperty() {};
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
     bool           mEnabled;
     /*
      * Gateway of the VPN server to connect to
      */
-    struct in_addr mVpnGateway;
+    struct in_addr mGateway;
+
+    struct {
+        VpnEnabledProperty *propEnabled;
+    } mStaticProperties;
+
+    struct {
+        IPV4AddressPropertyHelper *propGateway;
+    } mDynamicProperties;
 
 public:
     VpnController(PropertyManager *propmngr, IControllerHandler *handlers);
@@ -37,13 +89,9 @@
     virtual int start();
     virtual int stop();
 
-    virtual int set(const char *name, const char *value);
-    virtual const char *get(const char *name, char *buffer, size_t maxlen);
-
 protected:
     virtual int enable() = 0;
     virtual int disable() = 0;
-
 };
 
 #endif
diff --git a/nexus/WifiController.cpp b/nexus/WifiController.cpp
index ef5ecae..c218c30 100644
--- a/nexus/WifiController.cpp
+++ b/nexus/WifiController.cpp
@@ -23,9 +23,8 @@
 
 #include "Supplicant.h"
 #include "WifiController.h"
-#include "WifiScanner.h"
 #include "NetworkManager.h"
-#include "ErrorCode.h"
+#include "ResponseCode.h"
 #include "WifiNetwork.h"
 #include "ISupplicantEventHandler.h"
 #include "SupplicantState.h"
@@ -37,11 +36,12 @@
 #include "SupplicantStateChangeEvent.h"
 #include "SupplicantConnectionTimeoutEvent.h"
 #include "SupplicantDisconnectedEvent.h"
+#include "WifiStatusPoller.h"
 
 WifiController::WifiController(PropertyManager *mPropMngr,
                                IControllerHandler *handlers,
                                char *modpath, char *modname, char *modargs) :
-                Controller("WIFI", mPropMngr, handlers) {
+                Controller("wifi", mPropMngr, handlers) {
     strncpy(mModulePath, modpath, sizeof(mModulePath));
     strncpy(mModuleName, modname, sizeof(mModuleName));
     strncpy(mModuleArgs, modargs, sizeof(mModuleArgs));
@@ -49,22 +49,59 @@
     mLatestScanResults = new ScanResultCollection();
     pthread_mutex_init(&mLatestScanResultsLock, NULL);
 
-    mSupplicant = new Supplicant(this, this);
-    mScanner = new WifiScanner(mSupplicant, 10);
-    mCurrentScanMode = 0;
+    pthread_mutex_init(&mLock, NULL);
 
+    mSupplicant = new Supplicant(this, this);
+    mActiveScan = false;
     mEnabled = false;
+    mScanOnly = false;
+    mPacketFilter = false;
+    mBluetoothCoexScan = false;
+    mBluetoothCoexMode = 0;
+    mCurrentlyConnectedNetworkId = -1;
+    mStatusPoller = new WifiStatusPoller(this);
+    mRssiEventThreshold = 5;
+    mLastLinkSpeed = 0;
 
     mSupplicantState = SupplicantState::UNKNOWN;
+
+    mStaticProperties.propEnabled = new WifiEnabledProperty(this);
+    mStaticProperties.propScanOnly = new WifiScanOnlyProperty(this);
+    mStaticProperties.propAllowedChannels = new WifiAllowedChannelsProperty(this);
+
+    mStaticProperties.propRssiEventThreshold =
+            new IntegerPropertyHelper("RssiEventThreshold", false, &mRssiEventThreshold);
+
+    mDynamicProperties.propSupplicantState = new WifiSupplicantStateProperty(this);
+    mDynamicProperties.propActiveScan = new WifiActiveScanProperty(this);
+    mDynamicProperties.propInterface = new WifiInterfaceProperty(this);
+    mDynamicProperties.propSearching = new WifiSearchingProperty(this);
+    mDynamicProperties.propPacketFilter = new WifiPacketFilterProperty(this);
+    mDynamicProperties.propBluetoothCoexScan = new WifiBluetoothCoexScanProperty(this);
+    mDynamicProperties.propBluetoothCoexMode = new WifiBluetoothCoexModeProperty(this);
+    mDynamicProperties.propCurrentNetwork = new WifiCurrentNetworkProperty(this);
+
+    mDynamicProperties.propRssi = new IntegerPropertyHelper("Rssi", true, &mLastRssi);
+    mDynamicProperties.propLinkSpeed = new IntegerPropertyHelper("LinkSpeed", true, &mLastLinkSpeed);
+
+    mDynamicProperties.propSuspended = new WifiSuspendedProperty(this);
+    mDynamicProperties.propNetCount = new WifiNetCountProperty(this);
+    mDynamicProperties.propTriggerScan = new WifiTriggerScanProperty(this);
 }
 
 int WifiController::start() {
-    mPropMngr->registerProperty("wifi.enabled", this);
+    mPropMngr->attachProperty("wifi", mStaticProperties.propEnabled);
+    mPropMngr->attachProperty("wifi", mStaticProperties.propScanOnly);
+    mPropMngr->attachProperty("wifi", mStaticProperties.propAllowedChannels);
+    mPropMngr->attachProperty("wifi", mStaticProperties.propRssiEventThreshold);
     return 0;
 }
 
 int WifiController::stop() {
-    mPropMngr->unregisterProperty("wifi.enabled");
+    mPropMngr->detachProperty("wifi", mStaticProperties.propEnabled);
+    mPropMngr->detachProperty("wifi", mStaticProperties.propScanOnly);
+    mPropMngr->detachProperty("wifi", mStaticProperties.propAllowedChannels);
+    mPropMngr->detachProperty("wifi", mStaticProperties.propRssiEventThreshold);
     return 0;
 }
 
@@ -114,9 +151,21 @@
     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);
+    LOGW("TODO: Set # of allowed regulatory channels!");
+
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propSupplicantState);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propActiveScan);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propInterface);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propSearching);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propPacketFilter);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propBluetoothCoexScan);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propBluetoothCoexMode);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propCurrentNetwork);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propRssi);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propLinkSpeed);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propSuspended);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propNetCount);
+    mPropMngr->attachProperty("wifi", mDynamicProperties.propTriggerScan);
 
     LOGI("Enabled successfully");
     return 0;
@@ -135,17 +184,87 @@
     return -1;
 }
 
+bool WifiController::getSuspended() {
+    pthread_mutex_lock(&mLock);
+    bool r = mSuspended;
+    pthread_mutex_unlock(&mLock);
+    return r;
+}
+
+int WifiController::setSuspend(bool suspend) {
+
+    pthread_mutex_lock(&mLock);
+    if (suspend == mSuspended) {
+        LOGW("Suspended state already = %d", suspend);
+        pthread_mutex_unlock(&mLock);
+        return 0;
+    }
+
+    if (suspend) {
+        mHandlers->onControllerSuspending(this);
+
+        char tmp[80];
+        LOGD("Suspending from supplicant state %s",
+             SupplicantState::toString(mSupplicantState,
+                                       tmp,
+                                       sizeof(tmp)));
+
+        if (mSupplicantState != SupplicantState::IDLE) {
+            LOGD("Forcing Supplicant disconnect");
+            if (mSupplicant->disconnect()) {
+                LOGW("Error disconnecting (%s)", strerror(errno));
+            }
+        }
+
+        LOGD("Stopping Supplicant driver");
+        if (mSupplicant->stopDriver()) {
+            LOGE("Error stopping driver (%s)", strerror(errno));
+            pthread_mutex_unlock(&mLock);
+            return -1;
+        }
+    } else {
+        LOGD("Resuming");
+
+        if (mSupplicant->startDriver()) {
+            LOGE("Error resuming driver (%s)", strerror(errno));
+            pthread_mutex_unlock(&mLock);
+            return -1;
+        }
+        // XXX: set regulatory max channels 
+        if (mScanOnly)
+            mSupplicant->triggerScan();
+        else
+            mSupplicant->reconnect();
+
+        mHandlers->onControllerResumed(this);
+    }
+
+    mSuspended = suspend;
+    pthread_mutex_unlock(&mLock);
+    LOGD("Suspend / Resume completed");
+    return 0;
+}
+
 void WifiController::sendStatusBroadcast(const char *msg) {
     NetworkManager::Instance()->
                     getBroadcaster()->
-                    sendBroadcast(ErrorCode::UnsolicitedInformational, msg, false);
+                    sendBroadcast(ResponseCode::UnsolicitedInformational, msg, false);
 }
 
 int WifiController::disable() {
 
-    mPropMngr->unregisterProperty("wifi.scanmode");
-    mPropMngr->unregisterProperty("wifi.supplicant.state");
-    mPropMngr->unregisterProperty("wifi.scanmode");
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propSupplicantState);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propActiveScan);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propInterface);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propSearching);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propPacketFilter);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propBluetoothCoexScan);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propBluetoothCoexMode);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propCurrentNetwork);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propRssi);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propLinkSpeed);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propSuspended);
+    mPropMngr->detachProperty("wifi", mDynamicProperties.propNetCount);
 
     if (mSupplicant->isStarted()) {
         sendStatusBroadcast("Stopping WPA Supplicant");
@@ -178,35 +297,61 @@
     return 0;
 }
 
-int WifiController::setScanMode(uint32_t mode) {
-    int rc = 0;
+int WifiController::triggerScan() {
+    pthread_mutex_lock(&mLock);
+    if (verifyNotSuspended()) {
+        pthread_mutex_unlock(&mLock);
+        return -1;
+    }
 
-    if (mCurrentScanMode == mode)
+    switch (mSupplicantState) {
+        case SupplicantState::DISCONNECTED:
+        case SupplicantState::INACTIVE:
+        case SupplicantState::SCANNING:
+        case SupplicantState::IDLE:
+            break;
+        default:
+            // Switch to scan only mode
+            mSupplicant->setApScanMode(2);
+            break;
+    }
+
+    int rc = mSupplicant->triggerScan();
+    pthread_mutex_unlock(&mLock);
+    return rc;
+}
+
+int WifiController::setActiveScan(bool active) {
+    pthread_mutex_lock(&mLock);
+    if (mActiveScan == active) {
+        pthread_mutex_unlock(&mLock);
         return 0;
+    }
+    mActiveScan = active;
 
-    if (!(mode & SCAN_ENABLE_MASK)) {
-        if (mCurrentScanMode & SCAN_REPEAT_MASK)
-            mScanner->stop();
-    } else if (mode & SCAN_REPEAT_MASK)
-        rc = mScanner->start(mode & SCAN_ACTIVE_MASK);
-    else
-        rc = mSupplicant->triggerScan(mode & SCAN_ACTIVE_MASK);
-
-    mCurrentScanMode = mode;
+    int rc = mSupplicant->setScanMode(active);
+    pthread_mutex_unlock(&mLock);
     return rc;
 }
 
 WifiNetwork *WifiController::createNetwork() {
+    pthread_mutex_lock(&mLock);
     WifiNetwork *wn = mSupplicant->createNetwork();
+    pthread_mutex_unlock(&mLock);
     return wn;
 }
 
 int WifiController::removeNetwork(int networkId) {
+    pthread_mutex_lock(&mLock);
     WifiNetwork *wn = mSupplicant->lookupNetwork(networkId);
 
-    if (!wn)
+    if (!wn) {
+        pthread_mutex_unlock(&mLock);
         return -1;
-    return mSupplicant->removeNetwork(wn);
+    }
+    int rc = mSupplicant->removeNetwork(wn);
+    pthread_mutex_unlock(&mLock);
+    return rc;
 }
 
 ScanResultCollection *WifiController::createScanResults() {
@@ -225,45 +370,59 @@
     return mSupplicant->createNetworkList();
 }
 
-int WifiController::set(const char *name, const char *value) {
+int WifiController::setPacketFilter(bool enable) {
     int rc;
 
-    if (!strcmp(name, "wifi.enabled")) {
-        int en = atoi(value);
+    pthread_mutex_lock(&mLock);
+    if (enable)
+        rc = mSupplicant->enablePacketFilter();
+    else
+        rc = mSupplicant->disablePacketFilter();
 
-        if (en == mEnabled)
-            return 0;
-        rc = (en ? enable() : disable());
-        if (!rc)
-            mEnabled = en;
-    } else if (!strcmp(name, "wifi.interface")) {
-        errno = EROFS;
-        return -1;
-    } else if (!strcmp(name, "wifi.scanmode"))
-        return setScanMode((uint32_t) strtoul(value, NULL, 0));
-    else if (!strcmp(name, "wifi.supplicant.state")) {
-        errno = EROFS;
-        return -1;
-    } else
-        return Controller::set(name, value);
+    if (!rc)
+        mPacketFilter = enable;
+    pthread_mutex_unlock(&mLock);
     return rc;
 }
 
-const char *WifiController::get(const char *name, char *buffer, size_t maxsize) {
+int WifiController::setBluetoothCoexistenceScan(bool enable) {
+    int rc;
 
-    if (!strcmp(name, "wifi.enabled"))
-        snprintf(buffer, maxsize, "%d", mEnabled);
-    else if (!strcmp(name, "wifi.interface")) {
-        snprintf(buffer, maxsize, "%s",
-                 (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);
+    pthread_mutex_lock(&mLock);
+
+    if (enable)
+        rc = mSupplicant->enableBluetoothCoexistenceScan();
     else
-        return Controller::get(name, buffer, maxsize);
+        rc = mSupplicant->disableBluetoothCoexistenceScan();
 
-    return buffer;
+    if (!rc)
+        mBluetoothCoexScan = enable;
+    pthread_mutex_unlock(&mLock);
+    return rc;
+}
+
+int WifiController::setScanOnly(bool scanOnly) {
+    pthread_mutex_lock(&mLock);
+    int rc = mSupplicant->setApScanMode((scanOnly ? 2 : 1));
+    if (!rc)
+        mScanOnly = scanOnly;
+    if (!mSuspended) {
+        if (scanOnly)
+            mSupplicant->disconnect();
+        else
+            mSupplicant->reconnect();
+    }
+    pthread_mutex_unlock(&mLock);
+    return rc;
+}
+
+int WifiController::setBluetoothCoexistenceMode(int mode) {
+    pthread_mutex_lock(&mLock);
+    int rc = mSupplicant->setBluetoothCoexistenceMode(mode);
+    if (!rc)
+        mBluetoothCoexMode = mode;
+    pthread_mutex_unlock(&mLock);
+    return rc;
 }
 
 void WifiController::onAssociatingEvent(SupplicantAssociatingEvent *evt) {
@@ -296,6 +455,7 @@
         return;
     }
     
+    mCurrentlyConnectedNetworkId = ss->getId();
     if (!(wn = mSupplicant->lookupNetwork(ss->getId()))) {
         LOGW("Error looking up connected network id %d (%s)",
              ss->getId(), strerror(errno));
@@ -303,7 +463,7 @@
     }
   
     delete ss;
-    mHandlers->onInterfaceConnected(this, wn->getIfaceCfg());
+    mHandlers->onInterfaceConnected(this);
 }
 
 void WifiController::onScanResultsEvent(SupplicantScanResultsEvent *evt) {
@@ -314,6 +474,10 @@
         return;
     }
 
+    mNumScanResultsSinceLastStateChange++;
+    if (mNumScanResultsSinceLastStateChange >= 3)
+        mIsSupplicantSearching = false;
+
     size_t len = 4096;
 
     if (mSupplicant->sendCommand("SCAN_RESULTS", reply, &len)) {
@@ -346,10 +510,14 @@
     while((linep = strtok_r(NULL, "\n", &linep_next)))
         mLatestScanResults->push_back(new ScanResult(linep));
 
+    // Switch handling of scan results back to normal mode
+    mSupplicant->setApScanMode(1);
+
     char *tmp;
     asprintf(&tmp, "Scan results ready (%d)", mLatestScanResults->size());
     NetworkManager::Instance()->getBroadcaster()->
-                                sendBroadcast(ErrorCode::UnsolicitedInformational, tmp, false);
+                                sendBroadcast(ResponseCode::ScanResultsReady,
+                                              tmp, false);
     free(tmp);
     pthread_mutex_unlock(&mLatestScanResultsLock);
     free(reply);
@@ -359,11 +527,35 @@
     char tmp[32];
     char tmp2[32];
     
+    if (evt->getState() == mSupplicantState)
+        return;
+
     LOGD("onStateChangeEvent(%s -> %s)", 
          SupplicantState::toString(mSupplicantState, tmp, sizeof(tmp)),
          SupplicantState::toString(evt->getState(), tmp2, sizeof(tmp2)));
 
+    if (evt->getState() != SupplicantState::SCANNING) {
+        mIsSupplicantSearching = true;
+        mNumScanResultsSinceLastStateChange = 0;
+    }
+
+    char *tmp3;
+    asprintf(&tmp3,
+             "Supplicant state changed from %d (%s) -> %d (%s)",
+             mSupplicantState, tmp, evt->getState(), tmp2);
+
     mSupplicantState = evt->getState();
+
+    if (mSupplicantState == SupplicantState::COMPLETED) {
+        mStatusPoller->start();
+    } else if (mStatusPoller->isStarted()) {
+        mStatusPoller->stop();
+    }
+
+    NetworkManager::Instance()->getBroadcaster()->
+                                sendBroadcast(ResponseCode::SupplicantStateChange,
+                                              tmp3, false);
+    free(tmp3);
 }
 
 void WifiController::onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt) {
@@ -371,7 +563,8 @@
 }
 
 void WifiController::onDisconnectedEvent(SupplicantDisconnectedEvent *evt) {
-    mHandlers->onInterfaceDisconnected(this, getBoundInterface());
+    mCurrentlyConnectedNetworkId = -1;
+    mHandlers->onInterfaceDisconnected(this);
 }
 
 #if 0
@@ -411,3 +604,216 @@
     LOGD("onDriverStateEvent(%s)", evt->getEvent());
 }
 #endif
+
+void WifiController::onStatusPollInterval() {
+    pthread_mutex_lock(&mLock);
+    int rssi;
+    if (mSupplicant->getRssi(&rssi)) {
+        LOGE("Failed to get rssi (%s)", strerror(errno));
+        pthread_mutex_unlock(&mLock);
+        return;
+    }
+
+    if (abs(mLastRssi - rssi) > mRssiEventThreshold) {
+        char *tmp3;
+        asprintf(&tmp3, "RSSI changed from %d -> %d",
+                 mLastRssi, rssi);
+        mLastRssi = rssi;
+        NetworkManager::Instance()->getBroadcaster()->
+                               sendBroadcast(ResponseCode::RssiChange,
+                                             tmp3, false);
+        free(tmp3);
+    }
+
+    int linkspeed = mSupplicant->getLinkSpeed();
+    if (linkspeed != mLastLinkSpeed) {
+        char *tmp3;
+        asprintf(&tmp3, "Link speed changed from %d -> %d",
+                 mLastLinkSpeed, linkspeed);
+        mLastLinkSpeed = linkspeed;
+        NetworkManager::Instance()->getBroadcaster()->
+                               sendBroadcast(ResponseCode::LinkSpeedChange,
+                                             tmp3, false);
+        free(tmp3);
+        
+    }
+    pthread_mutex_unlock(&mLock);
+}
+
+int WifiController::verifyNotSuspended() {
+    if (mSuspended) {
+        errno = ESHUTDOWN;
+        return -1;
+    }
+    return 0;
+}
+
+/*
+ * Property inner classes
+ */
+
+WifiController::WifiIntegerProperty::WifiIntegerProperty(WifiController *c, 
+                                                         const char *name,
+                                                         bool ro,
+                                                         int elements) :
+                IntegerProperty(name, ro, elements) {
+    mWc = c;
+}
+
+WifiController::WifiStringProperty::WifiStringProperty(WifiController *c, 
+                                                       const char *name,
+                                                       bool ro, int elements) :
+                StringProperty(name, ro, elements) {
+    mWc = c;
+}
+
+WifiController::WifiEnabledProperty::WifiEnabledProperty(WifiController *c) :
+                WifiIntegerProperty(c, "Enabled", false, 1) {
+}
+
+int WifiController::WifiEnabledProperty::get(int idx, int *buffer) {
+    *buffer = mWc->mEnabled;
+    return 0;
+}
+int WifiController::WifiEnabledProperty::set(int idx, int value) {
+    int rc = (value ? mWc->enable() : mWc->disable());
+    if (!rc)
+        mWc->mEnabled = value;
+    return rc;
+}
+
+WifiController::WifiScanOnlyProperty::WifiScanOnlyProperty(WifiController *c) :
+                WifiIntegerProperty(c, "ScanOnly", false, 1) {
+}
+int WifiController::WifiScanOnlyProperty::get(int idx, int *buffer) {
+    *buffer = mWc->mScanOnly;
+    return 0;
+}
+int WifiController::WifiScanOnlyProperty::set(int idx, int value) {
+    return mWc->setScanOnly(value == 1);
+}
+
+WifiController::WifiAllowedChannelsProperty::WifiAllowedChannelsProperty(WifiController *c) :
+                WifiIntegerProperty(c, "AllowedChannels", false, 1) {
+}
+int WifiController::WifiAllowedChannelsProperty::get(int idx, int *buffer) {
+    *buffer = mWc->mNumAllowedChannels;
+    return 0;
+}
+int WifiController::WifiAllowedChannelsProperty::set(int idx, int value) {
+    // XXX: IMPL
+    errno = ENOSYS;
+    return -1;
+}
+
+WifiController::WifiSupplicantStateProperty::WifiSupplicantStateProperty(WifiController *c) :
+                WifiStringProperty(c, "SupplicantState", true, 1) {
+}
+int WifiController::WifiSupplicantStateProperty::get(int idx, char *buffer, size_t max) {
+    if (!SupplicantState::toString(mWc->mSupplicantState, buffer, max))
+        return -1;
+    return 0;
+}
+
+WifiController::WifiActiveScanProperty::WifiActiveScanProperty(WifiController *c) :
+                WifiIntegerProperty(c, "ActiveScan", false, 1) {
+}
+int WifiController::WifiActiveScanProperty::get(int idx, int *buffer) {
+    *buffer = mWc->mActiveScan;
+    return 0;
+}
+int WifiController::WifiActiveScanProperty::set(int idx, int value) {
+    return mWc->setActiveScan(value);
+}
+
+WifiController::WifiInterfaceProperty::WifiInterfaceProperty(WifiController *c) :
+                WifiStringProperty(c, "Interface", true, 1) {
+}
+int WifiController::WifiInterfaceProperty::get(int idx, char *buffer, size_t max) {
+    strncpy(buffer, (mWc->getBoundInterface() ? mWc->getBoundInterface() : "none"), max);
+    return 0;
+}
+
+WifiController::WifiSearchingProperty::WifiSearchingProperty(WifiController *c) :
+                WifiIntegerProperty(c, "Searching", true, 1) {
+}
+int WifiController::WifiSearchingProperty::get(int idx, int *buffer) {
+    *buffer = mWc->mIsSupplicantSearching;
+    return 0;
+}
+
+WifiController::WifiPacketFilterProperty::WifiPacketFilterProperty(WifiController *c) :
+                WifiIntegerProperty(c, "PacketFilter", false, 1) {
+}
+int WifiController::WifiPacketFilterProperty::get(int idx, int *buffer) {
+    *buffer = mWc->mPacketFilter;
+    return 0;
+}
+int WifiController::WifiPacketFilterProperty::set(int idx, int value) {
+    return mWc->setPacketFilter(value);
+}
+
+WifiController::WifiBluetoothCoexScanProperty::WifiBluetoothCoexScanProperty(WifiController *c) :
+                WifiIntegerProperty(c, "BluetoothCoexScan", false, 1) {
+}
+int WifiController::WifiBluetoothCoexScanProperty::get(int idx, int *buffer) {
+    *buffer = mWc->mBluetoothCoexScan;
+    return 0;
+}
+int WifiController::WifiBluetoothCoexScanProperty::set(int idx, int value) {
+    return mWc->setBluetoothCoexistenceScan(value == 1);
+}
+
+WifiController::WifiBluetoothCoexModeProperty::WifiBluetoothCoexModeProperty(WifiController *c) :
+                WifiIntegerProperty(c, "BluetoothCoexMode", false, 1) {
+}
+int WifiController::WifiBluetoothCoexModeProperty::get(int idx, int *buffer) {
+    *buffer = mWc->mBluetoothCoexMode;
+    return 0;
+}
+int WifiController::WifiBluetoothCoexModeProperty::set(int idx, int value) {
+    return mWc->setBluetoothCoexistenceMode(value);
+}
+
+WifiController::WifiCurrentNetworkProperty::WifiCurrentNetworkProperty(WifiController *c) :
+                WifiIntegerProperty(c, "CurrentlyConnectedNetworkId", true, 1) {
+}
+int WifiController::WifiCurrentNetworkProperty::get(int idx, int *buffer) {
+    *buffer = mWc->mCurrentlyConnectedNetworkId;
+    return 0;
+}
+
+WifiController::WifiSuspendedProperty::WifiSuspendedProperty(WifiController *c) :
+                WifiIntegerProperty(c, "Suspended", false, 1) {
+}
+int WifiController::WifiSuspendedProperty::get(int idx, int *buffer) {
+    *buffer = mWc->getSuspended();
+    return 0;
+}
+int WifiController::WifiSuspendedProperty::set(int idx, int value) {
+    return mWc->setSuspend(value == 1);
+}
+
+WifiController::WifiNetCountProperty::WifiNetCountProperty(WifiController *c) :
+                WifiIntegerProperty(c, "NetCount", true, 1) {
+}
+int WifiController::WifiNetCountProperty::get(int idx, int *buffer) {
+    pthread_mutex_lock(&mWc->mLock);
+    *buffer = mWc->mSupplicant->getNetworkCount();
+    pthread_mutex_unlock(&mWc->mLock);
+    return 0;
+}
+
+WifiController::WifiTriggerScanProperty::WifiTriggerScanProperty(WifiController *c) :
+                WifiIntegerProperty(c, "TriggerScan", false, 1) {
+}
+int WifiController::WifiTriggerScanProperty::get(int idx, int *buffer) {
+    // XXX: Need action type
+    *buffer = 0;
+    return 0;
+}
+
+int WifiController::WifiTriggerScanProperty::set(int idx, int value) {
+    return mWc->triggerScan();
+}
+
diff --git a/nexus/WifiController.h b/nexus/WifiController.h
index c61d97a..b1524f6 100644
--- a/nexus/WifiController.h
+++ b/nexus/WifiController.h
@@ -23,41 +23,207 @@
 #include "ScanResult.h"
 #include "WifiNetwork.h"
 #include "ISupplicantEventHandler.h"
+#include "IWifiStatusPollerHandler.h"
 
 class NetInterface;
 class Supplicant;
-class WifiScanner;
 class SupplicantAssociatingEvent;
 class SupplicantAssociatedEvent;
 class SupplicantConnectedEvent;
 class SupplicantScanResultsEvent;
 class SupplicantStateChangeEvent;
 class SupplicantDisconnectedEvent;
+class WifiStatusPoller;
 
-class WifiController : public Controller, public ISupplicantEventHandler {
-public:
-    static const uint32_t SCAN_ENABLE_MASK       = 0x01;
-    static const uint32_t SCAN_ACTIVE_MASK       = 0x02;
-    static const uint32_t SCAN_REPEAT_MASK       = 0x04;
+class WifiController : public Controller,
+                       public ISupplicantEventHandler,
+                       public IWifiStatusPollerHandler {
 
-    static const uint32_t SCANMODE_NONE               = 0;
-    static const uint32_t SCANMODE_PASSIVE_ONESHOT    = SCAN_ENABLE_MASK;
-    static const uint32_t SCANMODE_PASSIVE_CONTINUOUS = SCAN_ENABLE_MASK | SCAN_REPEAT_MASK;
-    static const uint32_t SCANMODE_ACTIVE_ONESHOT     = SCAN_ENABLE_MASK | SCAN_ACTIVE_MASK;
-    static const uint32_t SCANMODE_ACTIVE_CONTINUOUS  = SCAN_ENABLE_MASK | SCAN_ACTIVE_MASK | SCAN_REPEAT_MASK;
+    class WifiIntegerProperty : public IntegerProperty {
+    protected:
+        WifiController *mWc;
+    public:
+        WifiIntegerProperty(WifiController *c, const char *name, bool ro, 
+                            int elements);
+        virtual ~WifiIntegerProperty() {}
+        virtual int set(int idx, int value) = 0;
+        virtual int get(int idx, int *buffer) = 0;
+    };
+    friend class WifiController::WifiIntegerProperty;
 
-private:
+    class WifiStringProperty : public StringProperty {
+    protected:
+        WifiController *mWc;
+    public:
+        WifiStringProperty(WifiController *c, const char *name, bool ro, 
+                            int elements);
+        virtual ~WifiStringProperty() {}
+        virtual int set(int idx, const char *value) = 0;
+        virtual int get(int idx, char *buffer, size_t max) = 0;
+    };
+    friend class WifiController::WifiStringProperty;
+
+    class WifiEnabledProperty : public WifiIntegerProperty {
+    public:
+        WifiEnabledProperty(WifiController *c);
+        virtual ~WifiEnabledProperty() {}
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiScanOnlyProperty : public WifiIntegerProperty {
+    public:
+        WifiScanOnlyProperty(WifiController *c);
+        virtual ~WifiScanOnlyProperty() {}
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiAllowedChannelsProperty : public WifiIntegerProperty {
+    public:
+        WifiAllowedChannelsProperty(WifiController *c);
+        virtual ~WifiAllowedChannelsProperty() {}
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiActiveScanProperty : public WifiIntegerProperty {
+    public:
+        WifiActiveScanProperty(WifiController *c);
+        virtual ~WifiActiveScanProperty() {}
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiSearchingProperty : public WifiIntegerProperty {
+    public:
+        WifiSearchingProperty(WifiController *c);
+        virtual ~WifiSearchingProperty() {}
+        int set(int idx, int value) { return -1; }
+        int get(int idx, int *buffer);
+    };
+
+    class WifiPacketFilterProperty : public WifiIntegerProperty {
+    public:
+        WifiPacketFilterProperty(WifiController *c);
+        virtual ~WifiPacketFilterProperty() {}
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiBluetoothCoexScanProperty : public WifiIntegerProperty {
+    public:
+        WifiBluetoothCoexScanProperty(WifiController *c);
+        virtual ~WifiBluetoothCoexScanProperty() {}
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiBluetoothCoexModeProperty : public WifiIntegerProperty {
+    public:
+        WifiBluetoothCoexModeProperty(WifiController *c);
+        virtual ~WifiBluetoothCoexModeProperty() {}
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiCurrentNetworkProperty : public WifiIntegerProperty {
+    public:
+        WifiCurrentNetworkProperty(WifiController *c);
+        virtual ~WifiCurrentNetworkProperty() {}
+        int set(int idx, int value) { return -1; }
+        int get(int idx, int *buffer);
+    };
+
+    class WifiSuspendedProperty : public WifiIntegerProperty {
+    public:
+        WifiSuspendedProperty(WifiController *c);
+        virtual ~WifiSuspendedProperty() {}
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiNetCountProperty : public WifiIntegerProperty {
+    public:
+        WifiNetCountProperty(WifiController *c);
+        virtual ~WifiNetCountProperty() {}
+        int set(int idx, int value) { return -1; }
+        int get(int idx, int *buffer);
+    };
+
+    class WifiTriggerScanProperty : public WifiIntegerProperty {
+    public:
+        WifiTriggerScanProperty(WifiController *c);
+        virtual ~WifiTriggerScanProperty() {}
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiSupplicantStateProperty : public WifiStringProperty {
+    public:
+        WifiSupplicantStateProperty(WifiController *c);
+        virtual ~WifiSupplicantStateProperty() {}
+        int set(int idx, const char *value) { return -1; }
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiInterfaceProperty : public WifiStringProperty {
+    public:
+        WifiInterfaceProperty(WifiController *c);
+        virtual ~WifiInterfaceProperty() {}
+        int set(int idx, const char *value) { return -1; }
+        int get(int idx, char *buffer, size_t max);
+    };
+
     Supplicant *mSupplicant;
     char        mModulePath[255];
     char        mModuleName[64];
     char        mModuleArgs[255];
 
-    uint32_t    mCurrentScanMode;
-    WifiScanner *mScanner;
     int         mSupplicantState;
+    bool        mActiveScan;
+    bool        mScanOnly;
+    bool        mPacketFilter;
+    bool        mBluetoothCoexScan;
+    int         mBluetoothCoexMode;
+    int         mCurrentlyConnectedNetworkId;
+    bool        mSuspended;
+    int         mLastRssi;
+    int         mRssiEventThreshold;
+    int         mLastLinkSpeed;
+    int         mNumAllowedChannels;
 
     ScanResultCollection *mLatestScanResults;
     pthread_mutex_t      mLatestScanResultsLock;
+    pthread_mutex_t      mLock;
+    WifiStatusPoller     *mStatusPoller;
+
+    struct {
+        WifiEnabledProperty         *propEnabled;
+        WifiScanOnlyProperty        *propScanOnly;
+        WifiAllowedChannelsProperty *propAllowedChannels;
+        IntegerPropertyHelper       *propRssiEventThreshold;
+    } mStaticProperties;
+
+    struct {
+        WifiActiveScanProperty        *propActiveScan;
+        WifiSearchingProperty         *propSearching;
+        WifiPacketFilterProperty      *propPacketFilter;
+        WifiBluetoothCoexScanProperty *propBluetoothCoexScan;
+        WifiBluetoothCoexModeProperty *propBluetoothCoexMode;
+        WifiCurrentNetworkProperty    *propCurrentNetwork;
+        IntegerPropertyHelper         *propRssi;
+        IntegerPropertyHelper         *propLinkSpeed;
+        WifiSuspendedProperty         *propSuspended;
+        WifiNetCountProperty          *propNetCount;
+        WifiSupplicantStateProperty   *propSupplicantState;
+        WifiInterfaceProperty         *propInterface;
+        WifiTriggerScanProperty       *propTriggerScan;
+    } mDynamicProperties;
+
+    // True if supplicant is currently searching for a network
+    bool mIsSupplicantSearching;
+    int  mNumScanResultsSinceLastStateChange;
 
     bool        mEnabled;
 
@@ -72,9 +238,6 @@
     int removeNetwork(int networkId);
     WifiNetworkCollection *createNetworkList();
 
-    virtual int set(const char *name, const char *value);
-    virtual const char *get(const char *name, char *buffer, size_t maxlen);
-
     ScanResultCollection *createScanResults();
 
     char *getModulePath() { return mModulePath; }
@@ -94,18 +257,25 @@
 
 private:
     void sendStatusBroadcast(const char *msg);
-    int setScanMode(uint32_t mode);
+    int setActiveScan(bool active);
+    int triggerScan();
     int enable();
     int disable();
+    int setSuspend(bool suspend);
+    bool getSuspended();
+    int setBluetoothCoexistenceScan(bool enable);
+    int setBluetoothCoexistenceMode(int mode);
+    int setPacketFilter(bool enable);
+    int setScanOnly(bool scanOnly);
 
     // 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);
+    void onAssociatingEvent(SupplicantAssociatingEvent *evt);
+    void onAssociatedEvent(SupplicantAssociatedEvent *evt);
+    void onConnectedEvent(SupplicantConnectedEvent *evt);
+    void onScanResultsEvent(SupplicantScanResultsEvent *evt);
+    void onStateChangeEvent(SupplicantStateChangeEvent *evt);
+    void onConnectionTimeoutEvent(SupplicantConnectionTimeoutEvent *evt);
+    void onDisconnectedEvent(SupplicantDisconnectedEvent *evt);
 #if 0
     virtual void onTerminatingEvent(SupplicantEvent *evt);
     virtual void onPasswordChangedEvent(SupplicantEvent *evt);
@@ -118,6 +288,9 @@
     virtual void onDriverStateEvent(SupplicantEvent *evt);
 #endif
 
+    void onStatusPollInterval();
+
+    int verifyNotSuspended();
 };
 
 #endif
diff --git a/nexus/WifiNetwork.cpp b/nexus/WifiNetwork.cpp
index 7059bd0..6a0f684 100644
--- a/nexus/WifiNetwork.cpp
+++ b/nexus/WifiNetwork.cpp
@@ -26,17 +26,7 @@
 #include "WifiNetwork.h"
 #include "Supplicant.h"
 #include "WifiController.h"
-#include "InterfaceConfig.h"
 
-const char *WifiNetwork::PropertyNames[] = { "ssid", "bssid", "psk", "wepkey.1",
-                                             "wepkey.2", "wepkey.3", "wepkey.4",
-                                             "defkeyidx", "pri", "hiddenssid",
-                                             "AllowedKeyManagement",
-                                             "AllowedProtocols",
-                                             "AllowedAuthAlgorithms",
-                                             "AllowedPairwiseCiphers",
-                                             "AllowedGroupCiphers",
-                                             "enabled", '\0' };
 WifiNetwork::WifiNetwork() {
    // This is private to restrict copy constructors
 }
@@ -76,11 +66,11 @@
     mDefaultKeyIndex = -1;
     mPriority = -1;
     mHiddenSsid = NULL;
-    mAllowedKeyManagement = KeyManagementMask::UNKNOWN;
-    mAllowedProtocols = 0;
-    mAllowedAuthAlgorithms = 0;
-    mAllowedPairwiseCiphers = 0;
-    mAllowedGroupCiphers = 0;
+    mKeyManagement = KeyManagementMask::UNKNOWN;
+    mProtocols = 0;
+    mAuthAlgorithms = 0;
+    mPairwiseCiphers = 0;
+    mGroupCiphers = 0;
     mEnabled = true;
 
     if (flags && flags[0] != '\0') {
@@ -90,11 +80,8 @@
             LOGW("Unsupported flags '%s'", flags);
     }
 
-    char *tmp2;
-    asprintf(&tmp2, "wifi.net.%d", mNetid);
-    mIfaceCfg = new InterfaceConfig(tmp2);
-    free(tmp2);
     free(tmp);
+    createProperties();
 }
 
 WifiNetwork::WifiNetwork(WifiController *c, Supplicant *suppl, int networkId) {
@@ -108,17 +95,13 @@
     mDefaultKeyIndex = -1;
     mPriority = -1;
     mHiddenSsid = NULL;
-    mAllowedKeyManagement = 0;
-    mAllowedProtocols = 0;
-    mAllowedAuthAlgorithms = 0;
-    mAllowedPairwiseCiphers = 0;
-    mAllowedGroupCiphers = 0;
+    mKeyManagement = 0;
+    mProtocols = 0;
+    mAuthAlgorithms = 0;
+    mPairwiseCiphers = 0;
+    mGroupCiphers = 0;
     mEnabled = false;
-
-    char *tmp2;
-    asprintf(&tmp2, "wifi.net.%d", mNetid);
-    mIfaceCfg = new InterfaceConfig(tmp2);
-    free(tmp2);
+    createProperties();
 }
 
 WifiNetwork *WifiNetwork::clone() {
@@ -140,15 +123,35 @@
     r->mPriority = mPriority;
     if (mHiddenSsid)
         r->mHiddenSsid = strdup(mHiddenSsid);
-    r->mAllowedKeyManagement = mAllowedKeyManagement;
-    r->mAllowedProtocols = mAllowedProtocols;
-    r->mAllowedAuthAlgorithms = mAllowedAuthAlgorithms;
-    r->mAllowedPairwiseCiphers = mAllowedPairwiseCiphers;
-    r->mAllowedGroupCiphers = mAllowedGroupCiphers;
+    r->mKeyManagement = mKeyManagement;
+    r->mProtocols = mProtocols;
+    r->mAuthAlgorithms = mAuthAlgorithms;
+    r->mPairwiseCiphers = mPairwiseCiphers;
+    r->mGroupCiphers = mGroupCiphers;
     return r;
 }
 
+void WifiNetwork::createProperties() {
+    asprintf(&mPropNamespace, "wifi.net.%d", mNetid);
+
+    mStaticProperties.propEnabled = new WifiNetworkEnabledProperty(this);
+    mStaticProperties.propSsid = new WifiNetworkSsidProperty(this);
+    mStaticProperties.propBssid = new WifiNetworkBssidProperty(this);
+    mStaticProperties.propPsk = new WifiNetworkPskProperty(this);
+    mStaticProperties.propWepKey = new WifiNetworkWepKeyProperty(this);
+    mStaticProperties.propDefKeyIdx = new WifiNetworkDefaultKeyIndexProperty(this);
+    mStaticProperties.propPriority = new WifiNetworkPriorityProperty(this);
+    mStaticProperties.propKeyManagement = new WifiNetworkKeyManagementProperty(this);
+    mStaticProperties.propProtocols = new WifiNetworkProtocolsProperty(this);
+    mStaticProperties.propAuthAlgorithms = new WifiNetworkAuthAlgorithmsProperty(this);
+    mStaticProperties.propPairwiseCiphers = new WifiNetworkPairwiseCiphersProperty(this);
+    mStaticProperties.propGroupCiphers = new WifiNetworkGroupCiphersProperty(this);
+    mStaticProperties.propHiddenSsid = new WifiNetworkHiddenSsidProperty(this);
+}
+
 WifiNetwork::~WifiNetwork() {
+    if (mPropNamespace)
+        free(mPropNamespace);
     if (mSsid)
         free(mSsid);
     if (mBssid)
@@ -162,13 +165,26 @@
 
     if (mHiddenSsid)
         free(mHiddenSsid);
-    if (mIfaceCfg)
-        delete(mIfaceCfg);
+
+    delete mStaticProperties.propEnabled;
+    delete mStaticProperties.propSsid;
+    delete mStaticProperties.propBssid;
+    delete mStaticProperties.propPsk;
+    delete mStaticProperties.propWepKey;
+    delete mStaticProperties.propDefKeyIdx;
+    delete mStaticProperties.propPriority;
+    delete mStaticProperties.propKeyManagement;
+    delete mStaticProperties.propProtocols;
+    delete mStaticProperties.propAuthAlgorithms;
+    delete mStaticProperties.propPairwiseCiphers;
+    delete mStaticProperties.propGroupCiphers;
+    delete mStaticProperties.propHiddenSsid;
 }
 
 int WifiNetwork::refresh() {
     char buffer[255];
     size_t len;
+    uint32_t mask;
 
     len = sizeof(buffer);
     if (mSuppl->getNetworkVar(mNetid, "psk", buffer, len))
@@ -198,46 +214,47 @@
 
     len = sizeof(buffer);
     if (mSuppl->getNetworkVar(mNetid, "key_mgmt", buffer, len)) {
-        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);
+        if (WifiNetwork::parseKeyManagementMask(buffer, &mask)) {
+            LOGE("Error parsing key_mgmt (%s)", strerror(errno));
+        } else {
+           mKeyManagement = mask;
+        }
     }
 
     len = sizeof(buffer);
     if (mSuppl->getNetworkVar(mNetid, "proto", buffer, len)) {
-        // TODO
+        if (WifiNetwork::parseProtocolsMask(buffer, &mask)) {
+            LOGE("Error parsing proto (%s)", strerror(errno));
+        } else {
+           mProtocols = mask;
+        }
     }
 
     len = sizeof(buffer);
     if (mSuppl->getNetworkVar(mNetid, "auth_alg", buffer, len)) {
-        // TODO
+        if (WifiNetwork::parseAuthAlgorithmsMask(buffer, &mask)) {
+            LOGE("Error parsing auth_alg (%s)", strerror(errno));
+        } else {
+           mAuthAlgorithms = mask;
+        }
     }
 
     len = sizeof(buffer);
     if (mSuppl->getNetworkVar(mNetid, "pairwise", buffer, len)) {
-        // TODO
+        if (WifiNetwork::parsePairwiseCiphersMask(buffer, &mask)) {
+            LOGE("Error parsing pairwise (%s)", strerror(errno));
+        } else {
+           mPairwiseCiphers = mask;
+        }
     }
 
     len = sizeof(buffer);
     if (mSuppl->getNetworkVar(mNetid, "group", buffer, len)) {
-        // TODO
+        if (WifiNetwork::parseGroupCiphersMask(buffer, &mask)) {
+            LOGE("Error parsing group (%s)", strerror(errno));
+        } else {
+           mGroupCiphers = mask;
+        }
     }
 
     return 0;
@@ -246,179 +263,10 @@
     return -1;
 }
 
-int WifiNetwork::set(const char *name, const char *value) {
-    char *n_tmp = strdup(name + strlen("wifi.net."));
-    char *n_next = n_tmp;
-    char *n_local;
-    char *n_rest;
-    int rc = 0;
-
-    if (!strsep(&n_next, ".")) // skip net id
-        goto out_inval;
-
-    if (!(n_local = strsep(&n_next, ".")))
-        goto out_inval;
-
-    n_rest = n_next;
-
-//    LOGD("set(): var '%s'(%s / %s) = %s", name, n_local, n_rest, value);
-    if (!strcasecmp(n_local, "enabled"))
-        rc = setEnabled(atoi(value));
-    else if (!strcmp(n_local, "ssid"))
-        rc = setSsid(value);
-    else if (!strcasecmp(n_local, "bssid"))
-        rc = setBssid(value);
-    else if (!strcasecmp(n_local, "psk"))
-        rc = setPsk(value);
-    else if (!strcasecmp(n_local, "wepkey"))
-        rc = setWepKey(atoi(n_rest) -1, value);
-    else if (!strcasecmp(n_local, "defkeyidx"))
-        rc = setDefaultKeyIndex(atoi(value));
-    else if (!strcasecmp(n_local, "pri"))
-        rc = setPriority(atoi(value));
-    else if (!strcasecmp(n_local, "hiddenssid"))
-        rc = setHiddenSsid(value);
-    else if (!strcasecmp(n_local, "AllowedKeyManagement")) {
-        uint32_t mask = 0;
-        bool none = false;
-        char *v_tmp = strdup(value);
-        char *v_next = v_tmp;
-        char *v_token;
-
-        while((v_token = strsep(&v_next, " "))) {
-            if (!strcasecmp(v_token, "NONE")) {
-                mask = KeyManagementMask::NONE;
-                none = true;
-            } else if (!none) {
-                if (!strcasecmp(v_token, "WPA_PSK"))
-                    mask |= KeyManagementMask::WPA_PSK;
-                else if (!strcasecmp(v_token, "WPA_EAP"))
-                    mask |= KeyManagementMask::WPA_EAP;
-                else if (!strcasecmp(v_token, "IEEE8021X"))
-                    mask |= KeyManagementMask::IEEE8021X;
-                else {
-                    errno = EINVAL;
-                    rc = -1;
-                    free(v_tmp);
-                    goto out;
-                }
-            } else {
-                errno = EINVAL;
-                rc = -1;
-                free(v_tmp);
-                goto out;
-            }
-        }
-        free(v_tmp);
-    } else if (!strcasecmp(n_local, "AllowedProtocols")) {
-        // TODO
-    } else if (!strcasecmp(n_local, "AllowedPairwiseCiphers")) {
-        // TODO
-    } else if (!strcasecmp(n_local, "AllowedAuthAlgorithms")) {
-        // TODO
-    } else if (!strcasecmp(n_local, "AllowedGroupCiphers")) {
-        // TODO
-    } else {
-        errno = ENOENT;
-        free(n_tmp);
-        return -1;
-    }
-
-out:
-    free(n_tmp);
-    return rc;
-
-out_inval:
-    errno = EINVAL;
-    free(n_tmp);
-    return -1;
-}
-
-const char *WifiNetwork::get(const char *name, char *buffer, size_t maxsize) {
-    char *n_tmp = strdup(name + strlen("wifi.net."));
-    char *n_next = n_tmp;
-    char *n_local;
-    char fc[64];
-    char rc[128];
-
-    if (!strsep(&n_next, ".")) // skip net id
-        goto out_inval;
-
-    if (!(n_local = strsep(&n_next, ".")))
-        goto out_inval;
-
-
-    strncpy(fc, n_local, sizeof(fc));
-    rc[0] = '\0';
-    if (n_next)
-        strncpy(rc, n_next, sizeof(rc));
-
-    free(n_tmp);
-
-    if (!strcasecmp(fc, "enabled"))
-        snprintf(buffer, maxsize, "%d", getEnabled());
-    else if (!strcasecmp(fc, "ssid")) {
-        strncpy(buffer,
-                getSsid() ? getSsid() : "none",
-                maxsize);
-    } else if (!strcasecmp(fc, "bssid")) {
-        strncpy(buffer,
-                getBssid() ? getBssid() : "none",
-                maxsize);
-    } else if (!strcasecmp(fc, "psk")) {
-        strncpy(buffer,
-                getPsk() ? getPsk() : "none",
-                maxsize);
-    } else if (!strcasecmp(fc, "wepkey")) {
-        strncpy(buffer,
-                getWepKey(atoi(rc)-1) ? getWepKey(atoi(rc)-1) : "none",
-                maxsize);
-    } else if (!strcasecmp(fc, "defkeyidx"))
-        snprintf(buffer, maxsize, "%d", getDefaultKeyIndex());
-    else if (!strcasecmp(fc, "pri"))
-        snprintf(buffer, maxsize, "%d", getPriority());
-    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);
-    } else {
-        strncpy(buffer, "(internal error)", maxsize);
-        errno = ENOENT;
-        return NULL;
-    }
-
-    return buffer;
-
-out_inval:
-    errno = EINVAL;
-    free(n_tmp);
-    return NULL;
-}
-
 int WifiNetwork::setSsid(const char *ssid) {
-    if (mSuppl->setNetworkVar(mNetid, "ssid", ssid))
+    char tmp[255];
+    snprintf(tmp, sizeof(tmp), "\"%s\"", ssid);
+    if (mSuppl->setNetworkVar(mNetid, "ssid", tmp))
         return -1;
     if (mSsid)
         free(mSsid);
@@ -436,7 +284,9 @@
 }
 
 int WifiNetwork::setPsk(const char *psk) {
-    if (mSuppl->setNetworkVar(mNetid, "psk", psk))
+    char tmp[255];
+    snprintf(tmp, sizeof(tmp), "\"%s\"", psk);
+    if (mSuppl->setNetworkVar(mNetid, "psk", tmp))
         return -1;
 
     if (mPsk)
@@ -491,28 +341,34 @@
     return 0;
 }
 
-int WifiNetwork::setAllowedKeyManagement(uint32_t mask) {
-    char accum[255];
+int WifiNetwork::setKeyManagement(uint32_t mask) {
+    char accum[64] = {'\0'};
 
     if (mask == KeyManagementMask::NONE)
         strcpy(accum, "NONE");
     else {
-        if (mask & KeyManagementMask::WPA_PSK)
-            strcat(accum, "WPA_PSK ");
-        if (mask & KeyManagementMask::WPA_EAP)
-            strcat(accum, "WPA_EAP ");
-        if (mask & KeyManagementMask::IEEE8021X)
-            strcat(accum, "IEEE8021X ");
+        if (mask & KeyManagementMask::WPA_PSK) 
+            strcat(accum, "WPA-PSK");
+        if (mask & KeyManagementMask::WPA_EAP) {
+            if (accum[0] != '\0')
+                strcat(accum, " ");
+            strcat(accum, "WPA-EAP");
+        }
+        if (mask & KeyManagementMask::IEEE8021X) {
+            if (accum[0] != '\0')
+                strcat(accum, " ");
+            strcat(accum, "IEEE8021X");
+        }
     }
 
     if (mSuppl->setNetworkVar(mNetid, "key_mgmt", accum))
         return -1;
-    mAllowedKeyManagement = mask;
+    mKeyManagement = mask;
     return 0;
 }
 
-int WifiNetwork::setAllowedProtocols(uint32_t mask) {
-    char accum[255];
+int WifiNetwork::setProtocols(uint32_t mask) {
+    char accum[64];
 
     accum[0] = '\0';
 
@@ -524,15 +380,18 @@
 
     if (mSuppl->setNetworkVar(mNetid, "proto", accum))
         return -1;
-    mAllowedProtocols = mask;
+    mProtocols = mask;
     return 0;
 }
 
-int WifiNetwork::setAllowedAuthAlgorithms(uint32_t mask) {
-    char accum[255];
+int WifiNetwork::setAuthAlgorithms(uint32_t mask) {
+    char accum[64];
 
     accum[0] = '\0';
 
+    if (mask == 0)
+        strcpy(accum, "");
+
     if (mask & AuthenticationAlgorithmMask::OPEN)
         strcpy(accum, "OPEN ");
 
@@ -545,12 +404,14 @@
     if (mSuppl->setNetworkVar(mNetid, "auth_alg", accum))
         return -1;
 
-    mAllowedAuthAlgorithms = mask;
+    mAuthAlgorithms = mask;
     return 0;
 }
 
-int WifiNetwork::setAllowedPairwiseCiphers(uint32_t mask) {
-    char accum[255];
+int WifiNetwork::setPairwiseCiphers(uint32_t mask) {
+    char accum[64];
+
+    accum[0] = '\0';
 
     if (mask == PairwiseCiphersMask::NONE)
         strcpy(accum, "NONE");
@@ -564,12 +425,14 @@
     if (mSuppl->setNetworkVar(mNetid, "pairwise", accum))
         return -1;
 
-    mAllowedPairwiseCiphers = mask;
+    mPairwiseCiphers = mask;
     return 0;
 }
 
-int WifiNetwork::setAllowedGroupCiphers(uint32_t mask) {
-    char accum[255];
+int WifiNetwork::setGroupCiphers(uint32_t mask) {
+    char accum[64];
+
+    accum[0] = '\0';
 
     if (mask & GroupCiphersMask::WEP40)
         strcat(accum, "WEP40 ");
@@ -582,7 +445,7 @@
 
     if (mSuppl->setNetworkVar(mNetid, "group", accum))
         return -1;
-    mAllowedGroupCiphers = mask;
+    mGroupCiphers = mask;
     return 0;
 }
 
@@ -594,7 +457,7 @@
             errno = EAGAIN;
             return -1;
         }
-        if (getAllowedKeyManagement() == KeyManagementMask::UNKNOWN) {
+        if (getKeyManagement() == KeyManagementMask::UNKNOWN) {
             LOGE("Cannot enable network when KeyManagement is not set");
             errno = EAGAIN;
             return -1;
@@ -608,29 +471,500 @@
     return 0;
 }
 
-int WifiNetwork::registerProperties() {
-    for (const char **p = WifiNetwork::PropertyNames; *p != '\0'; p++) {
-        char *tmp;
-        asprintf(&tmp, "wifi.net.%d.%s", mNetid, *p);
-
-        if (NetworkManager::Instance()->getPropMngr()->registerProperty(tmp,
-                                                                        this)) {
-            free(tmp);
-            return -1;
-        }
-        free(tmp);
-    }
+int WifiNetwork::attachProperties(PropertyManager *pm, const char *nsName) {
+    pm->attachProperty(nsName, mStaticProperties.propSsid);
+    pm->attachProperty(nsName, mStaticProperties.propBssid);
+    pm->attachProperty(nsName, mStaticProperties.propPsk);
+    pm->attachProperty(nsName, mStaticProperties.propWepKey);
+    pm->attachProperty(nsName, mStaticProperties.propDefKeyIdx);
+    pm->attachProperty(nsName, mStaticProperties.propPriority);
+    pm->attachProperty(nsName, mStaticProperties.propKeyManagement);
+    pm->attachProperty(nsName, mStaticProperties.propProtocols);
+    pm->attachProperty(nsName, mStaticProperties.propAuthAlgorithms);
+    pm->attachProperty(nsName, mStaticProperties.propPairwiseCiphers);
+    pm->attachProperty(nsName, mStaticProperties.propGroupCiphers);
+    pm->attachProperty(nsName, mStaticProperties.propHiddenSsid);
+    pm->attachProperty(nsName, mStaticProperties.propEnabled);
     return 0;
 }
 
-int WifiNetwork::unregisterProperties() {
-    for (const char **p = WifiNetwork::PropertyNames; *p != '\0'; p++) {
-        char *tmp;
-        asprintf(&tmp, "wifi.net.%d.%s", mNetid, *p);
+int WifiNetwork::detachProperties(PropertyManager *pm, const char *nsName) {
+    pm->detachProperty(nsName, mStaticProperties.propEnabled);
+    pm->detachProperty(nsName, mStaticProperties.propSsid);
+    pm->detachProperty(nsName, mStaticProperties.propBssid);
+    pm->detachProperty(nsName, mStaticProperties.propPsk);
+    pm->detachProperty(nsName, mStaticProperties.propWepKey);
+    pm->detachProperty(nsName, mStaticProperties.propDefKeyIdx);
+    pm->detachProperty(nsName, mStaticProperties.propPriority);
+    pm->detachProperty(nsName, mStaticProperties.propKeyManagement);
+    pm->detachProperty(nsName, mStaticProperties.propProtocols);
+    pm->detachProperty(nsName, mStaticProperties.propAuthAlgorithms);
+    pm->detachProperty(nsName, mStaticProperties.propPairwiseCiphers);
+    pm->detachProperty(nsName, mStaticProperties.propGroupCiphers);
+    pm->detachProperty(nsName, mStaticProperties.propHiddenSsid);
+    return 0;
+}
 
-        if (NetworkManager::Instance()->getPropMngr()->unregisterProperty(tmp))
-            LOGW("Unable to remove property '%s' (%s)", tmp, strerror(errno));
-        free(tmp);
+int WifiNetwork::parseKeyManagementMask(const char *buffer, uint32_t *mask) {
+    bool none = false;
+    char *v_tmp = strdup(buffer);
+    char *v_next = v_tmp;
+    char *v_token;
+
+//    LOGD("parseKeyManagementMask(%s)", buffer);
+    *mask = 0;
+
+    while((v_token = strsep(&v_next, " "))) {
+        if (!strcasecmp(v_token, "NONE")) {
+            *mask = KeyManagementMask::NONE;
+            none = true;
+        } else if (!none) {
+            if (!strcasecmp(v_token, "WPA-PSK"))
+                *mask |= KeyManagementMask::WPA_PSK;
+            else if (!strcasecmp(v_token, "WPA-EAP"))
+                *mask |= KeyManagementMask::WPA_EAP;
+            else if (!strcasecmp(v_token, "IEEE8021X"))
+                *mask |= KeyManagementMask::IEEE8021X;
+            else {
+                LOGW("Invalid KeyManagementMask value '%s'", v_token);
+                errno = EINVAL;
+                free(v_tmp);
+                return -1;
+            }
+        } else {
+            LOGW("KeyManagementMask value '%s' when NONE", v_token);
+            errno = EINVAL;
+            free(v_tmp);
+            return -1;
+        }
+    }
+    free(v_tmp);
+    return 0;
+}
+
+int WifiNetwork::parseProtocolsMask(const char *buffer, uint32_t *mask) {
+    bool none = false;
+    char *v_tmp = strdup(buffer);
+    char *v_next = v_tmp;
+    char *v_token;
+
+//    LOGD("parseProtocolsMask(%s)", buffer);
+    *mask = 0;
+    while((v_token = strsep(&v_next, " "))) {
+        if (!strcasecmp(v_token, "WPA"))
+            *mask |= SecurityProtocolMask::WPA;
+        else if (!strcasecmp(v_token, "RSN"))
+            *mask |= SecurityProtocolMask::RSN;
+        else {
+            LOGW("Invalid ProtocolsMask value '%s'", v_token);
+            errno = EINVAL;
+            free(v_tmp);
+            return -1;
+        }
+    }
+
+    free(v_tmp);
+    return 0;
+}
+
+int WifiNetwork::parseAuthAlgorithmsMask(const char *buffer, uint32_t *mask) {
+    bool none = false;
+    char *v_tmp = strdup(buffer);
+    char *v_next = v_tmp;
+    char *v_token;
+
+//    LOGD("parseAuthAlgorithmsMask(%s)", buffer);
+
+    *mask = 0;
+    if (buffer[0] == '\0')
+        return 0;
+
+    while((v_token = strsep(&v_next, " "))) {
+        if (!strcasecmp(v_token, "OPEN"))
+            *mask |= AuthenticationAlgorithmMask::OPEN;
+        else if (!strcasecmp(v_token, "SHARED"))
+            *mask |= AuthenticationAlgorithmMask::SHARED;
+        else if (!strcasecmp(v_token, "LEAP"))
+            *mask |= AuthenticationAlgorithmMask::LEAP;
+        else {
+            LOGW("Invalid AuthAlgorithmsMask value '%s'", v_token);
+            errno = EINVAL;
+            free(v_tmp);
+            return -1;
+        }
+    }
+    free(v_tmp);
+    return 0;
+}
+
+int WifiNetwork::parsePairwiseCiphersMask(const char *buffer, uint32_t *mask) {
+    bool none = false;
+    char *v_tmp = strdup(buffer);
+    char *v_next = v_tmp;
+    char *v_token;
+
+//    LOGD("parsePairwiseCiphersMask(%s)", buffer);
+
+    *mask = 0;
+    while((v_token = strsep(&v_next, " "))) {
+        if (!strcasecmp(v_token, "NONE")) {
+            *mask = PairwiseCiphersMask::NONE;
+            none = true;
+        } else if (!none) {
+            if (!strcasecmp(v_token, "TKIP"))
+                *mask |= PairwiseCiphersMask::TKIP;
+            else if (!strcasecmp(v_token, "CCMP"))
+                *mask |= PairwiseCiphersMask::CCMP;
+        else {
+                LOGW("PairwiseCiphersMask value '%s' when NONE", v_token);
+                errno = EINVAL;
+                free(v_tmp);
+                return -1;
+            }
+        } else {
+            LOGW("Invalid PairwiseCiphersMask value '%s'", v_token);
+            errno = EINVAL;
+            free(v_tmp);
+            return -1;
+        }
+    }
+    free(v_tmp);
+    return 0;
+}
+
+int WifiNetwork::parseGroupCiphersMask(const char *buffer, uint32_t *mask) {
+    bool none = false;
+    char *v_tmp = strdup(buffer);
+    char *v_next = v_tmp;
+    char *v_token;
+
+//    LOGD("parseGroupCiphersMask(%s)", buffer);
+
+    *mask = 0;
+    while((v_token = strsep(&v_next, " "))) {
+        if (!strcasecmp(v_token, "WEP40"))
+            *mask |= GroupCiphersMask::WEP40;
+        else if (!strcasecmp(v_token, "WEP104"))
+            *mask |= GroupCiphersMask::WEP104;
+        else if (!strcasecmp(v_token, "TKIP"))
+            *mask |= GroupCiphersMask::TKIP;
+        else if (!strcasecmp(v_token, "CCMP"))
+            *mask |= GroupCiphersMask::CCMP;
+        else {
+            LOGW("Invalid GroupCiphersMask value '%s'", v_token);
+            errno = EINVAL;
+            free(v_tmp);
+            return -1;
+        }
+    }
+    free(v_tmp);
+    return 0;
+}
+
+WifiNetwork::WifiNetworkIntegerProperty::WifiNetworkIntegerProperty(WifiNetwork *wn,
+                                                      const char *name,
+                                                      bool ro,
+                                                      int elements) :
+             IntegerProperty(name, ro, elements) {
+    mWn = wn;
+}
+
+WifiNetwork::WifiNetworkStringProperty::WifiNetworkStringProperty(WifiNetwork *wn,
+                                                                  const char *name,
+                                                              bool ro, int elements) :
+             StringProperty(name, ro, elements) {
+    mWn = wn;
+}
+
+WifiNetwork::WifiNetworkEnabledProperty::WifiNetworkEnabledProperty(WifiNetwork *wn) :
+                WifiNetworkIntegerProperty(wn, "Enabled", false, 1) {
+}
+
+int WifiNetwork::WifiNetworkEnabledProperty::get(int idx, int *buffer) {
+    *buffer = mWn->mEnabled;
+    return 0;
+}
+int WifiNetwork::WifiNetworkEnabledProperty::set(int idx, int value) {
+    return mWn->setEnabled(value == 1);
+}
+
+WifiNetwork::WifiNetworkSsidProperty::WifiNetworkSsidProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "Ssid", false, 1) {
+}
+
+int WifiNetwork::WifiNetworkSsidProperty::get(int idx, char *buffer, size_t max) {
+    strncpy(buffer,
+            mWn->getSsid() ? mWn->getSsid() : "none",
+            max);
+    return 0;
+}
+int WifiNetwork::WifiNetworkSsidProperty::set(int idx, const char *value) {
+    return mWn->setSsid(value);
+}
+
+WifiNetwork::WifiNetworkBssidProperty::WifiNetworkBssidProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "Bssid", false, 1) {
+}
+int WifiNetwork::WifiNetworkBssidProperty::get(int idx, char *buffer, size_t max) {
+    strncpy(buffer,
+            mWn->getBssid() ? mWn->getBssid() : "none",
+            max);
+    return 0;
+}
+int WifiNetwork::WifiNetworkBssidProperty::set(int idx, const char *value) {
+    return mWn->setBssid(value);
+}
+
+WifiNetwork::WifiNetworkPskProperty::WifiNetworkPskProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "Psk", false, 1) {
+}
+int WifiNetwork::WifiNetworkPskProperty::get(int idx, char *buffer, size_t max) {
+    strncpy(buffer,
+            mWn->getPsk() ? mWn->getPsk() : "none",
+            max);
+    return 0;
+}
+int WifiNetwork::WifiNetworkPskProperty::set(int idx, const char *value) {
+    return mWn->setPsk(value);
+}
+
+WifiNetwork::WifiNetworkWepKeyProperty::WifiNetworkWepKeyProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "WepKey", false, 4) {
+}
+
+int WifiNetwork::WifiNetworkWepKeyProperty::get(int idx, char *buffer, size_t max) {
+    const char *key = mWn->getWepKey(idx);
+
+    strncpy(buffer, (key ? key : "none"), max);
+    return 0;
+}
+int WifiNetwork::WifiNetworkWepKeyProperty::set(int idx, const char *value) {
+    return mWn->setWepKey(idx, value);
+}
+
+WifiNetwork::WifiNetworkDefaultKeyIndexProperty::WifiNetworkDefaultKeyIndexProperty(WifiNetwork *wn) :
+                WifiNetworkIntegerProperty(wn, "DefaultKeyIndex", false,  1) {
+}
+int WifiNetwork::WifiNetworkDefaultKeyIndexProperty::get(int idx, int *buffer) {
+    *buffer = mWn->getDefaultKeyIndex();
+    return 0;
+}
+int WifiNetwork::WifiNetworkDefaultKeyIndexProperty::set(int idx, int value) {
+    return mWn->setDefaultKeyIndex(value);
+}
+
+WifiNetwork::WifiNetworkPriorityProperty::WifiNetworkPriorityProperty(WifiNetwork *wn) :
+                WifiNetworkIntegerProperty(wn, "Priority", false, 1) {
+}
+int WifiNetwork::WifiNetworkPriorityProperty::get(int idx, int *buffer) {
+    *buffer = mWn->getPriority();
+    return 0;
+}
+int WifiNetwork::WifiNetworkPriorityProperty::set(int idx, int value) {
+    return mWn->setPriority(value);
+}
+
+WifiNetwork::WifiNetworkKeyManagementProperty::WifiNetworkKeyManagementProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "KeyManagement", false, 1) {
+}
+int WifiNetwork::WifiNetworkKeyManagementProperty::get(int idx, char *buffer, size_t max) {
+
+    if (mWn->getKeyManagement() == KeyManagementMask::NONE)
+        strncpy(buffer, "NONE", max);
+    else {
+        char tmp[80] = { '\0' };
+
+        if (mWn->getKeyManagement() & KeyManagementMask::WPA_PSK)
+            strcat(tmp, "WPA-PSK");
+        if (mWn->getKeyManagement() & KeyManagementMask::WPA_EAP) {
+            if (tmp[0] != '\0')
+                strcat(tmp, " ");
+            strcat(tmp, "WPA-EAP");
+        }
+        if (mWn->getKeyManagement() & KeyManagementMask::IEEE8021X) {
+            if (tmp[0] != '\0')
+                strcat(tmp, " ");
+            strcat(tmp, "IEEE8021X");
+        }
+        if (tmp[0] == '\0') {
+            strncpy(buffer, "(internal error)", max);
+            errno = ENOENT;
+            return -1;
+        }
+        if (tmp[strlen(tmp)] == ' ')
+            tmp[strlen(tmp)] = '\0';
+
+        strncpy(buffer, tmp, max);
     }
     return 0;
 }
+int WifiNetwork::WifiNetworkKeyManagementProperty::set(int idx, const char *value) {
+    uint32_t mask;
+    if (mWn->parseKeyManagementMask(value, &mask))
+        return -1;
+    return mWn->setKeyManagement(mask);
+}
+
+WifiNetwork::WifiNetworkProtocolsProperty::WifiNetworkProtocolsProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "Protocols", false, 1) {
+}
+int WifiNetwork::WifiNetworkProtocolsProperty::get(int idx, char *buffer, size_t max) {
+    char tmp[80] = { '\0' };
+
+    if (mWn->getProtocols() & SecurityProtocolMask::WPA)
+        strcat(tmp, "WPA");
+    if (mWn->getProtocols() & SecurityProtocolMask::RSN) {
+        if (tmp[0] != '\0')
+            strcat(tmp, " ");
+        strcat(tmp, "RSN");
+    }
+
+    if (tmp[0] == '\0') {
+        strncpy(buffer, "(internal error)", max);
+        errno = ENOENT;
+        return NULL;
+    }
+    if (tmp[strlen(tmp)] == ' ')
+        tmp[strlen(tmp)] = '\0';
+
+    strncpy(buffer, tmp, max);
+    return 0;
+}
+int WifiNetwork::WifiNetworkProtocolsProperty::set(int idx, const char *value) {
+    uint32_t mask;
+    if (mWn->parseProtocolsMask(value, &mask))
+        return -1;
+    return mWn->setProtocols(mask);
+}
+
+WifiNetwork::WifiNetworkAuthAlgorithmsProperty::WifiNetworkAuthAlgorithmsProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "AuthAlgorithms", false, 1) {
+}
+int WifiNetwork::WifiNetworkAuthAlgorithmsProperty::get(int idx, char *buffer, size_t max) {
+    char tmp[80] = { '\0' };
+
+    if (mWn->getAuthAlgorithms() == 0) {
+        strncpy(buffer, "NONE", max);
+        return 0;
+    }
+
+    if (mWn->getAuthAlgorithms() & AuthenticationAlgorithmMask::OPEN)
+        strcat(tmp, "OPEN");
+    if (mWn->getAuthAlgorithms() & AuthenticationAlgorithmMask::SHARED) {
+        if (tmp[0] != '\0')
+            strcat(tmp, " ");
+        strcat(tmp, "SHARED");
+    }
+    if (mWn->getAuthAlgorithms() & AuthenticationAlgorithmMask::LEAP) {
+        if (tmp[0] != '\0')
+            strcat(tmp, " ");
+        strcat(tmp, "LEAP");
+    }
+
+    if (tmp[0] == '\0') {
+        strncpy(buffer, "(internal error)", max);
+        errno = ENOENT;
+        return NULL;
+    }
+    if (tmp[strlen(tmp)] == ' ')
+        tmp[strlen(tmp)] = '\0';
+
+    strncpy(buffer, tmp, max);
+    return 0;
+}
+int WifiNetwork::WifiNetworkAuthAlgorithmsProperty::set(int idx, const char *value) {
+    uint32_t mask;
+    if (mWn->parseAuthAlgorithmsMask(value, &mask))
+        return -1;
+    return mWn->setAuthAlgorithms(mask);
+}
+
+WifiNetwork::WifiNetworkPairwiseCiphersProperty::WifiNetworkPairwiseCiphersProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "PairwiseCiphers", false, 1) {
+}
+int WifiNetwork::WifiNetworkPairwiseCiphersProperty::get(int idx, char *buffer, size_t max) {
+    if (mWn->getPairwiseCiphers() == PairwiseCiphersMask::NONE)
+        strncpy(buffer, "NONE", max);
+    else {
+        char tmp[80] = { '\0' };
+
+        if (mWn->getPairwiseCiphers() & PairwiseCiphersMask::TKIP)
+            strcat(tmp, "TKIP");
+        if (mWn->getPairwiseCiphers() & PairwiseCiphersMask::CCMP) {
+            if (tmp[0] != '\0')
+                strcat(tmp, " ");
+            strcat(tmp, "CCMP");
+        }
+        if (tmp[0] == '\0') {
+            strncpy(buffer, "(internal error)", max);
+            errno = ENOENT;
+            return NULL;
+        }
+        if (tmp[strlen(tmp)] == ' ')
+            tmp[strlen(tmp)] = '\0';
+
+        strncpy(buffer, tmp, max);
+    }
+    return 0;
+}
+int WifiNetwork::WifiNetworkPairwiseCiphersProperty::set(int idx, const char *value) {
+    uint32_t mask;
+    if (mWn->parsePairwiseCiphersMask(value, &mask))
+        return -1;
+    return mWn->setPairwiseCiphers(mask);
+}
+
+WifiNetwork::WifiNetworkGroupCiphersProperty::WifiNetworkGroupCiphersProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "GroupCiphers", false, 1) {
+}
+int WifiNetwork::WifiNetworkGroupCiphersProperty::get(int idx, char *buffer, size_t max) {
+   char tmp[80] = { '\0' };
+
+    if (mWn->getGroupCiphers() & GroupCiphersMask::WEP40)
+        strcat(tmp, "WEP40");
+    if (mWn->getGroupCiphers() & GroupCiphersMask::WEP104) {
+        if (tmp[0] != '\0')
+            strcat(tmp, " ");
+        strcat(tmp, "WEP104");
+    }
+    if (mWn->getGroupCiphers() & GroupCiphersMask::TKIP) {
+        if (tmp[0] != '\0')
+            strcat(tmp, " ");
+        strcat(tmp, "TKIP");
+    }
+    if (mWn->getGroupCiphers() & GroupCiphersMask::CCMP) {
+        if (tmp[0] != '\0')
+            strcat(tmp, " ");
+        strcat(tmp, "CCMP");
+    }
+
+    if (tmp[0] == '\0') {
+        strncpy(buffer, "(internal error)", max);
+        errno = ENOENT;
+        return -1;
+    }
+    if (tmp[strlen(tmp)] == ' ')
+        tmp[strlen(tmp)] = '\0';
+
+    strncpy(buffer, tmp, max);
+    return 0;
+}
+int WifiNetwork::WifiNetworkGroupCiphersProperty::set(int idx, const char *value) {
+    uint32_t mask;
+    if (mWn->parseGroupCiphersMask(value, &mask))
+        return -1;
+    return mWn->setGroupCiphers(mask);
+}
+
+WifiNetwork::WifiNetworkHiddenSsidProperty::WifiNetworkHiddenSsidProperty(WifiNetwork *wn) :
+                WifiNetworkStringProperty(wn, "HiddenSsid", false, 1) {
+}
+int WifiNetwork::WifiNetworkHiddenSsidProperty::get(int idx, char *buffer, size_t max) {
+    const char *scan_ssid = mWn->getHiddenSsid();
+    
+    strncpy(buffer, (scan_ssid ? scan_ssid : "none"), max);
+    return 0;
+}
+int WifiNetwork::WifiNetworkHiddenSsidProperty::set(int idx, const char *value) {
+    return mWn->setHiddenSsid(value);
+}
diff --git a/nexus/WifiNetwork.h b/nexus/WifiNetwork.h
index c2f5d23..15ec647 100644
--- a/nexus/WifiNetwork.h
+++ b/nexus/WifiNetwork.h
@@ -21,6 +21,10 @@
 
 #include <utils/List.h>
 
+#include "Property.h"
+
+class PropertyManager;
+
 class KeyManagementMask {
 public:
     static const uint32_t UNKNOWN   = 0;
@@ -60,19 +64,140 @@
 };
 
 class Supplicant;
-class InterfaceConfig;
 class Controller;
 class WifiController;
 
-#include "IPropertyProvider.h"
+class WifiNetwork {
+    class WifiNetworkIntegerProperty : public IntegerProperty {
+    protected:
+        WifiNetwork *mWn;
+    public:
+        WifiNetworkIntegerProperty(WifiNetwork *wn, const char *name, bool ro,
+                                   int elements);
+        virtual ~WifiNetworkIntegerProperty() {}
+        virtual int set(int idx, int value) = 0;
+        virtual int get(int idx, int *buffer) = 0;
+    };
+    friend class WifiNetwork::WifiNetworkIntegerProperty;
 
-class WifiNetwork : public IPropertyProvider{
-public:
-    static const char *PropertyNames[];
+    class WifiNetworkStringProperty : public StringProperty {
+    protected:
+        WifiNetwork *mWn;
+    public:
+        WifiNetworkStringProperty(WifiNetwork *wn, const char *name, bool ro,
+                                 int elements);
+        virtual ~WifiNetworkStringProperty() {}
+        virtual int set(int idx, const char *value) = 0;
+        virtual int get(int idx, char *buffer, size_t max) = 0;
+    };
+    friend class WifiNetwork::WifiNetworkStringProperty;
+
+    class WifiNetworkEnabledProperty : public WifiNetworkIntegerProperty {
+    public:
+        WifiNetworkEnabledProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkEnabledProperty() {};
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiNetworkPriorityProperty : public WifiNetworkIntegerProperty {
+    public:
+        WifiNetworkPriorityProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkPriorityProperty() {};
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiNetworkDefaultKeyIndexProperty : public WifiNetworkIntegerProperty {
+    public:
+        WifiNetworkDefaultKeyIndexProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkDefaultKeyIndexProperty() {};
+        int set(int idx, int value);
+        int get(int idx, int *buffer);
+    };
+
+    class WifiNetworkSsidProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkSsidProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkSsidProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiNetworkBssidProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkBssidProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkBssidProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiNetworkPskProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkPskProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkPskProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiNetworkKeyManagementProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkKeyManagementProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkKeyManagementProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiNetworkAuthAlgorithmsProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkAuthAlgorithmsProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkAuthAlgorithmsProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiNetworkProtocolsProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkProtocolsProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkProtocolsProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiNetworkWepKeyProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkWepKeyProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkWepKeyProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiNetworkPairwiseCiphersProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkPairwiseCiphersProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkPairwiseCiphersProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiNetworkGroupCiphersProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkGroupCiphersProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkGroupCiphersProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
+
+    class WifiNetworkHiddenSsidProperty : public WifiNetworkStringProperty {
+    public:
+        WifiNetworkHiddenSsidProperty(WifiNetwork *wn);
+        virtual ~WifiNetworkHiddenSsidProperty() {};
+        int set(int idx, const char *value);
+        int get(int idx, char *buffer, size_t max);
+    };
 
 private:
     Supplicant *mSuppl;
-    InterfaceConfig *mIfaceCfg;
     WifiController *mController;
 
     /*
@@ -128,33 +253,49 @@
     /*
      * The set of key management protocols supported by this configuration.
      */
-    uint32_t mAllowedKeyManagement;
+    uint32_t mKeyManagement;
 
     /*
      * The set of security protocols supported by this configuration.
      */
-    uint32_t mAllowedProtocols;
+    uint32_t mProtocols;
 
     /*
      * The set of authentication protocols supported by this configuration.
      */
-    uint32_t mAllowedAuthAlgorithms;
+    uint32_t mAuthAlgorithms;
 
     /*
      * The set of pairwise ciphers for WPA supported by this configuration.
      */
-    uint32_t mAllowedPairwiseCiphers;
+    uint32_t mPairwiseCiphers;
 
     /*
      * The set of group ciphers for WPA supported by this configuration.
      */
-    uint32_t mAllowedGroupCiphers;
+    uint32_t mGroupCiphers;
 
     /*
      * Set if this Network is enabled
      */
     bool mEnabled;
 
+    char *mPropNamespace;
+    struct {
+        WifiNetworkEnabledProperty               *propEnabled;
+        WifiNetworkSsidProperty                  *propSsid;
+        WifiNetworkBssidProperty                 *propBssid;
+        WifiNetworkPskProperty                   *propPsk;
+        WifiNetworkWepKeyProperty                *propWepKey;
+        WifiNetworkDefaultKeyIndexProperty       *propDefKeyIdx;
+        WifiNetworkPriorityProperty              *propPriority;
+        WifiNetworkKeyManagementProperty  *propKeyManagement;
+        WifiNetworkProtocolsProperty      *propProtocols;
+        WifiNetworkAuthAlgorithmsProperty *propAuthAlgorithms;
+        WifiNetworkPairwiseCiphersProperty       *propPairwiseCiphers;
+        WifiNetworkGroupCiphersProperty          *propGroupCiphers;
+        WifiNetworkHiddenSsidProperty            *propHiddenSsid;
+    } mStaticProperties;
 private:
     WifiNetwork();
 
@@ -165,8 +306,8 @@
     virtual ~WifiNetwork();
 
     WifiNetwork *clone();
-    int registerProperties();
-    int unregisterProperties();
+    int attachProperties(PropertyManager *pm, const char *nsName);
+    int detachProperties(PropertyManager *pm, const char *nsName);
 
     int getNetworkId() { return mNetid; }
     const char *getSsid() { return mSsid; }
@@ -176,19 +317,14 @@
     int getDefaultKeyIndex() { return mDefaultKeyIndex; }
     int getPriority() { return mPriority; }
     const char *getHiddenSsid() { return mHiddenSsid; }
-    uint32_t getAllowedKeyManagement() { return mAllowedKeyManagement; }
-    uint32_t getAllowedProtocols() { return mAllowedProtocols; }
-    uint32_t getAllowedAuthAlgorithms() { return mAllowedAuthAlgorithms; }
-    uint32_t getAllowedPairwiseCiphers() { return mAllowedPairwiseCiphers; }
-    uint32_t getAllowedGroupCiphers() { return mAllowedGroupCiphers; }
+    uint32_t getKeyManagement() { return mKeyManagement; }
+    uint32_t getProtocols() { return mProtocols; }
+    uint32_t getAuthAlgorithms() { return mAuthAlgorithms; }
+    uint32_t getPairwiseCiphers() { return mPairwiseCiphers; }
+    uint32_t getGroupCiphers() { return mGroupCiphers; }
     bool getEnabled() { return mEnabled; }
     Controller *getController() { return (Controller *) mController; }
 
-    int set(const char *name, const char *value);
-    const char *get(const char *name, char *buffer, size_t maxsize);
-
-    InterfaceConfig *getIfaceCfg() { return mIfaceCfg; }
-
     int setEnabled(bool enabled);
     int setSsid(const char *ssid);
     int setBssid(const char *bssid);
@@ -197,14 +333,22 @@
     int setDefaultKeyIndex(int idx);
     int setPriority(int pri);
     int setHiddenSsid(const char *ssid);
-    int setAllowedKeyManagement(uint32_t mask);
-    int setAllowedProtocols(uint32_t mask);
-    int setAllowedAuthAlgorithms(uint32_t mask);
-    int setAllowedPairwiseCiphers(uint32_t mask);
-    int setAllowedGroupCiphers(uint32_t mask);
+    int setKeyManagement(uint32_t mask);
+    int setProtocols(uint32_t mask);
+    int setAuthAlgorithms(uint32_t mask);
+    int setPairwiseCiphers(uint32_t mask);
+    int setGroupCiphers(uint32_t mask);
 
     // XXX:Should this really be exposed?.. meh
     int refresh();
+
+private:
+    int parseKeyManagementMask(const char *buffer, uint32_t *mask);
+    int parseProtocolsMask(const char *buffer, uint32_t *mask);
+    int parseAuthAlgorithmsMask(const char *buffer, uint32_t *mask);
+    int parsePairwiseCiphersMask(const char *buffer, uint32_t *mask);
+    int parseGroupCiphersMask(const char *buffer, uint32_t *mask);
+    void createProperties();
 };
 
 typedef android::List<WifiNetwork *> WifiNetworkCollection;
diff --git a/nexus/WifiStatusPoller.cpp b/nexus/WifiStatusPoller.cpp
new file mode 100644
index 0000000..cf71733
--- /dev/null
+++ b/nexus/WifiStatusPoller.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#define LOG_TAG "WifiStatusPoller"
+#include <cutils/log.h>
+
+#include "WifiStatusPoller.h"
+#include "IWifiStatusPollerHandler.h"
+
+
+WifiStatusPoller::WifiStatusPoller(IWifiStatusPollerHandler *handler) :
+                  mHandlers(handler) {
+    mPollingInterval = 5;
+    mStarted = false;
+}
+
+int WifiStatusPoller::start() {
+
+    if (pipe(mCtrlPipe))
+        return -1;
+
+    if (pthread_create(&mThread, NULL, WifiStatusPoller::threadStart, this))
+        return -1;
+
+    return 0;
+}
+
+int WifiStatusPoller::stop() {
+    char c = 0;
+
+    if (write(mCtrlPipe[1], &c, 1) != 1) {
+        LOGE("Error writing to control pipe (%s)", strerror(errno));
+        return -1;
+    }
+
+    void *ret;
+    if (pthread_join(mThread, &ret)) {
+        LOGE("Error joining to listener thread (%s)", strerror(errno));
+        return -1;
+    }
+    close(mCtrlPipe[0]);
+    close(mCtrlPipe[1]);
+    return 0;
+}
+
+void *WifiStatusPoller::threadStart(void *obj) {
+    WifiStatusPoller *me = reinterpret_cast<WifiStatusPoller *>(obj);
+
+    me->mStarted = true;
+    LOGD("Starting");
+    me->run();
+    me->mStarted = false;
+    LOGD("Stopping");
+    pthread_exit(NULL);
+    return NULL;
+}
+
+void WifiStatusPoller::run() {
+
+    while(1) {
+        struct timeval to;
+        fd_set read_fds;
+        int rc = 0;
+        int max = 0;
+
+        FD_ZERO(&read_fds);
+        to.tv_usec = 0;
+        to.tv_sec = mPollingInterval;
+
+        FD_SET(mCtrlPipe[0], &read_fds);
+        max = mCtrlPipe[0];
+
+        if ((rc = select(max + 1, &read_fds, NULL, NULL, &to)) < 0) {
+            LOGE("select failed (%s)", strerror(errno));
+            sleep(1);
+            continue;
+        } else if (!rc) {
+            mHandlers->onStatusPollInterval();
+        }
+        if (FD_ISSET(mCtrlPipe[0], &read_fds))
+            break;
+    }
+}
diff --git a/nexus/WifiStatusPoller.h b/nexus/WifiStatusPoller.h
new file mode 100644
index 0000000..202bbbf
--- /dev/null
+++ b/nexus/WifiStatusPoller.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _WIFI_STATUS_POLLER_H
+#define _WIFI_STATUS_POLLER_H
+
+#include <pthread.h>
+
+class IWifiStatusPollerHandler;
+
+class WifiStatusPoller {
+    pthread_t                mThread;
+    int                      mCtrlPipe[2];
+    int                      mPollingInterval;
+    IWifiStatusPollerHandler *mHandlers;
+    bool                     mStarted;
+
+public:
+    WifiStatusPoller(IWifiStatusPollerHandler *handler);
+    virtual ~WifiStatusPoller() {}
+
+    int start();
+    int stop();
+    bool isStarted() { return mStarted; }
+
+    void setPollingInterval(int interval);
+    int getPollingInterval() { return mPollingInterval; }
+    
+private:
+    static void *threadStart(void *obj);
+    void run();
+};
+
+#endif