resolved conflicts for merge of e36a3a2f to jb-dev-plus-aosp

Change-Id: I566b0e96327f318313c17a11d35539554b075f59
diff --git a/Android.mk b/Android.mk
index c20898b..655038d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -6,8 +6,11 @@
                   BandwidthController.cpp              \
                   CommandListener.cpp                  \
                   DnsProxyListener.cpp                 \
+                  MDnsSdListener.cpp                   \
+                  IdletimerController.cpp              \
                   NatController.cpp                    \
                   NetdCommand.cpp                      \
+                  NetdConstants.cpp                    \
                   NetlinkHandler.cpp                   \
                   NetlinkManager.cpp                   \
                   PanController.cpp                    \
@@ -27,15 +30,17 @@
 LOCAL_C_INCLUDES := $(KERNEL_HEADERS) \
                     $(LOCAL_PATH)/../bluetooth/bluedroid/include \
                     $(LOCAL_PATH)/../bluetooth/bluez-clean-headers \
+                    external/mdnsresponder/mDNSShared \
                     external/openssl/include \
                     external/stlport/stlport \
                     bionic \
+                    bionic/libc/private \
                     $(call include-path-for, libhardware_legacy)/hardware_legacy
 
-LOCAL_CFLAGS :=
+LOCAL_CFLAGS := -Werror=format
 
 LOCAL_SHARED_LIBRARIES := libstlport libsysutils libcutils libnetutils \
-                          libcrypto libhardware_legacy
+                          libcrypto libhardware_legacy libmdnssd
 
 ifneq ($(BOARD_HOSTAPD_DRIVER),)
   LOCAL_CFLAGS += -DHAVE_HOSTAPD
diff --git a/BandwidthController.cpp b/BandwidthController.cpp
index 4c15394..31cdcab 100644
--- a/BandwidthController.cpp
+++ b/BandwidthController.cpp
@@ -41,18 +41,16 @@
 #include <cutils/log.h>
 #include <cutils/properties.h>
 
-extern "C" int logwrap(int argc, const char **argv, int background);
+extern "C" int logwrap(int argc, const char **argv);
 extern "C" int system_nosh(const char *command);
 
+#include "NetdConstants.h"
 #include "BandwidthController.h"
-#include "oem_iptables_hook.h"
 
 /* Alphabetical */
-const char BandwidthController::ALERT_IPT_TEMPLATE[] = "%s %s %s -m quota2 ! --quota %lld --name %s";
+#define ALERT_IPT_TEMPLATE "%s %s %s -m quota2 ! --quota %lld --name %s"
 const int  BandwidthController::ALERT_RULE_POS_IN_COSTLY_CHAIN = 4;
 const char BandwidthController::ALERT_GLOBAL_NAME[] = "globalAlert";
-const char BandwidthController::IP6TABLES_PATH[] = "/system/bin/ip6tables";
-const char BandwidthController::IPTABLES_PATH[] = "/system/bin/iptables";
 const int  BandwidthController::MAX_CMD_ARGS = 32;
 const int  BandwidthController::MAX_CMD_LEN = 1024;
 const int  BandwidthController::MAX_IFACENAME_LEN = 64;
@@ -64,33 +62,32 @@
  * Some comments about the rules:
  *  * Ordering
  *    - when an interface is marked as costly it should be INSERTED into the INPUT/OUTPUT chains.
- *      E.g. "-I INPUT -i rmnet0 --goto costly"
+ *      E.g. "-I bw_INPUT -i rmnet0 --jump costly"
  *    - quota'd rules in the costly chain should be before penalty_box lookups.
+ *    - the qtaguid counting is done at the end of the bw_INPUT/bw_OUTPUT user chains.
  *
  * * global quota vs per interface quota
  *   - global quota for all costly interfaces uses a single costly chain:
  *    . initial rules
  *      iptables -N costly_shared
- *      iptables -I INPUT -i iface0 --goto costly_shared
- *      iptables -I OUTPUT -o iface0 --goto costly_shared
+ *      iptables -I bw_INPUT -i iface0 --jump costly_shared
+ *      iptables -I bw_OUTPUT -o iface0 --jump costly_shared
  *      iptables -I costly_shared -m quota \! --quota 500000 \
  *          --jump REJECT --reject-with icmp-net-prohibited
  *      iptables -A costly_shared --jump penalty_box
- *      iptables -A costly_shared -m owner --socket-exists
  *
  *    . adding a new iface to this, E.g.:
- *      iptables -I INPUT -i iface1 --goto costly_shared
- *      iptables -I OUTPUT -o iface1 --goto costly_shared
+ *      iptables -I bw_INPUT -i iface1 --jump costly_shared
+ *      iptables -I bw_OUTPUT -o iface1 --jump costly_shared
  *
  *   - quota per interface. This is achieve by having "costly" chains per quota.
  *     E.g. adding a new costly interface iface0 with its own quota:
  *      iptables -N costly_iface0
- *      iptables -I INPUT -i iface0 --goto costly_iface0
- *      iptables -I OUTPUT -o iface0 --goto costly_iface0
+ *      iptables -I bw_INPUT -i iface0 --jump costly_iface0
+ *      iptables -I bw_OUTPUT -o iface0 --jump costly_iface0
  *      iptables -A costly_iface0 -m quota \! --quota 500000 \
  *          --jump REJECT --reject-with icmp-net-prohibited
  *      iptables -A costly_iface0 --jump penalty_box
- *      iptables -A costly_iface0 -m owner --socket-exists
  *
  * * penalty_box handling:
  *  - only one penalty_box for all interfaces
@@ -98,58 +95,89 @@
  *    iptables -A penalty_box -m owner --uid-owner app_3 \
  *        --jump REJECT --reject-with icmp-net-prohibited
  */
-const char *BandwidthController::IPT_CLEANUP_COMMANDS[] = {
-    /* Cleanup rules. */
-    "-F",
-    "-t raw -F",
-    /* TODO: If at some point we need more user chains than here, then we will need
-     * a different cleanup approach.
+const char *BandwidthController::IPT_FLUSH_COMMANDS[] = {
+    /*
+     * Cleanup rules.
+     * Should normally include costly_<iface>, but we rely on the way they are setup
+     * to allow coexistance.
      */
-    "-X",  /* Should normally only be costly_shared, penalty_box, and costly_<iface>  */
+    "-F bw_INPUT",
+    "-F bw_OUTPUT",
+    "-F bw_FORWARD",
+    "-F penalty_box",
+    "-F costly_shared",
+
+    "-t raw -F bw_raw_PREROUTING",
+    "-t mangle -F bw_mangle_POSTROUTING",
+};
+
+/* The cleanup commands assume flushing has been done. */
+const char *BandwidthController::IPT_CLEANUP_COMMANDS[] = {
+    /* Delete hooks to custom chains. */
+    "-D INPUT -j bw_INPUT",
+    "-D OUTPUT -j bw_OUTPUT",
+    "-D FORWARD -j bw_FORWARD",
+
+    "-t raw -D bw_raw_PREROUTING",
+    "-t mangle -D bw_mangle_POSTROUTING",
+
+    "-X bw_INPUT",
+    "-X bw_OUTPUT",
+    "-X bw_FORWARD",
+    "-X penalty_box",
+    "-X costly_shared",
+
+    "-t raw -X bw_raw_PREROUTING",
+    "-t mangle -X bw_mangle_POSTROUTING",
 };
 
 const char *BandwidthController::IPT_SETUP_COMMANDS[] = {
     /* Created needed chains. */
+    "-N bw_INPUT",
+    "-A INPUT -j bw_INPUT",
+
+    "-N bw_OUTPUT",
+    "-A OUTPUT -j bw_OUTPUT",
+
+    "-N bw_FORWARD",
+    "-I FORWARD -j bw_FORWARD",
+
     "-N costly_shared",
     "-N penalty_box",
+
+    "-t raw -N bw_raw_PREROUTING",
+    "-t raw -A PREROUTING -j bw_raw_PREROUTING",
+    "-t mangle -N bw_mangle_POSTROUTING",
+    "-t mangle -A POSTROUTING -j bw_mangle_POSTROUTING",
 };
 
 const char *BandwidthController::IPT_BASIC_ACCOUNTING_COMMANDS[] = {
-    "-F INPUT",
-    "-A INPUT -i lo --jump ACCEPT",
-    "-A INPUT -m owner --socket-exists", /* This is a tracking rule. */
+    "-A bw_INPUT -i lo --jump RETURN",
+    "-A bw_INPUT -m owner --socket-exists", /* This is a tracking rule. */
 
-    "-F OUTPUT",
-    "-A OUTPUT -o lo --jump ACCEPT",
-    "-A OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
+    "-A bw_OUTPUT -o lo --jump RETURN",
+    "-A bw_OUTPUT -m owner --socket-exists", /* This is a tracking rule. */
 
-    "-F costly_shared",
     "-A costly_shared --jump penalty_box",
-    "-A costly_shared -m owner --socket-exists", /* This is a tracking rule. */
-    /* TODO(jpa): Figure out why iptables doesn't correctly return from this
-     * chain. For now, hack the chain exit with an ACCEPT.
-     */
-    "-A costly_shared --jump ACCEPT",
+
+    "-t raw -A bw_raw_PREROUTING ! -i lo+ -m owner --socket-exists", /* This is a tracking rule. */
+    "-t mangle -A bw_mangle_POSTROUTING ! -o lo+ -m owner --socket-exists", /* This is a tracking rule. */
 };
 
 BandwidthController::BandwidthController(void) {
     char value[PROPERTY_VALUE_MAX];
 
-    property_get("persist.bandwidth.enable", value, "0");
-    if (!strcmp(value, "1")) {
-        enableBandwidthControl();
-    }
-
     property_get("persist.bandwidth.uselogwrap", value, "0");
     useLogwrapCall = !strcmp(value, "1");
 }
 
-int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling) {
+int BandwidthController::runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling,
+                                         IptFailureLog failureHandling) {
     int res = 0;
 
-    LOGV("runIpxtablesCmd(cmd=%s)", cmd);
-    res |= runIptablesCmd(cmd, rejectHandling, IptIpV4);
-    res |= runIptablesCmd(cmd, rejectHandling, IptIpV6);
+    ALOGV("runIpxtablesCmd(cmd=%s)", cmd);
+    res |= runIptablesCmd(cmd, rejectHandling, IptIpV4, failureHandling);
+    res |= runIptablesCmd(cmd, rejectHandling, IptIpV6, failureHandling);
     return res;
 }
 
@@ -161,7 +189,7 @@
 }
 
 int BandwidthController::runIptablesCmd(const char *cmd, IptRejectOp rejectHandling,
-                                        IptIpVer iptVer) {
+                                        IptIpVer iptVer, IptFailureLog failureHandling) {
     char buffer[MAX_CMD_LEN];
     const char *argv[MAX_CMD_ARGS];
     int argc = 0;
@@ -190,7 +218,7 @@
         res = system_nosh(fullCmd.c_str());
     } else {
         if (StrncpyAndCheck(buffer, fullCmd.c_str(), sizeof(buffer))) {
-            LOGE("iptables command too long");
+            ALOGE("iptables command too long");
             return -1;
         }
 
@@ -198,22 +226,45 @@
         while ((tmp = strsep(&next, " "))) {
             argv[argc++] = tmp;
             if (argc >= MAX_CMD_ARGS) {
-                LOGE("iptables argument overflow");
+                ALOGE("iptables argument overflow");
                 return -1;
             }
         }
 
         argv[argc] = NULL;
-        res = logwrap(argc, argv, 0);
+        res = logwrap(argc, argv);
     }
-    if (res) {
-        LOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res);
+    if (res && failureHandling == IptFailShow) {
+        ALOGE("runIptablesCmd(): failed %s res=%d", fullCmd.c_str(), res);
     }
     return res;
 }
 
-int BandwidthController::enableBandwidthControl(void) {
+int BandwidthController::setupIptablesHooks(void) {
+
+    /* Some of the initialCommands are allowed to fail */
+    runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
+            IPT_FLUSH_COMMANDS, RunCmdFailureOk);
+
+    runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
+            IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
+
+    runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
+            IPT_SETUP_COMMANDS, RunCmdFailureBad);
+
+    return 0;
+
+}
+
+int BandwidthController::enableBandwidthControl(bool force) {
     int res;
+    char value[PROPERTY_VALUE_MAX];
+
+    if (!force) {
+            property_get("persist.bandwidth.enable", value, "1");
+            if (!strcmp(value, "0"))
+                    return 0;
+    }
 
     /* Let's pretend we started from scratch ... */
     sharedQuotaIfaces.clear();
@@ -223,39 +274,36 @@
     globalAlertTetherCount = 0;
     sharedQuotaBytes = sharedAlertBytes = 0;
 
+    res = runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
+            IPT_FLUSH_COMMANDS, RunCmdFailureOk);
 
-    /* Some of the initialCommands are allowed to fail */
-    runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
-            IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
-    runCommands(sizeof(IPT_SETUP_COMMANDS) / sizeof(char*),
-            IPT_SETUP_COMMANDS, RunCmdFailureOk);
-    res = runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
+    res |= runCommands(sizeof(IPT_BASIC_ACCOUNTING_COMMANDS) / sizeof(char*),
             IPT_BASIC_ACCOUNTING_COMMANDS, RunCmdFailureBad);
 
-    setupOemIptablesHook();
-
     return res;
 
 }
 
 int BandwidthController::disableBandwidthControl(void) {
-    /* The IPT_CLEANUP_COMMANDS are allowed to fail. */
-    runCommands(sizeof(IPT_CLEANUP_COMMANDS) / sizeof(char*),
-            IPT_CLEANUP_COMMANDS, RunCmdFailureOk);
-    setupOemIptablesHook();
+    runCommands(sizeof(IPT_FLUSH_COMMANDS) / sizeof(char*),
+            IPT_FLUSH_COMMANDS, RunCmdFailureOk);
     return 0;
 }
 
 int BandwidthController::runCommands(int numCommands, const char *commands[],
                                      RunCmdErrHandling cmdErrHandling) {
     int res = 0;
-    LOGV("runCommands(): %d commands", numCommands);
+    IptFailureLog failureLogging = IptFailShow;
+    if (cmdErrHandling == RunCmdFailureOk) {
+        failureLogging = IptFailHide;
+    }
+    ALOGV("runCommands(): %d commands", numCommands);
     for (int cmdNum = 0; cmdNum < numCommands; cmdNum++) {
-        res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd);
-        if (res && cmdErrHandling != RunCmdFailureBad)
+        res = runIpxtablesCmd(commands[cmdNum], IptRejectNoAdd, failureLogging);
+        if (res && cmdErrHandling != RunCmdFailureOk)
             return res;
     }
-    return cmdErrHandling == RunCmdFailureBad ? res : 0;
+    return 0;
 }
 
 std::string BandwidthController::makeIptablesNaughtyCmd(IptOp op, int uid) {
@@ -296,6 +344,7 @@
     IptOp op;
     int appUids[numUids];
     std::string naughtyCmd;
+    std::list<int /*uid*/>::iterator it;
 
     switch (appOp) {
     case NaughtyAppOpAdd:
@@ -306,20 +355,44 @@
         op = IptOpDelete;
         failLogTemplate = "Failed to delete app uid %d from penalty box.";
         break;
+    default:
+        ALOGE("Unexpected app Op %d", appOp);
+        return -1;
     }
 
     for (uidNum = 0; uidNum < numUids; uidNum++) {
         appUids[uidNum] = atol(appStrUids[uidNum]);
         if (appUids[uidNum] == 0) {
-            LOGE(failLogTemplate, appUids[uidNum]);
+            ALOGE(failLogTemplate, appUids[uidNum]);
             goto fail_parse;
         }
     }
 
     for (uidNum = 0; uidNum < numUids; uidNum++) {
-        naughtyCmd = makeIptablesNaughtyCmd(op, appUids[uidNum]);
+        int uid = appUids[uidNum];
+        for (it = naughtyAppUids.begin(); it != naughtyAppUids.end(); it++) {
+            if (*it == uid)
+                break;
+        }
+        bool found = (it != naughtyAppUids.end());
+
+        if (appOp == NaughtyAppOpRemove) {
+            if (!found) {
+                ALOGE("No such appUid %d to remove", uid);
+                return -1;
+            }
+            naughtyAppUids.erase(it);
+        } else {
+            if (found) {
+                ALOGE("appUid %d exists already", uid);
+                return -1;
+            }
+            naughtyAppUids.push_front(uid);
+        }
+
+        naughtyCmd = makeIptablesNaughtyCmd(op, uid);
         if (runIpxtablesCmd(naughtyCmd.c_str(), IptRejectAdd)) {
-            LOGE(failLogTemplate, appUids[uidNum]);
+            ALOGE(failLogTemplate, uid);
             goto fail_with_uidNum;
         }
     }
@@ -338,7 +411,7 @@
     char *buff;
     const char *opFlag;
 
-    LOGV("makeIptablesQuotaCmd(%d, %lld)", op, quota);
+    ALOGV("makeIptablesQuotaCmd(%d, %lld)", op, quota);
 
     switch (op) {
     case IptOpInsert:
@@ -363,7 +436,7 @@
 
 int BandwidthController::prepCostlyIface(const char *ifn, QuotaType quotaType) {
     char cmd[MAX_CMD_LEN];
-    int res = 0;
+    int res = 0, res1, res2;
     int ruleInsertPos = 1;
     std::string costString;
     const char *costCString;
@@ -374,30 +447,43 @@
         costString = "costly_";
         costString += ifn;
         costCString = costString.c_str();
-        snprintf(cmd, sizeof(cmd), "-N %s", costCString);
-        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
-        snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
-        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
-        snprintf(cmd, sizeof(cmd), "-A %s -m owner --socket-exists", costCString);
-        res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
-        /* TODO(jpa): Figure out why iptables doesn't correctly return from this
-         * chain. For now, hack the chain exit with an ACCEPT.
+        /*
+         * Flush the costly_<iface> is allowed to fail in case it didn't exist.
+         * Creating a new one is allowed to fail in case it existed.
+         * This helps with netd restarts.
          */
-        snprintf(cmd, sizeof(cmd), "-A %s --jump ACCEPT", costCString);
+        snprintf(cmd, sizeof(cmd), "-F %s", costCString);
+        res1 = runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide);
+        snprintf(cmd, sizeof(cmd), "-N %s", costCString);
+        res2 = runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide);
+        res = (res1 && res2) || (!res1 && !res2);
+
+        snprintf(cmd, sizeof(cmd), "-A %s -j penalty_box", costCString);
         res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
         break;
     case QuotaShared:
         costCString = "costly_shared";
         break;
+    default:
+        ALOGE("Unexpected quotatype %d", quotaType);
+        return -1;
     }
 
     if (globalAlertBytes) {
         /* The alert rule comes 1st */
         ruleInsertPos = 2;
     }
-    snprintf(cmd, sizeof(cmd), "-I INPUT %d -i %s --goto %s", ruleInsertPos, ifn, costCString);
+
+    snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
+    runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide);
+
+    snprintf(cmd, sizeof(cmd), "-I bw_INPUT %d -i %s --jump %s", ruleInsertPos, ifn, costCString);
     res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
-    snprintf(cmd, sizeof(cmd), "-I OUTPUT %d -o %s --goto %s", ruleInsertPos, ifn, costCString);
+
+    snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
+    runIpxtablesCmd(cmd, IptRejectNoAdd, IptFailHide);
+
+    snprintf(cmd, sizeof(cmd), "-I bw_OUTPUT %d -o %s --jump %s", ruleInsertPos, ifn, costCString);
     res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
     return res;
 }
@@ -417,11 +503,14 @@
     case QuotaShared:
         costCString = "costly_shared";
         break;
+    default:
+        ALOGE("Unexpected quotatype %d", quotaType);
+        return -1;
     }
 
-    snprintf(cmd, sizeof(cmd), "-D INPUT -i %s --goto %s", ifn, costCString);
+    snprintf(cmd, sizeof(cmd), "-D bw_INPUT -i %s --jump %s", ifn, costCString);
     res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
-    snprintf(cmd, sizeof(cmd), "-D OUTPUT -o %s --goto %s", ifn, costCString);
+    snprintf(cmd, sizeof(cmd), "-D bw_OUTPUT -o %s --jump %s", ifn, costCString);
     res |= runIpxtablesCmd(cmd, IptRejectNoAdd);
 
     /* The "-N costly_shared" is created upfront, no need to handle it here. */
@@ -446,11 +535,11 @@
 
     if (!maxBytes) {
         /* Don't talk about -1, deprecate it. */
-        LOGE("Invalid bytes value. 1..max_int64.");
+        ALOGE("Invalid bytes value. 1..max_int64.");
         return -1;
     }
     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
-        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
+        ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
         return -1;
     }
     ifaceName = ifn;
@@ -471,7 +560,7 @@
             quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
             res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
             if (res) {
-                LOGE("Failed set quota rule");
+                ALOGE("Failed set quota rule");
                 goto fail;
             }
             sharedQuotaBytes = maxBytes;
@@ -483,7 +572,7 @@
     if (maxBytes != sharedQuotaBytes) {
         res |= updateQuota(costName, maxBytes);
         if (res) {
-            LOGE("Failed update quota for %s", costName);
+            ALOGE("Failed update quota for %s", costName);
             goto fail;
         }
         sharedQuotaBytes = maxBytes;
@@ -510,7 +599,7 @@
     const char *costName = "shared";
 
     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
-        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
+        ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
         return -1;
     }
     ifaceName = ifn;
