Add connect event reporting

Adds reporting of connect events including netId, destination IP address,
destination port, uid and connect latency.

Also enables the relevant tests in the connect_benchmark.

Currently ignores the new data it receives, further work will be
done in the subsequent CLs.

Test: for now just the benchmarking, in the future CTS

Bug: 29748723

(cherry picked from commit 4b9b78aa02336de9291e5085401cef44c03c3bba)

Change-Id: If8e5ddcd2d29271e2f63a3338a3daf83e7afccdc
diff --git a/server/EventReporter.cpp b/server/EventReporter.cpp
index fd09b8a..c9238f4 100644
--- a/server/EventReporter.cpp
+++ b/server/EventReporter.cpp
@@ -37,6 +37,7 @@
 }
 
 android::sp<INetdEventListener> EventReporter::getNetdEventListener() {
+    std::lock_guard<std::mutex> lock(mutex);
     if (mNetdEventListener == nullptr) {
         // Use checkService instead of getService because getService waits for 5 seconds for the
         // service to become available. The DNS resolver inside netd is started much earlier in the
diff --git a/server/EventReporter.h b/server/EventReporter.h
index cc0e912..3338a04 100644
--- a/server/EventReporter.h
+++ b/server/EventReporter.h
@@ -19,6 +19,7 @@
 
 #include <atomic>
 #include <binder/IServiceManager.h>
+#include <mutex>
 
 #include "android/net/metrics/INetdEventListener.h"
 
@@ -31,16 +32,20 @@
     int getMetricsReportingLevel() const;
 
     // Returns the binder reference to the netd events listener service, attempting to fetch it if
-    // we do not have it already. This method mutates internal state without taking a lock and must
-    // only be called on one thread. This is safe because we only call this in the runCommand
-    // methods of our commands, which are only called by FrameworkListener::onDataAvailable, which
-    // is only called from SocketListener::runListener, which is a single-threaded select loop.
+    // we do not have it already. This method is threadsafe.
     android::sp<android::net::metrics::INetdEventListener> getNetdEventListener();
 
 private:
     std::atomic_int mReportingLevel{
             android::net::metrics::INetdEventListener::REPORTING_LEVEL_FULL};
+    // TODO: consider changing this into an atomic type such as
+    // std::atomic<android::net::metrics::INetdEventListener> and deleting the mutex.
+    //
+    // Alternatively, if this locking causes a performance penalty, have each single-threaded
+    // caller (DnsProxyListener, FwmarkServer) keep their own per-thread copy of NetdEventListener
+    // and remove mNetdEventListener entirely.
     android::sp<android::net::metrics::INetdEventListener> mNetdEventListener;
+    std::mutex mutex;
 
 };
 
diff --git a/server/FwmarkServer.cpp b/server/FwmarkServer.cpp
index 530e96a..de129ab 100644
--- a/server/FwmarkServer.cpp
+++ b/server/FwmarkServer.cpp
@@ -18,14 +18,20 @@
 
 #include "Fwmark.h"
 #include "FwmarkCommand.h"
+#include "NetdConstants.h"
 #include "NetworkController.h"
 #include "resolv_netid.h"
 
 #include <sys/socket.h>
 #include <unistd.h>
+#include <utils/String16.h>
 
-FwmarkServer::FwmarkServer(NetworkController* networkController) :
-        SocketListener("fwmarkd", true), mNetworkController(networkController) {
+using android::String16;
+using android::net::metrics::INetdEventListener;
+
+FwmarkServer::FwmarkServer(NetworkController* networkController, EventReporter* eventReporter) :
+        SocketListener("fwmarkd", true), mNetworkController(networkController),
+        mEventReporter(eventReporter) {
 }
 
 bool FwmarkServer::onDataAvailable(SocketClient* client) {
@@ -47,15 +53,16 @@
 
 int FwmarkServer::processClient(SocketClient* client, int* socketFd) {
     FwmarkCommand command;
+    FwmarkConnectInfo connectInfo;
 
-    iovec iov;
-    iov.iov_base = &command;
-    iov.iov_len = sizeof(command);
-
+    iovec iov[2] = {
+        { &command, sizeof(command) },
+        { &connectInfo, sizeof(connectInfo) },
+    };
     msghdr message;
     memset(&message, 0, sizeof(message));
-    message.msg_iov = &iov;
-    message.msg_iovlen = 1;
+    message.msg_iov = iov;
+    message.msg_iovlen = ARRAY_SIZE(iov);
 
     union {
         cmsghdr cmh;
@@ -71,7 +78,9 @@
         return -errno;
     }
 
-    if (messageLength != sizeof(command)) {
+    if (!((command.cmdId != FwmarkCommand::ON_CONNECT_COMPLETE && messageLength == sizeof(command))
+            || (command.cmdId == FwmarkCommand::ON_CONNECT_COMPLETE
+            && messageLength == sizeof(command) + sizeof(connectInfo)))) {
         return -EBADMSG;
     }
 
@@ -152,6 +161,27 @@
             break;
         }
 
+        case FwmarkCommand::ON_CONNECT_COMPLETE: {
+            // Called after a socket connect() completes.
+            // This reports connect event including netId, destination IP address, destination port,
+            // uid and connect latency
+            android::sp<android::net::metrics::INetdEventListener> netdEventListener =
+                    mEventReporter->getNetdEventListener();
+
+            if (netdEventListener != nullptr) {
+                char addrstr[INET6_ADDRSTRLEN];
+                char portstr[sizeof("65536")];
+                const int ret = getnameinfo((sockaddr*) &connectInfo.addr, sizeof(connectInfo.addr),
+                        addrstr, sizeof(addrstr), portstr, sizeof(portstr),
+                        NI_NUMERICHOST | NI_NUMERICSERV);
+
+                netdEventListener->onConnectEvent(fwmark.netId, connectInfo.latencyMs,
+                        (ret == 0) ? String16(addrstr) : String16(""),
+                        (ret == 0) ? strtoul(portstr, NULL, 10) : 0, client->getUid());
+            }
+            break;
+        }
+
         case FwmarkCommand::SELECT_NETWORK: {
             fwmark.netId = command.netId;
             if (command.netId == NETID_UNSET) {
diff --git a/server/FwmarkServer.h b/server/FwmarkServer.h
index 12096be..644acb8 100644
--- a/server/FwmarkServer.h
+++ b/server/FwmarkServer.h
@@ -17,13 +17,15 @@
 #ifndef NETD_SERVER_FWMARK_SERVER_H
 #define NETD_SERVER_FWMARK_SERVER_H
 
+#include "android/net/metrics/INetdEventListener.h"
+#include "EventReporter.h"
 #include "sysutils/SocketListener.h"
 
 class NetworkController;
 
 class FwmarkServer : public SocketListener {
 public:
-    explicit FwmarkServer(NetworkController* networkController);
+    explicit FwmarkServer(NetworkController* networkController, EventReporter* eventReporter);
 
 private:
     // Overridden from SocketListener:
@@ -33,6 +35,7 @@
     int processClient(SocketClient* client, int* socketFd);
 
     NetworkController* const mNetworkController;
+    EventReporter* mEventReporter;
 };
 
 #endif  // NETD_SERVER_FWMARK_SERVER_H
diff --git a/server/binder/android/net/metrics/INetdEventListener.aidl b/server/binder/android/net/metrics/INetdEventListener.aidl
index 5a5ed8f..49a20f2 100644
--- a/server/binder/android/net/metrics/INetdEventListener.aidl
+++ b/server/binder/android/net/metrics/INetdEventListener.aidl
@@ -48,4 +48,15 @@
      */
     void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs, String hostname,
             in String[] ipAddresses, int ipAddressesCount, int uid);
+
+    /**
+     * Logs a single connect library call.
+     *
+     * @param netId the ID of the network the lookup was performed on.
+     * @param latencyMs the latency of the function call.
+     * @param ipAddr destination IP address.
+     * @param port destination port number.
+     * @param uid the UID of the application that performed the connection.
+     */
+    void onConnectEvent(int netId, int latencyMs, String ipAddr, int port, int uid);
 }
diff --git a/server/main.cpp b/server/main.cpp
index da611cf..ae3a71a 100644
--- a/server/main.cpp
+++ b/server/main.cpp
@@ -99,7 +99,7 @@
         exit(1);
     }
 
-    FwmarkServer fwmarkServer(&gCtls->netCtrl);
+    FwmarkServer fwmarkServer(&gCtls->netCtrl, &gCtls->eventReporter);
     if (fwmarkServer.startListener()) {
         ALOGE("Unable to start FwmarkServer (%s)", strerror(errno));
         exit(1);