Merge "Export AIDL files as a filegroup for framework.jar"
diff --git a/libnetdutils/Netfilter.cpp b/libnetdutils/Netfilter.cpp
index 6e36f34..bb43de0 100644
--- a/libnetdutils/Netfilter.cpp
+++ b/libnetdutils/Netfilter.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <endian.h>
+#include <arpa/inet.h>
 #include <linux/netfilter.h>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netlink.h>
@@ -25,6 +25,6 @@
 std::ostream& operator<<(std::ostream& os, const nfgenmsg& msg) {
     return os << std::hex << "nfgenmsg["
               << "family: 0x" << static_cast<int>(msg.nfgen_family) << ", version: 0x"
-              << static_cast<int>(msg.version) << ", res_id: 0x" << be16toh(msg.res_id) << "]"
+              << static_cast<int>(msg.version) << ", res_id: 0x" << ntohs(msg.res_id) << "]"
               << std::dec;
 }
diff --git a/server/Android.mk b/server/Android.mk
index 51d2918..d91d063 100644
--- a/server/Android.mk
+++ b/server/Android.mk
@@ -87,6 +87,7 @@
         libnetdaidl \
         libnetutils \
         libnetdutils \
+        libselinux \
         libssl \
         libsysutils \
         libbase \
diff --git a/server/Controllers.cpp b/server/Controllers.cpp
index 4231a53..8b02f60 100644
--- a/server/Controllers.cpp
+++ b/server/Controllers.cpp
@@ -188,13 +188,18 @@
 Controllers::Controllers()
     : clatdCtrl(&netCtrl),
       wakeupCtrl(
-          [this](const std::string& prefix, uid_t uid, gid_t gid, uint64_t timestampNs) {
+          [this](const WakeupController::ReportArgs& args) {
               const auto listener = eventReporter.getNetdEventListener();
               if (listener == nullptr) {
                   ALOGE("getNetdEventListener() returned nullptr. dropping wakeup event");
                   return;
               }
-              listener->onWakeupEvent(String16(prefix.c_str()), uid, gid, timestampNs);
+              String16 prefix = String16(args.prefix.c_str());
+              String16 srcIp = String16(args.srcIp.c_str());
+              String16 dstIp = String16(args.dstIp.c_str());
+              listener->onWakeupEvent(prefix, args.uid, args.ethertype, args.ipNextHeader,
+                                      args.dstHw, srcIp, dstIp, args.srcPort, args.dstPort,
+                                      args.timestampNs);
           },
           &iptablesRestoreCtrl) {
     InterfaceController::initializeAll();
diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp
index 5fe4cbe..32b856a 100644
--- a/server/FwmarkServer.cpp
+++ b/server/FwmarkServer.cpp
@@ -24,6 +24,7 @@
 #include "resolv_netid.h"
 
 #include <netinet/in.h>
+#include <selinux/selinux.h>
 #include <sys/socket.h>
 #include <unistd.h>
 #include <utils/String16.h>
@@ -36,10 +37,33 @@
 namespace android {
 namespace net {
 
-const char UPDATE_DEVICE_STATS[] = "android.permission.UPDATE_DEVICE_STATS";
+constexpr const char *UPDATE_DEVICE_STATS = "android.permission.UPDATE_DEVICE_STATS";
+constexpr const char *SYSTEM_SERVER_CONTEXT = "u:r:system_server:s0";
+
+bool isSystemServer(SocketClient* client) {
+    if (client->getUid() != AID_SYSTEM) {
+        return false;
+    }
+
+    char *context;
+    if (getpeercon(client->getSocket(), &context)) {
+        return false;
+    }
+
+    // We can't use context_new and context_type_get as they're private to libselinux. So just do
+    // a string match instead.
+    bool ret = !strcmp(context, SYSTEM_SERVER_CONTEXT);
+    freecon(context);
+
+    return ret;
+}
 
 bool hasUpdateDeviceStatsPermission(SocketClient* client) {
-    return checkPermission(String16(UPDATE_DEVICE_STATS), client->getPid(), client->getUid());
+    // If the caller is the system server, allow without any further checks.
+    // Otherwise, if the system server's binder thread pool is full, and all the threads are
+    // blocked on a thread that's waiting for us to complete, we deadlock. http://b/69389492
+    return isSystemServer(client) ||
+           checkPermission(String16(UPDATE_DEVICE_STATS), client->getPid(), client->getUid());
 }
 
 FwmarkServer::FwmarkServer(NetworkController* networkController, EventReporter* eventReporter,
diff --git a/server/NFLogListener.cpp b/server/NFLogListener.cpp
index 874cb16..7e3242f 100644
--- a/server/NFLogListener.cpp
+++ b/server/NFLogListener.cpp
@@ -19,7 +19,7 @@
 #include <sstream>
 #include <vector>
 
-#include <endian.h>
+#include <arpa/inet.h>
 #include <linux/netfilter/nfnetlink_log.h>
 
 #include <cutils/log.h>
@@ -46,8 +46,7 @@
 constexpr int kNFLogConfigMsgType = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_CONFIG;
 constexpr int kNFLogPacketMsgType = (NFNL_SUBSYS_ULOG << 8) | NFULNL_MSG_PACKET;
 constexpr int kNetlinkDoneMsgType = (NFNL_SUBSYS_NONE << 8) | NLMSG_DONE;
-constexpr size_t kPacketRange = 0;
-constexpr size_t kCopyMode = NFULNL_COPY_NONE;
+constexpr size_t kDefaultPacketRange = 0;
 
 namespace {
 
@@ -94,11 +93,11 @@
     msg.nlhdr.nlmsg_type = kNFLogConfigMsgType;
     msg.nlhdr.nlmsg_flags = NLM_F_REQUEST;
     msg.nfhdr.nfgen_family = AF_UNSPEC;
-    msg.nfhdr.res_id = htobe16(nfLogGroup);
+    msg.nfhdr.res_id = htons(nfLogGroup);
     msg.attr.nfa_len = sizeof(msg.attr) + sizeof(msg.mode);
     msg.attr.nfa_type = NFULA_CFG_MODE;
     msg.mode.copy_mode = mode;
-    msg.mode.copy_range = htobe32(range);
+    msg.mode.copy_range = htonl(range);
     return send(makeSlice(msg));
 }
 
@@ -115,7 +114,7 @@
     msg.nlhdr.nlmsg_type = kNFLogConfigMsgType;
     msg.nlhdr.nlmsg_flags = NLM_F_REQUEST;
     msg.nfhdr.nfgen_family = AF_UNSPEC;
-    msg.nfhdr.res_id = htobe16(nfLogGroup);
+    msg.nfhdr.res_id = htons(nfLogGroup);
     msg.attr.nfa_len = sizeof(msg.attr) + sizeof(msg.cmd);
     msg.attr.nfa_type = NFULA_CFG_CMD;
     msg.cmd.command = NFULNL_CFG_CMD_BIND;
@@ -135,7 +134,7 @@
     msg.nlhdr.nlmsg_type = kNFLogConfigMsgType;
     msg.nlhdr.nlmsg_flags = NLM_F_REQUEST;
     msg.nfhdr.nfgen_family = AF_UNSPEC;
-    msg.nfhdr.res_id = htobe16(nfLogGroup);
+    msg.nfhdr.res_id = htons(nfLogGroup);
     msg.attr.nfa_len = sizeof(msg.attr) + sizeof(msg.cmd);
     msg.attr.nfa_type = NFULA_CFG_CMD;
     msg.cmd.command = NFULNL_CFG_CMD_UNBIND;
@@ -151,7 +150,7 @@
         nfgenmsg nfmsg = {};
         extract(msg, nfmsg);
         std::lock_guard<std::mutex> guard(mMutex);
-        const auto& fn = findWithDefault(mDispatchMap, be16toh(nfmsg.res_id), kDefaultDispatchFn);
+        const auto& fn = findWithDefault(mDispatchMap, ntohs(nfmsg.res_id), kDefaultDispatchFn);
         fn(nlmsg, nfmsg, drop(msg, sizeof(nfmsg)));
     };
     expectOk(mListener->subscribe(kNFLogPacketMsgType, rxHandler));
@@ -176,6 +175,11 @@
 }
 
 Status NFLogListener::subscribe(uint16_t nfLogGroup, const DispatchFn& fn) {
+    return subscribe(nfLogGroup, kDefaultPacketRange, fn);
+}
+
+Status NFLogListener::subscribe(
+        uint16_t nfLogGroup, uint32_t copyRange, const DispatchFn& fn) {
     const auto sendFn = [this](const Slice msg) { return mListener->send(msg); };
     // Install fn into the dispatch map BEFORE requesting delivery of messages
     {
@@ -185,7 +189,8 @@
     RETURN_IF_NOT_OK(cfgCmdBind(sendFn, nfLogGroup));
 
     // Mode must be set for every nfLogGroup
-    return cfgMode(sendFn, nfLogGroup, kPacketRange, kCopyMode);
+    const uint8_t copyMode = copyRange > 0 ? NFULNL_COPY_PACKET : NFULNL_COPY_NONE;
+    return cfgMode(sendFn, nfLogGroup, copyRange, copyMode);
 }
 
 Status NFLogListener::unsubscribe(uint16_t nfLogGroup) {
diff --git a/server/NFLogListener.h b/server/NFLogListener.h
index 9e5b8a4..7eb9d10 100644
--- a/server/NFLogListener.h
+++ b/server/NFLogListener.h
@@ -40,6 +40,11 @@
     // subscribe() and join() must not be called from the stack of fn().
     virtual netdutils::Status subscribe(uint16_t nfLogGroup, const DispatchFn& fn) = 0;
 
+    // Overloaded version of subscribe which allows to specify a copyRange for obtaining packet
+    // payloads.
+    virtual netdutils::Status subscribe(
+            uint16_t nfLogGroup, uint32_t copyRange, const DispatchFn& fn) = 0;
+
     // Halt delivery of messages from a nfLogGroup previously subscribed to above.
     //
     // Threadsafe.
@@ -64,6 +69,9 @@
 
     netdutils::Status subscribe(uint16_t nfLogGroup, const DispatchFn& fn) override;
 
+    netdutils::Status subscribe(
+            uint16_t nfLogGroup, uint32_t copyRange, const DispatchFn& fn) override;
+
     netdutils::Status unsubscribe(uint16_t nfLogGroup) override;
 
   private:
diff --git a/server/NFLogListenerTest.cpp b/server/NFLogListenerTest.cpp
index d397b2a..c7ee439 100644
--- a/server/NFLogListenerTest.cpp
+++ b/server/NFLogListenerTest.cpp
@@ -19,7 +19,7 @@
 #include <iostream>
 #include <mutex>
 
-#include <endian.h>
+#include <arpa/inet.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <linux/netfilter/nfnetlink_log.h>
@@ -92,7 +92,7 @@
 
         msg.nlmsg.nlmsg_type = kNFLogPacketMsgType;
         msg.nlmsg.nlmsg_len = sizeof(msg);
-        msg.nfmsg.res_id = htobe16(type);
+        msg.nfmsg.res_id = htons(type);
         mPacketFn(msg.nlmsg, drop(makeSlice(msg), sizeof(msg.nlmsg)));
     }
 
@@ -121,7 +121,7 @@
     constexpr uint16_t kType = 38;
     const auto dispatchFn = [&invocations, kType](const nlmsghdr&, const nfgenmsg& nfmsg,
                                                   const netdutils::Slice) {
-        EXPECT_EQ(kType, be16toh(nfmsg.res_id));
+        EXPECT_EQ(kType, ntohs(nfmsg.res_id));
         ++invocations;
     };
     subscribe(kType, dispatchFn);
diff --git a/server/NetlinkCommands.h b/server/NetlinkCommands.h
index 7c0d4a8..ef1ff48 100644
--- a/server/NetlinkCommands.h
+++ b/server/NetlinkCommands.h
@@ -29,7 +29,13 @@
 const sockaddr_nl KERNEL_NLADDR = {AF_NETLINK, 0, 0, 0};
 
 const uint16_t NETLINK_REQUEST_FLAGS = NLM_F_REQUEST | NLM_F_ACK;
-const uint16_t NETLINK_CREATE_REQUEST_FLAGS = NETLINK_REQUEST_FLAGS | NLM_F_CREATE | NLM_F_EXCL;
+const uint16_t NETLINK_ROUTE_CREATE_FLAGS = NETLINK_REQUEST_FLAGS | NLM_F_CREATE | NLM_F_EXCL;
+// Don't create rules with NLM_F_EXCL, because operations such as changing network permissions rely
+// on make-before-break. The kernel did not complain about duplicate rules until ~4.9, at which
+// point it started returning EEXIST. See for example b/69607866 . We can't just ignore the EEXIST
+// because if we hit it, the rule was not created, but we will think it was, and we'll then trip up
+// trying to delete it.
+const uint16_t NETLINK_RULE_CREATE_FLAGS = NETLINK_REQUEST_FLAGS | NLM_F_CREATE;
 const uint16_t NETLINK_DUMP_FLAGS = NLM_F_REQUEST | NLM_F_DUMP;
 
 // Generic code for processing netlink dumps.
diff --git a/server/RouteController.cpp b/server/RouteController.cpp
index 2799075..198c8a8 100644
--- a/server/RouteController.cpp
+++ b/server/RouteController.cpp
@@ -283,7 +283,7 @@
         { PADDING_BUFFER,    oifPadding },
     };
 
-    uint16_t flags = (action == RTM_NEWRULE) ? NETLINK_CREATE_REQUEST_FLAGS : NETLINK_REQUEST_FLAGS;
+    uint16_t flags = (action == RTM_NEWRULE) ? NETLINK_RULE_CREATE_FLAGS : NETLINK_REQUEST_FLAGS;
     for (size_t i = 0; i < ARRAY_SIZE(AF_FAMILIES); ++i) {
         rule.family = AF_FAMILIES[i];
         if (int ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), nullptr)) {
@@ -400,8 +400,7 @@
         { &PRIO_THROW,   isDefaultThrowRoute ? sizeof(PRIO_THROW) : 0 },
     };
 
-    uint16_t flags = (action == RTM_NEWROUTE) ? NETLINK_CREATE_REQUEST_FLAGS :
-                                                NETLINK_REQUEST_FLAGS;
+    uint16_t flags = (action == RTM_NEWROUTE) ? NETLINK_ROUTE_CREATE_FLAGS : NETLINK_REQUEST_FLAGS;
     int ret = sendNetlinkRequest(action, flags, iov, ARRAY_SIZE(iov), nullptr);
     if (ret) {
         ALOGE("Error %s route %s -> %s %s to table %u: %s",
diff --git a/server/WakeupController.cpp b/server/WakeupController.cpp
index 9e1e43b..07ab87a 100644
--- a/server/WakeupController.cpp
+++ b/server/WakeupController.cpp
@@ -16,11 +16,19 @@
 
 #define LOG_TAG "WakeupController"
 
-#include <endian.h>
+#include <arpa/inet.h>
+#include <iostream>
 #include <linux/netfilter/nfnetlink.h>
 #include <linux/netfilter/nfnetlink_log.h>
-#include <iostream>
+#include <sys/socket.h>
+#include <netinet/if_ether.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
 
+#include <android-base/strings.h>
 #include <android-base/stringprintf.h>
 #include <cutils/log.h>
 #include <netdutils/Netfilter.h>
@@ -39,6 +47,72 @@
 
 const char WakeupController::LOCAL_MANGLE_INPUT[] = "wakeupctrl_mangle_INPUT";
 
+const uint32_t WakeupController::kDefaultPacketCopyRange =
+        sizeof(struct tcphdr) + sizeof(struct ip6_hdr);
+
+static void extractIpPorts(WakeupController::ReportArgs& args, Slice payload) {
+    switch (args.ipNextHeader) {
+        case IPPROTO_TCP: {
+            struct tcphdr header;
+            if (extract(payload, header) < sizeof(struct tcphdr)) {
+                return;
+            }
+            args.srcPort = ntohs(header.th_sport);
+            args.dstPort = ntohs(header.th_dport);
+            break;
+        }
+        case IPPROTO_UDP: {
+            struct udphdr header;
+            if (extract(payload, header) < sizeof(struct udphdr)) {
+                return;
+            }
+            args.srcPort = ntohs(header.uh_sport);
+            args.dstPort = ntohs(header.uh_dport);
+            break;
+        }
+        default:
+            break;
+    }
+}
+
+static void extractIpHeader(WakeupController::ReportArgs& args, Slice payload) {
+    switch (args.ethertype) {
+        case ETH_P_IP: {
+            struct iphdr header;
+            if (extract(payload, header) < sizeof(struct iphdr)) {
+                return;
+            }
+            args.ipNextHeader = header.protocol;
+            char addr[INET_ADDRSTRLEN] = {};
+            inet_ntop(AF_INET, &header.saddr, addr, sizeof(addr));
+            args.srcIp = addr;
+            inet_ntop(AF_INET, &header.daddr, addr, sizeof(addr));
+            args.dstIp = addr;
+            extractIpPorts(args, drop(payload, header.ihl * 4)); // ipv4 IHL counts 32 bit words.
+            break;
+        }
+        case ETH_P_IPV6: {
+            struct ip6_hdr header;
+            if (extract(payload, header) < sizeof(struct ip6_hdr)) {
+                return;
+            }
+            args.ipNextHeader = header.ip6_nxt;
+            char addr[INET6_ADDRSTRLEN] = {};
+            inet_ntop(AF_INET6, &header.ip6_src, addr, sizeof(addr));
+            args.srcIp = addr;
+            inet_ntop(AF_INET6, &header.ip6_dst, addr, sizeof(addr));
+            args.dstIp = addr;
+            // TODO: also deal with extension headers
+            if (args.ipNextHeader == IPPROTO_TCP || args.ipNextHeader == IPPROTO_UDP) {
+                extractIpPorts(args, drop(payload, sizeof(header)));
+            }
+            break;
+        }
+        default:
+            break;
+    }
+}
+
 WakeupController::~WakeupController() {
     expectOk(mListener->unsubscribe(NetlinkManager::NFLOG_WAKEUP_GROUP));
 }
@@ -46,40 +120,75 @@
 netdutils::Status WakeupController::init(NFLogListenerInterface* listener) {
     mListener = listener;
     const auto msgHandler = [this](const nlmsghdr&, const nfgenmsg&, const Slice msg) {
-        std::string prefix;
-        uid_t uid = -1;
-        gid_t gid = -1;
-        uint64_t timestampNs = -1;
-        const auto attrHandler = [&prefix, &uid, &gid, &timestampNs](const nlattr attr,
-                                                                     const Slice payload) {
+
+        struct WakeupController::ReportArgs args = {
+            .uid = -1,
+            .gid = -1,
+            .ethertype = -1,
+            .ipNextHeader = -1,
+            .srcPort = -1,
+            .dstPort = -1,
+            // and all other fields set to 0 as the default
+        };
+        bool parseAgain = false;
+
+        const auto attrHandler = [&args, &parseAgain](const nlattr attr, const Slice payload) {
             switch (attr.nla_type) {
                 case NFULA_TIMESTAMP: {
                     timespec ts = {};
                     extract(payload, ts);
                     constexpr uint64_t kNsPerS = 1000000000ULL;
-                    timestampNs = be32toh(ts.tv_nsec) + (be32toh(ts.tv_sec) * kNsPerS);
+                    args.timestampNs = ntohl(ts.tv_nsec) + (ntohl(ts.tv_sec) * kNsPerS);
                     break;
                 }
                 case NFULA_PREFIX:
                     // Strip trailing '\0'
-                    prefix = toString(take(payload, payload.size() - 1));
+                    args.prefix = toString(take(payload, payload.size() - 1));
                     break;
                 case NFULA_UID:
-                    extract(payload, uid);
-                    uid = be32toh(uid);
+                    extract(payload, args.uid);
+                    args.uid = ntohl(args.uid);
                     break;
                 case NFULA_GID:
-                    extract(payload, gid);
-                    gid = be32toh(gid);
+                    extract(payload, args.gid);
+                    args.gid = ntohl(args.gid);
+                    break;
+                case NFULA_HWADDR: {
+                    struct nfulnl_msg_packet_hw hwaddr = {};
+                    extract(payload, hwaddr);
+                    size_t hwAddrLen = ntohs(hwaddr.hw_addrlen);
+                    hwAddrLen = std::min(hwAddrLen, sizeof(hwaddr.hw_addr));
+                    args.dstHw.assign(hwaddr.hw_addr, hwaddr.hw_addr + hwAddrLen);
+                    break;
+                }
+                case NFULA_PACKET_HDR: {
+                    struct nfulnl_msg_packet_hdr packetHdr = {};
+                    extract(payload, packetHdr);
+                    args.ethertype = ntohs(packetHdr.hw_protocol);
+                    break;
+                }
+                case NFULA_PAYLOAD:
+                    // The packet payload is expected to come last in the Netlink message.
+                    // At that point NFULA_PACKET_HDR has already been parsed and processed.
+                    // If this is not the case, set parseAgain to true.
+                    parseAgain = (args.ethertype == -1);
+                    extractIpHeader(args, payload);
                     break;
                 default:
                     break;
             }
         };
+
         forEachNetlinkAttribute(msg, attrHandler);
-        mReport(prefix, uid, gid, timestampNs);
+        if (parseAgain) {
+            // NFULA_PAYLOAD was parsed before NFULA_PACKET_HDR.
+            // Now that the ethertype is known, reparse msg for correctly extracting the payload.
+            forEachNetlinkAttribute(msg, attrHandler);
+        }
+        mReport(args);
     };
-    return mListener->subscribe(NetlinkManager::NFLOG_WAKEUP_GROUP, msgHandler);
+    return mListener->subscribe(NetlinkManager::NFLOG_WAKEUP_GROUP,
+            WakeupController::kDefaultPacketCopyRange, msgHandler);
 }
 
 Status WakeupController::addInterface(const std::string& ifName, const std::string& prefix,
@@ -100,10 +209,11 @@
     constexpr int kRateLimit = 10;
     const char kFormat[] =
         "*mangle\n%s %s -i %s -j NFLOG --nflog-prefix %s --nflog-group %d --nflog-threshold %d"
-        " -m mark --mark 0x%08x/0x%08x -m limit --limit %d/s\nCOMMIT\n";
+        " --nflog-range %d -m mark --mark 0x%08x/0x%08x -m limit --limit %d/s\nCOMMIT\n";
     const auto cmd = StringPrintf(
-        kFormat, action.c_str(), WakeupController::LOCAL_MANGLE_INPUT, ifName.c_str(),
-        prefix.c_str(), NetlinkManager::NFLOG_WAKEUP_GROUP, kBatch, mark, mask, kRateLimit);
+            kFormat, action.c_str(), WakeupController::LOCAL_MANGLE_INPUT, ifName.c_str(),
+            prefix.c_str(), NetlinkManager::NFLOG_WAKEUP_GROUP, kBatch,
+            WakeupController::kDefaultPacketCopyRange, mark, mask, kRateLimit);
 
     std::string out;
     auto rv = mIptables->execute(V4V6, cmd, &out);
diff --git a/server/WakeupController.h b/server/WakeupController.h
index e147f3e..841051b 100644
--- a/server/WakeupController.h
+++ b/server/WakeupController.h
@@ -29,11 +29,30 @@
 
 class WakeupController {
   public:
-    using ReportFn = std::function<void(const std::string&, uid_t, gid_t, uint64_t)>;
+
+    // Simple data struct for passing back packet wakeup event information to the ReportFn callback.
+    struct ReportArgs {
+        std::string prefix;
+        uint64_t timestampNs;
+        int uid;
+        int gid;
+        int ethertype;
+        int ipNextHeader;
+        std::vector<uint8_t> dstHw;
+        std::string srcIp;
+        std::string dstIp;
+        int srcPort;
+        int dstPort;
+    };
+
+    // Callback that is triggered for every wakeup event.
+    using ReportFn = std::function<void(const struct ReportArgs&)>;
 
     // iptables chain where wakeup packets are matched
     static const char LOCAL_MANGLE_INPUT[];
 
+    static const uint32_t kDefaultPacketCopyRange;
+
     WakeupController(ReportFn report, IptablesRestoreInterface* iptables)
         : mReport(report), mIptables(iptables) {}
 
diff --git a/server/WakeupControllerTest.cpp b/server/WakeupControllerTest.cpp
index 05e899c..f7812e5 100644
--- a/server/WakeupControllerTest.cpp
+++ b/server/WakeupControllerTest.cpp
@@ -16,6 +16,12 @@
 
 #include <linux/netfilter/nfnetlink_log.h>
 
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -32,12 +38,16 @@
 namespace android {
 namespace net {
 
+const uint32_t kDefaultPacketCopyRange = WakeupController::kDefaultPacketCopyRange;
+
 using netdutils::status::ok;
 
 class MockNetdEventListener {
   public:
-    MOCK_METHOD4(onWakeupEvent,
-                 void(const std::string& prefix, uid_t uid, gid_t gid, uint64_t timestampNs));
+    MOCK_METHOD10(onWakeupEvent, void(
+            const std::string& prefix, int uid, int ether, int ipNextHeader,
+            std::vector<uint8_t> dstHw, const std::string& srcIp, const std::string& dstIp,
+            int srcPort, int dstPort, uint64_t timestampNs));
 };
 
 class MockIptablesRestore : public IptablesRestoreInterface {
@@ -51,15 +61,19 @@
   public:
     ~MockNFLogListener() override = default;
     MOCK_METHOD2(subscribe, netdutils::Status(uint16_t nfLogGroup, const DispatchFn& fn));
+    MOCK_METHOD3(subscribe,
+            netdutils::Status(uint16_t nfLogGroup, uint32_t copyRange, const DispatchFn& fn));
     MOCK_METHOD1(unsubscribe, netdutils::Status(uint16_t nfLogGroup));
 };
 
 class WakeupControllerTest : public Test {
   protected:
     WakeupControllerTest() {
-        EXPECT_CALL(mListener, subscribe(NetlinkManager::NFLOG_WAKEUP_GROUP, _))
-            .WillOnce(DoAll(SaveArg<1>(&mMessageHandler), Return(ok)));
-        EXPECT_CALL(mListener, unsubscribe(NetlinkManager::NFLOG_WAKEUP_GROUP)).WillOnce(Return(ok));
+        EXPECT_CALL(mListener,
+            subscribe(NetlinkManager::NFLOG_WAKEUP_GROUP, kDefaultPacketCopyRange, _))
+            .WillOnce(DoAll(SaveArg<2>(&mMessageHandler), Return(ok)));
+        EXPECT_CALL(mListener,
+            unsubscribe(NetlinkManager::NFLOG_WAKEUP_GROUP)).WillOnce(Return(ok));
         mController.init(&mListener);
     }
 
@@ -67,14 +81,16 @@
     StrictMock<MockIptablesRestore> mIptables;
     StrictMock<MockNFLogListener> mListener;
     WakeupController mController{
-        [this](const std::string& prefix, uid_t uid, gid_t gid, uint64_t timestampNs) {
-            mEventListener.onWakeupEvent(prefix, uid, gid, timestampNs);
+        [this](const WakeupController::ReportArgs& args) {
+            mEventListener.onWakeupEvent(args.prefix, args.uid, args.ethertype, args.ipNextHeader,
+                                         args.dstHw, args.srcIp, args.dstIp, args.srcPort,
+                                         args.dstPort, args.timestampNs);
         },
         &mIptables};
     NFLogListenerInterface::DispatchFn mMessageHandler;
 };
 
-TEST_F(WakeupControllerTest, msgHandler) {
+TEST_F(WakeupControllerTest, msgHandlerWithPartialAttributes) {
     const char kPrefix[] = "test:prefix";
     const uid_t kUid = 8734;
     const gid_t kGid = 2222;
@@ -96,23 +112,100 @@
 
     msg.uidAttr.nla_type = NFULA_UID;
     msg.uidAttr.nla_len = sizeof(msg.uidAttr) + sizeof(msg.uid);
-    msg.uid = htobe32(kUid);
+    msg.uid = htonl(kUid);
 
     msg.gidAttr.nla_type = NFULA_GID;
     msg.gidAttr.nla_len = sizeof(msg.gidAttr) + sizeof(msg.gid);
-    msg.gid = htobe32(kGid);
+    msg.gid = htonl(kGid);
 
     msg.tsAttr.nla_type = NFULA_TIMESTAMP;
     msg.tsAttr.nla_len = sizeof(msg.tsAttr) + sizeof(msg.ts);
-    msg.ts.tv_sec = htobe32(kTsNs / kNsPerS);
-    msg.ts.tv_nsec = htobe32(kTsNs % kNsPerS);
+    msg.ts.tv_sec = htonl(kTsNs / kNsPerS);
+    msg.ts.tv_nsec = htonl(kTsNs % kNsPerS);
 
     msg.prefixAttr.nla_type = NFULA_PREFIX;
     msg.prefixAttr.nla_len = sizeof(msg.prefixAttr) + sizeof(msg.prefix);
     memcpy(msg.prefix, kPrefix, sizeof(kPrefix));
 
     auto payload = drop(netdutils::makeSlice(msg), offsetof(Msg, uidAttr));
-    EXPECT_CALL(mEventListener, onWakeupEvent(kPrefix, kUid, kGid, kTsNs));
+    EXPECT_CALL(mEventListener,
+            onWakeupEvent(kPrefix, kUid, -1, -1, std::vector<uint8_t>(), "", "", -1, -1, kTsNs));
+    mMessageHandler(msg.nlmsg, msg.nfmsg, payload);
+}
+
+TEST_F(WakeupControllerTest, msgHandler) {
+    const char kPrefix[] = "test:prefix";
+    const uid_t kUid = 8734;
+    const gid_t kGid = 2222;
+    const std::vector<uint8_t> kMacAddr = {11, 22, 33, 44, 55, 66};
+    const char* kSrcIpAddr = "192.168.2.1";
+    const char* kDstIpAddr = "192.168.2.23";
+    const uint16_t kEthertype = 0x800;
+    const uint8_t kIpNextHeader = 6;
+    const uint16_t kSrcPort = 1238;
+    const uint16_t kDstPort = 4567;
+    const uint64_t kNsPerS = 1000000000ULL;
+    const uint64_t kTsNs = 9999 + (34 * kNsPerS);
+
+    struct Msg {
+        nlmsghdr nlmsg;
+        nfgenmsg nfmsg;
+        nlattr uidAttr;
+        uid_t uid;
+        nlattr gidAttr;
+        gid_t gid;
+        nlattr tsAttr;
+        timespec ts;
+        nlattr prefixAttr;
+        char prefix[sizeof(kPrefix)];
+        nlattr packetHeaderAttr;
+        struct nfulnl_msg_packet_hdr packetHeader;
+        nlattr hardwareAddrAttr;
+        struct nfulnl_msg_packet_hw hardwareAddr;
+        nlattr packetPayloadAttr;
+        struct iphdr ipHeader;
+        struct tcphdr tcpHeader;
+    } msg = {};
+
+    msg.prefixAttr.nla_type = NFULA_PREFIX;
+    msg.prefixAttr.nla_len = sizeof(msg.prefixAttr) + sizeof(msg.prefix);
+    memcpy(msg.prefix, kPrefix, sizeof(kPrefix));
+
+    msg.uidAttr.nla_type = NFULA_UID;
+    msg.uidAttr.nla_len = sizeof(msg.uidAttr) + sizeof(msg.uid);
+    msg.uid = htonl(kUid);
+
+    msg.gidAttr.nla_type = NFULA_GID;
+    msg.gidAttr.nla_len = sizeof(msg.gidAttr) + sizeof(msg.gid);
+    msg.gid = htonl(kGid);
+
+    msg.tsAttr.nla_type = NFULA_TIMESTAMP;
+    msg.tsAttr.nla_len = sizeof(msg.tsAttr) + sizeof(msg.ts);
+    msg.ts.tv_sec = htonl(kTsNs / kNsPerS);
+    msg.ts.tv_nsec = htonl(kTsNs % kNsPerS);
+
+    msg.packetHeaderAttr.nla_type = NFULA_PACKET_HDR;
+    msg.packetHeaderAttr.nla_len = sizeof(msg.packetHeaderAttr) + sizeof(msg.packetHeader);
+    msg.packetHeader.hw_protocol = htons(kEthertype);
+
+    msg.hardwareAddrAttr.nla_type = NFULA_HWADDR;
+    msg.hardwareAddrAttr.nla_len = sizeof(msg.hardwareAddrAttr) + sizeof(msg.hardwareAddr);
+    msg.hardwareAddr.hw_addrlen = htons(kMacAddr.size());
+    std::copy(kMacAddr.begin(), kMacAddr.end(), msg.hardwareAddr.hw_addr);
+
+    msg.packetPayloadAttr.nla_type = NFULA_PAYLOAD;
+    msg.packetPayloadAttr.nla_len =
+            sizeof(msg.packetPayloadAttr) + sizeof(msg.ipHeader) + sizeof(msg.tcpHeader);
+    msg.ipHeader.protocol = IPPROTO_TCP;
+    msg.ipHeader.ihl = sizeof(msg.ipHeader) / 4; // ipv4 IHL counts 32 bit words.
+    inet_pton(AF_INET, kSrcIpAddr, &msg.ipHeader.saddr);
+    inet_pton(AF_INET, kDstIpAddr, &msg.ipHeader.daddr);
+    msg.tcpHeader.th_sport = htons(kSrcPort);
+    msg.tcpHeader.th_dport = htons(kDstPort);
+
+    auto payload = drop(netdutils::makeSlice(msg), offsetof(Msg, uidAttr));
+    EXPECT_CALL(mEventListener, onWakeupEvent(kPrefix, kUid, kEthertype, kIpNextHeader, kMacAddr,
+                                              kSrcIpAddr, kDstIpAddr, kSrcPort, kDstPort, kTsNs));
     mMessageHandler(msg.nlmsg, msg.nfmsg, payload);
 }
 
@@ -140,7 +233,7 @@
 
     msg.uidAttr.nla_type = 999;
     msg.uidAttr.nla_len = sizeof(msg.uidAttr) + sizeof(msg.uid);
-    msg.uid = htobe32(kUid);
+    msg.uid = htonl(kUid);
 
     msg.invalid0.nla_type = 0;
     msg.invalid0.nla_len = 0;
@@ -149,19 +242,20 @@
 
     msg.gidAttr.nla_type = NFULA_GID;
     msg.gidAttr.nla_len = sizeof(msg.gidAttr) + sizeof(msg.gid);
-    msg.gid = htobe32(kGid);
+    msg.gid = htonl(kGid);
 
     msg.tsAttr.nla_type = NFULA_TIMESTAMP;
     msg.tsAttr.nla_len = sizeof(msg.tsAttr) - 2;
-    msg.ts.tv_sec = htobe32(kTsNs / kNsPerS);
-    msg.ts.tv_nsec = htobe32(kTsNs % kNsPerS);
+    msg.ts.tv_sec = htonl(kTsNs / kNsPerS);
+    msg.ts.tv_nsec = htonl(kTsNs % kNsPerS);
 
     msg.prefixAttr.nla_type = NFULA_UID;
     msg.prefixAttr.nla_len = sizeof(msg.prefixAttr) + sizeof(msg.prefix);
     memcpy(msg.prefix, kPrefix, sizeof(kPrefix));
 
     auto payload = drop(netdutils::makeSlice(msg), offsetof(Msg, uidAttr));
-    EXPECT_CALL(mEventListener, onWakeupEvent("", 1952805748, kGid, 0));
+    EXPECT_CALL(mEventListener,
+            onWakeupEvent("", 1952805748, -1, -1, std::vector<uint8_t>(), "", "", -1, -1, 0));
     mMessageHandler(msg.nlmsg, msg.nfmsg, payload);
 }
 
@@ -182,7 +276,8 @@
 
     const auto expected = std::string(ones, sizeof(ones) - 1);
     auto payload = drop(netdutils::makeSlice(msg), offsetof(Msg, prefixAttr));
-    EXPECT_CALL(mEventListener, onWakeupEvent(expected, -1, -1, -1));
+    EXPECT_CALL(mEventListener,
+            onWakeupEvent(expected, -1, -1, -1, std::vector<uint8_t>(), "", "", -1, -1, 0));
     mMessageHandler(msg.nlmsg, msg.nfmsg, payload);
 }
 
@@ -194,7 +289,7 @@
     const char kExpected[] =
         "*mangle\n-A wakeupctrl_mangle_INPUT -i test:prefix"
         " -j NFLOG --nflog-prefix wlan8 --nflog-group 3 --nflog-threshold 8"
-        " -m mark --mark 0x12345678/0x0f0f0f0f -m limit --limit 10/s\nCOMMIT\n";
+        " --nflog-range 60 -m mark --mark 0x12345678/0x0f0f0f0f -m limit --limit 10/s\nCOMMIT\n";
     EXPECT_CALL(mIptables, execute(V4V6, kExpected, _)).WillOnce(Return(0));
     mController.addInterface(kPrefix, kIfName, kMark, kMask);
 }
@@ -207,7 +302,7 @@
     const char kExpected[] =
         "*mangle\n-D wakeupctrl_mangle_INPUT -i test:prefix"
         " -j NFLOG --nflog-prefix wlan8 --nflog-group 3 --nflog-threshold 8"
-        " -m mark --mark 0x12345678/0xf0f0f0f0 -m limit --limit 10/s\nCOMMIT\n";
+        " --nflog-range 60 -m mark --mark 0x12345678/0xf0f0f0f0 -m limit --limit 10/s\nCOMMIT\n";
     EXPECT_CALL(mIptables, execute(V4V6, kExpected, _)).WillOnce(Return(0));
     mController.delInterface(kPrefix, kIfName, kMark, kMask);
 }
diff --git a/server/binder/android/net/metrics/INetdEventListener.aidl b/server/binder/android/net/metrics/INetdEventListener.aidl
index 7f4a9e4..2601038 100644
--- a/server/binder/android/net/metrics/INetdEventListener.aidl
+++ b/server/binder/android/net/metrics/INetdEventListener.aidl
@@ -64,10 +64,18 @@
     /**
      * Logs a single RX packet which caused the main CPU to exit sleep state.
      * @param prefix arbitrary string provided via wakeupAddInterface()
-     * @param UID of the destination process or -1 if no UID is available.
-     * @param GID of the destination process or -1 if no GID is available.
-     * @param receive timestamp for the offending packet. In units of nanoseconds and
+     * @param uid UID of the destination process or -1 if no UID is available.
+     * @param ethertype of the RX packet encoded in an int in native order, or -1 if not available.
+     * @param ipNextHeader ip protocol of the RX packet as IPPROTO_* number,
+              or -1 if the packet was not IPv4 or IPv6.
+     * @param dstHw destination hardware address, or 0 if not available.
+     * @param srcIp source IP address, or null if not available.
+     * @param dstIp destination IP address, or null if not available.
+     * @param srcPort src port of RX packet in native order, or -1 if the packet was not UDP or TCP.
+     * @param dstPort dst port of RX packet in native order, or -1 if the packet was not UDP or TCP.
+     * @param timestampNs receive timestamp for the offending packet. In units of nanoseconds and
      *        synchronized to CLOCK_MONOTONIC.
      */
-    void onWakeupEvent(String prefix, int uid, int gid, long timestampNs);
+    void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader, in byte[] dstHw,
+            String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs);
 }