@@ -520,7 +609,7 @@
             break;
     }
     if (it == sharedQuotaIfaces.end()) {
-        LOGE("No such iface %s to delete", ifn);
+        ALOGE("No such iface %s to delete", ifn);
         return -1;
     }
 
@@ -550,7 +639,7 @@
 
     if (!maxBytes) {
         /* Don't talk about -1, deprecate it. */
-        LOGE("Invalid bytes value. 1..max_int64.");
+        ALOGE("Invalid bytes value. 1..max_int64.");
         return -1;
     }
     if (maxBytes == -1) {
@@ -558,7 +647,7 @@
     }
 
     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
-        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
+        ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
         return -1;
     }
     ifaceName = ifn;
@@ -575,7 +664,7 @@
         quotaCmd = makeIptablesQuotaCmd(IptOpInsert, costName, maxBytes);
         res |= runIpxtablesCmd(quotaCmd.c_str(), IptRejectAdd);
         if (res) {
-            LOGE("Failed set quota rule");
+            ALOGE("Failed set quota rule");
             goto fail;
         }
 
@@ -584,7 +673,7 @@
     } else {
         res |= updateQuota(costName, maxBytes);
         if (res) {
-            LOGE("Failed update quota for %s", iface);
+            ALOGE("Failed update quota for %s", iface);
             goto fail;
         }
         it->quota = maxBytes;
@@ -615,11 +704,11 @@
     fp = fopen(fname, "r");
     free(fname);
     if (!fp) {
-        LOGE("Reading quota %s failed (%s)", costName, strerror(errno));
+        ALOGE("Reading quota %s failed (%s)", costName, strerror(errno));
         return -1;
     }
     scanRes = fscanf(fp, "%lld", bytes);
-    LOGV("Read quota res=%d bytes=%lld", scanRes, *bytes);
+    ALOGV("Read quota res=%d bytes=%lld", scanRes, *bytes);
     fclose(fp);
     return scanRes == 1 ? 0 : -1;
 }
@@ -633,7 +722,7 @@
     std::list<QuotaInfo>::iterator it;
 
     if (StrncpyAndCheck(ifn, iface, sizeof(ifn))) {
-        LOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
+        ALOGE("Interface name longer than %d", MAX_IFACENAME_LEN);
         return -1;
     }
     ifaceName = ifn;
@@ -645,7 +734,7 @@
     }
 
     if (it == quotaIfaces.end()) {
-        LOGE("No such iface %s to delete", ifn);
+        ALOGE("No such iface %s to delete", ifn);
         return -1;
     }
 
@@ -665,7 +754,7 @@
     fp = fopen(fname, "w");
     free(fname);
     if (!fp) {
-        LOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
+        ALOGE("Updating quota %s failed (%s)", quotaName, strerror(errno));
         return -1;
     }
     fprintf(fp, "%lld\n", bytes);
@@ -693,13 +782,13 @@
     }
 
     ifaceLimiting = "! -i lo+";
-    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "INPUT",
-        bytes, alertName, alertName);
+    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_INPUT",
+        bytes, alertName);
     res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
     free(alertQuotaCmd);
     ifaceLimiting = "! -o lo+";
-    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "OUTPUT",
-        bytes, alertName, alertName);
+    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_OUTPUT",
+        bytes, alertName);
     res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
     free(alertQuotaCmd);
     return res;
@@ -725,8 +814,8 @@
     }
 
     ifaceLimiting = "! -i lo+";
-    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "FORWARD",
-        bytes, alertName, alertName);
+    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, ifaceLimiting, opFlag, "bw_FORWARD",
+        bytes, alertName);
     res = runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
     free(alertQuotaCmd);
     return res;
@@ -737,7 +826,7 @@
     int res = 0;
 
     if (!bytes) {
-        LOGE("Invalid bytes value. 1..max_int64.");
+        ALOGE("Invalid bytes value. 1..max_int64.");
         return -1;
     }
     if (globalAlertBytes) {
@@ -745,7 +834,7 @@
     } else {
         res = runIptablesAlertCmd(IptOpInsert, alertName, bytes);
         if (globalAlertTetherCount) {
-            LOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
+            ALOGV("setGlobalAlert for %d tether", globalAlertTetherCount);
             res |= runIptablesAlertFwdCmd(IptOpInsert, alertName, bytes);
         }
     }
@@ -758,7 +847,7 @@
     int res = 0;
 
     globalAlertTetherCount++;
-    LOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
+    ALOGV("setGlobalAlertInForwardChain(): %d tether", globalAlertTetherCount);
 
     /*
      * If there is no globalAlert active we are done.
@@ -780,7 +869,7 @@
     int res = 0;
 
     if (!globalAlertBytes) {
-        LOGE("No prior alert set");
+        ALOGE("No prior alert set");
         return -1;
     }
     res = runIptablesAlertCmd(IptOpDelete, alertName, globalAlertBytes);
@@ -796,7 +885,7 @@
     const char *alertName = ALERT_GLOBAL_NAME;
 
     if (!globalAlertTetherCount) {
-        LOGE("No prior alert set");
+        ALOGE("No prior alert set");
         return -1;
     }
 
@@ -817,11 +906,11 @@
 
 int BandwidthController::setSharedAlert(int64_t bytes) {
     if (!sharedQuotaBytes) {
-        LOGE("Need to have a prior shared quota set to set an alert");
+        ALOGE("Need to have a prior shared quota set to set an alert");
         return -1;
     }
     if (!bytes) {
-        LOGE("Invalid bytes value. 1..max_int64.");
+        ALOGE("Invalid bytes value. 1..max_int64.");
         return -1;
     }
     return setCostlyAlert("shared", bytes, &sharedAlertBytes);
@@ -835,7 +924,7 @@
     std::list<QuotaInfo>::iterator it;
 
     if (!bytes) {
-        LOGE("Invalid bytes value. 1..max_int64.");
+        ALOGE("Invalid bytes value. 1..max_int64.");
         return -1;
     }
     for (it = quotaIfaces.begin(); it != quotaIfaces.end(); it++) {
@@ -844,7 +933,7 @@
     }
 
     if (it == quotaIfaces.end()) {
-        LOGE("Need to have a prior interface quota set to set an alert");
+        ALOGE("Need to have a prior interface quota set to set an alert");
         return -1;
     }
 
@@ -860,7 +949,7 @@
     }
 
     if (it == quotaIfaces.end()) {
-        LOGE("No prior alert set for interface %s", iface);
+        ALOGE("No prior alert set for interface %s", iface);
         return -1;
     }
 
@@ -874,7 +963,7 @@
     char *alertName;
 
     if (!bytes) {
-        LOGE("Invalid bytes value. 1..max_int64.");
+        ALOGE("Invalid bytes value. 1..max_int64.");
         return -1;
     }
     asprintf(&alertName, "%sAlert", costName);
@@ -882,8 +971,7 @@
         res = updateQuota(alertName, *alertBytes);
     } else {
         asprintf(&chainNameAndPos, "costly_%s %d", costName, ALERT_RULE_POS_IN_COSTLY_CHAIN);
-        asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-I", chainNameAndPos, bytes, alertName,
-                 alertName);
+        asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "", "-I", chainNameAndPos, bytes, alertName);
         res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
         free(alertQuotaCmd);
         free(chainNameAndPos);
@@ -901,12 +989,12 @@
 
     asprintf(&alertName, "%sAlert", costName);
     if (!*alertBytes) {
-        LOGE("No prior alert set for %s alert", costName);
+        ALOGE("No prior alert set for %s alert", costName);
         return -1;
     }
 
     asprintf(&chainName, "costly_%s", costName);
-    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "-D", chainName, *alertBytes, alertName, alertName);
+    asprintf(&alertQuotaCmd, ALERT_IPT_TEMPLATE, "", "-D", chainName, *alertBytes, alertName);
     res |= runIpxtablesCmd(alertQuotaCmd, IptRejectNoAdd);
     free(alertQuotaCmd);
     free(chainName);
@@ -918,14 +1006,15 @@
 
 /*
  * Parse the ptks and bytes out of:
- * Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
+ * Chain FORWARD (policy RETURN 0 packets, 0 bytes)
  *     pkts      bytes target     prot opt in     out     source               destination
- *        0        0 ACCEPT     all  --  rmnet0 wlan0   0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
+ *        0        0 RETURN     all  --  rmnet0 wlan0   0.0.0.0/0            0.0.0.0/0            state RELATED,ESTABLISHED
  *        0        0 DROP       all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0            state INVALID
- *        0        0 ACCEPT     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0
+ *        0        0 RETURN     all  --  wlan0  rmnet0  0.0.0.0/0            0.0.0.0/0
  *
  */
