Add a binder RPC for tethering stats.

Bug: 32163131
Bug: 64995262
Test: netd_{unit,integration}_test pass
Change-Id: I9c7dfdaf6b823840b1691761930b53c30a4dcbc3
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index b2f362e..e5ef53f 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -60,6 +60,7 @@
 using android::net::INetd;
 using android::net::TunInterface;
 using android::net::UidRange;
+using android::os::PersistableBundle;
 
 static const char* IP_RULE_V4 = "-4";
 static const char* IP_RULE_V6 = "-6";
@@ -696,3 +697,79 @@
         EXPECT_EQ(td.expectedReturnCode, status.serviceSpecificErrorCode());
     }
 }
+
+void expectNoTestCounterRules() {
+    for (const auto& binary : { IPTABLES_PATH, IP6TABLES_PATH }) {
+        std::string command = StringPrintf("%s -w -nvL tetherctrl_counters", binary);
+        std::string allRules = Join(runCommand(command), "\n");
+        EXPECT_EQ(std::string::npos, allRules.find("netdtest_"));
+    }
+}
+
+void addTetherCounterValues(const char *path, std::string if1, std::string if2, int byte, int pkt) {
+    runCommand(StringPrintf("%s -w -A tetherctrl_counters -i %s -o %s -j RETURN -c %d %d",
+                            path, if1.c_str(), if2.c_str(), pkt, byte));
+}
+
+void delTetherCounterValues(const char *path, std::string if1, std::string if2) {
+    runCommand(StringPrintf("%s -w -D tetherctrl_counters -i %s -o %s -j RETURN",
+                            path, if1.c_str(), if2.c_str()));
+    runCommand(StringPrintf("%s -w -D tetherctrl_counters -i %s -o %s -j RETURN",
+                            path, if2.c_str(), if1.c_str()));
+}
+
+TEST_F(BinderTest, TestTetherGetStats) {
+    expectNoTestCounterRules();
+
+    // TODO: fold this into more comprehensive tests once we have binder RPCs for enabling and
+    // disabling tethering. We don't check the return value because these commands will fail if
+    // tethering is already enabled.
+    runCommand(StringPrintf("%s -w -N tetherctrl_counters", IPTABLES_PATH));
+    runCommand(StringPrintf("%s -w -N tetherctrl_counters", IP6TABLES_PATH));
+
+    std::string intIface1 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
+    std::string intIface2 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
+    std::string intIface3 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
+    std::string extIface1 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
+    std::string extIface2 = StringPrintf("netdtest_%u", arc4random_uniform(10000));
+
+    addTetherCounterValues(IPTABLES_PATH,  intIface1, extIface1, 123, 111);
+    addTetherCounterValues(IP6TABLES_PATH, intIface1, extIface1, 456,  10);
+    addTetherCounterValues(IPTABLES_PATH,  extIface1, intIface1, 321, 222);
+    addTetherCounterValues(IP6TABLES_PATH, extIface1, intIface1, 654,  20);
+    // RX is from external to internal, and TX is from internal to external.
+    // So rxBytes is 321 + 654  = 975, txBytes is 123 + 456 = 579, etc.
+    std::vector<int64_t> expected1 = { 975, 242, 579, 121 };
+
+    addTetherCounterValues(IPTABLES_PATH,  intIface2, extIface2, 1000, 333);
+    addTetherCounterValues(IP6TABLES_PATH, intIface2, extIface2, 3000,  30);
+
+    addTetherCounterValues(IPTABLES_PATH,  extIface2, intIface2, 2000, 444);
+    addTetherCounterValues(IP6TABLES_PATH, extIface2, intIface2, 4000,  40);
+
+    addTetherCounterValues(IP6TABLES_PATH, intIface3, extIface2, 1000,  25);
+    addTetherCounterValues(IP6TABLES_PATH, extIface2, intIface3, 2000,  35);
+    std::vector<int64_t> expected2 = { 8000, 519, 5000, 388 };
+
+    PersistableBundle stats;
+    binder::Status status = mNetd->tetherGetStats(&stats);
+    EXPECT_TRUE(status.isOk()) << "Getting tethering stats failed: " << status;
+
+    std::vector<int64_t> actual1;
+    EXPECT_TRUE(stats.getLongVector(String16(extIface1.c_str()), &actual1));
+    EXPECT_EQ(expected1, actual1);
+
+    std::vector<int64_t> actual2;
+    EXPECT_TRUE(stats.getLongVector(String16(extIface2.c_str()), &actual2));
+    EXPECT_EQ(expected2, actual2);
+
+    for (const auto& path : { IPTABLES_PATH, IP6TABLES_PATH }) {
+        delTetherCounterValues(path, intIface1, extIface1);
+        delTetherCounterValues(path, intIface2, extIface2);
+        if (path == IP6TABLES_PATH) {
+            delTetherCounterValues(path, intIface3, extIface2);
+        }
+    }
+
+    expectNoTestCounterRules();
+}