From 3d57c84476ce542a7d8d623cf1f208efc1a20026 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Sat, 9 Nov 2019 22:07:20 -0800 Subject: Consolidate the vendor space misc usage for Pixels The layout of the vendor space /misc partition was pretty confusing and lead to some usage conflicts. To formalize the layout, we create a pixel specific library with the definition & offset of various flags. The new library also handles the R/W. As a result, we will leave system domain /misc definitions in the libbootloader_message. We also switch the misc_writer binary to use more specific options instead of writing an arbitrary hex string. So we can avoid redefining the string & offset in both init script and recovery ui. Bug: 131775112 Test: unit tests pass, run misc_writer and check contents of /misc Change-Id: I00f8842a81d1929e31a1de4d5eb09575ffad47c0 --- misc_writer/Android.bp | 80 ++++++++++++++- misc_writer/include/misc_writer/misc_writer.h | 66 ++++++++++++ misc_writer/misc_writer.cpp | 127 ++++++++++------------- misc_writer/misc_writer_main.cpp | 109 ++++++++++++++++++++ misc_writer/misc_writer_test.cpp | 140 ++++++++++++++++++++++++++ 5 files changed, 442 insertions(+), 80 deletions(-) create mode 100644 misc_writer/include/misc_writer/misc_writer.h create mode 100644 misc_writer/misc_writer_main.cpp create mode 100644 misc_writer/misc_writer_test.cpp (limited to 'misc_writer') diff --git a/misc_writer/Android.bp b/misc_writer/Android.bp index 567143c79..73c44d2eb 100644 --- a/misc_writer/Android.bp +++ b/misc_writer/Android.bp @@ -14,14 +14,30 @@ // limitations under the License. // -cc_binary { - name: "misc_writer", +cc_defaults { + name: "misc_writer_defaults", vendor: true, + cpp_std: "experimental", - srcs: [ - "misc_writer.cpp", + cflags: [ + "-Wall", + "-Werror", + ], + + shared_libs: [ + "libbase", + ], + + static_libs: [ + "libbootloader_message_vendor", + "libfstab", ], +} +// TODO(xunchang) Remove duplicates after we convert the device specific librecovery_ui to recovery +// module. Then libmisc_writer can build as a vendor module available in recovery. +cc_library_static { + name: "libmisc_writer", cpp_std: "experimental", cflags: [ @@ -34,7 +50,61 @@ cc_binary { ], static_libs: [ - "libbootloader_message_vendor", + "libbootloader_message", "libfstab", ], + + srcs: [ + "misc_writer.cpp", + ], + + export_include_dirs: [ + "include", + ], +} + +cc_library_static { + name: "libmisc_writer_vendor", + defaults: [ + "misc_writer_defaults", + ], + + srcs: [ + "misc_writer.cpp", + ], + + export_include_dirs: [ + "include", + ], +} + +cc_binary { + name: "misc_writer", + defaults: [ + "misc_writer_defaults", + ], + + srcs: [ + "misc_writer_main.cpp", + ], + + static_libs: [ + "libmisc_writer_vendor", + ] +} + +cc_test { + name: "misc_writer_test", + defaults: [ + "misc_writer_defaults", + ], + + srcs: [ + "misc_writer_test.cpp", + ], + test_suites: ["device-tests"], + + static_libs: [ + "libmisc_writer_vendor", + ] } diff --git a/misc_writer/include/misc_writer/misc_writer.h b/misc_writer/include/misc_writer/misc_writer.h new file mode 100644 index 000000000..6a32ffe46 --- /dev/null +++ b/misc_writer/include/misc_writer/misc_writer.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +enum class MiscWriterActions : int32_t { + kSetDarkThemeFlag = 0, + kClearDarkThemeFlag, + kSetSotaFlag, + kClearSotaFlag, + + kUnset = -1, +}; + +class MiscWriter { + public: + static constexpr uint32_t kThemeFlagOffsetInVendorSpace = 0; + static constexpr char kDarkThemeFlag[] = "theme-dark"; + static constexpr uint32_t kSotaFlagOffsetInVendorSpace = 32; + static constexpr char kSotaFlag[] = "enable-sota"; + + // Returns true of |size| bytes data starting from |offset| is fully inside the vendor space. + static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size); + // Writes the given data to the vendor space in /misc partition, at the given offset. Note that + // offset is in relative to the start of the vendor space. + static bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, + std::string* err); + + explicit MiscWriter(const MiscWriterActions& action) : action_(action) {} + + // Performs the stored MiscWriterActions. If |override_offset| is set, writes to the input offset + // in the vendor space of /misc instead of the default offset. + bool PerformAction(std::optional override_offset = std::nullopt); + + private: + MiscWriterActions action_{ MiscWriterActions::kUnset }; +}; + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android diff --git a/misc_writer/misc_writer.cpp b/misc_writer/misc_writer.cpp index 1d9702ebf..bf589d31f 100644 --- a/misc_writer/misc_writer.cpp +++ b/misc_writer/misc_writer.cpp @@ -14,93 +14,70 @@ * limitations under the License. */ -#include -#include -#include -#include -#include +#include "misc_writer/misc_writer.h" -#include -#include -#include -#include +#include +#include #include -#include +#include #include -using namespace std::string_literals; +namespace android { +namespace hardware { +namespace google { +namespace pixel { -static std::vector ParseHexString(std::string_view hex_string) { - auto length = hex_string.size(); - if (length % 2 != 0 || length == 0) { - return {}; - } - - std::vector result(length / 2); - for (size_t i = 0; i < length / 2; i++) { - auto sub = "0x" + std::string(hex_string.substr(i * 2, 2)); - if (!android::base::ParseUint(sub, &result[i])) { - return {}; - } - } - return result; +bool MiscWriter::OffsetAndSizeInVendorSpace(size_t offset, size_t size) { + auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC; + return size <= total_size && offset <= total_size - size; } -static int Usage(std::string_view name) { - std::cerr << name << " usage:\n"; - std::cerr << name << " [--vendor-space-offset ] --hex-string 0xABCDEF\n"; - std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc " - "partition. Offset defaults to 0 if unspecified.\n"; - return EXIT_FAILURE; +bool MiscWriter::WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, + std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); } -// misc_writer is a vendor tool that writes data to the vendor space in /misc. -int main(int argc, char** argv) { - constexpr struct option OPTIONS[] = { - { "vendor-space-offset", required_argument, nullptr, 0 }, - { "hex-string", required_argument, nullptr, 0 }, - { nullptr, 0, nullptr, 0 }, - }; - - // Offset defaults to 0 if unspecified. +bool MiscWriter::PerformAction(std::optional override_offset) { size_t offset = 0; - std::string_view hex_string; - - int arg; - int option_index; - while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { - if (arg != 0) { - LOG(ERROR) << "Invalid command argument"; - return Usage(argv[0]); - } - auto option_name = OPTIONS[option_index].name; - if (option_name == "vendor-space-offset"s) { - if (!android::base::ParseUint(optarg, &offset)) { - LOG(ERROR) << "Failed to parse the offset: " << optarg; - return Usage(argv[0]); - } - } else if (option_name == "hex-string"s) { - hex_string = optarg; - } - } - - if (hex_string.starts_with("0x") || hex_string.starts_with("0X")) { - hex_string = hex_string.substr(2); - } - if (hex_string.empty()) { - LOG(ERROR) << "Invalid input hex string: " << hex_string; - return Usage(argv[0]); + std::string content; + switch (action_) { + case MiscWriterActions::kSetDarkThemeFlag: + case MiscWriterActions::kClearDarkThemeFlag: + offset = override_offset.value_or(kThemeFlagOffsetInVendorSpace); + content = (action_ == MiscWriterActions::kSetDarkThemeFlag) + ? kDarkThemeFlag + : std::string(strlen(kDarkThemeFlag), 0); + break; + case MiscWriterActions::kSetSotaFlag: + case MiscWriterActions::kClearSotaFlag: + offset = override_offset.value_or(kSotaFlagOffsetInVendorSpace); + content = (action_ == MiscWriterActions::kSetSotaFlag) ? kSotaFlag + : std::string(strlen(kSotaFlag), 0); + break; + case MiscWriterActions::kUnset: + LOG(ERROR) << "The misc writer action must be set"; + return false; } - auto data = ParseHexString(hex_string); - if (data.empty()) { - LOG(ERROR) << "Failed to parse the input hex string: " << hex_string; - return EXIT_FAILURE; - } - if (std::string err; !WriteMiscPartitionVendorSpace(data.data(), data.size(), offset, &err)) { - LOG(ERROR) << "Failed to write to misc partition: " << err; - return EXIT_FAILURE; + if (std::string err; + !WriteMiscPartitionVendorSpace(content.data(), content.size(), offset, &err)) { + LOG(ERROR) << "Failed to write " << content << " at offset " << offset << " : " << err; + return false; } - return EXIT_SUCCESS; + return true; } + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android diff --git a/misc_writer/misc_writer_main.cpp b/misc_writer/misc_writer_main.cpp new file mode 100644 index 000000000..69a9fe3df --- /dev/null +++ b/misc_writer/misc_writer_main.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "misc_writer/misc_writer.h" + +using namespace std::string_literals; +using android::hardware::google::pixel::MiscWriter; +using android::hardware::google::pixel::MiscWriterActions; + +static int Usage(std::string_view name) { + std::cerr << name << " usage:\n"; + std::cerr << name << " [--override-vendor-space-offset ] --\n"; + std::cerr << "Supported misc_writer_action is one of: \n"; + std::cerr << " --set-dark-theme Write the dark theme flag\n"; + std::cerr << " --clear-dark-theme Clear the dark theme flag\n"; + std::cerr << " --set-sota Write the silent OTA flag\n"; + std::cerr << " --clear-sota Clear the silent OTA flag\n"; + std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc " + "partition.\nDefault offset is used for each action unless " + "--override-vendor-space-offset is specified.\n"; + return EXIT_FAILURE; +} + +// misc_writer is a vendor tool that writes data to the vendor space in /misc. +int main(int argc, char** argv) { + constexpr struct option OPTIONS[] = { + { "set-dark-theme", no_argument, nullptr, 0 }, + { "clear-dark-theme", no_argument, nullptr, 0 }, + { "set-sota", no_argument, nullptr, 0 }, + { "clear-sota", no_argument, nullptr, 0 }, + { "override-vendor-space-offset", required_argument, nullptr, 0 }, + { nullptr, 0, nullptr, 0 }, + }; + + std::map action_map{ + { "set-dark-theme", MiscWriterActions::kSetDarkThemeFlag }, + { "clear-dark-theme", MiscWriterActions::kClearDarkThemeFlag }, + { "set-sota", MiscWriterActions::kSetSotaFlag }, + { "clear-sota", MiscWriterActions::kClearSotaFlag }, + }; + + std::unique_ptr misc_writer; + std::optional override_offset; + + int arg; + int option_index = 0; + while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { + if (arg != 0) { + LOG(ERROR) << "Invalid command argument"; + return Usage(argv[0]); + } + auto option_name = OPTIONS[option_index].name; + if (option_name == "override-vendor-space-offset"s) { + LOG(WARNING) << "Overriding the vendor space offset in misc partition to " << optarg; + size_t offset; + if (!android::base::ParseUint(optarg, &offset)) { + LOG(ERROR) << "Failed to parse the offset: " << optarg; + return Usage(argv[0]); + } + override_offset = offset; + } else if (auto iter = action_map.find(option_name); iter != action_map.end()) { + if (misc_writer) { + LOG(ERROR) << "Misc writer action has already been set"; + return Usage(argv[0]); + } + misc_writer = std::make_unique(iter->second); + } else { + LOG(FATAL) << "Unreachable path, option_name: " << option_name; + } + } + + if (!misc_writer) { + LOG(ERROR) << "An action must be specified for misc writer"; + return Usage(argv[0]); + } + + if (!misc_writer->PerformAction(override_offset)) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} diff --git a/misc_writer/misc_writer_test.cpp b/misc_writer/misc_writer_test.cpp new file mode 100644 index 000000000..e8b207afd --- /dev/null +++ b/misc_writer/misc_writer_test.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "misc_writer/misc_writer.h" + +using namespace std::string_literals; + +namespace android { +namespace hardware { +namespace google { +namespace pixel { + +class MiscWriterTest : public ::testing::Test { + protected: + void TearDown() override { + // Clear the vendor space. + auto zeros = std::string(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC, 0); + std::string err; + ASSERT_TRUE(MiscWriter::WriteMiscPartitionVendorSpace(zeros.data(), zeros.size(), 0, &err)) + << err; + } + + void CheckMiscPartitionVendorSpaceContent(size_t offset, const std::string& expected); + + std::unique_ptr misc_writer_; +}; + +void MiscWriterTest::CheckMiscPartitionVendorSpaceContent(size_t offset, + const std::string& expected) { + ASSERT_TRUE(MiscWriter::OffsetAndSizeInVendorSpace(offset, expected.size())); + std::string err; + auto misc_blk_device = get_misc_blk_device(&err); + ASSERT_FALSE(misc_blk_device.empty()); + android::base::unique_fd fd(open(misc_blk_device.c_str(), O_RDONLY)); + ASSERT_NE(-1, fd); + + std::string content(expected.size(), 0); + ASSERT_TRUE(android::base::ReadFullyAtOffset(fd, content.data(), content.size(), + VENDOR_SPACE_OFFSET_IN_MISC + offset)); + ASSERT_EQ(expected, content); +} + +TEST_F(MiscWriterTest, SetClearDarkTheme) { + misc_writer_ = std::make_unique(MiscWriterActions::kSetDarkThemeFlag); + ASSERT_TRUE(misc_writer_); + ASSERT_TRUE(misc_writer_->PerformAction()); + std::string expected = "theme-dark"; + CheckMiscPartitionVendorSpaceContent(0, expected); + + misc_writer_ = std::make_unique(MiscWriterActions::kClearDarkThemeFlag); + ASSERT_TRUE(misc_writer_->PerformAction()); + std::string zeros(expected.size(), 0); + CheckMiscPartitionVendorSpaceContent(0, zeros); +} + +TEST_F(MiscWriterTest, SetClearDarkTheme_OffsetOverride) { + misc_writer_ = std::make_unique(MiscWriterActions::kSetDarkThemeFlag); + size_t offset = 12360; + ASSERT_TRUE(misc_writer_->PerformAction(offset)); + std::string expected = "theme-dark"; + CheckMiscPartitionVendorSpaceContent(offset, expected); + + misc_writer_ = std::make_unique(MiscWriterActions::kClearDarkThemeFlag); + ASSERT_TRUE(misc_writer_->PerformAction(offset)); + std::string zeros(expected.size(), 0); + CheckMiscPartitionVendorSpaceContent(offset, zeros); +} + +TEST_F(MiscWriterTest, SetClearSota) { + misc_writer_ = std::make_unique(MiscWriterActions::kSetSotaFlag); + ASSERT_TRUE(misc_writer_); + ASSERT_TRUE(misc_writer_->PerformAction()); + std::string expected = "enable-sota"; + CheckMiscPartitionVendorSpaceContent(32, expected); + + // Test we can write to the override offset. + size_t override_offset = 12360; + ASSERT_FALSE(misc_writer_->PerformAction(override_offset)); + CheckMiscPartitionVendorSpaceContent(override_offset, expected); + + misc_writer_ = std::make_unique(MiscWriterActions::kClearSotaFlag); + ASSERT_TRUE(misc_writer_->PerformAction()); + std::string zeros(expected.size(), 0); + CheckMiscPartitionVendorSpaceContent(32, zeros); +} + +TEST_F(MiscWriterTest, WriteMiscPartitionVendorSpace) { + std::string kTestMessage = "kTestMessage"; + std::string err; + ASSERT_TRUE( + MiscWriter::WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), 0, &err)); + + CheckMiscPartitionVendorSpaceContent(0, kTestMessage); + + // Write with an offset. + ASSERT_TRUE(MiscWriter::WriteMiscPartitionVendorSpace("\x00\x00", 2, 5, &err)); + CheckMiscPartitionVendorSpaceContent(0, "kTest\x00\x00ssage"s); + + // Write with the right size. + auto start_offset = + WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC - kTestMessage.size(); + ASSERT_TRUE(MiscWriter::WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), + start_offset, &err)); + + // Out-of-bound write. + ASSERT_FALSE(MiscWriter::WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), + start_offset + 1, &err)); + + // Message won't fit. + std::string long_message(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC + 1, 'a'); + ASSERT_FALSE( + MiscWriter::WriteMiscPartitionVendorSpace(long_message.data(), long_message.size(), 0, &err)); +} + +} // namespace pixel +} // namespace google +} // namespace hardware +} // namespace android -- cgit v1.2.3