-int BandwidthController::parseForwardChainStats(TetherStats &stats, FILE *fp) {
+int BandwidthController::parseForwardChainStats(TetherStats &stats, FILE *fp,
+                                                std::string &extraProcessingInfo) {
     int res;
     char lineBuffer[MAX_IPT_OUTPUT_LINE_LEN];
     char iface0[MAX_IPT_OUTPUT_LINE_LEN];
@@ -938,19 +1027,21 @@
     while (NULL != (buffPtr = fgets(lineBuffer, MAX_IPT_OUTPUT_LINE_LEN, fp))) {
         /* Clean up, so a failed parse can still print info */
         iface0[0] = iface1[0] = rest[0] = packets = bytes = 0;
-        res = sscanf(buffPtr, "%lld %lld ACCEPT all -- %s %s 0.%s",
+        res = sscanf(buffPtr, "%lld %lld RETURN all -- %s %s 0.%s",
                 &packets, &bytes, iface0, iface1, rest);
-        LOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%lld bytes=%lld rest=<%s> orig line=<%s>", res,
+        ALOGV("parse res=%d iface0=<%s> iface1=<%s> pkts=%lld bytes=%lld rest=<%s> orig line=<%s>", res,
              iface0, iface1, packets, bytes, rest, buffPtr);
+        extraProcessingInfo += buffPtr;
+
         if (res != 5) {
             continue;
         }
         if ((stats.ifaceIn == iface0) && (stats.ifaceOut == iface1)) {
-            LOGV("iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
+            ALOGV("iface_in=%s iface_out=%s rx_bytes=%lld rx_packets=%lld ", iface0, iface1, bytes, packets);
             stats.rxPackets = packets;
             stats.rxBytes = bytes;
         } else if ((stats.ifaceOut == iface0) && (stats.ifaceIn == iface1)) {
-            LOGV("iface_in=%s iface_out=%s tx_bytes=%lld tx_packets=%lld ", iface1, iface0, bytes, packets);
+            ALOGV("iface_in=%s iface_out=%s tx_bytes=%lld tx_packets=%lld ", iface1, iface0, bytes, packets);
             stats.txPackets = packets;
             stats.txBytes = bytes;
         }
@@ -967,14 +1058,14 @@
     return msg;
 }
 
-int BandwidthController::getTetherStats(TetherStats &stats) {
+int BandwidthController::getTetherStats(TetherStats &stats, std::string &extraProcessingInfo) {
     int res;
     std::string fullCmd;
     FILE *iptOutput;
     const char *cmd;
 
     if (stats.rxBytes != -1 || stats.txBytes != -1) {
-        LOGE("Unexpected input stats. Byte counts should be -1.");
+        ALOGE("Unexpected input stats. Byte counts should be -1.");
         return -1;
     }
 
@@ -986,13 +1077,14 @@
      * the wanted info.
      */
     fullCmd = IPTABLES_PATH;
-    fullCmd += " -nvx -L FORWARD";
+    fullCmd += " -nvx -L natctrl_FORWARD";
     iptOutput = popen(fullCmd.c_str(), "r");
     if (!iptOutput) {
-            LOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
+            ALOGE("Failed to run %s err=%s", fullCmd.c_str(), strerror(errno));
+            extraProcessingInfo += "Failed to run iptables.";
         return -1;
     }
-    res = parseForwardChainStats(stats, iptOutput);
+    res = parseForwardChainStats(stats, iptOutput, extraProcessingInfo);
     pclose(iptOutput);
 
     /* Currently NatController doesn't do ipv6 tethering, so we are done. */
diff --git a/BandwidthController.h b/BandwidthController.h
index 861c63e..10e6ca2 100644
--- a/BandwidthController.h
+++ b/BandwidthController.h
@@ -46,7 +46,10 @@
     };
 
     BandwidthController();
-    int enableBandwidthControl(void);
+
+    int setupIptablesHooks(void);
+
+    int enableBandwidthControl(bool force);
     int disableBandwidthControl(void);
 
     int setInterfaceSharedQuota(const char *iface, int64_t bytes);
@@ -75,7 +78,7 @@
      * stats should have ifaceIn and ifaceOut initialized.
      * Byte counts should be left to the default (-1).
      */
-    int getTetherStats(TetherStats &stats);
+    int getTetherStats(TetherStats &stats, std::string &extraProcessingInfo);
 
 protected:
     class QuotaInfo {
@@ -93,7 +96,11 @@
     enum NaughtyAppOp { NaughtyAppOpAdd, NaughtyAppOpRemove };
     enum QuotaType { QuotaUnique, QuotaShared };
     enum RunCmdErrHandling { RunCmdFailureBad, RunCmdFailureOk };
-
+#if LOG_NDEBUG
+    enum IptFailureLog { IptFailShow, IptFailHide };
+#else
+    enum IptFailureLog { IptFailShow, IptFailHide = IptFailShow };
+#endif
     int maninpulateNaughtyApps(int numUids, char *appStrUids[], NaughtyAppOp appOp);
 
     int prepCostlyIface(const char *ifn, QuotaType quotaType);
@@ -108,8 +115,11 @@
     /* Runs for both ipv4 and ipv6 iptables */
     int runCommands(int numCommands, const char *commands[], RunCmdErrHandling cmdErrHandling);
     /* Runs for both ipv4 and ipv6 iptables, appends -j REJECT --reject-with ...  */
-    static int runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling);
-    static int runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, IptIpVer iptIpVer);
+    static int runIpxtablesCmd(const char *cmd, IptRejectOp rejectHandling,
+                               IptFailureLog failureHandling = IptFailShow);
+    static int runIptablesCmd(const char *cmd, IptRejectOp rejectHandling, IptIpVer iptIpVer,
+                              IptFailureLog failureHandling = IptFailShow);
+
 
     // Provides strncpy() + check overflow.
     static int StrncpyAndCheck(char *buffer, const char *src, size_t buffSize);
@@ -122,8 +132,10 @@
     /*
      * stats should have ifaceIn and ifaceOut initialized.
      * fp should be a file to the FORWARD rules of iptables.
+     * extraProcessingInfo: contains raw parsed data, and error info.
      */
-    static int parseForwardChainStats(TetherStats &stats, FILE *fp);
+    static int parseForwardChainStats(TetherStats &stats, FILE *fp,
+                                      std::string &extraProcessingInfo);
 
     /*------------------*/
 
@@ -145,16 +157,14 @@
     std::list<int /*appUid*/> naughtyAppUids;
 
 private:
+    static const char *IPT_FLUSH_COMMANDS[];
     static const char *IPT_CLEANUP_COMMANDS[];
     static const char *IPT_SETUP_COMMANDS[];
     static const char *IPT_BASIC_ACCOUNTING_COMMANDS[];
 
     /* Alphabetical */
-    static const char ALERT_IPT_TEMPLATE[];
     static const int  ALERT_RULE_POS_IN_COSTLY_CHAIN;
     static const char ALERT_GLOBAL_NAME[];
-    static const char IP6TABLES_PATH[];
-    static const char IPTABLES_PATH[];
     static const int  MAX_CMD_ARGS;
     static const int  MAX_CMD_LEN;
     static const int  MAX_IFACENAME_LEN;
diff --git a/CommandListener.cpp b/CommandListener.cpp
index 0fed54b..97e2fa2 100644
--- a/CommandListener.cpp
+++ b/CommandListener.cpp
@@ -37,7 +37,9 @@
 #include "ResponseCode.h"
 #include "ThrottleController.h"
 #include "BandwidthController.h"
+#include "IdletimerController.h"
 #include "SecondaryTableController.h"
+#include "oem_iptables_hook.h"
 
 
 TetherController *CommandListener::sTetherCtrl = NULL;
@@ -46,11 +48,12 @@
 PanController *CommandListener::sPanCtrl = NULL;
 SoftapController *CommandListener::sSoftapCtrl = NULL;
 BandwidthController * CommandListener::sBandwidthCtrl = NULL;
+IdletimerController * CommandListener::sIdletimerCtrl = NULL;
 ResolverController *CommandListener::sResolverCtrl = NULL;
 SecondaryTableController *CommandListener::sSecondaryTableCtrl = NULL;
 
 CommandListener::CommandListener() :
-                 FrameworkListener("netd") {
+                 FrameworkListener("netd", true) {
     registerCmd(new InterfaceCmd());
     registerCmd(new IpFwdCmd());
     registerCmd(new TetherCmd());
@@ -60,6 +63,7 @@
     registerCmd(new PanCmd());
     registerCmd(new SoftapCmd());
     registerCmd(new BandwidthControlCmd());
+    registerCmd(new IdletimerControlCmd());
     registerCmd(new ResolverCmd());
 
     if (!sSecondaryTableCtrl)
@@ -76,8 +80,34 @@
         sSoftapCtrl = new SoftapController();
     if (!sBandwidthCtrl)
         sBandwidthCtrl = new BandwidthController();
+    if (!sIdletimerCtrl)
+        sIdletimerCtrl = new IdletimerController();
     if (!sResolverCtrl)
         sResolverCtrl = new ResolverController();
+
+    /*
+     * This is the only time controllers are allowed to touch
+     * top-level chains in iptables.
+     * Each controller should setup custom chains and hook them into
+     * the top-level ones.
+     * THE ORDER IS IMPORTANT. TRIPPLE CHECK EACH setup function.
+     */
+    /* Does DROP in nat: PREROUTING, FORWARD, OUTPUT */
+    setupOemIptablesHook();
+    /* Does DROPs in FORWARD by default */
+    sNatCtrl->setupIptablesHooks();
+    /*
+     * Does REJECT in INPUT, OUTPUT. Does counting also.
+     * No DROP/REJECT allowed later in netfilter-flow hook order.
+     */
+    sBandwidthCtrl->setupIptablesHooks();
+    /*
+     * Counts in nat: PREROUTING, POSTROUTING.
+     * No DROP/REJECT allowed later in netfilter-flow hook order.
+     */
+    sIdletimerCtrl->setupIptablesHooks();
+
+    sBandwidthCtrl->enableBandwidthControl(false);
 }
 
 CommandListener::InterfaceCmd::InterfaceCmd() :
@@ -87,12 +117,12 @@
 int CommandListener::writeFile(const char *path, const char *value, int size) {
     int fd = open(path, O_WRONLY);
     if (fd < 0) {
-        LOGE("Failed to open %s: %s", path, strerror(errno));
+        ALOGE("Failed to open %s: %s", path, strerror(errno));
         return -1;
     }
 
     if (write(fd, value, size) != size) {
-        LOGE("Failed to write %s: %s", path, strerror(errno));
+        ALOGE("Failed to write %s: %s", path, strerror(errno));
         close(fd);
         return -1;
     }
@@ -274,7 +304,7 @@
             }
 
             if (ifc_get_hwaddr(argv[2], (void *) hwaddr)) {
-                LOGW("Failed to retrieve HW addr for %s (%s)", argv[2], strerror(errno));
+                ALOGW("Failed to retrieve HW addr for %s (%s)", argv[2], strerror(errno));
             }
 
             char *addr_s = strdup(inet_ntoa(addr));
@@ -289,7 +319,7 @@
 
             char *flag_s;
 
-            asprintf(&flag_s, "[%s%s%s%s%s%s]", updown, brdcst, loopbk, ppp, running, multi);
+            asprintf(&flag_s, "%s%s%s%s%s%s", updown, brdcst, loopbk, ppp, running, multi);
 
             char *msg = NULL;
             asprintf(&msg, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x %s %d %s",
@@ -305,12 +335,12 @@
             ifc_close();
             return 0;
         } else if (!strcmp(argv[1], "setcfg")) {
-            // arglist: iface addr prefixLength [flags]
+            // arglist: iface addr prefixLength flags
             if (argc < 5) {
                 cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
                 return 0;
             }
-            LOGD("Setting iface cfg");
+            ALOGD("Setting iface cfg");
 
             struct in_addr addr;
             unsigned flags = 0;
@@ -335,43 +365,34 @@
             }
 
             /* Process flags */
-            /* read from "[XX" arg to "YY]" arg */
-            bool bStarted = false;
             for (int i = 5; i < argc; i++) {
                 char *flag = argv[i];
-                if (!bStarted) {
-                    if (*flag == '[') {
-                        flag++;
-                        bStarted = true;
-                    } else {
-                        continue;
-                    }
-                }
-                int len = strlen(flag);
-                if (flag[len-1] == ']') {
-                    i = argc;  // stop after this loop
-                    flag[len-1] = 0;
-                }
                 if (!strcmp(flag, "up")) {
-                    LOGD("Trying to bring up %s", argv[2]);
+                    ALOGD("Trying to bring up %s", argv[2]);
                     if (ifc_up(argv[2])) {
-                        LOGE("Error upping interface");
+                        ALOGE("Error upping interface");
                         cli->sendMsg(ResponseCode::OperationFailed, "Failed to up interface", true);
                         ifc_close();
                         return 0;
                     }
                 } else if (!strcmp(flag, "down")) {
-                    LOGD("Trying to bring down %s", argv[2]);
+                    ALOGD("Trying to bring down %s", argv[2]);
                     if (ifc_down(argv[2])) {
-                        LOGE("Error downing interface");
+                        ALOGE("Error downing interface");
                         cli->sendMsg(ResponseCode::OperationFailed, "Failed to down interface", true);
                         ifc_close();
                         return 0;
                     }
                 } else if (!strcmp(flag, "broadcast")) {
-                    LOGD("broadcast flag ignored");
+                    // currently ignored
                 } else if (!strcmp(flag, "multicast")) {
-                    LOGD("multicast flag ignored");
+                    // currently ignored
+                } else if (!strcmp(flag, "running")) {
+                    // currently ignored
+                } else if (!strcmp(flag, "loopback")) {
+                    // currently ignored
+                } else if (!strcmp(flag, "point-to-point")) {
+                    // currently ignored
                 } else {
                     cli->sendMsg(ResponseCode::CommandParameterError, "Flag unsupported", false);
                     ifc_close();
@@ -384,7 +405,7 @@
             return 0;
         } else if (!strcmp(argv[1], "clearaddrs")) {
             // arglist: iface
-            LOGD("Clearing all IP addresses on %s", argv[2]);
+            ALOGD("Clearing all IP addresses on %s", argv[2]);
 
             ifc_clear_addresses(argv[2]);
 
@@ -847,7 +868,7 @@
 int CommandListener::readInterfaceCounters(const char *iface, unsigned long *rx, unsigned long *tx) {
     FILE *fp = fopen("/proc/net/dev", "r");
     if (!fp) {
-        LOGE("Failed to open /proc/net/dev (%s)", strerror(errno));
+        ALOGE("Failed to open /proc/net/dev (%s)", strerror(errno));
         return -1;
     }
 
@@ -914,10 +935,10 @@
         return 0;
     }
 
-    LOGV("bwctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]);
+    ALOGV("bwctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]);
 
     if (!strcmp(argv[1], "enable")) {
-        int rc = sBandwidthCtrl->enableBandwidthControl();
+        int rc = sBandwidthCtrl->enableBandwidthControl(true);
         sendGenericOkFail(cli, rc);
         return 0;
 
@@ -1154,6 +1175,7 @@
     }
     if (!strcmp(argv[1], "gettetherstats") || !strcmp(argv[1], "gts")) {
         BandwidthController::TetherStats tetherStats;
+        std::string extraProcessingInfo = "";
         if (argc != 4) {
             sendGenericSyntaxError(cli, "gettetherstats <interface0> <interface1>");
             return 0;
@@ -1161,9 +1183,10 @@
 
         tetherStats.ifaceIn = argv[2];
         tetherStats.ifaceOut = argv[3];
-        int rc = sBandwidthCtrl->getTetherStats(tetherStats);
+        int rc = sBandwidthCtrl->getTetherStats(tetherStats, extraProcessingInfo);
         if (rc) {
-            sendGenericOpFailed(cli, "Failed to get tethering stats");
+                extraProcessingInfo.insert(0, "Failed to get tethering stats.\n");
+                sendGenericOpFailed(cli, extraProcessingInfo.c_str());
             return 0;
         }
 
@@ -1177,3 +1200,63 @@
     cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown bandwidth cmd", false);
     return 0;
 }
+
+CommandListener::IdletimerControlCmd::IdletimerControlCmd() :
+    NetdCommand("idletimer") {
+}
+
+int CommandListener::IdletimerControlCmd::runCommand(SocketClient *cli, int argc, char **argv) {
+  // TODO(ashish): Change the error statements
+    if (argc < 2) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
+        return 0;
+    }
+
+    ALOGV("idletimerctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]);
+
+    if (!strcmp(argv[1], "enable")) {
+      if (0 != sIdletimerCtrl->enableIdletimerControl()) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
+      } else {
+        cli->sendMsg(ResponseCode::CommandOkay, "Enable success", false);
+      }
+      return 0;
+
+    }
+    if (!strcmp(argv[1], "disable")) {
+      if (0 != sIdletimerCtrl->disableIdletimerControl()) {
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
+      } else {
+        cli->sendMsg(ResponseCode::CommandOkay, "Disable success", false);
+      }
+      return 0;
+    }
+    if (!strcmp(argv[1], "add")) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
+            return 0;
+        }
+        if(0 != sIdletimerCtrl->addInterfaceIdletimer(argv[2], atoi(argv[3]))) {
+          cli->sendMsg(ResponseCode::OperationFailed, "Failed to add interface", false);
+        } else {
+          cli->sendMsg(ResponseCode::CommandOkay,  "Add success", false);
+        }
+        return 0;
+    }
+    if (!strcmp(argv[1], "remove")) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
+            return 0;
+        }
+        // ashish: fixme timeout
+        if (0 != sIdletimerCtrl->removeInterfaceIdletimer(argv[2], atoi(argv[3]))) {
+          cli->sendMsg(ResponseCode::OperationFailed, "Failed to remove interface", false);
+        } else {
+          cli->sendMsg(ResponseCode::CommandOkay, "Remove success", false);
+        }
+        return 0;
+    }
+
+    cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown idletimer cmd", false);
+    return 0;
+}
diff --git a/CommandListener.h b/CommandListener.h
index 0ed600b..a9da6d7 100644
--- a/CommandListener.h
+++ b/CommandListener.h
@@ -26,6 +26,7 @@
 #include "PanController.h"
 #include "SoftapController.h"
 #include "BandwidthController.h"
+#include "IdletimerController.h"
 #include "ResolverController.h"
 #include "SecondaryTableController.h"
 
@@ -36,6 +37,7 @@
     static PanController *sPanCtrl;
     static SoftapController *sSoftapCtrl;
     static BandwidthController *sBandwidthCtrl;
+    static IdletimerController *sIdletimerCtrl;
     static ResolverController *sResolverCtrl;
     static SecondaryTableController *sSecondaryTableCtrl;
 
@@ -116,6 +118,13 @@
         void sendGenericSyntaxError(SocketClient *cli, const char *usageMsg);
     };
 
+    class IdletimerControlCmd : public NetdCommand {
+    public:
+        IdletimerControlCmd();
+        virtual ~IdletimerControlCmd() {}
+        int runCommand(SocketClient *c, int argc, char ** argv);
+    };
+
     class ResolverCmd : public NetdCommand {
     public:
         ResolverCmd();
diff --git a/DnsProxyListener.cpp b/DnsProxyListener.cpp
index bcb961e..6c09e69 100644
--- a/DnsProxyListener.cpp
+++ b/DnsProxyListener.cpp
@@ -32,6 +32,7 @@
 #include <sysutils/SocketClient.h>
 
 #include "DnsProxyListener.h"
+#include "ResponseCode.h"
 
 DnsProxyListener::DnsProxyListener() :
                  FrameworkListener("dnsproxyd") {
@@ -68,13 +69,16 @@
 
 void DnsProxyListener::GetAddrInfoHandler::run() {
     if (DBG) {
-        LOGD("GetAddrInfoHandler, now for %s / %s", mHost, mService);
+        ALOGD("GetAddrInfoHandler, now for %s / %s", mHost, mService);
     }
 
     struct addrinfo* result = NULL;
-    int rv = getaddrinfo(mHost, mService, mHints, &result);
-    bool success = (mClient->sendData(&rv, sizeof(rv)) == 0);
-    if (rv == 0) {
+    uint32_t rv = getaddrinfo(mHost, mService, mHints, &result);
+    if (rv) {
+        // getaddrinfo failed
+        mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed, &rv, sizeof(rv));
+    } else {
+        bool success = !mClient->sendCode(ResponseCode::DnsProxyQueryResult);
         struct addrinfo* ai = result;
         while (ai && success) {
             success = sendLenAndData(mClient, sizeof(struct addrinfo), ai)
@@ -85,13 +89,13 @@
             ai = ai->ai_next;
         }
         success = success && sendLenAndData(mClient, 0, "");
+        if (!success) {
+            ALOGW("Error writing DNS result to client");
+        }
     }
     if (result) {
         freeaddrinfo(result);
     }
-    if (!success) {
-        LOGW("Error writing DNS result to client");
-    }
     mClient->decRef();
 }
 
@@ -103,12 +107,15 @@
                                             int argc, char **argv) {
     if (DBG) {
         for (int i = 0; i < argc; i++) {
-            LOGD("argv[%i]=%s", i, argv[i]);
+            ALOGD("argv[%i]=%s", i, argv[i]);
         }
     }
     if (argc != 7) {
-        LOGW("Invalid number of arguments to getaddrinfo: %i", argc);
-        sendLenAndData(cli, 0, NULL);
+        char* msg = NULL;
+        asprintf( &msg, "Invalid number of arguments to getaddrinfo: %i", argc);
+        ALOGW("%s", msg);
+        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+        free(msg);
         return -1;
     }
 
@@ -141,7 +148,7 @@
     }
 
     if (DBG) {
-        LOGD("GetAddrInfoHandler for %s / %s",
+        ALOGD("GetAddrInfoHandler for %s / %s",
              name ? name : "[nullhost]",
              service ? service : "[nullservice]");
     }
@@ -165,12 +172,16 @@
                                             int argc, char **argv) {
     if (DBG) {
         for (int i = 0; i < argc; i++) {
-            LOGD("argv[%i]=%s", i, argv[i]);
+            ALOGD("argv[%i]=%s", i, argv[i]);
         }
     }
+
     if (argc != 4) {
-        LOGW("Invalid number of arguments to gethostbyaddr: %i", argc);
-        sendLenAndData(cli, 0, NULL);
+        char* msg = NULL;
+        asprintf(&msg, "Invalid number of arguments to gethostbyaddr: %i", argc);
+        ALOGW("%s", msg);
+        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+        free(msg);
         return -1;
     }
 
@@ -182,9 +193,12 @@
     errno = 0;
     int result = inet_pton(addrFamily, addrStr, addr);
     if (result <= 0) {
-        LOGW("inet_pton(\"%s\") failed %s", addrStr, strerror(errno));
+        char* msg = NULL;
+        asprintf(&msg, "inet_pton(\"%s\") failed %s", addrStr, strerror(errno));
+        ALOGW("%s", msg);
+        cli->sendMsg(ResponseCode::OperationFailed, msg, false);
         free(addr);
-        sendLenAndData(cli, 0, NULL);
+        free(msg);
         return -1;
     }
 
@@ -215,7 +229,7 @@
 
 void DnsProxyListener::GetHostByAddrHandler::run() {
     if (DBG) {
-        LOGD("DnsProxyListener::GetHostByAddrHandler::run\n");
+        ALOGD("DnsProxyListener::GetHostByAddrHandler::run\n");
     }
 
     struct hostent* hp;
@@ -224,17 +238,25 @@
     hp = gethostbyaddr((char*)mAddress, mAddressLen, mAddressFamily);
 
     if (DBG) {
-        LOGD("GetHostByAddrHandler::run gethostbyaddr errno: %s hp->h_name = %s, name_len = %d\n",
+        ALOGD("GetHostByAddrHandler::run gethostbyaddr errno: %s hp->h_name = %s, name_len = %d\n",
                 hp ? "success" : strerror(errno),
                 (hp && hp->h_name) ? hp->h_name: "null",
                 (hp && hp->h_name) ? strlen(hp->h_name)+ 1 : 0);
     }
 
-    bool success = sendLenAndData(mClient, (hp && hp->h_name) ? strlen(hp->h_name)+ 1 : 0,
-            (hp && hp->h_name) ? hp->h_name : "");
+    bool failed = true;
+    if (hp) {
+        failed = mClient->sendBinaryMsg(ResponseCode::DnsProxyQueryResult,
+                                        hp->h_name ? hp->h_name : "",
+                                        hp->h_name ? strlen(hp->h_name)+ 1 : 0);
+    } else {
+        uint32_t error = h_errno;
+        failed = mClient->sendBinaryMsg(ResponseCode::DnsProxyOperationFailed,
+                                        &error, sizeof(error));
+    }
 
-    if (!success) {
-        LOGW("GetHostByAddrHandler: Error writing DNS result to client\n");
+    if (failed) {
+        ALOGW("GetHostByAddrHandler: Error writing DNS result to client\n");
     }
     mClient->decRef();
 }
diff --git a/IdletimerController.cpp b/IdletimerController.cpp
new file mode 100644
index 0000000..efe4f09
--- /dev/null
+++ b/IdletimerController.cpp
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// #define LOG_NDEBUG 0
+
+/*
+ * MODUS OPERANDI
+ * --------------
+ *
+ * IPTABLES command sequence:
+ *
+ * iptables -F
+ *
+ * iptables -t nat -F idletimer_PREROUTING
+ * iptables -t nat -F idletimer_POSTROUTING
+ *
+ *
+ * iptables -t nat -N idletimer_PREROUTING
+ * iptables -t nat -N idletimer_POSTROUTING
+ *
+ * iptables -t nat -D PREROUTING -j idletimer_PREROUTING
+ * iptables -t nat -D POSTROUTING -j idletimer_POSTROUTING
+ *
+ *
+ * iptables -t nat -I PREROUTING -j idletimer_PREROUTING
+ * iptables -t nat -I POSTROUTING -j idletimer_POSTROUTING
+ *
+ * # For notifications to work the lable name must match the name of a valid interface.
+ * # If the label name does match an interface, the rules will be a no-op.
+ *
+ * iptables -t nat -A idletimer_PREROUTING -i rmnet0 -j IDLETIMER  --timeout 5 --label test-chain --send_nl_msg 1
+ * iptables -t nat -A idletimer_POSTROUTING -o rmnet0 -j IDLETIMER  --timeout 5 --label test-chain --send_nl_msg 1
+ *
+ * iptables -nxvL -t nat
+ *
+ * =================
+ *
+ * ndc command sequence
+ * ------------------
+ * ndc idletimer enable
+ * ndc idletimer add <iface> <timeout>
+ * ndc idletimer remove <iface> <timeout>
+ *
+ * Monitor effect on the iptables chains after each step using:
+ *     iptables -nxvL -t nat
+ *
+ * Remember that the timeout value has to be same at the time of the
+ * removal.
+ *
+ * Note that currently if the name of the iface is incorrect, iptables
+ * will setup rules without checking if it is the name of a valid
+ * interface (although no notifications will ever be received).  It is
+ * the responsibility of code in Java land to ensure that the interface name
+ * is correct. The benefit of this, is that idletimers can be setup on
+ * interfaces than come and go.
+ *
+ * A remove should be called for each add command issued during cleanup, as duplicate
+ * entries of the rule may exist and will all have to removed.
+ *
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <string.h>
+#include <cutils/properties.h>
+
+#define LOG_TAG "IdletimerController"
+#include <cutils/log.h>
+
+#include "IdletimerController.h"
+#include "NetdConstants.h"
+
+extern "C" int system_nosh(const char *command);
+
+IdletimerController::IdletimerController() {
+}
+
+IdletimerController::~IdletimerController() {
+}
+/* return 0 or non-zero */
+int IdletimerController::runIpxtablesCmd(const char *cmd) {
+    char *buffer;
+    size_t len = strnlen(cmd, 255);
+    int res;
+
+    if (len == 255) {
+        ALOGE("command too long");
+        return -1;
+    }
+
+    asprintf(&buffer, "%s %s", IPTABLES_PATH, cmd);
+    res = system_nosh(buffer);
+    ALOGV("%s #%d", buffer, res);
+    free(buffer);
+
+    return res;
+}
+
+bool IdletimerController::setupIptablesHooks() {
+    runIpxtablesCmd("-t nat -D PREROUTING -j idletimer_nat_PREROUTING");
+    runIpxtablesCmd("-t nat -F idletimer_nat_PREROUTING");
+    runIpxtablesCmd("-t nat -N idletimer_nat_PREROUTING");
+
+    runIpxtablesCmd("-t nat -D POSTROUTING -j idletimer_nat_POSTROUTING");
+    runIpxtablesCmd("-t nat -F idletimer_nat_POSTROUTING");
+    runIpxtablesCmd("-t nat -N idletimer_nat_POSTROUTING");
+
+    if (runIpxtablesCmd("-t nat -I PREROUTING -j idletimer_nat_PREROUTING")
+        || runIpxtablesCmd("-t nat -I POSTROUTING -j idletimer_nat_POSTROUTING")) {
+        return false;
+    }
+    return true;
+}
+
+int IdletimerController::setDefaults() {
+  if (runIpxtablesCmd("-t nat -F idletimer_nat_PREROUTING")
+      || runIpxtablesCmd("-t nat -F idletimer_nat_POSTROUTING") )
+      return -1;
+  return 0;
+}
+
+int IdletimerController::enableIdletimerControl() {
+    int res = setDefaults();
+    return res;
+}
+
+int IdletimerController::disableIdletimerControl() {
+    int res = setDefaults();
+    return res;
+}
+
+int IdletimerController::modifyInterfaceIdletimer(IptOp op, const char *iface,
+                                                  uint32_t timeout) {
+  int res;
+  char *buffer;
+  asprintf(&buffer, "-t nat -%c idletimer_nat_PREROUTING -i %s -j IDLETIMER"
+           " --timeout %u --label %s --send_nl_msg 1",
+           (op == IptOpAdd) ? 'A' : 'D', iface, timeout, iface);
+  res = runIpxtablesCmd(buffer);
+  free(buffer);
+
+  asprintf(&buffer, "-t nat -%c idletimer_nat_POSTROUTING -o %s -j IDLETIMER"
+           " --timeout %u --label %s --send_nl_msg 1",
+           (op == IptOpAdd) ? 'A' : 'D', iface, timeout, iface);
+  res |= runIpxtablesCmd(buffer);
+  free(buffer);
+
+  return res;
+}
+
+int IdletimerController::addInterfaceIdletimer(const char *iface, uint32_t timeout) {
+  return modifyInterfaceIdletimer(IptOpAdd, iface, timeout);
+}
+
+int IdletimerController::removeInterfaceIdletimer(const char *iface, uint32_t timeout) {
+  return modifyInterfaceIdletimer(IptOpDelete, iface, timeout);
+}
diff --git a/IdletimerController.h b/IdletimerController.h
new file mode 100644
index 0000000..a55f7af
--- /dev/null
+++ b/IdletimerController.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 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 _IDLETIMER_CONTROLLER_H
+#define _IDLETIMER_CONTROLLER_H
+
+class IdletimerController {
+public:
+
+    IdletimerController();
+    virtual ~IdletimerController();
+
+    int enableIdletimerControl();
+    int disableIdletimerControl();
+    int addInterfaceIdletimer(const char *iface, uint32_t timeout);
+    int removeInterfaceIdletimer(const char *iface, uint32_t timeout);
+    bool setupIptablesHooks();
+
+ private:
+    enum IptOp { IptOpAdd, IptOpDelete };
+    int setDefaults();
+    int runIpxtablesCmd(const char *cmd);
+    int modifyInterfaceIdletimer(IptOp op, const char *iface, uint32_t timeout);
+};
+
+#endif
diff --git a/List.h b/List.h
new file mode 100644
index 0000000..856ce26
--- /dev/null
+++ b/List.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2005 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.
+ */
+
+//
+// Templated list class.  Normally we'd use STL, but we don't have that.
+// This class mimics STL's interfaces.
+//
+// Objects are copied into the list with the '=' operator or with copy-
+// construction, so if the compiler's auto-generated versions won't work for
+// you, define your own.
+//
+// The only class you want to use from here is "List".
+//
+#ifndef _NETD_LIST_H
+#define _NETD_LIST_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace android {
+namespace netd {
+
+/*
+ * Doubly-linked list.  Instantiate with "List<MyClass> myList".
+ *
+ * Objects added to the list are copied using the assignment operator,
+ * so this must be defined.
+ */
+template<typename T> 
+class List 
+{
+protected:
+    /*
+     * One element in the list.
+     */
+    class _Node {
+    public:
+        explicit _Node(const T& val) : mVal(val) {}
+        ~_Node() {}
+        inline T& getRef() { return mVal; }
+        inline const T& getRef() const { return mVal; }
+        inline _Node* getPrev() const { return mpPrev; }
+        inline _Node* getNext() const { return mpNext; }
+        inline void setVal(const T& val) { mVal = val; }
+        inline void setPrev(_Node* ptr) { mpPrev = ptr; }
+        inline void setNext(_Node* ptr) { mpNext = ptr; }
+    private:
+        friend class List;
+        friend class _ListIterator;
+        T           mVal;
+        _Node*      mpPrev;
+        _Node*      mpNext;
+    };
+
+    /*
+     * Iterator for walking through the list.
+     */
+    
+    template <typename TYPE>
+    struct CONST_ITERATOR {
+        typedef _Node const * NodePtr;
+        typedef const TYPE Type;
+    };
+    
+    template <typename TYPE>
+    struct NON_CONST_ITERATOR {
+        typedef _Node* NodePtr;
+        typedef TYPE Type;
+    };
+    
+    template<
+        typename U,
+        template <class> class Constness
+    > 
+    class _ListIterator {
+        typedef _ListIterator<U, Constness>     _Iter;
+        typedef typename Constness<U>::NodePtr  _NodePtr;
+        typedef typename Constness<U>::Type     _Type;
+
+        explicit _ListIterator(_NodePtr ptr) : mpNode(ptr) {}
+
+    public:
+        _ListIterator() {}
+        _ListIterator(const _Iter& rhs) : mpNode(rhs.mpNode) {}
+        ~_ListIterator() {}
+        
+        // this will handle conversions from iterator to const_iterator
+        // (and also all convertible iterators)
+        // Here, in this implementation, the iterators can be converted
+        // if the nodes can be converted
+        template<typename V> explicit 
+        _ListIterator(const V& rhs) : mpNode(rhs.mpNode) {}
+        
+
+        /*
+         * Dereference operator.  Used to get at the juicy insides.
+         */
+        _Type& operator*() const { return mpNode->getRef(); }
+        _Type* operator->() const { return &(mpNode->getRef()); }
+
+        /*
+         * Iterator comparison.
+         */
+        inline bool operator==(const _Iter& right) const { 
+            return mpNode == right.mpNode; }
+        
+        inline bool operator!=(const _Iter& right) const { 
+            return mpNode != right.mpNode; }
+
+        /*
+         * handle comparisons between iterator and const_iterator
+         */
+        template<typename OTHER>
+        inline bool operator==(const OTHER& right) const { 
+            return mpNode == right.mpNode; }
+        
+        template<typename OTHER>
+        inline bool operator!=(const OTHER& right) const { 
+            return mpNode != right.mpNode; }
+
+        /*
+         * Incr/decr, used to move through the list.
+         */
+        inline _Iter& operator++() {     // pre-increment
+            mpNode = mpNode->getNext();
+            return *this;
+        }
+        const _Iter operator++(int) {    // post-increment
+            _Iter tmp(*this);
+            mpNode = mpNode->getNext();
+            return tmp;
+        }
+        inline _Iter& operator--() {     // pre-increment
+            mpNode = mpNode->getPrev();
+            return *this;
+        }
+        const _Iter operator--(int) {   // post-increment
+            _Iter tmp(*this);
+            mpNode = mpNode->getPrev();
+            return tmp;
+        }
+
+        inline _NodePtr getNode() const { return mpNode; }
+
+        _NodePtr mpNode;    /* should be private, but older gcc fails */
+    private:
+        friend class List;
+    };
+
+public:
+    List() {
+        prep();
+    }
+    List(const List<T>& src) {      // copy-constructor
+        prep();
+        insert(begin(), src.begin(), src.end());
+    }
+    virtual ~List() {
+        clear();
+        delete[] (unsigned char*) mpMiddle;
+    }
+
+    typedef _ListIterator<T, NON_CONST_ITERATOR> iterator;
+    typedef _ListIterator<T, CONST_ITERATOR> const_iterator;
+
+    List<T>& operator=(const List<T>& right);
+
+    /* returns true if the list is empty */
+    inline bool empty() const { return mpMiddle->getNext() == mpMiddle; }
+
+    /* return #of elements in list */
+    size_t size() const {
+        return size_t(distance(begin(), end()));
+    }
+
+    /*
+     * Return the first element or one past the last element.  The
+     * _Node* we're returning is converted to an "iterator" by a
+     * constructor in _ListIterator.
+     */
+    inline iterator begin() { 
+        return iterator(mpMiddle->getNext()); 
+    }
+    inline const_iterator begin() const { 
+        return const_iterator(const_cast<_Node const*>(mpMiddle->getNext())); 
+    }
+    inline iterator end() { 
+        return iterator(mpMiddle); 
+    }
+    inline const_iterator end() const { 
+        return const_iterator(const_cast<_Node const*>(mpMiddle)); 
+    }
+
+    /* add the object to the head or tail of the list */
+    void push_front(const T& val) { insert(begin(), val); }
+    void push_back(const T& val) { insert(end(), val); }
+
+    /* insert before the current node; returns iterator at new node */
+    iterator insert(iterator posn, const T& val) 
+    {
+        _Node* newNode = new _Node(val);        // alloc & copy-construct
+        newNode->setNext(posn.getNode());
+        newNode->setPrev(posn.getNode()->getPrev());
+        posn.getNode()->getPrev()->setNext(newNode);
+        posn.getNode()->setPrev(newNode);
+        return iterator(newNode);
+    }
+
+    /* insert a range of elements before the current node */
+    void insert(iterator posn, const_iterator first, const_iterator last) {
+        for ( ; first != last; ++first)
+            insert(posn, *first);
+    }
+
+    /* remove one entry; returns iterator at next node */
+    iterator erase(iterator posn) {
+        _Node* pNext = posn.getNode()->getNext();
+        _Node* pPrev = posn.getNode()->getPrev();
+        pPrev->setNext(pNext);
+        pNext->setPrev(pPrev);
+        delete posn.getNode();
+        return iterator(pNext);
+    }
+
+    /* remove a range of elements */
+    iterator erase(iterator first, iterator last) {
+        while (first != last)
+            erase(first++);     // don't erase than incr later!
+        return iterator(last);
+    }
+
+    /* remove all contents of the list */
+    void clear() {
+        _Node* pCurrent = mpMiddle->getNext();
+        _Node* pNext;
+
+        while (pCurrent != mpMiddle) {
+            pNext = pCurrent->getNext();
+            delete pCurrent;
+            pCurrent = pNext;
+        }
+        mpMiddle->setPrev(mpMiddle);
+        mpMiddle->setNext(mpMiddle);
+    }
+
+    /*
+     * Measure the distance between two iterators.  On exist, "first"
+     * will be equal to "last".  The iterators must refer to the same
+     * list.
+     *
+     * FIXME: This is actually a generic iterator function. It should be a 
+     * template function at the top-level with specializations for things like
+     * vector<>, which can just do pointer math). Here we limit it to
+     * _ListIterator of the same type but different constness.
+     */
+    template<
+        typename U,
+        template <class> class CL,
+        template <class> class CR
+    > 
+    ptrdiff_t distance(
+            _ListIterator<U, CL> first, _ListIterator<U, CR> last) const 
+    {
+        ptrdiff_t count = 0;
+        while (first != last) {
+            ++first;
+            ++count;
+        }
+        return count;
+    }
+
+private:
+    /*
+     * I want a _Node but don't need it to hold valid data.  More
+     * to the point, I don't want T's constructor to fire, since it
+     * might have side-effects or require arguments.  So, we do this
+     * slightly uncouth storage alloc.
+     */
+    void prep() {
+        mpMiddle = (_Node*) new unsigned char[sizeof(_Node)];
+        mpMiddle->setPrev(mpMiddle);
+        mpMiddle->setNext(mpMiddle);
+    }
+
+    /*
+     * This node plays the role of "pointer to head" and "pointer to tail".
+     * It sits in the middle of a circular list of nodes.  The iterator
+     * runs around the circle until it encounters this one.
+     */
+    _Node*      mpMiddle;
+};
+
+/*
+ * Assignment operator.
+ *
+ * The simplest way to do this would be to clear out the target list and
+ * fill it with the source.  However, we can speed things along by
+ * re-using existing elements.
+ */
+template<class T>
+List<T>& List<T>::operator=(const List<T>& right)
+{
+    if (this == &right)
+        return *this;       // self-assignment
+    iterator firstDst = begin();
+    iterator lastDst = end();
+    const_iterator firstSrc = right.begin();
+    const_iterator lastSrc = right.end();
+    while (firstSrc != lastSrc && firstDst != lastDst)
+        *firstDst++ = *firstSrc++;
+    if (firstSrc == lastSrc)        // ran out of elements in source?
+        erase(firstDst, lastDst);   // yes, erase any extras
+    else
+        insert(lastDst, firstSrc, lastSrc);     // copy remaining over
+    return *this;
+}
+
+}; // namespace netd
+}; // namespace android
+
+#endif // _NETD_LIST_H
diff --git a/MDnsSdListener.cpp b/MDnsSdListener.cpp
new file mode 100644
index 0000000..52703df
--- /dev/null
+++ b/MDnsSdListener.cpp
@@ -0,0 +1,732 @@
+/*
+ * Copyright (C) 2010 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 <arpa/inet.h>
+#include <dirent.h>
+#include <errno.h>
+#include <linux/if.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <string.h>
+
+#define LOG_TAG "MDnsDS"
+#define DBG 1
+#define VDBG 1
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <sysutils/SocketClient.h>
+
+#include "MDnsSdListener.h"
+#include "ResponseCode.h"
+
+#define MDNS_SERVICE_NAME "mdnsd"
+#define MDNS_SERVICE_STATUS "init.svc.mdnsd"
+
+MDnsSdListener::MDnsSdListener() :
+                 FrameworkListener("mdns", true) {
+    Monitor *m = new Monitor();
+    registerCmd(new Handler(m, this));
+}
+
+MDnsSdListener::Handler::Handler(Monitor *m, MDnsSdListener *listener) :
+   NetdCommand("mdnssd") {
+   if (DBG) ALOGD("MDnsSdListener::Hander starting up");
+   mMonitor = m;
+   mListener = listener;
+}
+
+MDnsSdListener::Handler::~Handler() {}
+
+void MDnsSdListener::Handler::discover(SocketClient *cli,
+        const char *iface,
+        const char *regType,
+        const char *domain,
+        const int requestId,
+        const int requestFlags) {
+    if (VDBG) {
+        ALOGD("discover(%s, %s, %s, %d, %d)", iface, regType, domain, requestId,
+                requestFlags);
+    }
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    if (ref == NULL) {
+        ALOGE("requestId %d already in use during discover call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during discover call", false);
+        return;
+    }
+    if (VDBG) ALOGD("using ref %p", ref);
+    DNSServiceFlags nativeFlags = iToFlags(requestFlags);
+    int interfaceInt = ifaceNameToI(iface);
+
+    DNSServiceErrorType result = DNSServiceBrowse(ref, nativeFlags, interfaceInt, regType,
+            domain, &MDnsSdListenerDiscoverCallback, context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("Discover request %d got an error from DNSServiceBrowse %d", requestId, result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "Discover request got an error from DNSServiceBrowse", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("discover successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "Discover operation started", false);
+    return;
+}
+
+void MDnsSdListenerDiscoverCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *serviceName,
+        const char *regType, const char *replyDomain, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    char *msg;
+    int refNumber = context->mRefNumber;
+
+    if (errorCode != kDNSServiceErr_NoError) {
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceDiscoveryFailed, msg, false);
+        if (DBG) ALOGE("discover failure for %d, error= %d", refNumber, errorCode);
+    } else {
+        int respCode;
+        char *quotedServiceName = SocketClient::quoteArg(serviceName);
+        if (flags & kDNSServiceFlagsAdd) {
+            if (VDBG) {
+                ALOGD("Discover found new serviceName %s, regType %s and domain %s for %d",
+                        serviceName, regType, replyDomain, refNumber);
+            }
+            respCode = ResponseCode::ServiceDiscoveryServiceAdded;
+        } else {
+            if (VDBG) {
+                ALOGD("Discover lost serviceName %s, regType %s and domain %s for %d",
+                        serviceName, regType, replyDomain, refNumber);
+            }
+            respCode = ResponseCode::ServiceDiscoveryServiceRemoved;
+        }
+        asprintf(&msg, "%d %s %s %s", refNumber, quotedServiceName, regType, replyDomain);
+        free(quotedServiceName);
+        context->mListener->sendBroadcast(respCode, msg, false);
+    }
+    free(msg);
+}
+
+void MDnsSdListener::Handler::stop(SocketClient *cli, int argc, char **argv, const char *str) {
+    if (argc != 3) {
+        char *msg;
+        asprintf(&msg, "Invalid number of arguments to %s", str);
+        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+        free(msg);
+        return;
+    }
+    int requestId = atoi(argv[2]);
+    DNSServiceRef *ref = mMonitor->lookupServiceRef(requestId);
+    if (ref == NULL) {
+        if (DBG) ALOGE("%s stop used unknown requestId %d", str, requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError, "Unknown requestId", false);
+        return;
+    }
+    if (VDBG) ALOGD("Stopping %s with ref %p", str, ref);
+    DNSServiceRefDeallocate(*ref);
+    mMonitor->freeServiceRef(requestId);
+    char *msg;
+    asprintf(&msg, "%s stopped", str);
+    cli->sendMsg(ResponseCode::CommandOkay, msg, false);
+    free(msg);
+}
+
+void MDnsSdListener::Handler::serviceRegister(SocketClient *cli, int requestId,
+        const char *interfaceName, const char *serviceName, const char *serviceType,
+        const char *domain, const char *host, int port, int txtLen, void *txtRecord) {
+    if (VDBG) {
+        ALOGD("serviceRegister(%d, %s, %s, %s, %s, %s, %d, %d, <binary>)", requestId,
+                interfaceName, serviceName, serviceType, domain, host, port, txtLen);
+    }
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    port = htons(port);
+    if (ref == NULL) {
+        ALOGE("requestId %d already in use during register call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during register call", false);
+        return;
+    }
+    DNSServiceFlags nativeFlags = 0;
+    int interfaceInt = ifaceNameToI(interfaceName);
+    DNSServiceErrorType result = DNSServiceRegister(ref, interfaceInt, nativeFlags, serviceName,
+            serviceType, domain, host, port, txtLen, txtRecord, &MDnsSdListenerRegisterCallback,
+            context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("service register request %d got an error from DNSServiceRegister %d", requestId,
+                result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "serviceRegister request got an error from DNSServiceRegister", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("serviceRegister successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "serviceRegister started", false);
+    return;
+}
+
+void MDnsSdListenerRegisterCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        DNSServiceErrorType errorCode, const char *serviceName, const char *regType,
+        const char *domain, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    char *msg;
+    int refNumber = context->mRefNumber;
+    if (errorCode != kDNSServiceErr_NoError) {
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationFailed, msg, false);
+        if (DBG) ALOGE("register failure for %d, error= %d", refNumber, errorCode);
+    } else {
+        char *quotedServiceName = SocketClient::quoteArg(serviceName);
+        asprintf(&msg, "%d %s", refNumber, quotedServiceName);
+        free(quotedServiceName);
+        context->mListener->sendBroadcast(ResponseCode::ServiceRegistrationSucceeded, msg, false);
+        if (VDBG) ALOGD("register succeeded for %d as %s", refNumber, serviceName);
+    }
+    free(msg);
+}
+
+
+void MDnsSdListener::Handler::resolveService(SocketClient *cli, int requestId,
+        const char *interfaceName, const char *serviceName, const char *regType,
+        const char *domain) {
+    if (VDBG) {
+        ALOGD("resolveService(%d, %s, %s, %s, %s)", requestId, interfaceName,
+                serviceName, regType, domain);
+    }
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    if (ref == NULL) {
+        ALOGE("request Id %d already in use during resolve call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during resolve call", false);
+        return;
+    }
+    DNSServiceFlags nativeFlags = 0;
+    int interfaceInt = ifaceNameToI(interfaceName);
+    DNSServiceErrorType result = DNSServiceResolve(ref, nativeFlags, interfaceInt, serviceName,
+            regType, domain, &MDnsSdListenerResolveCallback, context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("service resolve request %d got an error from DNSServiceResolve %d", requestId,
+                result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "resolveService got an error from DNSServiceResolve", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("resolveService successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "resolveService started", false);
+    return;
+}
+
+void MDnsSdListenerResolveCallback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interface,
+        DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port,
+        uint16_t txtLen, const unsigned char *txtRecord, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    char *msg;
+    int refNumber = context->mRefNumber;
+    port = ntohs(port);
+    if (errorCode != kDNSServiceErr_NoError) {
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceResolveFailed, msg, false);
+        if (DBG) ALOGE("resolve failure for %d, error= %d", refNumber, errorCode);
+    } else {
+        char *quotedFullName = SocketClient::quoteArg(fullname);
+        char *quotedHostTarget = SocketClient::quoteArg(hosttarget);
+        asprintf(&msg, "%d %s %s %d %d", refNumber, quotedFullName, quotedHostTarget, port, txtLen);
+        free(quotedFullName);
+        free(quotedHostTarget);
+        context->mListener->sendBroadcast(ResponseCode::ServiceResolveSuccess, msg, false);
+        if (VDBG) {
+            ALOGD("resolve succeeded for %d finding %s at %s:%d with txtLen %d",
+                    refNumber, fullname, hosttarget, port, txtLen);
+        }
+    }
+    free(msg);
+}
+
+void MDnsSdListener::Handler::getAddrInfo(SocketClient *cli, int requestId,
+        const char *interfaceName, uint32_t protocol, const char *hostname) {
+    if (VDBG) ALOGD("getAddrInfo(%d, %s %d, %s)", requestId, interfaceName, protocol, hostname);
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    if (ref == NULL) {
+        ALOGE("request ID %d already in use during getAddrInfo call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during getAddrInfo call", false);
+        return;
+    }
+    DNSServiceFlags nativeFlags = 0;
+    int interfaceInt = ifaceNameToI(interfaceName);
+    DNSServiceErrorType result = DNSServiceGetAddrInfo(ref, nativeFlags, interfaceInt, protocol,
+            hostname, &MDnsSdListenerGetAddrInfoCallback, context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("getAddrInfo request %d got an error from DNSServiceGetAddrInfo %d", requestId,
+                result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "getAddrInfo request got an error from DNSServiceGetAddrInfo", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("getAddrInfo successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "getAddrInfo started", false);
+    return;
+}
+
+void MDnsSdListenerGetAddrInfoCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        uint32_t interface, DNSServiceErrorType errorCode, const char *hostname,
+        const struct sockaddr *const sa, uint32_t ttl, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    int refNumber = context->mRefNumber;
+
+    if (errorCode != kDNSServiceErr_NoError) {
+        char *msg;
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceGetAddrInfoFailed, msg, false);
+        if (DBG) ALOGE("getAddrInfo failure for %d, error= %d", refNumber, errorCode);
+        free(msg);
+    } else {
+        char addr[INET6_ADDRSTRLEN];
+        char *msg;
+        char *quotedHostname = SocketClient::quoteArg(hostname);
+        if (sa->sa_family == AF_INET) {
+            inet_ntop(sa->sa_family, &(((struct sockaddr_in *)sa)->sin_addr), addr, sizeof(addr));
+        } else {
+            inet_ntop(sa->sa_family, &(((struct sockaddr_in6 *)sa)->sin6_addr), addr, sizeof(addr));
+        }
+        asprintf(&msg, "%d %s %d %s", refNumber, quotedHostname, ttl, addr);
+        free(quotedHostname);
+        context->mListener->sendBroadcast(ResponseCode::ServiceGetAddrInfoSuccess, msg, false);
+        if (VDBG) {
+            ALOGD("getAddrInfo succeeded for %d: %s", refNumber, msg);
+        }
+        free(msg);
+    }
+}
+
+void MDnsSdListener::Handler::setHostname(SocketClient *cli, int requestId,
+        const char *hostname) {
+    if (VDBG) ALOGD("setHostname(%d, %s)", requestId, hostname);
+    Context *context = new Context(requestId, mListener);
+    DNSServiceRef *ref = mMonitor->allocateServiceRef(requestId, context);
+    if (ref == NULL) {
+        ALOGE("request Id %d already in use during setHostname call", requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "RequestId already in use during setHostname call", false);
+        return;
+    }
+    DNSServiceFlags nativeFlags = 0;
+    DNSServiceErrorType result = DNSSetHostname(ref, nativeFlags, hostname,
+            &MDnsSdListenerSetHostnameCallback, context);
+    if (result != kDNSServiceErr_NoError) {
+        ALOGE("setHostname request %d got an error from DNSSetHostname %d", requestId, result);
+        mMonitor->freeServiceRef(requestId);
+        cli->sendMsg(ResponseCode::CommandParameterError,
+                "setHostname got an error from DNSSetHostname", false);
+        return;
+    }
+    mMonitor->startMonitoring(requestId);
+    if (VDBG) ALOGD("setHostname successful");
+    cli->sendMsg(ResponseCode::CommandOkay, "setHostname started", false);
+    return;
+}
+
+void MDnsSdListenerSetHostnameCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        DNSServiceErrorType errorCode, const char *hostname, void *inContext) {
+    MDnsSdListener::Context *context = reinterpret_cast<MDnsSdListener::Context *>(inContext);
+    char *msg;
+    int refNumber = context->mRefNumber;
+    if (errorCode != kDNSServiceErr_NoError) {
+        asprintf(&msg, "%d %d", refNumber, errorCode);
+        context->mListener->sendBroadcast(ResponseCode::ServiceSetHostnameFailed, msg, false);
+        if (DBG) ALOGE("setHostname failure for %d, error= %d", refNumber, errorCode);
+    } else {
+        char *quotedHostname = SocketClient::quoteArg(hostname);
+        asprintf(&msg, "%d %s", refNumber, quotedHostname);
+        free(quotedHostname);
+        context->mListener->sendBroadcast(ResponseCode::ServiceSetHostnameSuccess, msg, false);
+        if (VDBG) ALOGD("setHostname succeeded for %d.  Set to %s", refNumber, hostname);
+    }
+    free(msg);
+}
+
+
+int MDnsSdListener::Handler::ifaceNameToI(const char *iface) {
+    return 0;
+}
+
+const char *MDnsSdListener::Handler::iToIfaceName(int i) {
+    return NULL;
+}
+
+DNSServiceFlags MDnsSdListener::Handler::iToFlags(int i) {
+    return 0;
+}
+
+int MDnsSdListener::Handler::flagsToI(DNSServiceFlags flags) {
+    return 0;
+}
+
+int MDnsSdListener::Handler::runCommand(SocketClient *cli,
+                                        int argc, char **argv) {
+    if (argc < 2) {
+        char* msg = NULL;
+        asprintf( &msg, "Invalid number of arguments to mdnssd: %i", argc);
+        ALOGW("%s", msg);
+        cli->sendMsg(ResponseCode::CommandParameterError, msg, false);
+        free(msg);
+        return -1;
+    }
+
+    char* cmd = argv[1];
+
+    if (strcmp(cmd, "discover") == 0) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd discover", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *serviceType = argv[3];
+
+        discover(cli, NULL, serviceType, NULL, requestId, 0);
+    } else if (strcmp(cmd, "stop-discover") == 0) {
+        stop(cli, argc, argv, "discover");
+    } else if (strcmp(cmd, "register") == 0) {
+        if (argc != 6) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd register", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *serviceName = argv[3];
+        char *serviceType = argv[4];
+        int port = atoi(argv[5]);
+        char *interfaceName = NULL; // will use all
+        char *domain = NULL;        // will use default
+        char *host = NULL;          // will use default hostname
+        int textLen = 0;
+        void *textRecord = NULL;
+
+        serviceRegister(cli, requestId, interfaceName, serviceName,
+                serviceType, domain, host, port, textLen, textRecord);
+    } else if (strcmp(cmd, "stop-register") == 0) {
+        stop(cli, argc, argv, "register");
+    } else if (strcmp(cmd, "resolve") == 0) {
+        if (argc != 6) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd resolve", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *interfaceName = NULL;  // will use all
+        char *serviceName = argv[3];
+        char *regType = argv[4];
+        char *domain = argv[5];
+        resolveService(cli, requestId, interfaceName, serviceName, regType, domain);
+    } else if (strcmp(cmd, "stop-resolve") == 0) {
+        stop(cli, argc, argv, "resolve");
+    } else if (strcmp(cmd, "start-service") == 0) {
+        if (mMonitor->startService()) {
+            cli->sendMsg(ResponseCode::CommandOkay, "Service Started", false);
+        } else {
+            cli->sendMsg(ResponseCode::ServiceStartFailed, "Service already running", false);
+        }
+    } else if (strcmp(cmd, "stop-service") == 0) {
+        if (mMonitor->stopService()) {
+            cli->sendMsg(ResponseCode::CommandOkay, "Service Stopped", false);
+        } else {
+            cli->sendMsg(ResponseCode::ServiceStopFailed, "Service still in use", false);
+        }
+    } else if (strcmp(cmd, "sethostname") == 0) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd sethostname", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *hostname = argv[3];
+        setHostname(cli, requestId, hostname);
+    } else if (strcmp(cmd, "stop-sethostname") == 0) {
+        stop(cli, argc, argv, "sethostname");
+    } else if (strcmp(cmd, "getaddrinfo") == 0) {
+        if (argc != 4) {
+            cli->sendMsg(ResponseCode::CommandParameterError,
+                    "Invalid number of arguments to mdnssd getaddrinfo", false);
+            return 0;
+        }
+        int requestId = atoi(argv[2]);
+        char *hostname = argv[3];
+        char *interfaceName = NULL;  // default
+        int protocol = 0;            // intelligient heuristic (both v4 + v6)
+        getAddrInfo(cli, requestId, interfaceName, protocol, hostname);
+    } else if (strcmp(cmd, "stop-getaddrinfo") == 0) {
+        stop(cli, argc, argv, "getaddrinfo");
+    } else {
+        if (VDBG) ALOGE("Unknown cmd %s", cmd);
+        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unknown mdnssd cmd", false);
+        return 0;
+    }
+    return 0;
+}
+
+MDnsSdListener::Monitor::Monitor() {
+    mHead = NULL;
+    pthread_mutex_init(&mHeadMutex, NULL);
+    socketpair(AF_LOCAL, SOCK_STREAM, 0, mCtrlSocketPair);
+    pthread_create(&mThread, NULL, MDnsSdListener::Monitor::threadStart, this);
+}
+
+void *MDnsSdListener::Monitor::threadStart(void *obj) {
+    Monitor *monitor = reinterpret_cast<Monitor *>(obj);
+
+    monitor->run();
+    delete monitor;
+    pthread_exit(NULL);
+    return NULL;
+}
+
+int MDnsSdListener::Monitor::startService() {
+    int result = 0;
+    char property_value[PROPERTY_VALUE_MAX];
+    pthread_mutex_lock(&mHeadMutex);
+    property_get(MDNS_SERVICE_STATUS, property_value, "");
+    if (strcmp("running", property_value) != 0) {
+        ALOGD("Starting MDNSD");
+        property_set("ctl.start", MDNS_SERVICE_NAME);
+        wait_for_property(MDNS_SERVICE_STATUS, "running", 5);
+        result = -1;
+    } else {
+        result = 0;
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+    return result;
+}
+
+int MDnsSdListener::Monitor::stopService() {
+    int result = 0;
+    pthread_mutex_lock(&mHeadMutex);
+    if (mHead == NULL) {
+        ALOGD("Stopping MDNSD");
+        property_set("ctl.stop", MDNS_SERVICE_NAME);
+        wait_for_property(MDNS_SERVICE_STATUS, "stopped", 5);
+        result = -1;
+    } else {
+        result = 0;
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+    return result;
+}
+
+void MDnsSdListener::Monitor::run() {
+    int pollCount = 1;
+    mPollSize = 10;
+
+    mPollFds = (struct pollfd *)calloc(sizeof(struct pollfd), mPollSize);
+    mPollRefs = (DNSServiceRef **)calloc(sizeof(DNSServiceRef *), mPollSize);
+
+    mPollFds[0].fd = mCtrlSocketPair[0];
+    mPollFds[0].events = POLLIN;
+
+    if (VDBG) ALOGD("MDnsSdListener starting to monitor");
+    while (1) {
+        if (VDBG) ALOGD("Going to poll with pollCount %d", pollCount);
+        int pollResults = poll(mPollFds, pollCount, 10000000);
+        if (pollResults < 0) {
+            ALOGE("Error in poll - got %d", errno);
+        } else if (pollResults > 0) {
+            if (VDBG) ALOGD("Monitor poll got data pollCount = %d, %d", pollCount, pollResults);
+            for(int i = 1; i < pollCount; i++) {
+                if (mPollFds[i].revents != 0) {
+                    if (VDBG) {
+                        ALOGD("Monitor found [%d].revents = %d - calling ProcessResults",
+                                i, mPollFds[i].revents);
+                    }
+                    DNSServiceProcessResult(*(mPollRefs[i]));
+                    mPollFds[i].revents = 0;
+                }
+            }
+            if (VDBG) ALOGD("controlSocket shows revent= %d", mPollFds[0].revents);
+            switch (mPollFds[0].revents) {
+                case POLLIN: {
+                    char readBuf[2];
+                    read(mCtrlSocketPair[0], &readBuf, 1);
+                    if (DBG) ALOGD("MDnsSdListener::Monitor got %c", readBuf[0]);
+                    if (memcmp(RESCAN, readBuf, 1) == 0) {
+                        pollCount = rescan();
+                    }
+                }
+            }
+            mPollFds[0].revents = 0;
+        } else {
+            if (VDBG) ALOGD("MDnsSdListener::Monitor poll timed out");
+        }
+    }
+    free(mPollFds);
+    free(mPollRefs);
+}
+
+#define DBG_RESCAN 0
+
+int MDnsSdListener::Monitor::rescan() {
+// rescan the list from mHead and make new pollfds and serviceRefs
+    if (VDBG) {
+        ALOGD("MDnsSdListener::Monitor poll rescanning - size=%d, live=%d", mPollSize, mLiveCount);
+    }
+    int count = 0;
+    pthread_mutex_lock(&mHeadMutex);
+    Element **prevPtr = &mHead;
+    int i = 1;
+    if (mPollSize <= mLiveCount) {
+        mPollSize = mLiveCount + 5;
+        free(mPollFds);
+        free(mPollRefs);
+        mPollFds = (struct pollfd *)calloc(sizeof(struct pollfd), mPollSize);
+        mPollRefs = (DNSServiceRef **)calloc(sizeof(DNSServiceRef *), mPollSize);
+    } else {
+        memset(mPollFds, sizeof(struct pollfd) * mPollSize, 0);
+        memset(mPollRefs, sizeof(DNSServiceRef *) * mPollSize, 0);
+    }
+    mPollFds[0].fd = mCtrlSocketPair[0];
+    mPollFds[0].events = POLLIN;
+    if (DBG_RESCAN) ALOGD("mHead = %p", mHead);
+    while (*prevPtr != NULL) {
+        if (DBG_RESCAN) ALOGD("checking %p, mReady = %d", *prevPtr, (*prevPtr)->mReady);
+        if ((*prevPtr)->mReady == 1) {
+            int fd = DNSServiceRefSockFD((*prevPtr)->mRef);
+            if (fd != -1) {
+                if (DBG_RESCAN) ALOGD("  adding FD %d", fd);
+                mPollFds[i].fd = fd;
+                mPollFds[i].events = POLLIN;
+                mPollRefs[i] = &((*prevPtr)->mRef);
+                i++;
+            } else {
+                ALOGE("Error retreving socket FD for live ServiceRef");
+            }
+            prevPtr = &((*prevPtr)->mNext); // advance to the next element
+        } else if ((*prevPtr)->mReady == -1) {
+            if (DBG_RESCAN) ALOGD("  removing %p from  play", *prevPtr);
+            Element *cur = *prevPtr;
+            *prevPtr = (cur)->mNext; // change our notion of this element and don't advance
+            delete cur;
+        }
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+    return i;
+}
+
+DNSServiceRef *MDnsSdListener::Monitor::allocateServiceRef(int id, Context *context) {
+    if (lookupServiceRef(id) != NULL) {
+        delete(context);
+        return NULL;
+    }
+    Element *e = new Element(id, context);
+    pthread_mutex_lock(&mHeadMutex);
+    e->mNext = mHead;
+    mHead = e;
+    pthread_mutex_unlock(&mHeadMutex);
+    return &(e->mRef);
+}
+
+DNSServiceRef *MDnsSdListener::Monitor::lookupServiceRef(int id) {
+    pthread_mutex_lock(&mHeadMutex);
+    Element *cur = mHead;
+    while (cur != NULL) {
+        if (cur->mId == id) {
+            DNSServiceRef *result = &(cur->mRef);
+            pthread_mutex_unlock(&mHeadMutex);
+            return result;
+        }
+        cur = cur->mNext;
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+    return NULL;
+}
+
+void MDnsSdListener::Monitor::startMonitoring(int id) {
+    if (VDBG) ALOGD("startMonitoring %d", id);
+    pthread_mutex_lock(&mHeadMutex);
+    Element *cur = mHead;
+    while (cur != NULL) {
+        if (cur->mId == id) {
+            if (DBG_RESCAN) ALOGD("marking %p as ready to be added", cur);
+            mLiveCount++;
+            cur->mReady = 1;
+            pthread_mutex_unlock(&mHeadMutex);
+            write(mCtrlSocketPair[1], RESCAN, 1);  // trigger a rescan for a fresh poll
+            if (VDBG) ALOGD("triggering rescan");
+            return;
+        }
+        cur = cur->mNext;
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+}
+
+#define NAP_TIME 200  // 200 ms between polls
+static int wait_for_property(const char *name, const char *desired_value, int maxwait)
+{
+    char value[PROPERTY_VALUE_MAX] = {'\0'};
+    int maxnaps = (maxwait * 1000) / NAP_TIME;
+
+    if (maxnaps < 1) {
+        maxnaps = 1;
+    }
+
+    while (maxnaps-- > 0) {
+        usleep(NAP_TIME * 1000);
+        if (property_get(name, value, NULL)) {
+            if (desired_value == NULL || strcmp(value, desired_value) == 0) {
+                return 0;
+            }
+        }
+    }
+    return -1; /* failure */
+}
+
+void MDnsSdListener::Monitor::freeServiceRef(int id) {
+    if (VDBG) ALOGD("freeServiceRef %d", id);
+    pthread_mutex_lock(&mHeadMutex);
+    Element **prevPtr = &mHead;
+    Element *cur;
+    while (*prevPtr != NULL) {
+        cur = *prevPtr;
+        if (cur->mId == id) {
+            if (DBG_RESCAN) ALOGD("marking %p as ready to be removed", cur);
+            mLiveCount--;
+            if (cur->mReady == 1) {
+                cur->mReady = -1; // tell poll thread to delete
+                write(mCtrlSocketPair[1], RESCAN, 1); // trigger a rescan for a fresh poll
+                if (VDBG) ALOGD("triggering rescan");
+            } else {
+                *prevPtr = cur->mNext;
+                delete cur;
+            }
+            pthread_mutex_unlock(&mHeadMutex);
+            return;
+        }
+        prevPtr = &(cur->mNext);
+    }
+    pthread_mutex_unlock(&mHeadMutex);
+}
diff --git a/MDnsSdListener.h b/MDnsSdListener.h
new file mode 100644
index 0000000..a3b14ad
--- /dev/null
+++ b/MDnsSdListener.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2012 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 _MDNSSDLISTENER_H__
+#define _MDNSSDLISTENER_H__
+
+#include <pthread.h>
+#include <sysutils/FrameworkListener.h>
+#include <dns_sd.h>
+
+#include "NetdCommand.h"
+
+// callbacks
+void MDnsSdListenerDiscoverCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        uint32_t interfaceIndex, DNSServiceErrorType errorCode,
+        const char *serviceName, const char *regType, const char *replyDomain,
+        void *inContext);
+
+void MDnsSdListenerRegisterCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        DNSServiceErrorType errorCode, const char *serviceName, const char *regType,
+        const char *domain, void *inContext);
+
+void MDnsSdListenerResolveCallback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interface,
+        DNSServiceErrorType errorCode, const char *fullname, const char *hosttarget, uint16_t port,
+        uint16_t txtLen, const unsigned char *txtRecord, void *inContext);
+
+void MDnsSdListenerSetHostnameCallback(DNSServiceRef, DNSServiceFlags flags,
+        DNSServiceErrorType errorCode, const char *hostname, void *inContext);
+
+void MDnsSdListenerGetAddrInfoCallback(DNSServiceRef sdRef, DNSServiceFlags flags,
+        uint32_t interface, DNSServiceErrorType errorCode, const char *hostname,
+        const struct sockaddr *const sa, uint32_t ttl, void *inContext);
+
+#define RESCAN "1"
+
+class MDnsSdListener : public FrameworkListener {
+public:
+    MDnsSdListener();
+    virtual ~MDnsSdListener() {}
+
+    class Context {
+    public:
+        MDnsSdListener *mListener;
+        int mRefNumber;
+
+        Context(int refNumber, MDnsSdListener *m) {
+            mRefNumber = refNumber;
+            mListener = m;
+        }
+
+        ~Context() {
+        }
+    };
+
+    class Monitor {
+    public:
+        Monitor();
+        virtual ~Monitor() {}
+        DNSServiceRef *allocateServiceRef(int id, Context *c);
+        void startMonitoring(int id);
+        DNSServiceRef *lookupServiceRef(int id);
+        void freeServiceRef(int id);
+        static void *threadStart(void *handler);
+        int startService();
+        int stopService();
+    private:
+        void run();
+        int rescan(); // returns the number of elements in the poll
+        class Element {
+        public:
+            int mId;
+            Element *mNext;
+            DNSServiceRef mRef;
+            Context *mContext;
+            int mReady;
+            Element(int id, Context *context)
+                    : mId(id), mNext(NULL), mContext(context), mReady(0) {}
+            virtual ~Element() { delete(mContext); }
+        };
+        Element *mHead;
+        int mLiveCount;
+        struct pollfd *mPollFds;
+        DNSServiceRef **mPollRefs;
+        int mPollSize;
+        pthread_t mThread;
+        int mCtrlSocketPair[2];
+        pthread_mutex_t mHeadMutex;
+    };
+
+    class Handler : public NetdCommand {
+    public:
+        Handler(Monitor *m, MDnsSdListener *listener);
+        virtual ~Handler();
+        int runCommand(SocketClient *c, int argc, char** argv);
+
+        MDnsSdListener *mListener; // needed for broadcast purposes
+    private:
+        void stop(SocketClient *cli, int argc, char **argv, const char *str);
+
+        void discover(SocketClient *cli, const char *iface, const char *regType,
+                const char *domain, const int requestNumber,
+                const int requestFlags);
+
+        void serviceRegister(SocketClient *cli, int requestId, const char *interfaceName,
+                const char *serviceName, const char *serviceType, const char *domain,
+                const char *host, int port, int textLen, void *txtRecord);
+
+        void resolveService(SocketClient *cli, int requestId,
+                const char *interfaceName, const char *serviceName, const char *regType,
+                const char *domain);
+
+        void setHostname(SocketClient *cli, int requestId, const char *hostname);
+
+        void getAddrInfo(SocketClient *cli, int requestId, const char *interfaceName,
+                uint32_t protocol, const char *hostname);
+
+        int ifaceNameToI(const char *iface);
+        const char *iToIfaceName(int i);
+        DNSServiceFlags iToFlags(int i);
+        int flagsToI(DNSServiceFlags flags);
+        Monitor *mMonitor;
+    };
+};
+
+static int wait_for_property(const char *name, const char *desired_value, int maxwait);
+
+#endif
diff --git a/NatController.cpp b/NatController.cpp
index 4ea5d24..77c4874 100644
--- a/NatController.cpp
+++ b/NatController.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+// #define LOG_NDEBUG 0
+
 #include <stdlib.h>
 #include <errno.h>
 #include <sys/socket.h>
@@ -29,16 +31,12 @@
 
 #include "NatController.h"
 #include "SecondaryTableController.h"
-#include "oem_iptables_hook.h"
+#include "NetdConstants.h"
 
 extern "C" int system_nosh(const char *command);
 
-static char IPTABLES_PATH[] = "/system/bin/iptables";
-static char IP_PATH[] = "/system/bin/ip";
-
 NatController::NatController(SecondaryTableController *ctrl) {
     secondaryTableCtrl = ctrl;
-    setDefaults();
 }
 
 NatController::~NatController() {
@@ -50,28 +48,51 @@
     int res;
 
     if (len == 255) {
-        LOGE("command too long");
+        ALOGE("command too long");
         errno = E2BIG;
         return -1;
     }
 
     asprintf(&buffer, "%s %s", path, cmd);
     res = system_nosh(buffer);
+    ALOGV("runCmd() buffer='%s' res=%d", buffer, res);
     free(buffer);
     return res;
 }
 
-int NatController::setDefaults() {
-
+int NatController::setupIptablesHooks() {
     if (runCmd(IPTABLES_PATH, "-P INPUT ACCEPT"))
         return -1;
     if (runCmd(IPTABLES_PATH, "-P OUTPUT ACCEPT"))
         return -1;
-    if (runCmd(IPTABLES_PATH, "-P FORWARD DROP"))
+    if (runCmd(IPTABLES_PATH, "-P FORWARD ACCEPT"))
         return -1;
-    if (runCmd(IPTABLES_PATH, "-F FORWARD"))
+
+    // Order is important!
+    // -D to delete any pre-existing jump rule, to prevent dupes (no-op if doesn't exist)
+    // -F to flush the chain (no-op if doesn't exist).
+    // -N to create the chain (no-op if already exist).
+
+    runCmd(IPTABLES_PATH, "-D FORWARD -j natctrl_FORWARD");
+    runCmd(IPTABLES_PATH, "-F natctrl_FORWARD");
+    runCmd(IPTABLES_PATH, "-N natctrl_FORWARD");
+    if (runCmd(IPTABLES_PATH, "-A FORWARD -j natctrl_FORWARD"))
         return -1;
-    if (runCmd(IPTABLES_PATH, "-t nat -F"))
+
+    runCmd(IPTABLES_PATH, "-t nat -D POSTROUTING -j natctrl_nat_POSTROUTING");
+    runCmd(IPTABLES_PATH, "-t nat -F natctrl_nat_POSTROUTING");
+    runCmd(IPTABLES_PATH, "-t nat -N natctrl_nat_POSTROUTING");
+    if (runCmd(IPTABLES_PATH, "-t nat -A POSTROUTING -j natctrl_nat_POSTROUTING"))
+        return -1;
+
+    setDefaults();
+    return 0;
+}
+
+int NatController::setDefaults() {
+    if (runCmd(IPTABLES_PATH, "-F natctrl_FORWARD"))
+        return -1;
+    if (runCmd(IPTABLES_PATH, "-t nat -F natctrl_nat_POSTROUTING"))
         return -1;
 
     runCmd(IP_PATH, "rule flush");
@@ -84,7 +105,6 @@
 
     natCount = 0;
 
-    setupOemIptablesHook();
     return 0;
 }
 
@@ -93,14 +113,6 @@
     return true;
 }
 
-const char *NatController::getVersion(const char *addr) {
-    if (strchr(addr, ':') != NULL) {
-        return "-6";
-    } else {
-        return "-4";
-    }
-}
-
 //  0    1       2       3       4            5
 // nat enable intface extface addrcnt nated-ipaddr/prelength
 int NatController::enableNat(const int argc, char **argv) {
@@ -113,29 +125,23 @@
     int tableNumber;
 
     if (!checkInterface(intIface) || !checkInterface(extIface)) {
-        LOGE("Invalid interface specified");
+        ALOGE("Invalid interface specified");
         errno = ENODEV;
         return -1;
     }
 
     if (argc < 5 + addrCount) {
-        LOGE("Missing Argument");
+        ALOGE("Missing Argument");
         errno = EINVAL;
         return -1;
     }
 
     tableNumber = secondaryTableCtrl->findTableNumber(extIface);
     if (tableNumber != -1) {
-        for(i = 0; i < addrCount && ret == 0; i++) {
-            snprintf(cmd, sizeof(cmd), "%s rule add from %s table %d", getVersion(argv[5+i]),
-                    argv[5+i], tableNumber + BASE_TABLE_NUMBER);
-            ret |= runCmd(IP_PATH, cmd);
-            if (ret) LOGE("IP rule %s got %d", cmd, ret);
+        for(i = 0; i < addrCount; i++) {
+            ret |= secondaryTableCtrl->modifyFromRule(tableNumber, ADD, argv[5+i]);
 
-            snprintf(cmd, sizeof(cmd), "route add %s dev %s table %d", argv[5+i], intIface,
-                    tableNumber + BASE_TABLE_NUMBER);
-            ret |= runCmd(IP_PATH, cmd);
-            if (ret) LOGE("IP route %s got %d", cmd, ret);
+            ret |= secondaryTableCtrl->modifyLocalRoute(tableNumber, ADD, intIface, argv[5+i]);
         }
         runCmd(IP_PATH, "route flush cache");
     }
@@ -143,32 +149,35 @@
     if (ret != 0 || setForwardRules(true, intIface, extIface) != 0) {
         if (tableNumber != -1) {
             for (i = 0; i < addrCount; i++) {
-                snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
-                        tableNumber + BASE_TABLE_NUMBER);
-                runCmd(IP_PATH, cmd);
+                secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
 
-                snprintf(cmd, sizeof(cmd), "%s rule del from %s table %d", getVersion(argv[5+i]),
-                        argv[5+i], tableNumber + BASE_TABLE_NUMBER);
-                runCmd(IP_PATH, cmd);
+                secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
             }
             runCmd(IP_PATH, "route flush cache");
         }
-        LOGE("Error setting forward rules");
+        ALOGE("Error setting forward rules");
         errno = ENODEV;
         return -1;
     }
 
+    /* Always make sure the drop rule is at the end */
+    snprintf(cmd, sizeof(cmd), "-D natctrl_FORWARD -j DROP");
+    runCmd(IPTABLES_PATH, cmd);
+    snprintf(cmd, sizeof(cmd), "-A natctrl_FORWARD -j DROP");
+    runCmd(IPTABLES_PATH, cmd);
+
+
     natCount++;
     // add this if we are the first added nat
     if (natCount == 1) {
-        snprintf(cmd, sizeof(cmd), "-t nat -A POSTROUTING -o %s -j MASQUERADE", extIface);
+        snprintf(cmd, sizeof(cmd), "-t nat -A natctrl_nat_POSTROUTING -o %s -j MASQUERADE", extIface);
         if (runCmd(IPTABLES_PATH, cmd)) {
-            LOGE("Error seting postroute rule: %s", cmd);
+            ALOGE("Error seting postroute rule: %s", cmd);
             // unwind what's been done, but don't care about success - what more could we do?
             for (i = 0; i < addrCount; i++) {
-                snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
-                        tableNumber + BASE_TABLE_NUMBER);
-                runCmd(IP_PATH, cmd);
+                secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
+
+                secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
             }
             setDefaults();
             return -1;
@@ -182,7 +191,7 @@
     char cmd[255];
 
     snprintf(cmd, sizeof(cmd),
-             "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
+             "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
              (add ? "A" : "D"),
              extIface, intIface);
     if (runCmd(IPTABLES_PATH, cmd) && add) {
@@ -190,36 +199,37 @@
     }
 
     snprintf(cmd, sizeof(cmd),
-            "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
+            "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
             (add ? "A" : "D"),
             intIface, extIface);
     if (runCmd(IPTABLES_PATH, cmd) && add) {
         // bail on error, but only if adding
         snprintf(cmd, sizeof(cmd),
-                "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
+                "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
                 (!add ? "A" : "D"),
                 extIface, intIface);
         runCmd(IPTABLES_PATH, cmd);
         return -1;
     }
 
-    snprintf(cmd, sizeof(cmd), "-%s FORWARD -i %s -o %s -j ACCEPT", (add ? "A" : "D"),
+    snprintf(cmd, sizeof(cmd), "-%s natctrl_FORWARD -i %s -o %s -j RETURN", (add ? "A" : "D"),
             intIface, extIface);
     if (runCmd(IPTABLES_PATH, cmd) && add) {
         // unwind what's been done, but don't care about success - what more could we do?
         snprintf(cmd, sizeof(cmd),
-                "-%s FORWARD -i %s -o %s -m state --state INVALID -j DROP",
+                "-%s natctrl_FORWARD -i %s -o %s -m state --state INVALID -j DROP",
                 (!add ? "A" : "D"),
                 intIface, extIface);
         runCmd(IPTABLES_PATH, cmd);
 
         snprintf(cmd, sizeof(cmd),
-                 "-%s FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j ACCEPT",
+                 "-%s natctrl_FORWARD -i %s -o %s -m state --state ESTABLISHED,RELATED -j RETURN",
                  (!add ? "A" : "D"),
                  extIface, intIface);
         runCmd(IPTABLES_PATH, cmd);
         return -1;
     }
+
     return 0;
 }
 
@@ -235,13 +245,13 @@
     int tableNumber;
 
     if (!checkInterface(intIface) || !checkInterface(extIface)) {
-        LOGE("Invalid interface specified");
+        ALOGE("Invalid interface specified");
         errno = ENODEV;
         return -1;
     }
 
     if (argc < 5 + addrCount) {
-        LOGE("Missing Argument");
+        ALOGE("Missing Argument");
         errno = EINVAL;
         return -1;
     }
@@ -251,15 +261,9 @@
     tableNumber = secondaryTableCtrl->findTableNumber(extIface);
     if (tableNumber != -1) {
         for (i = 0; i < addrCount; i++) {
-            snprintf(cmd, sizeof(cmd), "route del %s dev %s table %d", argv[5+i], intIface,
-                    tableNumber + BASE_TABLE_NUMBER);
-            // if the interface has gone down these will be gone already and give errors
-            // ignore them.
-            runCmd(IP_PATH, cmd);
+            secondaryTableCtrl->modifyLocalRoute(tableNumber, DEL, intIface, argv[5+i]);
 
-            snprintf(cmd, sizeof(cmd), "%s rule del from %s table %d", getVersion(argv[5+i]),
-                    argv[5+i], tableNumber + BASE_TABLE_NUMBER);
-            runCmd(IP_PATH, cmd);
+            secondaryTableCtrl->modifyFromRule(tableNumber, DEL, argv[5+i]);
         }
 
         runCmd(IP_PATH, "route flush cache");
diff --git a/NatController.h b/NatController.h
index eae32b4..1d328e5 100644
--- a/NatController.h
+++ b/NatController.h
@@ -19,8 +19,6 @@
 
 #include <linux/in.h>
 
-#include <utils/List.h>
-
 #include "SecondaryTableController.h"
 
 class NatController {
@@ -31,6 +29,7 @@
 
     int enableNat(const int argc, char **argv);
     int disableNat(const int argc, char **argv);
+    int setupIptablesHooks();
 
 private:
     int natCount;
@@ -40,7 +39,6 @@
     int runCmd(const char *path, const char *cmd);
     bool checkInterface(const char *iface);
     int setForwardRules(bool set, const char *intIface, const char *extIface);
-    const char *getVersion(const char *addr);
 };
 
 #endif
diff --git a/NetdConstants.cpp b/NetdConstants.cpp
new file mode 100644
index 0000000..e57b483
--- /dev/null
+++ b/NetdConstants.cpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 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 "NetdConstants.h"
+
+const char * const OEM_SCRIPT_PATH = "/system/bin/oem-iptables-init.sh";
+const char * const IPTABLES_PATH = "/system/bin/iptables";
+const char * const IP6TABLES_PATH = "/system/bin/ip6tables";
+const char * const TC_PATH = "/system/bin/tc";
+const char * const IP_PATH = "/system/bin/ip";
+const char * const ADD = "add";
+const char * const DEL = "del";
diff --git a/NetdConstants.h b/NetdConstants.h
new file mode 100644
index 0000000..9943a05
--- /dev/null
+++ b/NetdConstants.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 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 _NETD_CONSTANTS_H
+#define _NETD_CONSTANTS_H
+
+
+extern const char * const IPTABLES_PATH;
+extern const char * const IP6TABLES_PATH;
+extern const char * const IP_PATH;
+extern const char * const TC_PATH;
+extern const char * const OEM_SCRIPT_PATH;
+extern const char * const ADD;
+extern const char * const DEL;
+
+#endif
diff --git a/NetlinkHandler.cpp b/NetlinkHandler.cpp
index 8331ae0..94e9240 100644
--- a/NetlinkHandler.cpp
+++ b/NetlinkHandler.cpp
@@ -48,7 +48,7 @@
 void NetlinkHandler::onEvent(NetlinkEvent *evt) {
     const char *subsys = evt->getSubsystem();
     if (!subsys) {
-        LOGW("No subsystem found in netlink event");
+        ALOGW("No subsystem found in netlink event");
         return;
     }
 
@@ -68,12 +68,25 @@
         } else if (action == evt->NlActionLinkDown) {
             notifyInterfaceLinkChanged(iface, false);
         }
+
     } else if (!strcmp(subsys, "qlog")) {
         const char *alertName = evt->findParam("ALERT_NAME");
         const char *iface = evt->findParam("INTERFACE");
         notifyQuotaLimitReached(alertName, iface);
-    }
 
+    } else if (!strcmp(subsys, "xt_idletimer")) {
+        int action = evt->getAction();
+        const char *iface = evt->findParam("INTERFACE");
+        const char *state = evt->findParam("STATE");
+        if (state)
+            notifyInterfaceActivity(iface, !strcmp("active", state));
+
+#if !LOG_NDEBUG
+    } else if (strcmp(subsys, "platform") && strcmp(subsys, "backlight")) {
+        /* It is not a VSYNC or a backlight event */
+        ALOGV("unexpected event from subsystem %s", subsys);
+#endif
+    }
 }
 
 void NetlinkHandler::notifyInterfaceAdded(const char *name) {
@@ -117,3 +130,13 @@
     mNm->getBroadcaster()->sendBroadcast(ResponseCode::BandwidthControl,
             msg, false);
 }
+
+void NetlinkHandler::notifyInterfaceActivity(const char *name, bool isActive) {
+    char msg[255];
+
+    snprintf(msg, sizeof(msg), "Iface %s %s", name, isActive ? "active" : "idle");
+    ALOGV("Broadcasting interface activity msg: %s", msg);
+    mNm->getBroadcaster()->sendBroadcast(isActive ? ResponseCode::InterfaceActive
+            : ResponseCode::InterfaceIdle,
+            msg, false);
+}
diff --git a/NetlinkHandler.h b/NetlinkHandler.h
index 9466ca6..fe82934 100644
--- a/NetlinkHandler.h
+++ b/NetlinkHandler.h
@@ -38,5 +38,6 @@
     void notifyInterfaceChanged(const char *name, bool isUp);
     void notifyInterfaceLinkChanged(const char *name, bool isUp);
     void notifyQuotaLimitReached(const char *name, const char *iface);
+    void notifyInterfaceActivity(const char *name, bool isActive);
 };
 #endif
diff --git a/NetlinkManager.cpp b/NetlinkManager.cpp
index 196945b..8e2bc69 100644
--- a/NetlinkManager.cpp
+++ b/NetlinkManager.cpp
@@ -63,12 +63,12 @@
     nladdr.nl_groups = groups;
 
     if ((*sock = socket(PF_NETLINK, SOCK_DGRAM, netlinkFamily)) < 0) {
-        LOGE("Unable to create netlink socket: %s", strerror(errno));
+        ALOGE("Unable to create netlink socket: %s", strerror(errno));
         return NULL;
     }
 
     if (setsockopt(*sock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
-        LOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
+        ALOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
         close(*sock);
         return NULL;
     }
@@ -80,14 +80,14 @@
     }
 
     if (bind(*sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
-        LOGE("Unable to bind netlink socket: %s", strerror(errno));
+        ALOGE("Unable to bind netlink socket: %s", strerror(errno));
         close(*sock);
         return NULL;
     }
 
     NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);
     if (handler->start()) {
-        LOGE("Unable to start NetlinkHandler: %s", strerror(errno));
+        ALOGE("Unable to start NetlinkHandler: %s", strerror(errno));
         close(*sock);
         return NULL;
     }
@@ -108,9 +108,10 @@
 
     if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
         NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
-        LOGE("Unable to open quota2 logging socket");
+        ALOGE("Unable to open quota2 logging socket");
         // TODO: return -1 once the emulator gets a new kernel.
     }
+
     return 0;
 }
 
