From dedd271d9961dbe8b99ffa7d54ffd63ac326f866 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Tue, 22 Mar 2016 12:36:29 +0900 Subject: [PATCH] Add a binder RPC to enable/disable data saver. Bug: 26685616 Bug: 27506285 Change-Id: Id11ee717cfc1c79070b6bbec397986c25947646c --- server/BandwidthController.h | 3 + server/CommandListener.cpp | 2 +- server/NetdNativeService.cpp | 8 +++ server/NetdNativeService.h | 1 + server/binder/android/net/INetd.aidl | 18 ++++++ tests/binder_test.cpp | 122 +++++++++++++++++++++++++++-------- 6 files changed, 125 insertions(+), 29 deletions(-) diff --git a/server/BandwidthController.h b/server/BandwidthController.h index f62cb1b..9d59a6c 100644 --- a/server/BandwidthController.h +++ b/server/BandwidthController.h @@ -21,9 +21,12 @@ #include // for pair #include +#include class BandwidthController { public: + android::RWLock lock; + class TetherStats { public: TetherStats(void) diff --git a/server/CommandListener.cpp b/server/CommandListener.cpp index 968b415..6dc8485 100644 --- a/server/CommandListener.cpp +++ b/server/CommandListener.cpp @@ -190,7 +190,7 @@ CommandListener::CommandListener() : registerLockingCmd(new ListTtysCmd()); registerLockingCmd(new PppdCmd()); registerLockingCmd(new SoftapCmd()); - registerLockingCmd(new BandwidthControlCmd()); + registerLockingCmd(new BandwidthControlCmd(), gCtls->bandwidthCtrl.lock); registerLockingCmd(new IdletimerControlCmd()); registerLockingCmd(new ResolverCmd()); registerLockingCmd(new FirewallCmd(), gCtls->firewallCtrl.lock); diff --git a/server/NetdNativeService.cpp b/server/NetdNativeService.cpp index d39bdfa..97b41b2 100644 --- a/server/NetdNativeService.cpp +++ b/server/NetdNativeService.cpp @@ -114,7 +114,15 @@ binder::Status NetdNativeService::firewallReplaceUidChain(const android::String1 int err = gCtls->firewallCtrl.replaceUidChain(name.string(), isWhitelist, uids); *ret = (err == 0); return binder::Status::ok(); +} + +binder::Status NetdNativeService::bandwidthEnableDataSaver(bool enable, bool *ret) { + NETD_LOCKING_RPC(CONNECTIVITY_INTERNAL, gCtls->bandwidthCtrl.lock); + int err = gCtls->bandwidthCtrl.enableDataSaver(enable); + *ret = (err == 0); + return binder::Status::ok(); } + } // namespace net } // namespace android diff --git a/server/NetdNativeService.h b/server/NetdNativeService.h index b24e96e..f7409d8 100644 --- a/server/NetdNativeService.h +++ b/server/NetdNativeService.h @@ -36,6 +36,7 @@ class NetdNativeService : public BinderService, public BnNetd binder::Status firewallReplaceUidChain( const String16& chainName, bool isWhitelist, const std::vector& uids, bool *ret) override; + binder::Status bandwidthEnableDataSaver(bool enable, bool *ret) override; }; diff --git a/server/binder/android/net/INetd.aidl b/server/binder/android/net/INetd.aidl index b54c7fa..cca5b58 100644 --- a/server/binder/android/net/INetd.aidl +++ b/server/binder/android/net/INetd.aidl @@ -37,4 +37,22 @@ interface INetd { * @return true if the chain was successfully replaced, false otherwise. */ boolean firewallReplaceUidChain(String chainName, boolean isWhitelist, in int[] uids); + + /** + * Enables or disables data saver mode on costly network interfaces. + * + * - When disabled, all packets to/from apps in the penalty box chain are rejected on costly + * interfaces. Traffic to/from other apps or on other network interfaces is allowed. + * - When enabled, only apps that are in the happy box chain and not in the penalty box chain + * are allowed network connectivity on costly interfaces. All other packets on these + * interfaces are rejected. The happy box chain always contains all system UIDs; to disallow + * traffic from system UIDs, place them in the penalty box chain. + * + * By default, data saver mode is disabled. This command has no effect but might still return an + * error) if {@code enable} is the same as the current value. + * + * @param enable whether to enable or disable data saver mode. + * @return true if the if the operation was successful, false otherwise. + */ + boolean bandwidthEnableDataSaver(boolean enable); } diff --git a/tests/binder_test.cpp b/tests/binder_test.cpp index 737656b..ae743a9 100644 --- a/tests/binder_test.cpp +++ b/tests/binder_test.cpp @@ -16,11 +16,13 @@ * binder_test.cpp - unit tests for netd binder RPCs. */ -#include #include +#include +#include #include #include +#include #include #include @@ -78,30 +80,30 @@ static int randomUid() { return 100000 * arc4random_uniform(7) + 10000 + arc4random_uniform(5000); } -static int countNewlines(FILE *f) { - char buf[4096]; - int numNewlines = 0; - size_t bytesread; - while ((bytesread = fread(buf, 1, sizeof(buf), f)) > 0) { - for (size_t i = 0; i < bytesread; i++) { - if (buf[i] == '\n') { - numNewlines++; - } - } - } - return numNewlines; -} - -static int ruleLineLength(const char *binary, const char *chainName) { +static std::vector listIptablesRule(const char *binary, const char *chainName) { + std::vector lines; FILE *f; + std::string command = StringPrintf("%s -n -L %s", binary, chainName); - if ((f = popen(command.c_str(), "r")) == NULL) { + if ((f = popen(command.c_str(), "r")) == nullptr) { perror("popen"); - return -1; + return lines; } - int numLines = countNewlines(f); + + char *line = nullptr; + size_t linelen = 0; + while (getline(&line, &linelen, f) >= 0) { + lines.push_back(std::string(line, linelen)); + free(line); + line = nullptr; + } + pclose(f); - return numLines; + return lines; +} + +static int iptablesRuleLineLength(const char *binary, const char *chainName) { + return listIptablesRule(binary, chainName).size(); } @@ -120,34 +122,98 @@ TEST_F(BinderTest, TestFirewallReplaceUidChain) { mNetd->firewallReplaceUidChain(String16(chainName.c_str()), true, uids, &ret); } EXPECT_EQ(true, ret); - EXPECT_EQ((int) uids.size() + 4, ruleLineLength(IPTABLES_PATH, chainName.c_str())); - EXPECT_EQ((int) uids.size() + 4, ruleLineLength(IP6TABLES_PATH, chainName.c_str())); + EXPECT_EQ((int) uids.size() + 4, iptablesRuleLineLength(IPTABLES_PATH, chainName.c_str())); + EXPECT_EQ((int) uids.size() + 4, iptablesRuleLineLength(IP6TABLES_PATH, chainName.c_str())); { TimedOperation op("Clearing whitelist chain"); mNetd->firewallReplaceUidChain(String16(chainName.c_str()), false, noUids, &ret); } EXPECT_EQ(true, ret); - EXPECT_EQ(2, ruleLineLength(IPTABLES_PATH, chainName.c_str())); - EXPECT_EQ(2, ruleLineLength(IP6TABLES_PATH, chainName.c_str())); + EXPECT_EQ(2, iptablesRuleLineLength(IPTABLES_PATH, chainName.c_str())); + EXPECT_EQ(2, iptablesRuleLineLength(IP6TABLES_PATH, chainName.c_str())); { TimedOperation op(StringPrintf("Programming %d-UID blacklist chain", kNumUids)); mNetd->firewallReplaceUidChain(String16(chainName.c_str()), false, uids, &ret); } EXPECT_EQ(true, ret); - EXPECT_EQ((int) uids.size() + 3, ruleLineLength(IPTABLES_PATH, chainName.c_str())); - EXPECT_EQ((int) uids.size() + 3, ruleLineLength(IP6TABLES_PATH, chainName.c_str())); + EXPECT_EQ((int) uids.size() + 3, iptablesRuleLineLength(IPTABLES_PATH, chainName.c_str())); + EXPECT_EQ((int) uids.size() + 3, iptablesRuleLineLength(IP6TABLES_PATH, chainName.c_str())); { TimedOperation op("Clearing blacklist chain"); mNetd->firewallReplaceUidChain(String16(chainName.c_str()), false, noUids, &ret); } EXPECT_EQ(true, ret); - EXPECT_EQ(2, ruleLineLength(IPTABLES_PATH, chainName.c_str())); - EXPECT_EQ(2, ruleLineLength(IP6TABLES_PATH, chainName.c_str())); + EXPECT_EQ(2, iptablesRuleLineLength(IPTABLES_PATH, chainName.c_str())); + EXPECT_EQ(2, iptablesRuleLineLength(IP6TABLES_PATH, chainName.c_str())); // Check that the call fails if iptables returns an error. std::string veryLongStringName = "netd_binder_test_UnacceptablyLongIptablesChainName"; mNetd->firewallReplaceUidChain(String16(veryLongStringName.c_str()), true, noUids, &ret); EXPECT_EQ(false, ret); } + +static int bandwidthDataSaverEnabled(const char *binary) { + std::vector lines = listIptablesRule(binary, "bw_costly_shared"); + + // Output looks like this: + // + // Chain bw_costly_shared (0 references) + // target prot opt source destination + // bw_penalty_box all -- 0.0.0.0/0 0.0.0.0/0 + // bw_happy_box all -- 0.0.0.0/0 0.0.0.0/0 + // RETURN all -- 0.0.0.0/0 0.0.0.0/0 + EXPECT_EQ(5U, lines.size()); + if (lines.size() != 5) return -1; + + EXPECT_TRUE(android::base::StartsWith(lines[2], "bw_penalty_box ")); + EXPECT_TRUE(android::base::StartsWith(lines[3], "bw_happy_box ")); + EXPECT_TRUE(android::base::StartsWith(lines[4], "RETURN ") || + android::base::StartsWith(lines[4], "REJECT ")); + + return android::base::StartsWith(lines[4], "REJECT"); +} + +bool enableDataSaver(sp& netd, bool enable) { + TimedOperation op(enable ? " Enabling data saver" : "Disabling data saver"); + bool ret; + netd->bandwidthEnableDataSaver(enable, &ret); + return ret; +} + +int getDataSaverState() { + const int enabled4 = bandwidthDataSaverEnabled(IPTABLES_PATH); + const int enabled6 = bandwidthDataSaverEnabled(IP6TABLES_PATH); + EXPECT_EQ(enabled4, enabled6); + EXPECT_NE(-1, enabled4); + EXPECT_NE(-1, enabled6); + if (enabled4 != enabled6 || (enabled6 != 0 && enabled6 != 1)) { + return -1; + } + return enabled6; +} + +TEST_F(BinderTest, TestBandwidthEnableDataSaver) { + const int wasEnabled = getDataSaverState(); + ASSERT_NE(-1, wasEnabled); + + if (wasEnabled) { + ASSERT_TRUE(enableDataSaver(mNetd, false)); + EXPECT_EQ(0, getDataSaverState()); + } + + ASSERT_TRUE(enableDataSaver(mNetd, false)); + EXPECT_EQ(0, getDataSaverState()); + + ASSERT_TRUE(enableDataSaver(mNetd, true)); + EXPECT_EQ(1, getDataSaverState()); + + ASSERT_TRUE(enableDataSaver(mNetd, true)); + EXPECT_EQ(1, getDataSaverState()); + + if (!wasEnabled) { + ASSERT_TRUE(enableDataSaver(mNetd, false)); + EXPECT_EQ(0, getDataSaverState()); + } +} -- 2.11.0