Idletimer-related commands porting

Test: built, flashed, booted
      system/netd/tests/runtests.sh passes

Change-Id: I10eec44acca8e4d5a7c8de64b89590e3cccda597
diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp
index 27b117e..3730bce 100644
--- a/server/CommandListener.cpp
+++ b/server/CommandListener.cpp
@@ -114,7 +114,7 @@
     registerLockingCmd(new ListTtysCmd());
     registerLockingCmd(new PppdCmd());
     registerLockingCmd(new BandwidthControlCmd(), gCtls->bandwidthCtrl.lock);
-    registerLockingCmd(new IdletimerControlCmd());
+    registerLockingCmd(new IdletimerControlCmd(), gCtls->idletimerCtrl.lock);
     registerLockingCmd(new ResolverCmd());
     registerLockingCmd(new FirewallCmd(), gCtls->firewallCtrl.lock);
     registerLockingCmd(new ClatdCmd());
@@ -972,23 +972,6 @@
 
     ALOGV("idletimerctrlcmd: argc=%d %s %s ...", argc, argv[0], argv[1]);
 
-    if (!strcmp(argv[1], "enable")) {
-      if (0 != gCtls->idletimerCtrl.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 != gCtls->idletimerCtrl.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 != 5) {
             cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
diff --git a/server/IdletimerController.cpp b/server/IdletimerController.cpp
index 5c0cc86..acb8c6a 100644
--- a/server/IdletimerController.cpp
+++ b/server/IdletimerController.cpp
@@ -138,29 +138,6 @@
     return true;
 }
 
-int IdletimerController::setDefaults() {
-    std::vector<std::string> cmds = {
-        "*raw",
-        StringPrintf(":%s -", LOCAL_RAW_PREROUTING),
-        "COMMIT",
-        "*mangle",
-        StringPrintf(":%s -", LOCAL_MANGLE_POSTROUTING),
-        "COMMIT\n",
-    };
-
-    return execIptablesRestore(V4V6, Join(cmds, '\n'));
-}
-
-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,
                                                   const char *classLabel) {
@@ -181,7 +158,7 @@
         "COMMIT\n",
     };
 
-    return execIptablesRestore(V4V6, Join(cmds, '\n'));
+    return (execIptablesRestore(V4V6, Join(cmds, '\n')) == 0) ? 0 : -EREMOTEIO;
 }
 
 int IdletimerController::addInterfaceIdletimer(const char *iface,
diff --git a/server/IdletimerController.h b/server/IdletimerController.h
index 87e0b4e..5cd162c 100644
--- a/server/IdletimerController.h
+++ b/server/IdletimerController.h
@@ -26,8 +26,6 @@
     IdletimerController();
     virtual ~IdletimerController();
 
-    int enableIdletimerControl();
-    int disableIdletimerControl();
     int addInterfaceIdletimer(const char *iface, uint32_t timeout,
                               const char *classLabel);
     int removeInterfaceIdletimer(const char *iface, uint32_t timeout,
@@ -36,10 +34,10 @@
 
     static const char* LOCAL_RAW_PREROUTING;
     static const char* LOCAL_MANGLE_POSTROUTING;
+    std::mutex lock;
 
- private:
+  private:
     enum IptOp { IptOpAdd, IptOpDelete };
-    int setDefaults();
     int runIpxtablesCmd(int argc, const char **cmd);
     int modifyInterfaceIdletimer(IptOp op, const char *iface, uint32_t timeout,
                                  const char *classLabel);
diff --git a/server/IdletimerControllerTest.cpp b/server/IdletimerControllerTest.cpp
index ace3fd9..30c2298 100644
--- a/server/IdletimerControllerTest.cpp
+++ b/server/IdletimerControllerTest.cpp
@@ -40,29 +40,6 @@
     expectIptablesRestoreCommands(ExpectedIptablesCommands{});
 }
 
-TEST_F(IdletimerControllerTest, TestEnableDisable) {
-    std::vector<std::string> expected = {
-        "*raw\n"
-        ":idletimer_raw_PREROUTING -\n"
-        "COMMIT\n"
-        "*mangle\n"
-        ":idletimer_mangle_POSTROUTING -\n"
-        "COMMIT\n",
-    };
-
-    mIt.enableIdletimerControl();
-    expectIptablesRestoreCommands(expected);
-
-    mIt.enableIdletimerControl();
-    expectIptablesRestoreCommands(expected);
-
-    mIt.disableIdletimerControl();
-    expectIptablesRestoreCommands(expected);
-
-    mIt.disableIdletimerControl();
-    expectIptablesRestoreCommands(expected);
-}
-
 const std::vector<std::string> makeAddRemoveCommands(bool add) {
     const char *op = add ? "-A" : "-D";
     std::vector<std::string> cmds = {
diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp
index 12c78ef..c155ac1 100644
--- a/server/NetdNativeService.cpp
+++ b/server/NetdNativeService.cpp
@@ -747,5 +747,33 @@
     return binder::Status::ok();
 }
 
+binder::Status NetdNativeService::idletimerAddInterface(const std::string& ifName, int32_t timeout,
+                                                        const std::string& classLabel) {
+    NETD_LOCKING_RPC(NETWORK_STACK, gCtls->idletimerCtrl.lock);
+    auto entry = gLog.newEntry()
+                         .prettyFunction(__PRETTY_FUNCTION__)
+                         .arg(ifName)
+                         .arg(timeout)
+                         .arg(classLabel);
+    int res =
+            gCtls->idletimerCtrl.addInterfaceIdletimer(ifName.c_str(), timeout, classLabel.c_str());
+    gLog.log(entry.returns(res).withAutomaticDuration());
+    return statusFromErrcode(res);
+}
+
+binder::Status NetdNativeService::idletimerRemoveInterface(const std::string& ifName,
+                                                           int32_t timeout,
+                                                           const std::string& classLabel) {
+    NETD_LOCKING_RPC(NETWORK_STACK, gCtls->idletimerCtrl.lock);
+    auto entry = gLog.newEntry()
+                         .prettyFunction(__PRETTY_FUNCTION__)
+                         .arg(ifName)
+                         .arg(timeout)
+                         .arg(classLabel);
+    int res = gCtls->idletimerCtrl.removeInterfaceIdletimer(ifName.c_str(), timeout,
+                                                            classLabel.c_str());
+    gLog.log(entry.returns(res).withAutomaticDuration());
+    return statusFromErrcode(res);
+}
 }  // namespace net
 }  // namespace android
diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h
index 6ae5c89..3023f80 100644
--- a/server/NetdNativeService.h
+++ b/server/NetdNativeService.h
@@ -194,6 +194,12 @@
             int32_t oKey);
 
     binder::Status removeVirtualTunnelInterface(const std::string& deviceName);
+
+    // Idletimer-related commands
+    binder::Status idletimerAddInterface(const std::string& ifName, int32_t timeout,
+                                         const std::string& classLabel) override;
+    binder::Status idletimerRemoveInterface(const std::string& ifName, int32_t timeout,
+                                            const std::string& classLabel) override;
 };
 
 }  // namespace net
diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl
index 6ab6183..9401e92 100644
--- a/server/binder/android/net/INetd.aidl
+++ b/server/binder/android/net/INetd.aidl
@@ -563,4 +563,32 @@
     * running on the device.
     */
     boolean trafficCheckBpfStatsEnable();
+
+   /**
+    * Add idletimer for specific interface
+    *
+    * @param ifName Name of target interface
+    * @param timeout The time in seconds that will trigger idletimer
+    * @param classLabel The unique identifier for this idletimer
+    * @throws ServiceSpecificException in case of failure, with an error code indicating the
+    *         cause of the the failure.
+    */
+    void idletimerAddInterface(
+            in @utf8InCpp String ifName,
+            int timeout,
+            in @utf8InCpp String classLabel);
+
+   /**
+    * Remove idletimer for specific interface
+    *
+    * @param ifName Name of target interface
+    * @param timeout The time in seconds that will trigger idletimer
+    * @param classLabel The unique identifier for this idletimer
+    * @throws ServiceSpecificException in case of failure, with an error code indicating the
+    *         cause of the the failure.
+    */
+    void idletimerRemoveInterface(
+            in @utf8InCpp String ifName,
+            int timeout,
+            in @utf8InCpp String classLabel);
 }
diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp
index 689e2e6..e97d191 100644
--- a/tests/binder_test.cpp
+++ b/tests/binder_test.cpp
@@ -57,6 +57,8 @@
 #define IP6TABLES_PATH "/system/bin/ip6tables"
 #define IPTABLES_PATH "/system/bin/iptables"
 #define TUN_DEV "/dev/tun"