@@ -118,7 +119,7 @@
     int status = 0;
 
     if (mUeventHandler->stop()) {
-        LOGE("Unable to stop uevent NetlinkHandler: %s", strerror(errno));
+        ALOGE("Unable to stop uevent NetlinkHandler: %s", strerror(errno));
         status = -1;
     }
 
@@ -129,7 +130,7 @@
     mUeventSock = -1;
 
     if (mRouteHandler->stop()) {
-        LOGE("Unable to stop route NetlinkHandler: %s", strerror(errno));
+        ALOGE("Unable to stop route NetlinkHandler: %s", strerror(errno));
         status = -1;
     }
 
@@ -141,7 +142,7 @@
 
     if (mQuotaHandler) {
         if (mQuotaHandler->stop()) {
-            LOGE("Unable to stop quota NetlinkHandler: %s", strerror(errno));
+            ALOGE("Unable to stop quota NetlinkHandler: %s", strerror(errno));
             status = -1;
         }
 
@@ -151,5 +152,6 @@
         close(mQuotaSock);
         mQuotaSock = -1;
     }
+
     return status;
 }
diff --git a/NetlinkManager.h b/NetlinkManager.h
index c8f5507..6515ea4 100644
--- a/NetlinkManager.h
+++ b/NetlinkManager.h
@@ -32,9 +32,11 @@
     NetlinkHandler       *mUeventHandler;
     NetlinkHandler       *mRouteHandler;
     NetlinkHandler       *mQuotaHandler;
