Merge "Add NETLINK_ROUTE processing to the netlink client code, so that Ethernet physical-layer up/down events can be tracked.  Upper layers will use these events to enable/disable Ethernet connectivity."
diff --git a/include/sysutils/NetlinkEvent.h b/include/sysutils/NetlinkEvent.h
index b329b09..1ee9849 100644
--- a/include/sysutils/NetlinkEvent.h
+++ b/include/sysutils/NetlinkEvent.h
@@ -30,17 +30,23 @@
     const static int NlActionAdd;
     const static int NlActionRemove;
     const static int NlActionChange;
+    const static int NlActionLinkDown;
+    const static int NlActionLinkUp;
 
     NetlinkEvent();
     virtual ~NetlinkEvent();
 
-    bool decode(char *buffer, int size);
+    bool decode(char *buffer, int size, int format);
     const char *findParam(const char *paramName);
 
     const char *getSubsystem() { return mSubsystem; }
     int getAction() { return mAction; }
 
     void dump();
+
+ protected:
+    bool parseBinaryNetlinkMessage(char *buffer, int size);
+    bool parseAsciiNetlinkMessage(char *buffer, int size);
 };
 
 #endif
diff --git a/include/sysutils/NetlinkListener.h b/include/sysutils/NetlinkListener.h
index 2880046..1cf5f9f 100644
--- a/include/sysutils/NetlinkListener.h
+++ b/include/sysutils/NetlinkListener.h
@@ -22,9 +22,13 @@
 
 class NetlinkListener : public SocketListener {
     char mBuffer[64 * 1024];
+    int mFormat;
 
 public:
-    NetlinkListener(int socket);
+    static const int NETLINK_FORMAT_ASCII = 0;
+    static const int NETLINK_FORMAT_BINARY = 1;
+
+    NetlinkListener(int socket, int format);
     virtual ~NetlinkListener() {}
 
 protected:
diff --git a/libsysutils/src/NetlinkEvent.cpp b/libsysutils/src/NetlinkEvent.cpp
index 86c1f42..2e3143d 100644
--- a/libsysutils/src/NetlinkEvent.cpp
+++ b/libsysutils/src/NetlinkEvent.cpp
@@ -19,12 +19,20 @@
 #define LOG_TAG "NetlinkEvent"
 #include <cutils/log.h>
 
+#include <sysutils/NetlinkListener.h>
 #include <sysutils/NetlinkEvent.h>
 
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/rtnetlink.h>
+#include <linux/if.h>
+
 const int NetlinkEvent::NlActionUnknown = 0;
 const int NetlinkEvent::NlActionAdd = 1;
 const int NetlinkEvent::NlActionRemove = 2;
 const int NetlinkEvent::NlActionChange = 3;
+const int NetlinkEvent::NlActionLinkUp = 4;
+const int NetlinkEvent::NlActionLinkDown = 5;
 
 NetlinkEvent::NetlinkEvent() {
     mAction = NlActionUnknown;
@@ -56,7 +64,56 @@
     }
 }
 
-bool NetlinkEvent::decode(char *buffer, int size) {
+/*
+ * Parse an binary message from a NETLINK_ROUTE netlink socket.
+ */
+bool NetlinkEvent::parseBinaryNetlinkMessage(char *buffer, int size) {
+    size_t sz = size;
+    struct nlmsghdr *nh = (struct nlmsghdr *) buffer;
+
+    while (NLMSG_OK(nh, sz) && (nh->nlmsg_type != NLMSG_DONE)) {
+        if (nh->nlmsg_type == RTM_NEWLINK) {
+            int len = nh->nlmsg_len - sizeof(*nh);
+            struct ifinfomsg *ifi;
+
+            if (sizeof(*ifi) <= (size_t) len) {
+                ifi = (ifinfomsg *)NLMSG_DATA(nh);
+
+                if ((ifi->ifi_flags & IFF_LOOPBACK) == 0) {
+                    struct rtattr *rta = (struct rtattr *)
+                      ((char *) ifi + NLMSG_ALIGN(sizeof(*ifi)));
+                    len = NLMSG_PAYLOAD(nh, sizeof(*ifi));
+
+                    while(RTA_OK(rta, len)) {
+                        switch(rta->rta_type) {
+                        case IFLA_IFNAME:
+                            char buffer[16 + IFNAMSIZ];
+                            snprintf(buffer, sizeof(buffer), "INTERFACE=%s",
+                                     (char *) RTA_DATA(rta));
+                            mParams[0] = strdup(buffer);
+                            mAction = (ifi->ifi_flags & IFF_LOWER_UP) ?
+                              NlActionLinkUp : NlActionLinkDown;
+                            mSubsystem = strdup("net");
+                            break;
+                        }
+
+                        rta = RTA_NEXT(rta, len);
+                    }
+                }
+            }
+        }
+
+        nh = NLMSG_NEXT(nh, size);
+    }
+
+    return true;
+}
+
+/*
+ * Parse an ASCII-formatted message from a NETLINK_KOBJECT_UEVENT
+ * netlink socket.
+ */
+bool NetlinkEvent::parseAsciiNetlinkMessage(char *buffer, int size) {
     char *s = buffer;
     char *end;
     int param_idx = 0;
@@ -92,6 +149,14 @@
     return true;
 }
 
+bool NetlinkEvent::decode(char *buffer, int size, int format) {
+  if (format == NetlinkListener::NETLINK_FORMAT_BINARY) {
+    return parseBinaryNetlinkMessage(buffer, size);
+  } else {
+    return parseAsciiNetlinkMessage(buffer, size);
+  }
+}
+
 const char *NetlinkEvent::findParam(const char *paramName) {
     size_t len = strlen(paramName);
     for (int i = 0; mParams[i] && i < NL_PARAMS_MAX; ++i) {
diff --git a/libsysutils/src/NetlinkListener.cpp b/libsysutils/src/NetlinkListener.cpp
index e2a354e..8361120 100644
--- a/libsysutils/src/NetlinkListener.cpp
+++ b/libsysutils/src/NetlinkListener.cpp
@@ -25,8 +25,9 @@
 #include <sysutils/NetlinkListener.h>
 #include <sysutils/NetlinkEvent.h>
 
-NetlinkListener::NetlinkListener(int socket) :
+NetlinkListener::NetlinkListener(int socket, int format) :
                             SocketListener(socket, false) {
+    mFormat = format;
 }
 
 bool NetlinkListener::onDataAvailable(SocketClient *cli)
@@ -40,13 +41,14 @@
     }
 
     NetlinkEvent *evt = new NetlinkEvent();
-    if (!evt->decode(mBuffer, count)) {
+    int err = evt->decode(mBuffer, count, mFormat);
+
+    if (!err) {
         SLOGE("Error decoding NetlinkEvent");
-        goto out;
+    } else {
+        onEvent(evt);
     }
 
-    onEvent(evt);
-out:
     delete evt;
     return true;
 }