+#define RAW_TABLE "raw"
+#define MANGLE_TABLE "mangle"
 
 using namespace android;
 using namespace android::base;
@@ -150,7 +152,6 @@
 
 static std::vector<std::string> runCommand(const std::string& command) {
     std::vector<std::string> lines;
-
     FILE *f = popen(command.c_str(), "r");  // NOLINT(cert-env33-c)
     if (f == nullptr) {
         perror("popen");
@@ -958,3 +959,78 @@
 
     expectNoTestCounterRules();
 }
+namespace {
+
+constexpr char chainName_LOCAL_RAW_PREROUTING[] = "idletimer_raw_PREROUTING";
+constexpr char chainName_MANGLE_POSTROUTING[] = "idletimer_mangle_POSTROUTING";
+
+static std::vector<std::string> listIptablesRuleByTable(const char* binary, const char* table,
+                                                        const char* chainName) {
+    std::string command = StringPrintf("%s -t %s -w -n -v -L %s", binary, table, chainName);
+    return runCommand(command);
+}
+
+bool iptablesIdleTimerInterfcaeRuleExists(const char* binary, const char* chainName,
+                                          const std::string& expectedInterface,
+                                          const std::string& expectedRule, const char* table) {
+    std::vector<std::string> rules = listIptablesRuleByTable(binary, table, chainName);
+    for (const auto& rule : rules) {
+        if (rule.find(expectedInterface) != std::string::npos) {
+            if (rule.find(expectedRule) != std::string::npos) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+void expectIdletimerInterfaceRuleExists(const std::string& ifname, int timeout,
+                                        const std::string& classLable) {
+    std::string IdletimerRule =
+            StringPrintf("timeout:%u label:%s send_nl_msg:1", timeout, classLable.c_str());
+    for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
+        EXPECT_TRUE(iptablesIdleTimerInterfcaeRuleExists(binary, chainName_LOCAL_RAW_PREROUTING,
+                                                         ifname, IdletimerRule, RAW_TABLE));
+        EXPECT_TRUE(iptablesIdleTimerInterfcaeRuleExists(binary, chainName_MANGLE_POSTROUTING,
+                                                         ifname, IdletimerRule, MANGLE_TABLE));
+    }
+}
+
+void expectIdletimerInterfaceRuleNotExists(const std::string& ifname, int timeout,
+                                           const std::string& classLable) {
+    std::string IdletimerRule =
+            StringPrintf("timeout:%u label:%s send_nl_msg:1", timeout, classLable.c_str());
+    for (const auto& binary : {IPTABLES_PATH, IP6TABLES_PATH}) {
+        EXPECT_FALSE(iptablesIdleTimerInterfcaeRuleExists(binary, chainName_LOCAL_RAW_PREROUTING,
+                                                          ifname, IdletimerRule, RAW_TABLE));
+        EXPECT_FALSE(iptablesIdleTimerInterfcaeRuleExists(binary, chainName_MANGLE_POSTROUTING,
+                                                          ifname, IdletimerRule, MANGLE_TABLE));
+    }
+}
+
+TEST_F(BinderTest, TestIdletimerAddRemoveInterface) {
+    // TODO: We will get error in if expectIdletimerInterfaceRuleNotExists if there are the same
+    // rule in the table. Because we only check the result after calling remove function. We might
+    // check the actual rule which is removed by our function (maybe compare the results between
+    // calling function before and after)
+    binder::Status status;
+    const struct TestData {
+        const std::string ifname;
+        int32_t timeout;
+        const std::string classLabel;
+    } idleTestData[] = {
+            {"wlan0", 1234, "happyday"},
+            {"rmnet_data0", 4567, "friday"},
+    };
+    for (const auto& td : idleTestData) {
+        status = mNetd->idletimerAddInterface(td.ifname, td.timeout, td.classLabel);
+        EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+        expectIdletimerInterfaceRuleExists(td.ifname, td.timeout, td.classLabel);
+
+        status = mNetd->idletimerRemoveInterface(td.ifname, td.timeout, td.classLabel);
+        EXPECT_TRUE(status.isOk()) << status.exceptionMessage();
+        expectIdletimerInterfaceRuleNotExists(td.ifname, td.timeout, td.classLabel);
+    }
+}
+
+}  // namespace
\ No newline at end of file