+    NetlinkHandler       *mIfaceIdleTimerHandler;
     int                  mUeventSock;
     int                  mRouteSock;
     int                  mQuotaSock;
+    int                  mIfaceIdleTimerSock;
 
 public:
     virtual ~NetlinkManager();
@@ -50,6 +52,9 @@
     /* This is the nflog group arg that the xt_quota2 neftiler will use. */
     static const int NFLOG_QUOTA_GROUP;
 
+    /* This is the group that the xt_IDLETIMER netfilter will use. */
+    static const int IDLETIMER_GROUP;
+
 private:
     NetlinkManager();
     NetlinkHandler* setupSocket(int *sock, int netlinkFamily, int groups,
diff --git a/PanController.cpp b/PanController.cpp
index 253c943..164b01a 100644
--- a/PanController.cpp
+++ b/PanController.cpp
@@ -52,33 +52,33 @@
 
 #ifdef HAVE_BLUETOOTH
     if (!bt_is_enabled()) {
-        LOGE("Cannot start PAN services - Bluetooth not running");
+        ALOGE("Cannot start PAN services - Bluetooth not running");
         errno = ENODEV;
         return -1;
     }
 #else
-    LOGE("Cannot start PAN services - No Bluetooth support");
+    ALOGE("Cannot start PAN services - No Bluetooth support");
     errno = ENODEV;
     return -1;
 #endif
 
     if (mPid) {
-        LOGE("PAN already started");
+        ALOGE("PAN already started");
         errno = EBUSY;
         return -1;
     }
 
    if ((pid = fork()) < 0) {
-        LOGE("fork failed (%s)", strerror(errno));
+        ALOGE("fork failed (%s)", strerror(errno));
         return -1;
     }
 
     if (!pid) {
         if (execl("/system/bin/pand", "/system/bin/pand", "--nodetach", "--listen",
                   "--role", "NAP", (char *) NULL)) {
-            LOGE("execl failed (%s)", strerror(errno));
+            ALOGE("execl failed (%s)", strerror(errno));
         }
-        LOGE("Should never get here!");
+        ALOGE("Should never get here!");
         return 0;
     } else {
         mPid = pid;
@@ -89,15 +89,15 @@
 
 int PanController::stopPan() {
     if (mPid == 0) {
-        LOGE("PAN already stopped");
+        ALOGE("PAN already stopped");
         return 0;
     }
 
-    LOGD("Stopping PAN services");
+    ALOGD("Stopping PAN services");
     kill(mPid, SIGTERM);
     waitpid(mPid, NULL, 0);
     mPid = 0;
-    LOGD("PAN services stopped");
+    ALOGD("PAN services stopped");
     return 0;
 }
 
diff --git a/PanController.h b/PanController.h
index 4f52fee..a97e039 100644
--- a/PanController.h
+++ b/PanController.h
@@ -19,8 +19,6 @@
 
 #include <linux/in.h>
 
-#include <utils/List.h>
-
 class PanController {
     pid_t mPid;
 
diff --git a/PppController.cpp b/PppController.cpp
index 4caf382..6b54c31 100644
--- a/PppController.cpp
+++ b/PppController.cpp
@@ -34,8 +34,6 @@
 
 #include "PppController.h"
 
-extern "C" int logwrap(int argc, const char **argv, int background);
-
 PppController::PppController() {
     mTtys = new TtyCollection();
     mPid = 0;
@@ -56,7 +54,7 @@
     pid_t pid;
 
     if (mPid) {
-        LOGE("Multiple PPPD instances not currently supported");
+        ALOGE("Multiple PPPD instances not currently supported");
         errno = EBUSY;
         return -1;
     }
@@ -68,13 +66,13 @@
         }
     }
     if (it == mTtys->end()) {
-        LOGE("Invalid tty '%s' specified", tty);
+        ALOGE("Invalid tty '%s' specified", tty);
         errno = -EINVAL;
         return -1;
     }
 
     if ((pid = fork()) < 0) {
-        LOGE("fork failed (%s)", strerror(errno));
+        ALOGE("fork failed (%s)", strerror(errno));
         return -1;
     }
 
@@ -94,9 +92,9 @@
         // but not getting a connection
         if (execl("/system/bin/pppd", "/system/bin/pppd", "-detach", dev, "115200",
                   lr, "ms-dns", d1, "ms-dns", d2, "lcp-max-configure", "99999", (char *) NULL)) {
-            LOGE("execl failed (%s)", strerror(errno));
+            ALOGE("execl failed (%s)", strerror(errno));
         }
-        LOGE("Should never get here!");
+        ALOGE("Should never get here!");
         return 0;
     } else {
         mPid = pid;
@@ -107,15 +105,15 @@
 int PppController::detachPppd(const char *tty) {
 
     if (mPid == 0) {
-        LOGE("PPPD already stopped");
+        ALOGE("PPPD already stopped");
         return 0;
     }
 
-    LOGD("Stopping PPPD services on port %s", tty);
+    ALOGD("Stopping PPPD services on port %s", tty);
     kill(mPid, SIGTERM);
     waitpid(mPid, NULL, 0);
     mPid = 0;
-    LOGD("PPPD services on port %s stopped", tty);
+    ALOGD("PPPD services on port %s stopped", tty);
     return 0;
 }
 
@@ -134,7 +132,7 @@
 
     DIR *d = opendir("/sys/class/tty");
     if (!d) {
-        LOGE("Error opening /sys/class/tty (%s)", strerror(errno));
+        ALOGE("Error opening /sys/class/tty (%s)", strerror(errno));
         return -1;
     }
 
diff --git a/PppController.h b/PppController.h
index bb75435..cc74c8c 100644
--- a/PppController.h
+++ b/PppController.h
@@ -19,9 +19,9 @@
 
 #include <linux/in.h>
 
-#include <utils/List.h>
+#include "List.h"
 
-typedef android::List<char *> TtyCollection;
+typedef android::netd::List<char *> TtyCollection;
 
 class PppController {
     TtyCollection *mTtys;
diff --git a/ResolverController.cpp b/ResolverController.cpp
index b079f81..680be20 100644
--- a/ResolverController.cpp
+++ b/ResolverController.cpp
@@ -20,13 +20,16 @@
 #include <cutils/log.h>
 
 #include <linux/if.h>
-#include <resolv.h>
+
+// NOTE: <resolv_iface.h> is a private C library header that provides
+//       declarations for _resolv_set_default_iface() and others.
+#include <resolv_iface.h>
 
 #include "ResolverController.h"
 
 int ResolverController::setDefaultInterface(const char* iface) {
     if (DBG) {
-        LOGD("setDefaultInterface iface = %s\n", iface);
+        ALOGD("setDefaultInterface iface = %s\n", iface);
     }
 
     _resolv_set_default_iface(iface);
@@ -36,7 +39,7 @@
 
 int ResolverController::setInterfaceDnsServers(const char* iface, char** servers, int numservers) {
     if (DBG) {
-        LOGD("setInterfaceDnsServers iface = %s\n", iface);
+        ALOGD("setInterfaceDnsServers iface = %s\n", iface);
     }
 
     _resolv_set_nameservers_for_iface(iface, servers, numservers);
@@ -46,7 +49,7 @@
 
 int ResolverController::setInterfaceAddress(const char* iface, struct in_addr* addr) {
     if (DBG) {
-        LOGD("setInterfaceAddress iface = %s\n", iface);
+        ALOGD("setInterfaceAddress iface = %s\n", iface);
     }
 
     _resolv_set_addr_of_iface(iface, addr);
@@ -56,7 +59,7 @@
 
 int ResolverController::flushDefaultDnsCache() {
     if (DBG) {
-        LOGD("flushDefaultDnsCache\n");
+        ALOGD("flushDefaultDnsCache\n");
     }
 
     _resolv_flush_cache_for_default_iface();
@@ -66,7 +69,7 @@
 
 int ResolverController::flushInterfaceDnsCache(const char* iface) {
     if (DBG) {
-        LOGD("flushInterfaceDnsCache iface = %s\n", iface);
+        ALOGD("flushInterfaceDnsCache iface = %s\n", iface);
     }
 
     _resolv_flush_cache_for_iface(iface);
diff --git a/ResponseCode.h b/ResponseCode.h
index b01c141..5bebb0f 100644
--- a/ResponseCode.h
+++ b/ResponseCode.h
@@ -44,10 +44,14 @@
     static const int InterfaceTxThrottleResult = 219;
     static const int QuotaCounterResult        = 220;
     static const int TetheringStatsResult      = 221;
+    static const int DnsProxyQueryResult       = 222;
 
     // 400 series - The command was accepted but the requested action
     // did not take place.
-    static const int OperationFailed = 400;
+    static const int OperationFailed           = 400;
+    static const int DnsProxyOperationFailed   = 401;
+    static const int ServiceStartFailed        = 402;
+    static const int ServiceStopFailed         = 403;
 
     // 500 series - The command was not accepted and the requested
     // action did not take place.
@@ -55,7 +59,20 @@
     static const int CommandParameterError = 501;
 
     // 600 series - Unsolicited broadcasts
-    static const int InterfaceChange        = 600;
-    static const int BandwidthControl       = 601;
+    static const int InterfaceChange                = 600;
+    static const int BandwidthControl               = 601;
+    static const int ServiceDiscoveryFailed         = 602;
+    static const int ServiceDiscoveryServiceAdded   = 603;
+    static const int ServiceDiscoveryServiceRemoved = 604;
+    static const int ServiceRegistrationFailed      = 605;
+    static const int ServiceRegistrationSucceeded   = 606;
+    static const int ServiceResolveFailed           = 607;
+    static const int ServiceResolveSuccess          = 608;
+    static const int ServiceSetHostnameFailed       = 609;
+    static const int ServiceSetHostnameSuccess      = 610;
+    static const int ServiceGetAddrInfoFailed       = 611;
+    static const int ServiceGetAddrInfoSuccess      = 612;
+    static const int InterfaceActive                = 613;
+    static const int InterfaceIdle                  = 614;
 };
 #endif
diff --git a/SecondaryTableController.cpp b/SecondaryTableController.cpp
index 2289259..7d3de38 100644
--- a/SecondaryTableController.cpp
+++ b/SecondaryTableController.cpp
@@ -34,12 +34,9 @@
 extern "C" int system_nosh(const char *command);
 
 #include "ResponseCode.h"
+#include "NetdConstants.h"
 #include "SecondaryTableController.h"
 
-static char IP_PATH[] = "/system/bin/ip";
-static char ADD[] = "add";
-static char DEL[] = "del";
-
 SecondaryTableController::SecondaryTableController() {
     int i;
     for (i=0; i < INTERFACES_TRACKED; i++) {
@@ -69,7 +66,7 @@
     if (tableIndex == -1) {
         tableIndex = findTableNumber(""); // look for an empty slot
         if (tableIndex == -1) {
-            LOGE("Max number of NATed interfaces reached");
+            ALOGE("Max number of NATed interfaces reached");
             errno = ENODEV;
             cli->sendMsg(ResponseCode::OperationFailed, "Max number NATed", true);
             return -1;
@@ -82,8 +79,8 @@
     return modifyRoute(cli, ADD, iface, dest, prefix, gateway, tableIndex);
 }
 
-int SecondaryTableController::modifyRoute(SocketClient *cli, char *action, char *iface, char *dest,
-        int prefix, char *gateway, int tableIndex) {
+int SecondaryTableController::modifyRoute(SocketClient *cli, const char *action, char *iface,
+        char *dest, int prefix, char *gateway, int tableIndex) {
     char *cmd;
 
     if (strcmp("::", gateway) == 0) {
@@ -96,7 +93,7 @@
     }
 
     if (runAndFree(cli, cmd)) {
-        LOGE("ip route %s failed: %s route %s %s/%d via %s dev %s table %d", action,
+        ALOGE("ip route %s failed: %s route %s %s/%d via %s dev %s table %d", action,
                 IP_PATH, action, dest, prefix, gateway, iface, tableIndex+BASE_TABLE_NUMBER);
         errno = ENODEV;
         cli->sendMsg(ResponseCode::OperationFailed, "ip route modification failed", true);
@@ -111,15 +108,45 @@
             mInterfaceTable[tableIndex][0] = 0;
         }
     }
+    modifyRuleCount(tableIndex, action);
     cli->sendMsg(ResponseCode::CommandOkay, "Route modified", false);
     return 0;
 }
 
+void SecondaryTableController::modifyRuleCount(int tableIndex, const char *action) {
+    if (strcmp(action, ADD) == 0) {
+        mInterfaceRuleCount[tableIndex]++;
+    } else {
+        if (--mInterfaceRuleCount[tableIndex] < 1) {
+            mInterfaceRuleCount[tableIndex] = 0;
+            mInterfaceTable[tableIndex][0] = 0;
+        }
+    }
+}
+
+int SecondaryTableController::verifyTableIndex(int tableIndex) {
+    if ((tableIndex < 0) ||
+            (tableIndex >= INTERFACES_TRACKED) ||
+            (mInterfaceTable[tableIndex][0] == 0)) {
+        return -1;
+    } else {
+        return 0;
+    }
+}
+
+const char *SecondaryTableController::getVersion(const char *addr) {
+    if (strchr(addr, ':') != NULL) {
+        return "-6";
+    } else {
+        return "-4";
+    }
+}
+
 int SecondaryTableController::removeRoute(SocketClient *cli, char *iface, char *dest, int prefix,
         char *gateway) {
     int tableIndex = findTableNumber(iface);
     if (tableIndex == -1) {
-        LOGE("Interface not found");
+        ALOGE("Interface not found");
         errno = ENODEV;
         cli->sendMsg(ResponseCode::OperationFailed, "Interface not found", true);
         return -1;
@@ -128,12 +155,46 @@
     return modifyRoute(cli, DEL, iface, dest, prefix, gateway, tableIndex);
 }
 
+int SecondaryTableController::modifyFromRule(int tableIndex, const char *action,
+        const char *addr) {
+    char *cmd;
+
+    if (verifyTableIndex(tableIndex)) {
+        return -1;
+    }
+    asprintf(&cmd, "%s %s rule %s from %s table %d", IP_PATH, getVersion(addr),
+            action, addr, tableIndex + BASE_TABLE_NUMBER);
+    if (runAndFree(NULL, cmd)) {
+        return -1;
+    }
+
+    modifyRuleCount(tableIndex, action);
+    return 0;
+}
+
+int SecondaryTableController::modifyLocalRoute(int tableIndex, const char *action,
+        const char *iface, const char *addr) {
+    char *cmd;
+
+    if (verifyTableIndex(tableIndex)) {
+        return -1;
+    }
+
+    modifyRuleCount(tableIndex, action); // some del's will fail as the iface is already gone.
+
+    asprintf(&cmd, "%s route %s %s dev %s table %d", IP_PATH, action, addr, iface,
+            tableIndex+BASE_TABLE_NUMBER);
+    return runAndFree(NULL, cmd);
+}
+
 int SecondaryTableController::runAndFree(SocketClient *cli, char *cmd) {
     int ret = 0;
     if (strlen(cmd) >= 255) {
-        LOGE("ip command (%s) too long", cmd);
-        errno = E2BIG;
-        cli->sendMsg(ResponseCode::CommandSyntaxError, "Too long", true);
+        if (cli != NULL) {
+            ALOGE("ip command (%s) too long", cmd);
+            errno = E2BIG;
+            cli->sendMsg(ResponseCode::CommandSyntaxError, "Too long", true);
+        }
         free(cmd);
         return -1;
     }
diff --git a/SecondaryTableController.h b/SecondaryTableController.h
index 7f30178..58914df 100644
--- a/SecondaryTableController.h
+++ b/SecondaryTableController.h
@@ -38,13 +38,18 @@
     int addRoute(SocketClient *cli, char *iface, char *dest, int prefixLen, char *gateway);
     int removeRoute(SocketClient *cli, char *iface, char *dest, int prefixLen, char *gateway);
     int findTableNumber(const char *iface);
+    int modifyFromRule(int tableIndex, const char *action, const char *addr);
+    int modifyLocalRoute(int tableIndex, const char *action, const char *iface, const char *addr);
 
 private:
-    int modifyRoute(SocketClient *cli, char *action, char *iface, char *dest, int prefix,
+    int modifyRoute(SocketClient *cli, const char *action, char *iface, char *dest, int prefix,
             char *gateway, int tableIndex);
 
     char mInterfaceTable[INTERFACES_TRACKED][IFNAMSIZ + 1];
     int mInterfaceRuleCount[INTERFACES_TRACKED];
+    void modifyRuleCount(int tableIndex, const char *action);
+    int verifyTableIndex(int tableIndex);
+    const char *getVersion(const char *addr);
 
     int runAndFree(SocketClient *cli, char *cmd);
 };
diff --git a/SoftapController.cpp b/SoftapController.cpp
index cd61462..ce41544 100644
--- a/SoftapController.cpp
+++ b/SoftapController.cpp
@@ -47,7 +47,7 @@
     mPid = 0;
     mSock = socket(AF_INET, SOCK_DGRAM, 0);
     if (mSock < 0)
-        LOGE("Failed to open socket");
+        ALOGE("Failed to open socket");
     memset(mIface, 0, sizeof(mIface));
 }
 
@@ -71,7 +71,7 @@
     wrq.u.data.length = sizeof(tBuf) / sizeof(struct iw_priv_args);
     wrq.u.data.flags = 0;
     if ((ret = ioctl(mSock, SIOCGIWPRIV, &wrq)) < 0) {
-        LOGE("SIOCGIPRIV failed: %d", ret);
+        ALOGE("SIOCGIPRIV failed: %d", ret);
         return ret;
     }
 
@@ -84,7 +84,7 @@
     }
 
     if (i == wrq.u.data.length) {
-        LOGE("iface:%s, fname: %s - function not supported", iface, fname);
+        ALOGE("iface:%s, fname: %s - function not supported", iface, fname);
         return -1;
     }
 
@@ -96,7 +96,7 @@
                 break;
         }
         if (j == i) {
-            LOGE("iface:%s, fname: %s - invalid private ioctl", iface, fname);
+            ALOGE("iface:%s, fname: %s - invalid private ioctl", iface, fname);
             return -1;
         }
         sub_cmd = cmd;
@@ -119,18 +119,18 @@
     int ret;
 
     if (mSock < 0) {
-        LOGE("Softap driver start - failed to open socket");
+        ALOGE("Softap driver start - failed to open socket");
         return -1;
     }
     if (!iface || (iface[0] == '\0')) {
-        LOGD("Softap driver start - wrong interface");
+        ALOGD("Softap driver start - wrong interface");
         iface = mIface;
     }
 
     *mBuf = 0;
     ret = setCommand(iface, "START");
     if (ret < 0) {
-        LOGE("Softap driver start: %d", ret);
+        ALOGE("Softap driver start: %d", ret);
         return ret;
     }
 #ifdef HAVE_HOSTAPD
@@ -139,7 +139,7 @@
     ifc_close();
 #endif
     usleep(AP_DRIVER_START_DELAY);
-    LOGD("Softap driver start: %d", ret);
+    ALOGD("Softap driver start: %d", ret);
     return ret;
 }
 
@@ -147,11 +147,11 @@
     int ret;
 
     if (mSock < 0) {
-        LOGE("Softap driver stop - failed to open socket");
+        ALOGE("Softap driver stop - failed to open socket");
         return -1;
     }
     if (!iface || (iface[0] == '\0')) {
-        LOGD("Softap driver stop - wrong interface");
+        ALOGD("Softap driver stop - wrong interface");
         iface = mIface;
     }
     *mBuf = 0;
@@ -160,11 +160,11 @@
     ret = ifc_down(iface);
     ifc_close();
     if (ret < 0) {
-        LOGE("Softap %s down: %d", iface, ret);
+        ALOGE("Softap %s down: %d", iface, ret);
     }
 #endif
     ret = setCommand(iface, "STOP");
-    LOGD("Softap driver stop: %d", ret);
+    ALOGD("Softap driver stop: %d", ret);
     return ret;
 }
 
@@ -173,16 +173,16 @@
     int ret = 0;
 
     if (mPid) {
-        LOGE("Softap already started");
+        ALOGE("Softap already started");
         return 0;
     }
     if (mSock < 0) {
-        LOGE("Softap startap - failed to open socket");
+        ALOGE("Softap startap - failed to open socket");
         return -1;
     }
 #ifdef HAVE_HOSTAPD
     if ((pid = fork()) < 0) {
-        LOGE("fork failed (%s)", strerror(errno));
+        ALOGE("fork failed (%s)", strerror(errno));
         return -1;
     }
 #endif
@@ -192,20 +192,20 @@
         if (execl("/system/bin/hostapd", "/system/bin/hostapd",
                   "-e", WIFI_ENTROPY_FILE,
                   HOSTAPD_CONF_FILE, (char *) NULL)) {
-            LOGE("execl failed (%s)", strerror(errno));
+            ALOGE("execl failed (%s)", strerror(errno));
         }
 #endif
-        LOGE("Should never get here!");
+        ALOGE("Should never get here!");
         return -1;
     } else {
         *mBuf = 0;
         ret = setCommand(mIface, "AP_BSS_START");
         if (ret) {
-            LOGE("Softap startap - failed: %d", ret);
+            ALOGE("Softap startap - failed: %d", ret);
         }
         else {
            mPid = pid;
-           LOGD("Softap startap - Ok");
+           ALOGD("Softap startap - Ok");
            usleep(AP_BSS_START_DELAY);
         }
     }
@@ -217,23 +217,23 @@
     int ret;
 
     if (mPid == 0) {
-        LOGE("Softap already stopped");
+        ALOGE("Softap already stopped");
         return 0;
     }
 
 #ifdef HAVE_HOSTAPD
-    LOGD("Stopping Softap service");
+    ALOGD("Stopping Softap service");
     kill(mPid, SIGTERM);
     waitpid(mPid, NULL, 0);
 #endif
     if (mSock < 0) {
-        LOGE("Softap stopap - failed to open socket");
+        ALOGE("Softap stopap - failed to open socket");
         return -1;
     }
     *mBuf = 0;
     ret = setCommand(mIface, "AP_BSS_STOP");
     mPid = 0;
-    LOGD("Softap service stopped: %d", ret);
+    ALOGD("Softap service stopped: %d", ret);
     usleep(AP_BSS_STOP_DELAY);
     return ret;
 }
@@ -247,7 +247,7 @@
     if (pos < 0)
         return pos;
     if ((unsigned)(pos + strlen(cmd) + strlen(arg) + 1) >= sizeof(mBuf)) {
-        LOGE("Command line is too big");
+        ALOGE("Command line is too big");
         return -1;
     }
     pos += sprintf(&mBuf[pos], "%s=%s,", cmd, arg);
@@ -271,11 +271,11 @@
     char *ssid, *iface;
 
     if (mSock < 0) {
-        LOGE("Softap set - failed to open socket");
+        ALOGE("Softap set - failed to open socket");
         return -1;
     }
     if (argc < 4) {
-        LOGE("Softap set - missing arguments");
+        ALOGE("Softap set - missing arguments");
         return -1;
     }
 
@@ -293,7 +293,8 @@
     }
 
     asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface="
-            "/data/misc/wifi/hostapd\nssid=%s\nchannel=6\n", iface, ssid);
+            "/data/misc/wifi/hostapd\nssid=%s\nchannel=6\nieee80211n=1\n",
+            iface, ssid);
 
     if (argc > 5) {
         if (!strcmp(argv[5], "wpa-psk")) {
@@ -311,13 +312,13 @@
 
     fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY, 0660);
     if (fd < 0) {
-        LOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
+        ALOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
         free(wbuf);
         free(fbuf);
         return -1;
     }
     if (write(fd, fbuf, strlen(fbuf)) < 0) {
-        LOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
+        ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
         ret = -1;
     }
     close(fd);
@@ -326,14 +327,14 @@
 
     /* Note: apparently open can fail to set permissions correctly at times */
     if (chmod(HOSTAPD_CONF_FILE, 0660) < 0) {
-        LOGE("Error changing permissions of %s to 0660: %s",
+        ALOGE("Error changing permissions of %s to 0660: %s",
                 HOSTAPD_CONF_FILE, strerror(errno));
         unlink(HOSTAPD_CONF_FILE);
         return -1;
     }
 
     if (chown(HOSTAPD_CONF_FILE, AID_SYSTEM, AID_WIFI) < 0) {
-        LOGE("Error changing group ownership of %s to %d: %s",
+        ALOGE("Error changing group ownership of %s to %d: %s",
                 HOSTAPD_CONF_FILE, AID_WIFI, strerror(errno));
         unlink(HOSTAPD_CONF_FILE);
         return -1;
@@ -375,7 +376,7 @@
         i = addParam(i, "MAX_SCB", "8");
     }
     if ((i < 0) || ((unsigned)(i + 4) >= sizeof(mBuf))) {
-        LOGE("Softap set - command is too big");
+        ALOGE("Softap set - command is too big");
         return i;
     }
     sprintf(&mBuf[i], "END");
@@ -383,10 +384,10 @@
     /* system("iwpriv eth0 WL_AP_CFG ASCII_CMD=AP_CFG,SSID=\"AndroidAP\",SEC=\"open\",KEY=12345,CHANNEL=1,PREAMBLE=0,MAX_SCB=8,END"); */
     ret = setCommand(iface, "AP_SET_CFG");
     if (ret) {
-        LOGE("Softap set - failed: %d", ret);
+        ALOGE("Softap set - failed: %d", ret);
     }
     else {
-        LOGD("Softap set - Ok");
+        ALOGD("Softap set - Ok");
         usleep(AP_SET_CFG_DELAY);
     }
 #endif
@@ -419,11 +420,11 @@
     char *fwpath;
 
     if (mSock < 0) {
-        LOGE("Softap fwrealod - failed to open socket");
+        ALOGE("Softap fwrealod - failed to open socket");
         return -1;
     }
     if (argc < 4) {
-        LOGE("Softap fwreload - missing arguments");
+        ALOGE("Softap fwreload - missing arguments");
         return -1;
     }
 
@@ -445,10 +446,10 @@
     ret = setCommand(iface, "WL_FW_RELOAD");
 #endif
     if (ret) {
-        LOGE("Softap fwReload - failed: %d", ret);
+        ALOGE("Softap fwReload - failed: %d", ret);
     }
     else {
-        LOGD("Softap fwReload - Ok");
+        ALOGD("Softap fwReload - Ok");
     }
     return ret;
 }
@@ -458,16 +459,16 @@
     int ret;
 
     if (mSock < 0) {
-        LOGE("Softap clients - failed to open socket");
+        ALOGE("Softap clients - failed to open socket");
         return -1;
     }
     *mBuf = 0;
     ret = setCommand(mIface, "AP_GET_STA_LIST", SOFTAP_MAX_BUFFER_SIZE);
     if (ret) {
-        LOGE("Softap clients - failed: %d", ret);
+        ALOGE("Softap clients - failed: %d", ret);
     } else {
         asprintf(retbuf, "Softap clients:%s", mBuf);
-        LOGD("Softap clients:%s", mBuf);
+        ALOGD("Softap clients:%s", mBuf);
     }
     return ret;
 }
diff --git a/SoftapController.h b/SoftapController.h
index 647b4fa..0d275d2 100644
--- a/SoftapController.h
+++ b/SoftapController.h
@@ -19,7 +19,6 @@
 
 #include <linux/in.h>
 #include <net/if.h>
-#include <utils/List.h>
 
 #define SOFTAP_MAX_BUFFER_SIZE	4096
 #define AP_BSS_START_DELAY	200000
diff --git a/TetherController.cpp b/TetherController.cpp
index 4f54c4d..8d14a14 100644
--- a/TetherController.cpp
+++ b/TetherController.cpp
@@ -53,7 +53,7 @@
 
 int TetherController::setIpFwdEnabled(bool enable) {
 
-    LOGD("Setting IP forward enable = %d", enable);
+    ALOGD("Setting IP forward enable = %d", enable);
 
     // In BP tools mode, do not disable IP forwarding
     char bootmode[PROPERTY_VALUE_MAX] = {0};
@@ -64,12 +64,12 @@
 
     int fd = open("/proc/sys/net/ipv4/ip_forward", O_WRONLY);
     if (fd < 0) {
-        LOGE("Failed to open ip_forward (%s)", strerror(errno));
+        ALOGE("Failed to open ip_forward (%s)", strerror(errno));
         return -1;
     }
 
     if (write(fd, (enable ? "1" : "0"), 1) != 1) {
-        LOGE("Failed to write ip_forward (%s)", strerror(errno));
+        ALOGE("Failed to write ip_forward (%s)", strerror(errno));
         close(fd);
         return -1;
     }
@@ -81,13 +81,13 @@
     int fd = open("/proc/sys/net/ipv4/ip_forward", O_RDONLY);
 
     if (fd < 0) {
-        LOGE("Failed to open ip_forward (%s)", strerror(errno));
+        ALOGE("Failed to open ip_forward (%s)", strerror(errno));
         return false;
     }
 
     char enabled;
     if (read(fd, &enabled, 1) != 1) {
-        LOGE("Failed to read ip_forward (%s)", strerror(errno));
+        ALOGE("Failed to read ip_forward (%s)", strerror(errno));
         close(fd);
         return -1;
     }
@@ -98,18 +98,18 @@
 
 int TetherController::startTethering(int num_addrs, struct in_addr* addrs) {
     if (mDaemonPid != 0) {
-        LOGE("Tethering already started");
+        ALOGE("Tethering already started");
         errno = EBUSY;
         return -1;
     }
 
-    LOGD("Starting tethering services");
+    ALOGD("Starting tethering services");
 
     pid_t pid;
     int pipefd[2];
 
     if (pipe(pipefd) < 0) {
-        LOGE("pipe failed (%s)", strerror(errno));
+        ALOGE("pipe failed (%s)", strerror(errno));
         return -1;
     }
 
@@ -118,7 +118,7 @@
      * the daemon if it exits prematurely
      */
     if ((pid = fork()) < 0) {
-        LOGE("fork failed (%s)", strerror(errno));
+        ALOGE("fork failed (%s)", strerror(errno));
         close(pipefd[0]);
         close(pipefd[1]);
         return -1;
@@ -128,23 +128,25 @@
         close(pipefd[1]);
         if (pipefd[0] != STDIN_FILENO) {
             if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
-                LOGE("dup2 failed (%s)", strerror(errno));
+                ALOGE("dup2 failed (%s)", strerror(errno));
                 return -1;
             }
             close(pipefd[0]);
         }
 
-        int num_processed_args = 6 + (num_addrs/2) + 1; // 1 null for termination
+        int num_processed_args = 7 + (num_addrs/2) + 1; // 1 null for termination
         char **args = (char **)malloc(sizeof(char *) * num_processed_args);
         args[num_processed_args - 1] = NULL;
         args[0] = (char *)"/system/bin/dnsmasq";
         args[1] = (char *)"--keep-in-foreground";
         args[2] = (char *)"--no-resolv";
         args[3] = (char *)"--no-poll";
-        args[4] = (char *)"--pid-file";
-        args[5] = (char *)"";
+        // TODO: pipe through metered status from ConnService
+        args[4] = (char *)"--dhcp-option-force=43,ANDROID_METERED";
+        args[5] = (char *)"--pid-file";
+        args[6] = (char *)"";
 
-        int nextArg = 6;
+        int nextArg = 7;
         for (int addrIndex=0; addrIndex < num_addrs;) {
             char *start = strdup(inet_ntoa(addrs[addrIndex++]));
             char *end = strdup(inet_ntoa(addrs[addrIndex++]));
@@ -152,16 +154,16 @@
         }
 
         if (execv(args[0], args)) {
-            LOGE("execl failed (%s)", strerror(errno));
+            ALOGE("execl failed (%s)", strerror(errno));
         }
-        LOGE("Should never get here!");
+        ALOGE("Should never get here!");
         free(args);
         return 0;
     } else {
         close(pipefd[0]);
         mDaemonPid = pid;
         mDaemonFd = pipefd[1];
-        LOGD("Tethering services running");
+        ALOGD("Tethering services running");
     }
 
     return 0;
@@ -170,18 +172,18 @@
 int TetherController::stopTethering() {
 
     if (mDaemonPid == 0) {
-        LOGE("Tethering already stopped");
+        ALOGE("Tethering already stopped");
         return 0;
     }
 
-    LOGD("Stopping tethering services");
+    ALOGD("Stopping tethering services");
 
     kill(mDaemonPid, SIGTERM);
     waitpid(mDaemonPid, NULL, 0);
     mDaemonPid = 0;
     close(mDaemonFd);
     mDaemonFd = -1;
-    LOGD("Tethering services stopped");
+    ALOGD("Tethering services stopped");
     return 0;
 }
 
@@ -200,19 +202,19 @@
 
     mDnsForwarders->clear();
     for (i = 0; i < numServers; i++) {
-        LOGD("setDnsForwarders(%d = '%s')", i, servers[i]);
+        ALOGD("setDnsForwarders(%d = '%s')", i, servers[i]);
 
         struct in_addr a;
 
         if (!inet_aton(servers[i], &a)) {
-            LOGE("Failed to parse DNS server '%s'", servers[i]);
+            ALOGE("Failed to parse DNS server '%s'", servers[i]);
             mDnsForwarders->clear();
             return -1;
         }
 
         cmdLen += strlen(servers[i]);
         if (cmdLen + 2 >= MAX_CMD_SIZE) {
-            LOGD("Too many DNS servers listed");
+            ALOGD("Too many DNS servers listed");
             break;
         }
 
@@ -222,9 +224,9 @@
     }
 
     if (mDaemonFd != -1) {
-        LOGD("Sending update msg to dnsmasq [%s]", daemonCmd);
+        ALOGD("Sending update msg to dnsmasq [%s]", daemonCmd);
         if (write(mDaemonFd, daemonCmd, strlen(daemonCmd) +1) < 0) {
-            LOGE("Failed to send update command to dnsmasq (%s)", strerror(errno));
+            ALOGE("Failed to send update command to dnsmasq (%s)", strerror(errno));
             mDnsForwarders->clear();
             return -1;
         }
diff --git a/TetherController.h b/TetherController.h
index 1fd4f95..eef94fe 100644
--- a/TetherController.h
+++ b/TetherController.h
@@ -19,10 +19,10 @@
 
 #include <linux/in.h>
 
-#include <utils/List.h>
+#include "List.h"
 
-typedef android::List<char *> InterfaceCollection;
-typedef android::List<struct in_addr> NetAddressCollection;
+typedef android::netd::List<char *> InterfaceCollection;
+typedef android::netd::List<struct in_addr> NetAddressCollection;
 
 class TetherController {
     InterfaceCollection  *mInterfaces;
diff --git a/ThrottleController.cpp b/ThrottleController.cpp
index 1ae31b8..5bd7ad5 100644
--- a/ThrottleController.cpp
+++ b/ThrottleController.cpp
@@ -33,8 +33,7 @@
 
 
 #include "ThrottleController.h"
-
-static char TC_PATH[] = "/system/bin/tc";
+#include "NetdConstants.h"
 
 extern "C" int system_nosh(const char *command);
 extern "C" int ifc_init(void);
@@ -47,7 +46,7 @@
     int res;
 
     if (len == 255) {
-        LOGE("tc command too long");
+        ALOGE("tc command too long");
         errno = E2BIG;
         return -1;
     }
@@ -82,7 +81,7 @@
      */
     sprintf(cmd, "qdisc add dev %s root handle 1: htb default 1 r2q 1000", ifn);
     if (runTcCmd(cmd)) {
-        LOGE("Failed to add root qdisc (%s)", strerror(errno));
+        ALOGE("Failed to add root qdisc (%s)", strerror(errno));
         goto fail;
     }
 
@@ -91,7 +90,7 @@
      */
     sprintf(cmd, "class add dev %s parent 1: classid 1:1 htb rate %dkbit", ifn, txKbps);
     if (runTcCmd(cmd)) {
-        LOGE("Failed to add egress throttling class (%s)", strerror(errno));
+        ALOGE("Failed to add egress throttling class (%s)", strerror(errno));
         goto fail;
     }
 
@@ -100,7 +99,7 @@
      */
     ifc_init();
     if (ifc_up("ifb0")) {
-        LOGE("Failed to up ifb0 (%s)", strerror(errno));
+        ALOGE("Failed to up ifb0 (%s)", strerror(errno));
         goto fail;
     }
 
@@ -109,7 +108,7 @@
      */
     sprintf(cmd, "qdisc add dev ifb0 root handle 1: htb default 1 r2q 1000");
     if (runTcCmd(cmd)) {
-        LOGE("Failed to add root ifb qdisc (%s)", strerror(errno));
+        ALOGE("Failed to add root ifb qdisc (%s)", strerror(errno));
         goto fail;
     }
 
@@ -118,7 +117,7 @@
      */
     sprintf(cmd, "class add dev ifb0 parent 1: classid 1:1 htb rate %dkbit", rxKbps);
     if (runTcCmd(cmd)) {
-        LOGE("Failed to add ingress throttling class (%s)", strerror(errno));
+        ALOGE("Failed to add ingress throttling class (%s)", strerror(errno));
         goto fail;
     }
 
@@ -127,7 +126,7 @@
      */
     sprintf(cmd, "qdisc add dev %s ingress", ifn);
     if (runTcCmd(cmd)) {
-        LOGE("Failed to add ingress qdisc (%s)", strerror(errno));
+        ALOGE("Failed to add ingress qdisc (%s)", strerror(errno));
         goto fail;
     }
 
@@ -137,7 +136,7 @@
     sprintf(cmd, "filter add dev %s parent ffff: protocol ip prio 10 u32 match "
             "u32 0 0 flowid 1:1 action mirred egress redirect dev ifb0", ifn);
     if (runTcCmd(cmd)) {
-        LOGE("Failed to add ifb filter (%s)", strerror(errno));
+        ALOGE("Failed to add ifb filter (%s)", strerror(errno));
         goto fail;
     }
 
diff --git a/logwrapper.c b/logwrapper.c
index 3601b70..f5f4548 100644
--- a/logwrapper.c
+++ b/logwrapper.c
@@ -43,7 +43,7 @@
             } else if (buffer[b] == '\n') {
                 buffer[b] = '\0';
 
-                LOG(LOG_INFO, tag, "%s", &buffer[a]);
+                ALOG(LOG_INFO, tag, "%s", &buffer[a]);
                 a = b + 1;
             }
         }
@@ -51,7 +51,7 @@
         if (a == 0 && b == sizeof(buffer) - 1) {
             // buffer is full, flush
             buffer[b] = '\0';
-            LOG(LOG_INFO, tag, "%s", &buffer[a]);
+            ALOG(LOG_INFO, tag, "%s", &buffer[a]);
             b = 0;
         } else if (a != b) {
             // Keep left-overs
@@ -67,24 +67,24 @@
     // Flush remaining data
     if (a != b) {
         buffer[b] = '\0';
-        LOG(LOG_INFO, tag, "%s", &buffer[a]);
+        ALOG(LOG_INFO, tag, "%s", &buffer[a]);
     }
     status = 0xAAAA;
     if (wait(&status) != -1) {  // Wait for child
         if (WIFEXITED(status)) {
             if (WEXITSTATUS(status) != 0) {
-                LOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
+                ALOG(LOG_INFO, "logwrapper", "%s terminated by exit(%d)", tag,
                         WEXITSTATUS(status));
             }
             return WEXITSTATUS(status);
         } else if (WIFSIGNALED(status))
-            LOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
+            ALOG(LOG_INFO, "logwrapper", "%s terminated by signal %d", tag,
                     WTERMSIG(status));
         else if (WIFSTOPPED(status))
-            LOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
+            ALOG(LOG_INFO, "logwrapper", "%s stopped by signal %d", tag,
                     WSTOPSIG(status));
     } else
-        LOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
+        ALOG(LOG_INFO, "logwrapper", "%s wait() failed: %s (%d)", tag,
                 strerror(errno), errno);
     return -EAGAIN;
 }
@@ -97,13 +97,13 @@
 
     // XXX: PROTECT FROM VIKING KILLER
     if (execv(argv_child[0], argv_child)) {
-        LOG(LOG_ERROR, "logwrapper",
+        ALOG(LOG_ERROR, "logwrapper",
             "executing %s failed: %s", argv_child[0], strerror(errno));
     }
     _exit(1);
 }
 
-int logwrap(int argc, const char* argv[], int background)
+int logwrap(int argc, const char* argv[])
 {
     pid_t pid;
 
@@ -114,21 +114,21 @@
     /* Use ptty instead of socketpair so that STDOUT is not buffered */
     parent_ptty = open("/dev/ptmx", O_RDWR);
     if (parent_ptty < 0) {
-	LOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty");
-	return -errno;
+        ALOG(LOG_ERROR, "logwrapper", "Cannot create parent ptty");
+        return -errno;
     }
 
     if (grantpt(parent_ptty) || unlockpt(parent_ptty) ||
             ptsname_r(parent_ptty, child_devname, sizeof(child_devname))) {
         close(parent_ptty);
-	LOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx");
-	return -1;
+        ALOG(LOG_ERROR, "logwrapper", "Problem with /dev/ptmx");
+        return -1;
     }
 
     pid = fork();
     if (pid < 0) {
         close(parent_ptty);
-	LOG(LOG_ERROR, "logwrapper", "Failed to fork");
+        ALOG(LOG_ERROR, "logwrapper", "Failed to fork");
         return -errno;
     } else if (pid == 0) {
         /*
@@ -137,7 +137,7 @@
         child_ptty = open(child_devname, O_RDWR);
         if (child_ptty < 0) {
             close(parent_ptty);
-	    LOG(LOG_ERROR, "logwrapper", "Problem with child ptty");
+            ALOG(LOG_ERROR, "logwrapper", "Problem with child ptty");
             _exit(errno < 128 ? errno : 1);  // XXX lame
         }
 
@@ -147,22 +147,6 @@
         dup2(child_ptty, 2);
         close(child_ptty);
 
-        if (background) {
-            int fd = open("/dev/cpuctl/bg_non_interactive/tasks", O_WRONLY);
-            if (fd >= 0) {
-                char text[64];
-                sprintf(text, "%d", getpid());
-                if (write(fd, text, strlen(text)) < 0) {
-                    LOG(LOG_WARN, "logwrapper",
-                        "Unable to background process (%s)", strerror(errno));
-                }
-                close(fd);
-            } else {
-                LOG(LOG_WARN, "logwrapper",
-                    "Unable to background process (%s)", strerror(errno));
-            }
-        }
-
         child(argc, argv);
     } else {
         /*
@@ -201,7 +185,7 @@
      * reverted in Change: 11b4e9b2
      */
     if (strnlen(command, sizeof(buffer) - 1) == sizeof(buffer) - 1) {
-        LOGE("command line too long while processing: %s", command);
+        ALOGE("command line too long while processing: %s", command);
         errno = E2BIG;
         return -1;
     }
@@ -209,7 +193,7 @@
     while ((tmp = strsep(&next, " "))) {
         argp[i++] = tmp;
         if (i == 32) {
-            LOGE("argument overflow while processing: %s", command);
+            ALOGE("argument overflow while processing: %s", command);
             errno = E2BIG;
             return -1;
         }
diff --git a/main.cpp b/main.cpp
index 36712a4..b466e42 100644
--- a/main.cpp
+++ b/main.cpp
@@ -33,22 +33,26 @@
 #include "CommandListener.h"
 #include "NetlinkManager.h"
 #include "DnsProxyListener.h"
+#include "MDnsSdListener.h"
 
 static void coldboot(const char *path);
 static void sigchld_handler(int sig);
+static void blockSigpipe();
 
 int main() {
 
     CommandListener *cl;
     NetlinkManager *nm;
     DnsProxyListener *dpl;
+    MDnsSdListener *mdnsl;
 
-    LOGI("Netd 1.0 starting");
+    ALOGI("Netd 1.0 starting");
 
 //    signal(SIGCHLD, sigchld_handler);
+    blockSigpipe();
 
     if (!(nm = NetlinkManager::Instance())) {
-        LOGE("Unable to create NetlinkManager");
+        ALOGE("Unable to create NetlinkManager");
         exit(1);
     };
 
@@ -57,7 +61,7 @@
     nm->setBroadcaster((SocketListener *) cl);
 
     if (nm->start()) {
-        LOGE("Unable to start NetlinkManager (%s)", strerror(errno));
+        ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
         exit(1);
     }
 
@@ -66,15 +70,20 @@
     setenv("ANDROID_DNS_MODE", "local", 1);
     dpl = new DnsProxyListener();
     if (dpl->startListener()) {
-        LOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
+        ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
         exit(1);
     }
 
+    mdnsl = new MDnsSdListener();
+    if (mdnsl->startListener()) {
+        ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
+        exit(1);
+    }
     /*
      * Now that we're up, we can respond to commands
      */
     if (cl->startListener()) {
-        LOGE("Unable to start CommandListener (%s)", strerror(errno));
+        ALOGE("Unable to start CommandListener (%s)", strerror(errno));
         exit(1);
     }
 
@@ -83,7 +92,7 @@
         sleep(1000);
     }
 
-    LOGI("Netd exiting");
+    ALOGI("Netd exiting");
     exit(0);
 }
 
@@ -134,5 +143,15 @@
 
 static void sigchld_handler(int sig) {
     pid_t pid = wait(NULL);
-    LOGD("Child process %d exited", pid);
+    ALOGD("Child process %d exited", pid);
+}
+
+static void blockSigpipe()
+{
+    sigset_t mask;
+
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGPIPE);
+    if (sigprocmask(SIG_BLOCK, &mask, NULL) != 0)
+        ALOGW("WARNING: SIGPIPE not blocked\n");
 }
diff --git a/ndc.c b/ndc.c
index a828f9b..17750bd 100644
--- a/ndc.c
+++ b/ndc.c
@@ -37,24 +37,35 @@
 
 int main(int argc, char **argv) {
     int sock;
+    int cmdOffset = 0;
 
     if (argc < 2)
         usage(argv[0]);
 
-    if ((sock = socket_local_client("netd",
+    // try interpreting the first arg as the socket name - if it fails go back to netd
+
+    if ((sock = socket_local_client(argv[1],
                                      ANDROID_SOCKET_NAMESPACE_RESERVED,
                                      SOCK_STREAM)) < 0) {
-        fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
-        exit(4);
+        if ((sock = socket_local_client("netd",
+                                         ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                         SOCK_STREAM)) < 0) {
+            fprintf(stderr, "Error connecting (%s)\n", strerror(errno));
+            exit(4);
+        }
+    } else {
+        if (argc < 3) usage(argv[0]);
+        printf("Using alt socket %s\n", argv[1]);
+        cmdOffset = 1;
     }
 
-    if (!strcmp(argv[1], "monitor"))
+    if (!strcmp(argv[1+cmdOffset], "monitor"))
         exit(do_monitor(sock, 0));
-    exit(do_cmd(sock, argc, argv));
+    exit(do_cmd(sock, argc-cmdOffset, &(argv[cmdOffset])));
 }
 
 static int do_cmd(int sock, int argc, char **argv) {
-    char final_cmd[255] = { '\0' };
+    char final_cmd[255] = { '0', ' ', '\0' };
     int i;
 
     for (i = 1; i < argc; i++) {
@@ -114,7 +125,7 @@
                     return ECONNRESET;
                 return errno;
             }
-            
+
             int offset = 0;
             int i = 0;
 
@@ -142,7 +153,6 @@
 }
 
 static void usage(char *progname) {
-    fprintf(stderr, "Usage: %s <monitor>|<cmd> [arg1] [arg2...]\n", progname);
+    fprintf(stderr, "Usage: %s [sockname] <monitor>|<cmd> [arg1] [arg2...]\n", progname);
     exit(1);
 }
-
diff --git a/oem_iptables_hook.cpp b/oem_iptables_hook.cpp
index e50ea6a..d3026a9 100644
--- a/oem_iptables_hook.cpp
+++ b/oem_iptables_hook.cpp
@@ -24,11 +24,10 @@
 
 #define LOG_TAG "OemIptablesHook"
 #include <cutils/log.h>
+#include "NetdConstants.h"
 
 extern "C" int system_nosh(const char *command);
 
-static char IPTABLES_PATH[] = "/system/bin/iptables";
-static char OEM_SCRIPT_PATH[] = "/system/bin/oem-iptables-init.sh";
 
 static int runIptablesCmd(const char *cmd) {
     char *buffer;
@@ -36,7 +35,7 @@
     int res;
 
     if (len == 255) {
-        LOGE("command too long");
+        ALOGE("command too long");
         return -1;
     }
 
@@ -94,7 +93,7 @@
 static bool oemInitChains() {
     int ret = system(OEM_SCRIPT_PATH);
     if ((-1 == ret) || (0 != WEXITSTATUS(ret))) {
-        LOGE("%s failed: %s", OEM_SCRIPT_PATH, strerror(errno));
+        ALOGE("%s failed: %s", OEM_SCRIPT_PATH, strerror(errno));
         oemCleanupHooks();
         return false;
     }
@@ -108,7 +107,7 @@
         // but is needed for the case where netd has crashed/stopped and is
         // restarted.
         if (oemCleanupHooks() && oemSetupHooks() && oemInitChains()) {
-            LOGI("OEM iptable hook installed.");
+            ALOGI("OEM iptable hook installed.");
         }
     }
 }