diff options
-rw-r--r-- | cmds/dumpstate/DumpstateUtil.cpp | 17 | ||||
-rw-r--r-- | cmds/dumpstate/DumpstateUtil.h | 19 | ||||
-rw-r--r-- | cmds/dumpstate/README.md | 6 | ||||
-rw-r--r-- | cmds/dumpstate/tests/dumpstate_test.cpp | 56 |
4 files changed, 93 insertions, 5 deletions
diff --git a/cmds/dumpstate/DumpstateUtil.cpp b/cmds/dumpstate/DumpstateUtil.cpp index 600a500e86..97c8ae2045 100644 --- a/cmds/dumpstate/DumpstateUtil.cpp +++ b/cmds/dumpstate/DumpstateUtil.cpp @@ -101,13 +101,16 @@ CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Al } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRoot() { - values.account_mode_ = SU_ROOT; + if (!PropertiesHelper::IsUnroot()) { + values.account_mode_ = SU_ROOT; + } return *this; } CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::AsRootIfAvailable() { - if (!PropertiesHelper::IsUserBuild()) - values.account_mode_ = SU_ROOT; + if (!PropertiesHelper::IsUserBuild()) { + return AsRoot(); + } return *this; } @@ -176,6 +179,7 @@ CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeoutInMs(int64_t ti std::string PropertiesHelper::build_type_ = ""; int PropertiesHelper::dry_run_ = -1; +int PropertiesHelper::unroot_ = -1; bool PropertiesHelper::IsUserBuild() { if (build_type_.empty()) { @@ -191,6 +195,13 @@ bool PropertiesHelper::IsDryRun() { return dry_run_ == 1; } +bool PropertiesHelper::IsUnroot() { + if (unroot_ == -1) { + unroot_ = android::base::GetBoolProperty("dumpstate.unroot", false) ? 1 : 0; + } + return unroot_ == 1; +} + int DumpFileToFd(int out_fd, const std::string& title, const std::string& path) { android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC))); if (fd.get() < 0) { diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h index 8342099821..d69ffbfc98 100644 --- a/cmds/dumpstate/DumpstateUtil.h +++ b/cmds/dumpstate/DumpstateUtil.h @@ -97,9 +97,16 @@ class CommandOptions { public: /* Sets the command to always run, even on `dry-run` mode. */ CommandOptionsBuilder& Always(); - /* Sets the command's PrivilegeMode as `SU_ROOT` */ + /* + * Sets the command's PrivilegeMode as `SU_ROOT` unless overridden by system property + * 'dumpstate.unroot'. + */ CommandOptionsBuilder& AsRoot(); - /* If !IsUserBuild(), sets the command's PrivilegeMode as `SU_ROOT` */ + /* + * Runs AsRoot() on userdebug builds. No-op on user builds since 'su' is + * not available. This is used for commands that return some useful information even + * when run as shell. + */ CommandOptionsBuilder& AsRootIfAvailable(); /* Sets the command's PrivilegeMode as `DROP_ROOT` */ CommandOptionsBuilder& DropRoot(); @@ -162,9 +169,17 @@ class PropertiesHelper { */ static bool IsDryRun(); + /** + * Checks whether root availability should be overridden. + * + * Useful to verify how dumpstate would work in a device with an user build. + */ + static bool IsUnroot(); + private: static std::string build_type_; static int dry_run_; + static int unroot_; }; /* diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md index 0302ea5f7d..6e07b3164b 100644 --- a/cmds/dumpstate/README.md +++ b/cmds/dumpstate/README.md @@ -52,6 +52,12 @@ mmm -j frameworks/native/cmds/dumpstate/ && adb push ${OUT}/data/nativetest/dump adb shell setprop dumpstate.dry_run true ``` +## To emulate a device with user build + +``` +adb shell setprop dumpstate.unroot true +``` + ## To change the `dumpstate` version ``` diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp index b675c515fe..2cb980002a 100644 --- a/cmds/dumpstate/tests/dumpstate_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_test.cpp @@ -85,6 +85,10 @@ class DumpstateBaseTest : public Test { PropertiesHelper::build_type_ = build_type; } + void SetUnroot(bool unroot) const { + PropertiesHelper::unroot_ = unroot; + } + bool IsStandalone() const { return calls_ == 1; } @@ -650,6 +654,32 @@ TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild) { EXPECT_THAT(err, StrEq("stderr\n")); } +TEST_F(DumpstateTest, RunCommandAsRootNonUserBuild_withUnroot) { + if (!IsStandalone()) { + // TODO: temporarily disabled because it might cause other tests to fail after dropping + // to Shell - need to refactor tests to avoid this problem) + MYLOGE( + "Skipping DumpstateTest.RunCommandAsRootNonUserBuild_withUnroot() " + "on test suite\n") + return; + } + if (PropertiesHelper::IsUserBuild()) { + ALOGI("Skipping RunCommandAsRootNonUserBuild_withUnroot on user builds\n"); + return; + } + + // Same test as above, but with unroot property set, which will override su availability. + SetUnroot(true); + DropRoot(); + + EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, + CommandOptions::WithTimeout(1).AsRoot().Build())); + + // AsRoot is ineffective. + EXPECT_THAT(out, StrEq("2000\nstdout\n")); + EXPECT_THAT(err, StrEq("drop_root_user(): already running as Shell\nstderr\n")); +} + TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnUserBuild) { if (!IsStandalone()) { // TODO: temporarily disabled because it might cause other tests to fail after dropping @@ -692,6 +722,32 @@ TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild) { EXPECT_THAT(err, StrEq("stderr\n")); } +TEST_F(DumpstateTest, RunCommandAsRootIfAvailableOnDebugBuild_withUnroot) { + if (!IsStandalone()) { + // TODO: temporarily disabled because it might cause other tests to fail after dropping + // to Shell - need to refactor tests to avoid this problem) + MYLOGE( + "Skipping DumpstateTest.RunCommandAsRootIfAvailableOnDebugBuild_withUnroot() " + "on test suite\n") + return; + } + if (PropertiesHelper::IsUserBuild()) { + ALOGI("Skipping RunCommandAsRootIfAvailableOnDebugBuild_withUnroot on user builds\n"); + return; + } + // Same test as above, but with unroot property set, which will override su availability. + SetUnroot(true); + + DropRoot(); + + EXPECT_EQ(0, RunCommand("", {kSimpleCommand, "--uid"}, + CommandOptions::WithTimeout(1).AsRootIfAvailable().Build())); + + // It's a userdebug build, so "su root" should be available, but unroot=true overrides it. + EXPECT_THAT(out, StrEq("2000\nstdout\n")); + EXPECT_THAT(err, StrEq("stderr\n")); +} + TEST_F(DumpstateTest, DumpFileNotFoundNoTitle) { EXPECT_EQ(-1, DumpFile("", "/I/cant/believe/I/exist")); EXPECT_THAT(out, |