From 2078b22e4145fef2648cc714eae6588353940c4b Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 22 Mar 2017 12:27:26 -0700 Subject: Add the missing sr-Latn into png files and rename the png locale header Switch the locale header in the png files from Locale.toString() to Locale.toLanguageTag(). For example, en_US --> en-us and sr__#Latn --> sr-Latn. Also clean up recovery a bit to expect the new locale format. Bug: 35215015 Test: sr-Latn shows correctly under graphic tests && recovery tests pass Change-Id: Ic62bab7756cdc6e5f98f26076f7c2dd046f811db --- minui/Android.mk | 10 +++++-- minui/include/minui/minui.h | 3 ++- minui/resources.cpp | 33 +++++++++++++++++------- recovery.cpp | 2 +- res-hdpi/images/erasing_text.png | Bin 50680 -> 50184 bytes res-hdpi/images/error_text.png | Bin 36036 -> 35856 bytes res-hdpi/images/installing_security_text.png | Bin 113819 -> 113078 bytes res-hdpi/images/installing_text.png | Bin 104768 -> 104002 bytes res-hdpi/images/no_command_text.png | Bin 62037 -> 61417 bytes res-mdpi/images/erasing_text.png | Bin 29898 -> 29660 bytes res-mdpi/images/error_text.png | Bin 21147 -> 21123 bytes res-mdpi/images/installing_security_text.png | Bin 70010 -> 69603 bytes res-mdpi/images/installing_text.png | Bin 61701 -> 61281 bytes res-mdpi/images/no_command_text.png | Bin 34683 -> 34366 bytes res-xhdpi/images/erasing_text.png | Bin 73361 -> 73097 bytes res-xhdpi/images/error_text.png | Bin 52019 -> 52180 bytes res-xhdpi/images/installing_security_text.png | Bin 196871 -> 197146 bytes res-xhdpi/images/installing_text.png | Bin 175570 -> 175660 bytes res-xhdpi/images/no_command_text.png | Bin 86622 -> 86619 bytes res-xxhdpi/images/erasing_text.png | Bin 121608 -> 121637 bytes res-xxhdpi/images/error_text.png | Bin 84727 -> 84961 bytes res-xxhdpi/images/installing_security_text.png | Bin 447158 -> 447228 bytes res-xxhdpi/images/installing_text.png | Bin 415889 -> 416000 bytes res-xxhdpi/images/no_command_text.png | Bin 243028 -> 243226 bytes res-xxxhdpi/images/erasing_text.png | Bin 263768 -> 263646 bytes res-xxxhdpi/images/error_text.png | Bin 176936 -> 178458 bytes res-xxxhdpi/images/installing_security_text.png | Bin 610107 -> 610223 bytes res-xxxhdpi/images/installing_text.png | Bin 567834 -> 567987 bytes res-xxxhdpi/images/no_command_text.png | Bin 331159 -> 331473 bytes tests/unit/locale_test.cpp | 21 ++++++++------- 30 files changed, 45 insertions(+), 24 deletions(-) diff --git a/minui/Android.mk b/minui/Android.mk index 281f64912..4dfc65f8a 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -28,7 +28,10 @@ LOCAL_WHOLE_STATIC_LIBRARIES := \ libdrm \ libsync_recovery -LOCAL_STATIC_LIBRARIES := libpng +LOCAL_STATIC_LIBRARIES := \ + libpng \ + libbase + LOCAL_CFLAGS := -Werror LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include @@ -61,7 +64,10 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := libminui LOCAL_WHOLE_STATIC_LIBRARIES += libminui -LOCAL_SHARED_LIBRARIES := libpng +LOCAL_SHARED_LIBRARIES := \ + libpng \ + libbase + LOCAL_CFLAGS := -Werror LOCAL_C_INCLUDES := $(LOCAL_PATH)/include LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index a1749dfe6..78dd4cb98 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -20,6 +20,7 @@ #include #include +#include // // Graphics. @@ -93,7 +94,7 @@ int ev_get_epollfd(); // Resources // -bool matches_locale(const char* prefix, const char* locale); +bool matches_locale(const std::string& prefix, const std::string& locale); // res_create_*_surface() functions return 0 if no error, else // negative. diff --git a/minui/resources.cpp b/minui/resources.cpp index c0f9c5c85..86c731b02 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -25,8 +25,11 @@ #include #include +#include +#include #include +#include #include #include "minui/minui.h" @@ -371,16 +374,26 @@ int res_create_alpha_surface(const char* name, GRSurface** pSurface) { // This function tests if a locale string stored in PNG (prefix) matches // the locale string provided by the system (locale). -bool matches_locale(const char* prefix, const char* locale) { - if (locale == nullptr) { - return false; - } - - // Return true if the whole string of prefix matches the top part of - // locale. For instance, prefix == "en" matches locale == "en_US"; - // and prefix == "zh_CN" matches locale == "zh_CN_#Hans". - - return (strncmp(prefix, locale, strlen(prefix)) == 0); +bool matches_locale(const std::string& prefix, const std::string& locale) { + // According to the BCP 47 format, A locale string may consists of: + // language-{extlang}-{script}-{region}-{variant} + // The locale headers in PNG mostly consist of language-{region} except for sr-Latn, and some + // android's system locale can have the format language-{script}-{region}. + + // Return true if the whole string of prefix matches the top part of locale. Otherwise try to + // match the locale string without the {script} section. + // For instance, prefix == "en" matches locale == "en-US", prefix == "sr-Latn" matches locale + // == "sr-Latn-BA", and prefix == "zh-CN" matches locale == "zh-Hans-CN". + if (android::base::StartsWith(locale, prefix.c_str())) { + return true; + } + + size_t separator = prefix.find('-'); + if (separator == std::string::npos) { + return false; + } + std::regex loc_regex(prefix.substr(0, separator) + "-[A-Za-z]*" + prefix.substr(separator)); + return std::regex_match(locale, loc_regex); } int res_create_localized_alpha_surface(const char* name, diff --git a/recovery.cpp b/recovery.cpp index ccb8e5d95..a374eed8a 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -122,7 +122,7 @@ static const int BATTERY_READ_TIMEOUT_IN_SEC = 10; static const int BATTERY_OK_PERCENTAGE = 20; static const int BATTERY_WITH_CHARGER_OK_PERCENTAGE = 15; static constexpr const char* RECOVERY_WIPE = "/etc/recovery.wipe"; -static constexpr const char* DEFAULT_LOCALE = "en_US"; +static constexpr const char* DEFAULT_LOCALE = "en-US"; static std::string locale; static bool has_cache = false; diff --git a/res-hdpi/images/erasing_text.png b/res-hdpi/images/erasing_text.png index 684fc7c6c..0982544d2 100644 Binary files a/res-hdpi/images/erasing_text.png and b/res-hdpi/images/erasing_text.png differ diff --git a/res-hdpi/images/error_text.png b/res-hdpi/images/error_text.png index 00c485d70..3a06f6eb1 100644 Binary files a/res-hdpi/images/error_text.png and b/res-hdpi/images/error_text.png differ diff --git a/res-hdpi/images/installing_security_text.png b/res-hdpi/images/installing_security_text.png index dadcfc32b..b1acd2336 100644 Binary files a/res-hdpi/images/installing_security_text.png and b/res-hdpi/images/installing_security_text.png differ diff --git a/res-hdpi/images/installing_text.png b/res-hdpi/images/installing_text.png index abe73b4b9..f0f5d8b6c 100644 Binary files a/res-hdpi/images/installing_text.png and b/res-hdpi/images/installing_text.png differ diff --git a/res-hdpi/images/no_command_text.png b/res-hdpi/images/no_command_text.png index 958e10613..def503678 100644 Binary files a/res-hdpi/images/no_command_text.png and b/res-hdpi/images/no_command_text.png differ diff --git a/res-mdpi/images/erasing_text.png b/res-mdpi/images/erasing_text.png index 10e317829..82b4461ba 100644 Binary files a/res-mdpi/images/erasing_text.png and b/res-mdpi/images/erasing_text.png differ diff --git a/res-mdpi/images/error_text.png b/res-mdpi/images/error_text.png index 0022d10c1..adb45131f 100644 Binary files a/res-mdpi/images/error_text.png and b/res-mdpi/images/error_text.png differ diff --git a/res-mdpi/images/installing_security_text.png b/res-mdpi/images/installing_security_text.png index 7a4cd414b..54e556448 100644 Binary files a/res-mdpi/images/installing_security_text.png and b/res-mdpi/images/installing_security_text.png differ diff --git a/res-mdpi/images/installing_text.png b/res-mdpi/images/installing_text.png index ee95e569c..d42331820 100644 Binary files a/res-mdpi/images/installing_text.png and b/res-mdpi/images/installing_text.png differ diff --git a/res-mdpi/images/no_command_text.png b/res-mdpi/images/no_command_text.png index af7660908..cd77ff457 100644 Binary files a/res-mdpi/images/no_command_text.png and b/res-mdpi/images/no_command_text.png differ diff --git a/res-xhdpi/images/erasing_text.png b/res-xhdpi/images/erasing_text.png index 91cc35871..333edbe27 100644 Binary files a/res-xhdpi/images/erasing_text.png and b/res-xhdpi/images/erasing_text.png differ diff --git a/res-xhdpi/images/error_text.png b/res-xhdpi/images/error_text.png index 772b139e6..e26258438 100644 Binary files a/res-xhdpi/images/error_text.png and b/res-xhdpi/images/error_text.png differ diff --git a/res-xhdpi/images/installing_security_text.png b/res-xhdpi/images/installing_security_text.png index a7113a04a..e0f0f3ea7 100644 Binary files a/res-xhdpi/images/installing_security_text.png and b/res-xhdpi/images/installing_security_text.png differ diff --git a/res-xhdpi/images/installing_text.png b/res-xhdpi/images/installing_text.png index 566eb0658..a7e67f512 100644 Binary files a/res-xhdpi/images/installing_text.png and b/res-xhdpi/images/installing_text.png differ diff --git a/res-xhdpi/images/no_command_text.png b/res-xhdpi/images/no_command_text.png index b8da125cb..13aef7b71 100644 Binary files a/res-xhdpi/images/no_command_text.png and b/res-xhdpi/images/no_command_text.png differ diff --git a/res-xxhdpi/images/erasing_text.png b/res-xxhdpi/images/erasing_text.png index 86693f435..80e7c475e 100644 Binary files a/res-xxhdpi/images/erasing_text.png and b/res-xxhdpi/images/erasing_text.png differ diff --git a/res-xxhdpi/images/error_text.png b/res-xxhdpi/images/error_text.png index 9c4bcab95..32a1965b8 100644 Binary files a/res-xxhdpi/images/error_text.png and b/res-xxhdpi/images/error_text.png differ diff --git a/res-xxhdpi/images/installing_security_text.png b/res-xxhdpi/images/installing_security_text.png index f5ec698f8..c53c9ac21 100644 Binary files a/res-xxhdpi/images/installing_security_text.png and b/res-xxhdpi/images/installing_security_text.png differ diff --git a/res-xxhdpi/images/installing_text.png b/res-xxhdpi/images/installing_text.png index 100a5b303..38b18d20d 100644 Binary files a/res-xxhdpi/images/installing_text.png and b/res-xxhdpi/images/installing_text.png differ diff --git a/res-xxhdpi/images/no_command_text.png b/res-xxhdpi/images/no_command_text.png index 590030c8b..a0666d8dc 100644 Binary files a/res-xxhdpi/images/no_command_text.png and b/res-xxhdpi/images/no_command_text.png differ diff --git a/res-xxxhdpi/images/erasing_text.png b/res-xxxhdpi/images/erasing_text.png index 4cf5d76e8..4f7b37b51 100644 Binary files a/res-xxxhdpi/images/erasing_text.png and b/res-xxxhdpi/images/erasing_text.png differ diff --git a/res-xxxhdpi/images/error_text.png b/res-xxxhdpi/images/error_text.png index 8dd6f1236..052bf2142 100644 Binary files a/res-xxxhdpi/images/error_text.png and b/res-xxxhdpi/images/error_text.png differ diff --git a/res-xxxhdpi/images/installing_security_text.png b/res-xxxhdpi/images/installing_security_text.png index fa06f3147..a9e739b17 100644 Binary files a/res-xxxhdpi/images/installing_security_text.png and b/res-xxxhdpi/images/installing_security_text.png differ diff --git a/res-xxxhdpi/images/installing_text.png b/res-xxxhdpi/images/installing_text.png index d0f930160..2d1948677 100644 Binary files a/res-xxxhdpi/images/installing_text.png and b/res-xxxhdpi/images/installing_text.png differ diff --git a/res-xxxhdpi/images/no_command_text.png b/res-xxxhdpi/images/no_command_text.png index 233aec468..ee0c23865 100644 Binary files a/res-xxxhdpi/images/no_command_text.png and b/res-xxxhdpi/images/no_command_text.png differ diff --git a/tests/unit/locale_test.cpp b/tests/unit/locale_test.cpp index f73235005..cdaba0e8b 100644 --- a/tests/unit/locale_test.cpp +++ b/tests/unit/locale_test.cpp @@ -19,14 +19,15 @@ #include "minui/minui.h" TEST(LocaleTest, Misc) { - EXPECT_TRUE(matches_locale("zh_CN", "zh_CN_#Hans")); - EXPECT_TRUE(matches_locale("zh", "zh_CN_#Hans")); - EXPECT_FALSE(matches_locale("zh_HK", "zh_CN_#Hans")); - EXPECT_TRUE(matches_locale("en_GB", "en_GB")); - EXPECT_TRUE(matches_locale("en", "en_GB")); - EXPECT_FALSE(matches_locale("en_GB", "en")); - EXPECT_FALSE(matches_locale("en_GB", "en_US")); - EXPECT_FALSE(matches_locale("en_US", "")); - // Empty locale prefix in the PNG file will match the input locale. - EXPECT_TRUE(matches_locale("", "en_US")); + EXPECT_TRUE(matches_locale("zh-CN", "zh-Hans-CN")); + EXPECT_TRUE(matches_locale("zh", "zh-Hans-CN")); + EXPECT_FALSE(matches_locale("zh-HK", "zh-Hans-CN")); + EXPECT_TRUE(matches_locale("en-GB", "en-GB")); + EXPECT_TRUE(matches_locale("en", "en-GB")); + EXPECT_FALSE(matches_locale("en-GB", "en")); + EXPECT_FALSE(matches_locale("en-GB", "en-US")); + EXPECT_FALSE(matches_locale("en-US", "")); + // Empty locale prefix in the PNG file will match the input locale. + EXPECT_TRUE(matches_locale("", "en-US")); + EXPECT_TRUE(matches_locale("sr-Latn", "sr-Latn-BA")); } -- cgit v1.2.3 From 217d9f98595076f344746bffdafb4314191f3e1b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 20 Mar 2017 16:57:25 -0700 Subject: tests: Construct two bad packages at runtime for VerifierTest. For the BadPackage tests from VerifierTest: one alters the footer, and the other alters the metadata. Move the two tests to be based on otasigned_v3.zip (they're based on otasigned_v1.zip previously). Also construct the testdata files dynamically (to save the space and for better readability). Test: recovery_component_test Change-Id: I7604d563f8b4fa0c55fec8730c063384158e3abc --- tests/component/verifier_test.cpp | 49 +++++++++++++++++++++++++++++++++++--- tests/testdata/alter-footer.zip | Bin 4009 -> 0 bytes tests/testdata/alter-metadata.zip | Bin 4009 -> 0 bytes 3 files changed, 46 insertions(+), 3 deletions(-) delete mode 100644 tests/testdata/alter-footer.zip delete mode 100644 tests/testdata/alter-metadata.zip diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index 07a8c960f..2cfb6d301 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -115,6 +115,51 @@ TEST(VerifierTest, load_keys_invalid_keys) { ASSERT_FALSE(load_keys(key_file5.path, certs)); } +TEST(VerifierTest, BadPackage_AlteredFooter) { + std::string testkey_v3; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); + TemporaryFile key_file1; + ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); + std::vector certs; + ASSERT_TRUE(load_keys(key_file1.path, certs)); + + std::string package; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); + ASSERT_EQ(std::string("\xc0\x06\xff\xff\xd2\x06", 6), package.substr(package.size() - 6, 6)); + + // Alter the footer. + package[package.size() - 5] = '\x05'; + ASSERT_EQ(VERIFY_FAILURE, + verify_file(reinterpret_cast(package.data()), package.size(), + certs)); +} + +TEST(VerifierTest, BadPackage_AlteredContent) { + std::string testkey_v3; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); + TemporaryFile key_file1; + ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); + std::vector certs; + ASSERT_TRUE(load_keys(key_file1.path, certs)); + + std::string package; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); + ASSERT_GT(package.size(), static_cast(100)); + + // Alter the content. + std::string altered1(package); + altered1[50] += 1; + ASSERT_EQ(VERIFY_FAILURE, + verify_file(reinterpret_cast(altered1.data()), altered1.size(), + certs)); + + std::string altered2(package); + altered2[10] += 1; + ASSERT_EQ(VERIFY_FAILURE, + verify_file(reinterpret_cast(altered2.data()), altered2.size(), + certs)); +} + TEST_P(VerifierSuccessTest, VerifySucceed) { ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_SUCCESS); } @@ -157,6 +202,4 @@ INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest, INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest, ::testing::Values( std::vector({"random.zip", "v1"}), - std::vector({"fake-eocd.zip", "v1"}), - std::vector({"alter-metadata.zip", "v1"}), - std::vector({"alter-footer.zip", "v1"}))); + std::vector({"fake-eocd.zip", "v1"}))); diff --git a/tests/testdata/alter-footer.zip b/tests/testdata/alter-footer.zip deleted file mode 100644 index f497ec000..000000000 Binary files a/tests/testdata/alter-footer.zip and /dev/null differ diff --git a/tests/testdata/alter-metadata.zip b/tests/testdata/alter-metadata.zip deleted file mode 100644 index 1c71fbc49..000000000 Binary files a/tests/testdata/alter-metadata.zip and /dev/null differ -- cgit v1.2.3 From 7e61c6a8628bb72c90ca4d5c5b6a609323107d3b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 20 Mar 2017 16:57:25 -0700 Subject: tests: Construct two bad packages at runtime for VerifierTest. For the BadPackage tests from VerifierTest: one alters the footer, and the other alters the metadata. Move the two tests to be based on otasigned_v3.zip (they're based on otasigned_v1.zip previously). Also construct the testdata files dynamically (to save the space and for better readability). Test: recovery_component_test Change-Id: I7604d563f8b4fa0c55fec8730c063384158e3abc (cherry picked from commit 217d9f98595076f344746bffdafb4314191f3e1b) --- tests/component/verifier_test.cpp | 49 +++++++++++++++++++++++++++++++++++--- tests/testdata/alter-footer.zip | Bin 4009 -> 0 bytes tests/testdata/alter-metadata.zip | Bin 4009 -> 0 bytes 3 files changed, 46 insertions(+), 3 deletions(-) delete mode 100644 tests/testdata/alter-footer.zip delete mode 100644 tests/testdata/alter-metadata.zip diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index 4c0648714..4a3a981f7 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -117,6 +117,51 @@ TEST(VerifierTest, load_keys_invalid_keys) { ASSERT_FALSE(load_keys(key_file5.path, certs)); } +TEST(VerifierTest, BadPackage_AlteredFooter) { + std::string testkey_v3; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); + TemporaryFile key_file1; + ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); + std::vector certs; + ASSERT_TRUE(load_keys(key_file1.path, certs)); + + std::string package; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); + ASSERT_EQ(std::string("\xc0\x06\xff\xff\xd2\x06", 6), package.substr(package.size() - 6, 6)); + + // Alter the footer. + package[package.size() - 5] = '\x05'; + ASSERT_EQ(VERIFY_FAILURE, + verify_file(reinterpret_cast(package.data()), package.size(), + certs)); +} + +TEST(VerifierTest, BadPackage_AlteredContent) { + std::string testkey_v3; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); + TemporaryFile key_file1; + ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); + std::vector certs; + ASSERT_TRUE(load_keys(key_file1.path, certs)); + + std::string package; + ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); + ASSERT_GT(package.size(), static_cast(100)); + + // Alter the content. + std::string altered1(package); + altered1[50] += 1; + ASSERT_EQ(VERIFY_FAILURE, + verify_file(reinterpret_cast(altered1.data()), altered1.size(), + certs)); + + std::string altered2(package); + altered2[10] += 1; + ASSERT_EQ(VERIFY_FAILURE, + verify_file(reinterpret_cast(altered2.data()), altered2.size(), + certs)); +} + TEST(VerifierTest, BadPackage_SignatureStartOutOfBounds) { std::string testkey_v3; ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); @@ -174,6 +219,4 @@ INSTANTIATE_TEST_CASE_P(WrongHash, VerifierFailureTest, INSTANTIATE_TEST_CASE_P(BadPackage, VerifierFailureTest, ::testing::Values( std::vector({"random.zip", "v1"}), - std::vector({"fake-eocd.zip", "v1"}), - std::vector({"alter-metadata.zip", "v1"}), - std::vector({"alter-footer.zip", "v1"}))); + std::vector({"fake-eocd.zip", "v1"}))); diff --git a/tests/testdata/alter-footer.zip b/tests/testdata/alter-footer.zip deleted file mode 100644 index f497ec000..000000000 Binary files a/tests/testdata/alter-footer.zip and /dev/null differ diff --git a/tests/testdata/alter-metadata.zip b/tests/testdata/alter-metadata.zip deleted file mode 100644 index 1c71fbc49..000000000 Binary files a/tests/testdata/alter-metadata.zip and /dev/null differ -- cgit v1.2.3 From d2aecd465b7fe24f8302c85f3e062b21b9762d0f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 23 Mar 2017 14:43:44 -0700 Subject: updater: Clean up LoadSrcTgtVersion2(). Rename to LoadSourceBlocks() by moving the target blocks parsing part into the caller. This allows detecting whether the target blocks have already had the expected data before loading the source blocks. It doesn't affect anything when applying an update package for the first time, but it skips loading the unneeded source blocks when resuming an update. It additionally avoids unnecessarily dumping the "corrupt" source/stash blocks when resuming an update. Bug: 33694730 Test: Apply an incremental update with the new updater. Test: Resume an incremental update with the new updater. Change-Id: I794fd0d1045be7b3b7f8619285dc0dade01398d0 --- updater/blockimg.cpp | 289 +++++++++++++++++++++++++-------------------------- 1 file changed, 143 insertions(+), 146 deletions(-) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index c614ccc47..1cad6da92 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -696,7 +696,7 @@ static int LoadStash(CommandParameters& params, const std::string& id, bool veri } static int WriteStash(const std::string& base, const std::string& id, int blocks, - std::vector& buffer, bool checkspace, bool *exists) { + std::vector& buffer, bool checkspace, bool* exists) { if (base.empty()) { return -1; } @@ -883,96 +883,81 @@ static void MoveRange(std::vector& dest, const RangeSet& locs, } } -// Do a source/target load for move/bsdiff/imgdiff in version 2. -// We expect to parse the remainder of the parameter tokens as one of: -// -// -// (loads data from source image only) -// -// - <[stash_id:stash_range] ...> -// (loads data from stashes only) -// -// <[stash_id:stash_range] ...> -// (loads data from both source image and stashes) -// -// On return, params.buffer is filled with the loaded source data (rearranged and combined with -// stashed data as necessary). buffer may be reallocated if needed to accommodate the source data. -// *tgt is the target RangeSet. Any stashes required are loaded using LoadStash. - -static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& src_blocks, - bool* overlap) { +/** + * We expect to parse the remainder of the parameter tokens as one of: + * + * + * (loads data from source image only) + * + * - <[stash_id:stash_range] ...> + * (loads data from stashes only) + * + * <[stash_id:stash_range] ...> + * (loads data from both source image and stashes) + * + * On return, params.buffer is filled with the loaded source data (rearranged and combined with + * stashed data as necessary). buffer may be reallocated if needed to accommodate the source data. + * tgt is the target RangeSet for detecting overlaps. Any stashes required are loaded using + * LoadStash. + */ +static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size_t* src_blocks, + bool* overlap) { + CHECK(src_blocks != nullptr); + CHECK(overlap != nullptr); + + // + const std::string& token = params.tokens[params.cpos++]; + if (!android::base::ParseUint(token, src_blocks)) { + LOG(ERROR) << "invalid src_block_count \"" << token << "\""; + return -1; + } - // At least it needs to provide three parameters: , - // and "-"/. - if (params.cpos + 2 >= params.tokens.size()) { - LOG(ERROR) << "invalid parameters"; - return -1; - } + allocate(*src_blocks * BLOCKSIZE, params.buffer); - // - tgt = parse_range(params.tokens[params.cpos++]); + // "-" or [] + if (params.tokens[params.cpos] == "-") { + // no source ranges, only stashes + params.cpos++; + } else { + RangeSet src = parse_range(params.tokens[params.cpos++]); + *overlap = range_overlaps(src, tgt); - // - const std::string& token = params.tokens[params.cpos++]; - if (!android::base::ParseUint(token.c_str(), &src_blocks)) { - LOG(ERROR) << "invalid src_block_count \"" << token << "\""; - return -1; + if (ReadBlocks(src, params.buffer, params.fd) == -1) { + return -1; } - allocate(src_blocks * BLOCKSIZE, params.buffer); - - // "-" or [] - if (params.tokens[params.cpos] == "-") { - // no source ranges, only stashes - params.cpos++; - } else { - RangeSet src = parse_range(params.tokens[params.cpos++]); - int res = ReadBlocks(src, params.buffer, params.fd); - - if (overlap) { - *overlap = range_overlaps(src, tgt); - } - - if (res == -1) { - return -1; - } - - if (params.cpos >= params.tokens.size()) { - // no stashes, only source range - return 0; - } - - RangeSet locs = parse_range(params.tokens[params.cpos++]); - MoveRange(params.buffer, locs, params.buffer); + if (params.cpos >= params.tokens.size()) { + // no stashes, only source range + return 0; } - // <[stash_id:stash_range]> - while (params.cpos < params.tokens.size()) { - // Each word is a an index into the stash table, a colon, and - // then a rangeset describing where in the source block that - // stashed data should go. - std::vector tokens = android::base::Split(params.tokens[params.cpos++], ":"); - if (tokens.size() != 2) { - LOG(ERROR) << "invalid parameter"; - return -1; - } - - std::vector stash; - int res = LoadStash(params, tokens[0], false, nullptr, stash, true); - - if (res == -1) { - // These source blocks will fail verification if used later, but we - // will let the caller decide if this is a fatal failure - LOG(ERROR) << "failed to load stash " << tokens[0]; - continue; - } + RangeSet locs = parse_range(params.tokens[params.cpos++]); + MoveRange(params.buffer, locs, params.buffer); + } - RangeSet locs = parse_range(tokens[1]); + // <[stash_id:stash_range]> + while (params.cpos < params.tokens.size()) { + // Each word is a an index into the stash table, a colon, and then a RangeSet describing where + // in the source block that stashed data should go. + std::vector tokens = android::base::Split(params.tokens[params.cpos++], ":"); + if (tokens.size() != 2) { + LOG(ERROR) << "invalid parameter"; + return -1; + } - MoveRange(params.buffer, locs, stash); + std::vector stash; + if (LoadStash(params, tokens[0], false, nullptr, stash, true) == -1) { + // These source blocks will fail verification if used later, but we + // will let the caller decide if this is a fatal failure + LOG(ERROR) << "failed to load stash " << tokens[0]; + continue; } - return 0; + RangeSet locs = parse_range(tokens[1]); + MoveRange(params.buffer, locs, stash); + } + + return 0; } /** @@ -989,9 +974,8 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& * <[stash_id:stash_range] ...> * (loads data from both source image and stashes) * - * Parameters are the same as for LoadSrcTgtVersion2, except for 'onehash', which tells the function - * whether to expect separate source and targe block hashes, or if they are both the same and only - * one hash should be expected, and 'isunresumable', which receives a non-zero value if block + * 'onehash' tells whether to expect separate source and targe block hashes, or if they are both the + * same and only one hash should be expected. params.isunresumable will be set to true if block * verification fails in a way that the update cannot be resumed anymore. * * If the function is unable to load the necessary blocks or their contents don't match the hashes, @@ -1002,87 +986,100 @@ static int LoadSrcTgtVersion2(CommandParameters& params, RangeSet& tgt, size_t& * * If the return value is 0, source blocks have expected content and the command can be performed. */ -static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t& src_blocks, - bool onehash, bool& overlap) { - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing source hash"; - return -1; - } +static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t* src_blocks, + bool onehash, bool* overlap) { + CHECK(src_blocks != nullptr); + CHECK(overlap != nullptr); - std::string srchash = params.tokens[params.cpos++]; - std::string tgthash; + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing source hash"; + return -1; + } - if (onehash) { - tgthash = srchash; - } else { - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing target hash"; - return -1; - } - tgthash = params.tokens[params.cpos++]; - } + std::string srchash = params.tokens[params.cpos++]; + std::string tgthash; - if (LoadSrcTgtVersion2(params, tgt, src_blocks, &overlap) == -1) { - return -1; + if (onehash) { + tgthash = srchash; + } else { + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing target hash"; + return -1; } + tgthash = params.tokens[params.cpos++]; + } - std::vector tgtbuffer(tgt.size * BLOCKSIZE); + // At least it needs to provide three parameters: , and + // "-"/. + if (params.cpos + 2 >= params.tokens.size()) { + LOG(ERROR) << "invalid parameters"; + return -1; + } - if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) { - return -1; - } + // + tgt = parse_range(params.tokens[params.cpos++]); - if (VerifyBlocks(tgthash, tgtbuffer, tgt.size, false) == 0) { - // Target blocks already have expected content, command should be skipped. - return 1; - } + std::vector tgtbuffer(tgt.size * BLOCKSIZE); + if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) { + return -1; + } - if (VerifyBlocks(srchash, params.buffer, src_blocks, true) == 0) { - // If source and target blocks overlap, stash the source blocks so we can - // resume from possible write errors. In verify mode, we can skip stashing - // because the source blocks won't be overwritten. - if (overlap && params.canwrite) { - LOG(INFO) << "stashing " << src_blocks << " overlapping blocks to " << srchash; + // Return now if target blocks already have expected content. + if (VerifyBlocks(tgthash, tgtbuffer, tgt.size, false) == 0) { + return 1; + } - bool stash_exists = false; - if (WriteStash(params.stashbase, srchash, src_blocks, params.buffer, true, - &stash_exists) != 0) { - LOG(ERROR) << "failed to stash overlapping source blocks"; - return -1; - } + // Load source blocks. + if (LoadSourceBlocks(params, tgt, src_blocks, overlap) == -1) { + return -1; + } - params.stashed += src_blocks; - // Can be deleted when the write has completed. - if (!stash_exists) { - params.freestash = srchash; - } - } + if (VerifyBlocks(srchash, params.buffer, *src_blocks, true) == 0) { + // If source and target blocks overlap, stash the source blocks so we can + // resume from possible write errors. In verify mode, we can skip stashing + // because the source blocks won't be overwritten. + if (*overlap && params.canwrite) { + LOG(INFO) << "stashing " << *src_blocks << " overlapping blocks to " << srchash; + + bool stash_exists = false; + if (WriteStash(params.stashbase, srchash, *src_blocks, params.buffer, true, + &stash_exists) != 0) { + LOG(ERROR) << "failed to stash overlapping source blocks"; + return -1; + } - // Source blocks have expected content, command can proceed. - return 0; + params.stashed += *src_blocks; + // Can be deleted when the write has completed. + if (!stash_exists) { + params.freestash = srchash; + } } - if (overlap && LoadStash(params, srchash, true, nullptr, params.buffer, true) == 0) { - // Overlapping source blocks were previously stashed, command can proceed. - // We are recovering from an interrupted command, so we don't know if the - // stash can safely be deleted after this command. - return 0; - } + // Source blocks have expected content, command can proceed. + return 0; + } - // Valid source data not available, update cannot be resumed. - LOG(ERROR) << "partition has unexpected contents"; - PrintHashForCorruptedSourceBlocks(params, params.buffer); + if (*overlap && LoadStash(params, srchash, true, nullptr, params.buffer, true) == 0) { + // Overlapping source blocks were previously stashed, command can proceed. We are recovering + // from an interrupted command, so we don't know if the stash can safely be deleted after this + // command. + return 0; + } - params.isunresumable = true; + // Valid source data not available, update cannot be resumed. + LOG(ERROR) << "partition has unexpected contents"; + PrintHashForCorruptedSourceBlocks(params, params.buffer); - return -1; + params.isunresumable = true; + + return -1; } static int PerformCommandMove(CommandParameters& params) { size_t blocks = 0; bool overlap = false; RangeSet tgt; - int status = LoadSrcTgtVersion3(params, tgt, blocks, true, overlap); + int status = LoadSrcTgtVersion3(params, tgt, &blocks, true, &overlap); if (status == -1) { LOG(ERROR) << "failed to read blocks for move"; @@ -1270,13 +1267,13 @@ static int PerformCommandDiff(CommandParameters& params) { } size_t offset; - if (!android::base::ParseUint(params.tokens[params.cpos++].c_str(), &offset)) { + if (!android::base::ParseUint(params.tokens[params.cpos++], &offset)) { LOG(ERROR) << "invalid patch offset"; return -1; } size_t len; - if (!android::base::ParseUint(params.tokens[params.cpos++].c_str(), &len)) { + if (!android::base::ParseUint(params.tokens[params.cpos++], &len)) { LOG(ERROR) << "invalid patch len"; return -1; } @@ -1284,7 +1281,7 @@ static int PerformCommandDiff(CommandParameters& params) { RangeSet tgt; size_t blocks = 0; bool overlap = false; - int status = LoadSrcTgtVersion3(params, tgt, blocks, false, overlap); + int status = LoadSrcTgtVersion3(params, tgt, &blocks, false, &overlap); if (status == -1) { LOG(ERROR) << "failed to read blocks for diff"; @@ -1871,7 +1868,7 @@ Value* BlockImageRecoverFn(const char* name, State* state, LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read - fec::io fh(filename->data.c_str(), O_RDWR); + fec::io fh(filename->data, O_RDWR); if (!fh) { ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), -- cgit v1.2.3 From f7eb760fe76cb66c5d5341b03d6a66cc1f06d795 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 27 Mar 2017 15:12:48 -0700 Subject: applypatch: Change the ssize_t length parameters to size_t. Mostly for applypatch family APIs like ApplyBSDiffPatch() and ApplyImagePatch(). Changing to size_t doesn't indicate they would necessarily work with very large size_t (e.g. > ssize_t), just similar to write(2). But otherwise accepting negative length doesn't make much sense. Also change the return type of SinkFn from ssize_t to size_t. Callers tell a successful sink by comparing the number of written bytes against the desired value. Negative return values like -1 are not needed. This also makes it consistent with bsdiff::bspatch interface. Test: recovery_component_test Test: Apply an incremental with the new updater. Change-Id: I7ff1615203a5c9854134f75d019e266f4ea6e714 --- applypatch/applypatch.cpp | 37 +++++++------- applypatch/bspatch.cpp | 8 +-- applypatch/imgpatch.cpp | 17 +++---- applypatch/include/applypatch/applypatch.h | 18 +++---- applypatch/include/applypatch/imgpatch.h | 7 ++- tests/component/imgdiff_test.cpp | 2 +- updater/blockimg.cpp | 81 +++++++++++++++--------------- 7 files changed, 81 insertions(+), 89 deletions(-) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 7be3fdbde..06b8e3120 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -42,7 +42,7 @@ #include "print_sha1.h" static int LoadPartitionContents(const std::string& filename, FileContents* file); -static ssize_t FileSink(const unsigned char* data, ssize_t len, void* token); +static size_t FileSink(const unsigned char* data, size_t len, void* token); static int GenerateTarget(const FileContents& source_file, const std::unique_ptr& patch, const std::string& target_filename, const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data); @@ -194,8 +194,8 @@ int SaveFileContents(const char* filename, const FileContents* file) { return -1; } - ssize_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd); - if (bytes_written != static_cast(file->data.size())) { + size_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd); + if (bytes_written != file->data.size()) { printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written, file->data.size(), strerror(errno)); return -1; @@ -433,25 +433,24 @@ int ShowLicenses() { return 0; } -ssize_t FileSink(const unsigned char* data, ssize_t len, void* token) { - int fd = *static_cast(token); - ssize_t done = 0; - ssize_t wrote; - while (done < len) { - wrote = TEMP_FAILURE_RETRY(ota_write(fd, data+done, len-done)); - if (wrote == -1) { - printf("error writing %zd bytes: %s\n", (len-done), strerror(errno)); - return done; - } - done += wrote; +static size_t FileSink(const unsigned char* data, size_t len, void* token) { + int fd = *static_cast(token); + size_t done = 0; + while (done < len) { + ssize_t wrote = TEMP_FAILURE_RETRY(ota_write(fd, data + done, len - done)); + if (wrote == -1) { + printf("error writing %zd bytes: %s\n", (len - done), strerror(errno)); + return done; } - return done; + done += wrote; + } + return done; } -ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { - std::string* s = static_cast(token); - s->append(reinterpret_cast(data), len); - return len; +size_t MemorySink(const unsigned char* data, size_t len, void* token) { + std::string* s = static_cast(token); + s->append(reinterpret_cast(data), len); + return len; } // Return the amount of free space (in bytes) on the filesystem diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index 9920c2be1..8ef7a068d 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -60,8 +60,8 @@ void ShowBSDiffLicense() { ); } -int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, - ssize_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx) { +int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx) { auto sha_sink = [&](const uint8_t* data, size_t len) { len = sink(data, len, token); if (ctx) SHA1_Update(ctx, data, len); @@ -72,8 +72,8 @@ int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, const Valu patch->data.size(), sha_sink); } -int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, const Value* patch, - ssize_t patch_offset, std::vector* new_data) { +int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, std::vector* new_data) { auto vector_sink = [new_data](const uint8_t* data, size_t len) { new_data->insert(new_data->end(), data, data + len); return len; diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index adcc61fd6..7a88ffbbc 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -43,9 +43,8 @@ static inline int32_t Read4(const void *address) { return android::base::get_unaligned(address); } -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, - const unsigned char* patch_data, ssize_t patch_size, - SinkFn sink, void* token) { +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, + size_t patch_size, SinkFn sink, void* token) { Value patch(VAL_BLOB, std::string(reinterpret_cast(patch_data), patch_size)); return ApplyImagePatch(old_data, old_size, &patch, sink, token, nullptr, nullptr); @@ -57,8 +56,8 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, * file, and update the SHA context with the output data as well. * Return 0 on success. */ -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value* patch, - SinkFn sink, void* token, SHA_CTX* ctx, const Value* bonus_data) { +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, + void* token, SHA_CTX* ctx, const Value* bonus_data) { if (patch->data.size() < 12) { printf("patch too short to contain header\n"); return -1; @@ -97,7 +96,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value size_t src_len = static_cast(Read8(normal_header + 8)); size_t patch_offset = static_cast(Read8(normal_header + 16)); - if (src_start + src_len > static_cast(old_size)) { + if (src_start + src_len > old_size) { printf("source data too short\n"); return -1; } @@ -110,7 +109,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value return -1; } - ssize_t data_len = Read4(raw_header); + size_t data_len = static_cast(Read4(raw_header)); if (pos + data_len > patch->data.size()) { printf("failed to read chunk %d raw data\n", i); @@ -143,7 +142,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value int memLevel = Read4(deflate_header + 52); int strategy = Read4(deflate_header + 56); - if (src_start + src_len > static_cast(old_size)) { + if (src_start + src_len > old_size) { printf("source data too short\n"); return -1; } @@ -240,7 +239,7 @@ int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, const Value strm.avail_out = temp_data.size(); strm.next_out = temp_data.data(); ret = deflate(&strm, Z_FINISH); - ssize_t have = temp_data.size() - strm.avail_out; + size_t have = temp_data.size() - strm.avail_out; if (sink(temp_data.data(), have, token) != have) { printf("failed to write %zd compressed bytes to output\n", have); diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 4489decb6..52fb58287 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -41,7 +41,7 @@ struct FileContents { // and use it as the source instead. #define CACHE_TEMP_SOURCE "/cache/saved.file" -typedef ssize_t (*SinkFn)(const unsigned char*, ssize_t, void*); +using SinkFn = size_t (*)(const unsigned char*, size_t, void*); // applypatch.cpp int ShowLicenses(); @@ -66,18 +66,14 @@ int SaveFileContents(const char* filename, const FileContents* file); // bspatch.cpp void ShowBSDiffLicense(); -int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size, - const Value* patch, ssize_t patch_offset, - SinkFn sink, void* token, SHA_CTX* ctx); -int ApplyBSDiffPatchMem(const unsigned char* old_data, ssize_t old_size, - const Value* patch, ssize_t patch_offset, - std::vector* new_data); +int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx); +int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, + size_t patch_offset, std::vector* new_data); // imgpatch.cpp -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, - const Value* patch, - SinkFn sink, void* token, SHA_CTX* ctx, - const Value* bonus_data); +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, + void* token, SHA_CTX* ctx, const Value* bonus_data); // freecache.cpp int MakeFreeSpaceOnCache(size_t bytes_needed); diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h index 6549f79f0..16e4c4f57 100644 --- a/applypatch/include/applypatch/imgpatch.h +++ b/applypatch/include/applypatch/imgpatch.h @@ -19,10 +19,9 @@ #include -using SinkFn = ssize_t (*)(const unsigned char*, ssize_t, void*); +using SinkFn = size_t (*)(const unsigned char*, size_t, void*); -int ApplyImagePatch(const unsigned char* old_data, ssize_t old_size, - const unsigned char* patch_data, ssize_t patch_size, - SinkFn sink, void* token); +int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, + size_t patch_size, SinkFn sink, void* token); #endif // _APPLYPATCH_IMGPATCH_H diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 2f648501c..64e1f2990 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -27,7 +27,7 @@ using android::base::get_unaligned; -static ssize_t MemorySink(const unsigned char* data, ssize_t len, void* token) { +static size_t MemorySink(const unsigned char* data, size_t len, void* token) { std::string* s = static_cast(token); s->append(reinterpret_cast(data), len); return len; diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 1cad6da92..5a27ff41a 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -240,57 +240,56 @@ struct RangeSinkState { size_t p_remain; }; -static ssize_t RangeSinkWrite(const uint8_t* data, ssize_t size, void* token) { - RangeSinkState* rss = reinterpret_cast(token); +static size_t RangeSinkWrite(const uint8_t* data, size_t size, void* token) { + RangeSinkState* rss = static_cast(token); - if (rss->p_remain == 0) { - LOG(ERROR) << "range sink write overrun"; - return 0; - } - - ssize_t written = 0; - while (size > 0) { - size_t write_now = size; - - if (rss->p_remain < write_now) { - write_now = rss->p_remain; - } + if (rss->p_remain == 0) { + LOG(ERROR) << "range sink write overrun"; + return 0; + } - if (write_all(rss->fd, data, write_now) == -1) { - break; - } + size_t written = 0; + while (size > 0) { + size_t write_now = size; - data += write_now; - size -= write_now; + if (rss->p_remain < write_now) { + write_now = rss->p_remain; + } - rss->p_remain -= write_now; - written += write_now; + if (write_all(rss->fd, data, write_now) == -1) { + break; + } - if (rss->p_remain == 0) { - // move to the next block - ++rss->p_block; - if (rss->p_block < rss->tgt.count) { - rss->p_remain = (rss->tgt.pos[rss->p_block * 2 + 1] - - rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; + data += write_now; + size -= write_now; - off64_t offset = static_cast(rss->tgt.pos[rss->p_block*2]) * BLOCKSIZE; - if (!discard_blocks(rss->fd, offset, rss->p_remain)) { - break; - } + rss->p_remain -= write_now; + written += write_now; - if (!check_lseek(rss->fd, offset, SEEK_SET)) { - break; - } + if (rss->p_remain == 0) { + // Move to the next block. + ++rss->p_block; + if (rss->p_block < rss->tgt.count) { + rss->p_remain = + (rss->tgt.pos[rss->p_block * 2 + 1] - rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; + + off64_t offset = static_cast(rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; + if (!discard_blocks(rss->fd, offset, rss->p_remain)) { + break; + } - } else { - // we can't write any more; return how many bytes have - // been written so far. - break; - } + if (!check_lseek(rss->fd, offset, SEEK_SET)) { + break; } + + } else { + // We can't write any more; return how many bytes have been written so far. + break; + } } + } - return written; + return written; } // All of the data for all the 'new' transfers is contained in one @@ -338,7 +337,7 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { // At this point nti->rss is set, and we own it. The main // thread is waiting for it to disappear from nti. - ssize_t written = RangeSinkWrite(data, size, nti->rss); + size_t written = RangeSinkWrite(data, size, nti->rss); data += written; size -= written; -- cgit v1.2.3 From c0e1c46a707370952ea1ddeb71b176d04fe71bb9 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 1 Feb 2017 10:20:10 -0800 Subject: applypatch: Let Apply{BSDiff,Image}Patch accept std::function. Test: mmma bootable/recovery system/update_engine Test: recovery_component_test Change-Id: I93c2caa87bf94a53509bb37f98f2c02bcadb6f5c --- applypatch/applypatch.cpp | 24 ++--- applypatch/bspatch.cpp | 8 +- applypatch/imgpatch.cpp | 13 ++- applypatch/include/applypatch/applypatch.h | 7 +- applypatch/include/applypatch/imgpatch.h | 6 +- tests/component/imgdiff_test.cpp | 80 +++++---------- updater/blockimg.cpp | 151 +++++++++++++++-------------- 7 files changed, 128 insertions(+), 161 deletions(-) diff --git a/applypatch/applypatch.cpp b/applypatch/applypatch.cpp index 06b8e3120..51bf3932a 100644 --- a/applypatch/applypatch.cpp +++ b/applypatch/applypatch.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,7 @@ #include "print_sha1.h" static int LoadPartitionContents(const std::string& filename, FileContents* file); -static size_t FileSink(const unsigned char* data, size_t len, void* token); +static size_t FileSink(const unsigned char* data, size_t len, int fd); static int GenerateTarget(const FileContents& source_file, const std::unique_ptr& patch, const std::string& target_filename, const uint8_t target_sha1[SHA_DIGEST_LENGTH], const Value* bonus_data); @@ -194,7 +195,7 @@ int SaveFileContents(const char* filename, const FileContents* file) { return -1; } - size_t bytes_written = FileSink(file->data.data(), file->data.size(), &fd); + size_t bytes_written = FileSink(file->data.data(), file->data.size(), fd); if (bytes_written != file->data.size()) { printf("short write of \"%s\" (%zd bytes of %zu): %s\n", filename, bytes_written, file->data.size(), strerror(errno)); @@ -433,8 +434,7 @@ int ShowLicenses() { return 0; } -static size_t FileSink(const unsigned char* data, size_t len, void* token) { - int fd = *static_cast(token); +static size_t FileSink(const unsigned char* data, size_t len, int fd) { size_t done = 0; while (done < len) { ssize_t wrote = TEMP_FAILURE_RETRY(ota_write(fd, data + done, len - done)); @@ -447,12 +447,6 @@ static size_t FileSink(const unsigned char* data, size_t len, void* token) { return done; } -size_t MemorySink(const unsigned char* data, size_t len, void* token) { - std::string* s = static_cast(token); - s->append(reinterpret_cast(data), len); - return len; -} - // Return the amount of free space (in bytes) on the filesystem // containing filename. filename must exist. Return -1 on error. size_t FreeSpaceForFile(const char* filename) { @@ -646,9 +640,11 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr } // We store the decoded output in memory. - SinkFn sink = MemorySink; std::string memory_sink_str; // Don't need to reserve space. - void* token = &memory_sink_str; + SinkFn sink = [&memory_sink_str](const unsigned char* data, size_t len) { + memory_sink_str.append(reinterpret_cast(data), len); + return len; + }; SHA_CTX ctx; SHA1_Init(&ctx); @@ -656,10 +652,10 @@ static int GenerateTarget(const FileContents& source_file, const std::unique_ptr int result; if (use_bsdiff) { result = ApplyBSDiffPatch(source_file.data.data(), source_file.data.size(), patch.get(), 0, - sink, token, &ctx); + sink, &ctx); } else { result = ApplyImagePatch(source_file.data.data(), source_file.data.size(), patch.get(), sink, - token, &ctx, bonus_data); + &ctx, bonus_data); } if (result != 0) { diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index 8ef7a068d..f75a2c680 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -24,9 +24,9 @@ #include #include +#include #include "applypatch/applypatch.h" -#include "openssl/sha.h" void ShowBSDiffLicense() { puts("The bsdiff library used herein is:\n" @@ -61,9 +61,9 @@ void ShowBSDiffLicense() { } int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, - size_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx) { - auto sha_sink = [&](const uint8_t* data, size_t len) { - len = sink(data, len, token); + size_t patch_offset, SinkFn sink, SHA_CTX* ctx) { + auto sha_sink = [&sink, &ctx](const uint8_t* data, size_t len) { + len = sink(data, len); if (ctx) SHA1_Update(ctx, data, len); return len; }; diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 7a88ffbbc..7d8b7361c 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -44,10 +44,10 @@ static inline int32_t Read4(const void *address) { } int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, - size_t patch_size, SinkFn sink, void* token) { + size_t patch_size, SinkFn sink) { Value patch(VAL_BLOB, std::string(reinterpret_cast(patch_data), patch_size)); - return ApplyImagePatch(old_data, old_size, &patch, sink, token, nullptr, nullptr); + return ApplyImagePatch(old_data, old_size, &patch, sink, nullptr, nullptr); } /* @@ -57,7 +57,7 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsign * Return 0 on success. */ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, - void* token, SHA_CTX* ctx, const Value* bonus_data) { + SHA_CTX* ctx, const Value* bonus_data) { if (patch->data.size() < 12) { printf("patch too short to contain header\n"); return -1; @@ -100,7 +100,7 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* printf("source data too short\n"); return -1; } - ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, token, ctx); + ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx); } else if (type == CHUNK_RAW) { const char* raw_header = &patch->data[pos]; pos += 4; @@ -116,8 +116,7 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* return -1; } if (ctx) SHA1_Update(ctx, &patch->data[pos], data_len); - if (sink(reinterpret_cast(&patch->data[pos]), data_len, token) != - data_len) { + if (sink(reinterpret_cast(&patch->data[pos]), data_len) != data_len) { printf("failed to write chunk %d raw data\n", i); return -1; } @@ -241,7 +240,7 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* ret = deflate(&strm, Z_FINISH); size_t have = temp_data.size() - strm.avail_out; - if (sink(temp_data.data(), have, token) != have) { + if (sink(temp_data.data(), have) != have) { printf("failed to write %zd compressed bytes to output\n", have); return -1; } diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index 52fb58287..da55432d5 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -41,7 +42,7 @@ struct FileContents { // and use it as the source instead. #define CACHE_TEMP_SOURCE "/cache/saved.file" -using SinkFn = size_t (*)(const unsigned char*, size_t, void*); +using SinkFn = std::function; // applypatch.cpp int ShowLicenses(); @@ -67,13 +68,13 @@ int SaveFileContents(const char* filename, const FileContents* file); // bspatch.cpp void ShowBSDiffLicense(); int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, - size_t patch_offset, SinkFn sink, void* token, SHA_CTX* ctx); + size_t patch_offset, SinkFn sink, SHA_CTX* ctx); int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, size_t patch_offset, std::vector* new_data); // imgpatch.cpp int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, - void* token, SHA_CTX* ctx, const Value* bonus_data); + SHA_CTX* ctx, const Value* bonus_data); // freecache.cpp int MakeFreeSpaceOnCache(size_t bytes_needed); diff --git a/applypatch/include/applypatch/imgpatch.h b/applypatch/include/applypatch/imgpatch.h index 16e4c4f57..07c66094f 100644 --- a/applypatch/include/applypatch/imgpatch.h +++ b/applypatch/include/applypatch/imgpatch.h @@ -19,9 +19,11 @@ #include -using SinkFn = size_t (*)(const unsigned char*, size_t, void*); +#include + +using SinkFn = std::function; int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, - size_t patch_size, SinkFn sink, void* token); + size_t patch_size, SinkFn sink); #endif // _APPLYPATCH_IMGPATCH_H diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 64e1f2990..7d00a3d53 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include + #include #include @@ -27,12 +29,6 @@ using android::base::get_unaligned; -static size_t MemorySink(const unsigned char* data, size_t len, void* token) { - std::string* s = static_cast(token); - s->append(reinterpret_cast(data), len); - return len; -} - // Sanity check for the given imgdiff patch header. static void verify_patch_header(const std::string& patch, size_t* num_normal, size_t* num_raw, size_t* num_deflate) { @@ -79,6 +75,18 @@ static void verify_patch_header(const std::string& patch, size_t* num_normal, si if (num_deflate != nullptr) *num_deflate = deflate; } +static void verify_patched_image(const std::string& src, const std::string& patch, + const std::string& tgt) { + std::string patched; + ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), + reinterpret_cast(patch.data()), patch.size(), + [&patched](const unsigned char* data, size_t len) { + patched.append(reinterpret_cast(data), len); + return len; + })); + ASSERT_EQ(tgt, patched); +} + TEST(ImgdiffTest, invalid_args) { // Insufficient inputs. ASSERT_EQ(2, imgdiff(1, (const char* []){ "imgdiff" })); @@ -124,11 +132,7 @@ TEST(ImgdiffTest, image_mode_smoke) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, zip_mode_smoke_store) { @@ -177,11 +181,7 @@ TEST(ImgdiffTest, zip_mode_smoke_store) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, zip_mode_smoke_compressed) { @@ -230,11 +230,7 @@ TEST(ImgdiffTest, zip_mode_smoke_compressed) { ASSERT_EQ(1U, num_deflate); ASSERT_EQ(2U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) { @@ -286,11 +282,7 @@ TEST(ImgdiffTest, zip_mode_smoke_trailer_zeros) { ASSERT_EQ(1U, num_deflate); ASSERT_EQ(2U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_simple) { @@ -333,11 +325,7 @@ TEST(ImgdiffTest, image_mode_simple) { ASSERT_EQ(1U, num_deflate); ASSERT_EQ(2U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_different_num_chunks) { @@ -413,11 +401,7 @@ TEST(ImgdiffTest, image_mode_merge_chunks) { ASSERT_EQ(1U, num_deflate); ASSERT_EQ(2U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_spurious_magic) { @@ -454,11 +438,7 @@ TEST(ImgdiffTest, image_mode_spurious_magic) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_short_input1) { @@ -494,11 +474,7 @@ TEST(ImgdiffTest, image_mode_short_input1) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_short_input2) { @@ -534,11 +510,7 @@ TEST(ImgdiffTest, image_mode_short_input2) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(1U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } TEST(ImgdiffTest, image_mode_single_entry_long) { @@ -577,9 +549,5 @@ TEST(ImgdiffTest, image_mode_single_entry_long) { ASSERT_EQ(0U, num_deflate); ASSERT_EQ(0U, num_raw); - std::string patched; - ASSERT_EQ(0, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), - reinterpret_cast(patch.data()), patch.size(), - MemorySink, &patched)); - ASSERT_EQ(tgt, patched); + verify_patched_image(src, patch, tgt); } diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 5a27ff41a..8c0f885a1 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -240,9 +240,7 @@ struct RangeSinkState { size_t p_remain; }; -static size_t RangeSinkWrite(const uint8_t* data, size_t size, void* token) { - RangeSinkState* rss = static_cast(token); - +static size_t RangeSinkWrite(const uint8_t* data, size_t size, RangeSinkState* rss) { if (rss->p_remain == 0) { LOG(ERROR) << "range sink write overrun"; return 0; @@ -1258,92 +1256,95 @@ static int PerformCommandNew(CommandParameters& params) { } static int PerformCommandDiff(CommandParameters& params) { + // + if (params.cpos + 1 >= params.tokens.size()) { + LOG(ERROR) << "missing patch offset or length for " << params.cmdname; + return -1; + } - // - if (params.cpos + 1 >= params.tokens.size()) { - LOG(ERROR) << "missing patch offset or length for " << params.cmdname; - return -1; - } + size_t offset; + if (!android::base::ParseUint(params.tokens[params.cpos++], &offset)) { + LOG(ERROR) << "invalid patch offset"; + return -1; + } - size_t offset; - if (!android::base::ParseUint(params.tokens[params.cpos++], &offset)) { - LOG(ERROR) << "invalid patch offset"; - return -1; - } + size_t len; + if (!android::base::ParseUint(params.tokens[params.cpos++], &len)) { + LOG(ERROR) << "invalid patch len"; + return -1; + } - size_t len; - if (!android::base::ParseUint(params.tokens[params.cpos++], &len)) { - LOG(ERROR) << "invalid patch len"; - return -1; - } + RangeSet tgt; + size_t blocks = 0; + bool overlap = false; + int status = LoadSrcTgtVersion3(params, tgt, &blocks, false, &overlap); - RangeSet tgt; - size_t blocks = 0; - bool overlap = false; - int status = LoadSrcTgtVersion3(params, tgt, &blocks, false, &overlap); + if (status == -1) { + LOG(ERROR) << "failed to read blocks for diff"; + return -1; + } - if (status == -1) { - LOG(ERROR) << "failed to read blocks for diff"; - return -1; - } + if (status == 0) { + params.foundwrites = true; + } else if (params.foundwrites) { + LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; + } + if (params.canwrite) { if (status == 0) { - params.foundwrites = true; - } else if (params.foundwrites) { - LOG(WARNING) << "warning: commands executed out of order [" << params.cmdname << "]"; - } - - if (params.canwrite) { - if (status == 0) { - LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; - Value patch_value(VAL_BLOB, - std::string(reinterpret_cast(params.patch_start + offset), len)); - RangeSinkState rss(tgt); - rss.fd = params.fd; - rss.p_block = 0; - rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; - - off64_t offset = static_cast(tgt.pos[0]) * BLOCKSIZE; - if (!discard_blocks(params.fd, offset, rss.p_remain)) { - return -1; - } - - if (!check_lseek(params.fd, offset, SEEK_SET)) { - return -1; - } + LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; + Value patch_value( + VAL_BLOB, std::string(reinterpret_cast(params.patch_start + offset), len)); + RangeSinkState rss(tgt); + rss.fd = params.fd; + rss.p_block = 0; + rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; + + off64_t offset = static_cast(tgt.pos[0]) * BLOCKSIZE; + if (!discard_blocks(params.fd, offset, rss.p_remain)) { + return -1; + } - if (params.cmdname[0] == 'i') { // imgdiff - if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, - &RangeSinkWrite, &rss, nullptr, nullptr) != 0) { - LOG(ERROR) << "Failed to apply image patch."; - return -1; - } - } else { - if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, - 0, &RangeSinkWrite, &rss, nullptr) != 0) { - LOG(ERROR) << "Failed to apply bsdiff patch."; - return -1; - } - } + if (!check_lseek(params.fd, offset, SEEK_SET)) { + return -1; + } - // We expect the output of the patcher to fill the tgt ranges exactly. - if (rss.p_block != tgt.count || rss.p_remain != 0) { - LOG(ERROR) << "range sink underrun?"; - } - } else { - LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.size - << " [" << params.cmdline << "]"; + if (params.cmdname[0] == 'i') { // imgdiff + if (ApplyImagePatch( + params.buffer.data(), blocks * BLOCKSIZE, &patch_value, + std::bind(&RangeSinkWrite, std::placeholders::_1, std::placeholders::_2, &rss), + nullptr, nullptr) != 0) { + LOG(ERROR) << "Failed to apply image patch."; + return -1; } - } + } else { + if (ApplyBSDiffPatch( + params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0, + std::bind(&RangeSinkWrite, std::placeholders::_1, std::placeholders::_2, &rss), + nullptr) != 0) { + LOG(ERROR) << "Failed to apply bsdiff patch."; + return -1; + } + } - if (!params.freestash.empty()) { - FreeStash(params.stashbase, params.freestash); - params.freestash.clear(); + // We expect the output of the patcher to fill the tgt ranges exactly. + if (rss.p_block != tgt.count || rss.p_remain != 0) { + LOG(ERROR) << "range sink underrun?"; + } + } else { + LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.size << " [" + << params.cmdline << "]"; } + } - params.written += tgt.size; + if (!params.freestash.empty()) { + FreeStash(params.stashbase, params.freestash); + params.freestash.clear(); + } - return 0; + params.written += tgt.size; + + return 0; } static int PerformCommandErase(CommandParameters& params) { -- cgit v1.2.3 From bdc8c1a264ace01fa2b1e6bcd1b417ab7218d169 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 24 Mar 2017 10:39:05 -0700 Subject: tests: Add a test for --wipe_ab into UncryptTest. Also factor out the common parts in {setup,clear}_bcb into a separate function. Test: recovery_component_test Change-Id: I7b95cced925c8135e020dcb791ca2425d4f28449 --- tests/component/uncrypt_test.cpp | 185 +++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 103 deletions(-) diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp index 4f2b8164f..5e057e129 100644 --- a/tests/component/uncrypt_test.cpp +++ b/tests/component/uncrypt_test.cpp @@ -25,12 +25,15 @@ #include #include #include +#include #include #include #include #include "common/component_test_util.h" +using namespace std::string_literals; + static const std::string UNCRYPT_SOCKET = "/dev/socket/uncrypt"; static const std::string INIT_SVC_SETUP_BCB = "init.svc.setup-bcb"; static const std::string INIT_SVC_CLEAR_BCB = "init.svc.clear-bcb"; @@ -65,128 +68,104 @@ class UncryptTest : public ::testing::Test { has_misc = parse_misc(); } - bool has_misc; -}; - -TEST_F(UncryptTest, setup_bcb) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } - - // Trigger the setup-bcb service. - ASSERT_TRUE(android::base::SetProperty("ctl.start", "setup-bcb")); - - // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). - sleep(1); - - struct sockaddr_un un = {}; - un.sun_family = AF_UNIX; - strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path)); - - int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - ASSERT_NE(-1, sockfd); - - // Connect to the uncrypt socket. - bool success = false; - for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { - if (connect(sockfd, reinterpret_cast(&un), sizeof(struct sockaddr_un)) != 0) { - success = true; - break; + void SetupOrClearBcb(bool isSetup, const std::string& message, + const std::string& message_in_bcb) const { + if (!has_misc) { + GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; + return; } - sleep(1); - } - ASSERT_TRUE(success); - - // Send out the BCB message. - std::string message = "--update_message=abc value"; - std::string message_in_bcb = "recovery\n--update_message=abc value\n"; - int length = static_cast(message.size()); - int length_out = htonl(length); - ASSERT_TRUE(android::base::WriteFully(sockfd, &length_out, sizeof(int))) - << "Failed to write length: " << strerror(errno); - ASSERT_TRUE(android::base::WriteFully(sockfd, message.data(), length)) - << "Failed to write message: " << strerror(errno); - - // Check the status code from uncrypt. - int status; - ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int))); - ASSERT_EQ(100U, ntohl(status)); - // Ack having received the status code. - int code = 0; - ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int))); + // Trigger the setup-bcb service. + ASSERT_TRUE(android::base::SetProperty("ctl.start", isSetup ? "setup-bcb" : "clear-bcb")); - ASSERT_EQ(0, close(sockfd)); + // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). + sleep(1); - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "setup-bcb")); + sockaddr_un un = {}; + un.sun_family = AF_UNIX; + strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path)); - // Verify the message by reading from BCB directly. - bootloader_message boot; - std::string err; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; + int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); + ASSERT_NE(-1, sockfd); - ASSERT_EQ("boot-recovery", std::string(boot.command)); - ASSERT_EQ(message_in_bcb, std::string(boot.recovery)); + // Connect to the uncrypt socket. + bool success = false; + for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { + if (connect(sockfd, reinterpret_cast(&un), sizeof(sockaddr_un)) != 0) { + success = true; + break; + } + sleep(1); + } + ASSERT_TRUE(success); + + if (isSetup) { + // Send out the BCB message. + int length = static_cast(message.size()); + int length_out = htonl(length); + ASSERT_TRUE(android::base::WriteFully(sockfd, &length_out, sizeof(int))) + << "Failed to write length: " << strerror(errno); + ASSERT_TRUE(android::base::WriteFully(sockfd, message.data(), length)) + << "Failed to write message: " << strerror(errno); + } - // The rest of the boot.recovery message should be zero'd out. - ASSERT_LE(message_in_bcb.size(), sizeof(boot.recovery)); - size_t left = sizeof(boot.recovery) - message_in_bcb.size(); - ASSERT_EQ(std::string(left, '\0'), std::string(&boot.recovery[message_in_bcb.size()], left)); + // Check the status code from uncrypt. + int status; + ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int))); + ASSERT_EQ(100U, ntohl(status)); - // Clear the BCB. - ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; -} + // Ack having received the status code. + int code = 0; + ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int))); -TEST_F(UncryptTest, clear_bcb) { - if (!has_misc) { - GTEST_LOG_(INFO) << "Test skipped due to no /misc partition found on the device."; - return; - } + ASSERT_EQ(0, close(sockfd)); - // Trigger the clear-bcb service. - ASSERT_TRUE(android::base::SetProperty("ctl.start", "clear-bcb")); + ASSERT_TRUE(android::base::SetProperty("ctl.stop", isSetup ? "setup-bcb" : "clear-bcb")); - // Test tends to be flaky if proceeding immediately ("Transport endpoint is not connected"). - sleep(1); + // Verify the message by reading from BCB directly. + bootloader_message boot; + std::string err; + ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; - struct sockaddr_un un = {}; - un.sun_family = AF_UNIX; - strlcpy(un.sun_path, UNCRYPT_SOCKET.c_str(), sizeof(un.sun_path)); + if (isSetup) { + ASSERT_EQ("boot-recovery", std::string(boot.command)); + ASSERT_EQ(message_in_bcb, std::string(boot.recovery)); - int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); - ASSERT_NE(-1, sockfd); + // The rest of the boot.recovery message should be zero'd out. + ASSERT_LE(message_in_bcb.size(), sizeof(boot.recovery)); + size_t left = sizeof(boot.recovery) - message_in_bcb.size(); + ASSERT_EQ(std::string(left, '\0'), std::string(&boot.recovery[message_in_bcb.size()], left)); - // Connect to the uncrypt socket. - bool success = false; - for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) { - if (connect(sockfd, reinterpret_cast(&un), sizeof(struct sockaddr_un)) != 0) { - success = true; - break; + // Clear the BCB. + ASSERT_TRUE(clear_bootloader_message(&err)) << "Failed to clear BCB: " << err; + } else { + // All the bytes should be cleared. + ASSERT_EQ(std::string(sizeof(boot), '\0'), + std::string(reinterpret_cast(&boot), sizeof(boot))); } - sleep(1); } - ASSERT_TRUE(success); - // Check the status code from uncrypt. - int status; - ASSERT_TRUE(android::base::ReadFully(sockfd, &status, sizeof(int))); - ASSERT_EQ(100U, ntohl(status)); - - // Ack having received the status code. - int code = 0; - ASSERT_TRUE(android::base::WriteFully(sockfd, &code, sizeof(int))); + bool has_misc; +}; - ASSERT_EQ(0, close(sockfd)); +TEST_F(UncryptTest, setup_bcb) { + std::string message = "--update_message=abc value"; + std::string message_in_bcb = "recovery\n--update_message=abc value\n"; + SetupOrClearBcb(true, message, message_in_bcb); +} - ASSERT_TRUE(android::base::SetProperty("ctl.stop", "clear-bcb")); +TEST_F(UncryptTest, clear_bcb) { + SetupOrClearBcb(false, "", ""); +} - // Verify the content by reading from BCB directly. - bootloader_message boot; - std::string err; - ASSERT_TRUE(read_bootloader_message(&boot, &err)) << "Failed to read BCB: " << err; +TEST_F(UncryptTest, setup_bcb_wipe_ab) { + TemporaryFile wipe_package; + ASSERT_TRUE(android::base::WriteStringToFile(std::string(345, 'a'), wipe_package.path)); - // All the bytes should be cleared. - ASSERT_EQ(std::string(sizeof(boot), '\0'), - std::string(reinterpret_cast(&boot), sizeof(boot))); + // It's expected to store a wipe package in /misc, with the package size passed to recovery. + std::string message = + "--wipe_ab\n--wipe_package="s + wipe_package.path + "\n--reason=wipePackage"s; + std::string message_in_bcb = + "recovery\n--wipe_ab\n--wipe_package_size=345\n--reason=wipePackage\n"; + SetupOrClearBcb(true, message, message_in_bcb); } -- cgit v1.2.3 From 3ee2b9db5a5c9977c6bd726d0e5d12f5ac07d5cf Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 27 Mar 2017 14:12:26 -0700 Subject: Log temperature during OTA update Log the maximum temperature as well as the start/end temperature of an update to last_install. Check the temperature at the end of each block_image_update(verify). To get the maximum temp, we iterate through /sys/class/thermal/thermal_zone*/temp and find the maximum value present. Bug: 32518487 Test: temperature logs in last_install Change-Id: Iaf22a9fbc5b18611bbc5320ffea995417872e514 --- install.cpp | 52 ++++++++++++++++++++++++++++---- otautil/Android.mk | 12 +++++--- otautil/ThermalUtil.cpp | 80 +++++++++++++++++++++++++++++++++++++++++++++++++ otautil/ThermalUtil.h | 24 +++++++++++++++ 4 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 otautil/ThermalUtil.cpp create mode 100644 otautil/ThermalUtil.h diff --git a/install.cpp b/install.cpp index 7cef44a37..0a2fa3ca4 100644 --- a/install.cpp +++ b/install.cpp @@ -26,11 +26,15 @@ #include #include +#include #include +#include #include #include #include +#include #include +#include #include #include @@ -46,10 +50,13 @@ #include "error_code.h" #include "minui/minui.h" #include "otautil/SysUtil.h" +#include "otautil/ThermalUtil.h" #include "roots.h" #include "ui.h" #include "verifier.h" +using namespace std::chrono_literals; + #define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt"; static constexpr const char* AB_OTA_PAYLOAD = "payload.bin"; @@ -63,6 +70,8 @@ static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25; static constexpr float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; static constexpr float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; +static std::condition_variable finish_log_temperature; + // This function parses and returns the build.version.incremental static int parse_build_number(const std::string& str) { size_t pos = str.find('='); @@ -299,9 +308,19 @@ update_binary_command(const char* path, ZipArchiveHandle zip, int retry_count, } #endif // !AB_OTA_UPDATER +static void log_max_temperature(int* max_temperature) { + CHECK(max_temperature != nullptr); + std::mutex mtx; + std::unique_lock lck(mtx); + while (finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) { + *max_temperature = std::max(*max_temperature, GetMaxValueFromThermalZone()); + } +} + // If the package contains an update binary, extract it and run it. static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_cache, - std::vector& log_buffer, int retry_count) { + std::vector& log_buffer, int retry_count, + int* max_temperature) { read_source_target_build(zip, log_buffer); int pipefd[2]; @@ -392,6 +411,8 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_ } close(pipefd[1]); + std::thread temperature_logger(log_max_temperature, max_temperature); + *wipe_cache = false; bool retry_update = false; @@ -453,6 +474,10 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_ int status; waitpid(pid, &status, 0); + + finish_log_temperature.notify_one(); + temperature_logger.join(); + if (retry_update) { return INSTALL_RETRY; } @@ -466,7 +491,7 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_ static int really_install_package(const char *path, bool* wipe_cache, bool needs_mount, - std::vector& log_buffer, int retry_count) + std::vector& log_buffer, int retry_count, int* max_temperature) { ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); ui->Print("Finding update package...\n"); @@ -517,7 +542,7 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount, ui->Print("Retry attempt: %d\n", retry_count); } ui->SetEnableReboot(false); - int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count); + int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature); ui->SetEnableReboot(true); ui->Print("\n"); @@ -533,13 +558,17 @@ install_package(const char* path, bool* wipe_cache, const char* install_file, modified_flash = true; auto start = std::chrono::system_clock::now(); + int start_temperature = GetMaxValueFromThermalZone(); + int max_temperature = start_temperature; + int result; std::vector log_buffer; if (setup_install_mounts() != 0) { LOG(ERROR) << "failed to set up expected mounts for install; aborting"; result = INSTALL_ERROR; } else { - result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count); + result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count, + &max_temperature); } // Measure the time spent to apply OTA update in seconds. @@ -570,8 +599,21 @@ install_package(const char* path, bool* wipe_cache, const char* install_file, "time_total: " + std::to_string(time_total), "retry: " + std::to_string(retry_count), }; + + int end_temperature = GetMaxValueFromThermalZone(); + max_temperature = std::max(end_temperature, max_temperature); + if (start_temperature > 0) { + log_buffer.push_back("temperature_start: " + std::to_string(start_temperature)); + } + if (end_temperature > 0) { + log_buffer.push_back("temperature_end: " + std::to_string(end_temperature)); + } + if (max_temperature > 0) { + log_buffer.push_back("temperature_max: " + std::to_string(max_temperature)); + } + std::string log_content = android::base::Join(log_header, "\n") + "\n" + - android::base::Join(log_buffer, "\n"); + android::base::Join(log_buffer, "\n") + "\n"; if (!android::base::WriteStringToFile(log_content, install_file)) { PLOG(ERROR) << "failed to write " << install_file; } diff --git a/otautil/Android.mk b/otautil/Android.mk index e602f19ee..f7ca9a9ee 100644 --- a/otautil/Android.mk +++ b/otautil/Android.mk @@ -18,12 +18,16 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ SysUtil.cpp \ DirUtil.cpp \ - ZipUtil.cpp + ZipUtil.cpp \ + ThermalUtil.cpp -LOCAL_STATIC_LIBRARIES := libselinux libbase +LOCAL_STATIC_LIBRARIES := \ + libselinux \ + libbase LOCAL_MODULE := libotautil - -LOCAL_CFLAGS += -Werror -Wall +LOCAL_CFLAGS := \ + -Werror \ + -Wall include $(BUILD_STATIC_LIBRARY) diff --git a/otautil/ThermalUtil.cpp b/otautil/ThermalUtil.cpp new file mode 100644 index 000000000..13d36432a --- /dev/null +++ b/otautil/ThermalUtil.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2017 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 "ThermalUtil.h" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +static constexpr auto THERMAL_PREFIX = "/sys/class/thermal/"; + +static int thermal_filter(const dirent* de) { + if (android::base::StartsWith(de->d_name, "thermal_zone")) { + return 1; + } + return 0; +} + +static std::vector InitThermalPaths() { + dirent** namelist; + int n = scandir(THERMAL_PREFIX, &namelist, thermal_filter, alphasort); + if (n == -1) { + PLOG(ERROR) << "Failed to scandir " << THERMAL_PREFIX; + return {}; + } + if (n == 0) { + LOG(ERROR) << "Failed to find CPU thermal info in " << THERMAL_PREFIX; + return {}; + } + + std::vector thermal_paths; + while (n--) { + thermal_paths.push_back(THERMAL_PREFIX + std::string(namelist[n]->d_name) + "/temp"); + free(namelist[n]); + } + free(namelist); + return thermal_paths; +} + +int GetMaxValueFromThermalZone() { + static std::vector thermal_paths = InitThermalPaths(); + int max_temperature = -1; + for (const auto& path : thermal_paths) { + std::string content; + if (!android::base::ReadFileToString(path, &content)) { + PLOG(WARNING) << "Failed to read " << path; + continue; + } + + int temperature; + if (!android::base::ParseInt(android::base::Trim(content), &temperature)) { + LOG(WARNING) << "Failed to parse integer in " << content; + continue; + } + max_temperature = std::max(temperature, max_temperature); + } + LOG(INFO) << "current maximum temperature: " << max_temperature; + return max_temperature; +} \ No newline at end of file diff --git a/otautil/ThermalUtil.h b/otautil/ThermalUtil.h new file mode 100644 index 000000000..43ab55940 --- /dev/null +++ b/otautil/ThermalUtil.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef OTAUTIL_THERMALUTIL_H +#define OTAUTIL_THERMALUTIL_H + +// We can find the temperature reported by all sensors in /sys/class/thermal/thermal_zone*/temp. +// Their values are in millidegree Celsius; and we will log the maximum one. +int GetMaxValueFromThermalZone(); + +#endif // OTAUTIL_THERMALUTIL_H -- cgit v1.2.3 From c3c3496ab2e9bb26b12465bd723e11261e2afb38 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 28 Mar 2017 11:49:15 -0700 Subject: Suppress the unused variable warning in parser.yy The warning says: bootable/recovery/edify/parser.yy:123.3-9: warning: unused value: $1 Bug: 36678950 Test: mma Change-Id: Ic734c416a5a8f62fb5e516669102c1d2b4ad5083 --- edify/parser.yy | 3 +++ 1 file changed, 3 insertions(+) diff --git a/edify/parser.yy b/edify/parser.yy index 97205fe3b..b1685eb1f 100644 --- a/edify/parser.yy +++ b/edify/parser.yy @@ -23,6 +23,8 @@ #include #include +#include + #include "expr.h" #include "yydefs.h" #include "parser.h" @@ -121,6 +123,7 @@ arglist: /* empty */ { $$->emplace_back($1); } | arglist ',' expr { + UNUSED($1); $$->push_back(std::unique_ptr($3)); } ; -- cgit v1.2.3 From 545317f4fb99efd4d2c32187328e617ad6f69980 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Tue, 28 Mar 2017 13:24:56 -0700 Subject: update_verifier: raise priority and ioprio and start with exec_start Raise the priority and ioprio of update_verifier and launch with exec_start. This saves ~100ms of time before `class_start main` is executed. Bug: 36511808 Bug: 36102163 Test: Boot bullhead Test: Verify boottime decrease on sailfish Change-Id: I944a6c0d4368ead5b99171f49142da2523ed1bdd --- update_verifier/Android.mk | 2 ++ update_verifier/update_verifier.rc | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 update_verifier/update_verifier.rc diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk index c1051a54a..1acd5eca0 100644 --- a/update_verifier/Android.mk +++ b/update_verifier/Android.mk @@ -32,6 +32,8 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_CFLAGS := -Werror LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_INIT_RC := update_verifier.rc + ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1 endif diff --git a/update_verifier/update_verifier.rc b/update_verifier/update_verifier.rc new file mode 100644 index 000000000..fc8a64dee --- /dev/null +++ b/update_verifier/update_verifier.rc @@ -0,0 +1,11 @@ +service update_verifier_nonencrypted /system/bin/update_verifier nonencrypted + user root + class cache + priority -20 + ioprio rt 1 + +service update_verifier /system/bin/update_verifier ${vold.decrypt} + user root + class cache + priority -20 + ioprio rt 1 \ No newline at end of file -- cgit v1.2.3 From 28b79f797c1aacfffff89bf509f585d1f65287fa Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 28 Mar 2017 19:06:26 -0700 Subject: Fix the bad merge (duplicate tests). Test: mmma bootable/recovery Change-Id: I909164fed84fb17a7e1cb2427cb88208a69dc052 --- tests/component/verifier_test.cpp | 45 --------------------------------------- 1 file changed, 45 deletions(-) diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index ce5b075cf..4a3a981f7 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -177,51 +177,6 @@ TEST(VerifierTest, BadPackage_SignatureStartOutOfBounds) { package.size(), certs)); } -TEST(VerifierTest, BadPackage_AlteredFooter) { - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - TemporaryFile key_file1; - ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); - std::vector certs; - ASSERT_TRUE(load_keys(key_file1.path, certs)); - - std::string package; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); - ASSERT_EQ(std::string("\xc0\x06\xff\xff\xd2\x06", 6), package.substr(package.size() - 6, 6)); - - // Alter the footer. - package[package.size() - 5] = '\x05'; - ASSERT_EQ(VERIFY_FAILURE, - verify_file(reinterpret_cast(package.data()), package.size(), - certs)); -} - -TEST(VerifierTest, BadPackage_AlteredContent) { - std::string testkey_v3; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("testkey_v3.txt"), &testkey_v3)); - TemporaryFile key_file1; - ASSERT_TRUE(android::base::WriteStringToFile(testkey_v3, key_file1.path)); - std::vector certs; - ASSERT_TRUE(load_keys(key_file1.path, certs)); - - std::string package; - ASSERT_TRUE(android::base::ReadFileToString(from_testdata_base("otasigned_v3.zip"), &package)); - ASSERT_GT(package.size(), static_cast(100)); - - // Alter the content. - std::string altered1(package); - altered1[50] += 1; - ASSERT_EQ(VERIFY_FAILURE, - verify_file(reinterpret_cast(altered1.data()), altered1.size(), - certs)); - - std::string altered2(package); - altered2[10] += 1; - ASSERT_EQ(VERIFY_FAILURE, - verify_file(reinterpret_cast(altered2.data()), altered2.size(), - certs)); -} - TEST_P(VerifierSuccessTest, VerifySucceed) { ASSERT_EQ(verify_file(memmap.addr, memmap.length, certs, nullptr), VERIFY_SUCCESS); } -- cgit v1.2.3 From 5f85d1fb0a2f7dd85b2f1acabcce2d227a60b29c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 28 Mar 2017 21:12:36 -0700 Subject: Log the error message when failing to mount/umount. Test: Observe the error messaage for a umount failure case. Bug: 36686818 Change-Id: I28e335c2df4454dd0192f95e3909599fcc9dc1c0 --- Android.mk | 6 ++++-- mounts.cpp | 26 ++++++++++++++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/Android.mk b/Android.mk index 58b8a2240..037aa1673 100644 --- a/Android.mk +++ b/Android.mk @@ -29,9 +29,11 @@ include $(BUILD_STATIC_LIBRARY) # =============================== include $(CLEAR_VARS) LOCAL_SRC_FILES := mounts.cpp -LOCAL_CLANG := true -LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror +LOCAL_CFLAGS := \ + -Wall \ + -Werror LOCAL_MODULE := libmounts +LOCAL_STATIC_LIBRARIES := libbase include $(BUILD_STATIC_LIBRARY) # recovery (static executable) diff --git a/mounts.cpp b/mounts.cpp index f23376b06..fbcbac014 100644 --- a/mounts.cpp +++ b/mounts.cpp @@ -27,6 +27,8 @@ #include #include +#include + struct MountedVolume { std::string device; std::string mount_point; @@ -75,15 +77,23 @@ MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) { } int unmount_mounted_volume(MountedVolume* volume) { - // Intentionally pass the empty string to umount if the caller tries - // to unmount a volume they already unmounted using this - // function. - std::string mount_point = volume->mount_point; - volume->mount_point.clear(); - return umount(mount_point.c_str()); + // Intentionally pass the empty string to umount if the caller tries to unmount a volume they + // already unmounted using this function. + std::string mount_point = volume->mount_point; + volume->mount_point.clear(); + int result = umount(mount_point.c_str()); + if (result == -1) { + PLOG(WARNING) << "Failed to umount " << mount_point; + } + return result; } int remount_read_only(MountedVolume* volume) { - return mount(volume->device.c_str(), volume->mount_point.c_str(), volume->filesystem.c_str(), - MS_NOATIME | MS_NODEV | MS_NODIRATIME | MS_RDONLY | MS_REMOUNT, 0); + int result = mount(volume->device.c_str(), volume->mount_point.c_str(), + volume->filesystem.c_str(), + MS_NOATIME | MS_NODEV | MS_NODIRATIME | MS_RDONLY | MS_REMOUNT, 0); + if (result == -1) { + PLOG(WARNING) << "Failed to remount read-only " << volume->mount_point; + } + return result; } -- cgit v1.2.3 From 7368316de17b14260a4c3bb2b0b79354163ce12f Mon Sep 17 00:00:00 2001 From: Dan Shi Date: Tue, 28 Mar 2017 17:22:41 -0700 Subject: Add test config to minadbd_test Design doc: Generalized Suites & the Unification of APCT & CTS Workflows Design/Roadmap https://docs.google.com/document/d/1eabK3srlBLouMiBMrNP3xJPiRRdcoCquNxC8gBWPvx8/edit#heading=h.78vup5eivwzo Details about test configs changes are tracked in doc https://docs.google.com/document/d/1EWUjJ7fjy8ge_Nk0YQbFdRp8DSHo3z6GU0R8jLgrAcw/edit# Bug: 35882476 Test: local test Change-Id: I51e1b410536469d254ae7a353bc61a7df06c8324 --- minadbd/Android.mk | 1 + minadbd/AndroidTest.xml | 26 ++++++++++++++++++++++++++ tests/Android.mk | 2 ++ tests/AndroidTest.xml | 31 +++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+) create mode 100644 minadbd/AndroidTest.xml create mode 100644 tests/AndroidTest.xml diff --git a/minadbd/Android.mk b/minadbd/Android.mk index 7eef13ee0..de0b0c890 100644 --- a/minadbd/Android.mk +++ b/minadbd/Android.mk @@ -29,6 +29,7 @@ include $(CLEAR_VARS) LOCAL_CLANG := true LOCAL_MODULE := minadbd_test +LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_SRC_FILES := fuse_adb_provider_test.cpp LOCAL_CFLAGS := $(minadbd_cflags) LOCAL_C_INCLUDES := $(LOCAL_PATH) system/core/adb diff --git a/minadbd/AndroidTest.xml b/minadbd/AndroidTest.xml new file mode 100644 index 000000000..7ea235b7c --- /dev/null +++ b/minadbd/AndroidTest.xml @@ -0,0 +1,26 @@ + + + + + + \ No newline at end of file diff --git a/tests/Android.mk b/tests/Android.mk index ff6e14c9b..a8f1d7874 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -20,6 +20,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_CFLAGS := -Werror LOCAL_MODULE := recovery_unit_test +LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_STATIC_LIBRARIES := \ libverifier \ @@ -87,6 +88,7 @@ LOCAL_CFLAGS := \ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE := recovery_component_test +LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_C_INCLUDES := bootable/recovery LOCAL_SRC_FILES := \ component/applypatch_test.cpp \ diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml new file mode 100644 index 000000000..3999aa57d --- /dev/null +++ b/tests/AndroidTest.xml @@ -0,0 +1,31 @@ + + + + + + -- cgit v1.2.3 From 84a06485a94f6d2bfe78dbe8b69c517e542a0ce9 Mon Sep 17 00:00:00 2001 From: Bowgo Tsai Date: Wed, 29 Mar 2017 15:13:58 +0800 Subject: recovery: switch to fs_mgr_read_fstab_default() fs_mgr_read_fstab_default() is now aware of recovery mode so it's ok to switch to this new API. Bug: 35811655 Test: recovery boot sailfish Change-Id: I1025b606fd96e74a893ae99cefc1da01e4b27a23 --- roots.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roots.cpp b/roots.cpp index 5ba53c245..6e5ef9810 100644 --- a/roots.cpp +++ b/roots.cpp @@ -44,9 +44,9 @@ void load_volume_table() int i; int ret; - fstab = fs_mgr_read_fstab_with_dt("/etc/recovery.fstab"); + fstab = fs_mgr_read_fstab_default(); if (!fstab) { - LOG(ERROR) << "failed to read /etc/recovery.fstab"; + LOG(ERROR) << "failed to read default fstab"; return; } -- cgit v1.2.3 From 60a70afc0a79048b0fa0813862ee9fb31fd11dd4 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 26 Mar 2017 14:03:52 -0700 Subject: updater: Move RangeSinkWrite into RangeSinkState. Then rename RangeSinkState to RangeSinkWriter. RangeSinkWriter reads data from the given FD, and writes them to the desination RangeSet. Test: Apply an incremental with the new updater. Change-Id: I5e3ab6fc082efa1726562c55b56e2d418fe4acaf --- updater/blockimg.cpp | 310 ++++++++++++++++++++++++--------------------------- 1 file changed, 146 insertions(+), 164 deletions(-) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 8c0f885a1..e39fac30d 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -231,125 +231,135 @@ static void allocate(size_t size, std::vector& buffer) { buffer.resize(size); } -struct RangeSinkState { - explicit RangeSinkState(RangeSet& rs) : tgt(rs) { }; - - int fd; - const RangeSet& tgt; - size_t p_block; - size_t p_remain; -}; +/** + * RangeSinkWriter reads data from the given FD, and writes them to the destination specified by the + * given RangeSet. + */ +class RangeSinkWriter { + public: + RangeSinkWriter(int fd, const RangeSet& tgt) + : fd_(fd), tgt_(tgt), next_range_(0), current_range_left_(0) { + CHECK_NE(tgt.count, static_cast(0)); + }; -static size_t RangeSinkWrite(const uint8_t* data, size_t size, RangeSinkState* rss) { - if (rss->p_remain == 0) { - LOG(ERROR) << "range sink write overrun"; - return 0; + bool Finished() const { + return next_range_ == tgt_.count && current_range_left_ == 0; } - size_t written = 0; - while (size > 0) { - size_t write_now = size; - - if (rss->p_remain < write_now) { - write_now = rss->p_remain; - } - - if (write_all(rss->fd, data, write_now) == -1) { - break; + size_t Write(const uint8_t* data, size_t size) { + if (Finished()) { + LOG(ERROR) << "range sink write overrun; can't write " << size << " bytes"; + return 0; } - data += write_now; - size -= write_now; - - rss->p_remain -= write_now; - written += write_now; - - if (rss->p_remain == 0) { - // Move to the next block. - ++rss->p_block; - if (rss->p_block < rss->tgt.count) { - rss->p_remain = - (rss->tgt.pos[rss->p_block * 2 + 1] - rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; - - off64_t offset = static_cast(rss->tgt.pos[rss->p_block * 2]) * BLOCKSIZE; - if (!discard_blocks(rss->fd, offset, rss->p_remain)) { + size_t written = 0; + while (size > 0) { + // Move to the next range as needed. + if (current_range_left_ == 0) { + if (next_range_ < tgt_.count) { + off64_t offset = static_cast(tgt_.pos[next_range_ * 2]) * BLOCKSIZE; + current_range_left_ = + (tgt_.pos[next_range_ * 2 + 1] - tgt_.pos[next_range_ * 2]) * BLOCKSIZE; + next_range_++; + if (!discard_blocks(fd_, offset, current_range_left_)) { + break; + } + + if (!check_lseek(fd_, offset, SEEK_SET)) { + break; + } + } else { + // We can't write any more; return how many bytes have been written so far. break; } + } - if (!check_lseek(rss->fd, offset, SEEK_SET)) { - break; - } + size_t write_now = size; + if (current_range_left_ < write_now) { + write_now = current_range_left_; + } - } else { - // We can't write any more; return how many bytes have been written so far. + if (write_all(fd_, data, write_now) == -1) { break; } + + data += write_now; + size -= write_now; + + current_range_left_ -= write_now; + written += write_now; } - } - return written; -} + return written; + } -// All of the data for all the 'new' transfers is contained in one -// file in the update package, concatenated together in the order in -// which transfers.list will need it. We want to stream it out of the -// archive (it's compressed) without writing it to a temp file, but we -// can't write each section until it's that transfer's turn to go. -// -// To achieve this, we expand the new data from the archive in a -// background thread, and block that threads 'receive uncompressed -// data' function until the main thread has reached a point where we -// want some new data to be written. We signal the background thread -// with the destination for the data and block the main thread, -// waiting for the background thread to complete writing that section. -// Then it signals the main thread to wake up and goes back to -// blocking waiting for a transfer. -// -// NewThreadInfo is the struct used to pass information back and forth -// between the two threads. When the main thread wants some data -// written, it sets rss to the destination location and signals the -// condition. When the background thread is done writing, it clears -// rss and signals the condition again. + private: + // The input data. + int fd_; + // The destination for the data. + const RangeSet& tgt_; + // The next range that we should write to. + size_t next_range_; + // The number of bytes to write before moving to the next range. + size_t current_range_left_; +}; +/** + * All of the data for all the 'new' transfers is contained in one file in the update package, + * concatenated together in the order in which transfers.list will need it. We want to stream it out + * of the archive (it's compressed) without writing it to a temp file, but we can't write each + * section until it's that transfer's turn to go. + * + * To achieve this, we expand the new data from the archive in a background thread, and block that + * threads 'receive uncompressed data' function until the main thread has reached a point where we + * want some new data to be written. We signal the background thread with the destination for the + * data and block the main thread, waiting for the background thread to complete writing that + * section. Then it signals the main thread to wake up and goes back to blocking waiting for a + * transfer. + * + * NewThreadInfo is the struct used to pass information back and forth between the two threads. When + * the main thread wants some data written, it sets writer to the destination location and signals + * the condition. When the background thread is done writing, it clears writer and signals the + * condition again. + */ struct NewThreadInfo { - ZipArchiveHandle za; - ZipEntry entry; + ZipArchiveHandle za; + ZipEntry entry; - RangeSinkState* rss; + RangeSinkWriter* writer; - pthread_mutex_t mu; - pthread_cond_t cv; + pthread_mutex_t mu; + pthread_cond_t cv; }; static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { - NewThreadInfo* nti = reinterpret_cast(cookie); + NewThreadInfo* nti = static_cast(cookie); - while (size > 0) { - // Wait for nti->rss to be non-null, indicating some of this - // data is wanted. - pthread_mutex_lock(&nti->mu); - while (nti->rss == nullptr) { - pthread_cond_wait(&nti->cv, &nti->mu); - } - pthread_mutex_unlock(&nti->mu); + while (size > 0) { + // Wait for nti->writer to be non-null, indicating some of this data is wanted. + pthread_mutex_lock(&nti->mu); + while (nti->writer == nullptr) { + pthread_cond_wait(&nti->cv, &nti->mu); + } + pthread_mutex_unlock(&nti->mu); - // At this point nti->rss is set, and we own it. The main - // thread is waiting for it to disappear from nti. - size_t written = RangeSinkWrite(data, size, nti->rss); - data += written; - size -= written; + // At this point nti->writer is set, and we own it. The main thread is waiting for it to + // disappear from nti. + size_t written = nti->writer->Write(data, size); + data += written; + size -= written; - if (nti->rss->p_block == nti->rss->tgt.count) { - // we have written all the bytes desired by this rss. + if (nti->writer->Finished()) { + // We have written all the bytes desired by this writer. - pthread_mutex_lock(&nti->mu); - nti->rss = nullptr; - pthread_cond_broadcast(&nti->cv); - pthread_mutex_unlock(&nti->mu); - } + pthread_mutex_lock(&nti->mu); + nti->writer = nullptr; + pthread_cond_broadcast(&nti->cv); + pthread_mutex_unlock(&nti->mu); } + } - return true; + return true; } static void* unzip_new_data(void* cookie) { @@ -380,28 +390,26 @@ static int ReadBlocks(const RangeSet& src, std::vector& buffer, int fd) } static int WriteBlocks(const RangeSet& tgt, const std::vector& buffer, int fd) { - const uint8_t* data = buffer.data(); - - size_t p = 0; - for (size_t i = 0; i < tgt.count; ++i) { - off64_t offset = static_cast(tgt.pos[i * 2]) * BLOCKSIZE; - size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE; - if (!discard_blocks(fd, offset, size)) { - return -1; - } - - if (!check_lseek(fd, offset, SEEK_SET)) { - return -1; - } + size_t written = 0; + for (size_t i = 0; i < tgt.count; ++i) { + off64_t offset = static_cast(tgt.pos[i * 2]) * BLOCKSIZE; + size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE; + if (!discard_blocks(fd, offset, size)) { + return -1; + } - if (write_all(fd, data + p, size) == -1) { - return -1; - } + if (!check_lseek(fd, offset, SEEK_SET)) { + return -1; + } - p += size; + if (write_all(fd, buffer.data() + written, size) == -1) { + return -1; } - return 0; + written += size; + } + + return 0; } // Parameters for transfer list command functions @@ -1214,45 +1222,31 @@ static int PerformCommandZero(CommandParameters& params) { } static int PerformCommandNew(CommandParameters& params) { + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing target blocks for new"; + return -1; + } - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing target blocks for new"; - return -1; - } - - RangeSet tgt = parse_range(params.tokens[params.cpos++]); - - if (params.canwrite) { - LOG(INFO) << " writing " << tgt.size << " blocks of new data"; - - RangeSinkState rss(tgt); - rss.fd = params.fd; - rss.p_block = 0; - rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; - - off64_t offset = static_cast(tgt.pos[0]) * BLOCKSIZE; - if (!discard_blocks(params.fd, offset, tgt.size * BLOCKSIZE)) { - return -1; - } - - if (!check_lseek(params.fd, offset, SEEK_SET)) { - return -1; - } + RangeSet tgt = parse_range(params.tokens[params.cpos++]); - pthread_mutex_lock(¶ms.nti.mu); - params.nti.rss = &rss; - pthread_cond_broadcast(¶ms.nti.cv); + if (params.canwrite) { + LOG(INFO) << " writing " << tgt.size << " blocks of new data"; - while (params.nti.rss) { - pthread_cond_wait(¶ms.nti.cv, ¶ms.nti.mu); - } + RangeSinkWriter writer(params.fd, tgt); + pthread_mutex_lock(¶ms.nti.mu); + params.nti.writer = &writer; + pthread_cond_broadcast(¶ms.nti.cv); - pthread_mutex_unlock(¶ms.nti.mu); + while (params.nti.writer != nullptr) { + pthread_cond_wait(¶ms.nti.cv, ¶ms.nti.mu); } - params.written += tgt.size; + pthread_mutex_unlock(¶ms.nti.mu); + } - return 0; + params.written += tgt.size; + + return 0; } static int PerformCommandDiff(CommandParameters& params) { @@ -1295,40 +1289,28 @@ static int PerformCommandDiff(CommandParameters& params) { LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; Value patch_value( VAL_BLOB, std::string(reinterpret_cast(params.patch_start + offset), len)); - RangeSinkState rss(tgt); - rss.fd = params.fd; - rss.p_block = 0; - rss.p_remain = (tgt.pos[1] - tgt.pos[0]) * BLOCKSIZE; - - off64_t offset = static_cast(tgt.pos[0]) * BLOCKSIZE; - if (!discard_blocks(params.fd, offset, rss.p_remain)) { - return -1; - } - - if (!check_lseek(params.fd, offset, SEEK_SET)) { - return -1; - } + RangeSinkWriter writer(params.fd, tgt); if (params.cmdname[0] == 'i') { // imgdiff - if (ApplyImagePatch( - params.buffer.data(), blocks * BLOCKSIZE, &patch_value, - std::bind(&RangeSinkWrite, std::placeholders::_1, std::placeholders::_2, &rss), - nullptr, nullptr) != 0) { + if (ApplyImagePatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, + std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1, + std::placeholders::_2), + nullptr, nullptr) != 0) { LOG(ERROR) << "Failed to apply image patch."; return -1; } } else { - if (ApplyBSDiffPatch( - params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0, - std::bind(&RangeSinkWrite, std::placeholders::_1, std::placeholders::_2, &rss), - nullptr) != 0) { + if (ApplyBSDiffPatch(params.buffer.data(), blocks * BLOCKSIZE, &patch_value, 0, + std::bind(&RangeSinkWriter::Write, &writer, std::placeholders::_1, + std::placeholders::_2), + nullptr) != 0) { LOG(ERROR) << "Failed to apply bsdiff patch."; return -1; } } // We expect the output of the patcher to fill the tgt ranges exactly. - if (rss.p_block != tgt.count || rss.p_remain != 0) { + if (!writer.Finished()) { LOG(ERROR) << "range sink underrun?"; } } else { -- cgit v1.2.3 From 31b6bc5cedd2025139afced9ad8d22a46eb21a61 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 29 Mar 2017 23:03:37 -0700 Subject: tests: Remove LOCAL_ADDITIONAL_DEPENDENCIES. Per the comment in build/make/core/base_rules.mk: Ninja has an implicit dependency on the command being run, and kati will regenerate the ninja manifest if any read makefile changes, so there is no need to have dependencies on makefiles. Test: mmma bootable/recovery Change-Id: I27b97df10d40f39ad966be70b33811175a665439 --- tests/Android.mk | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/Android.mk b/tests/Android.mk index a8f1d7874..80eae8f37 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -21,7 +21,6 @@ include $(CLEAR_VARS) LOCAL_CFLAGS := -Werror LOCAL_MODULE := recovery_unit_test LOCAL_COMPATIBILITY_SUITE := device-tests -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_STATIC_LIBRARIES := \ libverifier \ libminui \ @@ -46,10 +45,8 @@ include $(BUILD_NATIVE_TEST) # Manual tests include $(CLEAR_VARS) -LOCAL_CLANG := true LOCAL_CFLAGS := -Werror LOCAL_MODULE := recovery_manual_test -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_STATIC_LIBRARIES := \ libminui \ libbase @@ -86,7 +83,6 @@ LOCAL_CFLAGS := \ -Werror \ -D_FILE_OFFSET_BITS=64 -LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk LOCAL_MODULE := recovery_component_test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_C_INCLUDES := bootable/recovery -- cgit v1.2.3 From 0bbc764bbe8fc04eba1bf708fbf97526950cbeef Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 29 Mar 2017 23:57:47 -0700 Subject: updater: Don't append newline when calling uiPrint(). LOG(INFO) already appends a newline. Don't print redundant newline. Test: No extra blank lines when calling ui_print(). And on-screen UI shows the same. Change-Id: I74e9a8504a7146a6cb3dae02fe2406d0dd54069b --- updater/blockimg.cpp | 3 ++- updater/install.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 8c0f885a1..4409cbefe 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1831,7 +1832,7 @@ Value* CheckFirstBlockFn(const char* name, State* state, uint16_t mount_count = *reinterpret_cast(&block0_buffer[0x400+0x34]); if (mount_count > 0) { - uiPrintf(state, "Device was remounted R/W %d times\n", mount_count); + uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count); uiPrintf(state, "Last remount happened on %s", ctime(&mount_time)); } diff --git a/updater/install.cpp b/updater/install.cpp index f91f3fc9f..857d7f1e0 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -181,8 +181,8 @@ Value* MountFn(const char* name, State* state, const std::vector>& argv) { std::vector args; if (!ReadArgs(state, argv, &args)) { - return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); + return ErrorAbort(state, kArgsParsingFailure, "%s(): Failed to parse the argument(s)", name); } - std::string buffer = android::base::Join(args, "") + "\n"; + std::string buffer = android::base::Join(args, ""); uiPrint(state, buffer); return StringValue(buffer); } -- cgit v1.2.3 From 3958a95f54f3039da80394911f64e5a4857f219d Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 1 Mar 2017 15:31:25 -0800 Subject: Update_verifier should read blocks in EIO mode Update_verifier will reboot the device if it fails to read some blocks on the care_map when veritymode=eio. Also make some partition name changes to match the care_map.txt. Test: Update_verifier reboots the device after read failures in eio mode. Change-Id: Icf68e6151dee72f626a9ab72946100cf482a4e6c --- update_verifier/update_verifier.cpp | 59 +++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index 83b1c46c4..72b6dccc5 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -19,9 +19,14 @@ * update. It gets invoked by init, and will only perform the verification if * it's the first boot post an A/B OTA update. * - * It relies on dm-verity to capture any corruption on the partitions being - * verified. dm-verity must be in enforcing mode, so that it will reboot the - * device on dm-verity failures. When that happens, the bootloader should + * Update_verifier relies on dm-verity to capture any corruption on the partitions + * being verified. And its behavior varies depending on the dm-verity mode. + * Upon detection of failures: + * enforcing mode: dm-verity reboots the device + * eio mode: dm-verity fails the read and update_verifier reboots the device + * other mode: not supported and update_verifier reboots the device + * + * After a predefined number of failing boot attempts, the bootloader should * mark the slot as unbootable and stops trying. Other dm-verity modes ( * for example, veritymode=EIO) are not accepted and simply lead to a * verification failure. @@ -35,6 +40,7 @@ #include #include #include +#include #include #include @@ -46,6 +52,7 @@ #include #include #include +#include using android::sp; using android::hardware::boot::V1_0::IBootControl; @@ -66,18 +73,9 @@ static int dm_name_filter(const dirent* de) { return 0; } -static bool read_blocks(const std::string& blk_device, const std::string& range_str) { - // Parse the partition in the end of the block_device string. - // Here is one example: "/dev/block/bootdevice/by-name/system" - std::string partition; - if (android::base::EndsWith(blk_device, "system")) { - partition = "system"; - } else if (android::base::EndsWith(blk_device, "vendor")) { - partition = "vendor"; - } else { - LOG(ERROR) << "Failed to parse partition string in " << blk_device; - return false; - } +static bool read_blocks(const std::string& partition, const std::string& range_str) { + CHECK(partition == "system" || partition == "vendor") + << "partition name should be system or vendor" << partition; // Iterate the content of "/sys/block/dm-X/dm/name". If it matches "system" // (or "vendor"), then dm-X is a dm-wrapped system/vendor partition. @@ -172,7 +170,7 @@ static bool verify_image(const std::string& care_map_name) { return true; } // Care map file has four lines (two lines if vendor partition is not present): - // First line has the block device name, e.g./dev/block/.../by-name/system. + // First line has the block partition name (system/vendor). // Second line holds all ranges of blocks to verify. // The next two lines have the same format but for vendor partition. std::string file_content; @@ -198,6 +196,14 @@ static bool verify_image(const std::string& care_map_name) { return true; } +static int reboot_device() { + if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) == -1) { + LOG(ERROR) << "Failed to reboot."; + return -1; + } + while (true) pause(); +} + int main(int argc, char** argv) { for (int i = 1; i < argc; i++) { LOG(INFO) << "Started with arg " << i << ": " << argv[i]; @@ -206,7 +212,7 @@ int main(int argc, char** argv) { sp module = IBootControl::getService(); if (module == nullptr) { LOG(ERROR) << "Error getting bootctrl module."; - return -1; + return reboot_device(); } uint32_t current_slot = module->getCurrentSlot(); @@ -221,18 +227,19 @@ int main(int argc, char** argv) { std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); if (verity_mode.empty()) { LOG(ERROR) << "Failed to get dm-verity mode."; - return -1; + return reboot_device(); } else if (android::base::EqualsIgnoreCase(verity_mode, "eio")) { - // We shouldn't see verity in EIO mode if the current slot hasn't booted - // successfully before. Therefore, fail the verification when veritymode=eio. - LOG(ERROR) << "Found dm-verity in EIO mode, skip verification."; - return -1; + // We shouldn't see verity in EIO mode if the current slot hasn't booted successfully before. + // Continue the verification until we fail to read some blocks. + LOG(WARNING) << "Found dm-verity in EIO mode."; } else if (verity_mode != "enforcing") { LOG(ERROR) << "Unexpected dm-verity mode : " << verity_mode << ", expecting enforcing."; - return -1; - } else if (!verify_image(CARE_MAP_FILE)) { + return reboot_device(); + } + + if (!verify_image(CARE_MAP_FILE)) { LOG(ERROR) << "Failed to verify all blocks in care map file."; - return -1; + return reboot_device(); } #else LOG(WARNING) << "dm-verity not enabled; marking without verification."; @@ -242,7 +249,7 @@ int main(int argc, char** argv) { module->markBootSuccessful([&cr](CommandResult result) { cr = result; }); if (!cr.success) { LOG(ERROR) << "Error marking booted successfully: " << cr.errMsg; - return -1; + return reboot_device(); } LOG(INFO) << "Marked slot " << current_slot << " as booted successfully."; } -- cgit v1.2.3 From 8f23757ad42352f57db0b2ab9fc8b6e92eeaedd9 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 26 Mar 2017 13:36:49 -0700 Subject: Move parse_range() and range_overlaps() into RangeSet. Also move RangeSet into a header file to make it testable, and add unit tests. In RangeSet::Parse() (the former parse_range()), use libbase logging to do assertions. This has the same effect as the previous exit(EXIT_FAILURE) to terminate the updater process and abort an update. The difference lies in the exit status code (i.e. WEXITSTATUS(status) in install.cpp), which changes from 1 (i.e. EXIT_FAILURE) to 0. Test: recovery_unit_test Test: Apply an incremental update with the new updater. Change-Id: Ie8393c78b0d8ae0fd5f0ca0646d871308d71fff0 --- tests/Android.mk | 2 + tests/unit/rangeset_test.cpp | 84 ++++++++++++++++++++++++ updater/blockimg.cpp | 128 ++++++------------------------------- updater/include/updater/rangeset.h | 95 +++++++++++++++++++++++++++ 4 files changed, 199 insertions(+), 110 deletions(-) create mode 100644 tests/unit/rangeset_test.cpp create mode 100644 updater/include/updater/rangeset.h diff --git a/tests/Android.mk b/tests/Android.mk index 80eae8f37..974aa0e2d 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -25,6 +25,7 @@ LOCAL_STATIC_LIBRARIES := \ libverifier \ libminui \ libotautil \ + libupdater \ libziparchive \ libutils \ libz \ @@ -35,6 +36,7 @@ LOCAL_SRC_FILES := \ unit/asn1_decoder_test.cpp \ unit/dirutil_test.cpp \ unit/locale_test.cpp \ + unit/rangeset_test.cpp \ unit/sysutil_test.cpp \ unit/zip_test.cpp \ unit/ziputil_test.cpp diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp new file mode 100644 index 000000000..e66da20e4 --- /dev/null +++ b/tests/unit/rangeset_test.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2017 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 "updater/rangeset.h" + +TEST(RangeSetTest, Parse_smoke) { + RangeSet rs = RangeSet::Parse("2,1,10"); + ASSERT_EQ(static_cast(1), rs.count); + ASSERT_EQ((std::vector{ 1, 10 }), rs.pos); + ASSERT_EQ(static_cast(9), rs.size); + + RangeSet rs2 = RangeSet::Parse("4,15,20,1,10"); + ASSERT_EQ(static_cast(2), rs2.count); + ASSERT_EQ((std::vector{ 15, 20, 1, 10 }), rs2.pos); + ASSERT_EQ(static_cast(14), rs2.size); + + // Leading zeros are fine. But android::base::ParseUint() doesn't like trailing zeros like "10 ". + ASSERT_EQ(rs, RangeSet::Parse(" 2, 1, 10")); + ASSERT_EXIT(RangeSet::Parse("2,1,10 "), ::testing::KilledBySignal(SIGABRT), ""); +} + +TEST(RangeSetTest, Parse_InvalidCases) { + // Insufficient number of tokens. + ASSERT_EXIT(RangeSet::Parse(""), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_EXIT(RangeSet::Parse("2,1"), ::testing::KilledBySignal(SIGABRT), ""); + + // The first token (i.e. the number of following tokens) is invalid. + ASSERT_EXIT(RangeSet::Parse("a,1,1"), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_EXIT(RangeSet::Parse("3,1,1"), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_EXIT(RangeSet::Parse("-3,1,1"), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_EXIT(RangeSet::Parse("2,1,2,3"), ::testing::KilledBySignal(SIGABRT), ""); + + // Invalid tokens. + ASSERT_EXIT(RangeSet::Parse("2,1,10a"), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_EXIT(RangeSet::Parse("2,,10"), ::testing::KilledBySignal(SIGABRT), ""); + + // Empty or negative range. + ASSERT_EXIT(RangeSet::Parse("2,2,2"), ::testing::KilledBySignal(SIGABRT), ""); + ASSERT_EXIT(RangeSet::Parse("2,2,1"), ::testing::KilledBySignal(SIGABRT), ""); +} + +TEST(RangeSetTest, Overlaps) { + RangeSet r1 = RangeSet::Parse("2,1,6"); + RangeSet r2 = RangeSet::Parse("2,5,10"); + ASSERT_TRUE(r1.Overlaps(r2)); + ASSERT_TRUE(r2.Overlaps(r1)); + + r2 = RangeSet::Parse("2,6,10"); + ASSERT_FALSE(r1.Overlaps(r2)); + ASSERT_FALSE(r2.Overlaps(r1)); + + ASSERT_FALSE(RangeSet::Parse("2,3,5").Overlaps(RangeSet::Parse("2,5,7"))); + ASSERT_FALSE(RangeSet::Parse("2,5,7").Overlaps(RangeSet::Parse("2,3,5"))); +} + +TEST(RangeSetTest, GetBlockNumber) { + RangeSet rs = RangeSet::Parse("2,1,10"); + ASSERT_EQ(static_cast(1), rs.GetBlockNumber(0)); + ASSERT_EQ(static_cast(6), rs.GetBlockNumber(5)); + ASSERT_EQ(static_cast(9), rs.GetBlockNumber(8)); + + // Out of bound. + ASSERT_EXIT(rs.GetBlockNumber(9), ::testing::KilledBySignal(SIGABRT), ""); +} diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index a1a5773d4..fc7a561eb 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -50,9 +50,10 @@ #include "edify/expr.h" #include "error_code.h" -#include "updater/install.h" #include "ota_io.h" #include "print_sha1.h" +#include "updater/install.h" +#include "updater/rangeset.h" #include "updater/updater.h" // Set this to 0 to interpret 'erase' transfers to mean do a @@ -65,100 +66,10 @@ static constexpr const char* STASH_DIRECTORY_BASE = "/cache/recovery"; static constexpr mode_t STASH_DIRECTORY_MODE = 0700; static constexpr mode_t STASH_FILE_MODE = 0600; -struct RangeSet { - size_t count; // Limit is INT_MAX. - size_t size; - std::vector pos; // Actual limit is INT_MAX. - - // Get the block number for the ith(starting from 0) block in the range set. - int get_block(size_t idx) const { - if (idx >= size) { - LOG(ERROR) << "index: " << idx << " is greater than range set size: " << size; - return -1; - } - for (size_t i = 0; i < pos.size(); i += 2) { - if (idx < pos[i + 1] - pos[i]) { - return pos[i] + idx; - } - idx -= (pos[i + 1] - pos[i]); - } - return -1; - } -}; - static CauseCode failure_type = kNoCause; static bool is_retry = false; static std::unordered_map stash_map; -static RangeSet parse_range(const std::string& range_text) { - RangeSet rs; - - std::vector pieces = android::base::Split(range_text, ","); - if (pieces.size() < 3) { - goto err; - } - - size_t num; - if (!android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) { - goto err; - } - - if (num == 0 || num % 2) { - goto err; // must be even - } else if (num != pieces.size() - 1) { - goto err; - } - - rs.pos.resize(num); - rs.count = num / 2; - rs.size = 0; - - for (size_t i = 0; i < num; i += 2) { - if (!android::base::ParseUint(pieces[i + 1], &rs.pos[i], static_cast(INT_MAX))) { - goto err; - } - - if (!android::base::ParseUint(pieces[i + 2], &rs.pos[i + 1], static_cast(INT_MAX))) { - goto err; - } - - if (rs.pos[i] >= rs.pos[i + 1]) { - goto err; // empty or negative range - } - - size_t sz = rs.pos[i + 1] - rs.pos[i]; - if (rs.size > SIZE_MAX - sz) { - goto err; // overflow - } - - rs.size += sz; - } - - return rs; - -err: - LOG(ERROR) << "failed to parse range '" << range_text << "'"; - exit(EXIT_FAILURE); -} - -static bool range_overlaps(const RangeSet& r1, const RangeSet& r2) { - for (size_t i = 0; i < r1.count; ++i) { - size_t r1_0 = r1.pos[i * 2]; - size_t r1_1 = r1.pos[i * 2 + 1]; - - for (size_t j = 0; j < r2.count; ++j) { - size_t r2_0 = r2.pos[j * 2]; - size_t r2_1 = r2.pos[j * 2 + 1]; - - if (!(r2_0 >= r1_1 || r1_0 >= r2_1)) { - return true; - } - } - } - - return false; -} - static int read_all(int fd, uint8_t* data, size_t size) { size_t so_far = 0; while (so_far < size) { @@ -469,7 +380,7 @@ static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params, return; } - RangeSet src = parse_range(params.tokens[pos++]); + RangeSet src = RangeSet::Parse(params.tokens[pos++]); RangeSet locs; // If there's no stashed blocks, content in the buffer is consecutive and has the same @@ -483,17 +394,15 @@ static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params, // Example: for the tokens <4,63946,63947,63948,63979> <4,6,7,8,39> ; // We want to print SHA-1 for the data in buffer[6], buffer[8], buffer[9] ... buffer[38]; // this corresponds to the 32 src blocks #63946, #63948, #63949 ... #63978. - locs = parse_range(params.tokens[pos++]); + locs = RangeSet::Parse(params.tokens[pos++]); CHECK_EQ(src.size, locs.size); CHECK_EQ(locs.pos.size() % 2, static_cast(0)); } LOG(INFO) << "printing hash in hex for " << src.size << " source blocks"; for (size_t i = 0; i < src.size; i++) { - int block_num = src.get_block(i); - CHECK_NE(block_num, -1); - int buffer_index = locs.get_block(i); - CHECK_NE(buffer_index, -1); + size_t block_num = src.GetBlockNumber(i); + size_t buffer_index = locs.GetBlockNumber(i); CHECK_LE((buffer_index + 1) * BLOCKSIZE, buffer.size()); uint8_t digest[SHA_DIGEST_LENGTH]; @@ -512,8 +421,7 @@ static void PrintHashForCorruptedStashedBlocks(const std::string& id, CHECK_EQ(src.size * BLOCKSIZE, buffer.size()); for (size_t i = 0; i < src.size; i++) { - int block_num = src.get_block(i); - CHECK_NE(block_num, -1); + size_t block_num = src.GetBlockNumber(i); uint8_t digest[SHA_DIGEST_LENGTH]; SHA1(buffer.data() + i * BLOCKSIZE, BLOCKSIZE, digest); @@ -925,8 +833,8 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size // no source ranges, only stashes params.cpos++; } else { - RangeSet src = parse_range(params.tokens[params.cpos++]); - *overlap = range_overlaps(src, tgt); + RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]); + *overlap = src.Overlaps(tgt); if (ReadBlocks(src, params.buffer, params.fd) == -1) { return -1; @@ -937,7 +845,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size return 0; } - RangeSet locs = parse_range(params.tokens[params.cpos++]); + RangeSet locs = RangeSet::Parse(params.tokens[params.cpos++]); MoveRange(params.buffer, locs, params.buffer); } @@ -959,7 +867,7 @@ static int LoadSourceBlocks(CommandParameters& params, const RangeSet& tgt, size continue; } - RangeSet locs = parse_range(tokens[1]); + RangeSet locs = RangeSet::Parse(tokens[1]); MoveRange(params.buffer, locs, stash); } @@ -1023,7 +931,7 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t* } // - tgt = parse_range(params.tokens[params.cpos++]); + tgt = RangeSet::Parse(params.tokens[params.cpos++]); std::vector tgtbuffer(tgt.size * BLOCKSIZE); if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) { @@ -1135,7 +1043,7 @@ static int PerformCommandStash(CommandParameters& params) { return 0; } - RangeSet src = parse_range(params.tokens[params.cpos++]); + RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]); allocate(src.size * BLOCKSIZE, params.buffer); if (ReadBlocks(src, params.buffer, params.fd) == -1) { @@ -1186,7 +1094,7 @@ static int PerformCommandZero(CommandParameters& params) { return -1; } - RangeSet tgt = parse_range(params.tokens[params.cpos++]); + RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); LOG(INFO) << " zeroing " << tgt.size << " blocks"; @@ -1228,7 +1136,7 @@ static int PerformCommandNew(CommandParameters& params) { return -1; } - RangeSet tgt = parse_range(params.tokens[params.cpos++]); + RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); if (params.canwrite) { LOG(INFO) << " writing " << tgt.size << " blocks of new data"; @@ -1351,7 +1259,7 @@ static int PerformCommandErase(CommandParameters& params) { return -1; } - RangeSet tgt = parse_range(params.tokens[params.cpos++]); + RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); if (params.canwrite) { LOG(INFO) << " erasing " << tgt.size << " blocks"; @@ -1733,7 +1641,7 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vectordata); + RangeSet rs = RangeSet::Parse(ranges->data); SHA_CTX ctx; SHA1_Init(&ctx); @@ -1871,7 +1779,7 @@ Value* BlockImageRecoverFn(const char* name, State* state, return StringValue(""); } - RangeSet rs = parse_range(ranges->data); + RangeSet rs = RangeSet::Parse(ranges->data); uint8_t buffer[BLOCKSIZE]; diff --git a/updater/include/updater/rangeset.h b/updater/include/updater/rangeset.h new file mode 100644 index 000000000..afaa82dcd --- /dev/null +++ b/updater/include/updater/rangeset.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 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 +#include +#include + +struct RangeSet { + size_t count; // Limit is INT_MAX. + size_t size; // The number of blocks in the RangeSet. + std::vector pos; // Actual limit is INT_MAX. + + static RangeSet Parse(const std::string& range_text) { + std::vector pieces = android::base::Split(range_text, ","); + CHECK_GE(pieces.size(), static_cast(3)) << "Invalid range text: " << range_text; + + size_t num; + CHECK(android::base::ParseUint(pieces[0], &num, static_cast(INT_MAX))) + << "Failed to parse the number of tokens: " << range_text; + + CHECK_NE(num, static_cast(0)) << "Invalid number of tokens: " << range_text; + CHECK_EQ(num % 2, static_cast(0)) << "Number of tokens must be even: " << range_text; + CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; + + std::vector pairs(num); + size_t size = 0; + for (size_t i = 0; i < num; i += 2) { + CHECK(android::base::ParseUint(pieces[i + 1], &pairs[i], static_cast(INT_MAX))); + CHECK(android::base::ParseUint(pieces[i + 2], &pairs[i + 1], static_cast(INT_MAX))); + CHECK_LT(pairs[i], pairs[i + 1]) + << "Empty or negative range: " << pairs[i] << ", " << pairs[i + 1]; + + size_t sz = pairs[i + 1] - pairs[i]; + CHECK_LE(size, SIZE_MAX - sz) << "RangeSet size overflow"; + size += sz; + } + + return RangeSet{ num / 2, size, std::move(pairs) }; + } + + // Get the block number for the i-th (starting from 0) block in the RangeSet. + size_t GetBlockNumber(size_t idx) const { + CHECK_LT(idx, size) << "Index " << idx << " is greater than RangeSet size " << size; + for (size_t i = 0; i < pos.size(); i += 2) { + if (idx < pos[i + 1] - pos[i]) { + return pos[i] + idx; + } + idx -= (pos[i + 1] - pos[i]); + } + CHECK(false); + return 0; // Unreachable, but to make compiler happy. + } + + // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" + // and "5,7" are not overlapped. + bool Overlaps(const RangeSet& other) const { + for (size_t i = 0; i < count; ++i) { + size_t start = pos[i * 2]; + size_t end = pos[i * 2 + 1]; + for (size_t j = 0; j < other.count; ++j) { + size_t other_start = other.pos[j * 2]; + size_t other_end = other.pos[j * 2 + 1]; + // [start, end) vs [other_start, other_end) + if (!(other_start >= end || start >= other_end)) { + return true; + } + } + } + return false; + } + + bool operator==(const RangeSet& other) const { + return (count == other.count && size == other.size && pos == other.pos); + } +}; -- cgit v1.2.3 From c97edcb4f49d6ea573c4d3b5c3b8bd2610fa8f7c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 31 Mar 2017 01:18:13 -0700 Subject: updater: Keep the parsed parameters in std::unique_ptr. We don't need to take raw pointers out of the parsed arguments. std::unique_ptr handles the dereferencing automatically. Test: mmma bootable/recovery Change-Id: I1beabf6e04dc350bdad7b36cee5fb345c82b28f2 --- updater/blockimg.cpp | 317 +++++++++++++++++++++++++-------------------------- 1 file changed, 157 insertions(+), 160 deletions(-) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index fc7a561eb..8199447a9 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -1317,10 +1317,10 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, return nullptr; } - const Value* blockdev_filename = args[0].get(); - const Value* transfer_list_value = args[1].get(); - const Value* new_data_fn = args[2].get(); - const Value* patch_data_fn = args[3].get(); + const std::unique_ptr& blockdev_filename = args[0]; + const std::unique_ptr& transfer_list_value = args[1]; + const std::unique_ptr& new_data_fn = args[2]; + const std::unique_ptr& patch_data_fn = args[3]; if (blockdev_filename->type != VAL_STRING) { ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name); @@ -1610,64 +1610,62 @@ Value* BlockImageUpdateFn(const char* name, State* state, } Value* RangeSha1Fn(const char* name, State* state, const std::vector>& argv) { - if (argv.size() != 2) { - ErrorAbort(state, kArgsParsingFailure, "range_sha1 expects 2 arguments, got %zu", - argv.size()); - return StringValue(""); - } + if (argv.size() != 2) { + ErrorAbort(state, kArgsParsingFailure, "range_sha1 expects 2 arguments, got %zu", argv.size()); + return StringValue(""); + } - std::vector> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } + std::vector> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } - const Value* blockdev_filename = args[0].get(); - const Value* ranges = args[1].get(); + const std::unique_ptr& blockdev_filename = args[0]; + const std::unique_ptr& ranges = args[1]; - if (blockdev_filename->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", - name); - return StringValue(""); - } - if (ranges->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); - return StringValue(""); - } + if (blockdev_filename->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "blockdev_filename argument to %s must be string", name); + return StringValue(""); + } + if (ranges->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); + return StringValue(""); + } - android::base::unique_fd fd(ota_open(blockdev_filename->data.c_str(), O_RDWR)); - if (fd == -1) { - ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", - blockdev_filename->data.c_str(), strerror(errno)); - return StringValue(""); - } + android::base::unique_fd fd(ota_open(blockdev_filename->data.c_str(), O_RDWR)); + if (fd == -1) { + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", blockdev_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - RangeSet rs = RangeSet::Parse(ranges->data); + RangeSet rs = RangeSet::Parse(ranges->data); - SHA_CTX ctx; - SHA1_Init(&ctx); + SHA_CTX ctx; + SHA1_Init(&ctx); - std::vector buffer(BLOCKSIZE); - for (size_t i = 0; i < rs.count; ++i) { - if (!check_lseek(fd, (off64_t)rs.pos[i*2] * BLOCKSIZE, SEEK_SET)) { - ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", - blockdev_filename->data.c_str(), strerror(errno)); - return StringValue(""); - } + std::vector buffer(BLOCKSIZE); + for (size_t i = 0; i < rs.count; ++i) { + if (!check_lseek(fd, (off64_t)rs.pos[i * 2] * BLOCKSIZE, SEEK_SET)) { + ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - for (size_t j = rs.pos[i*2]; j < rs.pos[i*2+1]; ++j) { - if (read_all(fd, buffer, BLOCKSIZE) == -1) { - ErrorAbort(state, kFreadFailure, "failed to read %s: %s", - blockdev_filename->data.c_str(), strerror(errno)); - return StringValue(""); - } + for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { + if (read_all(fd, buffer, BLOCKSIZE) == -1) { + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - SHA1_Update(&ctx, buffer.data(), BLOCKSIZE); - } + SHA1_Update(&ctx, buffer.data(), BLOCKSIZE); } - uint8_t digest[SHA_DIGEST_LENGTH]; - SHA1_Final(digest, &ctx); + } + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1_Final(digest, &ctx); - return StringValue(print_sha1(digest)); + return StringValue(print_sha1(digest)); } // This function checks if a device has been remounted R/W prior to an incremental @@ -1677,145 +1675,144 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector>& argv) { - if (argv.size() != 1) { - ErrorAbort(state, kArgsParsingFailure, "check_first_block expects 1 argument, got %zu", - argv.size()); - return StringValue(""); - } + if (argv.size() != 1) { + ErrorAbort(state, kArgsParsingFailure, "check_first_block expects 1 argument, got %zu", + argv.size()); + return StringValue(""); + } - std::vector> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } + std::vector> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } - const Value* arg_filename = args[0].get(); + const std::unique_ptr& arg_filename = args[0]; - if (arg_filename->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); - return StringValue(""); - } + if (arg_filename->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); + return StringValue(""); + } - android::base::unique_fd fd(ota_open(arg_filename->data.c_str(), O_RDONLY)); - if (fd == -1) { - ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data.c_str(), - strerror(errno)); - return StringValue(""); - } + android::base::unique_fd fd(ota_open(arg_filename->data.c_str(), O_RDONLY)); + if (fd == -1) { + ErrorAbort(state, kFileOpenFailure, "open \"%s\" failed: %s", arg_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - RangeSet blk0 {1 /*count*/, 1/*size*/, std::vector {0, 1}/*position*/}; - std::vector block0_buffer(BLOCKSIZE); + RangeSet blk0{ 1 /*count*/, 1 /*size*/, std::vector{ 0, 1 } /*position*/ }; + std::vector block0_buffer(BLOCKSIZE); - if (ReadBlocks(blk0, block0_buffer, fd) == -1) { - ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data.c_str(), - strerror(errno)); - return StringValue(""); - } + if (ReadBlocks(blk0, block0_buffer, fd) == -1) { + ErrorAbort(state, kFreadFailure, "failed to read %s: %s", arg_filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout - // Super block starts from block 0, offset 0x400 - // 0x2C: len32 Mount time - // 0x30: len32 Write time - // 0x34: len16 Number of mounts since the last fsck - // 0x38: len16 Magic signature 0xEF53 + // https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout + // Super block starts from block 0, offset 0x400 + // 0x2C: len32 Mount time + // 0x30: len32 Write time + // 0x34: len16 Number of mounts since the last fsck + // 0x38: len16 Magic signature 0xEF53 - time_t mount_time = *reinterpret_cast(&block0_buffer[0x400+0x2C]); - uint16_t mount_count = *reinterpret_cast(&block0_buffer[0x400+0x34]); + time_t mount_time = *reinterpret_cast(&block0_buffer[0x400 + 0x2C]); + uint16_t mount_count = *reinterpret_cast(&block0_buffer[0x400 + 0x34]); - if (mount_count > 0) { - uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count); - uiPrintf(state, "Last remount happened on %s", ctime(&mount_time)); - } + if (mount_count > 0) { + uiPrintf(state, "Device was remounted R/W %" PRIu16 " times", mount_count); + uiPrintf(state, "Last remount happened on %s", ctime(&mount_time)); + } - return StringValue("t"); + return StringValue("t"); } - Value* BlockImageRecoverFn(const char* name, State* state, const std::vector>& argv) { - if (argv.size() != 2) { - ErrorAbort(state, kArgsParsingFailure, "block_image_recover expects 2 arguments, got %zu", - argv.size()); - return StringValue(""); - } + if (argv.size() != 2) { + ErrorAbort(state, kArgsParsingFailure, "block_image_recover expects 2 arguments, got %zu", + argv.size()); + return StringValue(""); + } - std::vector> args; - if (!ReadValueArgs(state, argv, &args)) { - return nullptr; - } + std::vector> args; + if (!ReadValueArgs(state, argv, &args)) { + return nullptr; + } - const Value* filename = args[0].get(); - const Value* ranges = args[1].get(); + const std::unique_ptr& filename = args[0]; + const std::unique_ptr& ranges = args[1]; - if (filename->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); - return StringValue(""); - } - if (ranges->type != VAL_STRING) { - ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); - return StringValue(""); - } + if (filename->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "filename argument to %s must be string", name); + return StringValue(""); + } + if (ranges->type != VAL_STRING) { + ErrorAbort(state, kArgsParsingFailure, "ranges argument to %s must be string", name); + return StringValue(""); + } - // Output notice to log when recover is attempted - LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; + // Output notice to log when recover is attempted + LOG(INFO) << filename->data << " image corrupted, attempting to recover..."; - // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read - fec::io fh(filename->data, O_RDWR); + // When opened with O_RDWR, libfec rewrites corrupted blocks when they are read + fec::io fh(filename->data, O_RDWR); - if (!fh) { - ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), - strerror(errno)); - return StringValue(""); - } + if (!fh) { + ErrorAbort(state, kLibfecFailure, "fec_open \"%s\" failed: %s", filename->data.c_str(), + strerror(errno)); + return StringValue(""); + } - if (!fh.has_ecc() || !fh.has_verity()) { - ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors"); - return StringValue(""); - } + if (!fh.has_ecc() || !fh.has_verity()) { + ErrorAbort(state, kLibfecFailure, "unable to use metadata to correct errors"); + return StringValue(""); + } - fec_status status; + fec_status status; - if (!fh.get_status(status)) { - ErrorAbort(state, kLibfecFailure, "failed to read FEC status"); - return StringValue(""); - } + if (!fh.get_status(status)) { + ErrorAbort(state, kLibfecFailure, "failed to read FEC status"); + return StringValue(""); + } - RangeSet rs = RangeSet::Parse(ranges->data); + RangeSet rs = RangeSet::Parse(ranges->data); - uint8_t buffer[BLOCKSIZE]; + uint8_t buffer[BLOCKSIZE]; - for (size_t i = 0; i < rs.count; ++i) { - for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { - // Stay within the data area, libfec validates and corrects metadata - if (status.data_size <= (uint64_t)j * BLOCKSIZE) { - continue; - } + for (size_t i = 0; i < rs.count; ++i) { + for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { + // Stay within the data area, libfec validates and corrects metadata + if (status.data_size <= (uint64_t)j * BLOCKSIZE) { + continue; + } - if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) { - ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", - filename->data.c_str(), j, strerror(errno)); - return StringValue(""); - } + if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) { + ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", + filename->data.c_str(), j, strerror(errno)); + return StringValue(""); + } - // If we want to be able to recover from a situation where rewriting a corrected - // block doesn't guarantee the same data will be returned when re-read later, we - // can save a copy of corrected blocks to /cache. Note: - // - // 1. Maximum space required from /cache is the same as the maximum number of - // corrupted blocks we can correct. For RS(255, 253) and a 2 GiB partition, - // this would be ~16 MiB, for example. - // - // 2. To find out if this block was corrupted, call fec_get_status after each - // read and check if the errors field value has increased. - } + // If we want to be able to recover from a situation where rewriting a corrected + // block doesn't guarantee the same data will be returned when re-read later, we + // can save a copy of corrected blocks to /cache. Note: + // + // 1. Maximum space required from /cache is the same as the maximum number of + // corrupted blocks we can correct. For RS(255, 253) and a 2 GiB partition, + // this would be ~16 MiB, for example. + // + // 2. To find out if this block was corrupted, call fec_get_status after each + // read and check if the errors field value has increased. } - LOG(INFO) << "..." << filename->data << " image recovered successfully."; - return StringValue("t"); + } + LOG(INFO) << "..." << filename->data << " image recovered successfully."; + return StringValue("t"); } void RegisterBlockImageFunctions() { - RegisterFunction("block_image_verify", BlockImageVerifyFn); - RegisterFunction("block_image_update", BlockImageUpdateFn); - RegisterFunction("block_image_recover", BlockImageRecoverFn); - RegisterFunction("check_first_block", CheckFirstBlockFn); - RegisterFunction("range_sha1", RangeSha1Fn); + RegisterFunction("block_image_verify", BlockImageVerifyFn); + RegisterFunction("block_image_update", BlockImageUpdateFn); + RegisterFunction("block_image_recover", BlockImageRecoverFn); + RegisterFunction("check_first_block", CheckFirstBlockFn); + RegisterFunction("range_sha1", RangeSha1Fn); } -- cgit v1.2.3 From 5a176c0d3c9a1d02046513da83076b8601fb691a Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 31 Mar 2017 16:36:12 -0700 Subject: Use regular check for partition name instead of CHECK() Bug: 36260064 Test: Device reboots for invalid care_map. Change-Id: Id614f0d118fc2b9d9abf24918aa4b4324f4c94e1 --- update_verifier/update_verifier.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index 72b6dccc5..59f136c90 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -74,9 +74,10 @@ static int dm_name_filter(const dirent* de) { } static bool read_blocks(const std::string& partition, const std::string& range_str) { - CHECK(partition == "system" || partition == "vendor") - << "partition name should be system or vendor" << partition; - + if (partition != "system" && partition != "vendor") { + LOG(ERROR) << "partition name must be system or vendor: " << partition; + return false; + } // Iterate the content of "/sys/block/dm-X/dm/name". If it matches "system" // (or "vendor"), then dm-X is a dm-wrapped system/vendor partition. // Afterwards, update_verifier will read every block on the care_map_file of -- cgit v1.2.3 From 14eb2086c01121f0dcb44e753ba5d0ccdf06e6b0 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 1 Apr 2017 15:49:37 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 36832302 Change-Id: I1bfbb1791a4d7925491d7574c08994f6e7ff640d --- tools/recovery_l10n/res/values-af/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-am/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ar/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-az-rAZ/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-bg/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-bn-rBD/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ca/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-cs/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-da/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-de/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-el/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-en-rAU/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-en-rGB/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-en-rIN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-es-rUS/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-es/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-et-rEE/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-eu-rES/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-fa/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-fi/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-fr-rCA/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-fr/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-gl-rES/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-gu-rIN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-hi/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-hr/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-hu/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-hy-rAM/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-in/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-is-rIS/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-it/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-iw/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ja/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ka-rGE/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-kk-rKZ/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-km-rKH/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-kn-rIN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ko/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ky-rKG/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-lo-rLA/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-lt/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-lv/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-mk-rMK/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ml-rIN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-mn-rMN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-mr-rIN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ms-rMY/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-my-rMM/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-nb/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ne-rNP/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-nl/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-pa-rIN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-pl/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-pt-rBR/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-pt-rPT/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-pt/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ro/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ru/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-si-rLK/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-sk/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-sl/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-sq-rAL/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-sr/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-sv/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-sw/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ta-rIN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-te-rIN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-th/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-tl/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-tr/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-uk/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-ur-rPK/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-uz-rUZ/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-vi/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-zh-rCN/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-zh-rHK/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-zh-rTW/strings.xml | 9 +++++---- tools/recovery_l10n/res/values-zu/strings.xml | 9 +++++---- 78 files changed, 390 insertions(+), 312 deletions(-) diff --git a/tools/recovery_l10n/res/values-af/strings.xml b/tools/recovery_l10n/res/values-af/strings.xml index d5264184a..b1974da20 100644 --- a/tools/recovery_l10n/res/values-af/strings.xml +++ b/tools/recovery_l10n/res/values-af/strings.xml @@ -1,8 +1,9 @@ - "Installeer tans stelselopdatering..." - "Vee tans uit..." - "Geen bevel." - "Fout!" + "Installeer tans stelselopdatering" + "Vee tans uit" + "Geen opdrag nie" + "Fout!" + "Installeer tans sekuriteitopdatering" diff --git a/tools/recovery_l10n/res/values-am/strings.xml b/tools/recovery_l10n/res/values-am/strings.xml index cddb099bc..75c17fbad 100644 --- a/tools/recovery_l10n/res/values-am/strings.xml +++ b/tools/recovery_l10n/res/values-am/strings.xml @@ -1,8 +1,9 @@ - "የስርዓት ዝማኔ በመጫን ላይ…" - "በመደምሰስ ላይ…" - "ምንም ትዕዛዝ የለም።" - "ስህተት!" + "የሥርዓት ዝማኔን በመጫን ላይ…" + "በመደምሰስ ላይ" + "ምንም ትዕዛዝ የለም" + "ስህተት!" + "የደህንነት ዝማኔ በመጫን ላይ" diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml index d06b96644..601b5832b 100644 --- a/tools/recovery_l10n/res/values-ar/strings.xml +++ b/tools/recovery_l10n/res/values-ar/strings.xml @@ -1,8 +1,9 @@ - "جارٍ تثبيت تحديث النظام…" - "جارٍ المسح…" - "ليس هناك أي أمر." - "خطأ!" + "جارٍ تثبيت تحديث النظام" + "جارٍ محو البيانات" + "ليس هناك أي أمر" + "خطأ!" + "جارٍ تثبيت تحديث الأمان" diff --git a/tools/recovery_l10n/res/values-az-rAZ/strings.xml b/tools/recovery_l10n/res/values-az-rAZ/strings.xml index 3435573dc..c6765a9ea 100644 --- a/tools/recovery_l10n/res/values-az-rAZ/strings.xml +++ b/tools/recovery_l10n/res/values-az-rAZ/strings.xml @@ -1,8 +1,9 @@ - "Sistem güncəlləməsi quraşdırılır..." - "Silinir..." - "Əmr yoxdur." - "Xəta!" + "Sistem güncəlləməsi quraşdırılır..." + "Silinir" + "Əmr yoxdur" + "Xəta!" + "Təhlükəsizlik güncəlləməsi yüklənir" diff --git a/tools/recovery_l10n/res/values-bg/strings.xml b/tools/recovery_l10n/res/values-bg/strings.xml index 004f3b93e..9e628a2af 100644 --- a/tools/recovery_l10n/res/values-bg/strings.xml +++ b/tools/recovery_l10n/res/values-bg/strings.xml @@ -1,8 +1,9 @@ - "Системната актуализация се инсталира…" - "Изтрива се…" - "Без команда." - "Грешка!" + "Системната актуализация се инсталира" + "Изтрива се" + "Без команда" + "Грешка!" + "Актуализацията на сигурносттa се инсталира" diff --git a/tools/recovery_l10n/res/values-bn-rBD/strings.xml b/tools/recovery_l10n/res/values-bn-rBD/strings.xml index 4d2e590f4..0a481faf1 100644 --- a/tools/recovery_l10n/res/values-bn-rBD/strings.xml +++ b/tools/recovery_l10n/res/values-bn-rBD/strings.xml @@ -1,8 +1,9 @@ - "সিস্টেম আপডেট ইনস্টল করা হচ্ছে…" - "মোছা হচ্ছে…" - "কোনো নির্দেশ নেই।" - "ত্রুটি!" + "সিস্টেম আপডেট ইনস্টল করা হচ্ছে" + "মোছা হচ্ছে" + "কোনো আদেশ নেই" + "ত্রুটি!" + "নিরাপত্তার আপডেট ইনস্টল করা হচ্ছে" diff --git a/tools/recovery_l10n/res/values-ca/strings.xml b/tools/recovery_l10n/res/values-ca/strings.xml index 5d7b652c5..3f266d2df 100644 --- a/tools/recovery_l10n/res/values-ca/strings.xml +++ b/tools/recovery_l10n/res/values-ca/strings.xml @@ -1,8 +1,9 @@ - "S\'està instal·lant l\'actualització del sistema..." - "S\'està esborrant..." - "Cap ordre." - "Error!" + "S\'està instal·lant una actualització del sistema" + "S\'està esborrant" + "No hi ha cap ordre" + "S\'ha produït un error" + "S\'està instal·lant una actualització de seguretat" diff --git a/tools/recovery_l10n/res/values-cs/strings.xml b/tools/recovery_l10n/res/values-cs/strings.xml index 771235d04..eb436a810 100644 --- a/tools/recovery_l10n/res/values-cs/strings.xml +++ b/tools/recovery_l10n/res/values-cs/strings.xml @@ -1,8 +1,9 @@ - "Instalace aktualizace systému..." - "Mazání…" - "Žádný příkaz." - "Chyba!" + "Instalace aktualizace systému" + "Mazání" + "Žádný příkaz" + "Chyba!" + "Instalace aktualizace zabezpečení" diff --git a/tools/recovery_l10n/res/values-da/strings.xml b/tools/recovery_l10n/res/values-da/strings.xml index c28a76fbd..c6e64a245 100644 --- a/tools/recovery_l10n/res/values-da/strings.xml +++ b/tools/recovery_l10n/res/values-da/strings.xml @@ -1,8 +1,9 @@ - "Systemopdateringen installeres…" - "Sletter…" - "Ingen kommando." - "Fejl!" + "Installerer systemopdateringen" + "Sletter" + "Ingen kommando" + "Fejl!" + "Installerer sikkerhedsopdateringen" diff --git a/tools/recovery_l10n/res/values-de/strings.xml b/tools/recovery_l10n/res/values-de/strings.xml index 02d259059..6b6726a23 100644 --- a/tools/recovery_l10n/res/values-de/strings.xml +++ b/tools/recovery_l10n/res/values-de/strings.xml @@ -1,8 +1,9 @@ - "Systemupdate wird installiert…" - "Wird gelöscht…" - "Kein Befehl" - "Fehler" + "Systemupdate wird installiert" + "Wird gelöscht" + "Kein Befehl" + "Fehler" + "Sicherheitsupdate wird installiert" diff --git a/tools/recovery_l10n/res/values-el/strings.xml b/tools/recovery_l10n/res/values-el/strings.xml index aa2626b4b..4cb2da5f9 100644 --- a/tools/recovery_l10n/res/values-el/strings.xml +++ b/tools/recovery_l10n/res/values-el/strings.xml @@ -1,8 +1,9 @@ - "Εγκατάσταση ενημέρωσης συστήματος…" - "Διαγραφή…" - "Καμία εντολή." - "Σφάλμα!" + "Εγκατάσταση ενημέρωσης συστήματος" + "Διαγραφή" + "Καμία εντολή" + "Σφάλμα!" + "Εγκατάσταση ενημέρωσης ασφαλείας" diff --git a/tools/recovery_l10n/res/values-en-rAU/strings.xml b/tools/recovery_l10n/res/values-en-rAU/strings.xml index b70d678c1..dc75c2374 100644 --- a/tools/recovery_l10n/res/values-en-rAU/strings.xml +++ b/tools/recovery_l10n/res/values-en-rAU/strings.xml @@ -1,8 +1,9 @@ - "Installing system update…" - "Erasing…" - "No command." - "Error!" + "Installing system update" + "Erasing" + "No command" + "Error!" + "Installing security update" diff --git a/tools/recovery_l10n/res/values-en-rGB/strings.xml b/tools/recovery_l10n/res/values-en-rGB/strings.xml index b70d678c1..dc75c2374 100644 --- a/tools/recovery_l10n/res/values-en-rGB/strings.xml +++ b/tools/recovery_l10n/res/values-en-rGB/strings.xml @@ -1,8 +1,9 @@ - "Installing system update…" - "Erasing…" - "No command." - "Error!" + "Installing system update" + "Erasing" + "No command" + "Error!" + "Installing security update" diff --git a/tools/recovery_l10n/res/values-en-rIN/strings.xml b/tools/recovery_l10n/res/values-en-rIN/strings.xml index b70d678c1..dc75c2374 100644 --- a/tools/recovery_l10n/res/values-en-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-en-rIN/strings.xml @@ -1,8 +1,9 @@ - "Installing system update…" - "Erasing…" - "No command." - "Error!" + "Installing system update" + "Erasing" + "No command" + "Error!" + "Installing security update" diff --git a/tools/recovery_l10n/res/values-es-rUS/strings.xml b/tools/recovery_l10n/res/values-es-rUS/strings.xml index 256272ac7..06b86069b 100644 --- a/tools/recovery_l10n/res/values-es-rUS/strings.xml +++ b/tools/recovery_l10n/res/values-es-rUS/strings.xml @@ -1,8 +1,9 @@ - "Instalando actualización del sistema…" - "Borrando…" - "Ningún comando" - "Error" + "Instalando actualización del sistema" + "Borrando" + "Ningún comando" + "Error" + "Instalando actualización de seguridad" diff --git a/tools/recovery_l10n/res/values-es/strings.xml b/tools/recovery_l10n/res/values-es/strings.xml index 323f05505..d8618f2f4 100644 --- a/tools/recovery_l10n/res/values-es/strings.xml +++ b/tools/recovery_l10n/res/values-es/strings.xml @@ -1,8 +1,9 @@ - "Instalando actualización del sistema…" - "Borrando…" - "Sin comandos" - "Error" + "Instalando actualización del sistema" + "Borrando" + "Sin comandos" + "Error" + "Instalando actualización de seguridad" diff --git a/tools/recovery_l10n/res/values-et-rEE/strings.xml b/tools/recovery_l10n/res/values-et-rEE/strings.xml index 407a53d67..072a9ef80 100644 --- a/tools/recovery_l10n/res/values-et-rEE/strings.xml +++ b/tools/recovery_l10n/res/values-et-rEE/strings.xml @@ -1,8 +1,9 @@ - "Süsteemivärskenduste installimine ..." - "Kustutamine ..." - "Käsk puudub." - "Viga!" + "Süsteemivärskenduse installimine" + "Kustutamine" + "Käsk puudub" + "Viga!" + "Turvavärskenduse installimine" diff --git a/tools/recovery_l10n/res/values-eu-rES/strings.xml b/tools/recovery_l10n/res/values-eu-rES/strings.xml index 08d9c0672..5540469d0 100644 --- a/tools/recovery_l10n/res/values-eu-rES/strings.xml +++ b/tools/recovery_l10n/res/values-eu-rES/strings.xml @@ -1,8 +1,9 @@ - "Sistemaren eguneratzea instalatzen…" - "Ezabatzen…" - "Ez dago agindurik." - "Errorea!" + "Sistemaren eguneratzea instalatzen" + "Eduki guztia ezabatzen" + "Ez dago agindurik" + "Errorea" + "Segurtasun-eguneratzea instalatzen" diff --git a/tools/recovery_l10n/res/values-fa/strings.xml b/tools/recovery_l10n/res/values-fa/strings.xml index dd002face..cc390ae84 100644 --- a/tools/recovery_l10n/res/values-fa/strings.xml +++ b/tools/recovery_l10n/res/values-fa/strings.xml @@ -1,8 +1,9 @@ - "در حال نصب به‌روزرسانی سیستم ..." - "پاک کردن..." - "فرمانی موجود نیست." - "خطا!" + "در حال نصب به‌روزرسانی سیستم" + "در حال پاک کردن" + "فرمانی وجود ندارد" + "خطا!" + "در حال نصب به‌روزرسانی امنیتی" diff --git a/tools/recovery_l10n/res/values-fi/strings.xml b/tools/recovery_l10n/res/values-fi/strings.xml index b77417a98..5141642c8 100644 --- a/tools/recovery_l10n/res/values-fi/strings.xml +++ b/tools/recovery_l10n/res/values-fi/strings.xml @@ -1,8 +1,9 @@ - "Asennetaan järjestelmäpäivitystä..." - "Tyhjennetään..." - "Ei komentoa." - "Virhe!" + "Asennetaan järjestelmäpäivitystä" + "Tyhjennetään" + "Ei komentoa" + "Virhe!" + "Asennetaan tietoturvapäivitystä" diff --git a/tools/recovery_l10n/res/values-fr-rCA/strings.xml b/tools/recovery_l10n/res/values-fr-rCA/strings.xml index f2a85d86a..b2415290b 100644 --- a/tools/recovery_l10n/res/values-fr-rCA/strings.xml +++ b/tools/recovery_l10n/res/values-fr-rCA/strings.xml @@ -1,8 +1,9 @@ - "Installation de la mise à jour du système en cours…" - "Effacement en cours…" - "Aucune commande." - "Erreur!" + "Installation de la mise à jour du système en cours…" + "Suppression en cours..." + "Aucune commande" + "Erreur!" + "Installation de la mise à jour de sécurité en cours..." diff --git a/tools/recovery_l10n/res/values-fr/strings.xml b/tools/recovery_l10n/res/values-fr/strings.xml index cdb4a2668..f0472b5ac 100644 --- a/tools/recovery_l10n/res/values-fr/strings.xml +++ b/tools/recovery_l10n/res/values-fr/strings.xml @@ -1,8 +1,9 @@ - "Installation de la mise à jour du système en cours…" - "Effacement en cours…" - "Aucune commande." - "Erreur !" + "Installation de la mise à jour du système…" + "Suppression…" + "Aucune commande" + "Erreur !" + "Installation de la mise à jour de sécurité…" diff --git a/tools/recovery_l10n/res/values-gl-rES/strings.xml b/tools/recovery_l10n/res/values-gl-rES/strings.xml index 7546fbda4..42b2016c2 100644 --- a/tools/recovery_l10n/res/values-gl-rES/strings.xml +++ b/tools/recovery_l10n/res/values-gl-rES/strings.xml @@ -1,8 +1,9 @@ - "Instalando actualización do sistema..." - "Borrando..." - "Ningún comando" - "Erro" + "Instalando actualización do sistema" + "Borrando" + "Non hai ningún comando" + "Erro" + "Instalando actualización de seguranza" diff --git a/tools/recovery_l10n/res/values-gu-rIN/strings.xml b/tools/recovery_l10n/res/values-gu-rIN/strings.xml index a364b523c..2355a0f4f 100644 --- a/tools/recovery_l10n/res/values-gu-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-gu-rIN/strings.xml @@ -1,8 +1,9 @@ - "સિસ્ટમ અપડેટ ઇન્સ્ટોલ કરી રહ્યાં છે…" - "કાઢી નાખી રહ્યાં છે…" - "કોઈ આદેશ નથી." - "ભૂલ!" + "સિસ્ટમ અપડેટ ઇન્સ્ટૉલ કરી રહ્યાં છે" + "કાઢી નાખી રહ્યું છે" + "કોઈ આદેશ નથી" + "ભૂલ!" + "સુરક્ષા અપડેટ ઇન્સ્ટૉલ કરી રહ્યાં છે" diff --git a/tools/recovery_l10n/res/values-hi/strings.xml b/tools/recovery_l10n/res/values-hi/strings.xml index a470d12b6..a8a876ee4 100644 --- a/tools/recovery_l10n/res/values-hi/strings.xml +++ b/tools/recovery_l10n/res/values-hi/strings.xml @@ -1,8 +1,9 @@ - "सिस्टम के बारे में नई जानकारी मिल रही है…" - "मिटा रहा है…" - "कोई आदेश नहीं." - "त्रुटि!" + "सिस्टम अपडेट इंस्टॉल किया जा रहा है" + "मिटाया जा रहा है" + "कोई आदेश नहीं" + "गड़बड़ी!" + "सुरक्षा अपडेट इंस्टॉल किया जा रहा है" diff --git a/tools/recovery_l10n/res/values-hr/strings.xml b/tools/recovery_l10n/res/values-hr/strings.xml index 56225c015..3b75ff115 100644 --- a/tools/recovery_l10n/res/values-hr/strings.xml +++ b/tools/recovery_l10n/res/values-hr/strings.xml @@ -1,8 +1,9 @@ - "Instaliranje ažuriranja sustava…" - "Brisanje…" - "Nema naredbe." - "Pogreška!" + "Instaliranje ažuriranja sustava" + "Brisanje" + "Nema naredbe" + "Pogreška!" + "Instaliranje sigurnosnog ažuriranja" diff --git a/tools/recovery_l10n/res/values-hu/strings.xml b/tools/recovery_l10n/res/values-hu/strings.xml index a64f50176..12d4d9fe7 100644 --- a/tools/recovery_l10n/res/values-hu/strings.xml +++ b/tools/recovery_l10n/res/values-hu/strings.xml @@ -1,8 +1,9 @@ - "Rendszerfrissítés telepítése..." - "Törlés..." - "Nincs parancs." - "Hiba!" + "Rendszerfrissítés telepítése" + "Törlés" + "Nincs parancs" + "Hiba!" + "Biztonsági frissítés telepítése" diff --git a/tools/recovery_l10n/res/values-hy-rAM/strings.xml b/tools/recovery_l10n/res/values-hy-rAM/strings.xml index 7babe80c8..9d62bb763 100644 --- a/tools/recovery_l10n/res/values-hy-rAM/strings.xml +++ b/tools/recovery_l10n/res/values-hy-rAM/strings.xml @@ -1,8 +1,9 @@ - "Համակարգի թարմացման տեղադրում…" - "Ջնջում…" - "Հրամանը տրված չէ:" - "Սխալ" + "Համակարգի թարմացման տեղադրում" + "Ջնջում" + "Հրամանը տրված չէ" + "Սխալ" + "Անվտանգության թարմացման տեղադրում" diff --git a/tools/recovery_l10n/res/values-in/strings.xml b/tools/recovery_l10n/res/values-in/strings.xml index 93f9c2876..0e56e0dd9 100644 --- a/tools/recovery_l10n/res/values-in/strings.xml +++ b/tools/recovery_l10n/res/values-in/strings.xml @@ -1,8 +1,9 @@ - "Memasang pembaruan sistem…" - "Menghapus..." - "Tidak ada perintah." - "Kesalahan!" + "Memasang pembaruan sistem" + "Menghapus" + "Tidak ada perintah" + "Error!" + "Memasang pembaruan keamanan" diff --git a/tools/recovery_l10n/res/values-is-rIS/strings.xml b/tools/recovery_l10n/res/values-is-rIS/strings.xml index 926e85132..5065b6522 100644 --- a/tools/recovery_l10n/res/values-is-rIS/strings.xml +++ b/tools/recovery_l10n/res/values-is-rIS/strings.xml @@ -1,8 +1,9 @@ - "Setur upp kerfisuppfærslu…" - "Þurrkar út…" - "Engin skipun." - "Villa!" + "Setur upp kerfisuppfærslu" + "Eyðir" + "Engin skipun" + "Villa!" + "Setur upp öryggisuppfærslu" diff --git a/tools/recovery_l10n/res/values-it/strings.xml b/tools/recovery_l10n/res/values-it/strings.xml index 9defe36bd..2c0364e60 100644 --- a/tools/recovery_l10n/res/values-it/strings.xml +++ b/tools/recovery_l10n/res/values-it/strings.xml @@ -1,8 +1,9 @@ - "Installazione aggiornamento di sistema…" - "Cancellazione…" - "Nessun comando." - "Errore!" + "Installazione aggiornamento di sistema…" + "Cancellazione…" + "Nessun comando" + "Errore!" + "Installazione aggiornamento sicurezza…" diff --git a/tools/recovery_l10n/res/values-iw/strings.xml b/tools/recovery_l10n/res/values-iw/strings.xml index e43bb20a9..ea5e6f2c9 100644 --- a/tools/recovery_l10n/res/values-iw/strings.xml +++ b/tools/recovery_l10n/res/values-iw/strings.xml @@ -1,8 +1,9 @@ - "מתקין עדכון מערכת…" - "מוחק…" - "אין פקודה." - "שגיאה!" + "מתקין עדכון מערכת" + "מוחק" + "אין פקודה" + "שגיאה!" + "מתקין עדכון אבטחה" diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml index da0fa623a..36e029b0f 100644 --- a/tools/recovery_l10n/res/values-ja/strings.xml +++ b/tools/recovery_l10n/res/values-ja/strings.xml @@ -1,8 +1,9 @@ - "システムアップデートをインストールしています…" - "消去しています…" - "コマンドが指定されていません。" - "エラーです" + "システム アップデートをインストールしています" + "消去しています" + "コマンドが指定されていません" + "エラーが発生しました。" + "セキュリティ アップデートをインストールしています" diff --git a/tools/recovery_l10n/res/values-ka-rGE/strings.xml b/tools/recovery_l10n/res/values-ka-rGE/strings.xml index 2d27c1799..6a46b3677 100644 --- a/tools/recovery_l10n/res/values-ka-rGE/strings.xml +++ b/tools/recovery_l10n/res/values-ka-rGE/strings.xml @@ -1,8 +1,9 @@ - "სისტემის განახლების დაყენება…" - "მიმდინარეობს წაშლა…" - "ბრძანება არ არის." - "შეცდომა!" + "მიმდინარეობს სისტემის განახლების ინსტალაცია" + "მიმდინარეობს ამოშლა" + "ბრძანება არ არის" + "წარმოიქმნა შეცდომა!" + "მიმდინარეობს უსაფრთხოების განახლების ინსტალაცია" diff --git a/tools/recovery_l10n/res/values-kk-rKZ/strings.xml b/tools/recovery_l10n/res/values-kk-rKZ/strings.xml index 3ca05b9eb..a4bd86e66 100644 --- a/tools/recovery_l10n/res/values-kk-rKZ/strings.xml +++ b/tools/recovery_l10n/res/values-kk-rKZ/strings.xml @@ -1,8 +1,9 @@ - "Жүйе жаңартуларын орнатуда…" - "Өшіруде..." - "Пәрмен берілген жоқ." - "Қате!" + "Жүйе жаңартуы орнатылуда" + "Өшірілуде" + "Пәрмен жоқ" + "Қате!" + "Қауіпсіздік жаңартуы орнатылуда" diff --git a/tools/recovery_l10n/res/values-km-rKH/strings.xml b/tools/recovery_l10n/res/values-km-rKH/strings.xml index 0c1c272e0..313c0f457 100644 --- a/tools/recovery_l10n/res/values-km-rKH/strings.xml +++ b/tools/recovery_l10n/res/values-km-rKH/strings.xml @@ -1,8 +1,9 @@ - "កំពុង​ដំឡើង​បច្ចុប្បន្នភាព​ប្រព័ន្ធ…" - "កំពុង​លុប…" - "គ្មាន​ពាក្យ​បញ្ជា។" - "កំហុស!" + "កំពុងអាប់ដេតប្រព័ន្ធ" + "លុប" + "គ្មានពាក្យបញ្ជាទេ" + "កំហុស!" + "កំពុងដំឡើងការអាប់ដេតសុវត្ថិភាព" diff --git a/tools/recovery_l10n/res/values-kn-rIN/strings.xml b/tools/recovery_l10n/res/values-kn-rIN/strings.xml index be25d7a9d..5bf6260ee 100644 --- a/tools/recovery_l10n/res/values-kn-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-kn-rIN/strings.xml @@ -1,8 +1,9 @@ - "ಸಿಸ್ಟಂ ನವೀಕರಣವನ್ನು ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ…" - "ಅಳಿಸಲಾಗುತ್ತಿದೆ…" - "ಯಾವುದೇ ಆದೇಶವಿಲ್ಲ." - "ದೋಷ!" + "ಸಿಸ್ಟಂ ಅಪ್‌ಡೇಟ್‌ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ" + "ಅಳಿಸಲಾಗುತ್ತಿದೆ" + "ಯಾವುದೇ ಆದೇಶವಿಲ್ಲ" + "ದೋಷ!" + "ಭದ್ರತೆಯ ಅಪ್‌ಡೇಟ್‌ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ" diff --git a/tools/recovery_l10n/res/values-ko/strings.xml b/tools/recovery_l10n/res/values-ko/strings.xml index e46a87606..aca13bbe7 100644 --- a/tools/recovery_l10n/res/values-ko/strings.xml +++ b/tools/recovery_l10n/res/values-ko/strings.xml @@ -1,8 +1,9 @@ - "시스템 업데이트 설치 중…" - "지우는 중…" - "명령어가 없습니다." - "오류!" + "시스템 업데이트 설치" + "지우는 중" + "명령어 없음" + "오류!" + "보안 업데이트 설치 중" diff --git a/tools/recovery_l10n/res/values-ky-rKG/strings.xml b/tools/recovery_l10n/res/values-ky-rKG/strings.xml index e2ced27a4..0a6bd783a 100644 --- a/tools/recovery_l10n/res/values-ky-rKG/strings.xml +++ b/tools/recovery_l10n/res/values-ky-rKG/strings.xml @@ -1,8 +1,9 @@ - "Системдик жаңыртууларды орнотуу…" - "Өчүрүлүүдө…" - "Буйрук берилген жок." - "Ката!" + "Тутум жаңыртуусу орнотулууда" + "Тазаланууда" + "Буйрук берилген жок" + "Ката!" + "Коопсуздук жаңыртуусу орнотулууда" diff --git a/tools/recovery_l10n/res/values-lo-rLA/strings.xml b/tools/recovery_l10n/res/values-lo-rLA/strings.xml index 5880cca75..d3dbb3970 100644 --- a/tools/recovery_l10n/res/values-lo-rLA/strings.xml +++ b/tools/recovery_l10n/res/values-lo-rLA/strings.xml @@ -1,8 +1,9 @@ - "ກຳລັງຕິດຕັ້ງການອັບເດດລະບົບ..." - "ກຳລັງລຶບ..." - "ບໍ່ມີຄຳສັ່ງ." - "ຜິດພາດ!" + "ກຳລັງຕິດຕັ້ງການອັບເດດລະບົບ" + "ກຳລັງລຶບ" + "ບໍ່ມີຄຳສັ່ງ" + "ຜິດພາດ!" + "ກຳລັງຕິດຕັ້ງອັບເດດຄວາມປອດໄພ" diff --git a/tools/recovery_l10n/res/values-lt/strings.xml b/tools/recovery_l10n/res/values-lt/strings.xml index 957ac7557..d5d5e88fd 100644 --- a/tools/recovery_l10n/res/values-lt/strings.xml +++ b/tools/recovery_l10n/res/values-lt/strings.xml @@ -1,8 +1,9 @@ - "Diegiamas sistemos naujinys…" - "Ištrinama…" - "Nėra komandos." - "Klaida!" + "Diegiamas sistemos naujinys" + "Ištrinama" + "Nėra jokių komandų" + "Klaida!" + "Diegiamas saugos naujinys" diff --git a/tools/recovery_l10n/res/values-lv/strings.xml b/tools/recovery_l10n/res/values-lv/strings.xml index c5d5b93a6..d877f6a61 100644 --- a/tools/recovery_l10n/res/values-lv/strings.xml +++ b/tools/recovery_l10n/res/values-lv/strings.xml @@ -1,8 +1,9 @@ - "Notiek sistēmas atjauninājuma instalēšana..." - "Notiek dzēšana..." - "Nav nevienas komandas." - "Kļūda!" + "Notiek sistēmas atjauninājuma instalēšana" + "Notiek dzēšana" + "Nav nevienas komandas" + "Kļūda!" + "Notiek drošības atjauninājuma instalēšana" diff --git a/tools/recovery_l10n/res/values-mk-rMK/strings.xml b/tools/recovery_l10n/res/values-mk-rMK/strings.xml index d91a67cac..351459730 100644 --- a/tools/recovery_l10n/res/values-mk-rMK/strings.xml +++ b/tools/recovery_l10n/res/values-mk-rMK/strings.xml @@ -1,8 +1,9 @@ - "Се инсталира ажурирање на системот..." - "Се брише..." - "Нема наредба." - "Грешка!" + "Се инсталира ажурирање на системот" + "Се брише" + "Нема наредба" + "Грешка!" + "Се инсталира безбедносно ажурирање" diff --git a/tools/recovery_l10n/res/values-ml-rIN/strings.xml b/tools/recovery_l10n/res/values-ml-rIN/strings.xml index 38ebcd120..b506e2530 100644 --- a/tools/recovery_l10n/res/values-ml-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-ml-rIN/strings.xml @@ -1,8 +1,9 @@ - "സിസ്റ്റം അപ്‌ഡേറ്റ് ഇൻസ്റ്റാളുചെയ്യുന്നു…" - "മായ്‌ക്കുന്നു…" - "കമാൻഡ് ഒന്നുമില്ല." - "പിശക്!" + "സിസ്റ്റം അപ്‌ഡേറ്റ് ഇൻസ്റ്റാൾ ചെയ്യുന്നു" + "മായ്‌ക്കുന്നു" + "കമാൻഡ് ഒന്നുമില്ല" + "പിശക്!" + "സുരക്ഷാ അപ്ഡേറ്റ് ഇൻസ്റ്റാൾ ചെയ്യുന്നു" diff --git a/tools/recovery_l10n/res/values-mn-rMN/strings.xml b/tools/recovery_l10n/res/values-mn-rMN/strings.xml index 463cafeaf..e3dd2e90e 100644 --- a/tools/recovery_l10n/res/values-mn-rMN/strings.xml +++ b/tools/recovery_l10n/res/values-mn-rMN/strings.xml @@ -1,8 +1,9 @@ - "Системийн шинэчлэлтийг суулгаж байна…" - "Арилгаж байна…" - "Команд байхгүй." - "Алдаа!" + "Системийн шинэчлэлтийг суулгаж байна" + "Устгаж байна" + "Тушаал байхгүй" + "Алдаа!" + "Аюулгүй байдлын шинэчлэлтийг суулгаж байна" diff --git a/tools/recovery_l10n/res/values-mr-rIN/strings.xml b/tools/recovery_l10n/res/values-mr-rIN/strings.xml index 25c5d0c57..8cf86f773 100644 --- a/tools/recovery_l10n/res/values-mr-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-mr-rIN/strings.xml @@ -1,8 +1,9 @@ - "सिस्टम अद्यतन स्थापित करीत आहे..." - "मिटवित आहे…" - "कोणताही आदेश नाही." - "त्रुटी!" + "सिस्टम अद्यतन स्थापित करीत आहे" + "मिटवत आहे" + "कोणताही आदेश नाही" + "त्रुटी!" + "सुरक्षा अद्यतन स्थापित करीत आहे" diff --git a/tools/recovery_l10n/res/values-ms-rMY/strings.xml b/tools/recovery_l10n/res/values-ms-rMY/strings.xml index f5635910e..0e24ac4e1 100644 --- a/tools/recovery_l10n/res/values-ms-rMY/strings.xml +++ b/tools/recovery_l10n/res/values-ms-rMY/strings.xml @@ -1,8 +1,9 @@ - "Memasang kemas kini sistem..." - "Memadam..." - "Tiada arahan." - "Ralat!" + "Memasang kemas kini sistem" + "Memadam" + "Tiada perintah" + "Ralat!" + "Memasang kemas kini keselamatan" diff --git a/tools/recovery_l10n/res/values-my-rMM/strings.xml b/tools/recovery_l10n/res/values-my-rMM/strings.xml index 4091b1923..f13752461 100644 --- a/tools/recovery_l10n/res/values-my-rMM/strings.xml +++ b/tools/recovery_l10n/res/values-my-rMM/strings.xml @@ -1,8 +1,9 @@ - "စနစ်အား အဆင့်မြှင့်ခြင်း လုပ်ဆောင်နေသည်…" - "ဖျက်နေသည် ..." - "ညွှန်ကြားချက်မပေးထားပါ" - "မှားနေပါသည်!" + "စနစ်အပ်ဒိတ်ကို ထည့်သွင်းနေသည်" + "ဖျက်နေသည်" + "ညွှန်ကြားချက်မပေးထားပါ" + "မှားနေပါသည်!" + "လုံခြုံရေး အပ်ဒိတ်ကို ထည့်သွင်းနေသည်" diff --git a/tools/recovery_l10n/res/values-nb/strings.xml b/tools/recovery_l10n/res/values-nb/strings.xml index 4e89ad7c8..ad6f20e46 100644 --- a/tools/recovery_l10n/res/values-nb/strings.xml +++ b/tools/recovery_l10n/res/values-nb/strings.xml @@ -1,8 +1,9 @@ - "Installerer systemoppdateringen ..." - "Sletter ..." - "Ingen kommando." - "Feil!" + "Installerer systemoppdateringen" + "Tømmer" + "Ingen kommandoer" + "Feil!" + "Installerer sikkerhetsoppdateringen" diff --git a/tools/recovery_l10n/res/values-ne-rNP/strings.xml b/tools/recovery_l10n/res/values-ne-rNP/strings.xml index 835f275b4..1880e807b 100644 --- a/tools/recovery_l10n/res/values-ne-rNP/strings.xml +++ b/tools/recovery_l10n/res/values-ne-rNP/strings.xml @@ -1,8 +1,9 @@ - "प्रणाली अद्यावधिक स्थापना गर्दै..." - "मेटाइदै..." - "कुनै आदेश छैन।" - "त्रुटि!" + "प्रणालीको अद्यावधिकलाई स्थापना गर्दै" + "मेटाउँदै" + "कुनै आदेश छैन" + "त्रुटि!" + "सुरक्षा सम्बन्धी अद्यावधिकलाई स्थापना गर्दै" diff --git a/tools/recovery_l10n/res/values-nl/strings.xml b/tools/recovery_l10n/res/values-nl/strings.xml index be80a6b5c..0d6c15abb 100644 --- a/tools/recovery_l10n/res/values-nl/strings.xml +++ b/tools/recovery_l10n/res/values-nl/strings.xml @@ -1,8 +1,9 @@ - "Systeemupdate installeren…" - "Wissen…" - "Geen opdracht." - "Fout!" + "Systeemupdate installeren" + "Wissen" + "Geen opdracht" + "Fout!" + "Beveiligingsupdate installeren" diff --git a/tools/recovery_l10n/res/values-pa-rIN/strings.xml b/tools/recovery_l10n/res/values-pa-rIN/strings.xml index 39ef32f55..8564c9c36 100644 --- a/tools/recovery_l10n/res/values-pa-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-pa-rIN/strings.xml @@ -1,8 +1,9 @@ - "ਸਿਸਟਮ ਅਪਡੇਟ ਇੰਸਟੌਲ ਕਰ ਰਿਹਾ ਹੈ…" - "ਹਟਾ ਰਿਹਾ ਹੈ…" - "ਕੋਈ ਕਮਾਂਡ ਨਹੀਂ।" - "ਅਸ਼ੁੱਧੀ!" + "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + "ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ" + "ਕੋਈ ਕਮਾਂਡ ਨਹੀਂ" + "ਅਸ਼ੁੱਧੀ!" + "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" diff --git a/tools/recovery_l10n/res/values-pl/strings.xml b/tools/recovery_l10n/res/values-pl/strings.xml index b1e5b7b66..8d6db388d 100644 --- a/tools/recovery_l10n/res/values-pl/strings.xml +++ b/tools/recovery_l10n/res/values-pl/strings.xml @@ -1,8 +1,9 @@ - "Instaluję aktualizację systemu…" - "Usuwam…" - "Brak polecenia." - "Błąd" + "Instaluję aktualizację systemu" + "Kasuję" + "Brak polecenia" + "Błąd" + "Instaluję aktualizację zabezpieczeń" diff --git a/tools/recovery_l10n/res/values-pt-rBR/strings.xml b/tools/recovery_l10n/res/values-pt-rBR/strings.xml index 3cc57234e..b72704385 100644 --- a/tools/recovery_l10n/res/values-pt-rBR/strings.xml +++ b/tools/recovery_l10n/res/values-pt-rBR/strings.xml @@ -1,8 +1,9 @@ - "Instalando atualização do sistema..." - "Apagando..." - "Nenhum comando." - "Erro!" + "Instalando atualização do sistema" + "Apagando" + "Nenhum comando" + "Erro!" + "Instalando atualização de segurança" diff --git a/tools/recovery_l10n/res/values-pt-rPT/strings.xml b/tools/recovery_l10n/res/values-pt-rPT/strings.xml index 7d6bc18a9..981463739 100644 --- a/tools/recovery_l10n/res/values-pt-rPT/strings.xml +++ b/tools/recovery_l10n/res/values-pt-rPT/strings.xml @@ -1,8 +1,9 @@ - "A instalar a atualização do sistema..." - "A apagar…" - "Nenhum comando." - "Erro!" + "A instalar atualização do sistema" + "A apagar" + "Nenhum comando" + "Erro!" + "A instalar atualização de segurança" diff --git a/tools/recovery_l10n/res/values-pt/strings.xml b/tools/recovery_l10n/res/values-pt/strings.xml index 3cc57234e..b72704385 100644 --- a/tools/recovery_l10n/res/values-pt/strings.xml +++ b/tools/recovery_l10n/res/values-pt/strings.xml @@ -1,8 +1,9 @@ - "Instalando atualização do sistema..." - "Apagando..." - "Nenhum comando." - "Erro!" + "Instalando atualização do sistema" + "Apagando" + "Nenhum comando" + "Erro!" + "Instalando atualização de segurança" diff --git a/tools/recovery_l10n/res/values-ro/strings.xml b/tools/recovery_l10n/res/values-ro/strings.xml index ad924da08..8032865b8 100644 --- a/tools/recovery_l10n/res/values-ro/strings.xml +++ b/tools/recovery_l10n/res/values-ro/strings.xml @@ -1,8 +1,9 @@ - "Se instalează actualizarea de sistem…" - "Se efectuează ștergerea…" - "Nicio comandă." - "Eroare!" + "Se instalează actualizarea de sistem" + "Se șterge" + "Nicio comandă" + "Eroare!" + "Se instalează actualizarea de securitate" diff --git a/tools/recovery_l10n/res/values-ru/strings.xml b/tools/recovery_l10n/res/values-ru/strings.xml index de0da4004..feebecf31 100644 --- a/tools/recovery_l10n/res/values-ru/strings.xml +++ b/tools/recovery_l10n/res/values-ru/strings.xml @@ -1,8 +1,9 @@ - "Установка обновления системы…" - "Удаление…" - "Команды нет" - "Ошибка" + "Установка обновления системы…" + "Удаление…" + "Команды нет" + "Ошибка" + "Установка обновления системы безопасности…" diff --git a/tools/recovery_l10n/res/values-si-rLK/strings.xml b/tools/recovery_l10n/res/values-si-rLK/strings.xml index e717a9762..456cdc567 100644 --- a/tools/recovery_l10n/res/values-si-rLK/strings.xml +++ b/tools/recovery_l10n/res/values-si-rLK/strings.xml @@ -1,8 +1,9 @@ - "පද්ධති යාවත්කාල ස්ථාපනය කරමින්…" - "මකමින්...." - "විධානයක් නොමැත." - "දෝෂය!" + "පද්ධති යාවත්කාලීනය ස්ථාපනය කරමින්" + "මකමින්" + "විධානයක් නොමැත" + "දෝෂය!" + "ආරක්ෂක යාවත්කාලීනය ස්ථාපනය කරමින්" diff --git a/tools/recovery_l10n/res/values-sk/strings.xml b/tools/recovery_l10n/res/values-sk/strings.xml index cae6bce7c..b15f3802b 100644 --- a/tools/recovery_l10n/res/values-sk/strings.xml +++ b/tools/recovery_l10n/res/values-sk/strings.xml @@ -1,8 +1,9 @@ - "Inštalácia aktualizácie systému..." - "Prebieha mazanie..." - "Žiadny príkaz." - "Chyba!" + "Inštaluje sa aktualizácia systému" + "Prebieha vymazávanie" + "Žiadny príkaz" + "Chyba!" + "Inštaluje sa bezpečnostná aktualizácia" diff --git a/tools/recovery_l10n/res/values-sl/strings.xml b/tools/recovery_l10n/res/values-sl/strings.xml index 3f8d46fe6..d608b7506 100644 --- a/tools/recovery_l10n/res/values-sl/strings.xml +++ b/tools/recovery_l10n/res/values-sl/strings.xml @@ -1,8 +1,9 @@ - "Namestitev posodobitve sistema ..." - "Brisanje ..." - "Ni ukaza" - "Napaka" + "Nameščanje posodobitve sistema" + "Brisanje" + "Ni ukaza" + "Napaka" + "Nameščanje varnostne posodobitve" diff --git a/tools/recovery_l10n/res/values-sq-rAL/strings.xml b/tools/recovery_l10n/res/values-sq-rAL/strings.xml index 29f8ef592..1156931fb 100644 --- a/tools/recovery_l10n/res/values-sq-rAL/strings.xml +++ b/tools/recovery_l10n/res/values-sq-rAL/strings.xml @@ -1,8 +1,9 @@ - "Po instalon përditësimin e sistemit..." - "Po spastron..." - "Nuk ka komanda." - "Gabim!" + "Po instalon përditësimin e sistemit" + "Po spastron" + "Nuk ka komanda" + "Gabim!" + "Po instalon përditësimin e sigurisë" diff --git a/tools/recovery_l10n/res/values-sr/strings.xml b/tools/recovery_l10n/res/values-sr/strings.xml index 955326053..a593d8faa 100644 --- a/tools/recovery_l10n/res/values-sr/strings.xml +++ b/tools/recovery_l10n/res/values-sr/strings.xml @@ -1,8 +1,9 @@ - "Инсталирање ажурирања система..." - "Брисање..." - "Нема команде." - "Грешка!" + "Ажурирање система се инсталира" + "Брише се" + "Нема команде" + "Грешка!" + "Инсталира се безбедносно ажурирање" diff --git a/tools/recovery_l10n/res/values-sv/strings.xml b/tools/recovery_l10n/res/values-sv/strings.xml index f875d3008..b33ce253f 100644 --- a/tools/recovery_l10n/res/values-sv/strings.xml +++ b/tools/recovery_l10n/res/values-sv/strings.xml @@ -1,8 +1,9 @@ - "Installerar systemuppdatering ..." - "Tar bort ..." - "Inget kommando." - "Fel!" + "Systemuppdatering installeras" + "Rensar" + "Inget kommando" + "Fel!" + "Säkerhetsuppdatering installeras" diff --git a/tools/recovery_l10n/res/values-sw/strings.xml b/tools/recovery_l10n/res/values-sw/strings.xml index 1a5304649..156765881 100644 --- a/tools/recovery_l10n/res/values-sw/strings.xml +++ b/tools/recovery_l10n/res/values-sw/strings.xml @@ -1,8 +1,9 @@ - "Inasakinisha sasisho la mfumo…" - "Inafuta…" - "Hakuna amri." - "Hitilafu!" + "Inasakinisha sasisho la mfumo" + "Inafuta" + "Hakuna amri" + "Hitilafu fulani imetokea!" + "Inasakinisha sasisho la usalama" diff --git a/tools/recovery_l10n/res/values-ta-rIN/strings.xml b/tools/recovery_l10n/res/values-ta-rIN/strings.xml index f6f3e0e6a..d49186d8d 100644 --- a/tools/recovery_l10n/res/values-ta-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-ta-rIN/strings.xml @@ -1,8 +1,9 @@ - "முறைமை புதுப்பிப்பை நிறுவுகிறது…" - "அழிக்கிறது…" - "கட்டளை இல்லை." - "பிழை!" + "முறைமைப் புதுப்பிப்பை நிறுவுகிறது" + "அழிக்கிறது" + "கட்டளை இல்லை" + "பிழை!" + "பாதுகாப்புப் புதுப்பிப்பை நிறுவுகிறது" diff --git a/tools/recovery_l10n/res/values-te-rIN/strings.xml b/tools/recovery_l10n/res/values-te-rIN/strings.xml index 6d0d17af5..cfb02c915 100644 --- a/tools/recovery_l10n/res/values-te-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-te-rIN/strings.xml @@ -1,8 +1,9 @@ - "సిస్టమ్ నవీకరణను ఇన్‍స్టాల్ చేస్తోంది…" - "ఎరేజ్ చేస్తోంది…" - "ఆదేశం లేదు." - "లోపం!" + "సిస్టమ్ నవీకరణను ఇన్‍స్టాల్ చేస్తోంది" + "డేటాను తొలగిస్తోంది" + "ఆదేశం లేదు" + "లోపం సంభవించింది!" + "భద్రతా నవీకరణను ఇన్‌స్టాల్ చేస్తోంది" diff --git a/tools/recovery_l10n/res/values-th/strings.xml b/tools/recovery_l10n/res/values-th/strings.xml index bcdfa2b25..155affea0 100644 --- a/tools/recovery_l10n/res/values-th/strings.xml +++ b/tools/recovery_l10n/res/values-th/strings.xml @@ -1,8 +1,9 @@ - "กำลังติดตั้งการอัปเดตระบบ…" - "กำลังลบ…" - "ไม่มีคำสั่ง" - "ข้อผิดพลาด!" + "กำลังติดตั้งการอัปเดตระบบ" + "กำลังลบ" + "ไม่มีคำสั่ง" + "ข้อผิดพลาด!" + "กำลังติดตั้งการอัปเดตความปลอดภัย" diff --git a/tools/recovery_l10n/res/values-tl/strings.xml b/tools/recovery_l10n/res/values-tl/strings.xml index be2ba264c..555b42b8d 100644 --- a/tools/recovery_l10n/res/values-tl/strings.xml +++ b/tools/recovery_l10n/res/values-tl/strings.xml @@ -1,8 +1,9 @@ - "Ini-install ang update sa system…" - "Binubura…" - "Walang command." - "Error!" + "Nag-i-install ng pag-update ng system" + "Binubura" + "Walang command" + "Error!" + "Nag-i-install ng update sa seguridad" diff --git a/tools/recovery_l10n/res/values-tr/strings.xml b/tools/recovery_l10n/res/values-tr/strings.xml index 8629029ca..5387cb2ae 100644 --- a/tools/recovery_l10n/res/values-tr/strings.xml +++ b/tools/recovery_l10n/res/values-tr/strings.xml @@ -1,8 +1,9 @@ - "Sistem güncellemesi yükleniyor…" - "Siliniyor…" - "Komut yok." - "Hata!" + "Sistem güncellemesi yükleniyor" + "Siliniyor" + "Komut yok" + "Hata!" + "Güvenlik güncellemesi yükleniyor" diff --git a/tools/recovery_l10n/res/values-uk/strings.xml b/tools/recovery_l10n/res/values-uk/strings.xml index 762c06ff3..0c2fa164a 100644 --- a/tools/recovery_l10n/res/values-uk/strings.xml +++ b/tools/recovery_l10n/res/values-uk/strings.xml @@ -1,8 +1,9 @@ - "Встановлення оновлення системи…" - "Стирання…" - "Немає команди." - "Помилка!" + "Установлюється оновлення системи" + "Стирання" + "Немає команди" + "Помилка!" + "Установлюється оновлення системи безпеки" diff --git a/tools/recovery_l10n/res/values-ur-rPK/strings.xml b/tools/recovery_l10n/res/values-ur-rPK/strings.xml index dc6eb6aa1..12e32fbc1 100644 --- a/tools/recovery_l10n/res/values-ur-rPK/strings.xml +++ b/tools/recovery_l10n/res/values-ur-rPK/strings.xml @@ -1,8 +1,9 @@ - "سسٹم اپ ڈیٹ انسٹال ہو رہا ہے…" - "صاف کر رہا ہے…" - "کوئی کمانڈ نہیں ہے۔" - "خرابی!" + "سسٹم اپ ڈیٹ انسٹال ہو رہی ہے" + "صاف ہو رہا ہے" + "کوئی کمانڈ نہیں ہے" + "خرابی!" + "سیکیورٹی اپ ڈیٹ انسٹال ہو رہی ہے" diff --git a/tools/recovery_l10n/res/values-uz-rUZ/strings.xml b/tools/recovery_l10n/res/values-uz-rUZ/strings.xml index 287448418..2c309d646 100644 --- a/tools/recovery_l10n/res/values-uz-rUZ/strings.xml +++ b/tools/recovery_l10n/res/values-uz-rUZ/strings.xml @@ -1,8 +1,9 @@ - "Tizim yangilanishi o‘rnatilmoqda…" - "Tozalanmoqda…" - "Buyruq yo‘q." - "Xato!" + "Tizim yangilanishi o‘rnatilmoqda" + "Tozalanmoqda…" + "Buyruq yo‘q" + "Xato!" + "Xavfsizlik yangilanishi o‘rnatilmoqda" diff --git a/tools/recovery_l10n/res/values-vi/strings.xml b/tools/recovery_l10n/res/values-vi/strings.xml index ab4005b7f..c77d0c8c2 100644 --- a/tools/recovery_l10n/res/values-vi/strings.xml +++ b/tools/recovery_l10n/res/values-vi/strings.xml @@ -1,8 +1,9 @@ - "Đang cài đặt bản cập nhật hệ thống…" - "Đang xóa…" - "Không có lệnh nào." - "Lỗi!" + "Đang cài đặt bản cập nhật hệ thống" + "Đang xóa" + "Không có lệnh nào" + "Lỗi!" + "Đang cài đặt bản cập nhật bảo mật" diff --git a/tools/recovery_l10n/res/values-zh-rCN/strings.xml b/tools/recovery_l10n/res/values-zh-rCN/strings.xml index 2e1a6f57f..e06149791 100644 --- a/tools/recovery_l10n/res/values-zh-rCN/strings.xml +++ b/tools/recovery_l10n/res/values-zh-rCN/strings.xml @@ -1,8 +1,9 @@ - "正在安装系统更新…" - "正在清除…" - "无命令。" - "出错了!" + "正在安装系统更新" + "正在清空" + "无命令" + "出错了!" + "正在安装安全更新" diff --git a/tools/recovery_l10n/res/values-zh-rHK/strings.xml b/tools/recovery_l10n/res/values-zh-rHK/strings.xml index f615c7a29..ec3315d32 100644 --- a/tools/recovery_l10n/res/values-zh-rHK/strings.xml +++ b/tools/recovery_l10n/res/values-zh-rHK/strings.xml @@ -1,8 +1,9 @@ - "正在安裝系統更新…" - "正在清除…" - "沒有指令。" - "錯誤!" + "正在安裝系統更新" + "正在清除" + "沒有指令" + "錯誤!" + "正在安裝安全性更新" diff --git a/tools/recovery_l10n/res/values-zh-rTW/strings.xml b/tools/recovery_l10n/res/values-zh-rTW/strings.xml index f3f6a2c21..78eae2429 100644 --- a/tools/recovery_l10n/res/values-zh-rTW/strings.xml +++ b/tools/recovery_l10n/res/values-zh-rTW/strings.xml @@ -1,8 +1,9 @@ - "正在安裝系統更新…" - "清除中..." - "沒有指令。" - "錯誤!" + "正在安裝系統更新" + "清除中" + "沒有指令" + "錯誤!" + "正在安裝安全性更新" diff --git a/tools/recovery_l10n/res/values-zu/strings.xml b/tools/recovery_l10n/res/values-zu/strings.xml index 1f904a203..6b815e1ab 100644 --- a/tools/recovery_l10n/res/values-zu/strings.xml +++ b/tools/recovery_l10n/res/values-zu/strings.xml @@ -1,8 +1,9 @@ - "Ifaka isibuyekezo sesistimu…" - "Iyasula…" - "Awukho umyalo." - "Iphutha!" + "Ifaka isibuyekezo sesistimu" + "Iyasula" + "Awukho umyalo" + "Iphutha!" + "Ifaka isibuyekezo sokuphepha" -- cgit v1.2.3 From c3ef089dfac921ef6ca04d5341a4035e8c4feb51 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 4 Oct 2016 18:03:57 -0700 Subject: applypatch: Remove duplicate test files. CL in commit b8a693bbc73808924f4be8c4d47bbc4da0647e3a has ported the tests to gtest-based recovery_component_tests. Test: N/A Change-Id: I485fa11413df68a11416d4a9f91f090f302bcfc0 --- applypatch/applypatch.sh | 350 --------------------------------------- applypatch/imgdiff_test.sh | 118 ------------- applypatch/testdata/new.file | Bin 1388877 -> 0 bytes applypatch/testdata/old.file | Bin 1348051 -> 0 bytes applypatch/testdata/patch.bsdiff | Bin 57476 -> 0 bytes 5 files changed, 468 deletions(-) delete mode 100755 applypatch/applypatch.sh delete mode 100755 applypatch/imgdiff_test.sh delete mode 100644 applypatch/testdata/new.file delete mode 100644 applypatch/testdata/old.file delete mode 100644 applypatch/testdata/patch.bsdiff diff --git a/applypatch/applypatch.sh b/applypatch/applypatch.sh deleted file mode 100755 index 8ea68a1a9..000000000 --- a/applypatch/applypatch.sh +++ /dev/null @@ -1,350 +0,0 @@ -#!/bin/bash -# -# A test suite for applypatch. Run in a client where you have done -# envsetup, choosecombo, etc. -# -# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your -# system partition. -# -# -# TODO: find some way to get this run regularly along with the rest of -# the tests. - -EMULATOR_PORT=5580 -DATA_DIR=$ANDROID_BUILD_TOP/bootable/recovery/applypatch/testdata - -# This must be the filename that applypatch uses for its copies. -CACHE_TEMP_SOURCE=/cache/saved.file - -# Put all binaries and files here. We use /cache because it's a -# temporary filesystem in the emulator; it's created fresh each time -# the emulator starts. -WORK_DIR=/system - -# partition that WORK_DIR is located on, without the leading slash -WORK_FS=system - -# set to 0 to use a device instead -USE_EMULATOR=1 - -# ------------------------ - -tmpdir=$(mktemp -d) - -if [ "$USE_EMULATOR" == 1 ]; then - emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT & - pid_emulator=$! - ADB="adb -s emulator-$EMULATOR_PORT " -else - ADB="adb -d " -fi - -echo "waiting to connect to device" -$ADB wait-for-device -echo "device is available" -$ADB remount -# free up enough space on the system partition for the test to run. -$ADB shell rm -r /system/media - -# run a command on the device; exit with the exit status of the device -# command. -run_command() { - $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}' -} - -testname() { - echo - echo "$1"... - testname="$1" -} - -fail() { - echo - echo FAIL: $testname - echo - [ "$open_pid" == "" ] || kill $open_pid - [ "$pid_emulator" == "" ] || kill $pid_emulator - exit 1 -} - -sha1() { - sha1sum $1 | awk '{print $1}' -} - -free_space() { - run_command df | awk "/$1/ {print gensub(/K/, \"\", \"g\", \$6)}" -} - -cleanup() { - # not necessary if we're about to kill the emulator, but nice for - # running on real devices or already-running emulators. - testname "removing test files" - run_command rm $WORK_DIR/bloat.dat - run_command rm $WORK_DIR/old.file - run_command rm $WORK_DIR/foo - run_command rm $WORK_DIR/patch.bsdiff - run_command rm $WORK_DIR/applypatch - run_command rm $CACHE_TEMP_SOURCE - run_command rm /cache/bloat*.dat - - [ "$pid_emulator" == "" ] || kill $pid_emulator - - if [ $# == 0 ]; then - rm -rf $tmpdir - fi -} - -cleanup leave_tmp - -$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch - -BAD1_SHA1=$(printf "%040x" $RANDOM) -BAD2_SHA1=$(printf "%040x" $RANDOM) -OLD_SHA1=$(sha1 $DATA_DIR/old.file) -NEW_SHA1=$(sha1 $DATA_DIR/new.file) -NEW_SIZE=$(stat -c %s $DATA_DIR/new.file) - -# --------------- basic execution ---------------------- - -testname "usage message" -run_command $WORK_DIR/applypatch && fail - -testname "display license" -run_command $WORK_DIR/applypatch -l | grep -q -i copyright || fail - - -# --------------- check mode ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR - -testname "check mode single" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail - -testname "check mode multiple" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail - -testname "check mode failure" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail - -$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE -# put some junk in the old file -run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail - -testname "check mode cache (corrupted) single" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail - -testname "check mode cache (corrupted) multiple" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail - -testname "check mode cache (corrupted) failure" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail - -# remove the old file entirely -run_command rm $WORK_DIR/old.file - -testname "check mode cache (missing) single" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail - -testname "check mode cache (missing) multiple" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail - -testname "check mode cache (missing) failure" -run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail - - -# --------------- apply patch ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR -$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR -echo hello > $tmpdir/foo -$ADB push $tmpdir/foo $WORK_DIR - -# Check that the partition has enough space to apply the patch without -# copying. If it doesn't, we'll be testing the low-space condition -# when we intend to test the not-low-space condition. -testname "apply patches (with enough space)" -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS." -if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then - echo "Not enough space on /$WORK_FS to patch test file." - echo - echo "This doesn't mean that applypatch is necessarily broken;" - echo "just that /$WORK_FS doesn't have enough free space to" - echo "properly run this test." - exit 1 -fi - -testname "apply bsdiff patch" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -testname "reapply bsdiff patch" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - - -# --------------- apply patch in new location ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR -$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR - -# Check that the partition has enough space to apply the patch without -# copying. If it doesn't, we'll be testing the low-space condition -# when we intend to test the not-low-space condition. -testname "apply patch to new location (with enough space)" -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS." -if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then - echo "Not enough space on /$WORK_FS to patch test file." - echo - echo "This doesn't mean that applypatch is necessarily broken;" - echo "just that /$WORK_FS doesn't have enough free space to" - echo "properly run this test." - exit 1 -fi - -run_command rm $WORK_DIR/new.file -run_command rm $CACHE_TEMP_SOURCE - -testname "apply bsdiff patch to new location" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/new.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -testname "reapply bsdiff patch to new location" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/new.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE -# put some junk in the old file -run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail - -testname "apply bsdiff patch to new location with corrupted source" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo || fail -$ADB pull $WORK_DIR/new.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -# put some junk in the cache copy, too -run_command dd if=/dev/urandom of=$CACHE_TEMP_SOURCE count=100 bs=1024 || fail - -run_command rm $WORK_DIR/new.file -testname "apply bsdiff patch to new location with corrupted source and copy (no new file)" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail - -# put some junk in the new file -run_command dd if=/dev/urandom of=$WORK_DIR/new.file count=100 bs=1024 || fail - -testname "apply bsdiff patch to new location with corrupted source and copy (bad new file)" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file $WORK_DIR/new.file $NEW_SHA1 $NEW_SIZE $OLD_SHA1:$WORK_DIR/patch.bsdiff $BAD1_SHA1:$WORK_DIR/foo && fail - -# --------------- apply patch with low space on /system ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR -$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR - -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS; we'll soon fix that." -echo run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail -run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS now." - -testname "apply bsdiff patch with low space" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -testname "reapply bsdiff patch with low space" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -# --------------- apply patch with low space on /system and /cache ---------------------- - -$ADB push $DATA_DIR/old.file $WORK_DIR -$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR - -free_kb=$(free_space $WORK_FS) -echo "${free_kb}kb free on /$WORK_FS" - -run_command mkdir /cache/subdir -run_command 'echo > /cache/subdir/a.file' -run_command 'echo > /cache/a.file' -run_command mkdir /cache/recovery /cache/recovery/otatest -run_command 'echo > /cache/recovery/otatest/b.file' -run_command "echo > $CACHE_TEMP_SOURCE" -free_kb=$(free_space cache) -echo "${free_kb}kb free on /cache; we'll soon fix that." -run_command dd if=/dev/zero of=/cache/bloat_small.dat count=128 bs=1024 || fail -run_command dd if=/dev/zero of=/cache/bloat_large.dat count=$((free_kb-640)) bs=1024 || fail -free_kb=$(free_space cache) -echo "${free_kb}kb free on /cache now." - -testname "apply bsdiff patch with low space, full cache, can't delete enough" -$ADB shell 'cat >> /cache/bloat_large.dat' & open_pid=$! -echo "open_pid is $open_pid" - -# size check should fail even though it deletes some stuff -run_command $WORK_DIR/applypatch -s $NEW_SIZE && fail -run_command ls /cache/bloat_small.dat && fail # was deleted -run_command ls /cache/a.file && fail # was deleted -run_command ls /cache/recovery/otatest/b.file && fail # was deleted -run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open -run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir -run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy - -# should fail; not enough files can be deleted -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff && fail -run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open -run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir -run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy - -kill $open_pid # /cache/bloat_large.dat is no longer open - -testname "apply bsdiff patch with low space, full cache, can delete enough" - -# should succeed after deleting /cache/bloat_large.dat -run_command $WORK_DIR/applypatch -s $NEW_SIZE || fail -run_command ls /cache/bloat_large.dat && fail # was deleted -run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir -run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy - -# should succeed -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail -run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir -run_command ls $CACHE_TEMP_SOURCE && fail # was deleted because patching overwrote it, then deleted it - -# --------------- apply patch from cache ---------------------- - -$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE -# put some junk in the old file -run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail - -testname "apply bsdiff patch from cache (corrupted source) with low space" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - -$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE -# remove the old file entirely -run_command rm $WORK_DIR/old.file - -testname "apply bsdiff patch from cache (missing source) with low space" -run_command $WORK_DIR/applypatch $WORK_DIR/old.file - $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail -$ADB pull $WORK_DIR/old.file $tmpdir/patched -diff -q $DATA_DIR/new.file $tmpdir/patched || fail - - -# --------------- cleanup ---------------------- - -cleanup - -echo -echo PASS -echo - diff --git a/applypatch/imgdiff_test.sh b/applypatch/imgdiff_test.sh deleted file mode 100755 index dcdb922b4..000000000 --- a/applypatch/imgdiff_test.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/bash -# -# A script for testing imgdiff/applypatch. It takes two full OTA -# packages as arguments. It generates (on the host) patches for all -# the zip/jar/apk files they have in common, as well as boot and -# recovery images. It then applies the patches on the device (or -# emulator) and checks that the resulting file is correct. - -EMULATOR_PORT=5580 - -# set to 0 to use a device instead -USE_EMULATOR=0 - -# where on the device to do all the patching. -WORK_DIR=/data/local/tmp - -START_OTA_PACKAGE=$1 -END_OTA_PACKAGE=$2 - -# ------------------------ - -tmpdir=$(mktemp -d) - -if [ "$USE_EMULATOR" == 1 ]; then - emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT & - pid_emulator=$! - ADB="adb -s emulator-$EMULATOR_PORT " -else - ADB="adb -d " -fi - -echo "waiting to connect to device" -$ADB wait-for-device - -# run a command on the device; exit with the exit status of the device -# command. -run_command() { - $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}' -} - -testname() { - echo - echo "$1"... - testname="$1" -} - -fail() { - echo - echo FAIL: $testname - echo - [ "$open_pid" == "" ] || kill $open_pid - [ "$pid_emulator" == "" ] || kill $pid_emulator - exit 1 -} - -sha1() { - sha1sum $1 | awk '{print $1}' -} - -size() { - stat -c %s $1 | tr -d '\n' -} - -cleanup() { - # not necessary if we're about to kill the emulator, but nice for - # running on real devices or already-running emulators. - testname "removing test files" - run_command rm $WORK_DIR/applypatch - run_command rm $WORK_DIR/source - run_command rm $WORK_DIR/target - run_command rm $WORK_DIR/patch - - [ "$pid_emulator" == "" ] || kill $pid_emulator - - rm -rf $tmpdir -} - -$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch - -patch_and_apply() { - local fn=$1 - shift - - unzip -p $START_OTA_PACKAGE $fn > $tmpdir/source - unzip -p $END_OTA_PACKAGE $fn > $tmpdir/target - imgdiff "$@" $tmpdir/source $tmpdir/target $tmpdir/patch - bsdiff $tmpdir/source $tmpdir/target $tmpdir/patch.bs - echo "patch for $fn is $(size $tmpdir/patch) [of $(size $tmpdir/target)] ($(size $tmpdir/patch.bs) with bsdiff)" - echo "$fn $(size $tmpdir/patch) of $(size $tmpdir/target) bsdiff $(size $tmpdir/patch.bs)" >> /tmp/stats.txt - $ADB push $tmpdir/source $WORK_DIR/source || fail "source push failed" - run_command rm /data/local/tmp/target - $ADB push $tmpdir/patch $WORK_DIR/patch || fail "patch push failed" - run_command /data/local/tmp/applypatch /data/local/tmp/source \ - /data/local/tmp/target $(sha1 $tmpdir/target) $(size $tmpdir/target) \ - $(sha1 $tmpdir/source):/data/local/tmp/patch \ - || fail "applypatch of $fn failed" - $ADB pull /data/local/tmp/target $tmpdir/result - diff -q $tmpdir/target $tmpdir/result || fail "patch output not correct!" -} - -# --------------- basic execution ---------------------- - -for i in $((zipinfo -1 $START_OTA_PACKAGE; zipinfo -1 $END_OTA_PACKAGE) | \ - sort | uniq -d | egrep -e '[.](apk|jar|zip)$'); do - patch_and_apply $i -z -done -patch_and_apply boot.img -patch_and_apply system/recovery.img - - -# --------------- cleanup ---------------------- - -cleanup - -echo -echo PASS -echo - diff --git a/applypatch/testdata/new.file b/applypatch/testdata/new.file deleted file mode 100644 index cdeb8fd50..000000000 Binary files a/applypatch/testdata/new.file and /dev/null differ diff --git a/applypatch/testdata/old.file b/applypatch/testdata/old.file deleted file mode 100644 index 166c8732e..000000000 Binary files a/applypatch/testdata/old.file and /dev/null differ diff --git a/applypatch/testdata/patch.bsdiff b/applypatch/testdata/patch.bsdiff deleted file mode 100644 index b78d38573..000000000 Binary files a/applypatch/testdata/patch.bsdiff and /dev/null differ -- cgit v1.2.3 From bf5b77dbf73eef715bb49b43e113833efbcb2994 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 30 Mar 2017 16:57:29 -0700 Subject: Change the internal representation in RangeSet. This CL makes the following changes to RangeSet: - Uses std::pair to represent a Range; - Uses std::vector to represent a RangeSet; - Provides const iterators (forward and reverse); - Provides const accessor; - 'blocks()' returns the number of blocks (formerly 'size'); - 'size()' returns the number of Range's (formerly 'count'). Test: recovery_unit_test Test: Apply an incremental update with the new updater. Change-Id: Ia1fbb343370a152e1f7aa050cf914c2da09b1396 --- tests/unit/rangeset_test.cpp | 40 +++- updater/blockimg.cpp | 392 ++++++++++++++++++------------------- updater/include/updater/rangeset.h | 125 +++++++++--- 3 files changed, 318 insertions(+), 239 deletions(-) diff --git a/tests/unit/rangeset_test.cpp b/tests/unit/rangeset_test.cpp index e66da20e4..3c6d77ef5 100644 --- a/tests/unit/rangeset_test.cpp +++ b/tests/unit/rangeset_test.cpp @@ -25,14 +25,15 @@ TEST(RangeSetTest, Parse_smoke) { RangeSet rs = RangeSet::Parse("2,1,10"); - ASSERT_EQ(static_cast(1), rs.count); - ASSERT_EQ((std::vector{ 1, 10 }), rs.pos); - ASSERT_EQ(static_cast(9), rs.size); + ASSERT_EQ(static_cast(1), rs.size()); + ASSERT_EQ((Range{ 1, 10 }), rs[0]); + ASSERT_EQ(static_cast(9), rs.blocks()); RangeSet rs2 = RangeSet::Parse("4,15,20,1,10"); - ASSERT_EQ(static_cast(2), rs2.count); - ASSERT_EQ((std::vector{ 15, 20, 1, 10 }), rs2.pos); - ASSERT_EQ(static_cast(14), rs2.size); + ASSERT_EQ(static_cast(2), rs2.size()); + ASSERT_EQ((Range{ 15, 20 }), rs2[0]); + ASSERT_EQ((Range{ 1, 10 }), rs2[1]); + ASSERT_EQ(static_cast(14), rs2.blocks()); // Leading zeros are fine. But android::base::ParseUint() doesn't like trailing zeros like "10 ". ASSERT_EQ(rs, RangeSet::Parse(" 2, 1, 10")); @@ -82,3 +83,30 @@ TEST(RangeSetTest, GetBlockNumber) { // Out of bound. ASSERT_EXIT(rs.GetBlockNumber(9), ::testing::KilledBySignal(SIGABRT), ""); } + +TEST(RangeSetTest, equality) { + ASSERT_EQ(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,1,6")); + + ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,1,7")); + ASSERT_NE(RangeSet::Parse("2,1,6"), RangeSet::Parse("2,2,7")); + + // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5". + ASSERT_NE(RangeSet::Parse("4,1,5,8,10"), RangeSet::Parse("4,8,10,1,5")); +} + +TEST(RangeSetTest, iterators) { + RangeSet rs = RangeSet::Parse("4,1,5,8,10"); + std::vector ranges; + for (const auto& range : rs) { + ranges.push_back(range); + } + ASSERT_EQ((std::vector{ Range{ 1, 5 }, Range{ 8, 10 } }), ranges); + + ranges.clear(); + + // Reverse iterators. + for (auto it = rs.crbegin(); it != rs.crend(); it++) { + ranges.push_back(*it); + } + ASSERT_EQ((std::vector{ Range{ 8, 10 }, Range{ 1, 5 } }), ranges); +} diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 8199447a9..0f08d17eb 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -112,18 +112,17 @@ static int write_all(int fd, const std::vector& buffer, size_t size) { } static bool discard_blocks(int fd, off64_t offset, uint64_t size) { - // Don't discard blocks unless the update is a retry run. - if (!is_retry) { - return true; - } - - uint64_t args[2] = {static_cast(offset), size}; - int status = ioctl(fd, BLKDISCARD, &args); - if (status == -1) { - PLOG(ERROR) << "BLKDISCARD ioctl failed"; - return false; - } + // Don't discard blocks unless the update is a retry run. + if (!is_retry) { return true; + } + + uint64_t args[2] = { static_cast(offset), size }; + if (ioctl(fd, BLKDISCARD, &args) == -1) { + PLOG(ERROR) << "BLKDISCARD ioctl failed"; + return false; + } + return true; } static bool check_lseek(int fd, off64_t offset, int whence) { @@ -151,11 +150,11 @@ class RangeSinkWriter { public: RangeSinkWriter(int fd, const RangeSet& tgt) : fd_(fd), tgt_(tgt), next_range_(0), current_range_left_(0) { - CHECK_NE(tgt.count, static_cast(0)); + CHECK_NE(tgt.size(), static_cast(0)); }; bool Finished() const { - return next_range_ == tgt_.count && current_range_left_ == 0; + return next_range_ == tgt_.size() && current_range_left_ == 0; } size_t Write(const uint8_t* data, size_t size) { @@ -168,10 +167,10 @@ class RangeSinkWriter { while (size > 0) { // Move to the next range as needed. if (current_range_left_ == 0) { - if (next_range_ < tgt_.count) { - off64_t offset = static_cast(tgt_.pos[next_range_ * 2]) * BLOCKSIZE; - current_range_left_ = - (tgt_.pos[next_range_ * 2 + 1] - tgt_.pos[next_range_ * 2]) * BLOCKSIZE; + if (next_range_ < tgt_.size()) { + const Range& range = tgt_[next_range_]; + off64_t offset = static_cast(range.first) * BLOCKSIZE; + current_range_left_ = (range.second - range.first) * BLOCKSIZE; next_range_++; if (!discard_blocks(fd_, offset, current_range_left_)) { break; @@ -281,31 +280,28 @@ static void* unzip_new_data(void* cookie) { } static int ReadBlocks(const RangeSet& src, std::vector& buffer, int fd) { - size_t p = 0; - uint8_t* data = buffer.data(); - - for (size_t i = 0; i < src.count; ++i) { - if (!check_lseek(fd, (off64_t) src.pos[i * 2] * BLOCKSIZE, SEEK_SET)) { - return -1; - } - - size_t size = (src.pos[i * 2 + 1] - src.pos[i * 2]) * BLOCKSIZE; - - if (read_all(fd, data + p, size) == -1) { - return -1; - } + size_t p = 0; + for (const auto& range : src) { + if (!check_lseek(fd, static_cast(range.first) * BLOCKSIZE, SEEK_SET)) { + return -1; + } - p += size; + size_t size = (range.second - range.first) * BLOCKSIZE; + if (read_all(fd, buffer.data() + p, size) == -1) { + return -1; } - return 0; + p += size; + } + + return 0; } static int WriteBlocks(const RangeSet& tgt, const std::vector& buffer, int fd) { size_t written = 0; - for (size_t i = 0; i < tgt.count; ++i) { - off64_t offset = static_cast(tgt.pos[i * 2]) * BLOCKSIZE; - size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE; + for (const auto& range : tgt) { + off64_t offset = static_cast(range.first) * BLOCKSIZE; + size_t size = (range.second - range.first) * BLOCKSIZE; if (!discard_blocks(fd, offset, size)) { return -1; } @@ -386,21 +382,18 @@ static void PrintHashForCorruptedSourceBlocks(const CommandParameters& params, // If there's no stashed blocks, content in the buffer is consecutive and has the same // order as the source blocks. if (pos == params.tokens.size()) { - locs.count = 1; - locs.size = src.size; - locs.pos = { 0, src.size }; + locs = RangeSet(std::vector{ Range{ 0, src.blocks() } }); } else { // Otherwise, the next token is the offset of the source blocks in the target range. // Example: for the tokens <4,63946,63947,63948,63979> <4,6,7,8,39> ; // We want to print SHA-1 for the data in buffer[6], buffer[8], buffer[9] ... buffer[38]; // this corresponds to the 32 src blocks #63946, #63948, #63949 ... #63978. locs = RangeSet::Parse(params.tokens[pos++]); - CHECK_EQ(src.size, locs.size); - CHECK_EQ(locs.pos.size() % 2, static_cast(0)); + CHECK_EQ(src.blocks(), locs.blocks()); } - LOG(INFO) << "printing hash in hex for " << src.size << " source blocks"; - for (size_t i = 0; i < src.size; i++) { + LOG(INFO) << "printing hash in hex for " << src.blocks() << " source blocks"; + for (size_t i = 0; i < src.blocks(); i++) { size_t block_num = src.GetBlockNumber(i); size_t buffer_index = locs.GetBlockNumber(i); CHECK_LE((buffer_index + 1) * BLOCKSIZE, buffer.size()); @@ -418,9 +411,9 @@ static void PrintHashForCorruptedStashedBlocks(const std::string& id, const std::vector& buffer, const RangeSet& src) { LOG(INFO) << "printing hash in hex for stash_id: " << id; - CHECK_EQ(src.size * BLOCKSIZE, buffer.size()); + CHECK_EQ(src.blocks() * BLOCKSIZE, buffer.size()); - for (size_t i = 0; i < src.size; i++) { + for (size_t i = 0; i < src.blocks(); i++) { size_t block_num = src.GetBlockNumber(i); uint8_t digest[SHA_DIGEST_LENGTH]; @@ -440,7 +433,7 @@ static void PrintHashForMissingStashedBlocks(const std::string& id, int fd) { LOG(INFO) << "print hash in hex for source blocks in missing stash: " << id; const RangeSet& src = stash_map[id]; - std::vector buffer(src.size * BLOCKSIZE); + std::vector buffer(src.blocks() * BLOCKSIZE); if (ReadBlocks(src, buffer, fd) == -1) { LOG(ERROR) << "failed to read source blocks for stash: " << id; return; @@ -532,81 +525,77 @@ static void DeleteStash(const std::string& base) { static int LoadStash(CommandParameters& params, const std::string& id, bool verify, size_t* blocks, std::vector& buffer, bool printnoent) { - // In verify mode, if source range_set was saved for the given hash, - // check contents in the source blocks first. If the check fails, - // search for the stashed files on /cache as usual. - if (!params.canwrite) { - if (stash_map.find(id) != stash_map.end()) { - const RangeSet& src = stash_map[id]; - allocate(src.size * BLOCKSIZE, buffer); - - if (ReadBlocks(src, buffer, params.fd) == -1) { - LOG(ERROR) << "failed to read source blocks in stash map."; - return -1; - } - if (VerifyBlocks(id, buffer, src.size, true) != 0) { - LOG(ERROR) << "failed to verify loaded source blocks in stash map."; - PrintHashForCorruptedStashedBlocks(id, buffer, src); - return -1; - } - return 0; - } - } - - size_t blockcount = 0; + // In verify mode, if source range_set was saved for the given hash, check contents in the source + // blocks first. If the check fails, search for the stashed files on /cache as usual. + if (!params.canwrite) { + if (stash_map.find(id) != stash_map.end()) { + const RangeSet& src = stash_map[id]; + allocate(src.blocks() * BLOCKSIZE, buffer); - if (!blocks) { - blocks = &blockcount; + if (ReadBlocks(src, buffer, params.fd) == -1) { + LOG(ERROR) << "failed to read source blocks in stash map."; + return -1; + } + if (VerifyBlocks(id, buffer, src.blocks(), true) != 0) { + LOG(ERROR) << "failed to verify loaded source blocks in stash map."; + PrintHashForCorruptedStashedBlocks(id, buffer, src); + return -1; + } + return 0; } + } - std::string fn = GetStashFileName(params.stashbase, id, ""); + size_t blockcount = 0; + if (!blocks) { + blocks = &blockcount; + } - struct stat sb; - int res = stat(fn.c_str(), &sb); + std::string fn = GetStashFileName(params.stashbase, id, ""); - if (res == -1) { - if (errno != ENOENT || printnoent) { - PLOG(ERROR) << "stat \"" << fn << "\" failed"; - PrintHashForMissingStashedBlocks(id, params.fd); - } - return -1; + struct stat sb; + if (stat(fn.c_str(), &sb) == -1) { + if (errno != ENOENT || printnoent) { + PLOG(ERROR) << "stat \"" << fn << "\" failed"; + PrintHashForMissingStashedBlocks(id, params.fd); } + return -1; + } - LOG(INFO) << " loading " << fn; + LOG(INFO) << " loading " << fn; - if ((sb.st_size % BLOCKSIZE) != 0) { - LOG(ERROR) << fn << " size " << sb.st_size << " not multiple of block size " << BLOCKSIZE; - return -1; - } + if ((sb.st_size % BLOCKSIZE) != 0) { + LOG(ERROR) << fn << " size " << sb.st_size << " not multiple of block size " << BLOCKSIZE; + return -1; + } - android::base::unique_fd fd(TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_RDONLY))); - if (fd == -1) { - PLOG(ERROR) << "open \"" << fn << "\" failed"; - return -1; - } + android::base::unique_fd fd(TEMP_FAILURE_RETRY(ota_open(fn.c_str(), O_RDONLY))); + if (fd == -1) { + PLOG(ERROR) << "open \"" << fn << "\" failed"; + return -1; + } - allocate(sb.st_size, buffer); + allocate(sb.st_size, buffer); - if (read_all(fd, buffer, sb.st_size) == -1) { - return -1; - } + if (read_all(fd, buffer, sb.st_size) == -1) { + return -1; + } - *blocks = sb.st_size / BLOCKSIZE; + *blocks = sb.st_size / BLOCKSIZE; - if (verify && VerifyBlocks(id, buffer, *blocks, true) != 0) { - LOG(ERROR) << "unexpected contents in " << fn; - if (stash_map.find(id) == stash_map.end()) { - LOG(ERROR) << "failed to find source blocks number for stash " << id - << " when executing command: " << params.cmdname; - } else { - const RangeSet& src = stash_map[id]; - PrintHashForCorruptedStashedBlocks(id, buffer, src); - } - DeleteFile(fn); - return -1; + if (verify && VerifyBlocks(id, buffer, *blocks, true) != 0) { + LOG(ERROR) << "unexpected contents in " << fn; + if (stash_map.find(id) == stash_map.end()) { + LOG(ERROR) << "failed to find source blocks number for stash " << id + << " when executing command: " << params.cmdname; + } else { + const RangeSet& src = stash_map[id]; + PrintHashForCorruptedStashedBlocks(id, buffer, src); } + DeleteFile(fn); + return -1; + } - return 0; + return 0; } static int WriteStash(const std::string& base, const std::string& id, int blocks, @@ -780,21 +769,19 @@ static int FreeStash(const std::string& base, const std::string& id) { return 0; } +// Source contains packed data, which we want to move to the locations given in locs in the dest +// buffer. source and dest may be the same buffer. static void MoveRange(std::vector& dest, const RangeSet& locs, - const std::vector& source) { - // source contains packed data, which we want to move to the - // locations given in locs in the dest buffer. source and dest - // may be the same buffer. - - const uint8_t* from = source.data(); - uint8_t* to = dest.data(); - size_t start = locs.size; - for (int i = locs.count-1; i >= 0; --i) { - size_t blocks = locs.pos[i*2+1] - locs.pos[i*2]; - start -= blocks; - memmove(to + (locs.pos[i*2] * BLOCKSIZE), from + (start * BLOCKSIZE), - blocks * BLOCKSIZE); - } + const std::vector& source) { + const uint8_t* from = source.data(); + uint8_t* to = dest.data(); + size_t start = locs.blocks(); + // Must do the movement backward. + for (auto it = locs.crbegin(); it != locs.crend(); it++) { + size_t blocks = it->second - it->first; + start -= blocks; + memmove(to + (it->first * BLOCKSIZE), from + (start * BLOCKSIZE), blocks * BLOCKSIZE); + } } /** @@ -933,13 +920,13 @@ static int LoadSrcTgtVersion3(CommandParameters& params, RangeSet& tgt, size_t* // tgt = RangeSet::Parse(params.tokens[params.cpos++]); - std::vector tgtbuffer(tgt.size * BLOCKSIZE); + std::vector tgtbuffer(tgt.blocks() * BLOCKSIZE); if (ReadBlocks(tgt, tgtbuffer, params.fd) == -1) { return -1; } // Return now if target blocks already have expected content. - if (VerifyBlocks(tgthash, tgtbuffer, tgt.size, false) == 0) { + if (VerifyBlocks(tgthash, tgtbuffer, tgt.blocks(), false) == 0) { return 1; } @@ -1023,7 +1010,7 @@ static int PerformCommandMove(CommandParameters& params) { params.freestash.clear(); } - params.written += tgt.size; + params.written += tgt.blocks(); return 0; } @@ -1045,11 +1032,11 @@ static int PerformCommandStash(CommandParameters& params) { RangeSet src = RangeSet::Parse(params.tokens[params.cpos++]); - allocate(src.size * BLOCKSIZE, params.buffer); + allocate(src.blocks() * BLOCKSIZE, params.buffer); if (ReadBlocks(src, params.buffer, params.fd) == -1) { return -1; } - blocks = src.size; + blocks = src.blocks(); stash_map[id] = src; if (VerifyBlocks(id, params.buffer, blocks, true) != 0) { @@ -1088,46 +1075,45 @@ static int PerformCommandFree(CommandParameters& params) { } static int PerformCommandZero(CommandParameters& params) { + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing target blocks for zero"; + return -1; + } - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing target blocks for zero"; - return -1; - } + RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); - RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); + LOG(INFO) << " zeroing " << tgt.blocks() << " blocks"; - LOG(INFO) << " zeroing " << tgt.size << " blocks"; + allocate(BLOCKSIZE, params.buffer); + memset(params.buffer.data(), 0, BLOCKSIZE); + + if (params.canwrite) { + for (const auto& range : tgt) { + off64_t offset = static_cast(range.first) * BLOCKSIZE; + size_t size = (range.second - range.first) * BLOCKSIZE; + if (!discard_blocks(params.fd, offset, size)) { + return -1; + } - allocate(BLOCKSIZE, params.buffer); - memset(params.buffer.data(), 0, BLOCKSIZE); + if (!check_lseek(params.fd, offset, SEEK_SET)) { + return -1; + } - if (params.canwrite) { - for (size_t i = 0; i < tgt.count; ++i) { - off64_t offset = static_cast(tgt.pos[i * 2]) * BLOCKSIZE; - size_t size = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * BLOCKSIZE; - if (!discard_blocks(params.fd, offset, size)) { - return -1; - } - - if (!check_lseek(params.fd, offset, SEEK_SET)) { - return -1; - } - - for (size_t j = tgt.pos[i * 2]; j < tgt.pos[i * 2 + 1]; ++j) { - if (write_all(params.fd, params.buffer, BLOCKSIZE) == -1) { - return -1; - } - } + for (size_t j = range.first; j < range.second; ++j) { + if (write_all(params.fd, params.buffer, BLOCKSIZE) == -1) { + return -1; } + } } + } - if (params.cmdname[0] == 'z') { - // Update only for the zero command, as the erase command will call - // this if DEBUG_ERASE is defined. - params.written += tgt.size; - } + if (params.cmdname[0] == 'z') { + // Update only for the zero command, as the erase command will call + // this if DEBUG_ERASE is defined. + params.written += tgt.blocks(); + } - return 0; + return 0; } static int PerformCommandNew(CommandParameters& params) { @@ -1139,7 +1125,7 @@ static int PerformCommandNew(CommandParameters& params) { RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); if (params.canwrite) { - LOG(INFO) << " writing " << tgt.size << " blocks of new data"; + LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data"; RangeSinkWriter writer(params.fd, tgt); pthread_mutex_lock(¶ms.nti.mu); @@ -1153,7 +1139,7 @@ static int PerformCommandNew(CommandParameters& params) { pthread_mutex_unlock(¶ms.nti.mu); } - params.written += tgt.size; + params.written += tgt.blocks(); return 0; } @@ -1195,7 +1181,7 @@ static int PerformCommandDiff(CommandParameters& params) { if (params.canwrite) { if (status == 0) { - LOG(INFO) << "patching " << blocks << " blocks to " << tgt.size; + LOG(INFO) << "patching " << blocks << " blocks to " << tgt.blocks(); Value patch_value( VAL_BLOB, std::string(reinterpret_cast(params.patch_start + offset), len)); @@ -1223,7 +1209,7 @@ static int PerformCommandDiff(CommandParameters& params) { LOG(ERROR) << "range sink underrun?"; } } else { - LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.size << " [" + LOG(INFO) << "skipping " << blocks << " blocks already patched to " << tgt.blocks() << " [" << params.cmdline << "]"; } } @@ -1233,52 +1219,52 @@ static int PerformCommandDiff(CommandParameters& params) { params.freestash.clear(); } - params.written += tgt.size; + params.written += tgt.blocks(); return 0; } static int PerformCommandErase(CommandParameters& params) { - if (DEBUG_ERASE) { - return PerformCommandZero(params); - } + if (DEBUG_ERASE) { + return PerformCommandZero(params); + } - struct stat sb; - if (fstat(params.fd, &sb) == -1) { - PLOG(ERROR) << "failed to fstat device to erase"; - return -1; - } + struct stat sb; + if (fstat(params.fd, &sb) == -1) { + PLOG(ERROR) << "failed to fstat device to erase"; + return -1; + } - if (!S_ISBLK(sb.st_mode)) { - LOG(ERROR) << "not a block device; skipping erase"; - return -1; - } + if (!S_ISBLK(sb.st_mode)) { + LOG(ERROR) << "not a block device; skipping erase"; + return -1; + } - if (params.cpos >= params.tokens.size()) { - LOG(ERROR) << "missing target blocks for erase"; - return -1; - } + if (params.cpos >= params.tokens.size()) { + LOG(ERROR) << "missing target blocks for erase"; + return -1; + } + + RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); - RangeSet tgt = RangeSet::Parse(params.tokens[params.cpos++]); + if (params.canwrite) { + LOG(INFO) << " erasing " << tgt.blocks() << " blocks"; - if (params.canwrite) { - LOG(INFO) << " erasing " << tgt.size << " blocks"; - - for (size_t i = 0; i < tgt.count; ++i) { - uint64_t blocks[2]; - // offset in bytes - blocks[0] = tgt.pos[i * 2] * (uint64_t) BLOCKSIZE; - // length in bytes - blocks[1] = (tgt.pos[i * 2 + 1] - tgt.pos[i * 2]) * (uint64_t) BLOCKSIZE; - - if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) { - PLOG(ERROR) << "BLKDISCARD ioctl failed"; - return -1; - } - } + for (const auto& range : tgt) { + uint64_t blocks[2]; + // offset in bytes + blocks[0] = range.first * static_cast(BLOCKSIZE); + // length in bytes + blocks[1] = (range.second - range.first) * static_cast(BLOCKSIZE); + + if (ioctl(params.fd, BLKDISCARD, &blocks) == -1) { + PLOG(ERROR) << "BLKDISCARD ioctl failed"; + return -1; + } } + } - return 0; + return 0; } // Definitions for transfer list command functions @@ -1645,14 +1631,14 @@ Value* RangeSha1Fn(const char* name, State* state, const std::vector buffer(BLOCKSIZE); - for (size_t i = 0; i < rs.count; ++i) { - if (!check_lseek(fd, (off64_t)rs.pos[i * 2] * BLOCKSIZE, SEEK_SET)) { + for (const auto& range : rs) { + if (!check_lseek(fd, static_cast(range.first) * BLOCKSIZE, SEEK_SET)) { ErrorAbort(state, kLseekFailure, "failed to seek %s: %s", blockdev_filename->data.c_str(), strerror(errno)); return StringValue(""); } - for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { + for (size_t j = range.first; j < range.second; ++j) { if (read_all(fd, buffer, BLOCKSIZE) == -1) { ErrorAbort(state, kFreadFailure, "failed to read %s: %s", blockdev_filename->data.c_str(), strerror(errno)); @@ -1700,7 +1686,7 @@ Value* CheckFirstBlockFn(const char* name, State* state, return StringValue(""); } - RangeSet blk0{ 1 /*count*/, 1 /*size*/, std::vector{ 0, 1 } /*position*/ }; + RangeSet blk0(std::vector{ Range{ 0, 1 } }); std::vector block0_buffer(BLOCKSIZE); if (ReadBlocks(blk0, block0_buffer, fd) == -1) { @@ -1770,24 +1756,20 @@ Value* BlockImageRecoverFn(const char* name, State* state, } fec_status status; - if (!fh.get_status(status)) { ErrorAbort(state, kLibfecFailure, "failed to read FEC status"); return StringValue(""); } - RangeSet rs = RangeSet::Parse(ranges->data); - uint8_t buffer[BLOCKSIZE]; - - for (size_t i = 0; i < rs.count; ++i) { - for (size_t j = rs.pos[i * 2]; j < rs.pos[i * 2 + 1]; ++j) { + for (const auto& range : RangeSet::Parse(ranges->data)) { + for (size_t j = range.first; j < range.second; ++j) { // Stay within the data area, libfec validates and corrects metadata - if (status.data_size <= (uint64_t)j * BLOCKSIZE) { + if (status.data_size <= static_cast(j) * BLOCKSIZE) { continue; } - if (fh.pread(buffer, BLOCKSIZE, (off64_t)j * BLOCKSIZE) != BLOCKSIZE) { + if (fh.pread(buffer, BLOCKSIZE, static_cast(j) * BLOCKSIZE) != BLOCKSIZE) { ErrorAbort(state, kLibfecFailure, "failed to recover %s (block %zu): %s", filename->data.c_str(), j, strerror(errno)); return StringValue(""); diff --git a/updater/include/updater/rangeset.h b/updater/include/updater/rangeset.h index afaa82dcd..fad038043 100644 --- a/updater/include/updater/rangeset.h +++ b/updater/include/updater/rangeset.h @@ -19,16 +19,35 @@ #include #include +#include #include #include #include #include -struct RangeSet { - size_t count; // Limit is INT_MAX. - size_t size; // The number of blocks in the RangeSet. - std::vector pos; // Actual limit is INT_MAX. +using Range = std::pair; + +class RangeSet { + public: + RangeSet() : blocks_(0) {} + + explicit RangeSet(std::vector&& pairs) { + CHECK_NE(pairs.size(), static_cast(0)) << "Invalid number of tokens"; + + // Sanity check the input. + size_t result = 0; + for (const auto& range : pairs) { + CHECK_LT(range.first, range.second) + << "Empty or negative range: " << range.first << ", " << range.second; + size_t sz = range.second - range.first; + CHECK_LE(result, SIZE_MAX - sz) << "RangeSet size overflow"; + result += sz; + } + + ranges_ = pairs; + blocks_ = result; + } static RangeSet Parse(const std::string& range_text) { std::vector pieces = android::base::Split(range_text, ","); @@ -42,44 +61,43 @@ struct RangeSet { CHECK_EQ(num % 2, static_cast(0)) << "Number of tokens must be even: " << range_text; CHECK_EQ(num, pieces.size() - 1) << "Mismatching number of tokens: " << range_text; - std::vector pairs(num); - size_t size = 0; + std::vector pairs; for (size_t i = 0; i < num; i += 2) { - CHECK(android::base::ParseUint(pieces[i + 1], &pairs[i], static_cast(INT_MAX))); - CHECK(android::base::ParseUint(pieces[i + 2], &pairs[i + 1], static_cast(INT_MAX))); - CHECK_LT(pairs[i], pairs[i + 1]) - << "Empty or negative range: " << pairs[i] << ", " << pairs[i + 1]; - - size_t sz = pairs[i + 1] - pairs[i]; - CHECK_LE(size, SIZE_MAX - sz) << "RangeSet size overflow"; - size += sz; + size_t first; + CHECK(android::base::ParseUint(pieces[i + 1], &first, static_cast(INT_MAX))); + size_t second; + CHECK(android::base::ParseUint(pieces[i + 2], &second, static_cast(INT_MAX))); + + pairs.emplace_back(first, second); } - return RangeSet{ num / 2, size, std::move(pairs) }; + return RangeSet(std::move(pairs)); } // Get the block number for the i-th (starting from 0) block in the RangeSet. size_t GetBlockNumber(size_t idx) const { - CHECK_LT(idx, size) << "Index " << idx << " is greater than RangeSet size " << size; - for (size_t i = 0; i < pos.size(); i += 2) { - if (idx < pos[i + 1] - pos[i]) { - return pos[i] + idx; + CHECK_LT(idx, blocks_) << "Out of bound index " << idx << " (total blocks: " << blocks_ << ")"; + + for (const auto& range : ranges_) { + if (idx < range.second - range.first) { + return range.first + idx; } - idx -= (pos[i + 1] - pos[i]); + idx -= (range.second - range.first); } - CHECK(false); + + CHECK(false) << "Failed to find block number for index " << idx; return 0; // Unreachable, but to make compiler happy. } // RangeSet has half-closed half-open bounds. For example, "3,5" contains blocks 3 and 4. So "3,5" // and "5,7" are not overlapped. bool Overlaps(const RangeSet& other) const { - for (size_t i = 0; i < count; ++i) { - size_t start = pos[i * 2]; - size_t end = pos[i * 2 + 1]; - for (size_t j = 0; j < other.count; ++j) { - size_t other_start = other.pos[j * 2]; - size_t other_end = other.pos[j * 2 + 1]; + for (const auto& range : ranges_) { + size_t start = range.first; + size_t end = range.second; + for (const auto& other_range : other.ranges_) { + size_t other_start = other_range.first; + size_t other_end = other_range.second; // [start, end) vs [other_start, other_end) if (!(other_start >= end || start >= other_end)) { return true; @@ -89,7 +107,58 @@ struct RangeSet { return false; } + // size() gives the number of Range's in this RangeSet. + size_t size() const { + return ranges_.size(); + } + + // blocks() gives the number of all blocks in this RangeSet. + size_t blocks() const { + return blocks_; + } + + // We provide const iterators only. + std::vector::const_iterator cbegin() const { + return ranges_.cbegin(); + } + + std::vector::const_iterator cend() const { + return ranges_.cend(); + } + + // Need to provide begin()/end() since range-based loop expects begin()/end(). + std::vector::const_iterator begin() const { + return ranges_.cbegin(); + } + + std::vector::const_iterator end() const { + return ranges_.cend(); + } + + // Reverse const iterators for MoveRange(). + std::vector::const_reverse_iterator crbegin() const { + return ranges_.crbegin(); + } + + std::vector::const_reverse_iterator crend() const { + return ranges_.crend(); + } + + const Range& operator[](size_t i) const { + return ranges_[i]; + } + bool operator==(const RangeSet& other) const { - return (count == other.count && size == other.size && pos == other.pos); + // The orders of Range's matter. "4,1,5,8,10" != "4,8,10,1,5". + return (ranges_ == other.ranges_); } + + bool operator!=(const RangeSet& other) const { + return ranges_ != other.ranges_; + } + + private: + // Actual limit for each value and the total number are both INT_MAX. + std::vector ranges_; + size_t blocks_; }; -- cgit v1.2.3 From fb00d82f32446804f7149bc6846dcc580cf0db1d Mon Sep 17 00:00:00 2001 From: Alex Deymo Date: Tue, 8 Nov 2016 15:46:07 -0800 Subject: Boot control HAL based on the A/B headers. The added bootctrl.bcb module implement the legacy interface for the bootctrl HAL based on the Boot Control Block (BCB) and the headers already specified in the booloader_message library. This serves as a reference implementation of the boot_control HAL. Bug: 32707546 Test: Tested internally that a device can use this HAL to flip slots and recovery from a /misc wipe. Change-Id: Ic02e5aaf4de7d0a1780eac4e8705dae20d0b3e10 --- Android.mk | 1 + boot_control/Android.mk | 34 ++ boot_control/boot_control.cpp | 401 +++++++++++++++++++++ bootloader_message/bootloader_message.cpp | 7 + .../bootloader_message/bootloader_message.h | 5 + 5 files changed, 448 insertions(+) create mode 100644 boot_control/Android.mk create mode 100644 boot_control/boot_control.cpp diff --git a/Android.mk b/Android.mk index 037aa1673..d70101395 100644 --- a/Android.mk +++ b/Android.mk @@ -160,6 +160,7 @@ include $(BUILD_STATIC_LIBRARY) include \ $(LOCAL_PATH)/applypatch/Android.mk \ + $(LOCAL_PATH)/boot_control/Android.mk \ $(LOCAL_PATH)/bootloader_message/Android.mk \ $(LOCAL_PATH)/edify/Android.mk \ $(LOCAL_PATH)/minadbd/Android.mk \ diff --git a/boot_control/Android.mk b/boot_control/Android.mk new file mode 100644 index 000000000..27e3d9765 --- /dev/null +++ b/boot_control/Android.mk @@ -0,0 +1,34 @@ +# +# Copyright (C) 2017 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. +# + +LOCAL_PATH := $(my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := bootctrl.bcb +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_SRC_FILES := boot_control.cpp +LOCAL_CFLAGS := \ + -D_FILE_OFFSET_BITS=64 \ + -Werror \ + -Wall \ + -Wextra \ + -Wno-unused-parameter +LOCAL_SHARED_LIBRARIES := liblog +LOCAL_STATIC_LIBRARIES := libbootloader_message libfs_mgr libbase +LOCAL_POST_INSTALL_CMD := \ + $(hide) mkdir -p $(TARGET_OUT_SHARED_LIBRARIES)/hw && \ + ln -sf bootctrl.bcb.so $(TARGET_OUT_SHARED_LIBRARIES)/hw/bootctrl.default.so +include $(BUILD_SHARED_LIBRARY) diff --git a/boot_control/boot_control.cpp b/boot_control/boot_control.cpp new file mode 100644 index 000000000..ec97b6ced --- /dev/null +++ b/boot_control/boot_control.cpp @@ -0,0 +1,401 @@ +/* + * Copyright (C) 2015 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 + +#include + +struct boot_control_private_t { + // The base struct needs to be first in the list. + boot_control_module_t base; + + // Whether this struct was initialized with data from the bootloader message + // that doesn't change until next reboot. + bool initialized; + + // The path to the misc_device as reported in the fstab. + const char* misc_device; + + // The number of slots present on the device. + unsigned int num_slots; + + // The slot where we are running from. + unsigned int current_slot; +}; + +namespace { + +// The number of boot attempts that should be made from a new slot before +// rolling back to the previous slot. +constexpr unsigned int kDefaultBootAttempts = 7; +static_assert(kDefaultBootAttempts < 8, "tries_remaining field only has 3 bits"); + +constexpr unsigned int kMaxNumSlots = + sizeof(bootloader_control::slot_info) / sizeof(bootloader_control::slot_info[0]); +constexpr const char* kSlotSuffixes[kMaxNumSlots] = { "_a", "_b", "_c", "_d" }; +constexpr off_t kBootloaderControlOffset = offsetof(bootloader_message_ab, slot_suffix); + +static uint32_t CRC32(const uint8_t* buf, size_t size) { + static uint32_t crc_table[256]; + + // Compute the CRC-32 table only once. + if (!crc_table[1]) { + for (uint32_t i = 0; i < 256; ++i) { + uint32_t crc = i; + for (uint32_t j = 0; j < 8; ++j) { + uint32_t mask = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & mask); + } + crc_table[i] = crc; + } + } + + uint32_t ret = -1; + for (size_t i = 0; i < size; ++i) { + ret = (ret >> 8) ^ crc_table[(ret ^ buf[i]) & 0xFF]; + } + + return ~ret; +} + +// Return the little-endian representation of the CRC-32 of the first fields +// in |boot_ctrl| up to the crc32_le field. +uint32_t BootloaderControlLECRC(const bootloader_control* boot_ctrl) { + return htole32( + CRC32(reinterpret_cast(boot_ctrl), offsetof(bootloader_control, crc32_le))); +} + +bool LoadBootloaderControl(const char* misc_device, bootloader_control* buffer) { + android::base::unique_fd fd(open(misc_device, O_RDONLY)); + if (fd.get() == -1) { + PLOG(ERROR) << "failed to open " << misc_device; + return false; + } + if (lseek(fd, kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) { + PLOG(ERROR) << "failed to lseek " << misc_device; + return false; + } + if (!android::base::ReadFully(fd.get(), buffer, sizeof(bootloader_control))) { + PLOG(ERROR) << "failed to read " << misc_device; + return false; + } + return true; +} + +bool UpdateAndSaveBootloaderControl(const char* misc_device, bootloader_control* buffer) { + buffer->crc32_le = BootloaderControlLECRC(buffer); + android::base::unique_fd fd(open(misc_device, O_WRONLY | O_SYNC)); + if (fd.get() == -1) { + PLOG(ERROR) << "failed to open " << misc_device; + return false; + } + if (lseek(fd.get(), kBootloaderControlOffset, SEEK_SET) != kBootloaderControlOffset) { + PLOG(ERROR) << "failed to lseek " << misc_device; + return false; + } + if (!android::base::WriteFully(fd.get(), buffer, sizeof(bootloader_control))) { + PLOG(ERROR) << "failed to write " << misc_device; + return false; + } + return true; +} + +void InitDefaultBootloaderControl(const boot_control_private_t* module, + bootloader_control* boot_ctrl) { + memset(boot_ctrl, 0, sizeof(*boot_ctrl)); + + if (module->current_slot < kMaxNumSlots) { + strlcpy(boot_ctrl->slot_suffix, kSlotSuffixes[module->current_slot], + sizeof(boot_ctrl->slot_suffix)); + } + boot_ctrl->magic = BOOT_CTRL_MAGIC; + boot_ctrl->version = BOOT_CTRL_VERSION; + + // Figure out the number of slots by checking if the partitions exist, + // otherwise assume the maximum supported by the header. + boot_ctrl->nb_slot = kMaxNumSlots; + std::string base_path = module->misc_device; + size_t last_path_sep = base_path.rfind('/'); + if (last_path_sep != std::string::npos) { + // We test the existence of the "boot" partition on each possible slot, + // which is a partition required by Android Bootloader Requirements. + base_path = base_path.substr(0, last_path_sep + 1) + "boot"; + int last_existing_slot = -1; + int first_missing_slot = -1; + for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) { + std::string partition_path = base_path + kSlotSuffixes[slot]; + struct stat part_stat; + int err = stat(partition_path.c_str(), &part_stat); + if (!err) { + last_existing_slot = slot; + LOG(INFO) << "Found slot: " << kSlotSuffixes[slot]; + } else if (err < 0 && errno == ENOENT && first_missing_slot == -1) { + first_missing_slot = slot; + } + } + // We only declare that we found the actual number of slots if we found all + // the boot partitions up to the number of slots, and no boot partition + // after that. Not finding any of the boot partitions implies a problem so + // we just leave the number of slots in the maximum value. + if ((last_existing_slot != -1 && last_existing_slot + 1 == first_missing_slot) || + (first_missing_slot == -1 && last_existing_slot + 1 == kMaxNumSlots)) { + boot_ctrl->nb_slot = last_existing_slot + 1; + LOG(INFO) << "Found a system with " << last_existing_slot + 1 << " slots."; + } + } + + for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) { + slot_metadata entry = {}; + + if (slot < boot_ctrl->nb_slot) { + entry.priority = 7; + entry.tries_remaining = kDefaultBootAttempts; + entry.successful_boot = 0; + } else { + entry.priority = 0; // Unbootable + } + + // When the boot_control stored on disk is invalid, we assume that the + // current slot is successful. The bootloader should repair this situation + // before booting and write a valid boot_control slot, so if we reach this + // stage it means that the misc partition was corrupted since boot. + if (module->current_slot == slot) { + entry.successful_boot = 1; + } + + boot_ctrl->slot_info[slot] = entry; + } + boot_ctrl->recovery_tries_remaining = 0; + + boot_ctrl->crc32_le = BootloaderControlLECRC(boot_ctrl); +} + +// Return the index of the slot suffix passed or -1 if not a valid slot suffix. +int SlotSuffixToIndex(const char* suffix) { + for (unsigned int slot = 0; slot < kMaxNumSlots; ++slot) { + if (!strcmp(kSlotSuffixes[slot], suffix)) return slot; + } + return -1; +} + +// Initialize the boot_control_private struct with the information from +// the bootloader_message buffer stored in |boot_ctrl|. Returns whether the +// initialization succeeded. +bool BootControl_lazyInitialization(boot_control_private_t* module) { + if (module->initialized) return true; + + // Initialize the current_slot from the read-only property. If the property + // was not set (from either the command line or the device tree), we can later + // initialize it from the bootloader_control struct. + std::string suffix_prop = android::base::GetProperty("ro.boot.slot_suffix", ""); + module->current_slot = SlotSuffixToIndex(suffix_prop.c_str()); + + std::string err; + std::string device = get_bootloader_message_blk_device(&err); + if (device.empty()) return false; + + bootloader_control boot_ctrl; + if (!LoadBootloaderControl(device.c_str(), &boot_ctrl)) return false; + + // Note that since there isn't a module unload function this memory is leaked. + module->misc_device = strdup(device.c_str()); + module->initialized = true; + + // Validate the loaded data, otherwise we will destroy it and re-initialize it + // with the current information. + uint32_t computed_crc32 = BootloaderControlLECRC(&boot_ctrl); + if (boot_ctrl.crc32_le != computed_crc32) { + LOG(WARNING) << "Invalid boot control found, expected CRC-32 0x" << std::hex << computed_crc32 + << " but found 0x" << std::hex << boot_ctrl.crc32_le << ". Re-initializing."; + InitDefaultBootloaderControl(module, &boot_ctrl); + UpdateAndSaveBootloaderControl(device.c_str(), &boot_ctrl); + } + + module->num_slots = boot_ctrl.nb_slot; + return true; +} + +void BootControl_init(boot_control_module_t* module) { + BootControl_lazyInitialization(reinterpret_cast(module)); +} + +unsigned int BootControl_getNumberSlots(boot_control_module_t* module) { + return reinterpret_cast(module)->num_slots; +} + +unsigned int BootControl_getCurrentSlot(boot_control_module_t* module) { + return reinterpret_cast(module)->current_slot; +} + +int BootControl_markBootSuccessful(boot_control_module_t* module) { + boot_control_private_t* const bootctrl_module = reinterpret_cast(module); + + bootloader_control bootctrl; + if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + + bootctrl.slot_info[bootctrl_module->current_slot].successful_boot = 1; + // tries_remaining == 0 means that the slot is not bootable anymore, make + // sure we mark the current slot as bootable if it succeeds in the last + // attempt. + bootctrl.slot_info[bootctrl_module->current_slot].tries_remaining = 1; + if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + return 0; +} + +int BootControl_setActiveBootSlot(boot_control_module_t* module, unsigned int slot) { + boot_control_private_t* const bootctrl_module = reinterpret_cast(module); + + if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) { + // Invalid slot number. + return -1; + } + + bootloader_control bootctrl; + if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + + // Set every other slot with a lower priority than the new "active" slot. + const unsigned int kActivePriority = 15; + const unsigned int kActiveTries = 6; + for (unsigned int i = 0; i < bootctrl_module->num_slots; ++i) { + if (i != slot) { + if (bootctrl.slot_info[i].priority >= kActivePriority) + bootctrl.slot_info[i].priority = kActivePriority - 1; + } + } + + // Note that setting a slot as active doesn't change the successful bit. + // The successful bit will only be changed by setSlotAsUnbootable(). + bootctrl.slot_info[slot].priority = kActivePriority; + bootctrl.slot_info[slot].tries_remaining = kActiveTries; + + // Setting the current slot as active is a way to revert the operation that + // set *another* slot as active at the end of an updater. This is commonly + // used to cancel the pending update. We should only reset the verity_corrpted + // bit when attempting a new slot, otherwise the verity bit on the current + // slot would be flip. + if (slot != bootctrl_module->current_slot) bootctrl.slot_info[slot].verity_corrupted = 0; + + if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + return 0; +} + +int BootControl_setSlotAsUnbootable(struct boot_control_module* module, unsigned int slot) { + boot_control_private_t* const bootctrl_module = reinterpret_cast(module); + + if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) { + // Invalid slot number. + return -1; + } + + bootloader_control bootctrl; + if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + + // The only way to mark a slot as unbootable, regardless of the priority is to + // set the tries_remaining to 0. + bootctrl.slot_info[slot].successful_boot = 0; + bootctrl.slot_info[slot].tries_remaining = 0; + if (!UpdateAndSaveBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + return 0; +} + +int BootControl_isSlotBootable(struct boot_control_module* module, unsigned int slot) { + boot_control_private_t* const bootctrl_module = reinterpret_cast(module); + + if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) { + // Invalid slot number. + return -1; + } + + bootloader_control bootctrl; + if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + + return bootctrl.slot_info[slot].tries_remaining; +} + +int BootControl_isSlotMarkedSuccessful(struct boot_control_module* module, unsigned int slot) { + boot_control_private_t* const bootctrl_module = reinterpret_cast(module); + + if (slot >= kMaxNumSlots || slot >= bootctrl_module->num_slots) { + // Invalid slot number. + return -1; + } + + bootloader_control bootctrl; + if (!LoadBootloaderControl(bootctrl_module->misc_device, &bootctrl)) return -1; + + return bootctrl.slot_info[slot].successful_boot && bootctrl.slot_info[slot].tries_remaining; +} + +const char* BootControl_getSuffix(boot_control_module_t* module, unsigned int slot) { + if (slot >= kMaxNumSlots || slot >= reinterpret_cast(module)->num_slots) { + return NULL; + } + return kSlotSuffixes[slot]; +} + +static int BootControl_open(const hw_module_t* module __unused, const char* id __unused, + hw_device_t** device __unused) { + /* Nothing to do currently. */ + return 0; +} + +struct hw_module_methods_t BootControl_methods = { + .open = BootControl_open, +}; + +} // namespace + +boot_control_private_t HAL_MODULE_INFO_SYM = { + .base = + { + .common = + { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = BOOT_CONTROL_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = BOOT_CONTROL_HARDWARE_MODULE_ID, + .name = "AOSP reference bootctrl HAL", + .author = "The Android Open Source Project", + .methods = &BootControl_methods, + }, + .init = BootControl_init, + .getNumberSlots = BootControl_getNumberSlots, + .getCurrentSlot = BootControl_getCurrentSlot, + .markBootSuccessful = BootControl_markBootSuccessful, + .setActiveBootSlot = BootControl_setActiveBootSlot, + .setSlotAsUnbootable = BootControl_setSlotAsUnbootable, + .isSlotBootable = BootControl_isSlotBootable, + .getSuffix = BootControl_getSuffix, + .isSlotMarkedSuccessful = BootControl_isSlotMarkedSuccessful, + }, + .initialized = false, + .misc_device = nullptr, + .num_slots = 0, + .current_slot = 0, +}; diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index d8086be28..f91446b43 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -117,6 +117,13 @@ static bool write_misc_partition(const void* p, size_t size, const std::string& return true; } +std::string get_bootloader_message_blk_device(std::string* err) { + std::string misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) return ""; + if (!wait_for_device(misc_blk_device, err)) return ""; + return misc_blk_device; +} + bool read_bootloader_message_from(bootloader_message* boot, const std::string& misc_blk_device, std::string* err) { return read_misc_partition(boot, sizeof(*boot), misc_blk_device, diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index bc5104ddf..2ffbfc9e3 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -180,6 +180,11 @@ static_assert(sizeof(struct bootloader_control) == #include #include +// Return the block device name for the bootloader message partition and waits +// for the device for up to 10 seconds. In case of error returns the empty +// string. +std::string get_bootloader_message_blk_device(std::string* err); + // Read bootloader message into boot. Error message will be set in err. bool read_bootloader_message(bootloader_message* boot, std::string* err); -- cgit v1.2.3 From a015cd1d7a3af5d9c06622e00be47fee52ba4b02 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Mon, 3 Apr 2017 13:53:49 -0700 Subject: update_verifier: tweak priority of update_verifier for quick boot Highest ioprio is 0 for CFQ and we should run update_verifier with that. Tested on device and showing boottime decreased. Bug: 36511808 Bug: 36102163 Test: Boot marlin Change-Id: Iddd925951d976e21014b61e5590bcdae3cea8470 --- update_verifier/update_verifier.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/update_verifier/update_verifier.rc b/update_verifier/update_verifier.rc index fc8a64dee..8b60c3ffd 100644 --- a/update_verifier/update_verifier.rc +++ b/update_verifier/update_verifier.rc @@ -2,10 +2,10 @@ service update_verifier_nonencrypted /system/bin/update_verifier nonencrypted user root class cache priority -20 - ioprio rt 1 + ioprio rt 0 service update_verifier /system/bin/update_verifier ${vold.decrypt} user root class cache priority -20 - ioprio rt 1 \ No newline at end of file + ioprio rt 0 -- cgit v1.2.3 From 3a8002f8c0382894b65ea3cece784287a75c7881 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Fri, 31 Mar 2017 17:17:34 -0700 Subject: update_verifier: correct group in rc file update_verifier should be in the cache group, not 'class'. Also use PLOG instead of LOG if care_map.txt cannot be opened. Bug: 36818743 Test: boot sailfish Test: fake OTA on sailfish and verify update_verifier reads care_package Change-Id: I0ec844cac5ef5c63b18ebee90160854fd84ee829 --- update_verifier/update_verifier.cpp | 2 +- update_verifier/update_verifier.rc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index 59f136c90..350020f13 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -167,7 +167,7 @@ static bool verify_image(const std::string& care_map_name) { // in /data/ota_package. To allow the device to continue booting in this situation, // we should print a warning and skip the block verification. if (care_map_fd.get() == -1) { - LOG(WARNING) << "Warning: care map " << care_map_name << " not found."; + PLOG(WARNING) << "Failed to open " << care_map_name; return true; } // Care map file has four lines (two lines if vendor partition is not present): diff --git a/update_verifier/update_verifier.rc b/update_verifier/update_verifier.rc index fc8a64dee..75cfeb28d 100644 --- a/update_verifier/update_verifier.rc +++ b/update_verifier/update_verifier.rc @@ -1,11 +1,11 @@ service update_verifier_nonencrypted /system/bin/update_verifier nonencrypted user root - class cache + group cache priority -20 ioprio rt 1 service update_verifier /system/bin/update_verifier ${vold.decrypt} user root - class cache + group cache priority -20 ioprio rt 1 \ No newline at end of file -- cgit v1.2.3 From ad87d9dc7823d74f067a4d82a8884f39d523acc9 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 4 Apr 2017 19:59:40 -0700 Subject: tests: Use get_bootloader_message_blk_device() to find /misc. Since commit fb00d82f32446804f7149bc6846dcc580cf0db1d has added get_bootloader_message_blk_device() as an API, switch the tests-local implementation to it. Test: recovery_component_test on angler. Test: recovery_component_test on a local build that doesn't have /misc. Change-Id: I4f5f542cb9ef58292c587a677da73d8822db7262 --- tests/common/component_test_util.h | 43 ----------------------------- tests/component/bootloader_message_test.cpp | 5 ++-- tests/component/uncrypt_test.cpp | 5 ++-- 3 files changed, 4 insertions(+), 49 deletions(-) delete mode 100644 tests/common/component_test_util.h diff --git a/tests/common/component_test_util.h b/tests/common/component_test_util.h deleted file mode 100644 index 3fee32d62..000000000 --- a/tests/common/component_test_util.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2017 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 agree 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. - */ - -#ifndef _COMPONENT_TEST_UTIL_H -#define _COMPONENT_TEST_UTIL_H - -#include - -#include -#include - -// Check if the /misc entry exists in the fstab. -static bool parse_misc() { - std::unique_ptr fstab(fs_mgr_read_fstab_default(), - fs_mgr_free_fstab); - if (!fstab) { - GTEST_LOG_(INFO) << "Failed to read default fstab"; - return false; - } - - fstab_rec* record = fs_mgr_get_entry_for_mount_point(fstab.get(), "/misc"); - if (record == nullptr) { - GTEST_LOG_(INFO) << "Failed to find /misc in fstab."; - return false; - } - return true; -} - -#endif //_COMPONENT_TEST_UTIL_H - diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp index 0357accfe..b38bc7134 100644 --- a/tests/component/bootloader_message_test.cpp +++ b/tests/component/bootloader_message_test.cpp @@ -21,14 +21,13 @@ #include #include -#include "common/component_test_util.h" - class BootloaderMessageTest : public ::testing::Test { protected: BootloaderMessageTest() : has_misc(true) {} virtual void SetUp() override { - has_misc = parse_misc(); + std::string err; + has_misc = !get_bootloader_message_blk_device(&err).empty(); } virtual void TearDown() override { diff --git a/tests/component/uncrypt_test.cpp b/tests/component/uncrypt_test.cpp index 5e057e129..3925236a5 100644 --- a/tests/component/uncrypt_test.cpp +++ b/tests/component/uncrypt_test.cpp @@ -30,8 +30,6 @@ #include #include -#include "common/component_test_util.h" - using namespace std::string_literals; static const std::string UNCRYPT_SOCKET = "/dev/socket/uncrypt"; @@ -65,7 +63,8 @@ class UncryptTest : public ::testing::Test { ASSERT_TRUE(success) << "uncrypt service is not available."; - has_misc = parse_misc(); + std::string err; + has_misc = !get_bootloader_message_blk_device(&err).empty(); } void SetupOrClearBcb(bool isSetup, const std::string& message, -- cgit v1.2.3 From 3a8d98dd902da8b69cff731b3db03c75b176b751 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 3 Apr 2017 20:01:17 -0700 Subject: Abort the update if there's not enough new data Right now the update stuck in a deadlock if there's less new data than expection. Add some checkers and abort the update if such case happens. Also add a corresponding test. Bug: 36787146 Test: update aborts correctly on bullhead && recovery_component_test passes Change-Id: I914e4a2a4cf157b99ef2fc65bd21c6981e38ca47 --- tests/component/updater_test.cpp | 66 ++++++++++++++++++++++++++++++++++++++++ updater/blockimg.cpp | 30 +++++++++++++++--- 2 files changed, 92 insertions(+), 4 deletions(-) diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 5652ddf46..dc4b5d724 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -607,3 +607,69 @@ TEST_F(UpdaterTest, block_image_update) { ASSERT_EQ(0, fclose(updater_info.cmd_pipe)); CloseArchive(handle); } + +TEST_F(UpdaterTest, new_data_short_write) { + // Create a zip file with new_data. + TemporaryFile zip_file; + FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + ZipWriter zip_writer(zip_file_ptr); + + // Add the empty new data. + ASSERT_EQ(0, zip_writer.StartEntry("empty_new_data", 0)); + ASSERT_EQ(0, zip_writer.FinishEntry()); + // Add the short written new data. + ASSERT_EQ(0, zip_writer.StartEntry("short_new_data", 0)); + std::string new_data_short = std::string(10, 'a'); + ASSERT_EQ(0, zip_writer.WriteBytes(new_data_short.data(), new_data_short.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + // Add the data of exactly one block. + ASSERT_EQ(0, zip_writer.StartEntry("exact_new_data", 0)); + std::string new_data_exact = std::string(4096, 'a'); + ASSERT_EQ(0, zip_writer.WriteBytes(new_data_exact.data(), new_data_exact.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + // Add a dummy patch data. + ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); + ASSERT_EQ(0, zip_writer.FinishEntry()); + + std::vector transfer_list = { + "4", + "1", + "0", + "0", + "new 2,0,1", + }; + ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); + std::string commands = android::base::Join(transfer_list, '\n'); + ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + ASSERT_EQ(0, zip_writer.Finish()); + ASSERT_EQ(0, fclose(zip_file_ptr)); + + MemMapping map; + ASSERT_EQ(0, sysMapFile(zip_file.path, &map)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fopen(temp_pipe.path, "wb"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + // Updater should report the failure gracefully rather than stuck in deadlock. + TemporaryFile update_file; + std::string script_empty_data = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "empty_new_data", "patch_data"))"; + expect("", script_empty_data.c_str(), kNoCause, &updater_info); + + std::string script_short_data = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "short_new_data", "patch_data"))"; + expect("", script_short_data.c_str(), kNoCause, &updater_info); + + // Expect to write 1 block of new data successfully. + std::string script_exact_data = "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "exact_new_data", "patch_data"))"; + expect("t", script_exact_data.c_str(), kNoCause, &updater_info); +} diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 0f08d17eb..d5c170473 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -149,7 +149,7 @@ static void allocate(size_t size, std::vector& buffer) { class RangeSinkWriter { public: RangeSinkWriter(int fd, const RangeSet& tgt) - : fd_(fd), tgt_(tgt), next_range_(0), current_range_left_(0) { + : fd_(fd), tgt_(tgt), next_range_(0), current_range_left_(0), bytes_written_(0) { CHECK_NE(tgt.size(), static_cast(0)); }; @@ -201,9 +201,14 @@ class RangeSinkWriter { written += write_now; } + bytes_written_ += written; return written; } + size_t BytesWritten() const { + return bytes_written_; + } + private: // The input data. int fd_; @@ -213,6 +218,8 @@ class RangeSinkWriter { size_t next_range_; // The number of bytes to write before moving to the next range. size_t current_range_left_; + // Total bytes written by the writer. + size_t bytes_written_; }; /** @@ -238,6 +245,7 @@ struct NewThreadInfo { ZipEntry entry; RangeSinkWriter* writer; + bool receiver_available; pthread_mutex_t mu; pthread_cond_t cv; @@ -274,9 +282,16 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { } static void* unzip_new_data(void* cookie) { - NewThreadInfo* nti = static_cast(cookie); - ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti); - return nullptr; + NewThreadInfo* nti = static_cast(cookie); + ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti); + + pthread_mutex_lock(&nti->mu); + nti->receiver_available = false; + if (nti->writer != nullptr) { + pthread_cond_broadcast(&nti->cv); + } + pthread_mutex_unlock(&nti->mu); + return nullptr; } static int ReadBlocks(const RangeSet& src, std::vector& buffer, int fd) { @@ -1133,6 +1148,12 @@ static int PerformCommandNew(CommandParameters& params) { pthread_cond_broadcast(¶ms.nti.cv); while (params.nti.writer != nullptr) { + if (!params.nti.receiver_available) { + LOG(ERROR) << "missing " << (tgt.blocks() * BLOCKSIZE - params.nti.writer->BytesWritten()) + << " bytes of new data"; + pthread_mutex_unlock(¶ms.nti.mu); + return -1; + } pthread_cond_wait(¶ms.nti.cv, ¶ms.nti.mu); } @@ -1361,6 +1382,7 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, if (params.canwrite) { params.nti.za = za; params.nti.entry = new_entry; + params.nti.receiver_available = true; pthread_mutex_init(¶ms.nti.mu, nullptr); pthread_cond_init(¶ms.nti.cv, nullptr); -- cgit v1.2.3 From 4bb11c745bda05506875193306111baf3198d24e Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 6 Apr 2017 23:54:03 -0700 Subject: Protect filename_cache with lock in ota fault Bug: 33173924 Test: Apply a successfully update on bullhead Change-Id: I28cc356e216a3e957b5533c338ee6bc3c0920222 --- otafault/Android.mk | 7 ++++++- otafault/ota_io.cpp | 18 ++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/otafault/Android.mk b/otafault/Android.mk index 71c2c62f6..ec4cdb365 100644 --- a/otafault/Android.mk +++ b/otafault/Android.mk @@ -23,7 +23,12 @@ otafault_static_libs := \ libbase \ liblog -LOCAL_CFLAGS := -Werror +LOCAL_CFLAGS := \ + -Werror \ + -Wthread-safety \ + -Wthread-safety-negative \ + -D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS + LOCAL_SRC_FILES := config.cpp ota_io.cpp LOCAL_MODULE_TAGS := eng LOCAL_MODULE := libotafault diff --git a/otafault/ota_io.cpp b/otafault/ota_io.cpp index 3a89bb5dd..faae5275d 100644 --- a/otafault/ota_io.cpp +++ b/otafault/ota_io.cpp @@ -24,10 +24,13 @@ #include #include +#include +#include #include "config.h" -static std::map filename_cache; +static std::mutex filename_mutex; +static std::map filename_cache GUARDED_BY(filename_mutex); static std::string read_fault_file_name = ""; static std::string write_fault_file_name = ""; static std::string fsync_fault_file_name = ""; @@ -55,23 +58,28 @@ bool have_eio_error = false; int ota_open(const char* path, int oflags) { // Let the caller handle errors; we do not care if open succeeds or fails int fd = open(path, oflags); + std::lock_guard lock(filename_mutex); filename_cache[fd] = path; return fd; } int ota_open(const char* path, int oflags, mode_t mode) { int fd = open(path, oflags, mode); + std::lock_guard lock(filename_mutex); filename_cache[fd] = path; - return fd; } + return fd; +} FILE* ota_fopen(const char* path, const char* mode) { FILE* fh = fopen(path, mode); + std::lock_guard lock(filename_mutex); filename_cache[(intptr_t)fh] = path; return fh; } static int __ota_close(int fd) { // descriptors can be reused, so make sure not to leave them in the cache + std::lock_guard lock(filename_mutex); filename_cache.erase(fd); return close(fd); } @@ -85,6 +93,7 @@ int ota_close(unique_fd& fd) { } static int __ota_fclose(FILE* fh) { + std::lock_guard lock(filename_mutex); filename_cache.erase(reinterpret_cast(fh)); return fclose(fh); } @@ -99,6 +108,7 @@ int ota_fclose(unique_file& fh) { size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) { if (should_fault_inject(OTAIO_READ)) { + std::lock_guard lock(filename_mutex); auto cached = filename_cache.find((intptr_t)stream); const char* cached_path = cached->second; if (cached != filename_cache.end() && @@ -119,6 +129,7 @@ size_t ota_fread(void* ptr, size_t size, size_t nitems, FILE* stream) { ssize_t ota_read(int fd, void* buf, size_t nbyte) { if (should_fault_inject(OTAIO_READ)) { + std::lock_guard lock(filename_mutex); auto cached = filename_cache.find(fd); const char* cached_path = cached->second; if (cached != filename_cache.end() @@ -138,6 +149,7 @@ ssize_t ota_read(int fd, void* buf, size_t nbyte) { size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) { if (should_fault_inject(OTAIO_WRITE)) { + std::lock_guard lock(filename_mutex); auto cached = filename_cache.find((intptr_t)stream); const char* cached_path = cached->second; if (cached != filename_cache.end() && @@ -157,6 +169,7 @@ size_t ota_fwrite(const void* ptr, size_t size, size_t count, FILE* stream) { ssize_t ota_write(int fd, const void* buf, size_t nbyte) { if (should_fault_inject(OTAIO_WRITE)) { + std::lock_guard lock(filename_mutex); auto cached = filename_cache.find(fd); const char* cached_path = cached->second; if (cached != filename_cache.end() && @@ -176,6 +189,7 @@ ssize_t ota_write(int fd, const void* buf, size_t nbyte) { int ota_fsync(int fd) { if (should_fault_inject(OTAIO_FSYNC)) { + std::lock_guard lock(filename_mutex); auto cached = filename_cache.find(fd); const char* cached_path = cached->second; if (cached != filename_cache.end() && -- cgit v1.2.3 From 1d866050eba7614109a1edec42529d4d80b0998f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 10 Apr 2017 16:55:57 -0700 Subject: Verify the package compatibility with libvintf. verify_package_compatibility() is added to parse the compatibility entry (compatibility.zip) in a given OTA package. If entry is present, the information is sent to libvintf to check the compatibility. This CL doesn't actually call libvintf, since the API there is not available yet. Bug: 36597505 Test: Doesn't break the install with existing packages (i.e. w/o the compatibility entry). Test: recovery_component_test Change-Id: I3903ffa5f6ba33a5c0d761602ade6290c6752596 (cherry picked from commit 62e0bc7586077b3bde82759fb34b51b982cea20f) --- Android.mk | 24 +++++++++++-- error_code.h | 3 +- install.cpp | 73 ++++++++++++++++++++++++++++++++++++++++ install.h | 4 +++ tests/Android.mk | 3 +- tests/component/install_test.cpp | 57 +++++++++++++++++++++++++++++++ 6 files changed, 159 insertions(+), 5 deletions(-) create mode 100644 tests/component/install_test.cpp diff --git a/Android.mk b/Android.mk index d70101395..1b83d75ff 100644 --- a/Android.mk +++ b/Android.mk @@ -14,6 +14,10 @@ LOCAL_PATH := $(call my-dir) +# Needed by build/make/core/Makefile. +RECOVERY_API_VERSION := 3 +RECOVERY_FSTAB_VERSION := 2 + # libfusesideload (static library) # =============================== include $(CLEAR_VARS) @@ -36,6 +40,22 @@ LOCAL_MODULE := libmounts LOCAL_STATIC_LIBRARIES := libbase include $(BUILD_STATIC_LIBRARY) +# librecovery (static library) +# =============================== +include $(CLEAR_VARS) +LOCAL_SRC_FILES := \ + install.cpp +LOCAL_CFLAGS := -Wno-unused-parameter -Werror +LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) +LOCAL_MODULE := librecovery +LOCAL_STATIC_LIBRARIES := \ + libminui \ + libcrypto_utils \ + libcrypto \ + libbase + +include $(BUILD_STATIC_LIBRARY) + # recovery (static executable) # =============================== include $(CLEAR_VARS) @@ -45,7 +65,6 @@ LOCAL_SRC_FILES := \ asn1_decoder.cpp \ device.cpp \ fuse_sdcard_provider.cpp \ - install.cpp \ recovery.cpp \ roots.cpp \ rotate_logs.cpp \ @@ -65,8 +84,6 @@ LOCAL_REQUIRED_MODULES := mkfs.f2fs endif endif -RECOVERY_API_VERSION := 3 -RECOVERY_FSTAB_VERSION := 2 LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) LOCAL_CFLAGS += -Wno-unused-parameter -Werror LOCAL_CLANG := true @@ -76,6 +93,7 @@ LOCAL_C_INCLUDES += \ system/core/adb \ LOCAL_STATIC_LIBRARIES := \ + librecovery \ libbatterymonitor \ libbootloader_message \ libext4_utils \ diff --git a/error_code.h b/error_code.h index 5dad6b263..cde4ee6de 100644 --- a/error_code.h +++ b/error_code.h @@ -22,7 +22,8 @@ enum ErrorCode { kLowBattery = 20, kZipVerificationFailure, kZipOpenFailure, - kBootreasonInBlacklist + kBootreasonInBlacklist, + kPackageCompatibilityFailure, }; enum CauseCode { diff --git a/install.cpp b/install.cpp index 0a2fa3ca4..b4b869b98 100644 --- a/install.cpp +++ b/install.cpp @@ -489,6 +489,70 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_ return INSTALL_SUCCESS; } +// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the +// entry doesn't exist. Note that the compatibility info is packed in a zip file inside the OTA +// package. +bool verify_package_compatibility(ZipArchiveHandle package_zip) { + LOG(INFO) << "Verifying package compatibility..."; + + static constexpr const char* COMPATIBILITY_ZIP_ENTRY = "compatibility.zip"; + ZipString compatibility_entry_name(COMPATIBILITY_ZIP_ENTRY); + ZipEntry compatibility_entry; + if (FindEntry(package_zip, compatibility_entry_name, &compatibility_entry) != 0) { + LOG(INFO) << "Package doesn't contain " << COMPATIBILITY_ZIP_ENTRY << " entry"; + return true; + } + + std::string zip_content(compatibility_entry.uncompressed_length, '\0'); + int32_t ret; + if ((ret = ExtractToMemory(package_zip, &compatibility_entry, + reinterpret_cast(&zip_content[0]), + compatibility_entry.uncompressed_length)) != 0) { + LOG(ERROR) << "Failed to read " << COMPATIBILITY_ZIP_ENTRY << ": " << ErrorCodeString(ret); + return false; + } + + ZipArchiveHandle zip_handle; + ret = OpenArchiveFromMemory(static_cast(const_cast(zip_content.data())), + zip_content.size(), COMPATIBILITY_ZIP_ENTRY, &zip_handle); + if (ret != 0) { + LOG(ERROR) << "Failed to OpenArchiveFromMemory: " << ErrorCodeString(ret); + return false; + } + + // Iterate all the entries inside COMPATIBILITY_ZIP_ENTRY and read the contents. + void* cookie; + ret = StartIteration(zip_handle, &cookie, nullptr, nullptr); + if (ret != 0) { + LOG(ERROR) << "Failed to start iterating zip entries: " << ErrorCodeString(ret); + CloseArchive(zip_handle); + return false; + } + std::unique_ptr guard(cookie, EndIteration); + + std::vector compatibility_info; + ZipEntry info_entry; + ZipString info_name; + while (Next(cookie, &info_entry, &info_name) == 0) { + std::string content(info_entry.uncompressed_length, '\0'); + int32_t ret = ExtractToMemory(zip_handle, &info_entry, reinterpret_cast(&content[0]), + info_entry.uncompressed_length); + if (ret != 0) { + LOG(ERROR) << "Failed to read " << info_name.name << ": " << ErrorCodeString(ret); + CloseArchive(zip_handle); + return false; + } + compatibility_info.emplace_back(std::move(content)); + } + EndIteration(cookie); + CloseArchive(zip_handle); + + // TODO(b/36814503): Enable the actual verification when VintfObject::CheckCompatibility() lands. + // VintfObject::CheckCompatibility returns zero on success. + // return (android::vintf::VintfObject::CheckCompatibility(compatibility_info, true) == 0); + return true; +} + static int really_install_package(const char *path, bool* wipe_cache, bool needs_mount, std::vector& log_buffer, int retry_count, int* max_temperature) @@ -536,6 +600,15 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount, return INSTALL_CORRUPT; } + // Additionally verify the compatibility of the package. + if (!verify_package_compatibility(zip)) { + LOG(ERROR) << "Failed to verify package compatibility"; + log_buffer.push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure)); + sysReleaseMap(&map); + CloseArchive(zip); + return INSTALL_CORRUPT; + } + // Verify and install the contents of the package. ui->Print("Installing update...\n"); if (retry_count > 0) { diff --git a/install.h b/install.h index 1ec01e817..68f0a8d47 100644 --- a/install.h +++ b/install.h @@ -37,4 +37,8 @@ bool verify_package(const unsigned char* package_data, size_t package_size); // Return true if succeed, otherwise return false. bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data); +// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the +// entry doesn't exist. +bool verify_package_compatibility(ZipArchiveHandle package_zip); + #endif // RECOVERY_INSTALL_H_ diff --git a/tests/Android.mk b/tests/Android.mk index 974aa0e2d..a1f0d4892 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -93,6 +93,7 @@ LOCAL_SRC_FILES := \ component/bootloader_message_test.cpp \ component/edify_test.cpp \ component/imgdiff_test.cpp \ + component/install_test.cpp \ component/sideload_test.cpp \ component/uncrypt_test.cpp \ component/updater_test.cpp \ @@ -117,6 +118,7 @@ LOCAL_STATIC_LIBRARIES := \ libbsdiff \ libbspatch \ libotafault \ + librecovery \ libupdater \ libbootloader_message \ libverifier \ @@ -131,7 +133,6 @@ LOCAL_STATIC_LIBRARIES := \ libsparse \ libcrypto_utils \ libcrypto \ - libcutils \ libbz \ libziparchive \ libutils \ diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp new file mode 100644 index 000000000..3b6fbc301 --- /dev/null +++ b/tests/component/install_test.cpp @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2017 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 agree 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 "install.h" + +TEST(InstallTest, verify_package_compatibility_no_entry) { + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + // The archive must have something to be opened correctly. + ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + // Doesn't contain compatibility zip entry. + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + ASSERT_TRUE(verify_package_compatibility(zip)); + CloseArchive(zip); +} + +TEST(InstallTest, verify_package_compatibility_invalid_entry) { + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + ASSERT_EQ(0, writer.StartEntry("compatibility.zip", 0)); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + // Empty compatibility zip entry. + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + ASSERT_FALSE(verify_package_compatibility(zip)); + CloseArchive(zip); +} -- cgit v1.2.3 From 2aa0d3abb41195f2b86dc7c385c34a55618ac90e Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Thu, 13 Apr 2017 12:45:55 -0700 Subject: minadbd: switch adb_thread_create to std::thread. Test: treehugger Change-Id: I7edcb13eb1fc6d4f36f5a764a1b647fbf9cd063e --- minadbd/minadbd_services.cpp | 30 +++--------------------------- 1 file changed, 3 insertions(+), 27 deletions(-) diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 426d982eb..a6aa321ca 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -21,25 +21,13 @@ #include #include +#include + #include "adb.h" #include "fdevent.h" #include "fuse_adb_provider.h" #include "sysdeps.h" -typedef struct stinfo stinfo; - -struct stinfo { - void (*func)(int fd, void *cookie); - int fd; - void *cookie; -}; - -void service_bootstrap_func(void* x) { - stinfo* sti = reinterpret_cast(x); - sti->func(sti->fd, sti->cookie); - free(sti); -} - static void sideload_host_service(int sfd, void* data) { char* args = reinterpret_cast(data); int file_size; @@ -66,19 +54,7 @@ static int create_service_thread(void (*func)(int, void *), void *cookie) { return -1; } - stinfo* sti = static_cast(malloc(sizeof(stinfo))); - if(sti == 0) fatal("cannot allocate stinfo"); - sti->func = func; - sti->cookie = cookie; - sti->fd = s[1]; - - if (!adb_thread_create(service_bootstrap_func, sti)) { - free(sti); - adb_close(s[0]); - adb_close(s[1]); - printf("cannot create service thread\n"); - return -1; - } + std::thread([s, func, cookie]() { func(s[1], cookie); }).detach(); VLOG(SERVICES) << "service thread started, " << s[0] << ":" << s[1]; return s[0]; -- cgit v1.2.3 From e95902280c362cec38bc79c63e0da5cc35241e2b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 16 Apr 2017 15:40:10 -0700 Subject: libmounts: Remove two dead functions. find_mounted_volume_by_device() and remount_read_only() have no active users. And we don't have a code logic that requires these two functions. Test: mmma bootable/recovery Test: Code search shows no user. Change-Id: Ib11c2ba93ee087ea020c1337eb06686a6165f894 --- mounts.cpp | 17 ----------------- mounts.h | 4 ---- 2 files changed, 21 deletions(-) diff --git a/mounts.cpp b/mounts.cpp index fbcbac014..76fa65739 100644 --- a/mounts.cpp +++ b/mounts.cpp @@ -62,13 +62,6 @@ bool scan_mounted_volumes() { return true; } -MountedVolume* find_mounted_volume_by_device(const char* device) { - for (size_t i = 0; i < g_mounts_state.size(); ++i) { - if (g_mounts_state[i]->device == device) return g_mounts_state[i]; - } - return nullptr; -} - MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point) { for (size_t i = 0; i < g_mounts_state.size(); ++i) { if (g_mounts_state[i]->mount_point == mount_point) return g_mounts_state[i]; @@ -87,13 +80,3 @@ int unmount_mounted_volume(MountedVolume* volume) { } return result; } - -int remount_read_only(MountedVolume* volume) { - int result = mount(volume->device.c_str(), volume->mount_point.c_str(), - volume->filesystem.c_str(), - MS_NOATIME | MS_NODEV | MS_NODIRATIME | MS_RDONLY | MS_REMOUNT, 0); - if (result == -1) { - PLOG(WARNING) << "Failed to remount read-only " << volume->mount_point; - } - return result; -} diff --git a/mounts.h b/mounts.h index 1b7670329..0de1ebd0a 100644 --- a/mounts.h +++ b/mounts.h @@ -21,12 +21,8 @@ struct MountedVolume; bool scan_mounted_volumes(); -MountedVolume* find_mounted_volume_by_device(const char* device); - MountedVolume* find_mounted_volume_by_mount_point(const char* mount_point); int unmount_mounted_volume(MountedVolume* volume); -int remount_read_only(MountedVolume* volume); - #endif -- cgit v1.2.3 From 7fdcb19e10528afd1cd0a80fa2efa51e5e6e3aba Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 12 Apr 2017 23:52:20 -0700 Subject: Add the missing LOCAL_CFLAGS for librecovery. The -DAB_OTA_UDPATER part was missing when separating the librecovery module (commit 62e0bc7586077b3bde82759fb34b51b982cea20f). Bug: 37300957 Test: Build sailfish recovery image and sideload full OTA. Change-Id: Idd25662f6bc812c1aca95b096802f59b051f8d63 (cherry picked from commit 25dbe1717e3096ee59bfa4385cbcf80a972016a6) --- Android.mk | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Android.mk b/Android.mk index 1b83d75ff..9e374de8f 100644 --- a/Android.mk +++ b/Android.mk @@ -47,6 +47,11 @@ LOCAL_SRC_FILES := \ install.cpp LOCAL_CFLAGS := -Wno-unused-parameter -Werror LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) + +ifeq ($(AB_OTA_UPDATER),true) + LOCAL_CFLAGS += -DAB_OTA_UPDATER=1 +endif + LOCAL_MODULE := librecovery LOCAL_STATIC_LIBRARIES := \ libminui \ -- cgit v1.2.3 From 99c549db74ea697136820cf4bdca8a486747f569 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 17 Apr 2017 10:44:00 -0700 Subject: Fix the double free in verify_package_compatibility(). """ void* cookie; std::unique_ptr guard(cookie, EndIteration); ... EndIteration(cookie); """ The above pattern is buggy that frees 'cookie' twice. Bug: 37413730 Test: Build new recovery and adb sideload a previously crashed package that contains 'compatibility.zip'. Change-Id: I183c33827fb28a438ebaedda446e84cabe7cb92d (cherry picked from commit f978278995d02a58e311fe017bdbb2c3702dd3bc) --- install.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/install.cpp b/install.cpp index b4b869b98..6dcd3565e 100644 --- a/install.cpp +++ b/install.cpp @@ -544,7 +544,6 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { } compatibility_info.emplace_back(std::move(content)); } - EndIteration(cookie); CloseArchive(zip_handle); // TODO(b/36814503): Enable the actual verification when VintfObject::CheckCompatibility() lands. -- cgit v1.2.3 From 4608daf0fe6b5feb11afbfb6ae613d7a5161c95d Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Mon, 17 Apr 2017 16:55:54 -0700 Subject: init.rc: Remove sys.powerctl action Init now handles this property change directly. Bug: 37209359 Bug: 37415192 Test: Init reboots normally. Change-Id: I9a4925897fb83bed180d12c871d9a71126fa4aa8 --- etc/init.rc | 3 --- 1 file changed, 3 deletions(-) diff --git a/etc/init.rc b/etc/init.rc index 477e13d5e..2e3c7a739 100644 --- a/etc/init.rc +++ b/etc/init.rc @@ -75,9 +75,6 @@ on late-init trigger early-boot trigger boot -on property:sys.powerctl=* - powerctl ${sys.powerctl} - service ueventd /sbin/ueventd critical seclabel u:r:ueventd:s0 -- cgit v1.2.3 From bc4b1fe4c4305ebf0fbfc891b9b508c14b5c8ef8 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 17 Apr 2017 16:46:05 -0700 Subject: Add tests for update_binary_command(). Expose update_binary_command() through private/install.h for testing purpose. Also make minor clean-ups to install.cpp: a) adding more verbose logging on ExtractToMemory failures; b) update_binary_command() taking std::string instead of const char*; c) moving a few macro and global constants into update_binary_command(). Bug: 37300957 Test: recovery_component_test on marlin Test: Build new recovery and adb sideload on angler and sailfish. Change-Id: Ib2d9068af3fee038f01c90940ccaeb0a7da374fc --- install.cpp | 162 +++++++++++++++++++-------------------- private/install.h | 27 +++++++ tests/Android.mk | 4 + tests/component/install_test.cpp | 87 +++++++++++++++++++++ 4 files changed, 198 insertions(+), 82 deletions(-) create mode 100644 private/install.h diff --git a/install.cpp b/install.cpp index 6dcd3565e..4fb809996 100644 --- a/install.cpp +++ b/install.cpp @@ -57,9 +57,6 @@ using namespace std::chrono_literals; -#define ASSUMED_UPDATE_BINARY_NAME "META-INF/com/google/android/update-binary" -static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt"; -static constexpr const char* AB_OTA_PAYLOAD = "payload.bin"; #define PUBLIC_KEYS_FILE "/res/keys" static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status"; @@ -136,13 +133,11 @@ static void read_source_target_build(ZipArchiveHandle zip, std::vector* cmd); +// Extract the update binary from the open zip archive |zip| located at |path| and store into |cmd| +// the command line that should be called. The |status_fd| is the file descriptor the child process +// should use to report back the progress of the update. +int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count, + int status_fd, std::vector* cmd); #ifdef AB_OTA_UPDATER @@ -224,87 +219,90 @@ static int check_newer_ab_build(ZipArchiveHandle zip) { return 0; } -static int -update_binary_command(const char* path, ZipArchiveHandle zip, int retry_count, - int status_fd, std::vector* cmd) -{ - int ret = check_newer_ab_build(zip); - if (ret) { - return ret; - } +int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count, + int status_fd, std::vector* cmd) { + CHECK(cmd != nullptr); + int ret = check_newer_ab_build(zip); + if (ret != 0) { + return ret; + } - // For A/B updates we extract the payload properties to a buffer and obtain - // the RAW payload offset in the zip file. - ZipString property_name(AB_OTA_PAYLOAD_PROPERTIES); - ZipEntry properties_entry; - if (FindEntry(zip, property_name, &properties_entry) != 0) { - LOG(ERROR) << "Can't find " << AB_OTA_PAYLOAD_PROPERTIES; - return INSTALL_CORRUPT; - } - std::vector payload_properties( - properties_entry.uncompressed_length); - if (ExtractToMemory(zip, &properties_entry, payload_properties.data(), - properties_entry.uncompressed_length) != 0) { - LOG(ERROR) << "Can't extract " << AB_OTA_PAYLOAD_PROPERTIES; - return INSTALL_CORRUPT; - } + // For A/B updates we extract the payload properties to a buffer and obtain the RAW payload offset + // in the zip file. + static constexpr const char* AB_OTA_PAYLOAD_PROPERTIES = "payload_properties.txt"; + ZipString property_name(AB_OTA_PAYLOAD_PROPERTIES); + ZipEntry properties_entry; + if (FindEntry(zip, property_name, &properties_entry) != 0) { + LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD_PROPERTIES; + return INSTALL_CORRUPT; + } + uint32_t properties_entry_length = properties_entry.uncompressed_length; + std::vector payload_properties(properties_entry_length); + int32_t err = + ExtractToMemory(zip, &properties_entry, payload_properties.data(), properties_entry_length); + if (err != 0) { + LOG(ERROR) << "Failed to extract " << AB_OTA_PAYLOAD_PROPERTIES << ": " << ErrorCodeString(err); + return INSTALL_CORRUPT; + } - ZipString payload_name(AB_OTA_PAYLOAD); - ZipEntry payload_entry; - if (FindEntry(zip, payload_name, &payload_entry) != 0) { - LOG(ERROR) << "Can't find " << AB_OTA_PAYLOAD; - return INSTALL_CORRUPT; - } - long payload_offset = payload_entry.offset; - *cmd = { - "/sbin/update_engine_sideload", - android::base::StringPrintf("--payload=file://%s", path), - android::base::StringPrintf("--offset=%ld", payload_offset), - "--headers=" + std::string(payload_properties.begin(), - payload_properties.end()), - android::base::StringPrintf("--status_fd=%d", status_fd), - }; - return 0; + static constexpr const char* AB_OTA_PAYLOAD = "payload.bin"; + ZipString payload_name(AB_OTA_PAYLOAD); + ZipEntry payload_entry; + if (FindEntry(zip, payload_name, &payload_entry) != 0) { + LOG(ERROR) << "Failed to find " << AB_OTA_PAYLOAD; + return INSTALL_CORRUPT; + } + long payload_offset = payload_entry.offset; + *cmd = { + "/sbin/update_engine_sideload", + "--payload=file://" + path, + android::base::StringPrintf("--offset=%ld", payload_offset), + "--headers=" + std::string(payload_properties.begin(), payload_properties.end()), + android::base::StringPrintf("--status_fd=%d", status_fd), + }; + return 0; } #else // !AB_OTA_UPDATER -static int -update_binary_command(const char* path, ZipArchiveHandle zip, int retry_count, - int status_fd, std::vector* cmd) -{ - // On traditional updates we extract the update binary from the package. - ZipString binary_name(ASSUMED_UPDATE_BINARY_NAME); - ZipEntry binary_entry; - if (FindEntry(zip, binary_name, &binary_entry) != 0) { - return INSTALL_CORRUPT; - } +int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count, + int status_fd, std::vector* cmd) { + CHECK(cmd != nullptr); - const char* binary = "/tmp/update_binary"; - unlink(binary); - int fd = creat(binary, 0755); - if (fd < 0) { - PLOG(ERROR) << "Can't make " << binary; - return INSTALL_ERROR; - } - int error = ExtractEntryToFile(zip, &binary_entry, fd); - close(fd); + // On traditional updates we extract the update binary from the package. + static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; + ZipString binary_name(UPDATE_BINARY_NAME); + ZipEntry binary_entry; + if (FindEntry(zip, binary_name, &binary_entry) != 0) { + LOG(ERROR) << "Failed to find update binary " << UPDATE_BINARY_NAME; + return INSTALL_CORRUPT; + } - if (error != 0) { - LOG(ERROR) << "Can't copy " << ASSUMED_UPDATE_BINARY_NAME - << " : " << ErrorCodeString(error); - return INSTALL_ERROR; - } + const char* binary = "/tmp/update_binary"; + unlink(binary); + int fd = creat(binary, 0755); + if (fd == -1) { + PLOG(ERROR) << "Failed to create " << binary; + return INSTALL_ERROR; + } - *cmd = { - binary, - EXPAND(RECOVERY_API_VERSION), // defined in Android.mk - std::to_string(status_fd), - path, - }; - if (retry_count > 0) - cmd->push_back("retry"); - return 0; + int32_t error = ExtractEntryToFile(zip, &binary_entry, fd); + close(fd); + if (error != 0) { + LOG(ERROR) << "Failed to extract " << UPDATE_BINARY_NAME << ": " << ErrorCodeString(error); + return INSTALL_ERROR; + } + + *cmd = { + binary, + EXPAND(RECOVERY_API_VERSION), // defined in Android.mk + std::to_string(status_fd), + path, + }; + if (retry_count > 0) { + cmd->push_back("retry"); + } + return 0; } #endif // !AB_OTA_UPDATER diff --git a/private/install.h b/private/install.h new file mode 100644 index 000000000..12d303b01 --- /dev/null +++ b/private/install.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 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. + */ + +// Private headers exposed for testing purpose only. + +#pragma once + +#include +#include + +#include + +int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count, + int status_fd, std::vector* cmd); diff --git a/tests/Android.mk b/tests/Android.mk index a1f0d4892..20cbcc147 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -85,6 +85,10 @@ LOCAL_CFLAGS := \ -Werror \ -D_FILE_OFFSET_BITS=64 +ifeq ($(AB_OTA_UPDATER),true) +LOCAL_CFLAGS += -DAB_OTA_UPDATER=1 +endif + LOCAL_MODULE := recovery_component_test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_C_INCLUDES := bootable/recovery diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 3b6fbc301..fd3b28b07 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -16,12 +16,18 @@ #include +#include +#include + +#include +#include #include #include #include #include #include "install.h" +#include "private/install.h" TEST(InstallTest, verify_package_compatibility_no_entry) { TemporaryFile temp_file; @@ -55,3 +61,84 @@ TEST(InstallTest, verify_package_compatibility_invalid_entry) { ASSERT_FALSE(verify_package_compatibility(zip)); CloseArchive(zip); } + +TEST(InstallTest, update_binary_command_smoke) { +#ifdef AB_OTA_UPDATER + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.StartEntry("payload_properties.txt", kCompressStored)); + const std::string properties = "some_properties"; + ASSERT_EQ(0, writer.WriteBytes(properties.data(), properties.size())); + ASSERT_EQ(0, writer.FinishEntry()); + // A metadata entry is mandatory. + ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); + ASSERT_NE("", timestamp); + std::string metadata = android::base::Join( + std::vector{ + "ota-type=AB", "pre-device=" + device, "post-timestamp=" + timestamp, + }, + "\n"); + ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size())); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + int status_fd = 10; + std::string path = "/path/to/update.zip"; + std::vector cmd; + ASSERT_EQ(0, update_binary_command(path, zip, 0, status_fd, &cmd)); + ASSERT_EQ("/sbin/update_engine_sideload", cmd[0]); + ASSERT_EQ("--payload=file://" + path, cmd[1]); + ASSERT_EQ("--headers=" + properties, cmd[3]); + ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); + CloseArchive(zip); +#else + // Cannot test update_binary_command() because it tries to extract update-binary to /tmp. + GTEST_LOG_(INFO) << "Test skipped on non-A/B device."; +#endif // AB_OTA_UPDATER +} + +TEST(InstallTest, update_binary_command_invalid) { +#ifdef AB_OTA_UPDATER + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + // Missing payload_properties.txt. + ASSERT_EQ(0, writer.StartEntry("payload.bin", kCompressStored)); + ASSERT_EQ(0, writer.FinishEntry()); + // A metadata entry is mandatory. + ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); + std::string device = android::base::GetProperty("ro.product.device", ""); + ASSERT_NE("", device); + std::string timestamp = android::base::GetProperty("ro.build.date.utc", ""); + ASSERT_NE("", timestamp); + std::string metadata = android::base::Join( + std::vector{ + "ota-type=AB", "pre-device=" + device, "post-timestamp=" + timestamp, + }, + "\n"); + ASSERT_EQ(0, writer.WriteBytes(metadata.data(), metadata.size())); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + int status_fd = 10; + std::string path = "/path/to/update.zip"; + std::vector cmd; + ASSERT_EQ(INSTALL_CORRUPT, update_binary_command(path, zip, 0, status_fd, &cmd)); + CloseArchive(zip); +#else + // Cannot test update_binary_command() because it tries to extract update-binary to /tmp. + GTEST_LOG_(INFO) << "Test skipped on non-A/B device."; +#endif // AB_OTA_UPDATER +} -- cgit v1.2.3 From 0ad2de5eab12dbf63ad43bd0c3e5ef729984cf81 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 18 Apr 2017 11:29:32 -0700 Subject: Add 'system' to update_verifier's gid This addresses the denial to /dev/cpuset/tasks: update_verifier: type=1400 audit(0.0:377): avc: denied { dac_override } for capability=1 scontext=u:r:update_verifier:s0 tcontext=u:r:update_verifier:s0 tclass=capability permissive=1 update_verifier: type=1400 audit(0.0:378): avc: granted { write } for name="tasks" dev="cgroup" ino=5 scontext=u:r:update_verifier:s0 tcontext=u:object_r:cgroup:s0 tclass=file Bug: 37358323 Test: denial message gone after adding system group Change-Id: I66b4925295a13fbc1c6f26a1bb9bd2f9cebcec3d --- update_verifier/update_verifier.rc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/update_verifier/update_verifier.rc b/update_verifier/update_verifier.rc index 808f2c055..862b06257 100644 --- a/update_verifier/update_verifier.rc +++ b/update_verifier/update_verifier.rc @@ -1,11 +1,11 @@ service update_verifier_nonencrypted /system/bin/update_verifier nonencrypted user root - group cache + group cache system priority -20 ioprio rt 0 service update_verifier /system/bin/update_verifier ${vold.decrypt} user root - group cache + group cache system priority -20 ioprio rt 0 -- cgit v1.2.3 From f8119fbafba0a25c93d9b2ba82b12f4668e7d9ca Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 18 Apr 2017 21:35:12 -0700 Subject: Minor clean up to install.cpp. - Move some macros / constants into functions; - Remove unneeded #include "minui/minui.h"; - Remove two dead constants (DEFAULT_{FILES,IMAGES}_PROGRESS_FRACTION). Test: mmma bootable/recovery Change-Id: Ib808f14b7569e06e23a8a7cc9b2d4e9aa5469de1 --- install.cpp | 127 +++++++++++++++++++++++++++++------------------------------- 1 file changed, 61 insertions(+), 66 deletions(-) diff --git a/install.cpp b/install.cpp index 4fb809996..95794ce07 100644 --- a/install.cpp +++ b/install.cpp @@ -48,7 +48,6 @@ #include "common.h" #include "error_code.h" -#include "minui/minui.h" #include "otautil/SysUtil.h" #include "otautil/ThermalUtil.h" #include "roots.h" @@ -57,15 +56,11 @@ using namespace std::chrono_literals; -#define PUBLIC_KEYS_FILE "/res/keys" static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; -static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status"; // Default allocation of progress bar segments to operations static constexpr int VERIFICATION_PROGRESS_TIME = 60; static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25; -static constexpr float DEFAULT_FILES_PROGRESS_FRACTION = 0.4; -static constexpr float DEFAULT_IMAGE_PROGRESS_FRACTION = 0.1; static std::condition_variable finish_log_temperature; @@ -621,80 +616,80 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount, return result; } -int -install_package(const char* path, bool* wipe_cache, const char* install_file, - bool needs_mount, int retry_count) -{ - modified_flash = true; - auto start = std::chrono::system_clock::now(); +int install_package(const char* path, bool* wipe_cache, const char* install_file, bool needs_mount, + int retry_count) { + modified_flash = true; + auto start = std::chrono::system_clock::now(); + + int start_temperature = GetMaxValueFromThermalZone(); + int max_temperature = start_temperature; + + int result; + std::vector log_buffer; + if (setup_install_mounts() != 0) { + LOG(ERROR) << "failed to set up expected mounts for install; aborting"; + result = INSTALL_ERROR; + } else { + result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count, + &max_temperature); + } - int start_temperature = GetMaxValueFromThermalZone(); - int max_temperature = start_temperature; + // Measure the time spent to apply OTA update in seconds. + std::chrono::duration duration = std::chrono::system_clock::now() - start; + int time_total = static_cast(duration.count()); - int result; - std::vector log_buffer; - if (setup_install_mounts() != 0) { - LOG(ERROR) << "failed to set up expected mounts for install; aborting"; - result = INSTALL_ERROR; + bool has_cache = volume_for_path("/cache") != nullptr; + // Skip logging the uncrypt_status on devices without /cache. + if (has_cache) { + static constexpr const char* UNCRYPT_STATUS = "/cache/recovery/uncrypt_status"; + if (ensure_path_mounted(UNCRYPT_STATUS) != 0) { + LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS; } else { - result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count, - &max_temperature); - } - - // Measure the time spent to apply OTA update in seconds. - std::chrono::duration duration = std::chrono::system_clock::now() - start; - int time_total = static_cast(duration.count()); - - bool has_cache = volume_for_path("/cache") != nullptr; - // Skip logging the uncrypt_status on devices without /cache. - if (has_cache) { - if (ensure_path_mounted(UNCRYPT_STATUS) != 0) { - LOG(WARNING) << "Can't mount " << UNCRYPT_STATUS; + std::string uncrypt_status; + if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) { + PLOG(WARNING) << "failed to read uncrypt status"; + } else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) { + LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status; } else { - std::string uncrypt_status; - if (!android::base::ReadFileToString(UNCRYPT_STATUS, &uncrypt_status)) { - PLOG(WARNING) << "failed to read uncrypt status"; - } else if (!android::base::StartsWith(uncrypt_status, "uncrypt_")) { - LOG(WARNING) << "corrupted uncrypt_status: " << uncrypt_status; - } else { - log_buffer.push_back(android::base::Trim(uncrypt_status)); - } + log_buffer.push_back(android::base::Trim(uncrypt_status)); } } + } - // The first two lines need to be the package name and install result. - std::vector log_header = { - path, - result == INSTALL_SUCCESS ? "1" : "0", - "time_total: " + std::to_string(time_total), - "retry: " + std::to_string(retry_count), - }; - - int end_temperature = GetMaxValueFromThermalZone(); - max_temperature = std::max(end_temperature, max_temperature); - if (start_temperature > 0) { - log_buffer.push_back("temperature_start: " + std::to_string(start_temperature)); - } - if (end_temperature > 0) { - log_buffer.push_back("temperature_end: " + std::to_string(end_temperature)); - } - if (max_temperature > 0) { - log_buffer.push_back("temperature_max: " + std::to_string(max_temperature)); - } + // The first two lines need to be the package name and install result. + std::vector log_header = { + path, + result == INSTALL_SUCCESS ? "1" : "0", + "time_total: " + std::to_string(time_total), + "retry: " + std::to_string(retry_count), + }; - std::string log_content = android::base::Join(log_header, "\n") + "\n" + - android::base::Join(log_buffer, "\n") + "\n"; - if (!android::base::WriteStringToFile(log_content, install_file)) { - PLOG(ERROR) << "failed to write " << install_file; - } + int end_temperature = GetMaxValueFromThermalZone(); + max_temperature = std::max(end_temperature, max_temperature); + if (start_temperature > 0) { + log_buffer.push_back("temperature_start: " + std::to_string(start_temperature)); + } + if (end_temperature > 0) { + log_buffer.push_back("temperature_end: " + std::to_string(end_temperature)); + } + if (max_temperature > 0) { + log_buffer.push_back("temperature_max: " + std::to_string(max_temperature)); + } - // Write a copy into last_log. - LOG(INFO) << log_content; + std::string log_content = + android::base::Join(log_header, "\n") + "\n" + android::base::Join(log_buffer, "\n") + "\n"; + if (!android::base::WriteStringToFile(log_content, install_file)) { + PLOG(ERROR) << "failed to write " << install_file; + } - return result; + // Write a copy into last_log. + LOG(INFO) << log_content; + + return result; } bool verify_package(const unsigned char* package_data, size_t package_size) { + static constexpr const char* PUBLIC_KEYS_FILE = "/res/keys"; std::vector loadedKeys; if (!load_keys(PUBLIC_KEYS_FILE, loadedKeys)) { LOG(ERROR) << "Failed to load keys"; -- cgit v1.2.3 From 8a7afcc6ed5467dd2e9800244d10a88cd359a0a3 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 18 Apr 2017 22:05:50 -0700 Subject: Add tests for read_metadata_from_package(). Test: recovery_component_test Change-Id: I672a6a4f101c72e82b9f25f165dccd1c9520627b --- install.cpp | 82 +++++++++++++++++++--------------------- tests/component/install_test.cpp | 50 ++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 43 deletions(-) diff --git a/install.cpp b/install.cpp index 95794ce07..73ddf5e92 100644 --- a/install.cpp +++ b/install.cpp @@ -56,8 +56,6 @@ using namespace std::chrono_literals; -static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; - // Default allocation of progress bar segments to operations static constexpr int VERIFICATION_PROGRESS_TIME = 60; static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25; @@ -79,53 +77,51 @@ static int parse_build_number(const std::string& str) { return -1; } -bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data) { - ZipString metadata_path(METADATA_PATH); - ZipEntry meta_entry; - if (meta_data == nullptr) { - LOG(ERROR) << "string* meta_data can't be nullptr"; - return false; - } - if (FindEntry(zip, metadata_path, &meta_entry) != 0) { - LOG(ERROR) << "Failed to find " << METADATA_PATH << " in update package"; - return false; - } +bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) { + CHECK(metadata != nullptr); - meta_data->resize(meta_entry.uncompressed_length, '\0'); - if (ExtractToMemory(zip, &meta_entry, reinterpret_cast(&(*meta_data)[0]), - meta_entry.uncompressed_length) != 0) { - LOG(ERROR) << "Failed to read metadata in update package"; - return false; - } - return true; + static constexpr const char* METADATA_PATH = "META-INF/com/android/metadata"; + ZipString path(METADATA_PATH); + ZipEntry entry; + if (FindEntry(zip, path, &entry) != 0) { + LOG(ERROR) << "Failed to find " << METADATA_PATH; + return false; + } + + uint32_t length = entry.uncompressed_length; + metadata->resize(length, '\0'); + int32_t err = ExtractToMemory(zip, &entry, reinterpret_cast(&(*metadata)[0]), length); + if (err != 0) { + LOG(ERROR) << "Failed to extract " << METADATA_PATH << ": " << ErrorCodeString(err); + return false; + } + return true; } // Read the build.version.incremental of src/tgt from the metadata and log it to last_install. static void read_source_target_build(ZipArchiveHandle zip, std::vector& log_buffer) { - std::string meta_data; - if (!read_metadata_from_package(zip, &meta_data)) { - return; - } - // Examples of the pre-build and post-build strings in metadata: - // pre-build-incremental=2943039 - // post-build-incremental=2951741 - std::vector lines = android::base::Split(meta_data, "\n"); - for (const std::string& line : lines) { - std::string str = android::base::Trim(line); - if (android::base::StartsWith(str, "pre-build-incremental")){ - int source_build = parse_build_number(str); - if (source_build != -1) { - log_buffer.push_back(android::base::StringPrintf("source_build: %d", - source_build)); - } - } else if (android::base::StartsWith(str, "post-build-incremental")) { - int target_build = parse_build_number(str); - if (target_build != -1) { - log_buffer.push_back(android::base::StringPrintf("target_build: %d", - target_build)); - } - } + std::string metadata; + if (!read_metadata_from_package(zip, &metadata)) { + return; + } + // Examples of the pre-build and post-build strings in metadata: + // pre-build-incremental=2943039 + // post-build-incremental=2951741 + std::vector lines = android::base::Split(metadata, "\n"); + for (const std::string& line : lines) { + std::string str = android::base::Trim(line); + if (android::base::StartsWith(str, "pre-build-incremental")) { + int source_build = parse_build_number(str); + if (source_build != -1) { + log_buffer.push_back(android::base::StringPrintf("source_build: %d", source_build)); + } + } else if (android::base::StartsWith(str, "post-build-incremental")) { + int target_build = parse_build_number(str); + if (target_build != -1) { + log_buffer.push_back(android::base::StringPrintf("target_build: %d", target_build)); + } } + } } // Extract the update binary from the open zip archive |zip| located at |path| and store into |cmd| diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index fd3b28b07..2143dd7bb 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -62,6 +62,56 @@ TEST(InstallTest, verify_package_compatibility_invalid_entry) { CloseArchive(zip); } +TEST(InstallTest, read_metadata_from_package_smoke) { + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + ASSERT_EQ(0, writer.StartEntry("META-INF/com/android/metadata", kCompressStored)); + const std::string content("abcdefg"); + ASSERT_EQ(0, writer.WriteBytes(content.data(), content.size())); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + std::string metadata; + ASSERT_TRUE(read_metadata_from_package(zip, &metadata)); + ASSERT_EQ(content, metadata); + CloseArchive(zip); + + TemporaryFile temp_file2; + FILE* zip_file2 = fdopen(temp_file2.fd, "w"); + ZipWriter writer2(zip_file2); + ASSERT_EQ(0, writer2.StartEntry("META-INF/com/android/metadata", kCompressDeflated)); + ASSERT_EQ(0, writer2.WriteBytes(content.data(), content.size())); + ASSERT_EQ(0, writer2.FinishEntry()); + ASSERT_EQ(0, writer2.Finish()); + ASSERT_EQ(0, fclose(zip_file2)); + + ASSERT_EQ(0, OpenArchive(temp_file2.path, &zip)); + metadata.clear(); + ASSERT_TRUE(read_metadata_from_package(zip, &metadata)); + ASSERT_EQ(content, metadata); + CloseArchive(zip); +} + +TEST(InstallTest, read_metadata_from_package_no_entry) { + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + ASSERT_EQ(0, writer.StartEntry("dummy_entry", kCompressStored)); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + std::string metadata; + ASSERT_FALSE(read_metadata_from_package(zip, &metadata)); + CloseArchive(zip); +} + TEST(InstallTest, update_binary_command_smoke) { #ifdef AB_OTA_UPDATER TemporaryFile temp_file; -- cgit v1.2.3 From 8706a98aa635236a95795f0a0c122bb3e591a50d Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Tue, 18 Apr 2017 08:28:26 -0700 Subject: Adding support for quiescent reboot to recovery Bug: 37401320 Test: build and push OTA and hit adb reboot recovery,quiescent. The screen should remain off throughout the upgrade process. Change-Id: Ibed3795c09e26c4fa73684d40b94e40c78394d3f --- common.h | 2 ++ recovery.cpp | 26 ++++++++++++++++++++------ ui.cpp | 2 +- updater/install.cpp | 5 ++++- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/common.h b/common.h index bda793599..62fb1324b 100644 --- a/common.h +++ b/common.h @@ -43,4 +43,6 @@ void ui_print(const char* format, ...); bool is_ro_debuggable(); +bool reboot(const std::string& command); + #endif // RECOVERY_COMMON_H diff --git a/recovery.cpp b/recovery.cpp index b24efa963..173baac16 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -209,6 +209,14 @@ bool is_ro_debuggable() { return android::base::GetBoolProperty("ro.debuggable", false); } +bool reboot(const std::string& command) { + std::string cmd = command; + if (android::base::GetBoolProperty("ro.boot.quiescent", false)) { + cmd += ",quiescent"; + } + return android::base::SetProperty(ANDROID_RB_PROPERTY, cmd); +} + static void redirect_stdio(const char* filename) { int pipefd[2]; if (pipe(pipefd) == -1) { @@ -1449,12 +1457,18 @@ int main(int argc, char **argv) { printf("reason is [%s]\n", reason); Device* device = make_device(); - ui = device->GetUI(); + if (android::base::GetBoolProperty("ro.boot.quiescent", false)) { + printf("Quiescent recovery mode.\n"); + ui = new StubRecoveryUI(); + } else { + ui = device->GetUI(); - if (!ui->Init(locale)) { - printf("Failed to initialize UI, use stub UI instead."); - ui = new StubRecoveryUI(); + if (!ui->Init(locale)) { + printf("Failed to initialize UI, use stub UI instead.\n"); + ui = new StubRecoveryUI(); + } } + // Set background string to "installing security update" for security update, // otherwise set it to "installing system update". ui->SetSystemUpdateText(security_update); @@ -1526,7 +1540,7 @@ int main(int argc, char **argv) { ui->Print("Retry attempt %d\n", retry_count); // Reboot and retry the update - if (!android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,recovery")) { + if (!reboot("reboot,recovery")) { ui->Print("Reboot failed\n"); } else { while (true) { @@ -1623,7 +1637,7 @@ int main(int argc, char **argv) { default: ui->Print("Rebooting...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,"); + reboot("reboot,"); break; } while (true) { diff --git a/ui.cpp b/ui.cpp index 9194ae3df..cad744930 100644 --- a/ui.cpp +++ b/ui.cpp @@ -227,7 +227,7 @@ void RecoveryUI::ProcessKey(int key_code, int updown) { case RecoveryUI::REBOOT: if (reboot_enabled) { - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,"); + reboot("reboot,"); while (true) { pause(); } } break; diff --git a/updater/install.cpp b/updater/install.cpp index 857d7f1e0..888239c83 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -890,7 +890,10 @@ Value* RebootNowFn(const char* name, State* state, const std::vector Date: Wed, 19 Apr 2017 14:00:52 -0700 Subject: Minor clean up to minadbd_services.cpp Switch some function signatures to std::string to avoid memory leak. Bug: 30039381 Test: sideload a package on angler Change-Id: Iae1e75871a782d6e5d6dde5dcf3f18469eb63f7d --- minadbd/minadbd_services.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index a6aa321ca..61c06cc0a 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include "adb.h" @@ -28,33 +29,30 @@ #include "fuse_adb_provider.h" #include "sysdeps.h" -static void sideload_host_service(int sfd, void* data) { - char* args = reinterpret_cast(data); +static void sideload_host_service(int sfd, const std::string& args) { int file_size; int block_size; - if (sscanf(args, "%d:%d", &file_size, &block_size) != 2) { - printf("bad sideload-host arguments: %s\n", args); + if (sscanf(args.c_str(), "%d:%d", &file_size, &block_size) != 2) { + printf("bad sideload-host arguments: %s\n", args.c_str()); exit(1); } - free(args); printf("sideload-host file size %d block size %d\n", file_size, block_size); int result = run_adb_fuse(sfd, file_size, block_size); printf("sideload_host finished\n"); - sleep(1); exit(result == 0 ? 0 : 1); } -static int create_service_thread(void (*func)(int, void *), void *cookie) { +static int create_service_thread(void (*func)(int, const std::string&), const std::string& args) { int s[2]; if (adb_socketpair(s)) { printf("cannot create service socket pair\n"); return -1; } - std::thread([s, func, cookie]() { func(s[1], cookie); }).detach(); + std::thread([s, func, args]() { func(s[1], args); }).detach(); VLOG(SERVICES) << "service thread started, " << s[0] << ":" << s[1]; return s[0]; @@ -69,7 +67,7 @@ int service_to_fd(const char* name, const atransport* transport) { // sideload-host). exit(3); } else if (!strncmp(name, "sideload-host:", 14)) { - char* arg = strdup(name + 14); + std::string arg(name + 14); ret = create_service_thread(sideload_host_service, arg); } if (ret >= 0) { -- cgit v1.2.3 From 919d2c9a5341d123fa1c97ffad3549c20e1dd021 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 10 Apr 2017 16:55:57 -0700 Subject: Call libvintf to verify package compatibility. The libvintf API has landed. Hook up to do the actual verification. Bug: 36597505 Test: recovery_component_test Test: m recoveryimage; adb sideload on angler and sailfish, with packages that contain dummy compatibility entries. Test: m recoveryimage; adb sideload on angler and sailfish, with packages that don't contain any compatibility entries. Change-Id: Idbd6f5aaef605ca51b20e667505d686de5ac781f (cherry picked from commit da320ac6ab53395ddff3cc08b88a61f977ed939a) --- Android.mk | 4 ++++ install.cpp | 15 ++++++++++----- tests/Android.mk | 3 +++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Android.mk b/Android.mk index 9e374de8f..adf478f00 100644 --- a/Android.mk +++ b/Android.mk @@ -55,6 +55,7 @@ endif LOCAL_MODULE := librecovery LOCAL_STATIC_LIBRARIES := \ libminui \ + libvintf_recovery \ libcrypto_utils \ libcrypto \ libbase @@ -114,6 +115,9 @@ LOCAL_STATIC_LIBRARIES := \ libfs_mgr \ libcrypto_utils \ libcrypto \ + libvintf_recovery \ + libvintf \ + libtinyxml2 \ libbase \ libcutils \ libutils \ diff --git a/install.cpp b/install.cpp index 95794ce07..2a647b1a7 100644 --- a/install.cpp +++ b/install.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include "common.h" @@ -539,10 +540,15 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { } CloseArchive(zip_handle); - // TODO(b/36814503): Enable the actual verification when VintfObject::CheckCompatibility() lands. - // VintfObject::CheckCompatibility returns zero on success. - // return (android::vintf::VintfObject::CheckCompatibility(compatibility_info, true) == 0); - return true; + // VintfObjectRecovery::CheckCompatibility returns zero on success. + std::string err; + int result = android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err); + if (result == 0) { + return true; + } + + LOG(ERROR) << "Failed to verify package compatibility (result " << result << "): " << err; + return false; } static int @@ -594,7 +600,6 @@ really_install_package(const char *path, bool* wipe_cache, bool needs_mount, // Additionally verify the compatibility of the package. if (!verify_package_compatibility(zip)) { - LOG(ERROR) << "Failed to verify package compatibility"; log_buffer.push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure)); sysReleaseMap(&map); CloseArchive(zip); diff --git a/tests/Android.mk b/tests/Android.mk index 20cbcc147..e52663031 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -132,6 +132,9 @@ LOCAL_STATIC_LIBRARIES := \ libdivsufsort64 \ libfs_mgr \ liblog \ + libvintf_recovery \ + libvintf \ + libtinyxml2 \ libselinux \ libext4_utils \ libsparse \ -- cgit v1.2.3 From f2784b6a43e54ed67bc30ac456f66f11bd16bc74 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 19 Apr 2017 12:37:12 -0700 Subject: Add more tests for verify_package_compatibility(). This now covers the actual calls to libvintf, and asserts we're getting identical results through verify_package_compatibility() and by calling libvintf directly. We were missing the coverage and introduced the double free bug (fixed by commit f978278995d02a58e311fe017bdbb2c3702dd3bc). Bug: 37413730 Test: recovery_component_test passes. Test: recovery_component_test fails w/o commit f978278995d02a58e311fe017bdbb2c3702dd3bc. Change-Id: If5195ea1c583fd7c440a1de289da82145e80e23c --- tests/component/install_test.cpp | 84 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 2143dd7bb..40201d76f 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -15,14 +15,17 @@ */ #include +#include #include #include +#include #include #include #include #include +#include #include #include @@ -112,6 +115,87 @@ TEST(InstallTest, read_metadata_from_package_no_entry) { CloseArchive(zip); } +TEST(InstallTest, verify_package_compatibility_with_libvintf_malformed_xml) { + TemporaryFile compatibility_zip_file; + FILE* compatibility_zip = fdopen(compatibility_zip_file.fd, "w"); + ZipWriter compatibility_zip_writer(compatibility_zip); + ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); + std::string malformed_xml = "malformed"; + ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(malformed_xml.data(), malformed_xml.size())); + ASSERT_EQ(0, compatibility_zip_writer.FinishEntry()); + ASSERT_EQ(0, compatibility_zip_writer.Finish()); + ASSERT_EQ(0, fclose(compatibility_zip)); + + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); + std::string compatibility_zip_content; + ASSERT_TRUE( + android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); + ASSERT_EQ(0, + writer.WriteBytes(compatibility_zip_content.data(), compatibility_zip_content.size())); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + std::vector compatibility_info; + compatibility_info.push_back(malformed_xml); + // Malformed compatibility zip is expected to be rejected by libvintf. But we defer that to + // libvintf. + std::string err; + bool result = + android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0; + ASSERT_EQ(result, verify_package_compatibility(zip)); + CloseArchive(zip); +} + +TEST(InstallTest, verify_package_compatibility_with_libvintf_system_manifest_xml) { + static constexpr const char* system_manifest_xml_path = "/system/manifest.xml"; + if (access(system_manifest_xml_path, R_OK) == -1) { + GTEST_LOG_(INFO) << "Test skipped on devices w/o /system/manifest.xml."; + return; + } + std::string system_manifest_xml_content; + ASSERT_TRUE( + android::base::ReadFileToString(system_manifest_xml_path, &system_manifest_xml_content)); + TemporaryFile compatibility_zip_file; + FILE* compatibility_zip = fdopen(compatibility_zip_file.fd, "w"); + ZipWriter compatibility_zip_writer(compatibility_zip); + ASSERT_EQ(0, compatibility_zip_writer.StartEntry("system_manifest.xml", kCompressDeflated)); + ASSERT_EQ(0, compatibility_zip_writer.WriteBytes(system_manifest_xml_content.data(), + system_manifest_xml_content.size())); + ASSERT_EQ(0, compatibility_zip_writer.FinishEntry()); + ASSERT_EQ(0, compatibility_zip_writer.Finish()); + ASSERT_EQ(0, fclose(compatibility_zip)); + + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + ASSERT_EQ(0, writer.StartEntry("compatibility.zip", kCompressStored)); + std::string compatibility_zip_content; + ASSERT_TRUE( + android::base::ReadFileToString(compatibility_zip_file.path, &compatibility_zip_content)); + ASSERT_EQ(0, + writer.WriteBytes(compatibility_zip_content.data(), compatibility_zip_content.size())); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + std::vector compatibility_info; + compatibility_info.push_back(system_manifest_xml_content); + std::string err; + bool result = + android::vintf::VintfObjectRecovery::CheckCompatibility(compatibility_info, &err) == 0; + // Make sure the result is consistent with libvintf library. + ASSERT_EQ(result, verify_package_compatibility(zip)); + CloseArchive(zip); +} + TEST(InstallTest, update_binary_command_smoke) { #ifdef AB_OTA_UPDATER TemporaryFile temp_file; -- cgit v1.2.3 From 0a3e4dca5847bec8a0e0e7f67c8bccf208d01b80 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 21 Apr 2017 09:26:48 -0700 Subject: tests: Remove obsolete classes in applypatch_test. ApplyPatchFullTest and ApplyPatchDoubleCacheTest were used for defining testcases for file-based OTA. The testcases have already been removed by commit 40e144dae877654f75e65242535036058ea48f58. This CL removes the obsolete class defnitions. Bug: 37559618 Test: recovery_component_test on angler and marlin respectively. Change-Id: I3f4f1dfc8580cf010365e671de256f68bbc0d99a --- tests/component/applypatch_test.cpp | 51 ++----------------------------------- 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 5cba68f8a..6fdf78e47 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -105,9 +105,6 @@ class ApplyPatchTest : public ::testing::Test { static size_t new_size; }; -std::string ApplyPatchTest::old_file; -std::string ApplyPatchTest::new_file; - static void cp(const std::string& src, const std::string& tgt) { std::string cmd = "cp " + src + " " + tgt; system(cmd.c_str()); @@ -132,48 +129,8 @@ class ApplyPatchCacheTest : public ApplyPatchTest { } }; -class ApplyPatchFullTest : public ApplyPatchCacheTest { - public: - static void SetUpTestCase() { - ApplyPatchTest::SetUpTestCase(); - - output_f = new TemporaryFile(); - output_loc = std::string(output_f->path); - - struct FileContents fc; - - ASSERT_EQ(0, LoadFileContents(&rand_file[0], &fc)); - patches.push_back( - std::make_unique(VAL_BLOB, std::string(fc.data.begin(), fc.data.end()))); - - ASSERT_EQ(0, LoadFileContents(&patch_file[0], &fc)); - patches.push_back( - std::make_unique(VAL_BLOB, std::string(fc.data.begin(), fc.data.end()))); - } - - static void TearDownTestCase() { - delete output_f; - patches.clear(); - } - - static std::vector> patches; - static TemporaryFile* output_f; - static std::string output_loc; -}; - -class ApplyPatchDoubleCacheTest : public ApplyPatchFullTest { - public: - virtual void SetUp() { - ApplyPatchCacheTest::SetUp(); - cp(cache_file, "/cache/reallysaved.file"); - } - - virtual void TearDown() { - cp("/cache/reallysaved.file", cache_file); - ApplyPatchCacheTest::TearDown(); - } -}; - +std::string ApplyPatchTest::old_file; +std::string ApplyPatchTest::new_file; std::string ApplyPatchTest::rand_file; std::string ApplyPatchTest::patch_file; std::string ApplyPatchTest::cache_file; @@ -184,10 +141,6 @@ std::string ApplyPatchTest::bad_sha1_b; size_t ApplyPatchTest::old_size; size_t ApplyPatchTest::new_size; -std::vector> ApplyPatchFullTest::patches; -TemporaryFile* ApplyPatchFullTest::output_f; -std::string ApplyPatchFullTest::output_loc; - TEST_F(ApplyPatchTest, CheckModeSkip) { std::vector sha1s; ASSERT_EQ(0, applypatch_check(&old_file[0], sha1s)); -- cgit v1.2.3 From d9d16297dd0cdc186ac7fbe867b61eb035e3548c Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 20 Apr 2017 18:08:21 -0700 Subject: Reboot the device on user build after the install fails Users can't do much after the install fails with the device showing "error" under recovery. So our best choice is to reboot the device since sometimes the system image is still bootable (i.e. on package verification failure). At worst the device would stuck in a boot loop where the users need the same professional knowledge to recover as before. Behaviors after installation failure (including data wipe): If recovery text is visible: No change. If recovery text is not visible: Old behavior: Wait under "error" screen. Reboot after UI timeout (120s) if not connected to usb charger. New behavior: Wait for 5s (shortens from the 120s timeout) under "error" screen and reboot (w or w/o charger). sideload-auto-reboot (only available for userdebug): Old behavior: Reboot immediately after installation failure. New behavior: Wait for 5s under "error" screen and reboot. Bug: 35386985 Test: On angler user, device auto reboots 5s after a failing OTA. Change-Id: I3ff0ead86e2ccec9445d6a2865bc2c463855f23c --- recovery.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index b24efa963..99126eedc 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1593,14 +1593,22 @@ int main(int argc, char **argv) { } } - if (!sideload_auto_reboot && (status == INSTALL_ERROR || status == INSTALL_CORRUPT)) { - copy_logs(); + if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { ui->SetBackground(RecoveryUI::ERROR); + if (!ui->IsTextVisible()) { + sleep(5); + } } Device::BuiltinAction after = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; - if ((status != INSTALL_SUCCESS && status != INSTALL_SKIPPED && !sideload_auto_reboot) || - ui->IsTextVisible()) { + // 1. If the recovery menu is visible, prompt and wait for commands. + // 2. If the state is INSTALL_NONE, wait for commands. (i.e. In user build, manually reboot into + // recovery to sideload a package.) + // 3. sideload_auto_reboot is an option only available in user-debug build, reboot the device + // without waiting. + // 4. In all other cases, reboot the device. Therefore, normal users will observe the device + // reboot after it shows the "error" screen for 5s. + if ((status == INSTALL_NONE && !sideload_auto_reboot) || ui->IsTextVisible()) { Device::BuiltinAction temp = prompt_and_wait(device, status); if (temp != Device::NO_ACTION) { after = temp; -- cgit v1.2.3 From b24510cd60c899f2967d3f0875ee4a744aa569c2 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 20 Apr 2017 17:54:27 -0700 Subject: librecovery: Remove -Wno-unused-parameter and add -Wall. Test: mmma bootable/recovery Change-Id: I5598d32bebb9dbda4a183a1502e0b7dc4918392e --- Android.mk | 2 +- install.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Android.mk b/Android.mk index adf478f00..5e2dddec4 100644 --- a/Android.mk +++ b/Android.mk @@ -45,7 +45,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ install.cpp -LOCAL_CFLAGS := -Wno-unused-parameter -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) ifeq ($(AB_OTA_UPDATER),true) diff --git a/install.cpp b/install.cpp index e5a59b832..e945d13ab 100644 --- a/install.cpp +++ b/install.cpp @@ -211,7 +211,7 @@ static int check_newer_ab_build(ZipArchiveHandle zip) { return 0; } -int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count, +int update_binary_command(const std::string& path, ZipArchiveHandle zip, int /* retry_count */, int status_fd, std::vector* cmd) { CHECK(cmd != nullptr); int ret = check_newer_ab_build(zip); -- cgit v1.2.3 From d8039acce641577b8387515e461d7c44558fc677 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 24 Apr 2017 12:13:54 -0700 Subject: recovery: Remove the include of adb.h. recovery.cpp doesn't have a direct dependency on adb.h (only minadbd does). 'adb.h' was once needed for DEFAULT_ADB_PORT, until being killed by commit acb2a2fa4c415944a27539461ba3757f3edbb128. Test: mmma bootable/recovery Change-Id: I84f87988d01c3f0c3d1383cc3cffde2e7d5e7aea --- Android.mk | 2 -- recovery.cpp | 1 - 2 files changed, 3 deletions(-) diff --git a/Android.mk b/Android.mk index 5e2dddec4..01c72a476 100644 --- a/Android.mk +++ b/Android.mk @@ -92,11 +92,9 @@ endif LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) LOCAL_CFLAGS += -Wno-unused-parameter -Werror -LOCAL_CLANG := true LOCAL_C_INCLUDES += \ system/vold \ - system/core/adb \ LOCAL_STATIC_LIBRARIES := \ librecovery \ diff --git a/recovery.cpp b/recovery.cpp index 99126eedc..587698170 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -40,7 +40,6 @@ #include #include -#include #include #include #include -- cgit v1.2.3 From 79a0e98bb5a92dedfe13a4abeddac032413ce4a0 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 25 Apr 2017 09:31:58 -0700 Subject: minadbd: Fix a failing test due to SIGPIPE. Test: minadbd_test on bullhead. Change-Id: Iab1c51ba8d3113a83e8b464c3640be770a6cda97 --- minadbd/fuse_adb_provider_test.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/minadbd/fuse_adb_provider_test.cpp b/minadbd/fuse_adb_provider_test.cpp index 0f2e881c7..31be2a64e 100644 --- a/minadbd/fuse_adb_provider_test.cpp +++ b/minadbd/fuse_adb_provider_test.cpp @@ -14,17 +14,17 @@ * limitations under the License. */ -#include "fuse_adb_provider.h" - -#include - #include #include +#include #include #include +#include + #include "adb_io.h" +#include "fuse_adb_provider.h" TEST(fuse_adb_provider, read_block_adb) { adb_data data = {}; @@ -46,9 +46,8 @@ TEST(fuse_adb_provider, read_block_adb) { uint32_t block = 1234U; const char expected_block[] = "00001234"; - ASSERT_EQ(0, read_block_adb(reinterpret_cast(&data), block, - reinterpret_cast(block_data), - sizeof(expected_data) - 1)); + ASSERT_EQ(0, read_block_adb(static_cast(&data), block, + reinterpret_cast(block_data), sizeof(expected_data) - 1)); // Check that read_block_adb requested the right block. char block_req[sizeof(expected_block)] = {}; @@ -80,9 +79,12 @@ TEST(fuse_adb_provider, read_block_adb_fail_write) { ASSERT_EQ(0, close(sockets[1])); + // write(2) raises SIGPIPE since the reading end has been closed. Ignore the signal to avoid + // failing the test. + signal(SIGPIPE, SIG_IGN); + char buf[1]; - ASSERT_EQ(-EIO, read_block_adb(reinterpret_cast(&data), 0, - reinterpret_cast(buf), 1)); + ASSERT_EQ(-EIO, read_block_adb(static_cast(&data), 0, reinterpret_cast(buf), 1)); close(sockets[0]); } -- cgit v1.2.3 From 391bb7bb924a1b0a7990a8a7e2875ee94387e73d Mon Sep 17 00:00:00 2001 From: Jinguang Dong Date: Wed, 26 Apr 2017 10:40:45 +0800 Subject: applypatch: Add determine the return value of ApplyDiffPatch and capture the error flow. Construct ota package which is bsdiff exception scene ,then do simulation test, native code can not capture exception scenes. Test: recovery_component_test Test: Apply an bsdiff exception scene ota package. Change-Id: Icd9f6eac78739bd35c74b9fcaaf8154335d680a5 --- applypatch/imgpatch.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 7d8b7361c..3d905f7b4 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -100,7 +100,10 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* printf("source data too short\n"); return -1; } - ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx); + if (ApplyBSDiffPatch(old_data + src_start, src_len, patch, patch_offset, sink, ctx) != 0) { + printf("Failed to apply bsdiff patch.\n"); + return -1; + } } else if (type == CHUNK_RAW) { const char* raw_header = &patch->data[pos]; pos += 4; -- cgit v1.2.3 From 8ab28082b77767cab144c3eeaee358598d8e3007 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 25 Apr 2017 21:13:21 -0700 Subject: applypatch: Remove the obsolete support for "applypatch -s". The SpaceMode (applypatch -s) was used in amend script (cupcake) only, which has been removed since commit 9ce2ebf5d300eba5f6086583b0941ef68a3e4b42 (platform/build). The later (and current) edify script uses apply_patch_space(). Note that other modes (PatchMode, CheckMode) of applypatch executable are mostly used by install-recovery.sh script. Test: No active user of "applypatch -s". Test: recovery_component_test Change-Id: I1d689b7fedd3884077e88ed1d6c22f7a2198859d --- applypatch/applypatch_modes.cpp | 18 +----------------- tests/component/applypatch_test.cpp | 14 -------------- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/applypatch/applypatch_modes.cpp b/applypatch/applypatch_modes.cpp index 7b191a801..aa32d57ef 100644 --- a/applypatch/applypatch_modes.cpp +++ b/applypatch/applypatch_modes.cpp @@ -44,19 +44,6 @@ static int CheckMode(int argc, const char** argv) { return applypatch_check(argv[2], sha1); } -static int SpaceMode(int argc, const char** argv) { - if (argc != 3) { - return 2; - } - - size_t bytes; - if (!android::base::ParseUint(argv[2], &bytes) || bytes == 0) { - printf("can't parse \"%s\" as byte count\n\n", argv[2]); - return 1; - } - return CacheSizeCheck(bytes); -} - // Parse arguments (which should be of the form ":" into the // new parallel arrays *sha1s and *files. Returns true on success. static bool ParsePatchArgs(int argc, const char** argv, std::vector* sha1s, @@ -175,13 +162,12 @@ int applypatch_modes(int argc, const char** argv) { "usage: %s [-b ] " "[: ...]\n" " or %s -c [ ...]\n" - " or %s -s \n" " or %s -l\n" "\n" "Filenames may be of the form\n" " EMMC::::::...\n" "to specify reading from or writing to an EMMC partition.\n\n", - argv[0], argv[0], argv[0], argv[0]); + argv[0], argv[0], argv[0]); return 2; } @@ -191,8 +177,6 @@ int applypatch_modes(int argc, const char** argv) { result = ShowLicenses(); } else if (strncmp(argv[1], "-c", 3) == 0) { result = CheckMode(argc, argv); - } else if (strncmp(argv[1], "-s", 3) == 0) { - result = SpaceMode(argc, argv); } else { result = PatchMode(argc, argv); } diff --git a/tests/component/applypatch_test.cpp b/tests/component/applypatch_test.cpp index 6fdf78e47..016fed9b1 100644 --- a/tests/component/applypatch_test.cpp +++ b/tests/component/applypatch_test.cpp @@ -377,20 +377,6 @@ TEST(ApplyPatchModesTest, CheckModeInvalidArgs) { ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-c" })); } -TEST(ApplyPatchModesTest, SpaceModeInvalidArgs) { - // Insufficient args. - ASSERT_EQ(2, applypatch_modes(2, (const char* []){ "applypatch", "-s" })); - - // Invalid bytes arg. - ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "x" })); - - // 0 is invalid. - ASSERT_EQ(1, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0" })); - - // 0x10 is fine. - ASSERT_EQ(0, applypatch_modes(3, (const char* []){ "applypatch", "-s", "0x10" })); -} - TEST(ApplyPatchModesTest, ShowLicenses) { ASSERT_EQ(0, applypatch_modes(2, (const char* []){ "applypatch", "-l" })); } -- cgit v1.2.3 From 83b0780dddcaddd661ac48f277f51f7cb6ac13f6 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 26 Apr 2017 14:30:56 -0700 Subject: Separate libupdate_verifier module and add testcases. Enable -Wall and expose verify_image() for testing purpose. Test: mmma bootable/recovery Test: recovery_component_test Change-Id: I1ee1db2a775bafdc1112e25a1bc7194d8d6aee4f --- tests/Android.mk | 6 ++ tests/component/update_verifier_test.cpp | 83 ++++++++++++++++++++++ update_verifier/Android.mk | 42 ++++++++--- .../include/update_verifier/update_verifier.h | 24 +++++++ update_verifier/update_verifier.cpp | 17 ++--- update_verifier/update_verifier_main.cpp | 23 ++++++ 6 files changed, 179 insertions(+), 16 deletions(-) create mode 100644 tests/component/update_verifier_test.cpp create mode 100644 update_verifier/include/update_verifier/update_verifier.h create mode 100644 update_verifier/update_verifier_main.cpp diff --git a/tests/Android.mk b/tests/Android.mk index e52663031..4e125ccce 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -89,6 +89,10 @@ ifeq ($(AB_OTA_UPDATER),true) LOCAL_CFLAGS += -DAB_OTA_UPDATER=1 endif +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) +LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1 +endif + LOCAL_MODULE := recovery_component_test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_C_INCLUDES := bootable/recovery @@ -101,6 +105,7 @@ LOCAL_SRC_FILES := \ component/sideload_test.cpp \ component/uncrypt_test.cpp \ component/updater_test.cpp \ + component/update_verifier_test.cpp \ component/verifier_test.cpp LOCAL_FORCE_STATIC_EXECUTABLE := true @@ -128,6 +133,7 @@ LOCAL_STATIC_LIBRARIES := \ libverifier \ libotautil \ libmounts \ + libupdate_verifier \ libdivsufsort \ libdivsufsort64 \ libfs_mgr \ diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp new file mode 100644 index 000000000..73b4478aa --- /dev/null +++ b/tests/component/update_verifier_test.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2017 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 + +class UpdateVerifierTest : public ::testing::Test { + protected: + void SetUp() override { +#ifdef PRODUCT_SUPPORTS_VERITY + verity_supported = true; +#else + verity_supported = false; +#endif + } + + bool verity_supported; +}; + +TEST_F(UpdateVerifierTest, verify_image_no_care_map) { + // Non-existing care_map is allowed. + ASSERT_TRUE(verify_image("/doesntexist")); +} + +TEST_F(UpdateVerifierTest, verify_image_smoke) { + // This test relies on dm-verity support. + if (!verity_supported) { + GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; + return; + } + + // The care map file can have only two or four lines. + TemporaryFile temp_file; + std::string content = "system\n2,0,1"; + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); + ASSERT_TRUE(verify_image(temp_file.path)); + + // Leading and trailing newlines should be accepted. + ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", temp_file.path)); + ASSERT_TRUE(verify_image(temp_file.path)); +} + +TEST_F(UpdateVerifierTest, verify_image_wrong_lines) { + // The care map file can have only two or four lines. + TemporaryFile temp_file; + ASSERT_FALSE(verify_image(temp_file.path)); + + ASSERT_TRUE(android::base::WriteStringToFile("line1", temp_file.path)); + ASSERT_FALSE(verify_image(temp_file.path)); + + ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", temp_file.path)); + ASSERT_FALSE(verify_image(temp_file.path)); +} + +TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) { + // This test relies on dm-verity support. + if (!verity_supported) { + GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; + return; + } + + TemporaryFile temp_file; + std::string content = "system\n2,1,0"; + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); + ASSERT_FALSE(verify_image(temp_file.path)); +} diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk index 1acd5eca0..37d9bfed3 100644 --- a/update_verifier/Android.mk +++ b/update_verifier/Android.mk @@ -14,12 +14,43 @@ LOCAL_PATH := $(call my-dir) +# libupdate_verifier (static library) +# =============================== include $(CLEAR_VARS) -LOCAL_CLANG := true -LOCAL_SRC_FILES := update_verifier.cpp +LOCAL_SRC_FILES := \ + update_verifier.cpp + +LOCAL_MODULE := libupdate_verifier +LOCAL_SHARED_LIBRARIES := \ + libbase \ + libcutils \ + android.hardware.boot@1.0 + +LOCAL_CFLAGS := -Wall -Werror + +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(LOCAL_PATH)/include + +LOCAL_C_INCLUDES := \ + $(LOCAL_PATH)/include + +ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) +LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1 +endif + +include $(BUILD_STATIC_LIBRARY) + +# update_verifier (executable) +# =============================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + update_verifier_main.cpp LOCAL_MODULE := update_verifier +LOCAL_STATIC_LIBRARIES := \ + libupdate_verifier LOCAL_SHARED_LIBRARIES := \ libbase \ libcutils \ @@ -29,13 +60,8 @@ LOCAL_SHARED_LIBRARIES := \ libhidlbase \ android.hardware.boot@1.0 -LOCAL_CFLAGS := -Werror -LOCAL_C_INCLUDES += $(LOCAL_PATH)/.. +LOCAL_CFLAGS := -Wall -Werror LOCAL_INIT_RC := update_verifier.rc -ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) - LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1 -endif - include $(BUILD_EXECUTABLE) diff --git a/update_verifier/include/update_verifier/update_verifier.h b/update_verifier/include/update_verifier/update_verifier.h new file mode 100644 index 000000000..16b394e98 --- /dev/null +++ b/update_verifier/include/update_verifier/update_verifier.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 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 + +int update_verifier(int argc, char** argv); + +// Exposed for testing purpose. +bool verify_image(const std::string& care_map_name); diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index 350020f13..1950cbd83 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -35,6 +35,8 @@ * verifier reaches the end after the verification. */ +#include "update_verifier/update_verifier.h" + #include #include #include @@ -59,12 +61,6 @@ using android::hardware::boot::V1_0::IBootControl; using android::hardware::boot::V1_0::BoolResult; using android::hardware::boot::V1_0::CommandResult; -constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt"; -constexpr auto DM_PATH_PREFIX = "/sys/block/"; -constexpr auto DM_PATH_SUFFIX = "/dm/name"; -constexpr auto DEV_PATH = "/dev/block/"; -constexpr int BLOCKSIZE = 4096; - // Find directories in format of "/sys/block/dm-X". static int dm_name_filter(const dirent* de) { if (android::base::StartsWith(de->d_name, "dm-")) { @@ -82,6 +78,7 @@ static bool read_blocks(const std::string& partition, const std::string& range_s // (or "vendor"), then dm-X is a dm-wrapped system/vendor partition. // Afterwards, update_verifier will read every block on the care_map_file of // "/dev/block/dm-X" to ensure the partition's integrity. + static constexpr auto DM_PATH_PREFIX = "/sys/block/"; dirent** namelist; int n = scandir(DM_PATH_PREFIX, &namelist, dm_name_filter, alphasort); if (n == -1) { @@ -93,6 +90,8 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return false; } + static constexpr auto DM_PATH_SUFFIX = "/dm/name"; + static constexpr auto DEV_PATH = "/dev/block/"; std::string dm_block_device; while (n--) { std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX; @@ -143,6 +142,7 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return false; } + static constexpr int BLOCKSIZE = 4096; if (lseek64(fd.get(), static_cast(range_start) * BLOCKSIZE, SEEK_SET) == -1) { PLOG(ERROR) << "lseek to " << range_start << " failed"; return false; @@ -161,7 +161,7 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return true; } -static bool verify_image(const std::string& care_map_name) { +bool verify_image(const std::string& care_map_name) { android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY))); // If the device is flashed before the current boot, it may not have care_map.txt // in /data/ota_package. To allow the device to continue booting in this situation, @@ -205,7 +205,7 @@ static int reboot_device() { while (true) pause(); } -int main(int argc, char** argv) { +int update_verifier(int argc, char** argv) { for (int i = 1; i < argc; i++) { LOG(INFO) << "Started with arg " << i << ": " << argv[i]; } @@ -238,6 +238,7 @@ int main(int argc, char** argv) { return reboot_device(); } + static constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt"; if (!verify_image(CARE_MAP_FILE)) { LOG(ERROR) << "Failed to verify all blocks in care map file."; return reboot_device(); diff --git a/update_verifier/update_verifier_main.cpp b/update_verifier/update_verifier_main.cpp new file mode 100644 index 000000000..46e8bbb59 --- /dev/null +++ b/update_verifier/update_verifier_main.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 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. + */ + +// See the comments in update_verifier.cpp. + +#include "update_verifier/update_verifier.h" + +int main(int argc, char** argv) { + return update_verifier(argc, argv); +} -- cgit v1.2.3 From 20126e1e28c69133e73b3304716aa22b64c2744d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 26 Apr 2017 12:30:46 -0700 Subject: libfusesideload: Clean up the CFLAGS for the module. Remove "-DADB_HOST=0" since it doesn't include adb related stuff. Test: mmma bootable/recovery Test: sideload on marlin Change-Id: Ia83a8edd31eedd803435d687513d561c873ffabe --- Android.mk | 5 ++--- fuse_sideload.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Android.mk b/Android.mk index 01c72a476..5ce5cd7dc 100644 --- a/Android.mk +++ b/Android.mk @@ -22,11 +22,10 @@ RECOVERY_FSTAB_VERSION := 2 # =============================== include $(CLEAR_VARS) LOCAL_SRC_FILES := fuse_sideload.cpp -LOCAL_CLANG := true -LOCAL_CFLAGS := -O2 -g -DADB_HOST=0 -Wall -Wno-unused-parameter -Werror +LOCAL_CFLAGS := -Wall -Werror LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE LOCAL_MODULE := libfusesideload -LOCAL_STATIC_LIBRARIES := libcutils libc libcrypto +LOCAL_STATIC_LIBRARIES := libcrypto include $(BUILD_STATIC_LIBRARY) # libmounts (static library) diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp index 1725e8823..279a976ad 100644 --- a/fuse_sideload.cpp +++ b/fuse_sideload.cpp @@ -226,11 +226,13 @@ static int handle_open(void* /* data */, struct fuse_data* fd, const struct fuse return NO_STATUS; } -static int handle_flush(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { +static int handle_flush(void* /* data */, struct fuse_data* /* fd */, + const struct fuse_in_header* /* hdr */) { return 0; } -static int handle_release(void* data, struct fuse_data* fd, const struct fuse_in_header* hdr) { +static int handle_release(void* /* data */, struct fuse_data* /* fd */, + const struct fuse_in_header* /* hdr */) { return 0; } -- cgit v1.2.3 From 8fa8f0b16c0dc03c2d841405dfb02a707c08d5b0 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 27 Apr 2017 11:47:35 -0700 Subject: Fix potential OOM in update_verifier Limit the size of each read to 1024 * BLOCKSIZE. (Same as the I/O limit of each transfer command for block based OTA). Bug: 37729708 Test: U_V sets slot successfully on sailfish, and it takes about ~20s (no noticeable time increase) Change-Id: I7a6cdc744fe4c0760e09e0afed75b89c16d8eac3 --- update_verifier/update_verifier.cpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index 1950cbd83..fdbcfde56 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -44,6 +44,7 @@ #include #include +#include #include #include @@ -142,17 +143,21 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return false; } - static constexpr int BLOCKSIZE = 4096; + static constexpr size_t BLOCKSIZE = 4096; if (lseek64(fd.get(), static_cast(range_start) * BLOCKSIZE, SEEK_SET) == -1) { PLOG(ERROR) << "lseek to " << range_start << " failed"; return false; } - size_t size = (range_end - range_start) * BLOCKSIZE; - std::vector buf(size); - if (!android::base::ReadFully(fd.get(), buf.data(), size)) { - PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end; - return false; + size_t remain = (range_end - range_start) * BLOCKSIZE; + while (remain > 0) { + size_t to_read = std::min(remain, 1024 * BLOCKSIZE); + std::vector buf(to_read); + if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) { + PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end; + return false; + } + remain -= to_read; } blk_count += (range_end - range_start); } -- cgit v1.2.3 From 4697d8b11b259a8dbed5998381265f7010eb6318 Mon Sep 17 00:00:00 2001 From: Alistair Strachan Date: Fri, 28 Apr 2017 15:05:33 -0700 Subject: minui: Fix breakage in graphics_adf. When graphics_adf was refactored in 557fa1f, a class member was introduced that was not initialized to zero. This meant that the first time Flip() was called, current_surface would have a junk value and cause a bad pointer rereference, crashing recovery. Make sure current_surface is initialized to 0 for the first Flip(). Test: Ran recovery on a device using the ADF backend. Change-Id: I9b8fac0a4d48cac990e5e9808a071c232de1ebfb --- minui/graphics_adf.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/minui/graphics_adf.cpp b/minui/graphics_adf.cpp index 1b15a04fb..a59df00c6 100644 --- a/minui/graphics_adf.cpp +++ b/minui/graphics_adf.cpp @@ -28,7 +28,8 @@ #include "minui/minui.h" -MinuiBackendAdf::MinuiBackendAdf() : intf_fd(-1), dev(), n_surfaces(0), surfaces() {} +MinuiBackendAdf::MinuiBackendAdf() + : intf_fd(-1), dev(), current_surface(0), n_surfaces(0), surfaces() {} int MinuiBackendAdf::SurfaceInit(const drm_mode_modeinfo* mode, GRSurfaceAdf* surf) { *surf = {}; -- cgit v1.2.3 From 0150d013eb096a85e541294ee9df320ef4227df0 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 1 May 2017 11:31:28 -0700 Subject: adb_install: Stop passing RecoveryUI as a parameter. It's already a global declared in common.h which is included by adb_install.cpp. Remove '#include "minadbd/fuse_adb_provider.h"' that's not needed by adb_install.cpp (minadbd takes care of that). Test: mmma bootable/recovery Change-Id: I6d08b7abc706b4b05de2ef46a57ced2204ad297e --- adb_install.cpp | 189 ++++++++++++++++++++++++++++---------------------------- adb_install.h | 4 +- recovery.cpp | 4 +- 3 files changed, 96 insertions(+), 101 deletions(-) diff --git a/adb_install.cpp b/adb_install.cpp index 79b8df91b..e7d7758f9 100644 --- a/adb_install.cpp +++ b/adb_install.cpp @@ -14,124 +14,121 @@ * limitations under the License. */ -#include -#include +#include "adb_install.h" + #include +#include +#include #include #include +#include #include #include -#include -#include -#include +#include + +#include -#include "ui.h" -#include "install.h" #include "common.h" -#include "adb_install.h" -#include "minadbd/fuse_adb_provider.h" #include "fuse_sideload.h" +#include "install.h" +#include "ui.h" -#include - -static void set_usb_driver(RecoveryUI* ui, bool enabled) { - int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY); - if (fd < 0) { - ui->Print("failed to open driver control: %s\n", strerror(errno)); - return; - } - if (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) { - ui->Print("failed to set driver control: %s\n", strerror(errno)); - } - if (close(fd) < 0) { - ui->Print("failed to close driver control: %s\n", strerror(errno)); - } +static void set_usb_driver(bool enabled) { + int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY); + if (fd < 0) { + ui->Print("failed to open driver control: %s\n", strerror(errno)); + return; + } + if (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) { + ui->Print("failed to set driver control: %s\n", strerror(errno)); + } + if (close(fd) < 0) { + ui->Print("failed to close driver control: %s\n", strerror(errno)); + } } -static void stop_adbd(RecoveryUI* ui) { - ui->Print("Stopping adbd...\n"); - android::base::SetProperty("ctl.stop", "adbd"); - set_usb_driver(ui, false); +static void stop_adbd() { + ui->Print("Stopping adbd...\n"); + android::base::SetProperty("ctl.stop", "adbd"); + set_usb_driver(false); } -static void maybe_restart_adbd(RecoveryUI* ui) { - if (is_ro_debuggable()) { - ui->Print("Restarting adbd...\n"); - set_usb_driver(ui, true); - android::base::SetProperty("ctl.start", "adbd"); - } +static void maybe_restart_adbd() { + if (is_ro_debuggable()) { + ui->Print("Restarting adbd...\n"); + set_usb_driver(true); + android::base::SetProperty("ctl.start", "adbd"); + } } -// How long (in seconds) we wait for the host to start sending us a -// package, before timing out. -#define ADB_INSTALL_TIMEOUT 300 - -int apply_from_adb(RecoveryUI* ui, bool* wipe_cache, const char* install_file) { - modified_flash = true; - - stop_adbd(ui); - set_usb_driver(ui, true); - - ui->Print("\n\nNow send the package you want to apply\n" - "to the device with \"adb sideload \"...\n"); - - pid_t child; - if ((child = fork()) == 0) { - execl("/sbin/recovery", "recovery", "--adbd", NULL); - _exit(EXIT_FAILURE); +int apply_from_adb(bool* wipe_cache, const char* install_file) { + modified_flash = true; + + stop_adbd(); + set_usb_driver(true); + + ui->Print( + "\n\nNow send the package you want to apply\n" + "to the device with \"adb sideload \"...\n"); + + pid_t child; + if ((child = fork()) == 0) { + execl("/sbin/recovery", "recovery", "--adbd", nullptr); + _exit(EXIT_FAILURE); + } + + // How long (in seconds) we wait for the host to start sending us a package, before timing out. + static constexpr int ADB_INSTALL_TIMEOUT = 300; + + // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host connects and starts serving a + // package. Poll for its appearance. (Note that inotify doesn't work with FUSE.) + int result = INSTALL_ERROR; + int status; + bool waited = false; + for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { + if (waitpid(child, &status, WNOHANG) != 0) { + result = INSTALL_ERROR; + waited = true; + break; } - // FUSE_SIDELOAD_HOST_PATHNAME will start to exist once the host - // connects and starts serving a package. Poll for its - // appearance. (Note that inotify doesn't work with FUSE.) - int result = INSTALL_ERROR; - int status; - bool waited = false; struct stat st; - for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { - if (waitpid(child, &status, WNOHANG) != 0) { - result = INSTALL_ERROR; - waited = true; - break; - } - - if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { - if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT-1) { - sleep(1); - continue; - } else { - ui->Print("\nTimed out waiting for package.\n\n"); - result = INSTALL_ERROR; - kill(child, SIGKILL); - break; - } - } - result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false, 0); + if (stat(FUSE_SIDELOAD_HOST_PATHNAME, &st) != 0) { + if (errno == ENOENT && i < ADB_INSTALL_TIMEOUT - 1) { + sleep(1); + continue; + } else { + ui->Print("\nTimed out waiting for package.\n\n"); + result = INSTALL_ERROR; + kill(child, SIGKILL); break; + } } + result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, install_file, false, 0); + break; + } - if (!waited) { - // Calling stat() on this magic filename signals the minadbd - // subprocess to shut down. - stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); - - // TODO(dougz): there should be a way to cancel waiting for a - // package (by pushing some button combo on the device). For now - // you just have to 'adb sideload' a file that's not a valid - // package, like "/dev/null". - waitpid(child, &status, 0); - } - - if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - if (WEXITSTATUS(status) == 3) { - ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n"); - } else if (!WIFSIGNALED(status)) { - ui->Print("\n(adbd status %d)\n", WEXITSTATUS(status)); - } + if (!waited) { + // Calling stat() on this magic filename signals the minadbd subprocess to shut down. + struct stat st; + stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); + + // TODO: there should be a way to cancel waiting for a package (by pushing some button combo on + // the device). For now you just have to 'adb sideload' a file that's not a valid package, like + // "/dev/null". + waitpid(child, &status, 0); + } + + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + if (WEXITSTATUS(status) == 3) { + ui->Print("\nYou need adb 1.0.32 or newer to sideload\nto this device.\n\n"); + } else if (!WIFSIGNALED(status)) { + ui->Print("\n(adbd status %d)\n", WEXITSTATUS(status)); } + } - set_usb_driver(ui, false); - maybe_restart_adbd(ui); + set_usb_driver(false); + maybe_restart_adbd(); - return result; + return result; } diff --git a/adb_install.h b/adb_install.h index efad436fa..e654c893d 100644 --- a/adb_install.h +++ b/adb_install.h @@ -17,8 +17,6 @@ #ifndef _ADB_INSTALL_H #define _ADB_INSTALL_H -class RecoveryUI; - -int apply_from_adb(RecoveryUI* h, bool* wipe_cache, const char* install_file); +int apply_from_adb(bool* wipe_cache, const char* install_file); #endif diff --git a/recovery.cpp b/recovery.cpp index 3041d6cac..944c24086 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1155,7 +1155,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { { bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD); if (adb) { - status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE); + status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE); } else { status = apply_from_sdcard(device, &should_wipe_cache); } @@ -1584,7 +1584,7 @@ int main(int argc, char **argv) { if (!sideload_auto_reboot) { ui->ShowText(true); } - status = apply_from_adb(ui, &should_wipe_cache, TEMPORARY_INSTALL_FILE); + status = apply_from_adb(&should_wipe_cache, TEMPORARY_INSTALL_FILE); if (status == INSTALL_SUCCESS && should_wipe_cache) { if (!wipe_cache(false, device)) { status = INSTALL_ERROR; -- cgit v1.2.3 From 29ee69bf27cc7ad7ef7e604684110b3f562ed42d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 1 May 2017 12:23:17 -0700 Subject: recovery: Change install_package() to take std::string. Also change the parameter type for log_buffer from reference to pointer, so the styles for parameters look consistent. Test: mmma bootable/recovery Test: sideload a package with the new recovery image Change-Id: I8f25580ccf22977624648b3e2181cca44dd67c1b --- install.cpp | 141 +++++++++++++++++++++++++++++++----------------------------- install.h | 11 +++-- 2 files changed, 77 insertions(+), 75 deletions(-) diff --git a/install.cpp b/install.cpp index e945d13ab..d86f46caa 100644 --- a/install.cpp +++ b/install.cpp @@ -100,7 +100,7 @@ bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) { } // Read the build.version.incremental of src/tgt from the metadata and log it to last_install. -static void read_source_target_build(ZipArchiveHandle zip, std::vector& log_buffer) { +static void read_source_target_build(ZipArchiveHandle zip, std::vector* log_buffer) { std::string metadata; if (!read_metadata_from_package(zip, &metadata)) { return; @@ -114,12 +114,12 @@ static void read_source_target_build(ZipArchiveHandle zip, std::vectorpush_back(android::base::StringPrintf("source_build: %d", source_build)); } } else if (android::base::StartsWith(str, "post-build-incremental")) { int target_build = parse_build_number(str); if (target_build != -1) { - log_buffer.push_back(android::base::StringPrintf("target_build: %d", target_build)); + log_buffer->push_back(android::base::StringPrintf("target_build: %d", target_build)); } } } @@ -308,8 +308,8 @@ static void log_max_temperature(int* max_temperature) { } // If the package contains an update binary, extract it and run it. -static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_cache, - std::vector& log_buffer, int retry_count, +static int try_update_binary(const std::string& path, ZipArchiveHandle zip, bool* wipe_cache, + std::vector* log_buffer, int retry_count, int* max_temperature) { read_source_target_build(zip, log_buffer); @@ -452,7 +452,7 @@ static int try_update_binary(const char* path, ZipArchiveHandle zip, bool* wipe_ } else if (command == "log") { if (!args.empty()) { // Save the logging request from updater and write to last_install later. - log_buffer.push_back(args); + log_buffer->push_back(args); } else { LOG(ERROR) << "invalid \"log\" parameters: " << line; } @@ -547,78 +547,81 @@ bool verify_package_compatibility(ZipArchiveHandle package_zip) { return false; } -static int -really_install_package(const char *path, bool* wipe_cache, bool needs_mount, - std::vector& log_buffer, int retry_count, int* max_temperature) -{ - ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); - ui->Print("Finding update package...\n"); - // Give verification half the progress bar... - ui->SetProgressType(RecoveryUI::DETERMINATE); - ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); - LOG(INFO) << "Update location: " << path; - - // Map the update package into memory. - ui->Print("Opening update package...\n"); - - if (path && needs_mount) { - if (path[0] == '@') { - ensure_path_mounted(path+1); - } else { - ensure_path_mounted(path); - } - } - - MemMapping map; - if (sysMapFile(path, &map) != 0) { - LOG(ERROR) << "failed to map file"; - return INSTALL_CORRUPT; - } - - // Verify package. - if (!verify_package(map.addr, map.length)) { - log_buffer.push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure)); - sysReleaseMap(&map); - return INSTALL_CORRUPT; +static int really_install_package(const std::string& path, bool* wipe_cache, bool needs_mount, + std::vector* log_buffer, int retry_count, + int* max_temperature) { + ui->SetBackground(RecoveryUI::INSTALLING_UPDATE); + ui->Print("Finding update package...\n"); + // Give verification half the progress bar... + ui->SetProgressType(RecoveryUI::DETERMINATE); + ui->ShowProgress(VERIFICATION_PROGRESS_FRACTION, VERIFICATION_PROGRESS_TIME); + LOG(INFO) << "Update location: " << path; + + // Map the update package into memory. + ui->Print("Opening update package...\n"); + + if (needs_mount) { + if (path[0] == '@') { + ensure_path_mounted(path.substr(1).c_str()); + } else { + ensure_path_mounted(path.c_str()); } + } - // Try to open the package. - ZipArchiveHandle zip; - int err = OpenArchiveFromMemory(map.addr, map.length, path, &zip); - if (err != 0) { - LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err); - log_buffer.push_back(android::base::StringPrintf("error: %d", kZipOpenFailure)); + MemMapping map; + if (sysMapFile(path.c_str(), &map) != 0) { + LOG(ERROR) << "failed to map file"; + return INSTALL_CORRUPT; + } - sysReleaseMap(&map); - CloseArchive(zip); - return INSTALL_CORRUPT; - } + // Verify package. + if (!verify_package(map.addr, map.length)) { + log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure)); + sysReleaseMap(&map); + return INSTALL_CORRUPT; + } - // Additionally verify the compatibility of the package. - if (!verify_package_compatibility(zip)) { - log_buffer.push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure)); - sysReleaseMap(&map); - CloseArchive(zip); - return INSTALL_CORRUPT; - } + // Try to open the package. + ZipArchiveHandle zip; + int err = OpenArchiveFromMemory(map.addr, map.length, path.c_str(), &zip); + if (err != 0) { + LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err); + log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure)); - // Verify and install the contents of the package. - ui->Print("Installing update...\n"); - if (retry_count > 0) { - ui->Print("Retry attempt: %d\n", retry_count); - } - ui->SetEnableReboot(false); - int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature); - ui->SetEnableReboot(true); - ui->Print("\n"); + sysReleaseMap(&map); + CloseArchive(zip); + return INSTALL_CORRUPT; + } + // Additionally verify the compatibility of the package. + if (!verify_package_compatibility(zip)) { + log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure)); sysReleaseMap(&map); CloseArchive(zip); - return result; + return INSTALL_CORRUPT; + } + + // Verify and install the contents of the package. + ui->Print("Installing update...\n"); + if (retry_count > 0) { + ui->Print("Retry attempt: %d\n", retry_count); + } + ui->SetEnableReboot(false); + int result = try_update_binary(path, zip, wipe_cache, log_buffer, retry_count, max_temperature); + ui->SetEnableReboot(true); + ui->Print("\n"); + + sysReleaseMap(&map); + CloseArchive(zip); + return result; } -int install_package(const char* path, bool* wipe_cache, const char* install_file, bool needs_mount, - int retry_count) { +int install_package(const std::string& path, bool* wipe_cache, const std::string& install_file, + bool needs_mount, int retry_count) { + CHECK(!path.empty()); + CHECK(!install_file.empty()); + CHECK(wipe_cache != nullptr); + modified_flash = true; auto start = std::chrono::system_clock::now(); @@ -631,7 +634,7 @@ int install_package(const char* path, bool* wipe_cache, const char* install_file LOG(ERROR) << "failed to set up expected mounts for install; aborting"; result = INSTALL_ERROR; } else { - result = really_install_package(path, wipe_cache, needs_mount, log_buffer, retry_count, + result = really_install_package(path, wipe_cache, needs_mount, &log_buffer, retry_count, &max_temperature); } diff --git a/install.h b/install.h index 68f0a8d47..f3fda3051 100644 --- a/install.h +++ b/install.h @@ -23,10 +23,9 @@ enum { INSTALL_SUCCESS, INSTALL_ERROR, INSTALL_CORRUPT, INSTALL_NONE, INSTALL_SKIPPED, INSTALL_RETRY }; -// Install the package specified by root_path. If INSTALL_SUCCESS is -// returned and *wipe_cache is true on exit, caller should wipe the -// cache partition. -int install_package(const char* root_path, bool* wipe_cache, const char* install_file, +// Installs the given update package. If INSTALL_SUCCESS is returned and *wipe_cache is true on +// exit, caller should wipe the cache partition. +int install_package(const std::string& package, bool* wipe_cache, const std::string& install_file, bool needs_mount, int retry_count); // Verify the package by ota keys. Return true if the package is verified successfully, @@ -35,9 +34,9 @@ bool verify_package(const unsigned char* package_data, size_t package_size); // Read meta data file of the package, write its content in the string pointed by meta_data. // Return true if succeed, otherwise return false. -bool read_metadata_from_package(ZipArchiveHandle zip, std::string* meta_data); +bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata); -// Verifes the compatibility info in a Treble-compatible package. Returns true directly if the +// Verifies the compatibility info in a Treble-compatible package. Returns true directly if the // entry doesn't exist. bool verify_package_compatibility(ZipArchiveHandle package_zip); -- cgit v1.2.3 From b656a154ea497c1a179079f082b0a0701453bec5 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 18 Apr 2017 23:54:29 -0700 Subject: Move sysMapFile and sysReleaseMap into MemMapping class. Test: recovery_component_test Test: recovery_unit_test Test: Apply an OTA on angler. Change-Id: I7170f03e4ce1fe06184ca1d7bcce0a695f33ac4d --- install.cpp | 6 +- otautil/SysUtil.cpp | 126 ++++++++++++++++++-------------------- otautil/SysUtil.h | 50 ++++++++------- tests/component/updater_test.cpp | 4 +- tests/component/verifier_test.cpp | 2 +- tests/unit/sysutil_test.cpp | 60 ++++++++---------- tests/unit/zip_test.cpp | 5 +- updater/updater.cpp | 3 +- 8 files changed, 114 insertions(+), 142 deletions(-) diff --git a/install.cpp b/install.cpp index d86f46caa..689f4a0c6 100644 --- a/install.cpp +++ b/install.cpp @@ -569,7 +569,7 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo } MemMapping map; - if (sysMapFile(path.c_str(), &map) != 0) { + if (!map.MapFile(path)) { LOG(ERROR) << "failed to map file"; return INSTALL_CORRUPT; } @@ -577,7 +577,6 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo // Verify package. if (!verify_package(map.addr, map.length)) { log_buffer->push_back(android::base::StringPrintf("error: %d", kZipVerificationFailure)); - sysReleaseMap(&map); return INSTALL_CORRUPT; } @@ -588,7 +587,6 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo LOG(ERROR) << "Can't open " << path << " : " << ErrorCodeString(err); log_buffer->push_back(android::base::StringPrintf("error: %d", kZipOpenFailure)); - sysReleaseMap(&map); CloseArchive(zip); return INSTALL_CORRUPT; } @@ -596,7 +594,6 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo // Additionally verify the compatibility of the package. if (!verify_package_compatibility(zip)) { log_buffer->push_back(android::base::StringPrintf("error: %d", kPackageCompatibilityFailure)); - sysReleaseMap(&map); CloseArchive(zip); return INSTALL_CORRUPT; } @@ -611,7 +608,6 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo ui->SetEnableReboot(true); ui->Print("\n"); - sysReleaseMap(&map); CloseArchive(zip); return result; } diff --git a/otautil/SysUtil.cpp b/otautil/SysUtil.cpp index a2133b953..dfa215073 100644 --- a/otautil/SysUtil.cpp +++ b/otautil/SysUtil.cpp @@ -16,14 +16,12 @@ #include "SysUtil.h" -#include #include -#include +#include // SIZE_MAX #include #include #include -#include #include #include @@ -32,9 +30,7 @@ #include #include -static bool sysMapFD(int fd, MemMapping* pMap) { - CHECK(pMap != nullptr); - +bool MemMapping::MapFD(int fd) { struct stat sb; if (fstat(fd, &sb) == -1) { PLOG(ERROR) << "fstat(" << fd << ") failed"; @@ -47,50 +43,49 @@ static bool sysMapFD(int fd, MemMapping* pMap) { return false; } - pMap->addr = static_cast(memPtr); - pMap->length = sb.st_size; - pMap->ranges.push_back({ memPtr, static_cast(sb.st_size) }); + addr = static_cast(memPtr); + length = sb.st_size; + ranges_.clear(); + ranges_.emplace_back(MappedRange{ memPtr, static_cast(sb.st_size) }); return true; } // A "block map" which looks like this (from uncrypt/uncrypt.cpp): // -// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device -// 49652 4096 # file size in bytes, block size -// 3 # count of block ranges -// 1000 1008 # block range 0 -// 2100 2102 # ... block range 1 -// 30 33 # ... block range 2 +// /dev/block/platform/msm_sdcc.1/by-name/userdata # block device +// 49652 4096 # file size in bytes, block size +// 3 # count of block ranges +// 1000 1008 # block range 0 +// 2100 2102 # ... block range 1 +// 30 33 # ... block range 2 // -// Each block range represents a half-open interval; the line "30 33" -// reprents the blocks [30, 31, 32]. -static int sysMapBlockFile(const char* filename, MemMapping* pMap) { - CHECK(pMap != nullptr); - +// Each block range represents a half-open interval; the line "30 33" reprents the blocks +// [30, 31, 32]. +bool MemMapping::MapBlockFile(const std::string& filename) { std::string content; if (!android::base::ReadFileToString(filename, &content)) { PLOG(ERROR) << "Failed to read " << filename; - return -1; + return false; } std::vector lines = android::base::Split(android::base::Trim(content), "\n"); if (lines.size() < 4) { LOG(ERROR) << "Block map file is too short: " << lines.size(); - return -1; + return false; } size_t size; - unsigned int blksize; - if (sscanf(lines[1].c_str(), "%zu %u", &size, &blksize) != 2) { + size_t blksize; + if (sscanf(lines[1].c_str(), "%zu %zu", &size, &blksize) != 2) { LOG(ERROR) << "Failed to parse file size and block size: " << lines[1]; - return -1; + return false; } size_t range_count; if (sscanf(lines[2].c_str(), "%zu", &range_count) != 1) { LOG(ERROR) << "Failed to parse block map header: " << lines[2]; - return -1; + return false; } size_t blocks; @@ -101,14 +96,14 @@ static int sysMapBlockFile(const char* filename, MemMapping* pMap) { lines.size() != 3 + range_count) { LOG(ERROR) << "Invalid data in block map file: size " << size << ", blksize " << blksize << ", range_count " << range_count << ", lines " << lines.size(); - return -1; + return false; } // Reserve enough contiguous address space for the whole file. void* reserve = mmap64(nullptr, blocks * blksize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); if (reserve == MAP_FAILED) { PLOG(ERROR) << "failed to reserve address space"; - return -1; + return false; } const std::string& block_dev = lines[0]; @@ -116,10 +111,10 @@ static int sysMapBlockFile(const char* filename, MemMapping* pMap) { if (fd == -1) { PLOG(ERROR) << "failed to open block device " << block_dev; munmap(reserve, blocks * blksize); - return -1; + return false; } - pMap->ranges.resize(range_count); + ranges_.clear(); unsigned char* next = static_cast(reserve); size_t remaining_size = blocks * blksize; @@ -129,84 +124,79 @@ static int sysMapBlockFile(const char* filename, MemMapping* pMap) { size_t start, end; if (sscanf(line.c_str(), "%zu %zu\n", &start, &end) != 2) { - LOG(ERROR) << "failed to parse range " << i << " in block map: " << line; + LOG(ERROR) << "failed to parse range " << i << ": " << line; success = false; break; } - size_t length = (end - start) * blksize; - if (end <= start || (end - start) > SIZE_MAX / blksize || length > remaining_size) { - LOG(ERROR) << "unexpected range in block map: " << start << " " << end; + size_t range_size = (end - start) * blksize; + if (end <= start || (end - start) > SIZE_MAX / blksize || range_size > remaining_size) { + LOG(ERROR) << "Invalid range: " << start << " " << end; success = false; break; } - void* addr = mmap64(next, length, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, - static_cast(start) * blksize); - if (addr == MAP_FAILED) { - PLOG(ERROR) << "failed to map block " << i; + void* range_start = mmap64(next, range_size, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, + static_cast(start) * blksize); + if (range_start == MAP_FAILED) { + PLOG(ERROR) << "failed to map range " << i << ": " << line; success = false; break; } - pMap->ranges[i].addr = addr; - pMap->ranges[i].length = length; + ranges_.emplace_back(MappedRange{ range_start, range_size }); - next += length; - remaining_size -= length; + next += range_size; + remaining_size -= range_size; } if (success && remaining_size != 0) { - LOG(ERROR) << "ranges in block map are invalid: remaining_size = " << remaining_size; + LOG(ERROR) << "Invalid ranges: remaining_size " << remaining_size; success = false; } if (!success) { munmap(reserve, blocks * blksize); - return -1; + return false; } - pMap->addr = static_cast(reserve); - pMap->length = size; + addr = static_cast(reserve); + length = size; LOG(INFO) << "mmapped " << range_count << " ranges"; - return 0; + return true; } -int sysMapFile(const char* fn, MemMapping* pMap) { - if (fn == nullptr || pMap == nullptr) { - LOG(ERROR) << "Invalid argument(s)"; - return -1; +bool MemMapping::MapFile(const std::string& fn) { + if (fn.empty()) { + LOG(ERROR) << "Empty filename"; + return false; } - *pMap = {}; - if (fn[0] == '@') { - if (sysMapBlockFile(fn + 1, pMap) != 0) { + // Block map file "@/cache/recovery/block.map". + if (!MapBlockFile(fn.substr(1))) { LOG(ERROR) << "Map of '" << fn << "' failed"; - return -1; + return false; } } else { // This is a regular file. - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn, O_RDONLY))); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(fn.c_str(), O_RDONLY))); if (fd == -1) { PLOG(ERROR) << "Unable to open '" << fn << "'"; - return -1; + return false; } - if (!sysMapFD(fd, pMap)) { + if (!MapFD(fd)) { LOG(ERROR) << "Map of '" << fn << "' failed"; - return -1; + return false; } } - return 0; + return true; } -/* - * Release a memory mapping. - */ -void sysReleaseMap(MemMapping* pMap) { - std::for_each(pMap->ranges.cbegin(), pMap->ranges.cend(), [](const MappedRange& range) { +MemMapping::~MemMapping() { + for (const auto& range : ranges_) { if (munmap(range.addr, range.length) == -1) { - PLOG(ERROR) << "munmap(" << range.addr << ", " << range.length << ") failed"; + PLOG(ERROR) << "Failed to munmap(" << range.addr << ", " << range.length << ")"; } - }); - pMap->ranges.clear(); + }; + ranges_.clear(); } diff --git a/otautil/SysUtil.h b/otautil/SysUtil.h index 6a79bf31f..52f6d20a7 100644 --- a/otautil/SysUtil.h +++ b/otautil/SysUtil.h @@ -19,37 +19,35 @@ #include +#include #include -struct MappedRange { - void* addr; - size_t length; -}; - /* * Use this to keep track of mapped segments. */ -struct MemMapping { - unsigned char* addr; /* start of data */ - size_t length; /* length of data */ - - std::vector ranges; +class MemMapping { + public: + ~MemMapping(); + // Map a file into a private, read-only memory segment. If 'filename' begins with an '@' + // character, it is a map of blocks to be mapped, otherwise it is treated as an ordinary file. + bool MapFile(const std::string& filename); + size_t ranges() const { + return ranges_.size(); + }; + + unsigned char* addr; // start of data + size_t length; // length of data + + private: + struct MappedRange { + void* addr; + size_t length; + }; + + bool MapBlockFile(const std::string& filename); + bool MapFD(int fd); + + std::vector ranges_; }; -/* - * Map a file into a private, read-only memory segment. If 'fn' - * begins with an '@' character, it is a map of blocks to be mapped, - * otherwise it is treated as an ordinary file. - * - * On success, "pMap" is filled in, and zero is returned. - */ -int sysMapFile(const char* fn, MemMapping* pMap); - -/* - * Release the pages associated with a shared memory segment. - * - * This does not free "pMap"; it just releases the memory. - */ -void sysReleaseMap(MemMapping* pMap); - #endif // _OTAUTIL_SYSUTIL diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index dc4b5d724..35e87fd56 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -570,7 +570,7 @@ TEST_F(UpdaterTest, block_image_update) { ASSERT_EQ(0, fclose(zip_file_ptr)); MemMapping map; - ASSERT_EQ(0, sysMapFile(zip_file.path, &map)); + ASSERT_TRUE(map.MapFile(zip_file.path)); ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); @@ -646,7 +646,7 @@ TEST_F(UpdaterTest, new_data_short_write) { ASSERT_EQ(0, fclose(zip_file_ptr)); MemMapping map; - ASSERT_EQ(0, sysMapFile(zip_file.path, &map)); + ASSERT_TRUE(map.MapFile(zip_file.path)); ZipArchiveHandle handle; ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); diff --git a/tests/component/verifier_test.cpp b/tests/component/verifier_test.cpp index 2cfb6d301..5338f05c6 100644 --- a/tests/component/verifier_test.cpp +++ b/tests/component/verifier_test.cpp @@ -38,7 +38,7 @@ class VerifierTest : public testing::TestWithParam> { void SetUp() override { std::vector args = GetParam(); std::string package = from_testdata_base(args[0]); - if (sysMapFile(package.c_str(), &memmap) != 0) { + if (!memmap.MapFile(package)) { FAIL() << "Failed to mmap " << package << ": " << strerror(errno) << "\n"; } diff --git a/tests/unit/sysutil_test.cpp b/tests/unit/sysutil_test.cpp index f4699664b..434ee25bf 100644 --- a/tests/unit/sysutil_test.cpp +++ b/tests/unit/sysutil_test.cpp @@ -27,27 +27,23 @@ TEST(SysUtilTest, InvalidArgs) { MemMapping mapping; // Invalid argument. - ASSERT_EQ(-1, sysMapFile(nullptr, &mapping)); - ASSERT_EQ(-1, sysMapFile("/somefile", nullptr)); + ASSERT_FALSE(mapping.MapFile("")); } -TEST(SysUtilTest, sysMapFileRegularFile) { +TEST(SysUtilTest, MapFileRegularFile) { TemporaryFile temp_file1; std::string content = "abc"; ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file1.path)); - // sysMapFile() should map the file to one range. + // MemMapping::MapFile() should map the file to one range. MemMapping mapping; - ASSERT_EQ(0, sysMapFile(temp_file1.path, &mapping)); + ASSERT_TRUE(mapping.MapFile(temp_file1.path)); ASSERT_NE(nullptr, mapping.addr); ASSERT_EQ(content.size(), mapping.length); - ASSERT_EQ(1U, mapping.ranges.size()); - - sysReleaseMap(&mapping); - ASSERT_EQ(0U, mapping.ranges.size()); + ASSERT_EQ(1U, mapping.ranges()); } -TEST(SysUtilTest, sysMapFileBlockMap) { +TEST(SysUtilTest, MapFileBlockMap) { // Create a file that has 10 blocks. TemporaryFile package; std::string content; @@ -63,78 +59,72 @@ TEST(SysUtilTest, sysMapFileBlockMap) { std::string block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n"; ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); - ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_TRUE(mapping.MapFile(filename)); ASSERT_EQ(file_size, mapping.length); - ASSERT_EQ(1U, mapping.ranges.size()); + ASSERT_EQ(1U, mapping.ranges()); // It's okay to not have the trailing '\n'. block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10"; ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); - ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_TRUE(mapping.MapFile(filename)); ASSERT_EQ(file_size, mapping.length); - ASSERT_EQ(1U, mapping.ranges.size()); + ASSERT_EQ(1U, mapping.ranges()); // Or having multiple trailing '\n's. block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n\n\n"; ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); - ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_TRUE(mapping.MapFile(filename)); ASSERT_EQ(file_size, mapping.length); - ASSERT_EQ(1U, mapping.ranges.size()); + ASSERT_EQ(1U, mapping.ranges()); // Multiple ranges. block_map_content = std::string(package.path) + "\n40960 4096\n3\n0 3\n3 5\n5 10\n"; ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path)); - ASSERT_EQ(0, sysMapFile(filename.c_str(), &mapping)); + ASSERT_TRUE(mapping.MapFile(filename)); ASSERT_EQ(file_size, mapping.length); - ASSERT_EQ(3U, mapping.ranges.size()); - - sysReleaseMap(&mapping); - ASSERT_EQ(0U, mapping.ranges.size()); + ASSERT_EQ(3U, mapping.ranges()); } -TEST(SysUtilTest, sysMapFileBlockMapInvalidBlockMap) { +TEST(SysUtilTest, MapFileBlockMapInvalidBlockMap) { MemMapping mapping; TemporaryFile temp_file; std::string filename = std::string("@") + temp_file.path; // Block map file is too short. ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + ASSERT_FALSE(mapping.MapFile(filename)); ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + ASSERT_FALSE(mapping.MapFile(filename)); // Block map file has unexpected number of lines. ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n1\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + ASSERT_FALSE(mapping.MapFile(filename)); ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n2\n0 1\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + ASSERT_FALSE(mapping.MapFile(filename)); // Invalid size/blksize/range_count. ASSERT_TRUE(android::base::WriteStringToFile("/somefile\nabc 4096\n1\n0 1\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + ASSERT_FALSE(mapping.MapFile(filename)); ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n\n0 1\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + ASSERT_FALSE(mapping.MapFile(filename)); // size/blksize/range_count don't match. ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n0 4096\n1\n0 1\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + ASSERT_FALSE(mapping.MapFile(filename)); ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 0\n1\n0 1\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + ASSERT_FALSE(mapping.MapFile(filename)); ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n0 1\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); + ASSERT_FALSE(mapping.MapFile(filename)); // Invalid block dev path. ASSERT_TRUE(android::base::WriteStringToFile("/doesntexist\n4096 4096\n1\n0 1\n", temp_file.path)); - ASSERT_EQ(-1, sysMapFile(filename.c_str(), &mapping)); - - sysReleaseMap(&mapping); - ASSERT_EQ(0U, mapping.ranges.size()); + ASSERT_FALSE(mapping.MapFile(filename)); } diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp index 4a1a49b97..df4e38cae 100644 --- a/tests/unit/zip_test.cpp +++ b/tests/unit/zip_test.cpp @@ -66,9 +66,9 @@ TEST(ZipTest, ExtractPackageRecursive) { } TEST(ZipTest, OpenFromMemory) { - MemMapping map; std::string zip_path = from_testdata_base("ziptest_dummy-update.zip"); - ASSERT_EQ(0, sysMapFile(zip_path.c_str(), &map)); + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_path)); // Map an update package into memory and open the archive from there. ZipArchiveHandle handle; @@ -85,6 +85,5 @@ TEST(ZipTest, OpenFromMemory) { ASSERT_EQ(0, ExtractEntryToFile(handle, &binary_entry, tmp_binary.fd)); CloseArchive(handle); - sysReleaseMap(&map); } diff --git a/updater/updater.cpp b/updater/updater.cpp index c09e267a5..749c86a1f 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -89,7 +89,7 @@ int main(int argc, char** argv) { const char* package_filename = argv[3]; MemMapping map; - if (sysMapFile(package_filename, &map) != 0) { + if (!map.MapFile(package_filename)) { LOG(ERROR) << "failed to map package " << argv[3]; return 3; } @@ -213,7 +213,6 @@ int main(int argc, char** argv) { if (updater_info.package_zip) { CloseArchive(updater_info.package_zip); } - sysReleaseMap(&map); return 0; } -- cgit v1.2.3 From ed13819a0d197d6851dca623707344ef2cad850b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 1 May 2017 22:30:39 -0700 Subject: fuse_sideload: Change the minimal block size to 4096. run_fuse_sideload() is passing the block size as the max_read option, so it will only handle a request that involves at most two blocks at a time. However, the minimal allowed value was set to 1024 prior to this CL, which is inconsistent with the kernel code (fs/fuse/inode.c) that sets it to the greater of 4096 and the passed-in max_read option. This would fail the calls with a block size / max_read less than 4096 due to the wrongly computed block indices. Note that we didn't observe real issue in practice, because we have been using 64 KiB block sizes for both of adb and sdcard sideload calls. The issue only shows up in my local CL (to come later) that uses 1024 block size in run_fuse_sideload() tests. Test: recovery_component_test Test: adb sideload with the new recovery image on angler Change-Id: Id9f0cfea13d0d193dcb7cd41a1553a23739545f2 --- Android.mk | 4 +- fuse_sideload.cpp | 278 +++++++++++++++++++------------------- tests/Android.mk | 1 + tests/component/sideload_test.cpp | 19 ++- 4 files changed, 161 insertions(+), 141 deletions(-) diff --git a/Android.mk b/Android.mk index 5ce5cd7dc..fbb0c665d 100644 --- a/Android.mk +++ b/Android.mk @@ -25,7 +25,9 @@ LOCAL_SRC_FILES := fuse_sideload.cpp LOCAL_CFLAGS := -Wall -Werror LOCAL_CFLAGS += -D_XOPEN_SOURCE -D_GNU_SOURCE LOCAL_MODULE := libfusesideload -LOCAL_STATIC_LIBRARIES := libcrypto +LOCAL_STATIC_LIBRARIES := \ + libcrypto \ + libbase include $(BUILD_STATIC_LIBRARY) # libmounts (static library) diff --git a/fuse_sideload.cpp b/fuse_sideload.cpp index 279a976ad..219374fdb 100644 --- a/fuse_sideload.cpp +++ b/fuse_sideload.cpp @@ -61,6 +61,9 @@ #include #include +#include + +#include #include #include "fuse_sideload.h" @@ -364,164 +367,163 @@ static int handle_read(void* data, struct fuse_data* fd, const struct fuse_in_he return NO_STATUS; } -int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, - uint64_t file_size, uint32_t block_size) -{ - int result; - - // If something's already mounted on our mountpoint, try to remove - // it. (Mostly in case of a previous abnormal exit.) - umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE); - - if (block_size < 1024) { - fprintf(stderr, "block size (%u) is too small\n", block_size); - return -1; - } - if (block_size > (1<<22)) { // 4 MiB - fprintf(stderr, "block size (%u) is too large\n", block_size); - return -1; - } - - struct fuse_data fd; - memset(&fd, 0, sizeof(fd)); - fd.vtab = vtab; - fd.cookie = cookie; - fd.file_size = file_size; - fd.block_size = block_size; - fd.file_blocks = (file_size == 0) ? 0 : (((file_size-1) / block_size) + 1); - - if (fd.file_blocks > (1<<18)) { - fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks); - result = -1; - goto done; - } - - fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH); - if (fd.hashes == NULL) { - fprintf(stderr, "failed to allocate %d bites for hashes\n", - fd.file_blocks * SHA256_DIGEST_LENGTH); - result = -1; - goto done; - } - - fd.uid = getuid(); - fd.gid = getgid(); - - fd.curr_block = -1; - fd.block_data = (uint8_t*)malloc(block_size); - if (fd.block_data == NULL) { - fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size); - result = -1; - goto done; +int run_fuse_sideload(struct provider_vtab* vtab, void* cookie, uint64_t file_size, + uint32_t block_size) { + // If something's already mounted on our mountpoint, try to remove it. (Mostly in case of a + // previous abnormal exit.) + umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_FORCE); + + // fs/fuse/inode.c in kernel code uses the greater of 4096 and the passed-in max_read. + if (block_size < 4096) { + fprintf(stderr, "block size (%u) is too small\n", block_size); + return -1; + } + if (block_size > (1 << 22)) { // 4 MiB + fprintf(stderr, "block size (%u) is too large\n", block_size); + return -1; + } + + struct fuse_data fd = {}; + fd.vtab = vtab; + fd.cookie = cookie; + fd.file_size = file_size; + fd.block_size = block_size; + fd.file_blocks = (file_size == 0) ? 0 : (((file_size - 1) / block_size) + 1); + + int result; + if (fd.file_blocks > (1 << 18)) { + fprintf(stderr, "file has too many blocks (%u)\n", fd.file_blocks); + result = -1; + goto done; + } + + fd.hashes = (uint8_t*)calloc(fd.file_blocks, SHA256_DIGEST_LENGTH); + if (fd.hashes == NULL) { + fprintf(stderr, "failed to allocate %d bites for hashes\n", + fd.file_blocks * SHA256_DIGEST_LENGTH); + result = -1; + goto done; + } + + fd.uid = getuid(); + fd.gid = getgid(); + + fd.curr_block = -1; + fd.block_data = (uint8_t*)malloc(block_size); + if (fd.block_data == NULL) { + fprintf(stderr, "failed to allocate %d bites for block_data\n", block_size); + result = -1; + goto done; + } + fd.extra_block = (uint8_t*)malloc(block_size); + if (fd.extra_block == NULL) { + fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size); + result = -1; + goto done; + } + + fd.ffd = open("/dev/fuse", O_RDWR); + if (fd.ffd < 0) { + perror("open /dev/fuse"); + result = -1; + goto done; + } + + { + std::string opts = android::base::StringPrintf( + "fd=%d,user_id=%d,group_id=%d,max_read=%u,allow_other,rootmode=040000", fd.ffd, fd.uid, + fd.gid, block_size); + + result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, "fuse", + MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts.c_str()); + if (result < 0) { + perror("mount"); + goto done; } - fd.extra_block = (uint8_t*)malloc(block_size); - if (fd.extra_block == NULL) { - fprintf(stderr, "failed to allocate %d bites for extra_block\n", block_size); + } + + uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX * 8]; + for (;;) { + ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer))); + if (len == -1) { + perror("read request"); + if (errno == ENODEV) { result = -1; - goto done; + break; + } + continue; } - fd.ffd = open("/dev/fuse", O_RDWR); - if (fd.ffd < 0) { - perror("open /dev/fuse"); - result = -1; - goto done; + if (static_cast(len) < sizeof(struct fuse_in_header)) { + fprintf(stderr, "request too short: len=%zd\n", len); + continue; } - char opts[256]; - snprintf(opts, sizeof(opts), - ("fd=%d,user_id=%d,group_id=%d,max_read=%u," - "allow_other,rootmode=040000"), - fd.ffd, fd.uid, fd.gid, block_size); - - result = mount("/dev/fuse", FUSE_SIDELOAD_HOST_MOUNTPOINT, - "fuse", MS_NOSUID | MS_NODEV | MS_RDONLY | MS_NOEXEC, opts); - if (result < 0) { - perror("mount"); - goto done; - } - uint8_t request_buffer[sizeof(struct fuse_in_header) + PATH_MAX*8]; - for (;;) { - ssize_t len = TEMP_FAILURE_RETRY(read(fd.ffd, request_buffer, sizeof(request_buffer))); - if (len == -1) { - perror("read request"); - if (errno == ENODEV) { - result = -1; - break; - } - continue; - } - - if ((size_t)len < sizeof(struct fuse_in_header)) { - fprintf(stderr, "request too short: len=%zu\n", (size_t)len); - continue; - } + struct fuse_in_header* hdr = reinterpret_cast(request_buffer); + void* data = request_buffer + sizeof(struct fuse_in_header); - struct fuse_in_header* hdr = (struct fuse_in_header*) request_buffer; - void* data = request_buffer + sizeof(struct fuse_in_header); + result = -ENOSYS; - result = -ENOSYS; + switch (hdr->opcode) { + case FUSE_INIT: + result = handle_init(data, &fd, hdr); + break; - switch (hdr->opcode) { - case FUSE_INIT: - result = handle_init(data, &fd, hdr); - break; + case FUSE_LOOKUP: + result = handle_lookup(data, &fd, hdr); + break; - case FUSE_LOOKUP: - result = handle_lookup(data, &fd, hdr); - break; + case FUSE_GETATTR: + result = handle_getattr(data, &fd, hdr); + break; - case FUSE_GETATTR: - result = handle_getattr(data, &fd, hdr); - break; + case FUSE_OPEN: + result = handle_open(data, &fd, hdr); + break; - case FUSE_OPEN: - result = handle_open(data, &fd, hdr); - break; + case FUSE_READ: + result = handle_read(data, &fd, hdr); + break; - case FUSE_READ: - result = handle_read(data, &fd, hdr); - break; + case FUSE_FLUSH: + result = handle_flush(data, &fd, hdr); + break; - case FUSE_FLUSH: - result = handle_flush(data, &fd, hdr); - break; + case FUSE_RELEASE: + result = handle_release(data, &fd, hdr); + break; - case FUSE_RELEASE: - result = handle_release(data, &fd, hdr); - break; - - default: - fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode); - break; - } + default: + fprintf(stderr, "unknown fuse request opcode %d\n", hdr->opcode); + break; + } - if (result == NO_STATUS_EXIT) { - result = 0; - break; - } + if (result == NO_STATUS_EXIT) { + result = 0; + break; + } - if (result != NO_STATUS) { - struct fuse_out_header outhdr; - outhdr.len = sizeof(outhdr); - outhdr.error = result; - outhdr.unique = hdr->unique; - TEMP_FAILURE_RETRY(write(fd.ffd, &outhdr, sizeof(outhdr))); - } + if (result != NO_STATUS) { + struct fuse_out_header outhdr; + outhdr.len = sizeof(outhdr); + outhdr.error = result; + outhdr.unique = hdr->unique; + TEMP_FAILURE_RETRY(write(fd.ffd, &outhdr, sizeof(outhdr))); } + } - done: - fd.vtab->close(fd.cookie); +done: + fd.vtab->close(fd.cookie); - result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH); - if (result < 0) { - printf("fuse_sideload umount failed: %s\n", strerror(errno)); - } + result = umount2(FUSE_SIDELOAD_HOST_MOUNTPOINT, MNT_DETACH); + if (result < 0) { + printf("fuse_sideload umount failed: %s\n", strerror(errno)); + } - if (fd.ffd) close(fd.ffd); - free(fd.hashes); - free(fd.block_data); - free(fd.extra_block); + if (fd.ffd) close(fd.ffd); + free(fd.hashes); + free(fd.block_data); + free(fd.extra_block); - return result; + return result; } diff --git a/tests/Android.mk b/tests/Android.mk index 4e125ccce..02a240127 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -126,6 +126,7 @@ LOCAL_STATIC_LIBRARIES := \ libimgpatch \ libbsdiff \ libbspatch \ + libfusesideload \ libotafault \ librecovery \ libupdater \ diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp index ea93e9b84..40cfc6975 100644 --- a/tests/component/sideload_test.cpp +++ b/tests/component/sideload_test.cpp @@ -13,9 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + #include + #include -TEST(SideloadTest, fusedevice) { - ASSERT_NE(-1, access("/dev/fuse", R_OK | W_OK)); +#include "fuse_sideload.h" + +TEST(SideloadTest, fuse_device) { + ASSERT_EQ(0, access("/dev/fuse", R_OK | W_OK)); +} + +TEST(SideloadTest, run_fuse_sideload_wrong_parameters) { + provider_vtab vtab; + vtab.close = [](void*) {}; + + ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, 4095)); + ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, 4096, (1 << 22) + 1)); + + // Too many blocks. + ASSERT_EQ(-1, run_fuse_sideload(&vtab, nullptr, ((1 << 18) + 1) * 4096, 4096)); } -- cgit v1.2.3 From e0c88793d184648a8d08b5d925b21cc6c9623ffc Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 2 May 2017 16:07:18 -0700 Subject: Add a default error code when updater script aborts We didn't report error/cause codes unless there's an explict "Abort()" call inside the updater script. As a result, some cause codes set by ErrorAbort() didn't show up in last_install. To fix the issue, add a default error code when the script terminates abnormally (i.e. with non zero status). Bug: 37912405 Test: error/cause code shows up in last_install when argument parsing fails Change-Id: Ic6d3bd1855b853aeaa0760071e593a00cf6f0209 --- error_code.h | 1 + updater/updater.cpp | 16 +++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/error_code.h b/error_code.h index cde4ee6de..0e79c87ca 100644 --- a/error_code.h +++ b/error_code.h @@ -24,6 +24,7 @@ enum ErrorCode { kZipOpenFailure, kBootreasonInBlacklist, kPackageCompatibilityFailure, + kScriptExecutionFailure, }; enum CauseCode { diff --git a/updater/updater.cpp b/updater/updater.cpp index c09e267a5..37e003e66 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -193,13 +193,15 @@ int main(int argc, char** argv) { } } - if (state.error_code != kNoError) { - fprintf(cmd_pipe, "log error: %d\n", state.error_code); - // Cause code should provide additional information about the abort; - // report only when an error exists. - if (state.cause_code != kNoCause) { - fprintf(cmd_pipe, "log cause: %d\n", state.cause_code); - } + // Installation has been aborted. Set the error code to kScriptExecutionFailure unless + // a more specific code has been set in errmsg. + if (state.error_code == kNoError) { + state.error_code = kScriptExecutionFailure; + } + fprintf(cmd_pipe, "log error: %d\n", state.error_code); + // Cause code should provide additional information about the abort. + if (state.cause_code != kNoCause) { + fprintf(cmd_pipe, "log cause: %d\n", state.cause_code); } if (updater_info.package_zip) { -- cgit v1.2.3 From ec9706738f35a859f66fd0758b73381055804f63 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 3 May 2017 11:00:48 -0700 Subject: Remove EXPAND/STRINGIFY macros. They are error-prone by putting anything into a string (e.g. EXPAND(RECOVERY_API_VERSION) would become "RECOVER_API_VERSION" if we forgot to pass -DRECOVERY_API_VERSION=3). RECOVERY_API_VERSION is the only user (in bootable/recovery) that gets stringified. Assign it to a typed var and sanity check the value. Don't see other reference to the macros from device-specific recovery directories (they can still define that locally if really needed). Test: recovery_component_test Test: Sideload an OTA on angler and marlin respectively. Change-Id: I358bbdf8f0a99db5ce4c7bc2fdcafe8013501b64 --- common.h | 4 ++-- install.cpp | 2 +- recovery.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common.h b/common.h index 62fb1324b..87b84772d 100644 --- a/common.h +++ b/common.h @@ -22,8 +22,8 @@ #include -#define STRINGIFY(x) #x -#define EXPAND(x) STRINGIFY(x) +static constexpr int kRecoveryApiVersion = RECOVERY_API_VERSION; // Defined in Android.mk. +static_assert(kRecoveryApiVersion >= 3, "Invalid recovery API version."); class RecoveryUI; diff --git a/install.cpp b/install.cpp index 689f4a0c6..2cc06603b 100644 --- a/install.cpp +++ b/install.cpp @@ -287,7 +287,7 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int ret *cmd = { binary, - EXPAND(RECOVERY_API_VERSION), // defined in Android.mk + std::to_string(kRecoveryApiVersion), std::to_string(status_fd), path, }; diff --git a/recovery.cpp b/recovery.cpp index 944c24086..6dd985831 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1501,7 +1501,7 @@ int main(int argc, char **argv) { property_list(print_property, NULL); printf("\n"); - ui->Print("Supported API: %d\n", RECOVERY_API_VERSION); + ui->Print("Supported API: %d\n", kRecoveryApiVersion); int status = INSTALL_SUCCESS; -- cgit v1.2.3 From f38401a27c8e34f6ddb42760dc977dcd62a62d85 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 3 May 2017 15:31:12 -0700 Subject: Update the comment for obsolete symlink handling . Symlink is a filebased OTA feature, and the corresponding updater function has been removed in https://android-review.googlesource.com/#/c/350357/. Also the only place where we call "package_extract_dir()" is to unpack some bootloader dir in vendor's code. We plan to remove it also in a separate bug. Bug: 31917448 Test: mma Change-Id: I3986d60958e64e0d5d8fa5f5bd508c579fb7fa2c --- otautil/ZipUtil.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/otautil/ZipUtil.cpp b/otautil/ZipUtil.cpp index 714c956ed..9cc97e499 100644 --- a/otautil/ZipUtil.cpp +++ b/otautil/ZipUtil.cpp @@ -74,7 +74,6 @@ bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path, if (path.back() == '/') { continue; } - //TODO(b/31917448) handle the symlink. if (dirCreateHierarchy(path.c_str(), UNZIP_DIRMODE, timestamp, true, sehnd) != 0) { LOG(ERROR) << "failed to create dir for " << path; -- cgit v1.2.3 From 8be0f39fec7f26164fd0791ff6d15bde65fc849c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 4 May 2017 00:29:31 +0000 Subject: Revert "Remove EXPAND/STRINGIFY macros." This reverts commit ec9706738f35a859f66fd0758b73381055804f63. Reason for revert: It's not a good idea to put RECOVERY_API_VERSION in common.h, which might be included by device-specific codes (but with RECOVERY_API_VERSION undefined). Change-Id: I9feb9c64a5af3e9165164622a59b043aa28a8b8c --- common.h | 4 ++-- install.cpp | 2 +- recovery.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/common.h b/common.h index 87b84772d..62fb1324b 100644 --- a/common.h +++ b/common.h @@ -22,8 +22,8 @@ #include -static constexpr int kRecoveryApiVersion = RECOVERY_API_VERSION; // Defined in Android.mk. -static_assert(kRecoveryApiVersion >= 3, "Invalid recovery API version."); +#define STRINGIFY(x) #x +#define EXPAND(x) STRINGIFY(x) class RecoveryUI; diff --git a/install.cpp b/install.cpp index 2cc06603b..689f4a0c6 100644 --- a/install.cpp +++ b/install.cpp @@ -287,7 +287,7 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int ret *cmd = { binary, - std::to_string(kRecoveryApiVersion), + EXPAND(RECOVERY_API_VERSION), // defined in Android.mk std::to_string(status_fd), path, }; diff --git a/recovery.cpp b/recovery.cpp index 6dd985831..944c24086 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1501,7 +1501,7 @@ int main(int argc, char **argv) { property_list(print_property, NULL); printf("\n"); - ui->Print("Supported API: %d\n", kRecoveryApiVersion); + ui->Print("Supported API: %d\n", RECOVERY_API_VERSION); int status = INSTALL_SUCCESS; -- cgit v1.2.3 From adeb41a8c0da3122a2907acb4aafd7ff9bce26af Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 28 Apr 2017 16:15:13 -0700 Subject: recovery: update mkfs.f2fs args to match f2fs-tools 1.8.0 mkfs.f2fs in 1.8.0 returns error if number of sectors is 0. Skip this argument to let mkfs detect device size. 0 sector is also not necessary for 1.4.1. Test: format userdata to f2fs and boot Bug: 37758867 Change-Id: If120988dfb678596c973d183572f870eb0b72a27 --- roots.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/roots.cpp b/roots.cpp index 6e5ef9810..727736b70 100644 --- a/roots.cpp +++ b/roots.cpp @@ -232,14 +232,14 @@ int format_volume(const char* volume, const char* directory) { << ") not supported on " << v->fs_type; return -1; } - char *num_sectors; - if (asprintf(&num_sectors, "%zd", length / 512) <= 0) { + char *num_sectors = nullptr; + if (length >= 512 && asprintf(&num_sectors, "%zd", length / 512) <= 0) { LOG(ERROR) << "format_volume: failed to create " << v->fs_type << " command for " << v->blk_device; return -1; } const char *f2fs_path = "/sbin/mkfs.f2fs"; - const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, NULL}; + const char* const f2fs_argv[] = {"mkfs.f2fs", "-t", "-d1", v->blk_device, num_sectors, nullptr}; result = exec_cmd(f2fs_path, (char* const*)f2fs_argv); free(num_sectors); -- cgit v1.2.3 From b168f5f8576b32b9c1bd6c5823dc56b09d497d11 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 16 Apr 2017 15:22:13 -0700 Subject: recovery: Use libverifier instead of rebuilding the sources. Test: mmma bootable/recovery Change-Id: Ie8cec009b00c121948179518ba9cbc26a82352bf --- Android.mk | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index fbb0c665d..0c3d4fdf2 100644 --- a/Android.mk +++ b/Android.mk @@ -69,7 +69,6 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := \ adb_install.cpp \ - asn1_decoder.cpp \ device.cpp \ fuse_sdcard_provider.cpp \ recovery.cpp \ @@ -77,7 +76,6 @@ LOCAL_SRC_FILES := \ rotate_logs.cpp \ screen_ui.cpp \ ui.cpp \ - verifier.cpp \ wear_ui.cpp \ wear_touch.cpp \ @@ -99,6 +97,7 @@ LOCAL_C_INCLUDES += \ LOCAL_STATIC_LIBRARIES := \ librecovery \ + libverifier \ libbatterymonitor \ libbootloader_message \ libext4_utils \ @@ -173,7 +172,6 @@ include $(BUILD_EXECUTABLE) # =============================== include $(CLEAR_VARS) LOCAL_MODULE := libverifier -LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ asn1_decoder.cpp \ verifier.cpp -- cgit v1.2.3 From 00d5757186c279ba5e8a52a6f5209be3e7152025 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 2 May 2017 15:48:54 -0700 Subject: Add a binary path param to update_binary_command(). This allows writing native tests for non-A/B update_binary_command(). Prior to this CL, it was extracting the updater to a hard-coded location (/tmp/update_binary) that's not available under the test environment. Test: recovery_component_test on angler and marlin respectively. Test: Sideload OTA packages on angler and marlin respectively. Change-Id: I78b9cc211d90c0a16a84e94e339b65759300e2a8 --- install.cpp | 44 +++++++++++---------- private/install.h | 8 +++- tests/component/install_test.cpp | 84 +++++++++++++++++++++++++++++++++++----- 3 files changed, 103 insertions(+), 33 deletions(-) diff --git a/install.cpp b/install.cpp index 689f4a0c6..a1f2e4fbd 100644 --- a/install.cpp +++ b/install.cpp @@ -51,6 +51,7 @@ #include "error_code.h" #include "otautil/SysUtil.h" #include "otautil/ThermalUtil.h" +#include "private/install.h" #include "roots.h" #include "ui.h" #include "verifier.h" @@ -125,12 +126,6 @@ static void read_source_target_build(ZipArchiveHandle zip, std::vector* cmd); - #ifdef AB_OTA_UPDATER // Parses the metadata of the OTA package in |zip| and checks whether we are @@ -211,8 +206,9 @@ static int check_newer_ab_build(ZipArchiveHandle zip) { return 0; } -int update_binary_command(const std::string& path, ZipArchiveHandle zip, int /* retry_count */, - int status_fd, std::vector* cmd) { +int update_binary_command(const std::string& package, ZipArchiveHandle zip, + const std::string& binary_path, int /* retry_count */, int status_fd, + std::vector* cmd) { CHECK(cmd != nullptr); int ret = check_newer_ab_build(zip); if (ret != 0) { @@ -246,8 +242,8 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int /* } long payload_offset = payload_entry.offset; *cmd = { - "/sbin/update_engine_sideload", - "--payload=file://" + path, + binary_path, + "--payload=file://" + package, android::base::StringPrintf("--offset=%ld", payload_offset), "--headers=" + std::string(payload_properties.begin(), payload_properties.end()), android::base::StringPrintf("--status_fd=%d", status_fd), @@ -257,8 +253,9 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int /* #else // !AB_OTA_UPDATER -int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count, - int status_fd, std::vector* cmd) { +int update_binary_command(const std::string& package, ZipArchiveHandle zip, + const std::string& binary_path, int retry_count, int status_fd, + std::vector* cmd) { CHECK(cmd != nullptr); // On traditional updates we extract the update binary from the package. @@ -270,11 +267,10 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int ret return INSTALL_CORRUPT; } - const char* binary = "/tmp/update_binary"; - unlink(binary); - int fd = creat(binary, 0755); + unlink(binary_path.c_str()); + int fd = creat(binary_path.c_str(), 0755); if (fd == -1) { - PLOG(ERROR) << "Failed to create " << binary; + PLOG(ERROR) << "Failed to create " << binary_path; return INSTALL_ERROR; } @@ -286,10 +282,10 @@ int update_binary_command(const std::string& path, ZipArchiveHandle zip, int ret } *cmd = { - binary, + binary_path, EXPAND(RECOVERY_API_VERSION), // defined in Android.mk std::to_string(status_fd), - path, + package, }; if (retry_count > 0) { cmd->push_back("retry"); @@ -308,7 +304,7 @@ static void log_max_temperature(int* max_temperature) { } // If the package contains an update binary, extract it and run it. -static int try_update_binary(const std::string& path, ZipArchiveHandle zip, bool* wipe_cache, +static int try_update_binary(const std::string& package, ZipArchiveHandle zip, bool* wipe_cache, std::vector* log_buffer, int retry_count, int* max_temperature) { read_source_target_build(zip, log_buffer); @@ -317,7 +313,13 @@ static int try_update_binary(const std::string& path, ZipArchiveHandle zip, bool pipe(pipefd); std::vector args; - int ret = update_binary_command(path, zip, retry_count, pipefd[1], &args); +#ifdef AB_OTA_UPDATER + int ret = update_binary_command(package, zip, "/sbin/update_engine_sideload", retry_count, + pipefd[1], &args); +#else + int ret = update_binary_command(package, zip, "/tmp/update-binary", retry_count, pipefd[1], + &args); +#endif if (ret) { close(pipefd[0]); close(pipefd[1]); @@ -472,7 +474,7 @@ static int try_update_binary(const std::string& path, ZipArchiveHandle zip, bool return INSTALL_RETRY; } if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { - LOG(ERROR) << "Error in " << path << " (Status " << WEXITSTATUS(status) << ")"; + LOG(ERROR) << "Error in " << package << " (Status " << WEXITSTATUS(status) << ")"; return INSTALL_ERROR; } diff --git a/private/install.h b/private/install.h index 12d303b01..ef64bd41d 100644 --- a/private/install.h +++ b/private/install.h @@ -23,5 +23,9 @@ #include -int update_binary_command(const std::string& path, ZipArchiveHandle zip, int retry_count, - int status_fd, std::vector* cmd); +// Extract the update binary from the open zip archive |zip| located at |package| to |binary_path|. +// Store the command line that should be called into |cmd|. The |status_fd| is the file descriptor +// the child process should use to report back the progress of the update. +int update_binary_command(const std::string& package, ZipArchiveHandle zip, + const std::string& binary_path, int retry_count, int status_fd, + std::vector* cmd); diff --git a/tests/component/install_test.cpp b/tests/component/install_test.cpp index 40201d76f..968196fc0 100644 --- a/tests/component/install_test.cpp +++ b/tests/component/install_test.cpp @@ -15,6 +15,8 @@ */ #include +#include +#include #include #include @@ -225,18 +227,62 @@ TEST(InstallTest, update_binary_command_smoke) { ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + ZipString payload_name("payload.bin"); + ZipEntry payload_entry; + ASSERT_EQ(0, FindEntry(zip, payload_name, &payload_entry)); int status_fd = 10; - std::string path = "/path/to/update.zip"; + std::string package = "/path/to/update.zip"; + std::string binary_path = "/sbin/update_engine_sideload"; std::vector cmd; - ASSERT_EQ(0, update_binary_command(path, zip, 0, status_fd, &cmd)); - ASSERT_EQ("/sbin/update_engine_sideload", cmd[0]); - ASSERT_EQ("--payload=file://" + path, cmd[1]); + ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); + ASSERT_EQ(5U, cmd.size()); + ASSERT_EQ(binary_path, cmd[0]); + ASSERT_EQ("--payload=file://" + package, cmd[1]); + ASSERT_EQ("--offset=" + std::to_string(payload_entry.offset), cmd[2]); ASSERT_EQ("--headers=" + properties, cmd[3]); ASSERT_EQ("--status_fd=" + std::to_string(status_fd), cmd[4]); CloseArchive(zip); #else - // Cannot test update_binary_command() because it tries to extract update-binary to /tmp. - GTEST_LOG_(INFO) << "Test skipped on non-A/B device."; + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + static constexpr const char* UPDATE_BINARY_NAME = "META-INF/com/google/android/update-binary"; + ASSERT_EQ(0, writer.StartEntry(UPDATE_BINARY_NAME, kCompressStored)); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + int status_fd = 10; + std::string package = "/path/to/update.zip"; + TemporaryDir td; + std::string binary_path = std::string(td.path) + "/update_binary"; + std::vector cmd; + ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); + ASSERT_EQ(4U, cmd.size()); + ASSERT_EQ(binary_path, cmd[0]); + ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION + ASSERT_EQ(std::to_string(status_fd), cmd[2]); + ASSERT_EQ(package, cmd[3]); + struct stat sb; + ASSERT_EQ(0, stat(binary_path.c_str(), &sb)); + ASSERT_EQ(static_cast(0755), sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + + // With non-zero retry count. update_binary will be removed automatically. + cmd.clear(); + ASSERT_EQ(0, update_binary_command(package, zip, binary_path, 2, status_fd, &cmd)); + ASSERT_EQ(5U, cmd.size()); + ASSERT_EQ(binary_path, cmd[0]); + ASSERT_EQ("3", cmd[1]); // RECOVERY_API_VERSION + ASSERT_EQ(std::to_string(status_fd), cmd[2]); + ASSERT_EQ(package, cmd[3]); + ASSERT_EQ("retry", cmd[4]); + sb = {}; + ASSERT_EQ(0, stat(binary_path.c_str(), &sb)); + ASSERT_EQ(static_cast(0755), sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + + CloseArchive(zip); #endif // AB_OTA_UPDATER } @@ -267,12 +313,30 @@ TEST(InstallTest, update_binary_command_invalid) { ZipArchiveHandle zip; ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); int status_fd = 10; - std::string path = "/path/to/update.zip"; + std::string package = "/path/to/update.zip"; + std::string binary_path = "/sbin/update_engine_sideload"; std::vector cmd; - ASSERT_EQ(INSTALL_CORRUPT, update_binary_command(path, zip, 0, status_fd, &cmd)); + ASSERT_EQ(INSTALL_CORRUPT, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); CloseArchive(zip); #else - // Cannot test update_binary_command() because it tries to extract update-binary to /tmp. - GTEST_LOG_(INFO) << "Test skipped on non-A/B device."; + TemporaryFile temp_file; + FILE* zip_file = fdopen(temp_file.fd, "w"); + ZipWriter writer(zip_file); + // The archive must have something to be opened correctly. + ASSERT_EQ(0, writer.StartEntry("dummy_entry", 0)); + ASSERT_EQ(0, writer.FinishEntry()); + ASSERT_EQ(0, writer.Finish()); + ASSERT_EQ(0, fclose(zip_file)); + + // Missing update binary. + ZipArchiveHandle zip; + ASSERT_EQ(0, OpenArchive(temp_file.path, &zip)); + int status_fd = 10; + std::string package = "/path/to/update.zip"; + TemporaryDir td; + std::string binary_path = std::string(td.path) + "/update_binary"; + std::vector cmd; + ASSERT_EQ(INSTALL_CORRUPT, update_binary_command(package, zip, binary_path, 0, status_fd, &cmd)); + CloseArchive(zip); #endif // AB_OTA_UPDATER } -- cgit v1.2.3 From f3ae55a167892dc486379a0444bfb8a38ce1f7f2 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Tue, 9 May 2017 15:49:36 -0700 Subject: otautil: Android.mk -> Android.bp Test: flash and boot recovery on internal angler Change-Id: Id8845b4b422d0078b251333eb6d30ce14771ef10 --- Android.bp | 3 +++ Android.mk | 1 - otautil/Android.bp | 34 ++++++++++++++++++++++++++++++++++ otautil/Android.mk | 33 --------------------------------- 4 files changed, 37 insertions(+), 34 deletions(-) create mode 100644 Android.bp create mode 100644 otautil/Android.bp delete mode 100644 otautil/Android.mk diff --git a/Android.bp b/Android.bp new file mode 100644 index 000000000..f919ebc83 --- /dev/null +++ b/Android.bp @@ -0,0 +1,3 @@ +subdirs = [ + "otautil", +] diff --git a/Android.mk b/Android.mk index fbb0c665d..bab9aa316 100644 --- a/Android.mk +++ b/Android.mk @@ -192,7 +192,6 @@ include \ $(LOCAL_PATH)/minadbd/Android.mk \ $(LOCAL_PATH)/minui/Android.mk \ $(LOCAL_PATH)/otafault/Android.mk \ - $(LOCAL_PATH)/otautil/Android.mk \ $(LOCAL_PATH)/tests/Android.mk \ $(LOCAL_PATH)/tools/Android.mk \ $(LOCAL_PATH)/uncrypt/Android.mk \ diff --git a/otautil/Android.bp b/otautil/Android.bp new file mode 100644 index 000000000..0b2314374 --- /dev/null +++ b/otautil/Android.bp @@ -0,0 +1,34 @@ +// 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. + +cc_library_static { + name: "libotautil", + + srcs: [ + "SysUtil.cpp", + "DirUtil.cpp", + "ZipUtil.cpp", + "ThermalUtil.cpp", + ], + + static_libs: [ + "libselinux", + "libbase", + ], + + cflags: [ + "-Werror", + "-Wall", + ], +} diff --git a/otautil/Android.mk b/otautil/Android.mk deleted file mode 100644 index f7ca9a9ee..000000000 --- a/otautil/Android.mk +++ /dev/null @@ -1,33 +0,0 @@ -# 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. - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_SRC_FILES := \ - SysUtil.cpp \ - DirUtil.cpp \ - ZipUtil.cpp \ - ThermalUtil.cpp - -LOCAL_STATIC_LIBRARIES := \ - libselinux \ - libbase - -LOCAL_MODULE := libotautil -LOCAL_CFLAGS := \ - -Werror \ - -Wall - -include $(BUILD_STATIC_LIBRARY) -- cgit v1.2.3 From 57130c45f85d68b4420e4dccb9c972c7f8946031 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 10 May 2017 12:11:21 -0700 Subject: recovery: Skip "/" in setup_install_mounts(). We don't want to do anything for "/" when preparing for an install. Bug: 36686818 Test: adb sideload on angler/marlin respectively. Change-Id: Id854dd0a743a0e163a8f13baf2514105091ddc67 --- roots.cpp | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/roots.cpp b/roots.cpp index 727736b70..9b4270256 100644 --- a/roots.cpp +++ b/roots.cpp @@ -260,26 +260,29 @@ int format_volume(const char* volume) { } int setup_install_mounts() { - if (fstab == NULL) { - LOG(ERROR) << "can't set up install mounts: no fstab loaded"; - return -1; - } - for (int i = 0; i < fstab->num_entries; ++i) { - Volume* v = fstab->recs + i; + if (fstab == nullptr) { + LOG(ERROR) << "can't set up install mounts: no fstab loaded"; + return -1; + } + for (int i = 0; i < fstab->num_entries; ++i) { + const Volume* v = fstab->recs + i; - if (strcmp(v->mount_point, "/tmp") == 0 || - strcmp(v->mount_point, "/cache") == 0) { - if (ensure_path_mounted(v->mount_point) != 0) { - LOG(ERROR) << "failed to mount " << v->mount_point; - return -1; - } + // We don't want to do anything with "/". + if (strcmp(v->mount_point, "/") == 0) { + continue; + } - } else { - if (ensure_path_unmounted(v->mount_point) != 0) { - LOG(ERROR) << "failed to unmount " << v->mount_point; - return -1; - } - } + if (strcmp(v->mount_point, "/tmp") == 0 || strcmp(v->mount_point, "/cache") == 0) { + if (ensure_path_mounted(v->mount_point) != 0) { + LOG(ERROR) << "failed to mount " << v->mount_point; + return -1; + } + } else { + if (ensure_path_unmounted(v->mount_point) != 0) { + LOG(ERROR) << "failed to unmount " << v->mount_point; + return -1; + } } - return 0; + } + return 0; } -- cgit v1.2.3 From 0167d4c382a697a81638af9b84f93ca50a78217f Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 11 May 2017 14:44:15 -0700 Subject: Don't write to /sys/class/android_usb/android0/enable with configfs. USB configfs doesn't use /s/c/a/a/enable. Trying to set that gives confusing message on screen when sideloading. Bug: 37713851 Test: adb sideload on device using with configfs. Test: adb sideload on marlin. Change-Id: Ifa55f90c2a5fe6bf9e7cee95882de9f6de686d73 --- adb_install.cpp | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/adb_install.cpp b/adb_install.cpp index e7d7758f9..ac0130651 100644 --- a/adb_install.cpp +++ b/adb_install.cpp @@ -26,7 +26,10 @@ #include #include +#include +#include #include +#include #include "common.h" #include "fuse_sideload.h" @@ -34,16 +37,22 @@ #include "ui.h" static void set_usb_driver(bool enabled) { - int fd = open("/sys/class/android_usb/android0/enable", O_WRONLY); - if (fd < 0) { - ui->Print("failed to open driver control: %s\n", strerror(errno)); + // USB configfs doesn't use /s/c/a/a/enable. + if (android::base::GetBoolProperty("sys.usb.configfs", false)) { return; } - if (TEMP_FAILURE_RETRY(write(fd, enabled ? "1" : "0", 1)) == -1) { - ui->Print("failed to set driver control: %s\n", strerror(errno)); + + static constexpr const char* USB_DRIVER_CONTROL = "/sys/class/android_usb/android0/enable"; + android::base::unique_fd fd(open(USB_DRIVER_CONTROL, O_WRONLY)); + if (fd == -1) { + PLOG(ERROR) << "Failed to open driver control"; + return; } - if (close(fd) < 0) { - ui->Print("failed to close driver control: %s\n", strerror(errno)); + // Not using android::base::WriteStringToFile since that will open with O_CREAT and give EPERM + // when USB_DRIVER_CONTROL doesn't exist. When it gives EPERM, we don't know whether that's due + // to non-existent USB_DRIVER_CONTROL or indeed a permission issue. + if (!android::base::WriteStringToFd(enabled ? "1" : "0", fd)) { + PLOG(ERROR) << "Failed to set driver control"; } } -- cgit v1.2.3 From 397a8137a0074ade60eee5efb32cc5a46c8af73c Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 12 May 2017 11:02:27 -0700 Subject: updater: Update the mkfs.f2fs argument to match f2fs-tools 1.8.0. Commit adeb41a8c0da3122a2907acb4aafd7ff9bce26af has switched the argument for recovery. This CL handles the case for updater. Note that there's a chance the updater may run against the old recovery (and f2fs 1.4.1 binary). Not sending a 0-sector argument to f2fs 1.4.1 also works. Bug: 37758867 Test: Make an OTA package that calls format f2fs, with mkfs.f2fs 1.8.0 and 1.4.1 binaries respectively. Change-Id: I4d4bbe8c57544d1c514b7aa37fbf22a0aab14e2c --- updater/install.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/updater/install.cpp b/updater/install.cpp index 888239c83..c5f9a89bb 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -317,9 +317,11 @@ Value* FormatFn(const char* name, State* state, const std::vector(f2fs_argv)); + const char* f2fs_argv[] = { + "mkfs.f2fs", "-t", "-d1", location.c_str(), (size < 512) ? nullptr : num_sectors.c_str(), + nullptr + }; + int status = exec_cmd(f2fs_path, const_cast(f2fs_argv)); if (status != 0) { LOG(ERROR) << name << ": mkfs.f2fs failed (" << status << ") on " << location; return StringValue(""); -- cgit v1.2.3 From ce5fa5e5384508655c804519d428a402bb3df1d9 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 15 May 2017 12:32:33 -0700 Subject: Print SHA1 of the patch if bsdiff fails with data error This will help us to identify the patch corruption. Meanwhile fix a wrong size parameter passed to bspatch. (patch->data.size() into patch->data.size() - patch_offset). Also remove the only usage of "ApplyBSDiffPatchMem()" and inline its Sink function for simplicity. Bug: 37855643 Test: Prints SHA1 for corrupted patch in imgdiff_test. Change-Id: Ibf2db8c08b0ded1409bb7c91a3547a6bf99c601d --- applypatch/bspatch.cpp | 38 ++++++++++++++++++----------- applypatch/imgpatch.cpp | 14 ++++++----- applypatch/include/applypatch/applypatch.h | 2 -- tests/component/imgdiff_test.cpp | 39 ++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 22 deletions(-) diff --git a/applypatch/bspatch.cpp b/applypatch/bspatch.cpp index f75a2c680..65ee614ef 100644 --- a/applypatch/bspatch.cpp +++ b/applypatch/bspatch.cpp @@ -23,10 +23,14 @@ #include #include +#include + +#include #include #include #include "applypatch/applypatch.h" +#include "print_sha1.h" void ShowBSDiffLicense() { puts("The bsdiff library used herein is:\n" @@ -67,18 +71,24 @@ int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value if (ctx) SHA1_Update(ctx, data, len); return len; }; - return bsdiff::bspatch(old_data, old_size, - reinterpret_cast(&patch->data[patch_offset]), - patch->data.size(), sha_sink); -} -int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, - size_t patch_offset, std::vector* new_data) { - auto vector_sink = [new_data](const uint8_t* data, size_t len) { - new_data->insert(new_data->end(), data, data + len); - return len; - }; - return bsdiff::bspatch(old_data, old_size, - reinterpret_cast(&patch->data[patch_offset]), - patch->data.size(), vector_sink); -} + CHECK(patch != nullptr); + CHECK_LE(patch_offset, patch->data.size()); + + int result = bsdiff::bspatch(old_data, old_size, + reinterpret_cast(&patch->data[patch_offset]), + patch->data.size() - patch_offset, sha_sink); + if (result != 0) { + LOG(ERROR) << "bspatch failed, result: " << result; + // print SHA1 of the patch in the case of a data error. + if (result == 2) { + uint8_t digest[SHA_DIGEST_LENGTH]; + SHA1(reinterpret_cast(patch->data.data() + patch_offset), + patch->data.size() - patch_offset, digest); + std::string patch_sha1 = print_sha1(digest); + LOG(ERROR) << "Patch may be corrupted, offset: " << patch_offset << ", SHA1: " + << patch_sha1; + } + } + return result; +} \ No newline at end of file diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 3d905f7b4..702a624ae 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -200,12 +200,14 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* } // Next, apply the bsdiff patch (in memory) to the uncompressed data. - std::vector uncompressed_target_data; - // TODO(senj): Remove the only usage of ApplyBSDiffPatchMem here, - // replace it with ApplyBSDiffPatch with a custom sink function that - // wraps the given sink function to stream output to save memory. - if (ApplyBSDiffPatchMem(expanded_source.data(), expanded_len, patch, patch_offset, - &uncompressed_target_data) != 0) { + std::vector uncompressed_target_data; + // TODO: replace the custom sink function passed into ApplyBSDiffPatch so that it wraps the + // given sink function to stream output to save memory. + if (ApplyBSDiffPatch(expanded_source.data(), expanded_len, patch, patch_offset, + [&uncompressed_target_data](const uint8_t* data, size_t len) { + uncompressed_target_data.insert(uncompressed_target_data.end(), data, data + len); + return len; + }, nullptr) != 0) { return -1; } if (uncompressed_target_data.size() != target_len) { diff --git a/applypatch/include/applypatch/applypatch.h b/applypatch/include/applypatch/applypatch.h index da55432d5..581360ef1 100644 --- a/applypatch/include/applypatch/applypatch.h +++ b/applypatch/include/applypatch/applypatch.h @@ -69,8 +69,6 @@ int SaveFileContents(const char* filename, const FileContents* file); void ShowBSDiffLicense(); int ApplyBSDiffPatch(const unsigned char* old_data, size_t old_size, const Value* patch, size_t patch_offset, SinkFn sink, SHA_CTX* ctx); -int ApplyBSDiffPatchMem(const unsigned char* old_data, size_t old_size, const Value* patch, - size_t patch_offset, std::vector* new_data); // imgpatch.cpp int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* patch, SinkFn sink, diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 7d00a3d53..6f5960bbd 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -551,3 +551,42 @@ TEST(ImgdiffTest, image_mode_single_entry_long) { verify_patched_image(src, patch, tgt); } + +TEST(ImgpatchTest, image_mode_patch_corruption) { + // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd). + const std::vector src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e', + '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', + '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', + '\x00', '\x00', '\x00' }; + const std::string src(src_data.cbegin(), src_data.cend()); + TemporaryFile src_file; + ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); + + // tgt: "abcdefgxyz" + gzipped "xxyyzz". + const std::vector tgt_data = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b', + '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac', + '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\x00', '\x00', '\x00' + }; + const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); + TemporaryFile tgt_file; + ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); + + TemporaryFile patch_file; + std::vector args = { + "imgdiff", src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + // Verify. + std::string patch; + ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); + verify_patched_image(src, patch, tgt); + + // Corrupt the end of the patch and expect the ApplyImagePatch to fail. + patch.insert(patch.end() - 10, 10, '0'); + ASSERT_EQ(-1, ApplyImagePatch(reinterpret_cast(src.data()), src.size(), + reinterpret_cast(patch.data()), patch.size(), + [](const unsigned char* /*data*/, size_t len) { return len; })); +} -- cgit v1.2.3 From 72449c9f995649b1427b2116985ed9826a141d46 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 16 May 2017 18:07:31 -0700 Subject: Fix the input parameter for "set_retry_bootloader_message" We're not updating argc & argv during get_args(), so some boot arguments missed when we set the boot message for retry. Bug: 38383406 Test: boot command sets correctly during retry attempt. Change-Id: Ie8583a22fad5e0084245e3431d4018518d508dfd --- recovery.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 944c24086..dfae7f03d 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1304,20 +1304,20 @@ static bool is_battery_ok() { } } -static void set_retry_bootloader_message(int retry_count, int argc, char** argv) { - std::vector options; - for (int i = 1; i < argc; ++i) { - if (strstr(argv[i], "retry_count") == nullptr) { - options.push_back(argv[i]); - } +static void set_retry_bootloader_message(int retry_count, const std::vector& args) { + std::vector options; + for (const auto& arg : args) { + if (!android::base::StartsWith(arg, "--retry_count")) { + options.push_back(arg); } + } - // Increment the retry counter by 1. - options.push_back(android::base::StringPrintf("--retry_count=%d", retry_count+1)); - std::string err; - if (!update_bootloader_message(options, &err)) { - LOG(ERROR) << err; - } + // Increment the retry counter by 1. + options.push_back(android::base::StringPrintf("--retry_count=%d", retry_count + 1)); + std::string err; + if (!update_bootloader_message(options, &err)) { + LOG(ERROR) << err; + } } static bool bootreason_in_blacklist() { @@ -1534,7 +1534,7 @@ int main(int argc, char **argv) { // times before we abandon this OTA update. if (status == INSTALL_RETRY && retry_count < EIO_RETRY_COUNT) { copy_logs(); - set_retry_bootloader_message(retry_count, argc, argv); + set_retry_bootloader_message(retry_count, args); // Print retry count on screen. ui->Print("Retry attempt %d\n", retry_count); -- cgit v1.2.3 From 600e80638493dc8bde9c7102d5adae2272e034e9 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Fri, 12 May 2017 17:08:20 -0700 Subject: libbootloader_message: convert to Android.bp Test: build Change-Id: Ia0c2e141673e37eea29306817d2f4b2c944213b0 --- Android.bp | 1 + Android.mk | 1 - bootloader_message/Android.bp | 26 ++++++++++++++++++++++++++ bootloader_message/Android.mk | 25 ------------------------- 4 files changed, 27 insertions(+), 26 deletions(-) create mode 100644 bootloader_message/Android.bp delete mode 100644 bootloader_message/Android.mk diff --git a/Android.bp b/Android.bp index f919ebc83..49438ad9e 100644 --- a/Android.bp +++ b/Android.bp @@ -1,3 +1,4 @@ subdirs = [ + "bootloader_message", "otautil", ] diff --git a/Android.mk b/Android.mk index 1f69d5d5a..e619db031 100644 --- a/Android.mk +++ b/Android.mk @@ -185,7 +185,6 @@ include $(BUILD_STATIC_LIBRARY) include \ $(LOCAL_PATH)/applypatch/Android.mk \ $(LOCAL_PATH)/boot_control/Android.mk \ - $(LOCAL_PATH)/bootloader_message/Android.mk \ $(LOCAL_PATH)/edify/Android.mk \ $(LOCAL_PATH)/minadbd/Android.mk \ $(LOCAL_PATH)/minui/Android.mk \ diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp new file mode 100644 index 000000000..f0d76e718 --- /dev/null +++ b/bootloader_message/Android.bp @@ -0,0 +1,26 @@ +// +// Copyright (C) 2017 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. +// + +cc_library_static { + name: "libbootloader_message", + srcs: ["bootloader_message.cpp"], + cppflags: ["-Werror"], + static_libs: [ + "libbase", + "libfs_mgr", + ], + export_include_dirs: ["include"], +} diff --git a/bootloader_message/Android.mk b/bootloader_message/Android.mk deleted file mode 100644 index a8c50819b..000000000 --- a/bootloader_message/Android.mk +++ /dev/null @@ -1,25 +0,0 @@ -# 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. - -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_CLANG := true -LOCAL_SRC_FILES := bootloader_message.cpp -LOCAL_MODULE := libbootloader_message -LOCAL_STATIC_LIBRARIES := libbase libfs_mgr -LOCAL_CFLAGS := -Werror -LOCAL_C_INCLUDES := $(LOCAL_PATH)/include -LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include -include $(BUILD_STATIC_LIBRARY) -- cgit v1.2.3 From 53c38b15381ace565227e49104a6fd64c4c28dcc Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 10 May 2017 14:47:45 -0700 Subject: kill package_extract_dir It's only used by file-based OTA which has been deprecated for O. Test: mma Change-Id: I439c93155ca94554d827142c99aa6c0845cc7561 --- otautil/Android.bp | 1 - otautil/ZipUtil.cpp | 120 ---------------------------- otautil/ZipUtil.h | 57 ------------- tests/Android.mk | 1 - tests/unit/zip_test.cpp | 37 --------- tests/unit/ziputil_test.cpp | 191 -------------------------------------------- updater/install.cpp | 32 -------- 7 files changed, 439 deletions(-) delete mode 100644 otautil/ZipUtil.cpp delete mode 100644 otautil/ZipUtil.h delete mode 100644 tests/unit/ziputil_test.cpp diff --git a/otautil/Android.bp b/otautil/Android.bp index 0b2314374..a2eaa0402 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -18,7 +18,6 @@ cc_library_static { srcs: [ "SysUtil.cpp", "DirUtil.cpp", - "ZipUtil.cpp", "ThermalUtil.cpp", ], diff --git a/otautil/ZipUtil.cpp b/otautil/ZipUtil.cpp deleted file mode 100644 index 9cc97e499..000000000 --- a/otautil/ZipUtil.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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 "ZipUtil.h" - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include "DirUtil.h" - -static constexpr mode_t UNZIP_DIRMODE = 0755; -static constexpr mode_t UNZIP_FILEMODE = 0644; - -bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path, - const std::string& dest_path, const struct utimbuf* timestamp, - struct selabel_handle* sehnd) { - if (!zip_path.empty() && zip_path[0] == '/') { - LOG(ERROR) << "ExtractPackageRecursive(): zip_path must be a relative path " << zip_path; - return false; - } - if (dest_path.empty() || dest_path[0] != '/') { - LOG(ERROR) << "ExtractPackageRecursive(): dest_path must be an absolute path " << dest_path; - return false; - } - - void* cookie; - std::string target_dir(dest_path); - if (dest_path.back() != '/') { - target_dir += '/'; - } - std::string prefix_path(zip_path); - if (!zip_path.empty() && zip_path.back() != '/') { - prefix_path += '/'; - } - const ZipString zip_prefix(prefix_path.c_str()); - - int ret = StartIteration(zip, &cookie, &zip_prefix, nullptr); - if (ret != 0) { - LOG(ERROR) << "failed to start iterating zip entries."; - return false; - } - - std::unique_ptr guard(cookie, EndIteration); - ZipEntry entry; - ZipString name; - int extractCount = 0; - while (Next(cookie, &entry, &name) == 0) { - std::string entry_name(name.name, name.name + name.name_length); - CHECK_LE(prefix_path.size(), entry_name.size()); - std::string path = target_dir + entry_name.substr(prefix_path.size()); - // Skip dir. - if (path.back() == '/') { - continue; - } - - if (dirCreateHierarchy(path.c_str(), UNZIP_DIRMODE, timestamp, true, sehnd) != 0) { - LOG(ERROR) << "failed to create dir for " << path; - return false; - } - - char *secontext = NULL; - if (sehnd) { - selabel_lookup(sehnd, &secontext, path.c_str(), UNZIP_FILEMODE); - setfscreatecon(secontext); - } - android::base::unique_fd fd(open(path.c_str(), O_CREAT|O_WRONLY|O_TRUNC, UNZIP_FILEMODE)); - if (fd == -1) { - PLOG(ERROR) << "Can't create target file \"" << path << "\""; - return false; - } - if (secontext) { - freecon(secontext); - setfscreatecon(NULL); - } - - int err = ExtractEntryToFile(zip, &entry, fd); - if (err != 0) { - LOG(ERROR) << "Error extracting \"" << path << "\" : " << ErrorCodeString(err); - return false; - } - - if (fsync(fd) != 0) { - PLOG(ERROR) << "Error syncing file descriptor when extracting \"" << path << "\""; - return false; - } - - if (timestamp != nullptr && utime(path.c_str(), timestamp)) { - PLOG(ERROR) << "Error touching \"" << path << "\""; - return false; - } - - LOG(INFO) << "Extracted file \"" << path << "\""; - ++extractCount; - } - - LOG(INFO) << "Extracted " << extractCount << " file(s)"; - return true; -} diff --git a/otautil/ZipUtil.h b/otautil/ZipUtil.h deleted file mode 100644 index cda405c2a..000000000 --- a/otautil/ZipUtil.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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. - */ - -#ifndef _OTAUTIL_ZIPUTIL_H -#define _OTAUTIL_ZIPUTIL_H - -#include - -#include - -#include -#include - -/* - * Inflate all files under zip_path to the directory specified by - * dest_path, which must exist and be a writable directory. The zip_path - * is allowed to be an empty string, in which case the whole package - * will be extracted. - * - * Directory entries are not extracted. - * - * The immediate children of zip_path will become the immediate - * children of dest_path; e.g., if the archive contains the entries - * - * a/b/c/one - * a/b/c/two - * a/b/c/d/three - * - * and ExtractPackageRecursive(a, "a/b/c", "/tmp", ...) is called, the resulting - * files will be - * - * /tmp/one - * /tmp/two - * /tmp/d/three - * - * If timestamp is non-NULL, file timestamps will be set accordingly. - * - * Returns true on success, false on failure. - */ -bool ExtractPackageRecursive(ZipArchiveHandle zip, const std::string& zip_path, - const std::string& dest_path, const struct utimbuf* timestamp, - struct selabel_handle* sehnd); - -#endif // _OTAUTIL_ZIPUTIL_H diff --git a/tests/Android.mk b/tests/Android.mk index 02a240127..262f4ffdd 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -39,7 +39,6 @@ LOCAL_SRC_FILES := \ unit/rangeset_test.cpp \ unit/sysutil_test.cpp \ unit/zip_test.cpp \ - unit/ziputil_test.cpp LOCAL_C_INCLUDES := bootable/recovery LOCAL_SHARED_LIBRARIES := liblog diff --git a/tests/unit/zip_test.cpp b/tests/unit/zip_test.cpp index df4e38cae..827668521 100644 --- a/tests/unit/zip_test.cpp +++ b/tests/unit/zip_test.cpp @@ -24,47 +24,10 @@ #include #include #include -#include #include #include "common/test_constants.h" -TEST(ZipTest, ExtractPackageRecursive) { - std::string zip_path = from_testdata_base("ziptest_valid.zip"); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - - // Extract the whole package into a temp directory. - TemporaryDir td; - ASSERT_NE(nullptr, td.path); - ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr); - - // Make sure all the files are extracted correctly. - std::string path(td.path); - ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK)); - ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK)); - ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK)); - ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK)); - - // The content of the file is the same as expected. - std::string content1; - ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1)); - ASSERT_EQ(kATxtContents, content1); - - std::string content2; - ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2)); - ASSERT_EQ(kDTxtContents, content2); - - CloseArchive(handle); - - // Clean up. - ASSERT_EQ(0, unlink((path + "/a.txt").c_str())); - ASSERT_EQ(0, unlink((path + "/b.txt").c_str())); - ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str())); - ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str())); - ASSERT_EQ(0, rmdir((path + "/b").c_str())); -} - TEST(ZipTest, OpenFromMemory) { std::string zip_path = from_testdata_base("ziptest_dummy-update.zip"); MemMapping map; diff --git a/tests/unit/ziputil_test.cpp b/tests/unit/ziputil_test.cpp deleted file mode 100644 index 14e541690..000000000 --- a/tests/unit/ziputil_test.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 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 -#include - -#include "common/test_constants.h" - -TEST(ZipUtilTest, invalid_args) { - std::string zip_path = from_testdata_base("ziptest_valid.zip"); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - - // zip_path must be a relative path. - ASSERT_FALSE(ExtractPackageRecursive(handle, "/a/b", "/tmp", nullptr, nullptr)); - - // dest_path must be an absolute path. - ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "tmp", nullptr, nullptr)); - ASSERT_FALSE(ExtractPackageRecursive(handle, "a/b", "", nullptr, nullptr)); - - CloseArchive(handle); -} - -TEST(ZipUtilTest, extract_all) { - std::string zip_path = from_testdata_base("ziptest_valid.zip"); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - - // Extract the whole package into a temp directory. - TemporaryDir td; - ExtractPackageRecursive(handle, "", td.path, nullptr, nullptr); - - // Make sure all the files are extracted correctly. - std::string path(td.path); - ASSERT_EQ(0, access((path + "/a.txt").c_str(), F_OK)); - ASSERT_EQ(0, access((path + "/b.txt").c_str(), F_OK)); - ASSERT_EQ(0, access((path + "/b/c.txt").c_str(), F_OK)); - ASSERT_EQ(0, access((path + "/b/d.txt").c_str(), F_OK)); - - // The content of the file is the same as expected. - std::string content1; - ASSERT_TRUE(android::base::ReadFileToString(path + "/a.txt", &content1)); - ASSERT_EQ(kATxtContents, content1); - - std::string content2; - ASSERT_TRUE(android::base::ReadFileToString(path + "/b/d.txt", &content2)); - ASSERT_EQ(kDTxtContents, content2); - - // Clean up the temp files under td. - ASSERT_EQ(0, unlink((path + "/a.txt").c_str())); - ASSERT_EQ(0, unlink((path + "/b.txt").c_str())); - ASSERT_EQ(0, unlink((path + "/b/c.txt").c_str())); - ASSERT_EQ(0, unlink((path + "/b/d.txt").c_str())); - ASSERT_EQ(0, rmdir((path + "/b").c_str())); - - CloseArchive(handle); -} - -TEST(ZipUtilTest, extract_prefix_with_slash) { - std::string zip_path = from_testdata_base("ziptest_valid.zip"); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - - // Extract all the entries starting with "b/". - TemporaryDir td; - ExtractPackageRecursive(handle, "b/", td.path, nullptr, nullptr); - - // Make sure all the files with "b/" prefix are extracted correctly. - std::string path(td.path); - ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK)); - ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK)); - - // And the rest are not extracted. - ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK)); - ASSERT_EQ(ENOENT, errno); - ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK)); - ASSERT_EQ(ENOENT, errno); - - // The content of the file is the same as expected. - std::string content1; - ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1)); - ASSERT_EQ(kCTxtContents, content1); - - std::string content2; - ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2)); - ASSERT_EQ(kDTxtContents, content2); - - // Clean up the temp files under td. - ASSERT_EQ(0, unlink((path + "/c.txt").c_str())); - ASSERT_EQ(0, unlink((path + "/d.txt").c_str())); - - CloseArchive(handle); -} - -TEST(ZipUtilTest, extract_prefix_without_slash) { - std::string zip_path = from_testdata_base("ziptest_valid.zip"); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - - // Extract all the file entries starting with "b/". - TemporaryDir td; - ExtractPackageRecursive(handle, "b", td.path, nullptr, nullptr); - - // Make sure all the files with "b/" prefix are extracted correctly. - std::string path(td.path); - ASSERT_EQ(0, access((path + "/c.txt").c_str(), F_OK)); - ASSERT_EQ(0, access((path + "/d.txt").c_str(), F_OK)); - - // And the rest are not extracted. - ASSERT_EQ(-1, access((path + "/a.txt").c_str(), F_OK)); - ASSERT_EQ(ENOENT, errno); - ASSERT_EQ(-1, access((path + "/b.txt").c_str(), F_OK)); - ASSERT_EQ(ENOENT, errno); - - // The content of the file is the same as expected. - std::string content1; - ASSERT_TRUE(android::base::ReadFileToString(path + "/c.txt", &content1)); - ASSERT_EQ(kCTxtContents, content1); - - std::string content2; - ASSERT_TRUE(android::base::ReadFileToString(path + "/d.txt", &content2)); - ASSERT_EQ(kDTxtContents, content2); - - // Clean up the temp files under td. - ASSERT_EQ(0, unlink((path + "/c.txt").c_str())); - ASSERT_EQ(0, unlink((path + "/d.txt").c_str())); - - CloseArchive(handle); -} - -TEST(ZipUtilTest, set_timestamp) { - std::string zip_path = from_testdata_base("ziptest_valid.zip"); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - - // Set the timestamp to 8/1/2008. - constexpr struct utimbuf timestamp = { 1217592000, 1217592000 }; - - // Extract all the entries starting with "b/". - TemporaryDir td; - ExtractPackageRecursive(handle, "b", td.path, ×tamp, nullptr); - - // Make sure all the files with "b/" prefix are extracted correctly. - std::string path(td.path); - std::string file_c = path + "/c.txt"; - std::string file_d = path + "/d.txt"; - ASSERT_EQ(0, access(file_c.c_str(), F_OK)); - ASSERT_EQ(0, access(file_d.c_str(), F_OK)); - - // Verify the timestamp. - timespec time; - time.tv_sec = 1217592000; - time.tv_nsec = 0; - - struct stat sb; - ASSERT_EQ(0, stat(file_c.c_str(), &sb)) << strerror(errno); - ASSERT_EQ(time.tv_sec, static_cast(sb.st_atime)); - ASSERT_EQ(time.tv_sec, static_cast(sb.st_mtime)); - - ASSERT_EQ(0, stat(file_d.c_str(), &sb)) << strerror(errno); - ASSERT_EQ(time.tv_sec, static_cast(sb.st_atime)); - ASSERT_EQ(time.tv_sec, static_cast(sb.st_mtime)); - - // Clean up the temp files under td. - ASSERT_EQ(0, unlink(file_c.c_str())); - ASSERT_EQ(0, unlink(file_d.c_str())); - - CloseArchive(handle); -} diff --git a/updater/install.cpp b/updater/install.cpp index c5f9a89bb..ff79edce0 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -61,7 +61,6 @@ #include "mounts.h" #include "ota_io.h" #include "otautil/DirUtil.h" -#include "otautil/ZipUtil.h" #include "print_sha1.h" #include "tune2fs.h" #include "updater/updater.h" @@ -389,36 +388,6 @@ Value* SetProgressFn(const char* name, State* state, const std::vector>&argv) { - if (argv.size() != 2) { - return ErrorAbort(state, kArgsParsingFailure, "%s() expects 2 args, got %zu", name, - argv.size()); - } - - std::vector args; - if (!ReadArgs(state, argv, &args)) { - return ErrorAbort(state, kArgsParsingFailure, "%s() Failed to parse the argument(s)", name); - } - const std::string& zip_path = args[0]; - const std::string& dest_path = args[1]; - - ZipArchiveHandle za = static_cast(state->cookie)->package_zip; - - // To create a consistent system image, never use the clock for timestamps. - constexpr struct utimbuf timestamp = { 1217592000, 1217592000 }; // 8/1/2008 default - - bool success = ExtractPackageRecursive(za, zip_path, dest_path, ×tamp, sehandle); - - return StringValue(success ? "t" : ""); -} - // package_extract_file(package_file[, dest_file]) // Extracts a single package_file from the update package and writes it to dest_file, // overwriting existing files if necessary. Without the dest_file argument, returns the @@ -1037,7 +1006,6 @@ void RegisterInstallFunctions() { RegisterFunction("format", FormatFn); RegisterFunction("show_progress", ShowProgressFn); RegisterFunction("set_progress", SetProgressFn); - RegisterFunction("package_extract_dir", PackageExtractDirFn); RegisterFunction("package_extract_file", PackageExtractFileFn); RegisterFunction("getprop", GetPropFn); -- cgit v1.2.3 From 6957555e295e8ae34ba5e8cfbfb1c7bd76b0024d Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 16 May 2017 15:51:46 -0700 Subject: Retry the update if ApplyBSDiffPatch | ApplyImagePatch fails We have seen one case when bspatch failed likely due to patch corruption. Since the package has passed verification before, we want to reboot and retry the patch command again since there's no alternative for users. We won't delete the stash before reboot, and the src has passed SHA1 check. If there's an error on the patch, it will fail the package verification during retry. Bug: 37855643 Test: angler reboots and retries the update when bspatch fails. Change-Id: I2ebac9621bd1f0649bb301b9a28a0dd079ed4e1d --- error_code.h | 1 + recovery.cpp | 9 +++++---- updater/blockimg.cpp | 2 ++ updater/updater.cpp | 4 ++++ 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/error_code.h b/error_code.h index 0e79c87ca..9fe047c91 100644 --- a/error_code.h +++ b/error_code.h @@ -44,6 +44,7 @@ enum CauseCode { kTune2FsFailure, kRebootFailure, kPackageExtractFileFailure, + kPatchApplicationFailure, kVendorFailure = 200 }; diff --git a/recovery.cpp b/recovery.cpp index dfae7f03d..122b89d0b 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -112,8 +112,9 @@ static const char *TEMPORARY_LOG_FILE = "/tmp/recovery.log"; static const char *TEMPORARY_INSTALL_FILE = "/tmp/last_install"; static const char *LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; static const char *LAST_LOG_FILE = "/cache/recovery/last_log"; -// We will try to apply the update package 5 times at most in case of an I/O error. -static const int EIO_RETRY_COUNT = 4; +// We will try to apply the update package 5 times at most in case of an I/O error or +// bspatch | imgpatch error. +static const int RETRY_LIMIT = 4; static const int BATTERY_READ_TIMEOUT_IN_SEC = 10; // GmsCore enters recovery mode to install package when having enough battery // percentage. Normally, the threshold is 40% without charger and 20% with charger. @@ -1530,9 +1531,9 @@ int main(int argc, char **argv) { } if (status != INSTALL_SUCCESS) { ui->Print("Installation aborted.\n"); - // When I/O error happens, reboot and retry installation EIO_RETRY_COUNT + // When I/O error happens, reboot and retry installation RETRY_LIMIT // times before we abandon this OTA update. - if (status == INSTALL_RETRY && retry_count < EIO_RETRY_COUNT) { + if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) { copy_logs(); set_retry_bootloader_message(retry_count, args); // Print retry count on screen. diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index d5c170473..df366b0b8 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -1213,6 +1213,7 @@ static int PerformCommandDiff(CommandParameters& params) { std::placeholders::_2), nullptr, nullptr) != 0) { LOG(ERROR) << "Failed to apply image patch."; + failure_type = kPatchApplicationFailure; return -1; } } else { @@ -1221,6 +1222,7 @@ static int PerformCommandDiff(CommandParameters& params) { std::placeholders::_2), nullptr) != 0) { LOG(ERROR) << "Failed to apply bsdiff patch."; + failure_type = kPatchApplicationFailure; return -1; } } diff --git a/updater/updater.cpp b/updater/updater.cpp index 1be8b6040..f5ff6df91 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -202,6 +202,10 @@ int main(int argc, char** argv) { // Cause code should provide additional information about the abort. if (state.cause_code != kNoCause) { fprintf(cmd_pipe, "log cause: %d\n", state.cause_code); + if (state.cause_code == kPatchApplicationFailure) { + LOG(INFO) << "Patch application failed, retry update."; + fprintf(cmd_pipe, "retry_update\n"); + } } if (updater_info.package_zip) { -- cgit v1.2.3 From 8ed9738b62b075205a81489b01ec882520da183a Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 8 May 2017 13:41:28 -0400 Subject: update_verifier: Support AVB. When using AVB, PRODUCT_SUPPORTS_VERITY is not set so check for BOARD_ENABLE_AVB as well. Also AVB sets up the root filesystem as 'vroot' so map that to 'system' since this is what is expected. Managed to test at least that the code is at least compiled in: $ fastboot --set-active=_a Setting current slot to 'a'... OKAY [ 0.023s] finished. total time: 0.023s $ fastboot reboot rebooting... finished. total time: 0.050s $ adb wait-for-device $ adb logcat |grep update_verifier 03-04 05:28:56.773 630 630 I /system/bin/update_verifier: Started with arg 1: nonencrypted 03-04 05:28:56.776 630 630 I /system/bin/update_verifier: Booting slot 0: isSlotMarkedSuccessful=0 03-04 05:28:56.776 630 630 W /system/bin/update_verifier: Failed to open /data/ota_package/care_map.txt: No such file or directory 03-04 05:28:56.788 630 630 I /system/bin/update_verifier: Marked slot 0 as booted successfully. 03-04 05:28:56.788 630 630 I /system/bin/update_verifier: Leaving update_verifier. Bug: None Test: Manually tested on device using AVB bootloader. Change-Id: I13c0fe1cc5d0f397e36f5e62fcc05c8dfee5fd85 --- tests/Android.mk | 4 ++++ tests/component/update_verifier_test.cpp | 2 +- update_verifier/Android.mk | 4 ++++ update_verifier/update_verifier.cpp | 21 +++++++++++++++------ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/tests/Android.mk b/tests/Android.mk index 02a240127..4ee59b526 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -93,6 +93,10 @@ ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1 endif +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_CFLAGS += -DBOARD_AVB_ENABLE=1 +endif + LOCAL_MODULE := recovery_component_test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_C_INCLUDES := bootable/recovery diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index 73b4478aa..5fc7ef63f 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -24,7 +24,7 @@ class UpdateVerifierTest : public ::testing::Test { protected: void SetUp() override { -#ifdef PRODUCT_SUPPORTS_VERITY +#if defined(PRODUCT_SUPPORTS_VERITY) || defined(BOARD_AVB_ENABLE) verity_supported = true; #else verity_supported = false; diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk index 37d9bfed3..33c5fe9e7 100644 --- a/update_verifier/Android.mk +++ b/update_verifier/Android.mk @@ -39,6 +39,10 @@ ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1 endif +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_CFLAGS += -DBOARD_AVB_ENABLE=1 +endif + include $(BUILD_STATIC_LIBRARY) # update_verifier (executable) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index fdbcfde56..d3a5185b8 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -99,12 +99,21 @@ static bool read_blocks(const std::string& partition, const std::string& range_s std::string content; if (!android::base::ReadFileToString(path, &content)) { PLOG(WARNING) << "Failed to read " << path; - } else if (android::base::Trim(content) == partition) { - dm_block_device = DEV_PATH + std::string(namelist[n]->d_name); - while (n--) { - free(namelist[n]); + } else { + std::string dm_block_name = android::base::Trim(content); +#ifdef BOARD_AVB_ENABLE + // AVB is using 'vroot' for the root block device but we're expecting 'system'. + if (dm_block_name == "vroot") { + dm_block_name = "system"; + } +#endif + if (dm_block_name == partition) { + dm_block_device = DEV_PATH + std::string(namelist[n]->d_name); + while (n--) { + free(namelist[n]); + } + break; } - break; } free(namelist[n]); } @@ -229,7 +238,7 @@ int update_verifier(int argc, char** argv) { if (is_successful == BoolResult::FALSE) { // The current slot has not booted successfully. -#ifdef PRODUCT_SUPPORTS_VERITY +#if defined(PRODUCT_SUPPORTS_VERITY) || defined(BOARD_AVB_ENABLE) std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); if (verity_mode.empty()) { LOG(ERROR) << "Failed to get dm-verity mode."; -- cgit v1.2.3 From a897f95bd5426aff62c7bb95170985b90123a468 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 17 May 2017 22:41:55 -0700 Subject: Implement a custom deflate sink function for bspatch This new sink function works as a wrapper of the old sink. It deflates the available patch data on the fly. Therefore, we don't need to store the full uncompressed patch data in memory. Test: recovery_component_test && apply an incremental update on angler Change-Id: I2274ec50a1607089abcc9d0954a2a748f28c3122 --- applypatch/imgpatch.cpp | 144 +++++++++++++++++++++++++++++------------------- 1 file changed, 86 insertions(+), 58 deletions(-) diff --git a/applypatch/imgpatch.cpp b/applypatch/imgpatch.cpp index 702a624ae..df75f98d4 100644 --- a/applypatch/imgpatch.cpp +++ b/applypatch/imgpatch.cpp @@ -26,12 +26,14 @@ #include #include +#include #include #include +#include +#include #include #include -#include #include #include @@ -43,6 +45,86 @@ static inline int32_t Read4(const void *address) { return android::base::get_unaligned(address); } +// This function is a wrapper of ApplyBSDiffPatch(). It has a custom sink function to deflate the +// patched data and stream the deflated data to output. +static bool ApplyBSDiffPatchAndStreamOutput(const uint8_t* src_data, size_t src_len, + const Value* patch, size_t patch_offset, + const char* deflate_header, SinkFn sink, SHA_CTX* ctx) { + size_t expected_target_length = static_cast(Read8(deflate_header + 32)); + int level = Read4(deflate_header + 40); + int method = Read4(deflate_header + 44); + int window_bits = Read4(deflate_header + 48); + int mem_level = Read4(deflate_header + 52); + int strategy = Read4(deflate_header + 56); + + std::unique_ptr strm(new z_stream(), deflateEnd); + strm->zalloc = Z_NULL; + strm->zfree = Z_NULL; + strm->opaque = Z_NULL; + strm->avail_in = 0; + strm->next_in = nullptr; + int ret = deflateInit2(strm.get(), level, method, window_bits, mem_level, strategy); + if (ret != Z_OK) { + LOG(ERROR) << "Failed to init uncompressed data deflation: " << ret; + return false; + } + + // Define a custom sink wrapper that feeds to bspatch. It deflates the available patch data on + // the fly and outputs the compressed data to the given sink. + size_t actual_target_length = 0; + size_t total_written = 0; + static constexpr size_t buffer_size = 32768; + auto compression_sink = [&](const uint8_t* data, size_t len) -> size_t { + // The input patch length for an update never exceeds INT_MAX. + strm->avail_in = len; + strm->next_in = data; + do { + std::vector buffer(buffer_size); + strm->avail_out = buffer_size; + strm->next_out = buffer.data(); + if (actual_target_length + len < expected_target_length) { + ret = deflate(strm.get(), Z_NO_FLUSH); + } else { + ret = deflate(strm.get(), Z_FINISH); + } + if (ret != Z_OK && ret != Z_STREAM_END) { + LOG(ERROR) << "Failed to deflate stream: " << ret; + // zero length indicates an error in the sink function of bspatch(). + return 0; + } + + size_t have = buffer_size - strm->avail_out; + total_written += have; + if (sink(buffer.data(), have) != have) { + LOG(ERROR) << "Failed to write " << have << " compressed bytes to output."; + return 0; + } + if (ctx) SHA1_Update(ctx, buffer.data(), have); + } while ((strm->avail_in != 0 || strm->avail_out == 0) && ret != Z_STREAM_END); + + actual_target_length += len; + return len; + }; + + if (ApplyBSDiffPatch(src_data, src_len, patch, patch_offset, compression_sink, nullptr) != 0) { + return false; + } + + if (ret != Z_STREAM_END) { + LOG(ERROR) << "ret is expected to be Z_STREAM_END, but it's " << ret; + return false; + } + + if (expected_target_length != actual_target_length) { + LOG(ERROR) << "target length is expected to be " << expected_target_length << ", but it's " + << actual_target_length; + return false; + } + LOG(DEBUG) << "bspatch writes " << total_written << " bytes in total to streaming output."; + + return true; +} + int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const unsigned char* patch_data, size_t patch_size, SinkFn sink) { Value patch(VAL_BLOB, std::string(reinterpret_cast(patch_data), patch_size)); @@ -137,12 +219,6 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* size_t src_len = static_cast(Read8(deflate_header + 8)); size_t patch_offset = static_cast(Read8(deflate_header + 16)); size_t expanded_len = static_cast(Read8(deflate_header + 24)); - size_t target_len = static_cast(Read8(deflate_header + 32)); - int level = Read4(deflate_header + 40); - int method = Read4(deflate_header + 44); - int windowBits = Read4(deflate_header + 48); - int memLevel = Read4(deflate_header + 52); - int strategy = Read4(deflate_header + 56); if (src_start + src_len > old_size) { printf("source data too short\n"); @@ -199,60 +275,12 @@ int ApplyImagePatch(const unsigned char* old_data, size_t old_size, const Value* } } - // Next, apply the bsdiff patch (in memory) to the uncompressed data. - std::vector uncompressed_target_data; - // TODO: replace the custom sink function passed into ApplyBSDiffPatch so that it wraps the - // given sink function to stream output to save memory. - if (ApplyBSDiffPatch(expanded_source.data(), expanded_len, patch, patch_offset, - [&uncompressed_target_data](const uint8_t* data, size_t len) { - uncompressed_target_data.insert(uncompressed_target_data.end(), data, data + len); - return len; - }, nullptr) != 0) { - return -1; - } - if (uncompressed_target_data.size() != target_len) { - printf("expected target len to be %zu, but it's %zu\n", target_len, - uncompressed_target_data.size()); + if (!ApplyBSDiffPatchAndStreamOutput(expanded_source.data(), expanded_len, patch, + patch_offset, deflate_header, sink, ctx)) { + LOG(ERROR) << "Fail to apply streaming bspatch."; return -1; } - // Now compress the target data and append it to the output. - - // we're done with the expanded_source data buffer, so we'll - // reuse that memory to receive the output of deflate. - if (expanded_source.size() < 32768U) { - expanded_source.resize(32768U); - } - - { - std::vector& temp_data = expanded_source; - - // now the deflate stream - z_stream strm; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = uncompressed_target_data.size(); - strm.next_in = uncompressed_target_data.data(); - int ret = deflateInit2(&strm, level, method, windowBits, memLevel, strategy); - if (ret != Z_OK) { - printf("failed to init uncompressed data deflation: %d\n", ret); - return -1; - } - do { - strm.avail_out = temp_data.size(); - strm.next_out = temp_data.data(); - ret = deflate(&strm, Z_FINISH); - size_t have = temp_data.size() - strm.avail_out; - - if (sink(temp_data.data(), have) != have) { - printf("failed to write %zd compressed bytes to output\n", have); - return -1; - } - if (ctx) SHA1_Update(ctx, temp_data.data(), have); - } while (ret != Z_STREAM_END); - deflateEnd(&strm); - } } else { printf("patch chunk %d is unknown type %d\n", i, type); return -1; -- cgit v1.2.3 From 379571098d1b597a2a5f159dfc7522cc4fde6c63 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 7 Jun 2017 17:59:55 -0700 Subject: Fix a race condition for temperature_logger It's a rare deadlock that happens on fugu when the updater exits too fast due to a format error. Bug: 62379170 Test: fake-ota fails gracefully without hanging. Change-Id: Icbd6cff4f6b44ca20f4d75f8039332111f696cc5 --- install.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/install.cpp b/install.cpp index a1f2e4fbd..db4ba9373 100644 --- a/install.cpp +++ b/install.cpp @@ -27,6 +27,7 @@ #include #include +#include #include #include #include @@ -294,11 +295,12 @@ int update_binary_command(const std::string& package, ZipArchiveHandle zip, } #endif // !AB_OTA_UPDATER -static void log_max_temperature(int* max_temperature) { +static void log_max_temperature(int* max_temperature, const std::atomic& logger_finished) { CHECK(max_temperature != nullptr); std::mutex mtx; std::unique_lock lck(mtx); - while (finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) { + while (!logger_finished.load() && + finish_log_temperature.wait_for(lck, 20s) == std::cv_status::timeout) { *max_temperature = std::max(*max_temperature, GetMaxValueFromThermalZone()); } } @@ -403,7 +405,8 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b } close(pipefd[1]); - std::thread temperature_logger(log_max_temperature, max_temperature); + std::atomic logger_finished(false); + std::thread temperature_logger(log_max_temperature, max_temperature, std::ref(logger_finished)); *wipe_cache = false; bool retry_update = false; @@ -467,6 +470,7 @@ static int try_update_binary(const std::string& package, ZipArchiveHandle zip, b int status; waitpid(pid, &status, 0); + logger_finished.store(true); finish_log_temperature.notify_one(); temperature_logger.join(); -- cgit v1.2.3 From 2330dd8733ce0b207058e3003a3b1efebc022394 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Wed, 14 Jun 2017 15:30:39 -0700 Subject: Fix "No file_contexts" warning Fixed by Loading the file_contexts specified in libselinux, whereas previously recovery loaded /file_contexts which no longer exists. Bug: 62587423 Test: build and flash recovery on Angler. Warning is gone. Test: Wipe data and cache. Test: sideload OTA Change-Id: I11581c878b860ac5f412e6e8e7acde811f37870f --- recovery.cpp | 9 +++------ updater/updater.cpp | 6 +++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 122b89d0b..852f1e862 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -53,6 +53,7 @@ #include #include /* private pmsg functions */ #include /* for AID_SYSTEM */ +#include #include #include #include @@ -1481,12 +1482,8 @@ int main(int argc, char **argv) { ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); - struct selinux_opt seopts[] = { - { SELABEL_OPT_PATH, "/file_contexts" } - }; - - sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); - + sehandle = selinux_android_file_context_handle(); + selinux_android_set_sehandle(sehandle); if (!sehandle) { ui->Print("Warning: No file_contexts\n"); } diff --git a/updater/updater.cpp b/updater/updater.cpp index f5ff6df91..1d8fa8e92 100644 --- a/updater/updater.cpp +++ b/updater/updater.cpp @@ -25,6 +25,7 @@ #include #include +#include #include #include #include @@ -139,9 +140,8 @@ int main(int argc, char** argv) { return 6; } - struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, "/file_contexts" } }; - - sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1); + sehandle = selinux_android_file_context_handle(); + selinux_android_set_sehandle(sehandle); if (!sehandle) { fprintf(cmd_pipe, "ui_print Warning: No file_contexts\n"); -- cgit v1.2.3 From a44dba7f4e7296077f65fd571232e8a61aed9418 Mon Sep 17 00:00:00 2001 From: Luke Song Date: Mon, 12 Jun 2017 16:08:33 -0700 Subject: Introduce VR recovery ui A version of screen ui with specific adjustments for vr device compatibility. Bug: 37779982 Test: "adb reboot recovery" to view Change-Id: If6b0f26c1b587f8d0176060685b5efb6c67593b1 --- Android.mk | 12 ++++++++++++ screen_ui.cpp | 17 +++++++++++------ screen_ui.h | 6 ++++-- vr_device.cpp | 23 +++++++++++++++++++++++ vr_ui.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vr_ui.h | 38 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 vr_device.cpp create mode 100644 vr_ui.cpp create mode 100644 vr_ui.h diff --git a/Android.mk b/Android.mk index e619db031..5348e365e 100644 --- a/Android.mk +++ b/Android.mk @@ -76,6 +76,7 @@ LOCAL_SRC_FILES := \ rotate_logs.cpp \ screen_ui.cpp \ ui.cpp \ + vr_ui.cpp \ wear_ui.cpp \ wear_touch.cpp \ @@ -182,6 +183,17 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) +# vr headset default device +# =============================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := vr_device.cpp + +# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk +LOCAL_MODULE := librecovery_ui_vr + +include $(BUILD_STATIC_LIBRARY) + include \ $(LOCAL_PATH)/applypatch/Android.mk \ $(LOCAL_PATH)/boot_control/Android.mk \ diff --git a/screen_ui.cpp b/screen_ui.cpp index bb2772dd8..61ef5911f 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -256,6 +256,10 @@ void ScreenRecoveryUI::DrawHorizontalRule(int* y) { *y += 4; } +void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { + gr_fill(x, y, x + width, y + height); +} + void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { gr_text(gr_sys_font(), x, *y, line, bold); *y += char_height_ + 4; @@ -310,15 +314,14 @@ void ScreenRecoveryUI::draw_screen_locked() { if (i == menu_sel) { // Draw the highlight bar. SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2); + DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); // Bold white text for the selected item. SetColor(MENU_SEL_FG); - gr_text(gr_sys_font(), 4, y, menu_[i], true); + DrawTextLine(TEXT_INDENT, &y, menu_[i], true); SetColor(MENU); } else { - gr_text(gr_sys_font(), 4, y, menu_[i], false); + DrawTextLine(TEXT_INDENT, &y, menu_[i], false); } - y += char_height_ + 4; } DrawHorizontalRule(&y); } @@ -329,10 +332,11 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - char_height_; + for (int ty = gr_fb_height() - char_height_ - log_bottom_offset_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { - gr_text(gr_sys_font(), 0, ty, text_[row], false); + int temp_y = ty; + DrawTextLine(0, &temp_y, text_[row], false); --row; if (row < 0) row = text_rows_ - 1; } @@ -453,6 +457,7 @@ bool ScreenRecoveryUI::InitTextParams() { gr_font_size(gr_sys_font(), &char_width_, &char_height_); text_rows_ = gr_fb_height() / char_height_; text_cols_ = gr_fb_width() / char_width_; + log_bottom_offset_ = 0; return true; } diff --git a/screen_ui.h b/screen_ui.h index a2322c36c..bd99254f6 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -107,6 +107,7 @@ class ScreenRecoveryUI : public RecoveryUI { // Log text overlay, displayed when a magic key is pressed. char** text_; size_t text_col_, text_row_, text_top_; + int log_bottom_offset_; bool show_text; bool show_text_ever; // has show_text ever been true? @@ -165,8 +166,9 @@ class ScreenRecoveryUI : public RecoveryUI { virtual int GetProgressBaseline(); virtual int GetTextBaseline(); - void DrawHorizontalRule(int* y); - void DrawTextLine(int x, int* y, const char* line, bool bold) const; + virtual void DrawHorizontalRule(int* y); + virtual void DrawHighlightBar(int x, int y, int width, int height) const; + virtual void DrawTextLine(int x, int* y, const char* line, bool bold) const; void DrawTextLines(int x, int* y, const char* const* lines) const; }; diff --git a/vr_device.cpp b/vr_device.cpp new file mode 100644 index 000000000..61e15cbb6 --- /dev/null +++ b/vr_device.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 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 "device.h" +#include "vr_ui.h" + +Device* make_device() { + return new Device(new VrRecoveryUI); +} + diff --git a/vr_ui.cpp b/vr_ui.cpp new file mode 100644 index 000000000..b2c65e3af --- /dev/null +++ b/vr_ui.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 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 "vr_ui.h" + +#include + +VrRecoveryUI::VrRecoveryUI() : + x_offset(400), + y_offset(400), + stereo_offset(100) { +} + +bool VrRecoveryUI::InitTextParams() { + if (gr_init() < 0) { + return false; + } + + gr_font_size(gr_sys_font(), &char_width_, &char_height_); + int mid_divide = gr_fb_width() / 2; + text_rows_ = (gr_fb_height() - 2 * y_offset) / char_height_; + text_cols_ = (mid_divide - x_offset - stereo_offset) / char_width_; + log_bottom_offset_ = gr_fb_height() - 2 * y_offset; + return true; +} + +void VrRecoveryUI::DrawHorizontalRule(int* y) { + SetColor(MENU); + *y += 4; + gr_fill(0, *y + y_offset, gr_fb_width(), *y + y_offset + 2); + *y += 4; +} + +void VrRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { + gr_fill(x, y + y_offset, x + width, y + y_offset + height); +} + +void VrRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { + int mid_divide = gr_fb_width() / 2; + gr_text(gr_sys_font(), x + x_offset + stereo_offset, *y + y_offset, line, bold); + gr_text(gr_sys_font(), x + x_offset - stereo_offset + mid_divide, *y + y_offset, line, bold); + *y += char_height_ + 4; +} diff --git a/vr_ui.h b/vr_ui.h new file mode 100644 index 000000000..85c570815 --- /dev/null +++ b/vr_ui.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef RECOVERY_VR_UI_H +#define RECOVERY_VR_UI_H + +#include "screen_ui.h" + +class VrRecoveryUI : public ScreenRecoveryUI { + public: + VrRecoveryUI(); + + protected: + // Pixel offsets to move drawing functions to visible range. + // Can vary per device depending on screen size and lens distortion. + int x_offset, y_offset, stereo_offset; + + bool InitTextParams() override; + + void DrawHorizontalRule(int* y) override; + void DrawHighlightBar(int x, int y, int width, int height) const override; + void DrawTextLine(int x, int* y, const char* line, bool bold) const override; +}; + +#endif // RECOVERY_VR_UI_H -- cgit v1.2.3 From d6c49be5de9eb9170ee58f8184646c3d36a91867 Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Thu, 15 Jun 2017 14:17:27 -0700 Subject: Add OWNERS in bootable/recovery * Owners are selected from top CL approvals or owners. They will be suggested to review/approve future CLs. * OWNERS files are recognized by the new find-owners plugin, see .md files in https://gerrit.googlesource.com/plugins/find-owners/+/master/src/main/resources/Documentation/ Test: build/make/tools/checkowners.py -c -v OWNERS Change-Id: Ifb3f0a31389b2d55357fb6b8ea60c42dd790169d --- OWNERS | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 OWNERS diff --git a/OWNERS b/OWNERS new file mode 100644 index 000000000..09754c650 --- /dev/null +++ b/OWNERS @@ -0,0 +1,3 @@ +enh+aosp-gerrit@google.com +tbao@google.com +xunchang@google.com -- cgit v1.2.3 From e2bd876043c3f26ad4712c2aad62b320368ad815 Mon Sep 17 00:00:00 2001 From: Luke Song Date: Mon, 12 Jun 2017 16:08:33 -0700 Subject: Introduce VR recovery ui A version of screen ui with specific adjustments for vr device compatibility. (cherrypick of a44dba7f4e7296077f65fd571232e8a61aed9418 to AOSP) Bug: 37779982 Test: "adb reboot recovery" to view Change-Id: If6b0f26c1b587f8d0176060685b5efb6c67593b1 --- Android.mk | 12 ++++++++++++ screen_ui.cpp | 17 +++++++++++------ screen_ui.h | 6 ++++-- vr_device.cpp | 23 +++++++++++++++++++++++ vr_ui.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vr_ui.h | 38 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 vr_device.cpp create mode 100644 vr_ui.cpp create mode 100644 vr_ui.h diff --git a/Android.mk b/Android.mk index e619db031..5348e365e 100644 --- a/Android.mk +++ b/Android.mk @@ -76,6 +76,7 @@ LOCAL_SRC_FILES := \ rotate_logs.cpp \ screen_ui.cpp \ ui.cpp \ + vr_ui.cpp \ wear_ui.cpp \ wear_touch.cpp \ @@ -182,6 +183,17 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) +# vr headset default device +# =============================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := vr_device.cpp + +# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk +LOCAL_MODULE := librecovery_ui_vr + +include $(BUILD_STATIC_LIBRARY) + include \ $(LOCAL_PATH)/applypatch/Android.mk \ $(LOCAL_PATH)/boot_control/Android.mk \ diff --git a/screen_ui.cpp b/screen_ui.cpp index bb2772dd8..61ef5911f 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -256,6 +256,10 @@ void ScreenRecoveryUI::DrawHorizontalRule(int* y) { *y += 4; } +void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { + gr_fill(x, y, x + width, y + height); +} + void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { gr_text(gr_sys_font(), x, *y, line, bold); *y += char_height_ + 4; @@ -310,15 +314,14 @@ void ScreenRecoveryUI::draw_screen_locked() { if (i == menu_sel) { // Draw the highlight bar. SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2); + DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); // Bold white text for the selected item. SetColor(MENU_SEL_FG); - gr_text(gr_sys_font(), 4, y, menu_[i], true); + DrawTextLine(TEXT_INDENT, &y, menu_[i], true); SetColor(MENU); } else { - gr_text(gr_sys_font(), 4, y, menu_[i], false); + DrawTextLine(TEXT_INDENT, &y, menu_[i], false); } - y += char_height_ + 4; } DrawHorizontalRule(&y); } @@ -329,10 +332,11 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - char_height_; + for (int ty = gr_fb_height() - char_height_ - log_bottom_offset_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { - gr_text(gr_sys_font(), 0, ty, text_[row], false); + int temp_y = ty; + DrawTextLine(0, &temp_y, text_[row], false); --row; if (row < 0) row = text_rows_ - 1; } @@ -453,6 +457,7 @@ bool ScreenRecoveryUI::InitTextParams() { gr_font_size(gr_sys_font(), &char_width_, &char_height_); text_rows_ = gr_fb_height() / char_height_; text_cols_ = gr_fb_width() / char_width_; + log_bottom_offset_ = 0; return true; } diff --git a/screen_ui.h b/screen_ui.h index a2322c36c..bd99254f6 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -107,6 +107,7 @@ class ScreenRecoveryUI : public RecoveryUI { // Log text overlay, displayed when a magic key is pressed. char** text_; size_t text_col_, text_row_, text_top_; + int log_bottom_offset_; bool show_text; bool show_text_ever; // has show_text ever been true? @@ -165,8 +166,9 @@ class ScreenRecoveryUI : public RecoveryUI { virtual int GetProgressBaseline(); virtual int GetTextBaseline(); - void DrawHorizontalRule(int* y); - void DrawTextLine(int x, int* y, const char* line, bool bold) const; + virtual void DrawHorizontalRule(int* y); + virtual void DrawHighlightBar(int x, int y, int width, int height) const; + virtual void DrawTextLine(int x, int* y, const char* line, bool bold) const; void DrawTextLines(int x, int* y, const char* const* lines) const; }; diff --git a/vr_device.cpp b/vr_device.cpp new file mode 100644 index 000000000..61e15cbb6 --- /dev/null +++ b/vr_device.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 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 "device.h" +#include "vr_ui.h" + +Device* make_device() { + return new Device(new VrRecoveryUI); +} + diff --git a/vr_ui.cpp b/vr_ui.cpp new file mode 100644 index 000000000..b2c65e3af --- /dev/null +++ b/vr_ui.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 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 "vr_ui.h" + +#include + +VrRecoveryUI::VrRecoveryUI() : + x_offset(400), + y_offset(400), + stereo_offset(100) { +} + +bool VrRecoveryUI::InitTextParams() { + if (gr_init() < 0) { + return false; + } + + gr_font_size(gr_sys_font(), &char_width_, &char_height_); + int mid_divide = gr_fb_width() / 2; + text_rows_ = (gr_fb_height() - 2 * y_offset) / char_height_; + text_cols_ = (mid_divide - x_offset - stereo_offset) / char_width_; + log_bottom_offset_ = gr_fb_height() - 2 * y_offset; + return true; +} + +void VrRecoveryUI::DrawHorizontalRule(int* y) { + SetColor(MENU); + *y += 4; + gr_fill(0, *y + y_offset, gr_fb_width(), *y + y_offset + 2); + *y += 4; +} + +void VrRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { + gr_fill(x, y + y_offset, x + width, y + y_offset + height); +} + +void VrRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { + int mid_divide = gr_fb_width() / 2; + gr_text(gr_sys_font(), x + x_offset + stereo_offset, *y + y_offset, line, bold); + gr_text(gr_sys_font(), x + x_offset - stereo_offset + mid_divide, *y + y_offset, line, bold); + *y += char_height_ + 4; +} diff --git a/vr_ui.h b/vr_ui.h new file mode 100644 index 000000000..85c570815 --- /dev/null +++ b/vr_ui.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef RECOVERY_VR_UI_H +#define RECOVERY_VR_UI_H + +#include "screen_ui.h" + +class VrRecoveryUI : public ScreenRecoveryUI { + public: + VrRecoveryUI(); + + protected: + // Pixel offsets to move drawing functions to visible range. + // Can vary per device depending on screen size and lens distortion. + int x_offset, y_offset, stereo_offset; + + bool InitTextParams() override; + + void DrawHorizontalRule(int* y) override; + void DrawHighlightBar(int x, int y, int width, int height) const override; + void DrawTextLine(int x, int* y, const char* line, bool bold) const override; +}; + +#endif // RECOVERY_VR_UI_H -- cgit v1.2.3 From ac31808cd37cfb98755e5821dbb2efb5fe5cb12a Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 21 Apr 2017 14:36:12 -0700 Subject: recovery: replace make_ext4 with e2fsprogs Execute mke2fs to create empty ext4 filesystem. Execute e2fsdroid to add files to filesystem. Test: enter recovery mode and wipe data Bug: 35219933 Change-Id: I10a9f4c1f4754ad864b2df45b1f879180ab33876 --- Android.mk | 8 ++++--- roots.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++------ updater/install.cpp | 27 +++++++++++++++++++++-- 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/Android.mk b/Android.mk index 5348e365e..344d5ad31 100644 --- a/Android.mk +++ b/Android.mk @@ -84,9 +84,11 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_REQUIRED_MODULES := e2fsdroid_static mke2fs_static mke2fs.conf + ifeq ($(TARGET_USERIMAGES_USE_F2FS),true) ifeq ($(HOST_OS),linux) -LOCAL_REQUIRED_MODULES := mkfs.f2fs +LOCAL_REQUIRED_MODULES += mkfs.f2fs endif endif @@ -101,6 +103,7 @@ LOCAL_STATIC_LIBRARIES := \ libverifier \ libbatterymonitor \ libbootloader_message \ + libfs_mgr \ libext4_utils \ libsparse \ libziparchive \ @@ -111,7 +114,6 @@ LOCAL_STATIC_LIBRARIES := \ libfusesideload \ libminui \ libpng \ - libfs_mgr \ libcrypto_utils \ libcrypto \ libvintf_recovery \ @@ -140,7 +142,7 @@ else endif ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),) -LOCAL_REQUIRED_MODULES := recovery-persist recovery-refresh +LOCAL_REQUIRED_MODULES += recovery-persist recovery-refresh endif include $(BUILD_EXECUTABLE) diff --git a/roots.cpp b/roots.cpp index 9b4270256..e98dfd448 100644 --- a/roots.cpp +++ b/roots.cpp @@ -27,7 +27,8 @@ #include #include -#include +#include +#include #include #include @@ -215,11 +216,60 @@ int format_volume(const char* volume, const char* directory) { } int result; if (strcmp(v->fs_type, "ext4") == 0) { - if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { - result = make_ext4fs_directory_align(v->blk_device, length, volume, sehandle, - directory, v->erase_blk_size, v->logical_blk_size); - } else { - result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory); + static constexpr int block_size = 4096; + int raid_stride = v->logical_blk_size / block_size; + int raid_stripe_width = v->erase_blk_size / block_size; + + // stride should be the max of 8kb and logical block size + if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) { + raid_stride = 8192 / block_size; + } + + const char* mke2fs_argv[] = { "/sbin/mke2fs_static", + "-F", + "-t", + "ext4", + "-b", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr }; + + int i = 5; + std::string block_size_str = std::to_string(block_size); + mke2fs_argv[i++] = block_size_str.c_str(); + + std::string ext_args; + if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { + ext_args = android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, + raid_stripe_width); + mke2fs_argv[i++] = "-E"; + mke2fs_argv[i++] = ext_args.c_str(); + } + + mke2fs_argv[i++] = v->blk_device; + + std::string size_str = std::to_string(length / block_size); + if (length != 0) { + mke2fs_argv[i++] = size_str.c_str(); + } + + result = exec_cmd(mke2fs_argv[0], const_cast(mke2fs_argv)); + if (result == 0 && directory != nullptr) { + const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", + "-e", + "-S", + "/file_contexts", + "-f", + directory, + "-a", + volume, + v->blk_device, + nullptr }; + + result = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); } } else { /* Has to be f2fs because we checked earlier. */ if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { diff --git a/updater/install.cpp b/updater/install.cpp index ff79edce0..c9a3a0799 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -302,9 +302,32 @@ Value* FormatFn(const char* name, State* state, const std::vector(mke2fs_argv)); + if (status != 0) { + LOG(WARNING) << name << ": mke2fs failed (" << status << ") on " << location + << ", falling back to make_ext4fs"; + status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle); + if (status != 0) { + LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location; + return StringValue(""); + } + return StringValue(location); + } + + const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-S", + "/file_contexts", "-a", mount_point.c_str(), + location.c_str(), nullptr }; + status = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); if (status != 0) { - LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location; + LOG(ERROR) << name << ": e2fsdroid failed (" << status << ") on " << location; return StringValue(""); } return StringValue(location); -- cgit v1.2.3 From 87f4650874346f1d0238e70b148a31cea5e19a2e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 19 Jun 2017 23:10:44 -0700 Subject: screen_ui: Allow setting screen margin space. For round screen or screens with rounded corners, we don't want to show texts within the margin which could otherwise be invisible. Move the density computation to ScreenRecoveryUI ctor so that the value can be used earlier. Note that this is similar to the existing stuff in wear UI (outer_width, outer_height). This CL gets ScreenRecoveryUI and WearRecoveryUI one-step closer. Bug: 62732748 Test: Setting and not setting margin_{width,height}_ on angler. Check the recovery texts (recovery menu as well as 'View recovery logs'). Change-Id: Ibf6238c9cc8949a42ed8a410e1c09d55b0b5a151 --- screen_ui.cpp | 138 +++++++++++++++++++++++++++++----------------------------- screen_ui.h | 11 ++++- 2 files changed, 78 insertions(+), 71 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index 61ef5911f..2dc1cc4c9 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -43,8 +43,6 @@ #include "screen_ui.h" #include "ui.h" -#define TEXT_INDENT 4 - // Return the current time as a double (including fractions of a second). static double now() { struct timeval tv; @@ -53,7 +51,8 @@ static double now() { } ScreenRecoveryUI::ScreenRecoveryUI() - : currentIcon(NONE), + : density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), + currentIcon(NONE), progressBarType(EMPTY), progressScopeStart(0), progressScopeSize(0), @@ -79,6 +78,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), max_stage(-1), + margin_width_(0), + margin_height_(0), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} GRSurface* ScreenRecoveryUI::GetCurrentFrame() { @@ -282,65 +283,66 @@ static const char* LONG_PRESS_HELP[] = { NULL }; -// Redraw everything on the screen. Does not flip pages. -// Should only be called with updateMutex locked. +// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex +// locked. void ScreenRecoveryUI::draw_screen_locked() { - if (!show_text) { - draw_background_locked(); - draw_foreground_locked(); - } else { - gr_color(0, 0, 0, 255); - gr_clear(); - - int y = 0; - if (show_menu) { - std::string recovery_fingerprint = - android::base::GetProperty("ro.bootimage.build.fingerprint", ""); - - SetColor(INFO); - DrawTextLine(TEXT_INDENT, &y, "Android Recovery", true); - for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) { - DrawTextLine(TEXT_INDENT, &y, chunk.c_str(), false); - } - DrawTextLines(TEXT_INDENT, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); - - SetColor(HEADER); - DrawTextLines(TEXT_INDENT, &y, menu_headers_); - - SetColor(MENU); - DrawHorizontalRule(&y); - y += 4; - for (int i = 0; i < menu_items; ++i) { - if (i == menu_sel) { - // Draw the highlight bar. - SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); - // Bold white text for the selected item. - SetColor(MENU_SEL_FG); - DrawTextLine(TEXT_INDENT, &y, menu_[i], true); - SetColor(MENU); - } else { - DrawTextLine(TEXT_INDENT, &y, menu_[i], false); - } - } - DrawHorizontalRule(&y); - } + if (!show_text) { + draw_background_locked(); + draw_foreground_locked(); + return; + } - // display from the bottom up, until we hit the top of the - // screen, the bottom of the menu, or we've displayed the - // entire text buffer. - SetColor(LOG); - int row = (text_top_ + text_rows_ - 1) % text_rows_; - size_t count = 0; - for (int ty = gr_fb_height() - char_height_ - log_bottom_offset_; - ty >= y && count < text_rows_; - ty -= char_height_, ++count) { - int temp_y = ty; - DrawTextLine(0, &temp_y, text_[row], false); - --row; - if (row < 0) row = text_rows_ - 1; - } + gr_color(0, 0, 0, 255); + gr_clear(); + + static constexpr int TEXT_INDENT = 4; + int x = TEXT_INDENT + margin_width_; + int y = margin_height_; + if (show_menu) { + std::string recovery_fingerprint = + android::base::GetProperty("ro.bootimage.build.fingerprint", ""); + + SetColor(INFO); + DrawTextLine(x, &y, "Android Recovery", true); + for (const auto& chunk : android::base::Split(recovery_fingerprint, ":")) { + DrawTextLine(x, &y, chunk.c_str(), false); + } + DrawTextLines(x, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); + + SetColor(HEADER); + DrawTextLines(x, &y, menu_headers_); + + SetColor(MENU); + DrawHorizontalRule(&y); + y += 4; + for (int i = 0; i < menu_items; ++i) { + if (i == menu_sel) { + // Draw the highlight bar. + SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); + DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); + // Bold white text for the selected item. + SetColor(MENU_SEL_FG); + DrawTextLine(x, &y, menu_[i], true); + SetColor(MENU); + } else { + DrawTextLine(x, &y, menu_[i], false); + } } + DrawHorizontalRule(&y); + } + + // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or + // we've displayed the entire text buffer. + SetColor(LOG); + int row = (text_top_ + text_rows_ - 1) % text_rows_; + size_t count = 0; + for (int ty = gr_fb_height() - margin_height_ - char_height_ - log_bottom_offset_; + ty >= y && count < text_rows_; ty -= char_height_, ++count) { + int temp_y = ty; + DrawTextLine(x, &temp_y, text_[row], false); + --row; + if (row < 0) row = text_rows_ - 1; + } } // Redraw everything on the screen and flip the screen (make it visible). @@ -450,15 +452,15 @@ void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) { } bool ScreenRecoveryUI::InitTextParams() { - if (gr_init() < 0) { - return false; - } + if (gr_init() < 0) { + return false; + } - gr_font_size(gr_sys_font(), &char_width_, &char_height_); - text_rows_ = gr_fb_height() / char_height_; - text_cols_ = gr_fb_width() / char_width_; - log_bottom_offset_ = 0; - return true; + gr_font_size(gr_sys_font(), &char_width_, &char_height_); + text_rows_ = (gr_fb_height() - margin_height_ * 2) / char_height_; + text_cols_ = (gr_fb_width() - margin_width_ * 2) / char_width_; + log_bottom_offset_ = 0; + return true; } bool ScreenRecoveryUI::Init(const std::string& locale) { @@ -467,8 +469,6 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { return false; } - density_ = static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f; - // Are we portrait or landscape? layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT; // Are we the large variant of our base layout? diff --git a/screen_ui.h b/screen_ui.h index bd99254f6..fd9f471e9 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -72,10 +72,11 @@ class ScreenRecoveryUI : public RecoveryUI { void SetColor(UIElement e); protected: + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. + const float density_; + Icon currentIcon; - // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. - float density_; // The layout to use. int layout_; @@ -136,6 +137,12 @@ class ScreenRecoveryUI : public RecoveryUI { int char_width_; int char_height_; + + // The margin that we don't want to use for showing texts (e.g. round screen, or screen with + // rounded corners). + int margin_width_; + int margin_height_; + pthread_mutex_t updateMutex; virtual bool InitTextParams(); -- cgit v1.2.3 From 171b4c4cbe6ba5af3fd7df87d227eda44e308727 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 19 Jun 2017 23:10:44 -0700 Subject: screen_ui: Allow setting screen margin space. We already have outer_width and outer_height in wear UI, and x_offset and y_offset in VR UI. This CL adds margin_width_ and margin_height_ to their base class (ScreenRecoveryUI) to shorten the gap. This will be in general useful for round or round-cornered screens. Move the density computation to ScreenRecoveryUI ctor so that the value can be used earlier. Bug: 62732748 Test: Setting and not setting margin_{width,height}_ on angler. Check the recovery texts (recovery menu as well as 'View recovery logs'). Change-Id: Ibf6238c9cc8949a42ed8a410e1c09d55b0b5a151 (cherry picked from commit 87f4650874346f1d0238e70b148a31cea5e19a2e) --- screen_ui.cpp | 138 +++++++++++++++++++++++++++++----------------------------- screen_ui.h | 11 ++++- 2 files changed, 78 insertions(+), 71 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index 61ef5911f..2dc1cc4c9 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -43,8 +43,6 @@ #include "screen_ui.h" #include "ui.h" -#define TEXT_INDENT 4 - // Return the current time as a double (including fractions of a second). static double now() { struct timeval tv; @@ -53,7 +51,8 @@ static double now() { } ScreenRecoveryUI::ScreenRecoveryUI() - : currentIcon(NONE), + : density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), + currentIcon(NONE), progressBarType(EMPTY), progressScopeStart(0), progressScopeSize(0), @@ -79,6 +78,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), max_stage(-1), + margin_width_(0), + margin_height_(0), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} GRSurface* ScreenRecoveryUI::GetCurrentFrame() { @@ -282,65 +283,66 @@ static const char* LONG_PRESS_HELP[] = { NULL }; -// Redraw everything on the screen. Does not flip pages. -// Should only be called with updateMutex locked. +// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex +// locked. void ScreenRecoveryUI::draw_screen_locked() { - if (!show_text) { - draw_background_locked(); - draw_foreground_locked(); - } else { - gr_color(0, 0, 0, 255); - gr_clear(); - - int y = 0; - if (show_menu) { - std::string recovery_fingerprint = - android::base::GetProperty("ro.bootimage.build.fingerprint", ""); - - SetColor(INFO); - DrawTextLine(TEXT_INDENT, &y, "Android Recovery", true); - for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) { - DrawTextLine(TEXT_INDENT, &y, chunk.c_str(), false); - } - DrawTextLines(TEXT_INDENT, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); - - SetColor(HEADER); - DrawTextLines(TEXT_INDENT, &y, menu_headers_); - - SetColor(MENU); - DrawHorizontalRule(&y); - y += 4; - for (int i = 0; i < menu_items; ++i) { - if (i == menu_sel) { - // Draw the highlight bar. - SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); - // Bold white text for the selected item. - SetColor(MENU_SEL_FG); - DrawTextLine(TEXT_INDENT, &y, menu_[i], true); - SetColor(MENU); - } else { - DrawTextLine(TEXT_INDENT, &y, menu_[i], false); - } - } - DrawHorizontalRule(&y); - } + if (!show_text) { + draw_background_locked(); + draw_foreground_locked(); + return; + } - // display from the bottom up, until we hit the top of the - // screen, the bottom of the menu, or we've displayed the - // entire text buffer. - SetColor(LOG); - int row = (text_top_ + text_rows_ - 1) % text_rows_; - size_t count = 0; - for (int ty = gr_fb_height() - char_height_ - log_bottom_offset_; - ty >= y && count < text_rows_; - ty -= char_height_, ++count) { - int temp_y = ty; - DrawTextLine(0, &temp_y, text_[row], false); - --row; - if (row < 0) row = text_rows_ - 1; - } + gr_color(0, 0, 0, 255); + gr_clear(); + + static constexpr int TEXT_INDENT = 4; + int x = TEXT_INDENT + margin_width_; + int y = margin_height_; + if (show_menu) { + std::string recovery_fingerprint = + android::base::GetProperty("ro.bootimage.build.fingerprint", ""); + + SetColor(INFO); + DrawTextLine(x, &y, "Android Recovery", true); + for (const auto& chunk : android::base::Split(recovery_fingerprint, ":")) { + DrawTextLine(x, &y, chunk.c_str(), false); + } + DrawTextLines(x, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); + + SetColor(HEADER); + DrawTextLines(x, &y, menu_headers_); + + SetColor(MENU); + DrawHorizontalRule(&y); + y += 4; + for (int i = 0; i < menu_items; ++i) { + if (i == menu_sel) { + // Draw the highlight bar. + SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); + DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); + // Bold white text for the selected item. + SetColor(MENU_SEL_FG); + DrawTextLine(x, &y, menu_[i], true); + SetColor(MENU); + } else { + DrawTextLine(x, &y, menu_[i], false); + } } + DrawHorizontalRule(&y); + } + + // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or + // we've displayed the entire text buffer. + SetColor(LOG); + int row = (text_top_ + text_rows_ - 1) % text_rows_; + size_t count = 0; + for (int ty = gr_fb_height() - margin_height_ - char_height_ - log_bottom_offset_; + ty >= y && count < text_rows_; ty -= char_height_, ++count) { + int temp_y = ty; + DrawTextLine(x, &temp_y, text_[row], false); + --row; + if (row < 0) row = text_rows_ - 1; + } } // Redraw everything on the screen and flip the screen (make it visible). @@ -450,15 +452,15 @@ void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) { } bool ScreenRecoveryUI::InitTextParams() { - if (gr_init() < 0) { - return false; - } + if (gr_init() < 0) { + return false; + } - gr_font_size(gr_sys_font(), &char_width_, &char_height_); - text_rows_ = gr_fb_height() / char_height_; - text_cols_ = gr_fb_width() / char_width_; - log_bottom_offset_ = 0; - return true; + gr_font_size(gr_sys_font(), &char_width_, &char_height_); + text_rows_ = (gr_fb_height() - margin_height_ * 2) / char_height_; + text_cols_ = (gr_fb_width() - margin_width_ * 2) / char_width_; + log_bottom_offset_ = 0; + return true; } bool ScreenRecoveryUI::Init(const std::string& locale) { @@ -467,8 +469,6 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { return false; } - density_ = static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f; - // Are we portrait or landscape? layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT; // Are we the large variant of our base layout? diff --git a/screen_ui.h b/screen_ui.h index bd99254f6..fd9f471e9 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -72,10 +72,11 @@ class ScreenRecoveryUI : public RecoveryUI { void SetColor(UIElement e); protected: + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. + const float density_; + Icon currentIcon; - // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. - float density_; // The layout to use. int layout_; @@ -136,6 +137,12 @@ class ScreenRecoveryUI : public RecoveryUI { int char_width_; int char_height_; + + // The margin that we don't want to use for showing texts (e.g. round screen, or screen with + // rounded corners). + int margin_width_; + int margin_height_; + pthread_mutex_t updateMutex; virtual bool InitTextParams(); -- cgit v1.2.3 From a92d8fb45676566a56d7c27d2e8fb644523adc94 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 20 Jun 2017 18:11:21 -0700 Subject: Use Makefile variables to specify margin settings. Instead of defining device-specific UI class, this CL allows using Makefile variables to specify margin values directly. Values explicitly defined via TARGET_RECOVERY_UI_MARGIN_HEIGHT and TARGET_RECOVERY_UI_MARGIN_WIDTH will be used. Otherwise they will default to zero. Bug: 62732748 Test: Specify the height and width and check recovery texts. Change-Id: Icb6f7466c8d407f877b93da38aebfdf7e6b41be7 --- Android.mk | 12 ++++++++++++ screen_ui.cpp | 16 ++++++++-------- screen_ui.h | 10 +++++----- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Android.mk b/Android.mk index 5348e365e..5ce9d335c 100644 --- a/Android.mk +++ b/Android.mk @@ -93,6 +93,18 @@ endif LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) LOCAL_CFLAGS += -Wno-unused-parameter -Werror +ifneq ($(TARGET_RECOVERY_UI_MARGIN_HEIGHT),) +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=$(TARGET_RECOVERY_UI_MARGIN_HEIGHT) +else +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=0 +endif + +ifneq ($(TARGET_RECOVERY_UI_MARGIN_WIDTH),) +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=$(TARGET_RECOVERY_UI_MARGIN_WIDTH) +else +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0 +endif + LOCAL_C_INCLUDES += \ system/vold \ diff --git a/screen_ui.cpp b/screen_ui.cpp index 2dc1cc4c9..d21a64890 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -51,7 +51,9 @@ static double now() { } ScreenRecoveryUI::ScreenRecoveryUI() - : density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), + : kMarginWidth(RECOVERY_UI_MARGIN_WIDTH), + kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT), + density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), currentIcon(NONE), progressBarType(EMPTY), progressScopeStart(0), @@ -78,8 +80,6 @@ ScreenRecoveryUI::ScreenRecoveryUI() animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), max_stage(-1), - margin_width_(0), - margin_height_(0), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} GRSurface* ScreenRecoveryUI::GetCurrentFrame() { @@ -296,8 +296,8 @@ void ScreenRecoveryUI::draw_screen_locked() { gr_clear(); static constexpr int TEXT_INDENT = 4; - int x = TEXT_INDENT + margin_width_; - int y = margin_height_; + int x = TEXT_INDENT + kMarginWidth; + int y = kMarginHeight; if (show_menu) { std::string recovery_fingerprint = android::base::GetProperty("ro.bootimage.build.fingerprint", ""); @@ -336,7 +336,7 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - margin_height_ - char_height_ - log_bottom_offset_; + for (int ty = gr_fb_height() - kMarginHeight - char_height_ - log_bottom_offset_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { int temp_y = ty; DrawTextLine(x, &temp_y, text_[row], false); @@ -457,8 +457,8 @@ bool ScreenRecoveryUI::InitTextParams() { } gr_font_size(gr_sys_font(), &char_width_, &char_height_); - text_rows_ = (gr_fb_height() - margin_height_ * 2) / char_height_; - text_cols_ = (gr_fb_width() - margin_width_ * 2) / char_width_; + text_rows_ = (gr_fb_height() - kMarginHeight * 2) / char_height_; + text_cols_ = (gr_fb_width() - kMarginWidth * 2) / char_width_; log_bottom_offset_ = 0; return true; } diff --git a/screen_ui.h b/screen_ui.h index fd9f471e9..e961c1c93 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -72,6 +72,11 @@ class ScreenRecoveryUI : public RecoveryUI { void SetColor(UIElement e); protected: + // The margin that we don't want to use for showing texts (e.g. round screen, or screen with + // rounded corners). + const int kMarginWidth; + const int kMarginHeight; + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. const float density_; @@ -138,11 +143,6 @@ class ScreenRecoveryUI : public RecoveryUI { int char_width_; int char_height_; - // The margin that we don't want to use for showing texts (e.g. round screen, or screen with - // rounded corners). - int margin_width_; - int margin_height_; - pthread_mutex_t updateMutex; virtual bool InitTextParams(); -- cgit v1.2.3 From 336cbce2526e4ce6990aed5b98d39814e6456ea2 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 8 May 2017 13:41:28 -0400 Subject: update_verifier: Support AVB. When using AVB, PRODUCT_SUPPORTS_VERITY is not set so check for BOARD_ENABLE_AVB as well. Also AVB sets up the root filesystem as 'vroot' so map that to 'system' since this is what is expected. Managed to test at least that the code is at least compiled in: $ fastboot --set-active=_a Setting current slot to 'a'... OKAY [ 0.023s] finished. total time: 0.023s $ fastboot reboot rebooting... finished. total time: 0.050s $ adb wait-for-device $ adb logcat |grep update_verifier 03-04 05:28:56.773 630 630 I /system/bin/update_verifier: Started with arg 1: nonencrypted 03-04 05:28:56.776 630 630 I /system/bin/update_verifier: Booting slot 0: isSlotMarkedSuccessful=0 03-04 05:28:56.776 630 630 W /system/bin/update_verifier: Failed to open /data/ota_package/care_map.txt: No such file or directory 03-04 05:28:56.788 630 630 I /system/bin/update_verifier: Marked slot 0 as booted successfully. 03-04 05:28:56.788 630 630 I /system/bin/update_verifier: Leaving update_verifier. Bug: 62464819 Test: Manually tested on device using AVB bootloader. Merged-In: I13c0fe1cc5d0f397e36f5e62fcc05c8dfee5fd85 Change-Id: I2834b17688053411e7b904e31df9c83bf904cd56 --- tests/Android.mk | 4 ++++ tests/component/update_verifier_test.cpp | 2 +- update_verifier/Android.mk | 4 ++++ update_verifier/update_verifier.cpp | 21 +++++++++++++++------ 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/tests/Android.mk b/tests/Android.mk index 262f4ffdd..346873dbe 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -92,6 +92,10 @@ ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1 endif +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_CFLAGS += -DBOARD_AVB_ENABLE=1 +endif + LOCAL_MODULE := recovery_component_test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_C_INCLUDES := bootable/recovery diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index 73b4478aa..5fc7ef63f 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -24,7 +24,7 @@ class UpdateVerifierTest : public ::testing::Test { protected: void SetUp() override { -#ifdef PRODUCT_SUPPORTS_VERITY +#if defined(PRODUCT_SUPPORTS_VERITY) || defined(BOARD_AVB_ENABLE) verity_supported = true; #else verity_supported = false; diff --git a/update_verifier/Android.mk b/update_verifier/Android.mk index 37d9bfed3..33c5fe9e7 100644 --- a/update_verifier/Android.mk +++ b/update_verifier/Android.mk @@ -39,6 +39,10 @@ ifeq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SUPPORTS_VERITY),true) LOCAL_CFLAGS += -DPRODUCT_SUPPORTS_VERITY=1 endif +ifeq ($(BOARD_AVB_ENABLE),true) +LOCAL_CFLAGS += -DBOARD_AVB_ENABLE=1 +endif + include $(BUILD_STATIC_LIBRARY) # update_verifier (executable) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index fdbcfde56..d3a5185b8 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -99,12 +99,21 @@ static bool read_blocks(const std::string& partition, const std::string& range_s std::string content; if (!android::base::ReadFileToString(path, &content)) { PLOG(WARNING) << "Failed to read " << path; - } else if (android::base::Trim(content) == partition) { - dm_block_device = DEV_PATH + std::string(namelist[n]->d_name); - while (n--) { - free(namelist[n]); + } else { + std::string dm_block_name = android::base::Trim(content); +#ifdef BOARD_AVB_ENABLE + // AVB is using 'vroot' for the root block device but we're expecting 'system'. + if (dm_block_name == "vroot") { + dm_block_name = "system"; + } +#endif + if (dm_block_name == partition) { + dm_block_device = DEV_PATH + std::string(namelist[n]->d_name); + while (n--) { + free(namelist[n]); + } + break; } - break; } free(namelist[n]); } @@ -229,7 +238,7 @@ int update_verifier(int argc, char** argv) { if (is_successful == BoolResult::FALSE) { // The current slot has not booted successfully. -#ifdef PRODUCT_SUPPORTS_VERITY +#if defined(PRODUCT_SUPPORTS_VERITY) || defined(BOARD_AVB_ENABLE) std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); if (verity_mode.empty()) { LOG(ERROR) << "Failed to get dm-verity mode."; -- cgit v1.2.3 From bb7e005a7906b02857ba328c5dfb11f1f3cb938e Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 22 Jun 2017 17:35:06 -0700 Subject: Remove the obsolete package_extract_dir() test package_extract_dir() was removed in go/aog/402383, and the corresponding UpdaterTest should be removed as well. Bug: 62918308 Test: mma && code search Change-Id: Ibe9c473a5d41d2fa4d26abca5684e71b104891b0 --- tests/component/updater_test.cpp | 96 ---------------------------------------- 1 file changed, 96 deletions(-) diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 35e87fd56..0298a7645 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -224,102 +224,6 @@ TEST_F(UpdaterTest, file_getprop) { expect("", script6.c_str(), kNoCause); } -TEST_F(UpdaterTest, package_extract_dir) { - // package_extract_dir expects 2 arguments. - expect(nullptr, "package_extract_dir()", kArgsParsingFailure); - expect(nullptr, "package_extract_dir(\"arg1\")", kArgsParsingFailure); - expect(nullptr, "package_extract_dir(\"arg1\", \"arg2\", \"arg3\")", kArgsParsingFailure); - - std::string zip_path = from_testdata_base("ziptest_valid.zip"); - ZipArchiveHandle handle; - ASSERT_EQ(0, OpenArchive(zip_path.c_str(), &handle)); - - // Need to set up the ziphandle. - UpdaterInfo updater_info; - updater_info.package_zip = handle; - - // Extract "b/c.txt" and "b/d.txt" with package_extract_dir("b", ""). - TemporaryDir td; - std::string temp_dir(td.path); - std::string script("package_extract_dir(\"b\", \"" + temp_dir + "\")"); - expect("t", script.c_str(), kNoCause, &updater_info); - - // Verify. - std::string data; - std::string file_c = temp_dir + "/c.txt"; - ASSERT_TRUE(android::base::ReadFileToString(file_c, &data)); - ASSERT_EQ(kCTxtContents, data); - - std::string file_d = temp_dir + "/d.txt"; - ASSERT_TRUE(android::base::ReadFileToString(file_d, &data)); - ASSERT_EQ(kDTxtContents, data); - - // Modify the contents in order to retry. It's expected to be overwritten. - ASSERT_TRUE(android::base::WriteStringToFile("random", file_c)); - ASSERT_TRUE(android::base::WriteStringToFile("random", file_d)); - - // Extract again and verify. - expect("t", script.c_str(), kNoCause, &updater_info); - - ASSERT_TRUE(android::base::ReadFileToString(file_c, &data)); - ASSERT_EQ(kCTxtContents, data); - ASSERT_TRUE(android::base::ReadFileToString(file_d, &data)); - ASSERT_EQ(kDTxtContents, data); - - // Clean up the temp files under td. - ASSERT_EQ(0, unlink(file_c.c_str())); - ASSERT_EQ(0, unlink(file_d.c_str())); - - // Extracting "b/" (with slash) should give the same result. - script = "package_extract_dir(\"b/\", \"" + temp_dir + "\")"; - expect("t", script.c_str(), kNoCause, &updater_info); - - ASSERT_TRUE(android::base::ReadFileToString(file_c, &data)); - ASSERT_EQ(kCTxtContents, data); - ASSERT_TRUE(android::base::ReadFileToString(file_d, &data)); - ASSERT_EQ(kDTxtContents, data); - - ASSERT_EQ(0, unlink(file_c.c_str())); - ASSERT_EQ(0, unlink(file_d.c_str())); - - // Extracting "" is allowed. The entries will carry the path name. - script = "package_extract_dir(\"\", \"" + temp_dir + "\")"; - expect("t", script.c_str(), kNoCause, &updater_info); - - std::string file_a = temp_dir + "/a.txt"; - ASSERT_TRUE(android::base::ReadFileToString(file_a, &data)); - ASSERT_EQ(kATxtContents, data); - std::string file_b = temp_dir + "/b.txt"; - ASSERT_TRUE(android::base::ReadFileToString(file_b, &data)); - ASSERT_EQ(kBTxtContents, data); - std::string file_b_c = temp_dir + "/b/c.txt"; - ASSERT_TRUE(android::base::ReadFileToString(file_b_c, &data)); - ASSERT_EQ(kCTxtContents, data); - std::string file_b_d = temp_dir + "/b/d.txt"; - ASSERT_TRUE(android::base::ReadFileToString(file_b_d, &data)); - ASSERT_EQ(kDTxtContents, data); - - ASSERT_EQ(0, unlink(file_a.c_str())); - ASSERT_EQ(0, unlink(file_b.c_str())); - ASSERT_EQ(0, unlink(file_b_c.c_str())); - ASSERT_EQ(0, unlink(file_b_d.c_str())); - ASSERT_EQ(0, rmdir((temp_dir + "/b").c_str())); - - // Extracting non-existent entry should still give "t". - script = "package_extract_dir(\"doesntexist\", \"" + temp_dir + "\")"; - expect("t", script.c_str(), kNoCause, &updater_info); - - // Only relative zip_path is allowed. - script = "package_extract_dir(\"/b\", \"" + temp_dir + "\")"; - expect("", script.c_str(), kNoCause, &updater_info); - - // Only absolute dest_path is allowed. - script = "package_extract_dir(\"b\", \"path\")"; - expect("", script.c_str(), kNoCause, &updater_info); - - CloseArchive(handle); -} - // TODO: Test extracting to block device. TEST_F(UpdaterTest, package_extract_file) { // package_extract_file expects 1 or 2 arguments. -- cgit v1.2.3 From 4521b7027fd4305b7cb3a86607a8a0b0a2a48ca7 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 20 Jun 2017 18:11:21 -0700 Subject: Use Makefile variables to specify margin settings. Instead of defining device-specific UI class, this CL allows using Makefile variables to specify margin values directly. Values explicitly defined via TARGET_RECOVERY_UI_MARGIN_HEIGHT and TARGET_RECOVERY_UI_MARGIN_WIDTH will be used. Otherwise they will default to zero. Bug: 62732748 Test: Specify the height and width and check recovery texts. Change-Id: Icb6f7466c8d407f877b93da38aebfdf7e6b41be7 (cherry picked from commit a92d8fb45676566a56d7c27d2e8fb644523adc94) --- Android.mk | 12 ++++++++++++ screen_ui.cpp | 16 ++++++++-------- screen_ui.h | 10 +++++----- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Android.mk b/Android.mk index 5348e365e..5ce9d335c 100644 --- a/Android.mk +++ b/Android.mk @@ -93,6 +93,18 @@ endif LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) LOCAL_CFLAGS += -Wno-unused-parameter -Werror +ifneq ($(TARGET_RECOVERY_UI_MARGIN_HEIGHT),) +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=$(TARGET_RECOVERY_UI_MARGIN_HEIGHT) +else +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=0 +endif + +ifneq ($(TARGET_RECOVERY_UI_MARGIN_WIDTH),) +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=$(TARGET_RECOVERY_UI_MARGIN_WIDTH) +else +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0 +endif + LOCAL_C_INCLUDES += \ system/vold \ diff --git a/screen_ui.cpp b/screen_ui.cpp index 2dc1cc4c9..d21a64890 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -51,7 +51,9 @@ static double now() { } ScreenRecoveryUI::ScreenRecoveryUI() - : density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), + : kMarginWidth(RECOVERY_UI_MARGIN_WIDTH), + kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT), + density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), currentIcon(NONE), progressBarType(EMPTY), progressScopeStart(0), @@ -78,8 +80,6 @@ ScreenRecoveryUI::ScreenRecoveryUI() animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), max_stage(-1), - margin_width_(0), - margin_height_(0), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} GRSurface* ScreenRecoveryUI::GetCurrentFrame() { @@ -296,8 +296,8 @@ void ScreenRecoveryUI::draw_screen_locked() { gr_clear(); static constexpr int TEXT_INDENT = 4; - int x = TEXT_INDENT + margin_width_; - int y = margin_height_; + int x = TEXT_INDENT + kMarginWidth; + int y = kMarginHeight; if (show_menu) { std::string recovery_fingerprint = android::base::GetProperty("ro.bootimage.build.fingerprint", ""); @@ -336,7 +336,7 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - margin_height_ - char_height_ - log_bottom_offset_; + for (int ty = gr_fb_height() - kMarginHeight - char_height_ - log_bottom_offset_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { int temp_y = ty; DrawTextLine(x, &temp_y, text_[row], false); @@ -457,8 +457,8 @@ bool ScreenRecoveryUI::InitTextParams() { } gr_font_size(gr_sys_font(), &char_width_, &char_height_); - text_rows_ = (gr_fb_height() - margin_height_ * 2) / char_height_; - text_cols_ = (gr_fb_width() - margin_width_ * 2) / char_width_; + text_rows_ = (gr_fb_height() - kMarginHeight * 2) / char_height_; + text_cols_ = (gr_fb_width() - kMarginWidth * 2) / char_width_; log_bottom_offset_ = 0; return true; } diff --git a/screen_ui.h b/screen_ui.h index fd9f471e9..e961c1c93 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -72,6 +72,11 @@ class ScreenRecoveryUI : public RecoveryUI { void SetColor(UIElement e); protected: + // The margin that we don't want to use for showing texts (e.g. round screen, or screen with + // rounded corners). + const int kMarginWidth; + const int kMarginHeight; + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. const float density_; @@ -138,11 +143,6 @@ class ScreenRecoveryUI : public RecoveryUI { int char_width_; int char_height_; - // The margin that we don't want to use for showing texts (e.g. round screen, or screen with - // rounded corners). - int margin_width_; - int margin_height_; - pthread_mutex_t updateMutex; virtual bool InitTextParams(); -- cgit v1.2.3 From 8f7547edfa7ca76e2d2c0a162911e4925675258e Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 19 Jun 2017 23:10:44 -0700 Subject: screen_ui: Allow setting screen margin space. For round screen or screens with rounded corners, we don't want to show texts within the margin which could otherwise be invisible. Move the density computation to ScreenRecoveryUI ctor so that the value can be used earlier. Note that this is similar to the existing stuff in wear UI (outer_width, outer_height). This CL gets ScreenRecoveryUI and WearRecoveryUI one-step closer. Bug: 62732748 Test: Setting and not setting margin_{width,height}_ on angler. Check the recovery texts (recovery menu as well as 'View recovery logs'). Change-Id: Ibf6238c9cc8949a42ed8a410e1c09d55b0b5a151 (cherry picked from commit 87f4650874346f1d0238e70b148a31cea5e19a2e) --- screen_ui.cpp | 136 +++++++++++++++++++++++++++++----------------------------- screen_ui.h | 11 ++++- 2 files changed, 77 insertions(+), 70 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index bb2772dd8..10e56dec4 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -43,8 +43,6 @@ #include "screen_ui.h" #include "ui.h" -#define TEXT_INDENT 4 - // Return the current time as a double (including fractions of a second). static double now() { struct timeval tv; @@ -53,7 +51,8 @@ static double now() { } ScreenRecoveryUI::ScreenRecoveryUI() - : currentIcon(NONE), + : density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), + currentIcon(NONE), progressBarType(EMPTY), progressScopeStart(0), progressScopeSize(0), @@ -79,6 +78,8 @@ ScreenRecoveryUI::ScreenRecoveryUI() animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), max_stage(-1), + margin_width_(0), + margin_height_(0), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} GRSurface* ScreenRecoveryUI::GetCurrentFrame() { @@ -278,65 +279,66 @@ static const char* LONG_PRESS_HELP[] = { NULL }; -// Redraw everything on the screen. Does not flip pages. -// Should only be called with updateMutex locked. +// Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex +// locked. void ScreenRecoveryUI::draw_screen_locked() { - if (!show_text) { - draw_background_locked(); - draw_foreground_locked(); - } else { - gr_color(0, 0, 0, 255); - gr_clear(); - - int y = 0; - if (show_menu) { - std::string recovery_fingerprint = - android::base::GetProperty("ro.bootimage.build.fingerprint", ""); - - SetColor(INFO); - DrawTextLine(TEXT_INDENT, &y, "Android Recovery", true); - for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) { - DrawTextLine(TEXT_INDENT, &y, chunk.c_str(), false); - } - DrawTextLines(TEXT_INDENT, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); - - SetColor(HEADER); - DrawTextLines(TEXT_INDENT, &y, menu_headers_); - - SetColor(MENU); - DrawHorizontalRule(&y); - y += 4; - for (int i = 0; i < menu_items; ++i) { - if (i == menu_sel) { - // Draw the highlight bar. - SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2); - // Bold white text for the selected item. - SetColor(MENU_SEL_FG); - gr_text(gr_sys_font(), 4, y, menu_[i], true); - SetColor(MENU); - } else { - gr_text(gr_sys_font(), 4, y, menu_[i], false); - } - y += char_height_ + 4; - } - DrawHorizontalRule(&y); - } + if (!show_text) { + draw_background_locked(); + draw_foreground_locked(); + return; + } - // display from the bottom up, until we hit the top of the - // screen, the bottom of the menu, or we've displayed the - // entire text buffer. - SetColor(LOG); - int row = (text_top_ + text_rows_ - 1) % text_rows_; - size_t count = 0; - for (int ty = gr_fb_height() - char_height_; - ty >= y && count < text_rows_; - ty -= char_height_, ++count) { - gr_text(gr_sys_font(), 0, ty, text_[row], false); - --row; - if (row < 0) row = text_rows_ - 1; - } + gr_color(0, 0, 0, 255); + gr_clear(); + + static constexpr int TEXT_INDENT = 4; + int x = TEXT_INDENT + margin_width_; + int y = margin_height_; + if (show_menu) { + std::string recovery_fingerprint = + android::base::GetProperty("ro.bootimage.build.fingerprint", ""); + + SetColor(INFO); + DrawTextLine(x, &y, "Android Recovery", true); + for (const auto& chunk : android::base::Split(recovery_fingerprint, ":")) { + DrawTextLine(x, &y, chunk.c_str(), false); + } + DrawTextLines(x, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); + + SetColor(HEADER); + DrawTextLines(x, &y, menu_headers_); + + SetColor(MENU); + DrawHorizontalRule(&y); + y += 4; + for (int i = 0; i < menu_items; ++i) { + if (i == menu_sel) { + // Draw the highlight bar. + SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); + gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2); + // Bold white text for the selected item. + SetColor(MENU_SEL_FG); + gr_text(gr_sys_font(), 4, y, menu_[i], true); + SetColor(MENU); + } else { + gr_text(gr_sys_font(), 4, y, menu_[i], false); + } + y += char_height_ + 4; } + DrawHorizontalRule(&y); + } + + // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or + // we've displayed the entire text buffer. + SetColor(LOG); + int row = (text_top_ + text_rows_ - 1) % text_rows_; + size_t count = 0; + for (int ty = gr_fb_height() - margin_height_ - char_height_; + ty >= y && count < text_rows_; ty -= char_height_, ++count) { + gr_text(gr_sys_font(), 0, ty, text_[row], false); + --row; + if (row < 0) row = text_rows_ - 1; + } } // Redraw everything on the screen and flip the screen (make it visible). @@ -446,14 +448,14 @@ void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) { } bool ScreenRecoveryUI::InitTextParams() { - if (gr_init() < 0) { - return false; - } + if (gr_init() < 0) { + return false; + } - gr_font_size(gr_sys_font(), &char_width_, &char_height_); - text_rows_ = gr_fb_height() / char_height_; - text_cols_ = gr_fb_width() / char_width_; - return true; + gr_font_size(gr_sys_font(), &char_width_, &char_height_); + text_rows_ = (gr_fb_height() - margin_height_ * 2) / char_height_; + text_cols_ = (gr_fb_width() - margin_width_ * 2) / char_width_; + return true; } bool ScreenRecoveryUI::Init(const std::string& locale) { @@ -462,8 +464,6 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { return false; } - density_ = static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f; - // Are we portrait or landscape? layout_ = (gr_fb_width() > gr_fb_height()) ? LANDSCAPE : PORTRAIT; // Are we the large variant of our base layout? diff --git a/screen_ui.h b/screen_ui.h index a2322c36c..86cbd5cd3 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -72,10 +72,11 @@ class ScreenRecoveryUI : public RecoveryUI { void SetColor(UIElement e); protected: + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. + const float density_; + Icon currentIcon; - // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. - float density_; // The layout to use. int layout_; @@ -135,6 +136,12 @@ class ScreenRecoveryUI : public RecoveryUI { int char_width_; int char_height_; + + // The margin that we don't want to use for showing texts (e.g. round screen, or screen with + // rounded corners). + int margin_width_; + int margin_height_; + pthread_mutex_t updateMutex; virtual bool InitTextParams(); -- cgit v1.2.3 From 855eaffff674f0c90cf6fe107f8d10b50efc3965 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 20 Jun 2017 18:11:21 -0700 Subject: Use Makefile variables to specify margin settings. Instead of defining device-specific UI class, this CL allows using Makefile variables to specify margin values directly. Values explicitly defined via TARGET_RECOVERY_UI_MARGIN_HEIGHT and TARGET_RECOVERY_UI_MARGIN_WIDTH will be used. Otherwise they will default to zero. Bug: 62732748 Test: Specify the height and width and check recovery texts. Change-Id: Icb6f7466c8d407f877b93da38aebfdf7e6b41be7 (cherry picked from commit a92d8fb45676566a56d7c27d2e8fb644523adc94) --- Android.mk | 12 ++++++++++++ screen_ui.cpp | 16 ++++++++-------- screen_ui.h | 10 +++++----- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/Android.mk b/Android.mk index e619db031..5f3a8a45d 100644 --- a/Android.mk +++ b/Android.mk @@ -92,6 +92,18 @@ endif LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) LOCAL_CFLAGS += -Wno-unused-parameter -Werror +ifneq ($(TARGET_RECOVERY_UI_MARGIN_HEIGHT),) +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=$(TARGET_RECOVERY_UI_MARGIN_HEIGHT) +else +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=0 +endif + +ifneq ($(TARGET_RECOVERY_UI_MARGIN_WIDTH),) +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=$(TARGET_RECOVERY_UI_MARGIN_WIDTH) +else +LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0 +endif + LOCAL_C_INCLUDES += \ system/vold \ diff --git a/screen_ui.cpp b/screen_ui.cpp index 10e56dec4..4ca96a9b2 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -51,7 +51,9 @@ static double now() { } ScreenRecoveryUI::ScreenRecoveryUI() - : density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), + : kMarginWidth(RECOVERY_UI_MARGIN_WIDTH), + kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT), + density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), currentIcon(NONE), progressBarType(EMPTY), progressScopeStart(0), @@ -78,8 +80,6 @@ ScreenRecoveryUI::ScreenRecoveryUI() animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), max_stage(-1), - margin_width_(0), - margin_height_(0), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} GRSurface* ScreenRecoveryUI::GetCurrentFrame() { @@ -292,8 +292,8 @@ void ScreenRecoveryUI::draw_screen_locked() { gr_clear(); static constexpr int TEXT_INDENT = 4; - int x = TEXT_INDENT + margin_width_; - int y = margin_height_; + int x = TEXT_INDENT + kMarginWidth; + int y = kMarginHeight; if (show_menu) { std::string recovery_fingerprint = android::base::GetProperty("ro.bootimage.build.fingerprint", ""); @@ -333,7 +333,7 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - margin_height_ - char_height_; + for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { gr_text(gr_sys_font(), 0, ty, text_[row], false); --row; @@ -453,8 +453,8 @@ bool ScreenRecoveryUI::InitTextParams() { } gr_font_size(gr_sys_font(), &char_width_, &char_height_); - text_rows_ = (gr_fb_height() - margin_height_ * 2) / char_height_; - text_cols_ = (gr_fb_width() - margin_width_ * 2) / char_width_; + text_rows_ = (gr_fb_height() - kMarginHeight * 2) / char_height_; + text_cols_ = (gr_fb_width() - kMarginWidth * 2) / char_width_; return true; } diff --git a/screen_ui.h b/screen_ui.h index 86cbd5cd3..58032d80b 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -72,6 +72,11 @@ class ScreenRecoveryUI : public RecoveryUI { void SetColor(UIElement e); protected: + // The margin that we don't want to use for showing texts (e.g. round screen, or screen with + // rounded corners). + const int kMarginWidth; + const int kMarginHeight; + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. const float density_; @@ -137,11 +142,6 @@ class ScreenRecoveryUI : public RecoveryUI { int char_width_; int char_height_; - // The margin that we don't want to use for showing texts (e.g. round screen, or screen with - // rounded corners). - int margin_width_; - int margin_height_; - pthread_mutex_t updateMutex; virtual bool InitTextParams(); -- cgit v1.2.3 From 81a8e4cab2a20fd1b1a4716563d4d2586bd1e1de Mon Sep 17 00:00:00 2001 From: Luke Song Date: Fri, 23 Jun 2017 14:33:46 -0700 Subject: Restructure vr_ui Get rid of pixel offset variables, and use makefile variables in BoardConfigs. Bug: 37779982 Test: Verified vr ui has same behavior. Change-Id: Ifbf44e27d7101aedbe3c0e6db4b8181d56efadfd --- Android.mk | 6 ++++++ screen_ui.cpp | 3 +-- screen_ui.h | 1 - vr_ui.cpp | 31 +++++-------------------------- vr_ui.h | 4 +--- 5 files changed, 13 insertions(+), 32 deletions(-) diff --git a/Android.mk b/Android.mk index bbb204660..95c8823e3 100644 --- a/Android.mk +++ b/Android.mk @@ -107,6 +107,12 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0 endif +ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) +LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) +else +LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=0 +endif + LOCAL_C_INCLUDES += \ system/vold \ diff --git a/screen_ui.cpp b/screen_ui.cpp index d21a64890..c41bb22e1 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -336,7 +336,7 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - kMarginHeight - char_height_ - log_bottom_offset_; + for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { int temp_y = ty; DrawTextLine(x, &temp_y, text_[row], false); @@ -459,7 +459,6 @@ bool ScreenRecoveryUI::InitTextParams() { gr_font_size(gr_sys_font(), &char_width_, &char_height_); text_rows_ = (gr_fb_height() - kMarginHeight * 2) / char_height_; text_cols_ = (gr_fb_width() - kMarginWidth * 2) / char_width_; - log_bottom_offset_ = 0; return true; } diff --git a/screen_ui.h b/screen_ui.h index e961c1c93..2500575ca 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -113,7 +113,6 @@ class ScreenRecoveryUI : public RecoveryUI { // Log text overlay, displayed when a magic key is pressed. char** text_; size_t text_col_, text_row_, text_top_; - int log_bottom_offset_; bool show_text; bool show_text_ever; // has show_text ever been true? diff --git a/vr_ui.cpp b/vr_ui.cpp index b2c65e3af..8b8261e35 100644 --- a/vr_ui.cpp +++ b/vr_ui.cpp @@ -18,39 +18,18 @@ #include -VrRecoveryUI::VrRecoveryUI() : - x_offset(400), - y_offset(400), - stereo_offset(100) { -} +VrRecoveryUI::VrRecoveryUI() : kStereoOffset(RECOVERY_UI_VR_STEREO_OFFSET) {} bool VrRecoveryUI::InitTextParams() { - if (gr_init() < 0) { - return false; - } - - gr_font_size(gr_sys_font(), &char_width_, &char_height_); + if (!ScreenRecoveryUI::InitTextParams()) return false; int mid_divide = gr_fb_width() / 2; - text_rows_ = (gr_fb_height() - 2 * y_offset) / char_height_; - text_cols_ = (mid_divide - x_offset - stereo_offset) / char_width_; - log_bottom_offset_ = gr_fb_height() - 2 * y_offset; + text_cols_ = (mid_divide - kMarginWidth - kStereoOffset) / char_width_; return true; } -void VrRecoveryUI::DrawHorizontalRule(int* y) { - SetColor(MENU); - *y += 4; - gr_fill(0, *y + y_offset, gr_fb_width(), *y + y_offset + 2); - *y += 4; -} - -void VrRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { - gr_fill(x, y + y_offset, x + width, y + y_offset + height); -} - void VrRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { int mid_divide = gr_fb_width() / 2; - gr_text(gr_sys_font(), x + x_offset + stereo_offset, *y + y_offset, line, bold); - gr_text(gr_sys_font(), x + x_offset - stereo_offset + mid_divide, *y + y_offset, line, bold); + gr_text(gr_sys_font(), x + kStereoOffset, *y, line, bold); + gr_text(gr_sys_font(), x - kStereoOffset + mid_divide, *y, line, bold); *y += char_height_ + 4; } diff --git a/vr_ui.h b/vr_ui.h index 85c570815..31ca4a61d 100644 --- a/vr_ui.h +++ b/vr_ui.h @@ -26,12 +26,10 @@ class VrRecoveryUI : public ScreenRecoveryUI { protected: // Pixel offsets to move drawing functions to visible range. // Can vary per device depending on screen size and lens distortion. - int x_offset, y_offset, stereo_offset; + const int kStereoOffset; bool InitTextParams() override; - void DrawHorizontalRule(int* y) override; - void DrawHighlightBar(int x, int y, int width, int height) const override; void DrawTextLine(int x, int* y, const char* line, bool bold) const override; }; -- cgit v1.2.3 From 9d4839c60fc51068ba8a389545b2751a7c5a14b0 Mon Sep 17 00:00:00 2001 From: Luke Song Date: Fri, 23 Jun 2017 14:33:46 -0700 Subject: Restructure vr_ui Get rid of pixel offset variables, and use makefile variables in BoardConfigs. Bug: 37779982 Test: Verified vr ui has same behavior. Change-Id: Ifbf44e27d7101aedbe3c0e6db4b8181d56efadfd (cherry picked from commit 81a8e4cab2a20fd1b1a4716563d4d2586bd1e1de) --- Android.mk | 6 ++++++ screen_ui.cpp | 3 +-- screen_ui.h | 1 - vr_ui.cpp | 31 +++++-------------------------- vr_ui.h | 4 +--- 5 files changed, 13 insertions(+), 32 deletions(-) diff --git a/Android.mk b/Android.mk index 5ce9d335c..4ac8856ea 100644 --- a/Android.mk +++ b/Android.mk @@ -105,6 +105,12 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0 endif +ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) +LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) +else +LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=0 +endif + LOCAL_C_INCLUDES += \ system/vold \ diff --git a/screen_ui.cpp b/screen_ui.cpp index d21a64890..c41bb22e1 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -336,7 +336,7 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - kMarginHeight - char_height_ - log_bottom_offset_; + for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { int temp_y = ty; DrawTextLine(x, &temp_y, text_[row], false); @@ -459,7 +459,6 @@ bool ScreenRecoveryUI::InitTextParams() { gr_font_size(gr_sys_font(), &char_width_, &char_height_); text_rows_ = (gr_fb_height() - kMarginHeight * 2) / char_height_; text_cols_ = (gr_fb_width() - kMarginWidth * 2) / char_width_; - log_bottom_offset_ = 0; return true; } diff --git a/screen_ui.h b/screen_ui.h index e961c1c93..2500575ca 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -113,7 +113,6 @@ class ScreenRecoveryUI : public RecoveryUI { // Log text overlay, displayed when a magic key is pressed. char** text_; size_t text_col_, text_row_, text_top_; - int log_bottom_offset_; bool show_text; bool show_text_ever; // has show_text ever been true? diff --git a/vr_ui.cpp b/vr_ui.cpp index b2c65e3af..8b8261e35 100644 --- a/vr_ui.cpp +++ b/vr_ui.cpp @@ -18,39 +18,18 @@ #include -VrRecoveryUI::VrRecoveryUI() : - x_offset(400), - y_offset(400), - stereo_offset(100) { -} +VrRecoveryUI::VrRecoveryUI() : kStereoOffset(RECOVERY_UI_VR_STEREO_OFFSET) {} bool VrRecoveryUI::InitTextParams() { - if (gr_init() < 0) { - return false; - } - - gr_font_size(gr_sys_font(), &char_width_, &char_height_); + if (!ScreenRecoveryUI::InitTextParams()) return false; int mid_divide = gr_fb_width() / 2; - text_rows_ = (gr_fb_height() - 2 * y_offset) / char_height_; - text_cols_ = (mid_divide - x_offset - stereo_offset) / char_width_; - log_bottom_offset_ = gr_fb_height() - 2 * y_offset; + text_cols_ = (mid_divide - kMarginWidth - kStereoOffset) / char_width_; return true; } -void VrRecoveryUI::DrawHorizontalRule(int* y) { - SetColor(MENU); - *y += 4; - gr_fill(0, *y + y_offset, gr_fb_width(), *y + y_offset + 2); - *y += 4; -} - -void VrRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { - gr_fill(x, y + y_offset, x + width, y + y_offset + height); -} - void VrRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { int mid_divide = gr_fb_width() / 2; - gr_text(gr_sys_font(), x + x_offset + stereo_offset, *y + y_offset, line, bold); - gr_text(gr_sys_font(), x + x_offset - stereo_offset + mid_divide, *y + y_offset, line, bold); + gr_text(gr_sys_font(), x + kStereoOffset, *y, line, bold); + gr_text(gr_sys_font(), x - kStereoOffset + mid_divide, *y, line, bold); *y += char_height_ + 4; } diff --git a/vr_ui.h b/vr_ui.h index 85c570815..31ca4a61d 100644 --- a/vr_ui.h +++ b/vr_ui.h @@ -26,12 +26,10 @@ class VrRecoveryUI : public ScreenRecoveryUI { protected: // Pixel offsets to move drawing functions to visible range. // Can vary per device depending on screen size and lens distortion. - int x_offset, y_offset, stereo_offset; + const int kStereoOffset; bool InitTextParams() override; - void DrawHorizontalRule(int* y) override; - void DrawHighlightBar(int x, int y, int width, int height) const override; void DrawTextLine(int x, int* y, const char* line, bool bold) const override; }; -- cgit v1.2.3 From 5d2e3bd109539282d1829e3579c1f762a9486382 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 23 Jun 2017 22:23:50 -0700 Subject: Formatting RecoveryUI related files. All cosmetic changes about indentation reformatting in this CL. Test: mmma bootable/recovery Change-Id: I4539e6244697d1f356b7eb10b961b52d7db561f7 --- screen_ui.cpp | 758 +++++++++++++++++++++++++++++----------------------------- screen_ui.h | 216 +++++++++-------- ui.cpp | 330 ++++++++++++------------- ui.h | 296 +++++++++++------------ vr_ui.h | 16 +- wear_ui.cpp | 503 +++++++++++++++++++------------------- wear_ui.h | 80 +++---- 7 files changed, 1100 insertions(+), 1099 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index c41bb22e1..6172b631a 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -45,9 +45,9 @@ // Return the current time as a double (including fractions of a second). static double now() { - struct timeval tv; - gettimeofday(&tv, nullptr); - return tv.tv_sec + tv.tv_usec / 1000000.0; + struct timeval tv; + gettimeofday(&tv, nullptr); + return tv.tv_sec + tv.tv_usec / 1000000.0; } ScreenRecoveryUI::ScreenRecoveryUI() @@ -83,24 +83,29 @@ ScreenRecoveryUI::ScreenRecoveryUI() updateMutex(PTHREAD_MUTEX_INITIALIZER) {} GRSurface* ScreenRecoveryUI::GetCurrentFrame() { - if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { - return intro_done ? loopFrames[current_frame] : introFrames[current_frame]; - } - return error_icon; + if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { + return intro_done ? loopFrames[current_frame] : introFrames[current_frame]; + } + return error_icon; } GRSurface* ScreenRecoveryUI::GetCurrentText() { - switch (currentIcon) { - case ERASING: return erasing_text; - case ERROR: return error_text; - case INSTALLING_UPDATE: return installing_text; - case NO_COMMAND: return no_command_text; - case NONE: abort(); - } + switch (currentIcon) { + case ERASING: + return erasing_text; + case ERROR: + return error_text; + case INSTALLING_UPDATE: + return installing_text; + case NO_COMMAND: + return no_command_text; + case NONE: + abort(); + } } int ScreenRecoveryUI::PixelsFromDp(int dp) const { - return dp * density_; + return dp * density_; } // Here's the intended layout: @@ -121,53 +126,52 @@ int ScreenRecoveryUI::PixelsFromDp(int dp) const { enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX }; enum Dimension { PROGRESS = 0, TEXT = 1, ICON = 2, DIMENSION_MAX }; static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = { - { 194, 32, 68, }, // PORTRAIT - { 340, 32, 68, }, // PORTRAIT_LARGE - { 131, 26, 56, }, // LANDSCAPE - { 262, 52, 112, }, // LANDSCAPE_LARGE + { 194, 32, 68, }, // PORTRAIT + { 340, 32, 68, }, // PORTRAIT_LARGE + { 131, 26, 56, }, // LANDSCAPE + { 262, 52, 112, }, // LANDSCAPE_LARGE }; int ScreenRecoveryUI::GetAnimationBaseline() { - return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) - - gr_get_height(loopFrames[0]); + return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) - gr_get_height(loopFrames[0]); } int ScreenRecoveryUI::GetTextBaseline() { - return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) - - gr_get_height(installing_text); + return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) - + gr_get_height(installing_text); } int ScreenRecoveryUI::GetProgressBaseline() { - return gr_fb_height() - PixelsFromDp(kLayouts[layout_][PROGRESS]) - - gr_get_height(progressBarFill); + return gr_fb_height() - PixelsFromDp(kLayouts[layout_][PROGRESS]) - + gr_get_height(progressBarFill); } // Clear the screen and draw the currently selected background icon (if any). // Should only be called with updateMutex locked. void ScreenRecoveryUI::draw_background_locked() { - pagesIdentical = false; - gr_color(0, 0, 0, 255); - gr_clear(); - - if (currentIcon != NONE) { - if (max_stage != -1) { - int stage_height = gr_get_height(stageMarkerEmpty); - int stage_width = gr_get_width(stageMarkerEmpty); - int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2; - int y = gr_fb_height() - stage_height; - for (int i = 0; i < max_stage; ++i) { - GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty; - gr_blit(stage_surface, 0, 0, stage_width, stage_height, x, y); - x += stage_width; - } - } + pagesIdentical = false; + gr_color(0, 0, 0, 255); + gr_clear(); - GRSurface* text_surface = GetCurrentText(); - int text_x = (gr_fb_width() - gr_get_width(text_surface)) / 2; - int text_y = GetTextBaseline(); - gr_color(255, 255, 255, 255); - gr_texticon(text_x, text_y, text_surface); + if (currentIcon != NONE) { + if (max_stage != -1) { + int stage_height = gr_get_height(stageMarkerEmpty); + int stage_width = gr_get_width(stageMarkerEmpty); + int x = (gr_fb_width() - max_stage * gr_get_width(stageMarkerEmpty)) / 2; + int y = gr_fb_height() - stage_height; + for (int i = 0; i < max_stage; ++i) { + GRSurface* stage_surface = (i < stage) ? stageMarkerFill : stageMarkerEmpty; + gr_blit(stage_surface, 0, 0, stage_width, stage_height, x, y); + x += stage_width; + } } + + GRSurface* text_surface = GetCurrentText(); + int text_x = (gr_fb_width() - gr_get_width(text_surface)) / 2; + int text_y = GetTextBaseline(); + gr_color(255, 255, 255, 255); + gr_texticon(text_x, text_y, text_surface); + } } // Draws the animation and progress bar (if any) on the screen. @@ -221,66 +225,66 @@ void ScreenRecoveryUI::draw_foreground_locked() { } void ScreenRecoveryUI::SetColor(UIElement e) { - switch (e) { - case INFO: - gr_color(249, 194, 0, 255); - break; - case HEADER: - gr_color(247, 0, 6, 255); - break; - case MENU: - case MENU_SEL_BG: - gr_color(0, 106, 157, 255); - break; - case MENU_SEL_BG_ACTIVE: - gr_color(0, 156, 100, 255); - break; - case MENU_SEL_FG: - gr_color(255, 255, 255, 255); - break; - case LOG: - gr_color(196, 196, 196, 255); - break; - case TEXT_FILL: - gr_color(0, 0, 0, 160); - break; - default: - gr_color(255, 255, 255, 255); - break; - } + switch (e) { + case INFO: + gr_color(249, 194, 0, 255); + break; + case HEADER: + gr_color(247, 0, 6, 255); + break; + case MENU: + case MENU_SEL_BG: + gr_color(0, 106, 157, 255); + break; + case MENU_SEL_BG_ACTIVE: + gr_color(0, 156, 100, 255); + break; + case MENU_SEL_FG: + gr_color(255, 255, 255, 255); + break; + case LOG: + gr_color(196, 196, 196, 255); + break; + case TEXT_FILL: + gr_color(0, 0, 0, 160); + break; + default: + gr_color(255, 255, 255, 255); + break; + } } void ScreenRecoveryUI::DrawHorizontalRule(int* y) { - SetColor(MENU); - *y += 4; - gr_fill(0, *y, gr_fb_width(), *y + 2); - *y += 4; + SetColor(MENU); + *y += 4; + gr_fill(0, *y, gr_fb_width(), *y + 2); + *y += 4; } void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { - gr_fill(x, y, x + width, y + height); + gr_fill(x, y, x + width, y + height); } void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { - gr_text(gr_sys_font(), x, *y, line, bold); - *y += char_height_ + 4; + gr_text(gr_sys_font(), x, *y, line, bold); + *y += char_height_ + 4; } void ScreenRecoveryUI::DrawTextLines(int x, int* y, const char* const* lines) const { - for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) { - DrawTextLine(x, y, lines[i], false); - } + for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) { + DrawTextLine(x, y, lines[i], false); + } } static const char* REGULAR_HELP[] = { - "Use volume up/down and power.", - NULL + "Use volume up/down and power.", + NULL }; static const char* LONG_PRESS_HELP[] = { - "Any button cycles highlight.", - "Long-press activates.", - NULL + "Any button cycles highlight.", + "Long-press activates.", + NULL }; // Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex @@ -336,8 +340,8 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - kMarginHeight - char_height_; - ty >= y && count < text_rows_; ty -= char_height_, ++count) { + for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; + ty -= char_height_, ++count) { int temp_y = ty; DrawTextLine(x, &temp_y, text_[row], false); --row; @@ -348,81 +352,81 @@ void ScreenRecoveryUI::draw_screen_locked() { // Redraw everything on the screen and flip the screen (make it visible). // Should only be called with updateMutex locked. void ScreenRecoveryUI::update_screen_locked() { - draw_screen_locked(); - gr_flip(); + draw_screen_locked(); + gr_flip(); } // Updates only the progress bar, if possible, otherwise redraws the screen. // Should only be called with updateMutex locked. void ScreenRecoveryUI::update_progress_locked() { - if (show_text || !pagesIdentical) { - draw_screen_locked(); // Must redraw the whole screen - pagesIdentical = true; - } else { - draw_foreground_locked(); // Draw only the progress bar and overlays - } - gr_flip(); + if (show_text || !pagesIdentical) { + draw_screen_locked(); // Must redraw the whole screen + pagesIdentical = true; + } else { + draw_foreground_locked(); // Draw only the progress bar and overlays + } + gr_flip(); } // Keeps the progress bar updated, even when the process is otherwise busy. void* ScreenRecoveryUI::ProgressThreadStartRoutine(void* data) { - reinterpret_cast(data)->ProgressThreadLoop(); - return nullptr; + reinterpret_cast(data)->ProgressThreadLoop(); + return nullptr; } void ScreenRecoveryUI::ProgressThreadLoop() { - double interval = 1.0 / animation_fps; - while (true) { - double start = now(); - pthread_mutex_lock(&updateMutex); - - bool redraw = false; - - // update the installation animation, if active - // skip this if we have a text overlay (too expensive to update) - if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && !show_text) { - if (!intro_done) { - if (current_frame == intro_frames - 1) { - intro_done = true; - current_frame = 0; - } else { - ++current_frame; - } - } else { - current_frame = (current_frame + 1) % loop_frames; - } - - redraw = true; - } + double interval = 1.0 / animation_fps; + while (true) { + double start = now(); + pthread_mutex_lock(&updateMutex); - // move the progress bar forward on timed intervals, if configured - int duration = progressScopeDuration; - if (progressBarType == DETERMINATE && duration > 0) { - double elapsed = now() - progressScopeTime; - float p = 1.0 * elapsed / duration; - if (p > 1.0) p = 1.0; - if (p > progress) { - progress = p; - redraw = true; - } + bool redraw = false; + + // update the installation animation, if active + // skip this if we have a text overlay (too expensive to update) + if ((currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) && !show_text) { + if (!intro_done) { + if (current_frame == intro_frames - 1) { + intro_done = true; + current_frame = 0; + } else { + ++current_frame; } + } else { + current_frame = (current_frame + 1) % loop_frames; + } - if (redraw) update_progress_locked(); + redraw = true; + } - pthread_mutex_unlock(&updateMutex); - double end = now(); - // minimum of 20ms delay between frames - double delay = interval - (end-start); - if (delay < 0.02) delay = 0.02; - usleep(static_cast(delay * 1000000)); + // move the progress bar forward on timed intervals, if configured + int duration = progressScopeDuration; + if (progressBarType == DETERMINATE && duration > 0) { + double elapsed = now() - progressScopeTime; + float p = 1.0 * elapsed / duration; + if (p > 1.0) p = 1.0; + if (p > progress) { + progress = p; + redraw = true; + } } + + if (redraw) update_progress_locked(); + + pthread_mutex_unlock(&updateMutex); + double end = now(); + // minimum of 20ms delay between frames + double delay = interval - (end - start); + if (delay < 0.02) delay = 0.02; + usleep(static_cast(delay * 1000000)); + } } void ScreenRecoveryUI::LoadBitmap(const char* filename, GRSurface** surface) { - int result = res_create_display_surface(filename, surface); - if (result < 0) { - LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")"; - } + int result = res_create_display_surface(filename, surface); + if (result < 0) { + LOG(ERROR) << "couldn't load bitmap " << filename << " (error " << result << ")"; + } } void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** surface) { @@ -433,22 +437,22 @@ void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, GRSurface** sur } static char** Alloc2d(size_t rows, size_t cols) { - char** result = new char*[rows]; - for (size_t i = 0; i < rows; ++i) { - result[i] = new char[cols]; - memset(result[i], 0, cols); - } - return result; + char** result = new char*[rows]; + for (size_t i = 0; i < rows; ++i) { + result[i] = new char[cols]; + memset(result[i], 0, cols); + } + return result; } // Choose the right background string to display during update. void ScreenRecoveryUI::SetSystemUpdateText(bool security_update) { - if (security_update) { - LoadLocalizedBitmap("installing_security_text", &installing_text); - } else { - LoadLocalizedBitmap("installing_text", &installing_text); - } - Redraw(); + if (security_update) { + LoadLocalizedBitmap("installing_security_text", &installing_text); + } else { + LoadLocalizedBitmap("installing_text", &installing_text); + } + Redraw(); } bool ScreenRecoveryUI::InitTextParams() { @@ -504,309 +508,309 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { } void ScreenRecoveryUI::LoadAnimation() { - std::unique_ptr dir(opendir("/res/images"), closedir); - dirent* de; - std::vector intro_frame_names; - std::vector loop_frame_names; - - while ((de = readdir(dir.get())) != nullptr) { - int value, num_chars; - if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) { - intro_frame_names.emplace_back(de->d_name, num_chars); - } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) { - loop_frame_names.emplace_back(de->d_name, num_chars); - } + std::unique_ptr dir(opendir("/res/images"), closedir); + dirent* de; + std::vector intro_frame_names; + std::vector loop_frame_names; + + while ((de = readdir(dir.get())) != nullptr) { + int value, num_chars; + if (sscanf(de->d_name, "intro%d%n.png", &value, &num_chars) == 1) { + intro_frame_names.emplace_back(de->d_name, num_chars); + } else if (sscanf(de->d_name, "loop%d%n.png", &value, &num_chars) == 1) { + loop_frame_names.emplace_back(de->d_name, num_chars); } + } - intro_frames = intro_frame_names.size(); - loop_frames = loop_frame_names.size(); + intro_frames = intro_frame_names.size(); + loop_frames = loop_frame_names.size(); - // It's okay to not have an intro. - if (intro_frames == 0) intro_done = true; - // But you must have an animation. - if (loop_frames == 0) abort(); + // It's okay to not have an intro. + if (intro_frames == 0) intro_done = true; + // But you must have an animation. + if (loop_frames == 0) abort(); - std::sort(intro_frame_names.begin(), intro_frame_names.end()); - std::sort(loop_frame_names.begin(), loop_frame_names.end()); + std::sort(intro_frame_names.begin(), intro_frame_names.end()); + std::sort(loop_frame_names.begin(), loop_frame_names.end()); - introFrames = new GRSurface*[intro_frames]; - for (size_t i = 0; i < intro_frames; i++) { - LoadBitmap(intro_frame_names.at(i).c_str(), &introFrames[i]); - } + introFrames = new GRSurface*[intro_frames]; + for (size_t i = 0; i < intro_frames; i++) { + LoadBitmap(intro_frame_names.at(i).c_str(), &introFrames[i]); + } - loopFrames = new GRSurface*[loop_frames]; - for (size_t i = 0; i < loop_frames; i++) { - LoadBitmap(loop_frame_names.at(i).c_str(), &loopFrames[i]); - } + loopFrames = new GRSurface*[loop_frames]; + for (size_t i = 0; i < loop_frames; i++) { + LoadBitmap(loop_frame_names.at(i).c_str(), &loopFrames[i]); + } } void ScreenRecoveryUI::SetBackground(Icon icon) { - pthread_mutex_lock(&updateMutex); + pthread_mutex_lock(&updateMutex); - currentIcon = icon; - update_screen_locked(); + currentIcon = icon; + update_screen_locked(); - pthread_mutex_unlock(&updateMutex); + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::SetProgressType(ProgressType type) { - pthread_mutex_lock(&updateMutex); - if (progressBarType != type) { - progressBarType = type; - } - progressScopeStart = 0; - progressScopeSize = 0; - progress = 0; - update_progress_locked(); - pthread_mutex_unlock(&updateMutex); + pthread_mutex_lock(&updateMutex); + if (progressBarType != type) { + progressBarType = type; + } + progressScopeStart = 0; + progressScopeSize = 0; + progress = 0; + update_progress_locked(); + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::ShowProgress(float portion, float seconds) { - pthread_mutex_lock(&updateMutex); - progressBarType = DETERMINATE; - progressScopeStart += progressScopeSize; - progressScopeSize = portion; - progressScopeTime = now(); - progressScopeDuration = seconds; - progress = 0; - update_progress_locked(); - pthread_mutex_unlock(&updateMutex); + pthread_mutex_lock(&updateMutex); + progressBarType = DETERMINATE; + progressScopeStart += progressScopeSize; + progressScopeSize = portion; + progressScopeTime = now(); + progressScopeDuration = seconds; + progress = 0; + update_progress_locked(); + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::SetProgress(float fraction) { - pthread_mutex_lock(&updateMutex); - if (fraction < 0.0) fraction = 0.0; - if (fraction > 1.0) fraction = 1.0; - if (progressBarType == DETERMINATE && fraction > progress) { - // Skip updates that aren't visibly different. - int width = gr_get_width(progressBarEmpty); - float scale = width * progressScopeSize; - if ((int) (progress * scale) != (int) (fraction * scale)) { - progress = fraction; - update_progress_locked(); - } + pthread_mutex_lock(&updateMutex); + if (fraction < 0.0) fraction = 0.0; + if (fraction > 1.0) fraction = 1.0; + if (progressBarType == DETERMINATE && fraction > progress) { + // Skip updates that aren't visibly different. + int width = gr_get_width(progressBarEmpty); + float scale = width * progressScopeSize; + if ((int)(progress * scale) != (int)(fraction * scale)) { + progress = fraction; + update_progress_locked(); } - pthread_mutex_unlock(&updateMutex); + } + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::SetStage(int current, int max) { - pthread_mutex_lock(&updateMutex); - stage = current; - max_stage = max; - pthread_mutex_unlock(&updateMutex); + pthread_mutex_lock(&updateMutex); + stage = current; + max_stage = max; + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) { - std::string str; - android::base::StringAppendV(&str, fmt, ap); + std::string str; + android::base::StringAppendV(&str, fmt, ap); - if (copy_to_stdout) { - fputs(str.c_str(), stdout); - } + if (copy_to_stdout) { + fputs(str.c_str(), stdout); + } - pthread_mutex_lock(&updateMutex); - if (text_rows_ > 0 && text_cols_ > 0) { - for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) { - if (*ptr == '\n' || text_col_ >= text_cols_) { - text_[text_row_][text_col_] = '\0'; - text_col_ = 0; - text_row_ = (text_row_ + 1) % text_rows_; - if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; - } - if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; - } + pthread_mutex_lock(&updateMutex); + if (text_rows_ > 0 && text_cols_ > 0) { + for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) { + if (*ptr == '\n' || text_col_ >= text_cols_) { text_[text_row_][text_col_] = '\0'; - update_screen_locked(); + text_col_ = 0; + text_row_ = (text_row_ + 1) % text_rows_; + if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; + } + if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; } - pthread_mutex_unlock(&updateMutex); + text_[text_row_][text_col_] = '\0'; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::Print(const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - PrintV(fmt, true, ap); - va_end(ap); + va_list ap; + va_start(ap, fmt); + PrintV(fmt, true, ap); + va_end(ap); } void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - PrintV(fmt, false, ap); - va_end(ap); + va_list ap; + va_start(ap, fmt); + PrintV(fmt, false, ap); + va_end(ap); } void ScreenRecoveryUI::PutChar(char ch) { - pthread_mutex_lock(&updateMutex); - if (ch != '\n') text_[text_row_][text_col_++] = ch; - if (ch == '\n' || text_col_ >= text_cols_) { - text_col_ = 0; - ++text_row_; + pthread_mutex_lock(&updateMutex); + if (ch != '\n') text_[text_row_][text_col_++] = ch; + if (ch == '\n' || text_col_ >= text_cols_) { + text_col_ = 0; + ++text_row_; - if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; - } - pthread_mutex_unlock(&updateMutex); + if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; + } + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::ClearText() { - pthread_mutex_lock(&updateMutex); - text_col_ = 0; - text_row_ = 0; - text_top_ = 1; - for (size_t i = 0; i < text_rows_; ++i) { - memset(text_[i], 0, text_cols_ + 1); - } - pthread_mutex_unlock(&updateMutex); + pthread_mutex_lock(&updateMutex); + text_col_ = 0; + text_row_ = 0; + text_top_ = 1; + for (size_t i = 0; i < text_rows_; ++i) { + memset(text_[i], 0, text_cols_ + 1); + } + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::ShowFile(FILE* fp) { - std::vector offsets; - offsets.push_back(ftello(fp)); - ClearText(); - - struct stat sb; - fstat(fileno(fp), &sb); - - bool show_prompt = false; - while (true) { - if (show_prompt) { - PrintOnScreenOnly("--(%d%% of %d bytes)--", - static_cast(100 * (double(ftello(fp)) / double(sb.st_size))), - static_cast(sb.st_size)); - Redraw(); - while (show_prompt) { - show_prompt = false; - int key = WaitKey(); - if (key == KEY_POWER || key == KEY_ENTER) { - return; - } else if (key == KEY_UP || key == KEY_VOLUMEUP) { - if (offsets.size() <= 1) { - show_prompt = true; - } else { - offsets.pop_back(); - fseek(fp, offsets.back(), SEEK_SET); - } - } else { - if (feof(fp)) { - return; - } - offsets.push_back(ftello(fp)); - } - } - ClearText(); - } - - int ch = getc(fp); - if (ch == EOF) { - while (text_row_ < text_rows_ - 1) PutChar('\n'); + std::vector offsets; + offsets.push_back(ftello(fp)); + ClearText(); + + struct stat sb; + fstat(fileno(fp), &sb); + + bool show_prompt = false; + while (true) { + if (show_prompt) { + PrintOnScreenOnly("--(%d%% of %d bytes)--", + static_cast(100 * (double(ftello(fp)) / double(sb.st_size))), + static_cast(sb.st_size)); + Redraw(); + while (show_prompt) { + show_prompt = false; + int key = WaitKey(); + if (key == KEY_POWER || key == KEY_ENTER) { + return; + } else if (key == KEY_UP || key == KEY_VOLUMEUP) { + if (offsets.size() <= 1) { show_prompt = true; + } else { + offsets.pop_back(); + fseek(fp, offsets.back(), SEEK_SET); + } } else { - PutChar(ch); - if (text_col_ == 0 && text_row_ >= text_rows_ - 1) { - show_prompt = true; - } + if (feof(fp)) { + return; + } + offsets.push_back(ftello(fp)); } + } + ClearText(); } + + int ch = getc(fp); + if (ch == EOF) { + while (text_row_ < text_rows_ - 1) PutChar('\n'); + show_prompt = true; + } else { + PutChar(ch); + if (text_col_ == 0 && text_row_ >= text_rows_ - 1) { + show_prompt = true; + } + } + } } void ScreenRecoveryUI::ShowFile(const char* filename) { - FILE* fp = fopen_path(filename, "re"); - if (fp == nullptr) { - Print(" Unable to open %s: %s\n", filename, strerror(errno)); - return; - } + FILE* fp = fopen_path(filename, "re"); + if (fp == nullptr) { + Print(" Unable to open %s: %s\n", filename, strerror(errno)); + return; + } - char** old_text = text_; - size_t old_text_col = text_col_; - size_t old_text_row = text_row_; - size_t old_text_top = text_top_; + char** old_text = text_; + size_t old_text_col = text_col_; + size_t old_text_row = text_row_; + size_t old_text_top = text_top_; - // Swap in the alternate screen and clear it. - text_ = file_viewer_text_; - ClearText(); + // Swap in the alternate screen and clear it. + text_ = file_viewer_text_; + ClearText(); - ShowFile(fp); - fclose(fp); + ShowFile(fp); + fclose(fp); - text_ = old_text; - text_col_ = old_text_col; - text_row_ = old_text_row; - text_top_ = old_text_top; + text_ = old_text; + text_col_ = old_text_col; + text_row_ = old_text_row; + text_top_ = old_text_top; } -void ScreenRecoveryUI::StartMenu(const char* const * headers, const char* const * items, +void ScreenRecoveryUI::StartMenu(const char* const* headers, const char* const* items, int initial_selection) { - pthread_mutex_lock(&updateMutex); - if (text_rows_ > 0 && text_cols_ > 0) { - menu_headers_ = headers; - size_t i = 0; - for (; i < text_rows_ && items[i] != nullptr; ++i) { - strncpy(menu_[i], items[i], text_cols_ - 1); - menu_[i][text_cols_ - 1] = '\0'; - } - menu_items = i; - show_menu = true; - menu_sel = initial_selection; - update_screen_locked(); + pthread_mutex_lock(&updateMutex); + if (text_rows_ > 0 && text_cols_ > 0) { + menu_headers_ = headers; + size_t i = 0; + for (; i < text_rows_ && items[i] != nullptr; ++i) { + strncpy(menu_[i], items[i], text_cols_ - 1); + menu_[i][text_cols_ - 1] = '\0'; } - pthread_mutex_unlock(&updateMutex); + menu_items = i; + show_menu = true; + menu_sel = initial_selection; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); } int ScreenRecoveryUI::SelectMenu(int sel) { - pthread_mutex_lock(&updateMutex); - if (show_menu) { - int old_sel = menu_sel; - menu_sel = sel; + pthread_mutex_lock(&updateMutex); + if (show_menu) { + int old_sel = menu_sel; + menu_sel = sel; - // Wrap at top and bottom. - if (menu_sel < 0) menu_sel = menu_items - 1; - if (menu_sel >= menu_items) menu_sel = 0; + // Wrap at top and bottom. + if (menu_sel < 0) menu_sel = menu_items - 1; + if (menu_sel >= menu_items) menu_sel = 0; - sel = menu_sel; - if (menu_sel != old_sel) update_screen_locked(); - } - pthread_mutex_unlock(&updateMutex); - return sel; + sel = menu_sel; + if (menu_sel != old_sel) update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); + return sel; } void ScreenRecoveryUI::EndMenu() { - pthread_mutex_lock(&updateMutex); - if (show_menu && text_rows_ > 0 && text_cols_ > 0) { - show_menu = false; - update_screen_locked(); - } - pthread_mutex_unlock(&updateMutex); + pthread_mutex_lock(&updateMutex); + if (show_menu && text_rows_ > 0 && text_cols_ > 0) { + show_menu = false; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); } bool ScreenRecoveryUI::IsTextVisible() { - pthread_mutex_lock(&updateMutex); - int visible = show_text; - pthread_mutex_unlock(&updateMutex); - return visible; + pthread_mutex_lock(&updateMutex); + int visible = show_text; + pthread_mutex_unlock(&updateMutex); + return visible; } bool ScreenRecoveryUI::WasTextEverVisible() { - pthread_mutex_lock(&updateMutex); - int ever_visible = show_text_ever; - pthread_mutex_unlock(&updateMutex); - return ever_visible; + pthread_mutex_lock(&updateMutex); + int ever_visible = show_text_ever; + pthread_mutex_unlock(&updateMutex); + return ever_visible; } void ScreenRecoveryUI::ShowText(bool visible) { - pthread_mutex_lock(&updateMutex); - show_text = visible; - if (show_text) show_text_ever = true; - update_screen_locked(); - pthread_mutex_unlock(&updateMutex); + pthread_mutex_lock(&updateMutex); + show_text = visible; + if (show_text) show_text_ever = true; + update_screen_locked(); + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::Redraw() { - pthread_mutex_lock(&updateMutex); - update_screen_locked(); - pthread_mutex_unlock(&updateMutex); + pthread_mutex_lock(&updateMutex); + update_screen_locked(); + pthread_mutex_unlock(&updateMutex); } void ScreenRecoveryUI::KeyLongPress(int) { - // Redraw so that if we're in the menu, the highlight - // will change color to indicate a successful long press. - Redraw(); + // Redraw so that if we're in the menu, the highlight + // will change color to indicate a successful long press. + Redraw(); } diff --git a/screen_ui.h b/screen_ui.h index 2500575ca..2a3d8c30b 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -30,152 +30,158 @@ struct GRSurface; // Implementation of RecoveryUI appropriate for devices with a screen // (shows an icon + a progress bar, text logging, menu, etc.) class ScreenRecoveryUI : public RecoveryUI { - public: - ScreenRecoveryUI(); + public: + ScreenRecoveryUI(); - bool Init(const std::string& locale) override; + bool Init(const std::string& locale) override; - // overall recovery state ("background image") - void SetBackground(Icon icon); - void SetSystemUpdateText(bool security_update); + // overall recovery state ("background image") + void SetBackground(Icon icon); + void SetSystemUpdateText(bool security_update); - // progress indicator - void SetProgressType(ProgressType type) override; - void ShowProgress(float portion, float seconds) override; - void SetProgress(float fraction) override; + // progress indicator + void SetProgressType(ProgressType type) override; + void ShowProgress(float portion, float seconds) override; + void SetProgress(float fraction) override; - void SetStage(int current, int max) override; + void SetStage(int current, int max) override; - // text log - void ShowText(bool visible) override; - bool IsTextVisible() override; - bool WasTextEverVisible() override; + // text log + void ShowText(bool visible) override; + bool IsTextVisible() override; + bool WasTextEverVisible() override; - // printing messages - void Print(const char* fmt, ...) __printflike(2, 3); - void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3); - void ShowFile(const char* filename); + // printing messages + void Print(const char* fmt, ...) __printflike(2, 3); + void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3); + void ShowFile(const char* filename); - // menu display - void StartMenu(const char* const * headers, const char* const * items, - int initial_selection); - int SelectMenu(int sel); - void EndMenu(); + // menu display + void StartMenu(const char* const* headers, const char* const* items, int initial_selection); + int SelectMenu(int sel); + void EndMenu(); - void KeyLongPress(int); + void KeyLongPress(int); - void Redraw(); + void Redraw(); - enum UIElement { - HEADER, MENU, MENU_SEL_BG, MENU_SEL_BG_ACTIVE, MENU_SEL_FG, LOG, TEXT_FILL, INFO - }; - void SetColor(UIElement e); + enum UIElement { + HEADER, + MENU, + MENU_SEL_BG, + MENU_SEL_BG_ACTIVE, + MENU_SEL_FG, + LOG, + TEXT_FILL, + INFO + }; + void SetColor(UIElement e); - protected: - // The margin that we don't want to use for showing texts (e.g. round screen, or screen with - // rounded corners). - const int kMarginWidth; - const int kMarginHeight; + protected: + // The margin that we don't want to use for showing texts (e.g. round screen, or screen with + // rounded corners). + const int kMarginWidth; + const int kMarginHeight; - // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. - const float density_; + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. + const float density_; - Icon currentIcon; + Icon currentIcon; - // The layout to use. - int layout_; + // The layout to use. + int layout_; - GRSurface* error_icon; + GRSurface* error_icon; - GRSurface* erasing_text; - GRSurface* error_text; - GRSurface* installing_text; - GRSurface* no_command_text; + GRSurface* erasing_text; + GRSurface* error_text; + GRSurface* installing_text; + GRSurface* no_command_text; - GRSurface** introFrames; - GRSurface** loopFrames; + GRSurface** introFrames; + GRSurface** loopFrames; - GRSurface* progressBarEmpty; - GRSurface* progressBarFill; - GRSurface* stageMarkerEmpty; - GRSurface* stageMarkerFill; + GRSurface* progressBarEmpty; + GRSurface* progressBarFill; + GRSurface* stageMarkerEmpty; + GRSurface* stageMarkerFill; - ProgressType progressBarType; + ProgressType progressBarType; - float progressScopeStart, progressScopeSize, progress; - double progressScopeTime, progressScopeDuration; + float progressScopeStart, progressScopeSize, progress; + double progressScopeTime, progressScopeDuration; - // true when both graphics pages are the same (except for the progress bar). - bool pagesIdentical; + // true when both graphics pages are the same (except for the progress bar). + bool pagesIdentical; - size_t text_cols_, text_rows_; + size_t text_cols_, text_rows_; - // Log text overlay, displayed when a magic key is pressed. - char** text_; - size_t text_col_, text_row_, text_top_; + // Log text overlay, displayed when a magic key is pressed. + char** text_; + size_t text_col_, text_row_, text_top_; - bool show_text; - bool show_text_ever; // has show_text ever been true? + bool show_text; + bool show_text_ever; // has show_text ever been true? - char** menu_; - const char* const* menu_headers_; - bool show_menu; - int menu_items, menu_sel; + char** menu_; + const char* const* menu_headers_; + bool show_menu; + int menu_items, menu_sel; - // An alternate text screen, swapped with 'text_' when we're viewing a log file. - char** file_viewer_text_; + // An alternate text screen, swapped with 'text_' when we're viewing a log file. + char** file_viewer_text_; - pthread_t progress_thread_; + pthread_t progress_thread_; - // Number of intro frames and loop frames in the animation. - size_t intro_frames; - size_t loop_frames; + // Number of intro frames and loop frames in the animation. + size_t intro_frames; + size_t loop_frames; - size_t current_frame; - bool intro_done; + size_t current_frame; + bool intro_done; - // Number of frames per sec (default: 30) for both parts of the animation. - int animation_fps; + // Number of frames per sec (default: 30) for both parts of the animation. + int animation_fps; - int stage, max_stage; + int stage, max_stage; - int char_width_; - int char_height_; + int char_width_; + int char_height_; - pthread_mutex_t updateMutex; + pthread_mutex_t updateMutex; - virtual bool InitTextParams(); + virtual bool InitTextParams(); - virtual void draw_background_locked(); - virtual void draw_foreground_locked(); - virtual void draw_screen_locked(); - virtual void update_screen_locked(); - virtual void update_progress_locked(); + virtual void draw_background_locked(); + virtual void draw_foreground_locked(); + virtual void draw_screen_locked(); + virtual void update_screen_locked(); + virtual void update_progress_locked(); - GRSurface* GetCurrentFrame(); - GRSurface* GetCurrentText(); + GRSurface* GetCurrentFrame(); + GRSurface* GetCurrentText(); - static void* ProgressThreadStartRoutine(void* data); - void ProgressThreadLoop(); + static void* ProgressThreadStartRoutine(void* data); + void ProgressThreadLoop(); - virtual void ShowFile(FILE*); - virtual void PrintV(const char*, bool, va_list); - void PutChar(char); - void ClearText(); + virtual void ShowFile(FILE*); + virtual void PrintV(const char*, bool, va_list); + void PutChar(char); + void ClearText(); - void LoadAnimation(); - void LoadBitmap(const char* filename, GRSurface** surface); - void LoadLocalizedBitmap(const char* filename, GRSurface** surface); + void LoadAnimation(); + void LoadBitmap(const char* filename, GRSurface** surface); + void LoadLocalizedBitmap(const char* filename, GRSurface** surface); - int PixelsFromDp(int dp) const; - virtual int GetAnimationBaseline(); - virtual int GetProgressBaseline(); - virtual int GetTextBaseline(); + int PixelsFromDp(int dp) const; + virtual int GetAnimationBaseline(); + virtual int GetProgressBaseline(); + virtual int GetTextBaseline(); - virtual void DrawHorizontalRule(int* y); - virtual void DrawHighlightBar(int x, int y, int width, int height) const; - virtual void DrawTextLine(int x, int* y, const char* line, bool bold) const; - void DrawTextLines(int x, int* y, const char* const* lines) const; + virtual void DrawHorizontalRule(int* y); + virtual void DrawHighlightBar(int x, int y, int width, int height) const; + virtual void DrawTextLine(int x, int* y, const char* line, bool bold) const; + void DrawTextLines(int x, int* y, const char* const* lines) const; }; #endif // RECOVERY_UI_H diff --git a/ui.cpp b/ui.cpp index cad744930..30b42a19a 100644 --- a/ui.cpp +++ b/ui.cpp @@ -71,23 +71,23 @@ RecoveryUI::RecoveryUI() } void RecoveryUI::OnKeyDetected(int key_code) { - if (key_code == KEY_POWER) { - has_power_key = true; - } else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) { - has_down_key = true; - } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) { - has_up_key = true; - } + if (key_code == KEY_POWER) { + has_power_key = true; + } else if (key_code == KEY_DOWN || key_code == KEY_VOLUMEDOWN) { + has_down_key = true; + } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) { + has_up_key = true; + } } // Reads input events, handles special hot keys, and adds to the key queue. static void* InputThreadLoop(void*) { - while (true) { - if (!ev_wait(-1)) { - ev_dispatch(); - } + while (true) { + if (!ev_wait(-1)) { + ev_dispatch(); } - return nullptr; + } + return nullptr; } bool RecoveryUI::InitScreensaver() { @@ -141,39 +141,39 @@ bool RecoveryUI::Init(const std::string& locale) { } int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { - struct input_event ev; - if (ev_get_input(fd, epevents, &ev) == -1) { - return -1; - } + struct input_event ev; + if (ev_get_input(fd, epevents, &ev) == -1) { + return -1; + } - if (ev.type == EV_SYN) { - return 0; - } else if (ev.type == EV_REL) { - if (ev.code == REL_Y) { - // accumulate the up or down motion reported by - // the trackball. When it exceeds a threshold - // (positive or negative), fake an up/down - // key event. - rel_sum += ev.value; - if (rel_sum > 3) { - ProcessKey(KEY_DOWN, 1); // press down key - ProcessKey(KEY_DOWN, 0); // and release it - rel_sum = 0; - } else if (rel_sum < -3) { - ProcessKey(KEY_UP, 1); // press up key - ProcessKey(KEY_UP, 0); // and release it - rel_sum = 0; - } - } - } else { + if (ev.type == EV_SYN) { + return 0; + } else if (ev.type == EV_REL) { + if (ev.code == REL_Y) { + // accumulate the up or down motion reported by + // the trackball. When it exceeds a threshold + // (positive or negative), fake an up/down + // key event. + rel_sum += ev.value; + if (rel_sum > 3) { + ProcessKey(KEY_DOWN, 1); // press down key + ProcessKey(KEY_DOWN, 0); // and release it rel_sum = 0; + } else if (rel_sum < -3) { + ProcessKey(KEY_UP, 1); // press up key + ProcessKey(KEY_UP, 0); // and release it + rel_sum = 0; + } } + } else { + rel_sum = 0; + } - if (ev.type == EV_KEY && ev.code <= KEY_MAX) { - ProcessKey(ev.code, ev.value); - } + if (ev.type == EV_KEY && ev.code <= KEY_MAX) { + ProcessKey(ev.code, ev.value); + } - return 0; + return 0; } // Process a key-up or -down event. A key is "registered" when it is @@ -189,82 +189,84 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { // // updown == 1 for key down events; 0 for key up events void RecoveryUI::ProcessKey(int key_code, int updown) { - bool register_key = false; - bool long_press = false; - bool reboot_enabled; + bool register_key = false; + bool long_press = false; + bool reboot_enabled; - pthread_mutex_lock(&key_queue_mutex); - key_pressed[key_code] = updown; - if (updown) { - ++key_down_count; - key_last_down = key_code; - key_long_press = false; - key_timer_t* info = new key_timer_t; - info->ui = this; - info->key_code = key_code; - info->count = key_down_count; - pthread_t thread; - pthread_create(&thread, nullptr, &RecoveryUI::time_key_helper, info); - pthread_detach(thread); - } else { - if (key_last_down == key_code) { - long_press = key_long_press; - register_key = true; - } - key_last_down = -1; + pthread_mutex_lock(&key_queue_mutex); + key_pressed[key_code] = updown; + if (updown) { + ++key_down_count; + key_last_down = key_code; + key_long_press = false; + key_timer_t* info = new key_timer_t; + info->ui = this; + info->key_code = key_code; + info->count = key_down_count; + pthread_t thread; + pthread_create(&thread, nullptr, &RecoveryUI::time_key_helper, info); + pthread_detach(thread); + } else { + if (key_last_down == key_code) { + long_press = key_long_press; + register_key = true; } - reboot_enabled = enable_reboot; - pthread_mutex_unlock(&key_queue_mutex); + key_last_down = -1; + } + reboot_enabled = enable_reboot; + pthread_mutex_unlock(&key_queue_mutex); + + if (register_key) { + switch (CheckKey(key_code, long_press)) { + case RecoveryUI::IGNORE: + break; + + case RecoveryUI::TOGGLE: + ShowText(!IsTextVisible()); + break; - if (register_key) { - switch (CheckKey(key_code, long_press)) { - case RecoveryUI::IGNORE: - break; - - case RecoveryUI::TOGGLE: - ShowText(!IsTextVisible()); - break; - - case RecoveryUI::REBOOT: - if (reboot_enabled) { - reboot("reboot,"); - while (true) { pause(); } - } - break; - - case RecoveryUI::ENQUEUE: - EnqueueKey(key_code); - break; + case RecoveryUI::REBOOT: + if (reboot_enabled) { + reboot("reboot,"); + while (true) { + pause(); + } } + break; + + case RecoveryUI::ENQUEUE: + EnqueueKey(key_code); + break; } + } } void* RecoveryUI::time_key_helper(void* cookie) { - key_timer_t* info = static_cast(cookie); - info->ui->time_key(info->key_code, info->count); - delete info; - return nullptr; + key_timer_t* info = static_cast(cookie); + info->ui->time_key(info->key_code, info->count); + delete info; + return nullptr; } void RecoveryUI::time_key(int key_code, int count) { - usleep(750000); // 750 ms == "long" - bool long_press = false; - pthread_mutex_lock(&key_queue_mutex); - if (key_last_down == key_code && key_down_count == count) { - long_press = key_long_press = true; - } - pthread_mutex_unlock(&key_queue_mutex); - if (long_press) KeyLongPress(key_code); + usleep(750000); // 750 ms == "long" + bool long_press = false; + pthread_mutex_lock(&key_queue_mutex); + if (key_last_down == key_code && key_down_count == count) { + long_press = key_long_press = true; + } + pthread_mutex_unlock(&key_queue_mutex); + if (long_press) KeyLongPress(key_code); } void RecoveryUI::EnqueueKey(int key_code) { - pthread_mutex_lock(&key_queue_mutex); - const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); - if (key_queue_len < queue_max) { - key_queue[key_queue_len++] = key_code; - pthread_cond_signal(&key_queue_cond); - } - pthread_mutex_unlock(&key_queue_mutex); + pthread_mutex_lock(&key_queue_mutex); + const int queue_max = sizeof(key_queue) / sizeof(key_queue[0]); + if (key_queue_len < queue_max) { + key_queue[key_queue_len++] = key_code; + pthread_cond_signal(&key_queue_cond); + } + pthread_mutex_unlock(&key_queue_mutex); } int RecoveryUI::WaitKey() { @@ -330,98 +332,96 @@ int RecoveryUI::WaitKey() { } bool RecoveryUI::IsUsbConnected() { - int fd = open("/sys/class/android_usb/android0/state", O_RDONLY); - if (fd < 0) { - printf("failed to open /sys/class/android_usb/android0/state: %s\n", - strerror(errno)); - return 0; - } + int fd = open("/sys/class/android_usb/android0/state", O_RDONLY); + if (fd < 0) { + printf("failed to open /sys/class/android_usb/android0/state: %s\n", strerror(errno)); + return 0; + } - char buf; - // USB is connected if android_usb state is CONNECTED or CONFIGURED. - int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C'); - if (close(fd) < 0) { - printf("failed to close /sys/class/android_usb/android0/state: %s\n", - strerror(errno)); - } - return connected; + char buf; + // USB is connected if android_usb state is CONNECTED or CONFIGURED. + int connected = (TEMP_FAILURE_RETRY(read(fd, &buf, 1)) == 1) && (buf == 'C'); + if (close(fd) < 0) { + printf("failed to close /sys/class/android_usb/android0/state: %s\n", strerror(errno)); + } + return connected; } bool RecoveryUI::IsKeyPressed(int key) { - pthread_mutex_lock(&key_queue_mutex); - int pressed = key_pressed[key]; - pthread_mutex_unlock(&key_queue_mutex); - return pressed; + pthread_mutex_lock(&key_queue_mutex); + int pressed = key_pressed[key]; + pthread_mutex_unlock(&key_queue_mutex); + return pressed; } bool RecoveryUI::IsLongPress() { - pthread_mutex_lock(&key_queue_mutex); - bool result = key_long_press; - pthread_mutex_unlock(&key_queue_mutex); - return result; + pthread_mutex_lock(&key_queue_mutex); + bool result = key_long_press; + pthread_mutex_unlock(&key_queue_mutex); + return result; } bool RecoveryUI::HasThreeButtons() { - return has_power_key && has_up_key && has_down_key; + return has_power_key && has_up_key && has_down_key; } void RecoveryUI::FlushKeys() { - pthread_mutex_lock(&key_queue_mutex); - key_queue_len = 0; - pthread_mutex_unlock(&key_queue_mutex); + pthread_mutex_lock(&key_queue_mutex); + key_queue_len = 0; + pthread_mutex_unlock(&key_queue_mutex); } RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { - pthread_mutex_lock(&key_queue_mutex); - key_long_press = false; - pthread_mutex_unlock(&key_queue_mutex); + pthread_mutex_lock(&key_queue_mutex); + key_long_press = false; + pthread_mutex_unlock(&key_queue_mutex); - // If we have power and volume up keys, that chord is the signal to toggle the text display. - if (HasThreeButtons()) { - if (key == KEY_VOLUMEUP && IsKeyPressed(KEY_POWER)) { - return TOGGLE; - } - } else { - // Otherwise long press of any button toggles to the text display, - // and there's no way to toggle back (but that's pretty useless anyway). - if (is_long_press && !IsTextVisible()) { - return TOGGLE; - } + // If we have power and volume up keys, that chord is the signal to toggle the text display. + if (HasThreeButtons()) { + if (key == KEY_VOLUMEUP && IsKeyPressed(KEY_POWER)) { + return TOGGLE; + } + } else { + // Otherwise long press of any button toggles to the text display, + // and there's no way to toggle back (but that's pretty useless anyway). + if (is_long_press && !IsTextVisible()) { + return TOGGLE; + } - // Also, for button-limited devices, a long press is translated to KEY_ENTER. - if (is_long_press && IsTextVisible()) { - EnqueueKey(KEY_ENTER); - return IGNORE; - } + // Also, for button-limited devices, a long press is translated to KEY_ENTER. + if (is_long_press && IsTextVisible()) { + EnqueueKey(KEY_ENTER); + return IGNORE; } + } - // Press power seven times in a row to reboot. - if (key == KEY_POWER) { - pthread_mutex_lock(&key_queue_mutex); - bool reboot_enabled = enable_reboot; - pthread_mutex_unlock(&key_queue_mutex); + // Press power seven times in a row to reboot. + if (key == KEY_POWER) { + pthread_mutex_lock(&key_queue_mutex); + bool reboot_enabled = enable_reboot; + pthread_mutex_unlock(&key_queue_mutex); - if (reboot_enabled) { - ++consecutive_power_keys; - if (consecutive_power_keys >= 7) { - return REBOOT; - } - } - } else { - consecutive_power_keys = 0; + if (reboot_enabled) { + ++consecutive_power_keys; + if (consecutive_power_keys >= 7) { + return REBOOT; + } } + } else { + consecutive_power_keys = 0; + } - last_key = key; - return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE; + last_key = key; + return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE; } void RecoveryUI::KeyLongPress(int) { } void RecoveryUI::SetEnableReboot(bool enabled) { - pthread_mutex_lock(&key_queue_mutex); - enable_reboot = enabled; - pthread_mutex_unlock(&key_queue_mutex); + pthread_mutex_lock(&key_queue_mutex); + enable_reboot = enabled; + pthread_mutex_unlock(&key_queue_mutex); } void RecoveryUI::SetLocale(const std::string& new_locale) { diff --git a/ui.h b/ui.h index 823eb6574..ef63e1d70 100644 --- a/ui.h +++ b/ui.h @@ -25,163 +25,163 @@ // Abstract class for controlling the user interface during recovery. class RecoveryUI { - public: - RecoveryUI(); + public: + RecoveryUI(); - virtual ~RecoveryUI() { } + virtual ~RecoveryUI() {} - // Initialize the object; called before anything else. UI texts will be - // initialized according to the given locale. Returns true on success. - virtual bool Init(const std::string& locale); + // Initialize the object; called before anything else. UI texts will be + // initialized according to the given locale. Returns true on success. + virtual bool Init(const std::string& locale); - // Show a stage indicator. Call immediately after Init(). - virtual void SetStage(int current, int max) = 0; + // Show a stage indicator. Call immediately after Init(). + virtual void SetStage(int current, int max) = 0; - // Set the overall recovery state ("background image"). - enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR }; - virtual void SetBackground(Icon icon) = 0; - virtual void SetSystemUpdateText(bool security_update) = 0; + // Set the overall recovery state ("background image"). + enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR }; + virtual void SetBackground(Icon icon) = 0; + virtual void SetSystemUpdateText(bool security_update) = 0; - // --- progress indicator --- - enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE }; - virtual void SetProgressType(ProgressType determinate) = 0; - - // Show a progress bar and define the scope of the next operation: - // portion - fraction of the progress bar the next operation will use - // seconds - expected time interval (progress bar moves at this minimum rate) - virtual void ShowProgress(float portion, float seconds) = 0; - - // Set progress bar position (0.0 - 1.0 within the scope defined - // by the last call to ShowProgress). - virtual void SetProgress(float fraction) = 0; - - // --- text log --- - - virtual void ShowText(bool visible) = 0; - - virtual bool IsTextVisible() = 0; - - virtual bool WasTextEverVisible() = 0; - - // Write a message to the on-screen log (shown if the user has - // toggled on the text display). Print() will also dump the message - // to stdout / log file, while PrintOnScreenOnly() not. - virtual void Print(const char* fmt, ...) __printflike(2, 3) = 0; - virtual void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3) = 0; - - virtual void ShowFile(const char* filename) = 0; + // --- progress indicator --- + enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE }; + virtual void SetProgressType(ProgressType determinate) = 0; + + // Show a progress bar and define the scope of the next operation: + // portion - fraction of the progress bar the next operation will use + // seconds - expected time interval (progress bar moves at this minimum rate) + virtual void ShowProgress(float portion, float seconds) = 0; + + // Set progress bar position (0.0 - 1.0 within the scope defined + // by the last call to ShowProgress). + virtual void SetProgress(float fraction) = 0; + + // --- text log --- + + virtual void ShowText(bool visible) = 0; + + virtual bool IsTextVisible() = 0; + + virtual bool WasTextEverVisible() = 0; + + // Write a message to the on-screen log (shown if the user has + // toggled on the text display). Print() will also dump the message + // to stdout / log file, while PrintOnScreenOnly() not. + virtual void Print(const char* fmt, ...) __printflike(2, 3) = 0; + virtual void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3) = 0; + + virtual void ShowFile(const char* filename) = 0; - // --- key handling --- + // --- key handling --- - // Wait for a key and return it. May return -1 after timeout. - virtual int WaitKey(); + // Wait for a key and return it. May return -1 after timeout. + virtual int WaitKey(); - virtual bool IsKeyPressed(int key); - virtual bool IsLongPress(); + virtual bool IsKeyPressed(int key); + virtual bool IsLongPress(); - // Returns true if you have the volume up/down and power trio typical - // of phones and tablets, false otherwise. - virtual bool HasThreeButtons(); - - // Erase any queued-up keys. - virtual void FlushKeys(); - - // Called on each key press, even while operations are in progress. - // Return value indicates whether an immediate operation should be - // triggered (toggling the display, rebooting the device), or if - // the key should be enqueued for use by the main thread. - enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE }; - virtual KeyAction CheckKey(int key, bool is_long_press); - - // Called when a key is held down long enough to have been a - // long-press (but before the key is released). This means that - // if the key is eventually registered (released without any other - // keys being pressed in the meantime), CheckKey will be called with - // 'is_long_press' true. - virtual void KeyLongPress(int key); - - // Normally in recovery there's a key sequence that triggers - // immediate reboot of the device, regardless of what recovery is - // doing (with the default CheckKey implementation, it's pressing - // the power button 7 times in row). Call this to enable or - // disable that feature. It is enabled by default. - virtual void SetEnableReboot(bool enabled); - - // --- menu display --- - - // Display some header text followed by a menu of items, which appears - // at the top of the screen (in place of any scrolling ui_print() - // output, if necessary). - virtual void StartMenu(const char* const * headers, const char* const * items, - int initial_selection) = 0; - - // Set the menu highlight to the given index, wrapping if necessary. - // Returns the actual item selected. - virtual int SelectMenu(int sel) = 0; - - // End menu mode, resetting the text overlay so that ui_print() - // statements will be displayed. - virtual void EndMenu() = 0; - - protected: - void EnqueueKey(int key_code); - - // The locale that's used to show the rendered texts. - std::string locale_; - bool rtl_locale_; - - // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25% - // of the max_brightness). Because the absolute values may vary across devices. These two - // values can be configured via subclassing. Setting brightness_normal_ to 0 to disable - // screensaver. - unsigned int brightness_normal_; - unsigned int brightness_dimmed_; - - private: - // Key event input queue - pthread_mutex_t key_queue_mutex; - pthread_cond_t key_queue_cond; - int key_queue[256], key_queue_len; - char key_pressed[KEY_MAX + 1]; // under key_queue_mutex - int key_last_down; // under key_queue_mutex - bool key_long_press; // under key_queue_mutex - int key_down_count; // under key_queue_mutex - bool enable_reboot; // under key_queue_mutex - int rel_sum; - - int consecutive_power_keys; - int last_key; - - bool has_power_key; - bool has_up_key; - bool has_down_key; - - struct key_timer_t { - RecoveryUI* ui; - int key_code; - int count; - }; - - pthread_t input_thread_; - - void OnKeyDetected(int key_code); - int OnInputEvent(int fd, uint32_t epevents); - void ProcessKey(int key_code, int updown); - - bool IsUsbConnected(); - - static void* time_key_helper(void* cookie); - void time_key(int key_code, int count); - - void SetLocale(const std::string&); - - enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF }; - ScreensaverState screensaver_state_; - // The following two contain the absolute values computed from brightness_normal_ and - // brightness_dimmed_ respectively. - unsigned int brightness_normal_value_; - unsigned int brightness_dimmed_value_; - bool InitScreensaver(); + // Returns true if you have the volume up/down and power trio typical + // of phones and tablets, false otherwise. + virtual bool HasThreeButtons(); + + // Erase any queued-up keys. + virtual void FlushKeys(); + + // Called on each key press, even while operations are in progress. + // Return value indicates whether an immediate operation should be + // triggered (toggling the display, rebooting the device), or if + // the key should be enqueued for use by the main thread. + enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE }; + virtual KeyAction CheckKey(int key, bool is_long_press); + + // Called when a key is held down long enough to have been a + // long-press (but before the key is released). This means that + // if the key is eventually registered (released without any other + // keys being pressed in the meantime), CheckKey will be called with + // 'is_long_press' true. + virtual void KeyLongPress(int key); + + // Normally in recovery there's a key sequence that triggers + // immediate reboot of the device, regardless of what recovery is + // doing (with the default CheckKey implementation, it's pressing + // the power button 7 times in row). Call this to enable or + // disable that feature. It is enabled by default. + virtual void SetEnableReboot(bool enabled); + + // --- menu display --- + + // Display some header text followed by a menu of items, which appears + // at the top of the screen (in place of any scrolling ui_print() + // output, if necessary). + virtual void StartMenu(const char* const* headers, const char* const* items, + int initial_selection) = 0; + + // Set the menu highlight to the given index, wrapping if necessary. + // Returns the actual item selected. + virtual int SelectMenu(int sel) = 0; + + // End menu mode, resetting the text overlay so that ui_print() + // statements will be displayed. + virtual void EndMenu() = 0; + + protected: + void EnqueueKey(int key_code); + + // The locale that's used to show the rendered texts. + std::string locale_; + bool rtl_locale_; + + // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25% + // of the max_brightness). Because the absolute values may vary across devices. These two + // values can be configured via subclassing. Setting brightness_normal_ to 0 to disable + // screensaver. + unsigned int brightness_normal_; + unsigned int brightness_dimmed_; + + private: + // Key event input queue + pthread_mutex_t key_queue_mutex; + pthread_cond_t key_queue_cond; + int key_queue[256], key_queue_len; + char key_pressed[KEY_MAX + 1]; // under key_queue_mutex + int key_last_down; // under key_queue_mutex + bool key_long_press; // under key_queue_mutex + int key_down_count; // under key_queue_mutex + bool enable_reboot; // under key_queue_mutex + int rel_sum; + + int consecutive_power_keys; + int last_key; + + bool has_power_key; + bool has_up_key; + bool has_down_key; + + struct key_timer_t { + RecoveryUI* ui; + int key_code; + int count; + }; + + pthread_t input_thread_; + + void OnKeyDetected(int key_code); + int OnInputEvent(int fd, uint32_t epevents); + void ProcessKey(int key_code, int updown); + + bool IsUsbConnected(); + + static void* time_key_helper(void* cookie); + void time_key(int key_code, int count); + + void SetLocale(const std::string&); + + enum class ScreensaverState { DISABLED, NORMAL, DIMMED, OFF }; + ScreensaverState screensaver_state_; + // The following two contain the absolute values computed from brightness_normal_ and + // brightness_dimmed_ respectively. + unsigned int brightness_normal_value_; + unsigned int brightness_dimmed_value_; + bool InitScreensaver(); }; #endif // RECOVERY_UI_H diff --git a/vr_ui.h b/vr_ui.h index 31ca4a61d..da2163402 100644 --- a/vr_ui.h +++ b/vr_ui.h @@ -20,17 +20,17 @@ #include "screen_ui.h" class VrRecoveryUI : public ScreenRecoveryUI { - public: - VrRecoveryUI(); + public: + VrRecoveryUI(); - protected: - // Pixel offsets to move drawing functions to visible range. - // Can vary per device depending on screen size and lens distortion. - const int kStereoOffset; + protected: + // Pixel offsets to move drawing functions to visible range. + // Can vary per device depending on screen size and lens distortion. + const int kStereoOffset; - bool InitTextParams() override; + bool InitTextParams() override; - void DrawTextLine(int x, int* y, const char* line, bool bold) const override; + void DrawTextLine(int x, int* y, const char* line, bool bold) const override; }; #endif // RECOVERY_VR_UI_H diff --git a/wear_ui.cpp b/wear_ui.cpp index 6c0286558..adc316dfc 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -45,167 +45,158 @@ static WearRecoveryUI* self = NULL; // Return the current time as a double (including fractions of a second). static double now() { - struct timeval tv; - gettimeofday(&tv, NULL); - return tv.tv_sec + tv.tv_usec / 1000000.0; + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec + tv.tv_usec / 1000000.0; } -WearRecoveryUI::WearRecoveryUI() : - progress_bar_y(259), - outer_height(0), - outer_width(0), - menu_unusable_rows(0) { - intro_frames = 22; - loop_frames = 60; - animation_fps = 30; +WearRecoveryUI::WearRecoveryUI() + : progress_bar_y(259), outer_height(0), outer_width(0), menu_unusable_rows(0) { + intro_frames = 22; + loop_frames = 60; + animation_fps = 30; - for (size_t i = 0; i < 5; i++) - backgroundIcon[i] = NULL; + for (size_t i = 0; i < 5; i++) backgroundIcon[i] = NULL; - self = this; + self = this; } int WearRecoveryUI::GetProgressBaseline() { - return progress_bar_y; + return progress_bar_y; } // Draw background frame on the screen. Does not flip pages. // Should only be called with updateMutex locked. // TODO merge drawing routines with screen_ui -void WearRecoveryUI::draw_background_locked() -{ - pagesIdentical = false; - gr_color(0, 0, 0, 255); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - - if (currentIcon != NONE) { - GRSurface* surface; - if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { - if (!intro_done) { - surface = introFrames[current_frame]; - } else { - surface = loopFrames[current_frame]; - } - } - else { - surface = backgroundIcon[currentIcon]; - } +void WearRecoveryUI::draw_background_locked() { + pagesIdentical = false; + gr_color(0, 0, 0, 255); + gr_fill(0, 0, gr_fb_width(), gr_fb_height()); + + if (currentIcon != NONE) { + GRSurface* surface; + if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { + if (!intro_done) { + surface = introFrames[current_frame]; + } else { + surface = loopFrames[current_frame]; + } + } else { + surface = backgroundIcon[currentIcon]; + } - int width = gr_get_width(surface); - int height = gr_get_height(surface); + int width = gr_get_width(surface); + int height = gr_get_height(surface); - int x = (gr_fb_width() - width) / 2; - int y = (gr_fb_height() - height) / 2; + int x = (gr_fb_width() - width) / 2; + int y = (gr_fb_height() - height) / 2; - gr_blit(surface, 0, 0, width, height, x, y); - } + gr_blit(surface, 0, 0, width, height, x, y); + } } static const char* HEADERS[] = { - "Swipe up/down to move.", - "Swipe left/right to select.", - "", - NULL + "Swipe up/down to move.", + "Swipe left/right to select.", + "", + NULL }; // TODO merge drawing routines with screen_ui -void WearRecoveryUI::draw_screen_locked() -{ - char cur_selection_str[50]; +void WearRecoveryUI::draw_screen_locked() { + char cur_selection_str[50]; + + draw_background_locked(); + if (!show_text) { + draw_foreground_locked(); + } else { + SetColor(TEXT_FILL); + gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - draw_background_locked(); - if (!show_text) { - draw_foreground_locked(); - } else { - SetColor(TEXT_FILL); - gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - - int y = outer_height; - int x = outer_width; - if (show_menu) { - std::string recovery_fingerprint = - android::base::GetProperty("ro.bootimage.build.fingerprint", ""); - SetColor(HEADER); - DrawTextLine(x + 4, &y, "Android Recovery", true); - for (auto& chunk: android::base::Split(recovery_fingerprint, ":")) { - DrawTextLine(x +4, &y, chunk.c_str(), false); - } - - // This is actually the help strings. - DrawTextLines(x + 4, &y, HEADERS); - SetColor(HEADER); - DrawTextLines(x + 4, &y, menu_headers_); - - // Show the current menu item number in relation to total number if - // items don't fit on the screen. - if (menu_items > menu_end - menu_start) { - sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items); - gr_text(gr_sys_font(), x+4, y, cur_selection_str, 1); - y += char_height_+4; - } - - // Menu begins here - SetColor(MENU); - - for (int i = menu_start; i < menu_end; ++i) { - - if (i == menu_sel) { - // draw the highlight bar - SetColor(MENU_SEL_BG); - gr_fill(x, y-2, gr_fb_width()-x, y+char_height_+2); - // white text of selected item - SetColor(MENU_SEL_FG); - if (menu_[i][0]) { - gr_text(gr_sys_font(), x + 4, y, menu_[i], 1); - } - SetColor(MENU); - } else if (menu_[i][0]) { - gr_text(gr_sys_font(), x + 4, y, menu_[i], 0); - } - y += char_height_+4; - } - SetColor(MENU); - y += 4; - gr_fill(0, y, gr_fb_width(), y+2); - y += 4; - } + int y = outer_height; + int x = outer_width; + if (show_menu) { + std::string recovery_fingerprint = + android::base::GetProperty("ro.bootimage.build.fingerprint", ""); + SetColor(HEADER); + DrawTextLine(x + 4, &y, "Android Recovery", true); + for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) { + DrawTextLine(x + 4, &y, chunk.c_str(), false); + } - SetColor(LOG); - - // display from the bottom up, until we hit the top of the - // screen, the bottom of the menu, or we've displayed the - // entire text buffer. - int ty; - int row = (text_top_ + text_rows_ - 1) % text_rows_; - size_t count = 0; - for (int ty = gr_fb_height() - char_height_ - outer_height; - ty > y + 2 && count < text_rows_; - ty -= char_height_, ++count) { - gr_text(gr_sys_font(), x+4, ty, text_[row], 0); - --row; - if (row < 0) row = text_rows_ - 1; + // This is actually the help strings. + DrawTextLines(x + 4, &y, HEADERS); + SetColor(HEADER); + DrawTextLines(x + 4, &y, menu_headers_); + + // Show the current menu item number in relation to total number if + // items don't fit on the screen. + if (menu_items > menu_end - menu_start) { + sprintf(cur_selection_str, "Current item: %d/%d", menu_sel + 1, menu_items); + gr_text(gr_sys_font(), x + 4, y, cur_selection_str, 1); + y += char_height_ + 4; + } + + // Menu begins here + SetColor(MENU); + + for (int i = menu_start; i < menu_end; ++i) { + if (i == menu_sel) { + // draw the highlight bar + SetColor(MENU_SEL_BG); + gr_fill(x, y - 2, gr_fb_width() - x, y + char_height_ + 2); + // white text of selected item + SetColor(MENU_SEL_FG); + if (menu_[i][0]) { + gr_text(gr_sys_font(), x + 4, y, menu_[i], 1); + } + SetColor(MENU); + } else if (menu_[i][0]) { + gr_text(gr_sys_font(), x + 4, y, menu_[i], 0); } + y += char_height_ + 4; + } + SetColor(MENU); + y += 4; + gr_fill(0, y, gr_fb_width(), y + 2); + y += 4; + } + + SetColor(LOG); + + // display from the bottom up, until we hit the top of the + // screen, the bottom of the menu, or we've displayed the + // entire text buffer. + int ty; + int row = (text_top_ + text_rows_ - 1) % text_rows_; + size_t count = 0; + for (int ty = gr_fb_height() - char_height_ - outer_height; ty > y + 2 && count < text_rows_; + ty -= char_height_, ++count) { + gr_text(gr_sys_font(), x + 4, ty, text_[row], 0); + --row; + if (row < 0) row = text_rows_ - 1; } + } } // TODO merge drawing routines with screen_ui void WearRecoveryUI::update_progress_locked() { - draw_screen_locked(); - gr_flip(); + draw_screen_locked(); + gr_flip(); } bool WearRecoveryUI::InitTextParams() { - if (!ScreenRecoveryUI::InitTextParams()) { - return false; - } + if (!ScreenRecoveryUI::InitTextParams()) { + return false; + } - text_cols_ = (gr_fb_width() - (outer_width * 2)) / char_width_; + text_cols_ = (gr_fb_width() - (outer_width * 2)) / char_width_; - if (text_rows_ > kMaxRows) text_rows_ = kMaxRows; - if (text_cols_ > kMaxCols) text_cols_ = kMaxCols; + if (text_rows_ > kMaxRows) text_rows_ = kMaxRows; + if (text_cols_ > kMaxCols) text_cols_ = kMaxCols; - visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_; - return true; + visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_; + return true; } bool WearRecoveryUI::Init(const std::string& locale) { @@ -222,7 +213,8 @@ bool WearRecoveryUI::Init(const std::string& locale) { return true; } -void WearRecoveryUI::SetStage(int current, int max) {} +void WearRecoveryUI::SetStage(int current, int max) { +} void WearRecoveryUI::Print(const char* fmt, ...) { char buf[256]; @@ -252,165 +244,164 @@ void WearRecoveryUI::Print(const char* fmt, ...) { pthread_mutex_unlock(&updateMutex); } -void WearRecoveryUI::StartMenu(const char* const * headers, const char* const * items, +void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* items, int initial_selection) { - pthread_mutex_lock(&updateMutex); - if (text_rows_ > 0 && text_cols_ > 0) { - menu_headers_ = headers; - size_t i = 0; - // "i < text_rows_" is removed from the loop termination condition, - // which is different from the one in ScreenRecoveryUI::StartMenu(). - // Because WearRecoveryUI supports scrollable menu, it's fine to have - // more entries than text_rows_. The menu may be truncated otherwise. - // Bug: 23752519 - for (; items[i] != nullptr; i++) { - strncpy(menu_[i], items[i], text_cols_ - 1); - menu_[i][text_cols_ - 1] = '\0'; - } - menu_items = i; - show_menu = true; - menu_sel = initial_selection; - menu_start = 0; - menu_end = visible_text_rows - 1 - menu_unusable_rows; - if (menu_items <= menu_end) - menu_end = menu_items; - update_screen_locked(); + pthread_mutex_lock(&updateMutex); + if (text_rows_ > 0 && text_cols_ > 0) { + menu_headers_ = headers; + size_t i = 0; + // "i < text_rows_" is removed from the loop termination condition, + // which is different from the one in ScreenRecoveryUI::StartMenu(). + // Because WearRecoveryUI supports scrollable menu, it's fine to have + // more entries than text_rows_. The menu may be truncated otherwise. + // Bug: 23752519 + for (; items[i] != nullptr; i++) { + strncpy(menu_[i], items[i], text_cols_ - 1); + menu_[i][text_cols_ - 1] = '\0'; } - pthread_mutex_unlock(&updateMutex); + menu_items = i; + show_menu = true; + menu_sel = initial_selection; + menu_start = 0; + menu_end = visible_text_rows - 1 - menu_unusable_rows; + if (menu_items <= menu_end) menu_end = menu_items; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); } int WearRecoveryUI::SelectMenu(int sel) { - int old_sel; - pthread_mutex_lock(&updateMutex); - if (show_menu) { - old_sel = menu_sel; - menu_sel = sel; - if (menu_sel < 0) menu_sel = 0; - if (menu_sel >= menu_items) menu_sel = menu_items-1; - if (menu_sel < menu_start) { - menu_start--; - menu_end--; - } else if (menu_sel >= menu_end && menu_sel < menu_items) { - menu_end++; - menu_start++; - } - sel = menu_sel; - if (menu_sel != old_sel) update_screen_locked(); + int old_sel; + pthread_mutex_lock(&updateMutex); + if (show_menu) { + old_sel = menu_sel; + menu_sel = sel; + if (menu_sel < 0) menu_sel = 0; + if (menu_sel >= menu_items) menu_sel = menu_items - 1; + if (menu_sel < menu_start) { + menu_start--; + menu_end--; + } else if (menu_sel >= menu_end && menu_sel < menu_items) { + menu_end++; + menu_start++; } - pthread_mutex_unlock(&updateMutex); - return sel; + sel = menu_sel; + if (menu_sel != old_sel) update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); + return sel; } void WearRecoveryUI::ShowFile(FILE* fp) { - std::vector offsets; - offsets.push_back(ftello(fp)); - ClearText(); - - struct stat sb; - fstat(fileno(fp), &sb); - - bool show_prompt = false; - while (true) { - if (show_prompt) { - Print("--(%d%% of %d bytes)--", - static_cast(100 * (double(ftello(fp)) / double(sb.st_size))), - static_cast(sb.st_size)); - Redraw(); - while (show_prompt) { - show_prompt = false; - int key = WaitKey(); - if (key == KEY_POWER || key == KEY_ENTER) { - return; - } else if (key == KEY_UP || key == KEY_VOLUMEUP) { - if (offsets.size() <= 1) { - show_prompt = true; - } else { - offsets.pop_back(); - fseek(fp, offsets.back(), SEEK_SET); - } - } else { - if (feof(fp)) { - return; - } - offsets.push_back(ftello(fp)); - } - } - ClearText(); - } - - int ch = getc(fp); - if (ch == EOF) { - text_row_ = text_top_ = text_rows_ - 2; + std::vector offsets; + offsets.push_back(ftello(fp)); + ClearText(); + + struct stat sb; + fstat(fileno(fp), &sb); + + bool show_prompt = false; + while (true) { + if (show_prompt) { + Print("--(%d%% of %d bytes)--", + static_cast(100 * (double(ftello(fp)) / double(sb.st_size))), + static_cast(sb.st_size)); + Redraw(); + while (show_prompt) { + show_prompt = false; + int key = WaitKey(); + if (key == KEY_POWER || key == KEY_ENTER) { + return; + } else if (key == KEY_UP || key == KEY_VOLUMEUP) { + if (offsets.size() <= 1) { show_prompt = true; + } else { + offsets.pop_back(); + fseek(fp, offsets.back(), SEEK_SET); + } } else { - PutChar(ch); - if (text_col_ == 0 && text_row_ >= text_rows_ - 2) { - text_top_ = text_row_; - show_prompt = true; - } + if (feof(fp)) { + return; + } + offsets.push_back(ftello(fp)); } + } + ClearText(); + } + + int ch = getc(fp); + if (ch == EOF) { + text_row_ = text_top_ = text_rows_ - 2; + show_prompt = true; + } else { + PutChar(ch); + if (text_col_ == 0 && text_row_ >= text_rows_ - 2) { + text_top_ = text_row_; + show_prompt = true; + } } + } } void WearRecoveryUI::PutChar(char ch) { - pthread_mutex_lock(&updateMutex); - if (ch != '\n') text_[text_row_][text_col_++] = ch; - if (ch == '\n' || text_col_ >= text_cols_) { - text_col_ = 0; - ++text_row_; - } - pthread_mutex_unlock(&updateMutex); + pthread_mutex_lock(&updateMutex); + if (ch != '\n') text_[text_row_][text_col_++] = ch; + if (ch == '\n' || text_col_ >= text_cols_) { + text_col_ = 0; + ++text_row_; + } + pthread_mutex_unlock(&updateMutex); } void WearRecoveryUI::ShowFile(const char* filename) { - FILE* fp = fopen_path(filename, "re"); - if (fp == nullptr) { - Print(" Unable to open %s: %s\n", filename, strerror(errno)); - return; - } - ShowFile(fp); - fclose(fp); + FILE* fp = fopen_path(filename, "re"); + if (fp == nullptr) { + Print(" Unable to open %s: %s\n", filename, strerror(errno)); + return; + } + ShowFile(fp); + fclose(fp); } void WearRecoveryUI::ClearText() { - pthread_mutex_lock(&updateMutex); - text_col_ = 0; - text_row_ = 0; - text_top_ = 1; - for (size_t i = 0; i < text_rows_; ++i) { - memset(text_[i], 0, text_cols_ + 1); - } - pthread_mutex_unlock(&updateMutex); + pthread_mutex_lock(&updateMutex); + text_col_ = 0; + text_row_ = 0; + text_top_ = 1; + for (size_t i = 0; i < text_rows_; ++i) { + memset(text_[i], 0, text_cols_ + 1); + } + pthread_mutex_unlock(&updateMutex); } void WearRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - PrintV(fmt, false, ap); - va_end(ap); + va_list ap; + va_start(ap, fmt); + PrintV(fmt, false, ap); + va_end(ap); } void WearRecoveryUI::PrintV(const char* fmt, bool copy_to_stdout, va_list ap) { - std::string str; - android::base::StringAppendV(&str, fmt, ap); + std::string str; + android::base::StringAppendV(&str, fmt, ap); - if (copy_to_stdout) { - fputs(str.c_str(), stdout); - } + if (copy_to_stdout) { + fputs(str.c_str(), stdout); + } - pthread_mutex_lock(&updateMutex); - if (text_rows_ > 0 && text_cols_ > 0) { - for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) { - if (*ptr == '\n' || text_col_ >= text_cols_) { - text_[text_row_][text_col_] = '\0'; - text_col_ = 0; - text_row_ = (text_row_ + 1) % text_rows_; - if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; - } - if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; - } + pthread_mutex_lock(&updateMutex); + if (text_rows_ > 0 && text_cols_ > 0) { + for (const char* ptr = str.c_str(); *ptr != '\0'; ++ptr) { + if (*ptr == '\n' || text_col_ >= text_cols_) { text_[text_row_][text_col_] = '\0'; - update_screen_locked(); + text_col_ = 0; + text_row_ = (text_row_ + 1) % text_rows_; + if (text_row_ == text_top_) text_top_ = (text_top_ + 1) % text_rows_; + } + if (*ptr != '\n') text_[text_row_][text_col_++] = *ptr; } - pthread_mutex_unlock(&updateMutex); + text_[text_row_][text_col_] = '\0'; + update_screen_locked(); + } + pthread_mutex_unlock(&updateMutex); } diff --git a/wear_ui.h b/wear_ui.h index 4cd852f21..e228f9fb5 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -22,64 +22,64 @@ #include class WearRecoveryUI : public ScreenRecoveryUI { - public: - WearRecoveryUI(); + public: + WearRecoveryUI(); - bool Init(const std::string& locale) override; + bool Init(const std::string& locale) override; - void SetStage(int current, int max) override; + void SetStage(int current, int max) override; - // printing messages - void Print(const char* fmt, ...) override; - void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3); - void ShowFile(const char* filename) override; - void ShowFile(FILE* fp) override; + // printing messages + void Print(const char* fmt, ...) override; + void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3); + void ShowFile(const char* filename) override; + void ShowFile(FILE* fp) override; - // menu display - void StartMenu(const char* const * headers, const char* const * items, - int initial_selection) override; - int SelectMenu(int sel) override; + // menu display + void StartMenu(const char* const* headers, const char* const* items, + int initial_selection) override; + int SelectMenu(int sel) override; - protected: - // progress bar vertical position, it's centered horizontally - int progress_bar_y; + protected: + // progress bar vertical position, it's centered horizontally + int progress_bar_y; - // outer of window - int outer_height, outer_width; + // outer of window + int outer_height, outer_width; - // Unusable rows when displaying the recovery menu, including the lines - // for headers (Android Recovery, build id and etc) and the bottom lines - // that may otherwise go out of the screen. - int menu_unusable_rows; + // Unusable rows when displaying the recovery menu, including the lines + // for headers (Android Recovery, build id and etc) and the bottom lines + // that may otherwise go out of the screen. + int menu_unusable_rows; - int GetProgressBaseline() override; + int GetProgressBaseline() override; - bool InitTextParams() override; + bool InitTextParams() override; - void update_progress_locked() override; + void update_progress_locked() override; - void PrintV(const char*, bool, va_list) override; + void PrintV(const char*, bool, va_list) override; - private: - GRSurface* backgroundIcon[5]; + private: + GRSurface* backgroundIcon[5]; - static const int kMaxCols = 96; - static const int kMaxRows = 96; + static const int kMaxCols = 96; + static const int kMaxRows = 96; - // Number of text rows seen on screen - int visible_text_rows; + // Number of text rows seen on screen + int visible_text_rows; - const char* const* menu_headers_; - int menu_start, menu_end; + const char* const* menu_headers_; + int menu_start, menu_end; - pthread_t progress_t; + pthread_t progress_t; - void draw_background_locked() override; - void draw_screen_locked() override; - void draw_progress_locked(); + void draw_background_locked() override; + void draw_screen_locked() override; + void draw_progress_locked(); - void PutChar(char); - void ClearText(); + void PutChar(char); + void ClearText(); }; #endif // RECOVERY_WEAR_UI_H -- cgit v1.2.3 From 99b2d774751041a5cfb5a48ac93292bb92acef98 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 23 Jun 2017 22:47:03 -0700 Subject: Add override specifier and member constness to RecoveryUI classes. Test: mmma bootable/recovery Change-Id: I66e328614423488a4027d7878f4569fbf3a3721e --- screen_ui.cpp | 14 ++++++------ screen_ui.h | 33 +++++++++++++++-------------- ui.h | 68 ++++++++++++++++++++++++++--------------------------------- wear_ui.cpp | 2 +- wear_ui.h | 7 +++--- 5 files changed, 58 insertions(+), 66 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index 6172b631a..bcfaaa4be 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -82,14 +82,14 @@ ScreenRecoveryUI::ScreenRecoveryUI() max_stage(-1), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} -GRSurface* ScreenRecoveryUI::GetCurrentFrame() { +GRSurface* ScreenRecoveryUI::GetCurrentFrame() const { if (currentIcon == INSTALLING_UPDATE || currentIcon == ERASING) { return intro_done ? loopFrames[current_frame] : introFrames[current_frame]; } return error_icon; } -GRSurface* ScreenRecoveryUI::GetCurrentText() { +GRSurface* ScreenRecoveryUI::GetCurrentText() const { switch (currentIcon) { case ERASING: return erasing_text; @@ -132,16 +132,16 @@ static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = { { 262, 52, 112, }, // LANDSCAPE_LARGE }; -int ScreenRecoveryUI::GetAnimationBaseline() { +int ScreenRecoveryUI::GetAnimationBaseline() const { return GetTextBaseline() - PixelsFromDp(kLayouts[layout_][ICON]) - gr_get_height(loopFrames[0]); } -int ScreenRecoveryUI::GetTextBaseline() { +int ScreenRecoveryUI::GetTextBaseline() const { return GetProgressBaseline() - PixelsFromDp(kLayouts[layout_][TEXT]) - gr_get_height(installing_text); } -int ScreenRecoveryUI::GetProgressBaseline() { +int ScreenRecoveryUI::GetProgressBaseline() const { return gr_fb_height() - PixelsFromDp(kLayouts[layout_][PROGRESS]) - gr_get_height(progressBarFill); } @@ -224,7 +224,7 @@ void ScreenRecoveryUI::draw_foreground_locked() { } } -void ScreenRecoveryUI::SetColor(UIElement e) { +void ScreenRecoveryUI::SetColor(UIElement e) const { switch (e) { case INFO: gr_color(249, 194, 0, 255); @@ -254,7 +254,7 @@ void ScreenRecoveryUI::SetColor(UIElement e) { } } -void ScreenRecoveryUI::DrawHorizontalRule(int* y) { +void ScreenRecoveryUI::DrawHorizontalRule(int* y) const { SetColor(MENU); *y += 4; gr_fill(0, *y, gr_fb_width(), *y + 2); diff --git a/screen_ui.h b/screen_ui.h index 2a3d8c30b..e8d3c3222 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -36,8 +36,8 @@ class ScreenRecoveryUI : public RecoveryUI { bool Init(const std::string& locale) override; // overall recovery state ("background image") - void SetBackground(Icon icon); - void SetSystemUpdateText(bool security_update); + void SetBackground(Icon icon) override; + void SetSystemUpdateText(bool security_update) override; // progress indicator void SetProgressType(ProgressType type) override; @@ -52,16 +52,17 @@ class ScreenRecoveryUI : public RecoveryUI { bool WasTextEverVisible() override; // printing messages - void Print(const char* fmt, ...) __printflike(2, 3); - void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3); - void ShowFile(const char* filename); + void Print(const char* fmt, ...) override __printflike(2, 3); + void PrintOnScreenOnly(const char* fmt, ...) override __printflike(2, 3); + void ShowFile(const char* filename) override; // menu display - void StartMenu(const char* const* headers, const char* const* items, int initial_selection); - int SelectMenu(int sel); - void EndMenu(); + void StartMenu(const char* const* headers, const char* const* items, + int initial_selection) override; + int SelectMenu(int sel) override; + void EndMenu() override; - void KeyLongPress(int); + void KeyLongPress(int) override; void Redraw(); @@ -75,7 +76,7 @@ class ScreenRecoveryUI : public RecoveryUI { TEXT_FILL, INFO }; - void SetColor(UIElement e); + void SetColor(UIElement e) const; protected: // The margin that we don't want to use for showing texts (e.g. round screen, or screen with @@ -158,8 +159,8 @@ class ScreenRecoveryUI : public RecoveryUI { virtual void update_screen_locked(); virtual void update_progress_locked(); - GRSurface* GetCurrentFrame(); - GRSurface* GetCurrentText(); + GRSurface* GetCurrentFrame() const; + GRSurface* GetCurrentText() const; static void* ProgressThreadStartRoutine(void* data); void ProgressThreadLoop(); @@ -174,11 +175,11 @@ class ScreenRecoveryUI : public RecoveryUI { void LoadLocalizedBitmap(const char* filename, GRSurface** surface); int PixelsFromDp(int dp) const; - virtual int GetAnimationBaseline(); - virtual int GetProgressBaseline(); - virtual int GetTextBaseline(); + virtual int GetAnimationBaseline() const; + virtual int GetProgressBaseline() const; + virtual int GetTextBaseline() const; - virtual void DrawHorizontalRule(int* y); + virtual void DrawHorizontalRule(int* y) const; virtual void DrawHighlightBar(int x, int y, int width, int height) const; virtual void DrawTextLine(int x, int* y, const char* line, bool bold) const; void DrawTextLines(int x, int* y, const char* const* lines) const; diff --git a/ui.h b/ui.h index ef63e1d70..7eb04aec8 100644 --- a/ui.h +++ b/ui.h @@ -30,14 +30,14 @@ class RecoveryUI { virtual ~RecoveryUI() {} - // Initialize the object; called before anything else. UI texts will be - // initialized according to the given locale. Returns true on success. + // Initializes the object; called before anything else. UI texts will be initialized according to + // the given locale. Returns true on success. virtual bool Init(const std::string& locale); - // Show a stage indicator. Call immediately after Init(). + // Shows a stage indicator. Called immediately after Init(). virtual void SetStage(int current, int max) = 0; - // Set the overall recovery state ("background image"). + // Sets the overall recovery state ("background image"). enum Icon { NONE, INSTALLING_UPDATE, ERASING, NO_COMMAND, ERROR }; virtual void SetBackground(Icon icon) = 0; virtual void SetSystemUpdateText(bool security_update) = 0; @@ -46,13 +46,13 @@ class RecoveryUI { enum ProgressType { EMPTY, INDETERMINATE, DETERMINATE }; virtual void SetProgressType(ProgressType determinate) = 0; - // Show a progress bar and define the scope of the next operation: + // Shows a progress bar and define the scope of the next operation: // portion - fraction of the progress bar the next operation will use // seconds - expected time interval (progress bar moves at this minimum rate) virtual void ShowProgress(float portion, float seconds) = 0; - // Set progress bar position (0.0 - 1.0 within the scope defined - // by the last call to ShowProgress). + // Sets progress bar position (0.0 - 1.0 within the scope defined by the last call to + // ShowProgress). virtual void SetProgress(float fraction) = 0; // --- text log --- @@ -63,9 +63,8 @@ class RecoveryUI { virtual bool WasTextEverVisible() = 0; - // Write a message to the on-screen log (shown if the user has - // toggled on the text display). Print() will also dump the message - // to stdout / log file, while PrintOnScreenOnly() not. + // Writes a message to the on-screen log (shown if the user has toggled on the text display). + // Print() will also dump the message to stdout / log file, while PrintOnScreenOnly() not. virtual void Print(const char* fmt, ...) __printflike(2, 3) = 0; virtual void PrintOnScreenOnly(const char* fmt, ...) __printflike(2, 3) = 0; @@ -73,54 +72,48 @@ class RecoveryUI { // --- key handling --- - // Wait for a key and return it. May return -1 after timeout. + // Waits for a key and return it. May return -1 after timeout. virtual int WaitKey(); virtual bool IsKeyPressed(int key); virtual bool IsLongPress(); - // Returns true if you have the volume up/down and power trio typical - // of phones and tablets, false otherwise. + // Returns true if you have the volume up/down and power trio typical of phones and tablets, false + // otherwise. virtual bool HasThreeButtons(); - // Erase any queued-up keys. + // Erases any queued-up keys. virtual void FlushKeys(); - // Called on each key press, even while operations are in progress. - // Return value indicates whether an immediate operation should be - // triggered (toggling the display, rebooting the device), or if + // Called on each key press, even while operations are in progress. Return value indicates whether + // an immediate operation should be triggered (toggling the display, rebooting the device), or if // the key should be enqueued for use by the main thread. enum KeyAction { ENQUEUE, TOGGLE, REBOOT, IGNORE }; virtual KeyAction CheckKey(int key, bool is_long_press); - // Called when a key is held down long enough to have been a - // long-press (but before the key is released). This means that - // if the key is eventually registered (released without any other - // keys being pressed in the meantime), CheckKey will be called with - // 'is_long_press' true. + // Called when a key is held down long enough to have been a long-press (but before the key is + // released). This means that if the key is eventually registered (released without any other keys + // being pressed in the meantime), CheckKey will be called with 'is_long_press' true. virtual void KeyLongPress(int key); - // Normally in recovery there's a key sequence that triggers - // immediate reboot of the device, regardless of what recovery is - // doing (with the default CheckKey implementation, it's pressing - // the power button 7 times in row). Call this to enable or - // disable that feature. It is enabled by default. + // Normally in recovery there's a key sequence that triggers immediate reboot of the device, + // regardless of what recovery is doing (with the default CheckKey implementation, it's pressing + // the power button 7 times in row). Call this to enable or disable that feature. It is enabled by + // default. virtual void SetEnableReboot(bool enabled); // --- menu display --- - // Display some header text followed by a menu of items, which appears - // at the top of the screen (in place of any scrolling ui_print() - // output, if necessary). + // Display some header text followed by a menu of items, which appears at the top of the screen + // (in place of any scrolling ui_print() output, if necessary). virtual void StartMenu(const char* const* headers, const char* const* items, int initial_selection) = 0; - // Set the menu highlight to the given index, wrapping if necessary. - // Returns the actual item selected. + // Sets the menu highlight to the given index, wrapping if necessary. Returns the actual item + // selected. virtual int SelectMenu(int sel) = 0; - // End menu mode, resetting the text overlay so that ui_print() - // statements will be displayed. + // Ends menu mode, resetting the text overlay so that ui_print() statements will be displayed. virtual void EndMenu() = 0; protected: @@ -130,10 +123,9 @@ class RecoveryUI { std::string locale_; bool rtl_locale_; - // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25% - // of the max_brightness). Because the absolute values may vary across devices. These two - // values can be configured via subclassing. Setting brightness_normal_ to 0 to disable - // screensaver. + // The normal and dimmed brightness percentages (default: 50 and 25, which means 50% and 25% of + // the max_brightness). Because the absolute values may vary across devices. These two values can + // be configured via subclassing. Setting brightness_normal_ to 0 to disable screensaver. unsigned int brightness_normal_; unsigned int brightness_dimmed_; diff --git a/wear_ui.cpp b/wear_ui.cpp index adc316dfc..640f04a79 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -61,7 +61,7 @@ WearRecoveryUI::WearRecoveryUI() self = this; } -int WearRecoveryUI::GetProgressBaseline() { +int WearRecoveryUI::GetProgressBaseline() const { return progress_bar_y; } diff --git a/wear_ui.h b/wear_ui.h index e228f9fb5..e108d4d7e 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -47,12 +47,11 @@ class WearRecoveryUI : public ScreenRecoveryUI { // outer of window int outer_height, outer_width; - // Unusable rows when displaying the recovery menu, including the lines - // for headers (Android Recovery, build id and etc) and the bottom lines - // that may otherwise go out of the screen. + // Unusable rows when displaying the recovery menu, including the lines for headers (Android + // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen. int menu_unusable_rows; - int GetProgressBaseline() override; + int GetProgressBaseline() const override; bool InitTextParams() override; -- cgit v1.2.3 From a29d8d69d23b2ed2ead83c67f84c9c474dd563cb Mon Sep 17 00:00:00 2001 From: Daniel Micay Date: Fri, 23 Jun 2017 08:33:24 -0400 Subject: avoid assuming build number is a 32-bit integer The install logging currently assumes that the build number is a 32-bit integer and prints an error when that doesn't hold true. However, that's only a convention used by Google builds. From build/core/version_defaults.mk: ifeq "" "$(BUILD_NUMBER)" # BUILD_NUMBER should be set to the source control value that # represents the current state of the source code. E.g., a # perforce changelist number or a git hash. Can be an arbitrary string # (to allow for source control that uses something other than numbers), # but must be a single word and a valid file name. # # If no BUILD_NUMBER is set, create a useful "I am an engineering build # from this date/time" value. Make it start with a non-digit so that # anyone trying to parse it as an integer will probably get "0". BUILD_NUMBER := eng.$(shell echo $${USER:0:6}).$(shell $(DATE) +%Y%m%d.%H%M%S) endif Change-Id: I8e7cec0618783f69545ba76d0dce2bbc1681784c --- install.cpp | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/install.cpp b/install.cpp index db4ba9373..7ba8f0139 100644 --- a/install.cpp +++ b/install.cpp @@ -66,18 +66,14 @@ static constexpr float VERIFICATION_PROGRESS_FRACTION = 0.25; static std::condition_variable finish_log_temperature; // This function parses and returns the build.version.incremental -static int parse_build_number(const std::string& str) { +static std::string parse_build_number(const std::string& str) { size_t pos = str.find('='); if (pos != std::string::npos) { - std::string num_string = android::base::Trim(str.substr(pos+1)); - int build_number; - if (android::base::ParseInt(num_string.c_str(), &build_number, 0)) { - return build_number; - } + return android::base::Trim(str.substr(pos+1)); } LOG(ERROR) << "Failed to parse build number in " << str; - return -1; + return ""; } bool read_metadata_from_package(ZipArchiveHandle zip, std::string* metadata) { @@ -114,14 +110,14 @@ static void read_source_target_build(ZipArchiveHandle zip, std::vectorpush_back(android::base::StringPrintf("source_build: %d", source_build)); + std::string source_build = parse_build_number(str); + if (!source_build.empty()) { + log_buffer->push_back("source_build: " + source_build); } } else if (android::base::StartsWith(str, "post-build-incremental")) { - int target_build = parse_build_number(str); - if (target_build != -1) { - log_buffer->push_back(android::base::StringPrintf("target_build: %d", target_build)); + std::string target_build = parse_build_number(str); + if (!target_build.empty()) { + log_buffer->push_back("target_build: " + target_build); } } } -- cgit v1.2.3 From 619b162d67e0dd4e5e99c78e67dd8729aa37320b Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Fri, 21 Apr 2017 14:36:12 -0700 Subject: recovery: replace make_ext4 with e2fsprogs Execute mke2fs to create empty ext4 filesystem. Execute e2fsdroid to add files to filesystem. Test: enter recovery mode and wipe data Bug: 35219933 Change-Id: I10a9f4c1f4754ad864b2df45b1f879180ab33876 Merged-In: I10a9f4c1f4754ad864b2df45b1f879180ab33876 --- Android.mk | 8 ++++--- roots.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++++++------ updater/install.cpp | 27 +++++++++++++++++++++-- 3 files changed, 86 insertions(+), 11 deletions(-) diff --git a/Android.mk b/Android.mk index 5f3a8a45d..cfd55a79a 100644 --- a/Android.mk +++ b/Android.mk @@ -83,9 +83,11 @@ LOCAL_MODULE := recovery LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_REQUIRED_MODULES := e2fsdroid_static mke2fs_static mke2fs.conf + ifeq ($(TARGET_USERIMAGES_USE_F2FS),true) ifeq ($(HOST_OS),linux) -LOCAL_REQUIRED_MODULES := mkfs.f2fs +LOCAL_REQUIRED_MODULES += mkfs.f2fs endif endif @@ -112,6 +114,7 @@ LOCAL_STATIC_LIBRARIES := \ libverifier \ libbatterymonitor \ libbootloader_message \ + libfs_mgr \ libext4_utils \ libsparse \ libziparchive \ @@ -122,7 +125,6 @@ LOCAL_STATIC_LIBRARIES := \ libfusesideload \ libminui \ libpng \ - libfs_mgr \ libcrypto_utils \ libcrypto \ libvintf_recovery \ @@ -151,7 +153,7 @@ else endif ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),) -LOCAL_REQUIRED_MODULES := recovery-persist recovery-refresh +LOCAL_REQUIRED_MODULES += recovery-persist recovery-refresh endif include $(BUILD_EXECUTABLE) diff --git a/roots.cpp b/roots.cpp index 9b4270256..e98dfd448 100644 --- a/roots.cpp +++ b/roots.cpp @@ -27,7 +27,8 @@ #include #include -#include +#include +#include #include #include @@ -215,11 +216,60 @@ int format_volume(const char* volume, const char* directory) { } int result; if (strcmp(v->fs_type, "ext4") == 0) { - if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { - result = make_ext4fs_directory_align(v->blk_device, length, volume, sehandle, - directory, v->erase_blk_size, v->logical_blk_size); - } else { - result = make_ext4fs_directory(v->blk_device, length, volume, sehandle, directory); + static constexpr int block_size = 4096; + int raid_stride = v->logical_blk_size / block_size; + int raid_stripe_width = v->erase_blk_size / block_size; + + // stride should be the max of 8kb and logical block size + if (v->logical_blk_size != 0 && v->logical_blk_size < 8192) { + raid_stride = 8192 / block_size; + } + + const char* mke2fs_argv[] = { "/sbin/mke2fs_static", + "-F", + "-t", + "ext4", + "-b", + nullptr, + nullptr, + nullptr, + nullptr, + nullptr, + nullptr }; + + int i = 5; + std::string block_size_str = std::to_string(block_size); + mke2fs_argv[i++] = block_size_str.c_str(); + + std::string ext_args; + if (v->erase_blk_size != 0 && v->logical_blk_size != 0) { + ext_args = android::base::StringPrintf("stride=%d,stripe-width=%d", raid_stride, + raid_stripe_width); + mke2fs_argv[i++] = "-E"; + mke2fs_argv[i++] = ext_args.c_str(); + } + + mke2fs_argv[i++] = v->blk_device; + + std::string size_str = std::to_string(length / block_size); + if (length != 0) { + mke2fs_argv[i++] = size_str.c_str(); + } + + result = exec_cmd(mke2fs_argv[0], const_cast(mke2fs_argv)); + if (result == 0 && directory != nullptr) { + const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", + "-e", + "-S", + "/file_contexts", + "-f", + directory, + "-a", + volume, + v->blk_device, + nullptr }; + + result = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); } } else { /* Has to be f2fs because we checked earlier. */ if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { diff --git a/updater/install.cpp b/updater/install.cpp index ff79edce0..c9a3a0799 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -302,9 +302,32 @@ Value* FormatFn(const char* name, State* state, const std::vector(mke2fs_argv)); + if (status != 0) { + LOG(WARNING) << name << ": mke2fs failed (" << status << ") on " << location + << ", falling back to make_ext4fs"; + status = make_ext4fs(location.c_str(), size, mount_point.c_str(), sehandle); + if (status != 0) { + LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location; + return StringValue(""); + } + return StringValue(location); + } + + const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", "-S", + "/file_contexts", "-a", mount_point.c_str(), + location.c_str(), nullptr }; + status = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); if (status != 0) { - LOG(ERROR) << name << ": make_ext4fs failed (" << status << ") on " << location; + LOG(ERROR) << name << ": e2fsdroid failed (" << status << ") on " << location; return StringValue(""); } return StringValue(location); -- cgit v1.2.3 From e1bb7a5752163a668fb764b2bf1d0d0cdf2bd43d Mon Sep 17 00:00:00 2001 From: Elliott Hughes Date: Wed, 28 Jun 2017 08:00:17 -0700 Subject: Add missing libziparchive dependency. Bug: http://b/37560614 Test: builds even after removing system/core/include/ziparchive Change-Id: Ib7ed45cafe83d24ed55aac28b4f41a073c371bc2 --- Android.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 5348e365e..84f675ba4 100644 --- a/Android.mk +++ b/Android.mk @@ -59,7 +59,8 @@ LOCAL_STATIC_LIBRARIES := \ libvintf_recovery \ libcrypto_utils \ libcrypto \ - libbase + libbase \ + libziparchive \ include $(BUILD_STATIC_LIBRARY) -- cgit v1.2.3 From 0bc54dd83fee524e4475770df16570923aea13cb Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 28 Jun 2017 15:27:34 -0700 Subject: wear_ui: Remove dead/duplicate codes. WearRecoveryUI::draw_progress_locked() has declaration only, where the definition was deleted in commit 5e7cfb9af64d5f6bf616d9b6fa40bd0ae82e781a. WearRecoveryUI::ClearText() is a duplicates of ScreenRecoveryUI::ClearText(). Test: Build swordfish recovery image. 'Run graphics test' and 'View recovery logs'. Change-Id: Ib66955d1d496f04359b4d6487160218e8f954478 --- wear_ui.cpp | 11 ----------- wear_ui.h | 2 -- 2 files changed, 13 deletions(-) diff --git a/wear_ui.cpp b/wear_ui.cpp index 640f04a79..a29746cdc 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -363,17 +363,6 @@ void WearRecoveryUI::ShowFile(const char* filename) { fclose(fp); } -void WearRecoveryUI::ClearText() { - pthread_mutex_lock(&updateMutex); - text_col_ = 0; - text_row_ = 0; - text_top_ = 1; - for (size_t i = 0; i < text_rows_; ++i) { - memset(text_[i], 0, text_cols_ + 1); - } - pthread_mutex_unlock(&updateMutex); -} - void WearRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) { va_list ap; va_start(ap, fmt); diff --git a/wear_ui.h b/wear_ui.h index e108d4d7e..a814118c7 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -75,10 +75,8 @@ class WearRecoveryUI : public ScreenRecoveryUI { void draw_background_locked() override; void draw_screen_locked() override; - void draw_progress_locked(); void PutChar(char); - void ClearText(); }; #endif // RECOVERY_WEAR_UI_H -- cgit v1.2.3 From 3250f723602244cd3a87327a14755dcde2f4e5dc Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 29 Jun 2017 14:32:05 -0700 Subject: screen_ui: Compute the top and bottom gaps. We're not actually following the gaps as in the comments. For example, Nexus 6P is supposed to use 220dp and 194dp gaps (top and bottom respectively), but the actual numbers are 185dp and 194dp. Because the animation icon and text sizes don't match the ones claimed (animation: expected 200dp or 700px, actual 800px; text: claimed 14sp, actual 76px). The top gap changes (shrinks) as we compute the baselines bottom-up. This CL switches to using computed gaps: the major UI elements always stay vertically centered, with identical top and bottom gaps. Bug: 63093285 Test: 'Run graphics test' on angler/volantis/fugu/ryu. Change-Id: I3cadbb34f728cf034afa47ac02a6deba8cb6b4e7 --- screen_ui.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index bcfaaa4be..378a4514a 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -112,24 +112,24 @@ int ScreenRecoveryUI::PixelsFromDp(int dp) const { // | portrait large landscape large // ---------+------------------------------------------------- -// gap | 220dp 366dp 142dp 284dp +// gap | // icon | (200dp) // gap | 68dp 68dp 56dp 112dp // text | (14sp) // gap | 32dp 32dp 26dp 52dp // progress | (2dp) -// gap | 194dp 340dp 131dp 262dp +// gap | -// Note that "baseline" is actually the *top* of each icon (because that's how our drawing -// routines work), so that's the more useful measurement for calling code. +// Note that "baseline" is actually the *top* of each icon (because that's how our drawing routines +// work), so that's the more useful measurement for calling code. We use even top and bottom gaps. enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX }; -enum Dimension { PROGRESS = 0, TEXT = 1, ICON = 2, DIMENSION_MAX }; +enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX }; static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = { - { 194, 32, 68, }, // PORTRAIT - { 340, 32, 68, }, // PORTRAIT_LARGE - { 131, 26, 56, }, // LANDSCAPE - { 262, 52, 112, }, // LANDSCAPE_LARGE + { 32, 68, }, // PORTRAIT + { 32, 68, }, // PORTRAIT_LARGE + { 26, 56, }, // LANDSCAPE + { 52, 112, }, // LANDSCAPE_LARGE }; int ScreenRecoveryUI::GetAnimationBaseline() const { @@ -142,8 +142,11 @@ int ScreenRecoveryUI::GetTextBaseline() const { } int ScreenRecoveryUI::GetProgressBaseline() const { - return gr_fb_height() - PixelsFromDp(kLayouts[layout_][PROGRESS]) - - gr_get_height(progressBarFill); + int elements_sum = gr_get_height(loopFrames[0]) + PixelsFromDp(kLayouts[layout_][ICON]) + + gr_get_height(installing_text) + PixelsFromDp(kLayouts[layout_][TEXT]) + + gr_get_height(progressBarFill); + int bottom_gap = (gr_fb_height() - elements_sum) / 2; + return gr_fb_height() - bottom_gap - gr_get_height(progressBarFill); } // Clear the screen and draw the currently selected background icon (if any). -- cgit v1.2.3 From f95e686dd04056db2ff0a6b2933cce07eba54e14 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 29 Jun 2017 14:32:05 -0700 Subject: screen_ui: Compute the top and bottom gaps. We're not actually following the gaps as in the comments. For example, Nexus 6P is supposed to use 220dp and 194dp gaps (top and bottom respectively), but the actual numbers are 185dp and 194dp. Because the animation icon and text sizes don't match the ones claimed (animation: expected 200dp or 700px, actual 800px; text: claimed 14sp, actual 76px). The top gap changes (shrinks) as we compute the baselines bottom-up. This CL switches to using computed gaps: the major UI elements always stay vertically centered, with identical top and bottom gaps. Bug: 63093285 Test: 'Run graphics test' on angler/volantis/fugu/ryu. Change-Id: I3cadbb34f728cf034afa47ac02a6deba8cb6b4e7 (cherry picked from commit 3250f723602244cd3a87327a14755dcde2f4e5dc) --- screen_ui.cpp | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index 4ca96a9b2..7334b713e 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -107,24 +107,24 @@ int ScreenRecoveryUI::PixelsFromDp(int dp) const { // | portrait large landscape large // ---------+------------------------------------------------- -// gap | 220dp 366dp 142dp 284dp +// gap | // icon | (200dp) // gap | 68dp 68dp 56dp 112dp // text | (14sp) // gap | 32dp 32dp 26dp 52dp // progress | (2dp) -// gap | 194dp 340dp 131dp 262dp +// gap | -// Note that "baseline" is actually the *top* of each icon (because that's how our drawing -// routines work), so that's the more useful measurement for calling code. +// Note that "baseline" is actually the *top* of each icon (because that's how our drawing routines +// work), so that's the more useful measurement for calling code. We use even top and bottom gaps. enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX }; -enum Dimension { PROGRESS = 0, TEXT = 1, ICON = 2, DIMENSION_MAX }; +enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX }; static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = { - { 194, 32, 68, }, // PORTRAIT - { 340, 32, 68, }, // PORTRAIT_LARGE - { 131, 26, 56, }, // LANDSCAPE - { 262, 52, 112, }, // LANDSCAPE_LARGE + { 32, 68, }, // PORTRAIT + { 32, 68, }, // PORTRAIT_LARGE + { 26, 56, }, // LANDSCAPE + { 52, 112, }, // LANDSCAPE_LARGE }; int ScreenRecoveryUI::GetAnimationBaseline() { @@ -138,8 +138,11 @@ int ScreenRecoveryUI::GetTextBaseline() { } int ScreenRecoveryUI::GetProgressBaseline() { - return gr_fb_height() - PixelsFromDp(kLayouts[layout_][PROGRESS]) - - gr_get_height(progressBarFill); + int elements_sum = gr_get_height(loopFrames[0]) + PixelsFromDp(kLayouts[layout_][ICON]) + + gr_get_height(installing_text) + PixelsFromDp(kLayouts[layout_][TEXT]) + + gr_get_height(progressBarFill); + int bottom_gap = (gr_fb_height() - elements_sum) / 2; + return gr_fb_height() - bottom_gap - gr_get_height(progressBarFill); } // Clear the screen and draw the currently selected background icon (if any). -- cgit v1.2.3 From ea78d86b44661c69fcb740b9d40b47af821dbd57 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 28 Jun 2017 14:52:17 -0700 Subject: Update ScreenRecoveryUI::Draw* function signatures. Move away from taking int* for the Y-offset. Change it to int and return the offset instead. Test: Check the recovery menu and 'Wipe data' menu. Change-Id: Ib15e070a0d576a0f8f66f35605cb8479e7071f26 --- screen_ui.cpp | 45 +++++++++++++++++++++------------------------ screen_ui.h | 10 +++++++--- vr_ui.cpp | 8 ++++---- vr_ui.h | 2 +- wear_ui.cpp | 10 +++++----- 5 files changed, 38 insertions(+), 37 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index 378a4514a..d9574d869 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -177,9 +177,8 @@ void ScreenRecoveryUI::draw_background_locked() { } } -// Draws the animation and progress bar (if any) on the screen. -// Does not flip pages. -// Should only be called with updateMutex locked. +// Draws the animation and progress bar (if any) on the screen. Does not flip pages. Should only be +// called with updateMutex locked. void ScreenRecoveryUI::draw_foreground_locked() { if (currentIcon != NONE) { GRSurface* frame = GetCurrentFrame(); @@ -257,26 +256,26 @@ void ScreenRecoveryUI::SetColor(UIElement e) const { } } -void ScreenRecoveryUI::DrawHorizontalRule(int* y) const { - SetColor(MENU); - *y += 4; - gr_fill(0, *y, gr_fb_width(), *y + 2); - *y += 4; +int ScreenRecoveryUI::DrawHorizontalRule(int y) const { + gr_fill(0, y + 4, gr_fb_width(), y + 6); + return 8; } void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { gr_fill(x, y, x + width, y + height); } -void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { - gr_text(gr_sys_font(), x, *y, line, bold); - *y += char_height_ + 4; +int ScreenRecoveryUI::DrawTextLine(int x, int y, const char* line, bool bold) const { + gr_text(gr_sys_font(), x, y, line, bold); + return char_height_ + 4; } -void ScreenRecoveryUI::DrawTextLines(int x, int* y, const char* const* lines) const { +int ScreenRecoveryUI::DrawTextLines(int x, int y, const char* const* lines) const { + int offset = 0; for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) { - DrawTextLine(x, y, lines[i], false); + offset += DrawTextLine(x, y + offset, lines[i], false); } + return offset; } static const char* REGULAR_HELP[] = { @@ -310,18 +309,17 @@ void ScreenRecoveryUI::draw_screen_locked() { android::base::GetProperty("ro.bootimage.build.fingerprint", ""); SetColor(INFO); - DrawTextLine(x, &y, "Android Recovery", true); + y += DrawTextLine(x, y, "Android Recovery", true); for (const auto& chunk : android::base::Split(recovery_fingerprint, ":")) { - DrawTextLine(x, &y, chunk.c_str(), false); + y += DrawTextLine(x, y, chunk.c_str(), false); } - DrawTextLines(x, &y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); + y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); SetColor(HEADER); - DrawTextLines(x, &y, menu_headers_); + y += DrawTextLines(x, y, menu_headers_); SetColor(MENU); - DrawHorizontalRule(&y); - y += 4; + y += DrawHorizontalRule(y) + 4; for (int i = 0; i < menu_items; ++i) { if (i == menu_sel) { // Draw the highlight bar. @@ -329,13 +327,13 @@ void ScreenRecoveryUI::draw_screen_locked() { DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); // Bold white text for the selected item. SetColor(MENU_SEL_FG); - DrawTextLine(x, &y, menu_[i], true); + y += DrawTextLine(x, y, menu_[i], true); SetColor(MENU); } else { - DrawTextLine(x, &y, menu_[i], false); + y += DrawTextLine(x, y, menu_[i], false); } } - DrawHorizontalRule(&y); + y += DrawHorizontalRule(y); } // Display from the bottom up, until we hit the top of the screen, the bottom of the menu, or @@ -345,8 +343,7 @@ void ScreenRecoveryUI::draw_screen_locked() { size_t count = 0; for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { - int temp_y = ty; - DrawTextLine(x, &temp_y, text_[row], false); + DrawTextLine(x, ty, text_[row], false); --row; if (row < 0) row = text_rows_ - 1; } diff --git a/screen_ui.h b/screen_ui.h index e8d3c3222..8402fac00 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -179,10 +179,14 @@ class ScreenRecoveryUI : public RecoveryUI { virtual int GetProgressBaseline() const; virtual int GetTextBaseline() const; - virtual void DrawHorizontalRule(int* y) const; + // Draws a highlight bar at (x, y) - (x + width, y + height). virtual void DrawHighlightBar(int x, int y, int width, int height) const; - virtual void DrawTextLine(int x, int* y, const char* line, bool bold) const; - void DrawTextLines(int x, int* y, const char* const* lines) const; + // Draws a horizontal rule at Y. Returns the offset it should be moving along Y-axis. + virtual int DrawHorizontalRule(int y) const; + // Draws a line of text. Returns the offset it should be moving along Y-axis. + virtual int DrawTextLine(int x, int y, const char* line, bool bold) const; + // Draws multiple text lines. Returns the offset it should be moving along Y-axis. + int DrawTextLines(int x, int y, const char* const* lines) const; }; #endif // RECOVERY_UI_H diff --git a/vr_ui.cpp b/vr_ui.cpp index 8b8261e35..125167268 100644 --- a/vr_ui.cpp +++ b/vr_ui.cpp @@ -27,9 +27,9 @@ bool VrRecoveryUI::InitTextParams() { return true; } -void VrRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { +int VrRecoveryUI::DrawTextLine(int x, int y, const char* line, bool bold) const { int mid_divide = gr_fb_width() / 2; - gr_text(gr_sys_font(), x + kStereoOffset, *y, line, bold); - gr_text(gr_sys_font(), x - kStereoOffset + mid_divide, *y, line, bold); - *y += char_height_ + 4; + gr_text(gr_sys_font(), x + kStereoOffset, y, line, bold); + gr_text(gr_sys_font(), x - kStereoOffset + mid_divide, y, line, bold); + return char_height_ + 4; } diff --git a/vr_ui.h b/vr_ui.h index da2163402..d996c145f 100644 --- a/vr_ui.h +++ b/vr_ui.h @@ -30,7 +30,7 @@ class VrRecoveryUI : public ScreenRecoveryUI { bool InitTextParams() override; - void DrawTextLine(int x, int* y, const char* line, bool bold) const override; + int DrawTextLine(int x, int y, const char* line, bool bold) const override; }; #endif // RECOVERY_VR_UI_H diff --git a/wear_ui.cpp b/wear_ui.cpp index a29746cdc..18c30d34a 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -95,7 +95,7 @@ void WearRecoveryUI::draw_background_locked() { } } -static const char* HEADERS[] = { +static const char* SWIPE_HELP[] = { "Swipe up/down to move.", "Swipe left/right to select.", "", @@ -119,15 +119,15 @@ void WearRecoveryUI::draw_screen_locked() { std::string recovery_fingerprint = android::base::GetProperty("ro.bootimage.build.fingerprint", ""); SetColor(HEADER); - DrawTextLine(x + 4, &y, "Android Recovery", true); + y += DrawTextLine(x + 4, y, "Android Recovery", true); for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) { - DrawTextLine(x + 4, &y, chunk.c_str(), false); + y += DrawTextLine(x + 4, y, chunk.c_str(), false); } // This is actually the help strings. - DrawTextLines(x + 4, &y, HEADERS); + y += DrawTextLines(x + 4, y, SWIPE_HELP); SetColor(HEADER); - DrawTextLines(x + 4, &y, menu_headers_); + y += DrawTextLines(x + 4, y, menu_headers_); // Show the current menu item number in relation to total number if // items don't fit on the screen. -- cgit v1.2.3 From edc6b52f006eda2cd23c2de654968be347b3f2a5 Mon Sep 17 00:00:00 2001 From: Luke Song Date: Mon, 12 Jun 2017 16:08:33 -0700 Subject: Introduce VR recovery ui A version of screen ui with specific adjustments for vr device compatibility. Cherry picked from commit a44dba7f4e7296077f65fd571232e8a61aed9418 Bug: 37779982 Test: "adb reboot recovery" to view Change-Id: If6b0f26c1b587f8d0176060685b5efb6c67593b1 --- Android.mk | 12 ++++++++++++ screen_ui.cpp | 18 +++++++++++------- screen_ui.h | 6 ++++-- vr_device.cpp | 23 +++++++++++++++++++++++ vr_ui.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ vr_ui.h | 38 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 144 insertions(+), 9 deletions(-) create mode 100644 vr_device.cpp create mode 100644 vr_ui.cpp create mode 100644 vr_ui.h diff --git a/Android.mk b/Android.mk index cfd55a79a..bbb204660 100644 --- a/Android.mk +++ b/Android.mk @@ -76,6 +76,7 @@ LOCAL_SRC_FILES := \ rotate_logs.cpp \ screen_ui.cpp \ ui.cpp \ + vr_ui.cpp \ wear_ui.cpp \ wear_touch.cpp \ @@ -196,6 +197,17 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) +# vr headset default device +# =============================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := vr_device.cpp + +# should match TARGET_RECOVERY_UI_LIB set in BoardConfig.mk +LOCAL_MODULE := librecovery_ui_vr + +include $(BUILD_STATIC_LIBRARY) + include \ $(LOCAL_PATH)/applypatch/Android.mk \ $(LOCAL_PATH)/boot_control/Android.mk \ diff --git a/screen_ui.cpp b/screen_ui.cpp index 7334b713e..ff51b53e1 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -260,6 +260,10 @@ void ScreenRecoveryUI::DrawHorizontalRule(int* y) { *y += 4; } +void ScreenRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { + gr_fill(x, y, x + width, y + height); +} + void ScreenRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { gr_text(gr_sys_font(), x, *y, line, bold); *y += char_height_ + 4; @@ -290,7 +294,6 @@ void ScreenRecoveryUI::draw_screen_locked() { draw_foreground_locked(); return; } - gr_color(0, 0, 0, 255); gr_clear(); @@ -318,15 +321,14 @@ void ScreenRecoveryUI::draw_screen_locked() { if (i == menu_sel) { // Draw the highlight bar. SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - gr_fill(0, y - 2, gr_fb_width(), y + char_height_ + 2); + DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); // Bold white text for the selected item. SetColor(MENU_SEL_FG); - gr_text(gr_sys_font(), 4, y, menu_[i], true); + DrawTextLine(x, &y, menu_[i], true); SetColor(MENU); } else { - gr_text(gr_sys_font(), 4, y, menu_[i], false); + DrawTextLine(x, &y, menu_[i], false); } - y += char_height_ + 4; } DrawHorizontalRule(&y); } @@ -336,9 +338,10 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - kMarginHeight - char_height_; + for (int ty = gr_fb_height() - kMarginHeight - char_height_ - log_bottom_offset_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { - gr_text(gr_sys_font(), 0, ty, text_[row], false); + int temp_y = ty; + DrawTextLine(x, &temp_y, text_[row], false); --row; if (row < 0) row = text_rows_ - 1; } @@ -458,6 +461,7 @@ bool ScreenRecoveryUI::InitTextParams() { gr_font_size(gr_sys_font(), &char_width_, &char_height_); text_rows_ = (gr_fb_height() - kMarginHeight * 2) / char_height_; text_cols_ = (gr_fb_width() - kMarginWidth * 2) / char_width_; + log_bottom_offset_ = 0; return true; } diff --git a/screen_ui.h b/screen_ui.h index 58032d80b..e961c1c93 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -113,6 +113,7 @@ class ScreenRecoveryUI : public RecoveryUI { // Log text overlay, displayed when a magic key is pressed. char** text_; size_t text_col_, text_row_, text_top_; + int log_bottom_offset_; bool show_text; bool show_text_ever; // has show_text ever been true? @@ -172,8 +173,9 @@ class ScreenRecoveryUI : public RecoveryUI { virtual int GetProgressBaseline(); virtual int GetTextBaseline(); - void DrawHorizontalRule(int* y); - void DrawTextLine(int x, int* y, const char* line, bool bold) const; + virtual void DrawHorizontalRule(int* y); + virtual void DrawHighlightBar(int x, int y, int width, int height) const; + virtual void DrawTextLine(int x, int* y, const char* line, bool bold) const; void DrawTextLines(int x, int* y, const char* const* lines) const; }; diff --git a/vr_device.cpp b/vr_device.cpp new file mode 100644 index 000000000..61e15cbb6 --- /dev/null +++ b/vr_device.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 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 "device.h" +#include "vr_ui.h" + +Device* make_device() { + return new Device(new VrRecoveryUI); +} + diff --git a/vr_ui.cpp b/vr_ui.cpp new file mode 100644 index 000000000..b2c65e3af --- /dev/null +++ b/vr_ui.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 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 "vr_ui.h" + +#include + +VrRecoveryUI::VrRecoveryUI() : + x_offset(400), + y_offset(400), + stereo_offset(100) { +} + +bool VrRecoveryUI::InitTextParams() { + if (gr_init() < 0) { + return false; + } + + gr_font_size(gr_sys_font(), &char_width_, &char_height_); + int mid_divide = gr_fb_width() / 2; + text_rows_ = (gr_fb_height() - 2 * y_offset) / char_height_; + text_cols_ = (mid_divide - x_offset - stereo_offset) / char_width_; + log_bottom_offset_ = gr_fb_height() - 2 * y_offset; + return true; +} + +void VrRecoveryUI::DrawHorizontalRule(int* y) { + SetColor(MENU); + *y += 4; + gr_fill(0, *y + y_offset, gr_fb_width(), *y + y_offset + 2); + *y += 4; +} + +void VrRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { + gr_fill(x, y + y_offset, x + width, y + y_offset + height); +} + +void VrRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { + int mid_divide = gr_fb_width() / 2; + gr_text(gr_sys_font(), x + x_offset + stereo_offset, *y + y_offset, line, bold); + gr_text(gr_sys_font(), x + x_offset - stereo_offset + mid_divide, *y + y_offset, line, bold); + *y += char_height_ + 4; +} diff --git a/vr_ui.h b/vr_ui.h new file mode 100644 index 000000000..85c570815 --- /dev/null +++ b/vr_ui.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 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. + */ + +#ifndef RECOVERY_VR_UI_H +#define RECOVERY_VR_UI_H + +#include "screen_ui.h" + +class VrRecoveryUI : public ScreenRecoveryUI { + public: + VrRecoveryUI(); + + protected: + // Pixel offsets to move drawing functions to visible range. + // Can vary per device depending on screen size and lens distortion. + int x_offset, y_offset, stereo_offset; + + bool InitTextParams() override; + + void DrawHorizontalRule(int* y) override; + void DrawHighlightBar(int x, int y, int width, int height) const override; + void DrawTextLine(int x, int* y, const char* line, bool bold) const override; +}; + +#endif // RECOVERY_VR_UI_H -- cgit v1.2.3 From 14ebc1e5ae6968424eb242f3b0330f82e475a1e4 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Wed, 5 Jul 2017 12:04:07 -0700 Subject: Fix a rare failure for imgdiff when random data equals gzip header In a rare case, a random chunk will pass both the gzip header check and the inflation process; but fail the uncompressed length check in the footer. This leads to a imgdiff failure. So, we should treat this chunk as 'normal' instead of 'inflated' while generating the patch. Bug: 63334984 Test: imgdiff generates patch successfully on previous failing images. Change-Id: Ice84f22d3653bce9756bda91e70528c0d2f264a0 --- applypatch/imgdiff.cpp | 25 ++++++++++++++----------- tests/component/imgdiff_test.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/applypatch/imgdiff.cpp b/applypatch/imgdiff.cpp index 41d73ab98..fc240644f 100644 --- a/applypatch/imgdiff.cpp +++ b/applypatch/imgdiff.cpp @@ -693,6 +693,20 @@ static bool ReadImage(const char* filename, std::vector* chunks, continue; } + // The footer contains the size of the uncompressed data. Double-check to make sure that it + // matches the size of the data we got when we actually did the decompression. + size_t footer_index = pos + raw_data_len + GZIP_FOOTER_LEN - 4; + if (sz - footer_index < 4) { + printf("Warning: invalid footer position; treating as a nomal chunk\n"); + continue; + } + size_t footer_size = get_unaligned(img->data() + footer_index); + if (footer_size != uncompressed_len) { + printf("Warning: footer size %zu != decompressed size %zu; treating as a nomal chunk\n", + footer_size, uncompressed_len); + continue; + } + ImageChunk body(CHUNK_DEFLATE, pos, img, raw_data_len); uncompressed_data.resize(uncompressed_len); body.SetUncompressedData(std::move(uncompressed_data)); @@ -704,17 +718,6 @@ static bool ReadImage(const char* filename, std::vector* chunks, chunks->emplace_back(CHUNK_NORMAL, pos, img, GZIP_FOOTER_LEN); pos += GZIP_FOOTER_LEN; - - // The footer (that we just skipped over) contains the size of - // the uncompressed data. Double-check to make sure that it - // matches the size of the data we got when we actually did - // the decompression. - size_t footer_size = get_unaligned(img->data() + pos - 4); - if (footer_size != body.DataLengthForPatch()) { - printf("Error: footer size %zu != decompressed size %zu\n", footer_size, - body.GetRawDataLength()); - return false; - } } else { // Use a normal chunk to take all the contents until the next gzip chunk (or EOF); we expect // the number of chunks to be small (5 for typical boot and recovery images). diff --git a/tests/component/imgdiff_test.cpp b/tests/component/imgdiff_test.cpp index 6f5960bbd..bf25aebb0 100644 --- a/tests/component/imgdiff_test.cpp +++ b/tests/component/imgdiff_test.cpp @@ -328,6 +328,39 @@ TEST(ImgdiffTest, image_mode_simple) { verify_patched_image(src, patch, tgt); } +TEST(ImgdiffTest, image_mode_bad_gzip) { + // Modify the uncompressed length in the gzip footer. + const std::vector src_data = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', '\x1f', '\x8b', '\x08', '\x00', '\xc4', '\x1e', + '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xac', + '\x02', '\x00', '\x67', '\xba', '\x8e', '\xeb', '\x03', + '\xff', '\xff', '\xff' }; + const std::string src(src_data.cbegin(), src_data.cend()); + TemporaryFile src_file; + ASSERT_TRUE(android::base::WriteStringToFile(src, src_file.path)); + + // Modify the uncompressed length in the gzip footer. + const std::vector tgt_data = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z', '\x1f', '\x8b', + '\x08', '\x00', '\x62', '\x1f', '\x53', '\x58', '\x00', '\x03', '\xab', '\xa8', '\xa8', '\xac', + '\xac', '\xaa', '\x02', '\x00', '\x96', '\x30', '\x06', '\xb7', '\x06', '\xff', '\xff', '\xff' + }; + const std::string tgt(tgt_data.cbegin(), tgt_data.cend()); + TemporaryFile tgt_file; + ASSERT_TRUE(android::base::WriteStringToFile(tgt, tgt_file.path)); + + TemporaryFile patch_file; + std::vector args = { + "imgdiff", src_file.path, tgt_file.path, patch_file.path, + }; + ASSERT_EQ(0, imgdiff(args.size(), args.data())); + + // Verify. + std::string patch; + ASSERT_TRUE(android::base::ReadFileToString(patch_file.path, &patch)); + verify_patched_image(src, patch, tgt); +} + TEST(ImgdiffTest, image_mode_different_num_chunks) { // src: "abcdefgh" + gzipped "xyz" (echo -n "xyz" | gzip -f | hd) + gzipped "test". const std::vector src_data = { -- cgit v1.2.3 From c392888df752479ffaeee2fa9e624d71ce2a4618 Mon Sep 17 00:00:00 2001 From: Luke Song Date: Fri, 23 Jun 2017 14:33:46 -0700 Subject: Restructure vr_ui Get rid of pixel offset variables, and use makefile variables in BoardConfigs. Cherry picked from commit 81a8e4cab2a20fd1b1a4716563d4d2586bd1e1de Bug: 37779982 Test: Verified vr ui has same behavior. Change-Id: Ifbf44e27d7101aedbe3c0e6db4b8181d56efadfd --- Android.mk | 6 ++++++ screen_ui.cpp | 3 +-- screen_ui.h | 1 - vr_ui.cpp | 31 +++++-------------------------- vr_ui.h | 4 +--- 5 files changed, 13 insertions(+), 32 deletions(-) diff --git a/Android.mk b/Android.mk index bbb204660..95c8823e3 100644 --- a/Android.mk +++ b/Android.mk @@ -107,6 +107,12 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0 endif +ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) +LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) +else +LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=0 +endif + LOCAL_C_INCLUDES += \ system/vold \ diff --git a/screen_ui.cpp b/screen_ui.cpp index ff51b53e1..a7d9c9f4b 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -338,7 +338,7 @@ void ScreenRecoveryUI::draw_screen_locked() { SetColor(LOG); int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - kMarginHeight - char_height_ - log_bottom_offset_; + for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { int temp_y = ty; DrawTextLine(x, &temp_y, text_[row], false); @@ -461,7 +461,6 @@ bool ScreenRecoveryUI::InitTextParams() { gr_font_size(gr_sys_font(), &char_width_, &char_height_); text_rows_ = (gr_fb_height() - kMarginHeight * 2) / char_height_; text_cols_ = (gr_fb_width() - kMarginWidth * 2) / char_width_; - log_bottom_offset_ = 0; return true; } diff --git a/screen_ui.h b/screen_ui.h index e961c1c93..2500575ca 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -113,7 +113,6 @@ class ScreenRecoveryUI : public RecoveryUI { // Log text overlay, displayed when a magic key is pressed. char** text_; size_t text_col_, text_row_, text_top_; - int log_bottom_offset_; bool show_text; bool show_text_ever; // has show_text ever been true? diff --git a/vr_ui.cpp b/vr_ui.cpp index b2c65e3af..8b8261e35 100644 --- a/vr_ui.cpp +++ b/vr_ui.cpp @@ -18,39 +18,18 @@ #include -VrRecoveryUI::VrRecoveryUI() : - x_offset(400), - y_offset(400), - stereo_offset(100) { -} +VrRecoveryUI::VrRecoveryUI() : kStereoOffset(RECOVERY_UI_VR_STEREO_OFFSET) {} bool VrRecoveryUI::InitTextParams() { - if (gr_init() < 0) { - return false; - } - - gr_font_size(gr_sys_font(), &char_width_, &char_height_); + if (!ScreenRecoveryUI::InitTextParams()) return false; int mid_divide = gr_fb_width() / 2; - text_rows_ = (gr_fb_height() - 2 * y_offset) / char_height_; - text_cols_ = (mid_divide - x_offset - stereo_offset) / char_width_; - log_bottom_offset_ = gr_fb_height() - 2 * y_offset; + text_cols_ = (mid_divide - kMarginWidth - kStereoOffset) / char_width_; return true; } -void VrRecoveryUI::DrawHorizontalRule(int* y) { - SetColor(MENU); - *y += 4; - gr_fill(0, *y + y_offset, gr_fb_width(), *y + y_offset + 2); - *y += 4; -} - -void VrRecoveryUI::DrawHighlightBar(int x, int y, int width, int height) const { - gr_fill(x, y + y_offset, x + width, y + y_offset + height); -} - void VrRecoveryUI::DrawTextLine(int x, int* y, const char* line, bool bold) const { int mid_divide = gr_fb_width() / 2; - gr_text(gr_sys_font(), x + x_offset + stereo_offset, *y + y_offset, line, bold); - gr_text(gr_sys_font(), x + x_offset - stereo_offset + mid_divide, *y + y_offset, line, bold); + gr_text(gr_sys_font(), x + kStereoOffset, *y, line, bold); + gr_text(gr_sys_font(), x - kStereoOffset + mid_divide, *y, line, bold); *y += char_height_ + 4; } diff --git a/vr_ui.h b/vr_ui.h index 85c570815..31ca4a61d 100644 --- a/vr_ui.h +++ b/vr_ui.h @@ -26,12 +26,10 @@ class VrRecoveryUI : public ScreenRecoveryUI { protected: // Pixel offsets to move drawing functions to visible range. // Can vary per device depending on screen size and lens distortion. - int x_offset, y_offset, stereo_offset; + const int kStereoOffset; bool InitTextParams() override; - void DrawHorizontalRule(int* y) override; - void DrawHighlightBar(int x, int y, int width, int height) const override; void DrawTextLine(int x, int* y, const char* line, bool bold) const override; }; -- cgit v1.2.3 From 107a34f9fc2cb2cfbb1ff83631f778c2c147db22 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Thu, 29 Jun 2017 17:04:21 -0700 Subject: Add support to decompress brotli compressed new data Add a new writer that can decode the brotli-compressed system/vendor new data stored in the OTA zip. Brotli generally gives better compression rate at the cost of slightly increased time consumption. The patch.dat is already compressed by BZ; so there's no point to further compress it. For the given 1.9G bullhead system image: Size: 875M -> 787M; ~10% reduction of package size. Time: 147s -> 153s; ~4% increase of the block_image_update execution time. (I guess I/O takes much longer time than decompression.) Also it takes 4 minutes to compress the system image on my local machine, 3 more minutes than zip. Test: recovery tests pass && apply a full OTA with brotli compressed system/vendor.new.dat on bullhead Change-Id: I232335ebf662a9c55579ca073ad45265700a621e --- tests/Android.mk | 1 + tests/component/updater_test.cpp | 67 +++++++++++++++ updater/Android.mk | 1 + updater/blockimg.cpp | 175 ++++++++++++++++++++++++++++++++------- 4 files changed, 212 insertions(+), 32 deletions(-) diff --git a/tests/Android.mk b/tests/Android.mk index 346873dbe..8b1dc1099 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -160,6 +160,7 @@ LOCAL_STATIC_LIBRARIES := \ libfec_rs \ libsquashfs_utils \ libcutils \ + libbrotli \ $(tune2fs_static_libraries) testdata_files := $(call find-subdir-files, testdata/*) diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 0298a7645..357a39ef7 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -15,10 +15,12 @@ */ #include +#include #include #include #include +#include #include #include #include @@ -29,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -576,4 +579,68 @@ TEST_F(UpdaterTest, new_data_short_write) { std::string script_exact_data = "block_image_update(\"" + std::string(update_file.path) + R"(", package_extract_file("transfer_list"), "exact_new_data", "patch_data"))"; expect("t", script_exact_data.c_str(), kNoCause, &updater_info); + CloseArchive(handle); +} + +TEST_F(UpdaterTest, brotli_new_data) { + // Create a zip file with new_data. + TemporaryFile zip_file; + FILE* zip_file_ptr = fdopen(zip_file.fd, "wb"); + ZipWriter zip_writer(zip_file_ptr); + + // Add a brotli compressed new data entry. + ASSERT_EQ(0, zip_writer.StartEntry("new.dat.br", 0)); + + auto generator = []() { return rand() % 128; }; + // Generate 2048 blocks of random data. + std::string brotli_new_data; + brotli_new_data.reserve(4096 * 2048); + generate_n(back_inserter(brotli_new_data), 4096 * 2048, generator); + + size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size()); + std::vector encoded_data(encoded_size); + ASSERT_TRUE(BrotliEncoderCompress( + BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, BROTLI_DEFAULT_MODE, brotli_new_data.size(), + reinterpret_cast(brotli_new_data.data()), &encoded_size, encoded_data.data())); + + ASSERT_EQ(0, zip_writer.WriteBytes(encoded_data.data(), encoded_size)); + ASSERT_EQ(0, zip_writer.FinishEntry()); + // Add a dummy patch data. + ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); + ASSERT_EQ(0, zip_writer.FinishEntry()); + + std::vector transfer_list = { + "4", "2048", "0", "0", "new 4,0,512,512,1024", "new 2,1024,2048", + }; + ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); + std::string commands = android::base::Join(transfer_list, '\n'); + ASSERT_EQ(0, zip_writer.WriteBytes(commands.data(), commands.size())); + ASSERT_EQ(0, zip_writer.FinishEntry()); + ASSERT_EQ(0, zip_writer.Finish()); + ASSERT_EQ(0, fclose(zip_file_ptr)); + + MemMapping map; + ASSERT_TRUE(map.MapFile(zip_file.path)); + ZipArchiveHandle handle; + ASSERT_EQ(0, OpenArchiveFromMemory(map.addr, map.length, zip_file.path, &handle)); + + // Set up the handler, command_pipe, patch offset & length. + UpdaterInfo updater_info; + updater_info.package_zip = handle; + TemporaryFile temp_pipe; + updater_info.cmd_pipe = fopen(temp_pipe.path, "wb"); + updater_info.package_zip_addr = map.addr; + updater_info.package_zip_len = map.length; + + // Check if we can decompress the new data correctly. + TemporaryFile update_file; + std::string script_new_data = + "block_image_update(\"" + std::string(update_file.path) + + R"(", package_extract_file("transfer_list"), "new.dat.br", "patch_data"))"; + expect("t", script_new_data.c_str(), kNoCause, &updater_info); + + std::string updated_content; + ASSERT_TRUE(android::base::ReadFileToString(update_file.path, &updated_content)); + ASSERT_EQ(brotli_new_data, updated_content); + CloseArchive(handle); } diff --git a/updater/Android.mk b/updater/Android.mk index a113fe86c..86dc48e30 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -47,6 +47,7 @@ updater_common_static_libraries := \ libcrypto_utils \ libcutils \ libtune2fs \ + libbrotli \ $(tune2fs_static_libraries) # libupdater (static library) diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index df366b0b8..2bec487fe 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -149,40 +150,32 @@ static void allocate(size_t size, std::vector& buffer) { class RangeSinkWriter { public: RangeSinkWriter(int fd, const RangeSet& tgt) - : fd_(fd), tgt_(tgt), next_range_(0), current_range_left_(0), bytes_written_(0) { + : fd_(fd), + tgt_(tgt), + next_range_(0), + current_range_left_(0), + bytes_written_(0) { CHECK_NE(tgt.size(), static_cast(0)); }; + virtual ~RangeSinkWriter() {}; + bool Finished() const { return next_range_ == tgt_.size() && current_range_left_ == 0; } - size_t Write(const uint8_t* data, size_t size) { + // Return number of bytes consumed; and 0 indicates a writing failure. + virtual size_t Write(const uint8_t* data, size_t size) { if (Finished()) { LOG(ERROR) << "range sink write overrun; can't write " << size << " bytes"; return 0; } - size_t written = 0; + size_t consumed = 0; while (size > 0) { // Move to the next range as needed. - if (current_range_left_ == 0) { - if (next_range_ < tgt_.size()) { - const Range& range = tgt_[next_range_]; - off64_t offset = static_cast(range.first) * BLOCKSIZE; - current_range_left_ = (range.second - range.first) * BLOCKSIZE; - next_range_++; - if (!discard_blocks(fd_, offset, current_range_left_)) { - break; - } - - if (!check_lseek(fd_, offset, SEEK_SET)) { - break; - } - } else { - // We can't write any more; return how many bytes have been written so far. - break; - } + if (!SeekToOutputRange()) { + break; } size_t write_now = size; @@ -198,21 +191,47 @@ class RangeSinkWriter { size -= write_now; current_range_left_ -= write_now; - written += write_now; + consumed += write_now; } - bytes_written_ += written; - return written; + bytes_written_ += consumed; + return consumed; } size_t BytesWritten() const { return bytes_written_; } - private: - // The input data. + protected: + // Set up the output cursor, move to next range if needed. + bool SeekToOutputRange() { + // We haven't finished the current range yet. + if (current_range_left_ != 0) { + return true; + } + // We can't write any more; let the write function return how many bytes have been written + // so far. + if (next_range_ >= tgt_.size()) { + return false; + } + + const Range& range = tgt_[next_range_]; + off64_t offset = static_cast(range.first) * BLOCKSIZE; + current_range_left_ = (range.second - range.first) * BLOCKSIZE; + next_range_++; + + if (!discard_blocks(fd_, offset, current_range_left_)) { + return false; + } + if (!check_lseek(fd_, offset, SEEK_SET)) { + return false; + } + return true; + } + + // The output file descriptor. int fd_; - // The destination for the data. + // The destination ranges for the data. const RangeSet& tgt_; // The next range that we should write to. size_t next_range_; @@ -222,6 +241,75 @@ class RangeSinkWriter { size_t bytes_written_; }; +class BrotliNewDataWriter : public RangeSinkWriter { + public: + BrotliNewDataWriter(int fd, const RangeSet& tgt, BrotliDecoderState* state) + : RangeSinkWriter(fd, tgt), state_(state) {} + + size_t Write(const uint8_t* data, size_t size) override { + if (Finished()) { + LOG(ERROR) << "Brotli new data write overrun; can't write " << size << " bytes"; + return 0; + } + CHECK(state_ != nullptr); + + size_t consumed = 0; + while (true) { + // Move to the next range as needed. + if (!SeekToOutputRange()) { + break; + } + + size_t available_in = size; + size_t write_now = std::min(32768, current_range_left_); + uint8_t buffer[write_now]; + + size_t available_out = write_now; + uint8_t* next_out = buffer; + + // The brotli decoder will update |data|, |available_in|, |next_out| and |available_out|. + BrotliDecoderResult result = BrotliDecoderDecompressStream( + state_, &available_in, &data, &available_out, &next_out, nullptr); + + // We don't have a way to recover from the decode error; report the failure. + if (result == BROTLI_DECODER_RESULT_ERROR) { + LOG(ERROR) << "Decompression failed with " + << BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state_)); + return 0; + } + + if (write_all(fd_, buffer, write_now - available_out) == -1) { + return 0; + } + + LOG(DEBUG) << "bytes written: " << write_now - available_out << ", bytes consumed " + << size - available_in << ", decoder status " << result; + + // Update the total bytes written to output by the current writer; this is different from the + // consumed input bytes. + bytes_written_ += write_now - available_out; + current_range_left_ -= (write_now - available_out); + consumed += (size - available_in); + + // Update the remaining size. The input data ptr is already updated by brotli decoder + // function. + size = available_in; + + // Continue if we have more output to write, or more input to consume. + if (result == BROTLI_DECODER_RESULT_SUCCESS || + (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && size == 0)) { + break; + } + } + + return consumed; + } + + private: + // Pointer to the decoder state. (initialized by PerformBlockImageUpdate) + BrotliDecoderState* state_; +}; + /** * All of the data for all the 'new' transfers is contained in one file in the update package, * concatenated together in the order in which transfers.list will need it. We want to stream it out @@ -243,8 +331,10 @@ class RangeSinkWriter { struct NewThreadInfo { ZipArchiveHandle za; ZipEntry entry; + bool brotli_compressed; - RangeSinkWriter* writer; + std::unique_ptr writer; + BrotliDecoderState* brotli_decoder_state; bool receiver_available; pthread_mutex_t mu; @@ -264,9 +354,16 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { // At this point nti->writer is set, and we own it. The main thread is waiting for it to // disappear from nti. - size_t written = nti->writer->Write(data, size); - data += written; - size -= written; + size_t consumed = nti->writer->Write(data, size); + + // We encounter a fatal error if we fail to consume any input bytes. If this happens, abort the + // extraction. + if (consumed == 0) { + LOG(ERROR) << "Failed to process " << size << " input bytes."; + return false; + } + data += consumed; + size -= consumed; if (nti->writer->Finished()) { // We have written all the bytes desired by this writer. @@ -1142,9 +1239,13 @@ static int PerformCommandNew(CommandParameters& params) { if (params.canwrite) { LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data"; - RangeSinkWriter writer(params.fd, tgt); pthread_mutex_lock(¶ms.nti.mu); - params.nti.writer = &writer; + if (params.nti.brotli_compressed) { + params.nti.writer = + std::make_unique(params.fd, tgt, params.nti.brotli_decoder_state); + } else { + params.nti.writer = std::make_unique(params.fd, tgt); + } pthread_cond_broadcast(¶ms.nti.cv); while (params.nti.writer != nullptr) { @@ -1384,6 +1485,12 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, if (params.canwrite) { params.nti.za = za; params.nti.entry = new_entry; + // The entry is compressed by brotli if has a 'br' extension. + params.nti.brotli_compressed = android::base::EndsWith(new_data_fn->data, ".br"); + if (params.nti.brotli_compressed) { + // Initialize brotli decoder state. + params.nti.brotli_decoder_state = BrotliDecoderCreateInstance(nullptr, nullptr, nullptr); + } params.nti.receiver_available = true; pthread_mutex_init(¶ms.nti.mu, nullptr); @@ -1526,6 +1633,10 @@ pbiudone: } // params.fd will be automatically closed because it's a unique_fd. + if (params.nti.brotli_decoder_state != nullptr) { + BrotliDecoderDestroyInstance(params.nti.brotli_decoder_state); + } + // Only delete the stash if the update cannot be resumed, or it's a verification run and we // created the stash. if (params.isunresumable || (!params.canwrite && params.createdstash)) { -- cgit v1.2.3 From 28b67f8eea62fa1521b988e2c42db87ff001ec26 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 10 Jul 2017 13:22:17 -0700 Subject: Import translations. DO NOT MERGE Change-Id: If9d38fc1db0f4abc498c5229aa6d45150b0ef5e7 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- tools/recovery_l10n/res/values-hi/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/recovery_l10n/res/values-hi/strings.xml b/tools/recovery_l10n/res/values-hi/strings.xml index de8757848..a8a876ee4 100644 --- a/tools/recovery_l10n/res/values-hi/strings.xml +++ b/tools/recovery_l10n/res/values-hi/strings.xml @@ -4,6 +4,6 @@ "सिस्टम अपडेट इंस्टॉल किया जा रहा है" "मिटाया जा रहा है" "कोई आदेश नहीं" - "त्रुटि!" + "गड़बड़ी!" "सुरक्षा अपडेट इंस्टॉल किया जा रहा है" -- cgit v1.2.3 From ca6ce2c848eb343021a19e51d8384b1da0c2266b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 13 Jul 2017 11:15:27 -0700 Subject: Fix the missing char when showing recovery logs. This is a regression when adding support for margin space in commit 171b4c4c. We're losing one char when showing long log lines. Because text_ is aware of kMarginWidth, but not the the added indent (TEXT_INDENT). Test: 'View recovery logs' on angler, with no missing character. Change-Id: I284d54681d603e85e69d8e9c25173b1437a704df --- screen_ui.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index d9574d869..8f792f162 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -301,15 +301,15 @@ void ScreenRecoveryUI::draw_screen_locked() { gr_color(0, 0, 0, 255); gr_clear(); - static constexpr int TEXT_INDENT = 4; - int x = TEXT_INDENT + kMarginWidth; int y = kMarginHeight; if (show_menu) { - std::string recovery_fingerprint = - android::base::GetProperty("ro.bootimage.build.fingerprint", ""); + static constexpr int kMenuIndent = 4; + int x = kMarginWidth + kMenuIndent; SetColor(INFO); y += DrawTextLine(x, y, "Android Recovery", true); + std::string recovery_fingerprint = + android::base::GetProperty("ro.bootimage.build.fingerprint", ""); for (const auto& chunk : android::base::Split(recovery_fingerprint, ":")) { y += DrawTextLine(x, y, chunk.c_str(), false); } @@ -343,7 +343,7 @@ void ScreenRecoveryUI::draw_screen_locked() { size_t count = 0; for (int ty = gr_fb_height() - kMarginHeight - char_height_; ty >= y && count < text_rows_; ty -= char_height_, ++count) { - DrawTextLine(x, ty, text_[row], false); + DrawTextLine(kMarginWidth, ty, text_[row], false); --row; if (row < 0) row = text_rows_ - 1; } -- cgit v1.2.3 From 7af933b6a6fd687bd17710ef6fda0ad5483e4d6d Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 11 Jul 2017 16:45:04 -0700 Subject: Remove the obsolete reference to /file_contexts. This file no longer exists: - /file_contexts has been split into plat_file_contexts and nonplat_file_contexts since commit b236eb6ca204cefcb926e19bd5682f9dcad4021d (system/sepolicy). - It was named /file_contexts.bin prior to the split. '-S file_contexts' is also no longer required by e2fsdroid, since commit 2fff6fb036cbbb6dedd7da3d208b312a9038a5ce (external/e2fsprogs). It will load the file contexts via libselinux. Test: Trigger the path by performing a data wipe for converting to FBE. Change-Id: I179939da409e5c0415ae0ea0bf5ddb23f9e6331e --- roots.cpp | 4 +--- updater/install.cpp | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/roots.cpp b/roots.cpp index e98dfd448..c4afd5de3 100644 --- a/roots.cpp +++ b/roots.cpp @@ -260,8 +260,6 @@ int format_volume(const char* volume, const char* directory) { if (result == 0 && directory != nullptr) { const char* e2fsdroid_argv[] = { "/sbin/e2fsdroid_static", "-e", - "-S", - "/file_contexts", "-f", directory, "-a", @@ -270,7 +268,7 @@ int format_volume(const char* volume, const char* directory) { nullptr }; result = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); - } + } } else { /* Has to be f2fs because we checked earlier. */ if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { LOG(ERROR) << "format_volume: crypt footer + negative length (" << length diff --git a/updater/install.cpp b/updater/install.cpp index c9a3a0799..bfe91e7f9 100644 --- a/updater/install.cpp +++ b/updater/install.cpp @@ -322,8 +322,7 @@ Value* FormatFn(const char* name, State* state, const std::vector(e2fsdroid_argv)); if (status != 0) { -- cgit v1.2.3 From de6735e80cc65be50381388640d94f1b1d0f20fa Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 10 Jul 2017 15:13:33 -0700 Subject: Fix the android-cloexec-* warnings in bootable/recovery Add the O_CLOEXEC or 'e' accordingly. Bug: 63510015 Test: recovery tests pass Change-Id: I7094bcc6af22c9687eb535116b2ca6a59178b303 --- install.cpp | 2 +- minui/resources.cpp | 2 +- recovery-persist.cpp | 30 ++++---- recovery.cpp | 48 ++++++------- tests/component/updater_test.cpp | 4 +- tests/manual/recovery_test.cpp | 2 +- verifier.cpp | 143 +++++++++++++++++++-------------------- 7 files changed, 115 insertions(+), 116 deletions(-) diff --git a/install.cpp b/install.cpp index 7ba8f0139..7fbf5c01f 100644 --- a/install.cpp +++ b/install.cpp @@ -265,7 +265,7 @@ int update_binary_command(const std::string& package, ZipArchiveHandle zip, } unlink(binary_path.c_str()); - int fd = creat(binary_path.c_str(), 0755); + int fd = open(binary_path.c_str(), O_CREAT | O_WRONLY | O_TRUNC | O_CLOEXEC, 0755); if (fd == -1) { PLOG(ERROR) << "Failed to create " << binary_path; return INSTALL_ERROR; diff --git a/minui/resources.cpp b/minui/resources.cpp index 86c731b02..8f8d36d27 100644 --- a/minui/resources.cpp +++ b/minui/resources.cpp @@ -56,7 +56,7 @@ static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr, snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name); resPath[sizeof(resPath)-1] = '\0'; - FILE* fp = fopen(resPath, "rb"); + FILE* fp = fopen(resPath, "rbe"); if (fp == NULL) { result = -1; goto exit; diff --git a/recovery-persist.cpp b/recovery-persist.cpp index d706ccac8..dbce7ff74 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -59,21 +59,21 @@ static void check_and_fclose(FILE *fp, const char *name) { } static void copy_file(const char* source, const char* destination) { - FILE* dest_fp = fopen(destination, "w"); - if (dest_fp == nullptr) { - PLOG(ERROR) << "Can't open " << destination; - } else { - FILE* source_fp = fopen(source, "r"); - if (source_fp != nullptr) { - char buf[4096]; - size_t bytes; - while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) { - fwrite(buf, 1, bytes, dest_fp); - } - check_and_fclose(source_fp, source); - } - check_and_fclose(dest_fp, destination); + FILE* dest_fp = fopen(destination, "we"); + if (dest_fp == nullptr) { + PLOG(ERROR) << "Can't open " << destination; + } else { + FILE* source_fp = fopen(source, "re"); + if (source_fp != nullptr) { + char buf[4096]; + size_t bytes; + while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) { + fwrite(buf, 1, bytes, dest_fp); + } + check_and_fclose(source_fp, source); } + check_and_fclose(dest_fp, destination); + } } static bool rotated = false; @@ -120,7 +120,7 @@ int main(int argc, char **argv) { */ bool has_cache = false; static const char mounts_file[] = "/proc/mounts"; - FILE *fp = fopen(mounts_file, "r"); + FILE* fp = fopen(mounts_file, "re"); if (!fp) { PLOG(ERROR) << "failed to open " << mounts_file; } else { diff --git a/recovery.cpp b/recovery.cpp index 122b89d0b..e2d993e23 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -249,7 +249,7 @@ static void redirect_stdio(const char* filename) { auto start = std::chrono::steady_clock::now(); // Child logger to actually write to the log file. - FILE* log_fp = fopen(filename, "a"); + FILE* log_fp = fopen(filename, "ae"); if (log_fp == nullptr) { PLOG(ERROR) << "fopen \"" << filename << "\" failed"; close(pipefd[0]); @@ -418,27 +418,27 @@ static void copy_log_file_to_pmsg(const char* source, const char* destination) { static off_t tmplog_offset = 0; static void copy_log_file(const char* source, const char* destination, bool append) { - FILE* dest_fp = fopen_path(destination, append ? "a" : "w"); - if (dest_fp == nullptr) { - PLOG(ERROR) << "Can't open " << destination; - } else { - FILE* source_fp = fopen(source, "r"); - if (source_fp != nullptr) { - if (append) { - fseeko(source_fp, tmplog_offset, SEEK_SET); // Since last write - } - char buf[4096]; - size_t bytes; - while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) { - fwrite(buf, 1, bytes, dest_fp); - } - if (append) { - tmplog_offset = ftello(source_fp); - } - check_and_fclose(source_fp, source); - } - check_and_fclose(dest_fp, destination); + FILE* dest_fp = fopen_path(destination, append ? "ae" : "we"); + if (dest_fp == nullptr) { + PLOG(ERROR) << "Can't open " << destination; + } else { + FILE* source_fp = fopen(source, "re"); + if (source_fp != nullptr) { + if (append) { + fseeko(source_fp, tmplog_offset, SEEK_SET); // Since last write + } + char buf[4096]; + size_t bytes; + while ((bytes = fread(buf, 1, sizeof(buf), source_fp)) != 0) { + fwrite(buf, 1, bytes, dest_fp); + } + if (append) { + tmplog_offset = ftello(source_fp); + } + check_and_fclose(source_fp, source); } + check_and_fclose(dest_fp, destination); + } } static void copy_logs() { @@ -487,7 +487,7 @@ static void finish_recovery() { if (!locale.empty() && has_cache) { LOG(INFO) << "Saving locale \"" << locale << "\""; - FILE* fp = fopen_path(LOCALE_FILE, "w"); + FILE* fp = fopen_path(LOCALE_FILE, "we"); if (!android::base::WriteStringToFd(locale, fileno(fp))) { PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE; } @@ -551,7 +551,7 @@ static bool erase_volume(const char* volume) { } std::string data(sb.st_size, '\0'); - FILE* f = fopen(path.c_str(), "rb"); + FILE* f = fopen(path.c_str(), "rbe"); fread(&data[0], 1, data.size(), f); fclose(f); @@ -579,7 +579,7 @@ static bool erase_volume(const char* volume) { ui->Print("Failed to make convert_fbe dir %s\n", strerror(errno)); return true; } - FILE* f = fopen(CONVERT_FBE_FILE, "wb"); + FILE* f = fopen(CONVERT_FBE_FILE, "wbe"); if (!f) { ui->Print("Failed to convert to file encryption %s\n", strerror(errno)); return true; diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 357a39ef7..01b86f224 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -485,7 +485,7 @@ TEST_F(UpdaterTest, block_image_update) { UpdaterInfo updater_info; updater_info.package_zip = handle; TemporaryFile temp_pipe; - updater_info.cmd_pipe = fopen(temp_pipe.path, "wb"); + updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe"); updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; @@ -561,7 +561,7 @@ TEST_F(UpdaterTest, new_data_short_write) { UpdaterInfo updater_info; updater_info.package_zip = handle; TemporaryFile temp_pipe; - updater_info.cmd_pipe = fopen(temp_pipe.path, "wb"); + updater_info.cmd_pipe = fopen(temp_pipe.path, "wbe"); updater_info.package_zip_addr = map.addr; updater_info.package_zip_len = map.length; diff --git a/tests/manual/recovery_test.cpp b/tests/manual/recovery_test.cpp index d36dd331e..92c6ef2d4 100644 --- a/tests/manual/recovery_test.cpp +++ b/tests/manual/recovery_test.cpp @@ -141,7 +141,7 @@ class ResourceTest : public testing::TestWithParam { // under recovery. void SetUp() override { std::string file_path = GetParam(); - fp = fopen(file_path.c_str(), "rb"); + fp = fopen(file_path.c_str(), "rbe"); ASSERT_NE(nullptr, fp); unsigned char header[8]; diff --git a/verifier.cpp b/verifier.cpp index 2ef9c4c37..18437fb7a 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -474,81 +474,80 @@ std::unique_ptr parse_ec_key(FILE* file) { // Otherwise returns false if the file failed to parse, or if it contains zero // keys. The contents in certs would be unspecified on failure. bool load_keys(const char* filename, std::vector& certs) { - std::unique_ptr f(fopen(filename, "r"), fclose); - if (!f) { - PLOG(ERROR) << "error opening " << filename; - return false; - } - - while (true) { - certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); - Certificate& cert = certs.back(); - uint32_t exponent = 0; - - char start_char; - if (fscanf(f.get(), " %c", &start_char) != 1) return false; - if (start_char == '{') { - // a version 1 key has no version specifier. - cert.key_type = Certificate::KEY_TYPE_RSA; - exponent = 3; - cert.hash_len = SHA_DIGEST_LENGTH; - } else if (start_char == 'v') { - int version; - if (fscanf(f.get(), "%d {", &version) != 1) return false; - switch (version) { - case 2: - cert.key_type = Certificate::KEY_TYPE_RSA; - exponent = 65537; - cert.hash_len = SHA_DIGEST_LENGTH; - break; - case 3: - cert.key_type = Certificate::KEY_TYPE_RSA; - exponent = 3; - cert.hash_len = SHA256_DIGEST_LENGTH; - break; - case 4: - cert.key_type = Certificate::KEY_TYPE_RSA; - exponent = 65537; - cert.hash_len = SHA256_DIGEST_LENGTH; - break; - case 5: - cert.key_type = Certificate::KEY_TYPE_EC; - cert.hash_len = SHA256_DIGEST_LENGTH; - break; - default: - return false; - } - } + std::unique_ptr f(fopen(filename, "re"), fclose); + if (!f) { + PLOG(ERROR) << "error opening " << filename; + return false; + } - if (cert.key_type == Certificate::KEY_TYPE_RSA) { - cert.rsa = parse_rsa_key(f.get(), exponent); - if (!cert.rsa) { - return false; - } + while (true) { + certs.emplace_back(0, Certificate::KEY_TYPE_RSA, nullptr, nullptr); + Certificate& cert = certs.back(); + uint32_t exponent = 0; + + char start_char; + if (fscanf(f.get(), " %c", &start_char) != 1) return false; + if (start_char == '{') { + // a version 1 key has no version specifier. + cert.key_type = Certificate::KEY_TYPE_RSA; + exponent = 3; + cert.hash_len = SHA_DIGEST_LENGTH; + } else if (start_char == 'v') { + int version; + if (fscanf(f.get(), "%d {", &version) != 1) return false; + switch (version) { + case 2: + cert.key_type = Certificate::KEY_TYPE_RSA; + exponent = 65537; + cert.hash_len = SHA_DIGEST_LENGTH; + break; + case 3: + cert.key_type = Certificate::KEY_TYPE_RSA; + exponent = 3; + cert.hash_len = SHA256_DIGEST_LENGTH; + break; + case 4: + cert.key_type = Certificate::KEY_TYPE_RSA; + exponent = 65537; + cert.hash_len = SHA256_DIGEST_LENGTH; + break; + case 5: + cert.key_type = Certificate::KEY_TYPE_EC; + cert.hash_len = SHA256_DIGEST_LENGTH; + break; + default: + return false; + } + } - LOG(INFO) << "read key e=" << exponent << " hash=" << cert.hash_len; - } else if (cert.key_type == Certificate::KEY_TYPE_EC) { - cert.ec = parse_ec_key(f.get()); - if (!cert.ec) { - return false; - } - } else { - LOG(ERROR) << "Unknown key type " << cert.key_type; - return false; - } + if (cert.key_type == Certificate::KEY_TYPE_RSA) { + cert.rsa = parse_rsa_key(f.get(), exponent); + if (!cert.rsa) { + return false; + } - // if the line ends in a comma, this file has more keys. - int ch = fgetc(f.get()); - if (ch == ',') { - // more keys to come. - continue; - } else if (ch == EOF) { - break; - } else { - LOG(ERROR) << "unexpected character between keys"; - return false; - } + LOG(INFO) << "read key e=" << exponent << " hash=" << cert.hash_len; + } else if (cert.key_type == Certificate::KEY_TYPE_EC) { + cert.ec = parse_ec_key(f.get()); + if (!cert.ec) { + return false; + } + } else { + LOG(ERROR) << "Unknown key type " << cert.key_type; + return false; } - return true; + // if the line ends in a comma, this file has more keys. + int ch = fgetc(f.get()); + if (ch == ',') { + // more keys to come. + continue; + } else if (ch == EOF) { + break; + } else { + LOG(ERROR) << "unexpected character between keys"; + return false; + } + } + return true; } -- cgit v1.2.3 From d774ff247e65426be7482921e3aac7a4323527f8 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Wed, 19 Jul 2017 18:51:03 +0900 Subject: Build libminui with BOARD_VNDK_VERSION Use libdrm_platform which is a platform variant of libdrm. Bug: 63741047 Bug: 37342627 Test: BOARD_VNDK_VERSION=current m -j libminui Test: ryu recovery graphics test shows various graphics Change-Id: Ifd2c1432781a96538585cdf818aa728d628a2f5a Merged-In: Ifd2c1432781a96538585cdf818aa728d628a2f5a (cherry picked from commit a48c494f138ddb99320583d00120674be1e3297c) --- minui/Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/minui/Android.mk b/minui/Android.mk index 4dfc65f8a..6522fcfd2 100644 --- a/minui/Android.mk +++ b/minui/Android.mk @@ -25,7 +25,7 @@ LOCAL_SRC_FILES := \ LOCAL_WHOLE_STATIC_LIBRARIES := \ libadf \ - libdrm \ + libdrm_platform \ libsync_recovery LOCAL_STATIC_LIBRARIES := \ -- cgit v1.2.3 From d9759e8a7ba89d7005d86cf8c8e3e561d04e56a4 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Fri, 21 Jul 2017 21:06:52 +0000 Subject: Fix a case when brotli writer fails to write last few blocks of data receive_new_data may exit too early if the zip processor has sent all the raw data. As a result, the last few 'new' commands will fail even though the brotli decoder has more output in its buffer. Restruct the code so that 'NewThreadInfo' owns the decoder state solely; and receive_brotli_new_data is responsible for the decompression. Also reduce the test data size to 100 blocks to avoid the test timeout. Bug: 63802629 Test: recovery_component_test. on bullhead, apply full updates with and w/o brotli compressed entries, apply an incremental update. Change-Id: Id429b2c2f31951897961525609fa12c3657216b7 (cherry picked from commit 6ed175d5412deeaec9691f85757e45452407b8e3) --- tests/component/updater_test.cpp | 19 ++++- updater/blockimg.cpp | 175 ++++++++++++++++++--------------------- 2 files changed, 96 insertions(+), 98 deletions(-) diff --git a/tests/component/updater_test.cpp b/tests/component/updater_test.cpp index 01b86f224..6c341c111 100644 --- a/tests/component/updater_test.cpp +++ b/tests/component/updater_test.cpp @@ -592,10 +592,10 @@ TEST_F(UpdaterTest, brotli_new_data) { ASSERT_EQ(0, zip_writer.StartEntry("new.dat.br", 0)); auto generator = []() { return rand() % 128; }; - // Generate 2048 blocks of random data. + // Generate 100 blocks of random data. std::string brotli_new_data; - brotli_new_data.reserve(4096 * 2048); - generate_n(back_inserter(brotli_new_data), 4096 * 2048, generator); + brotli_new_data.reserve(4096 * 100); + generate_n(back_inserter(brotli_new_data), 4096 * 100, generator); size_t encoded_size = BrotliEncoderMaxCompressedSize(brotli_new_data.size()); std::vector encoded_data(encoded_size); @@ -609,8 +609,19 @@ TEST_F(UpdaterTest, brotli_new_data) { ASSERT_EQ(0, zip_writer.StartEntry("patch_data", 0)); ASSERT_EQ(0, zip_writer.FinishEntry()); + // Write a few small chunks of new data, then a large chunk, and finally a few small chunks. + // This helps us to catch potential short writes. std::vector transfer_list = { - "4", "2048", "0", "0", "new 4,0,512,512,1024", "new 2,1024,2048", + "4", + "100", + "0", + "0", + "new 2,0,1", + "new 2,1,2", + "new 4,2,50,50,97", + "new 2,97,98", + "new 2,98,99", + "new 2,99,100", }; ASSERT_EQ(0, zip_writer.StartEntry("transfer_list", 0)); std::string commands = android::base::Join(transfer_list, '\n'); diff --git a/updater/blockimg.cpp b/updater/blockimg.cpp index 2bec487fe..a0b9ad233 100644 --- a/updater/blockimg.cpp +++ b/updater/blockimg.cpp @@ -158,20 +158,22 @@ class RangeSinkWriter { CHECK_NE(tgt.size(), static_cast(0)); }; - virtual ~RangeSinkWriter() {}; - bool Finished() const { return next_range_ == tgt_.size() && current_range_left_ == 0; } - // Return number of bytes consumed; and 0 indicates a writing failure. - virtual size_t Write(const uint8_t* data, size_t size) { + size_t AvailableSpace() const { + return tgt_.blocks() * BLOCKSIZE - bytes_written_; + } + + // Return number of bytes written; and 0 indicates a writing failure. + size_t Write(const uint8_t* data, size_t size) { if (Finished()) { LOG(ERROR) << "range sink write overrun; can't write " << size << " bytes"; return 0; } - size_t consumed = 0; + size_t written = 0; while (size > 0) { // Move to the next range as needed. if (!SeekToOutputRange()) { @@ -191,18 +193,18 @@ class RangeSinkWriter { size -= write_now; current_range_left_ -= write_now; - consumed += write_now; + written += write_now; } - bytes_written_ += consumed; - return consumed; + bytes_written_ += written; + return written; } size_t BytesWritten() const { return bytes_written_; } - protected: + private: // Set up the output cursor, move to next range if needed. bool SeekToOutputRange() { // We haven't finished the current range yet. @@ -241,75 +243,6 @@ class RangeSinkWriter { size_t bytes_written_; }; -class BrotliNewDataWriter : public RangeSinkWriter { - public: - BrotliNewDataWriter(int fd, const RangeSet& tgt, BrotliDecoderState* state) - : RangeSinkWriter(fd, tgt), state_(state) {} - - size_t Write(const uint8_t* data, size_t size) override { - if (Finished()) { - LOG(ERROR) << "Brotli new data write overrun; can't write " << size << " bytes"; - return 0; - } - CHECK(state_ != nullptr); - - size_t consumed = 0; - while (true) { - // Move to the next range as needed. - if (!SeekToOutputRange()) { - break; - } - - size_t available_in = size; - size_t write_now = std::min(32768, current_range_left_); - uint8_t buffer[write_now]; - - size_t available_out = write_now; - uint8_t* next_out = buffer; - - // The brotli decoder will update |data|, |available_in|, |next_out| and |available_out|. - BrotliDecoderResult result = BrotliDecoderDecompressStream( - state_, &available_in, &data, &available_out, &next_out, nullptr); - - // We don't have a way to recover from the decode error; report the failure. - if (result == BROTLI_DECODER_RESULT_ERROR) { - LOG(ERROR) << "Decompression failed with " - << BrotliDecoderErrorString(BrotliDecoderGetErrorCode(state_)); - return 0; - } - - if (write_all(fd_, buffer, write_now - available_out) == -1) { - return 0; - } - - LOG(DEBUG) << "bytes written: " << write_now - available_out << ", bytes consumed " - << size - available_in << ", decoder status " << result; - - // Update the total bytes written to output by the current writer; this is different from the - // consumed input bytes. - bytes_written_ += write_now - available_out; - current_range_left_ -= (write_now - available_out); - consumed += (size - available_in); - - // Update the remaining size. The input data ptr is already updated by brotli decoder - // function. - size = available_in; - - // Continue if we have more output to write, or more input to consume. - if (result == BROTLI_DECODER_RESULT_SUCCESS || - (result == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT && size == 0)) { - break; - } - } - - return consumed; - } - - private: - // Pointer to the decoder state. (initialized by PerformBlockImageUpdate) - BrotliDecoderState* state_; -}; - /** * All of the data for all the 'new' transfers is contained in one file in the update package, * concatenated together in the order in which transfers.list will need it. We want to stream it out @@ -354,16 +287,73 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { // At this point nti->writer is set, and we own it. The main thread is waiting for it to // disappear from nti. - size_t consumed = nti->writer->Write(data, size); + size_t write_now = std::min(size, nti->writer->AvailableSpace()); + if (nti->writer->Write(data, write_now) != write_now) { + LOG(ERROR) << "Failed to write " << write_now << " bytes."; + return false; + } + + data += write_now; + size -= write_now; + + if (nti->writer->Finished()) { + // We have written all the bytes desired by this writer. + + pthread_mutex_lock(&nti->mu); + nti->writer = nullptr; + pthread_cond_broadcast(&nti->cv); + pthread_mutex_unlock(&nti->mu); + } + } + + return true; +} + +static bool receive_brotli_new_data(const uint8_t* data, size_t size, void* cookie) { + NewThreadInfo* nti = static_cast(cookie); + + while (size > 0 || BrotliDecoderHasMoreOutput(nti->brotli_decoder_state)) { + // Wait for nti->writer to be non-null, indicating some of this data is wanted. + pthread_mutex_lock(&nti->mu); + while (nti->writer == nullptr) { + pthread_cond_wait(&nti->cv, &nti->mu); + } + pthread_mutex_unlock(&nti->mu); + + // At this point nti->writer is set, and we own it. The main thread is waiting for it to + // disappear from nti. + + size_t buffer_size = std::min(32768, nti->writer->AvailableSpace()); + if (buffer_size == 0) { + LOG(ERROR) << "No space left in output range"; + return false; + } + uint8_t buffer[buffer_size]; + size_t available_in = size; + size_t available_out = buffer_size; + uint8_t* next_out = buffer; + + // The brotli decoder will update |data|, |available_in|, |next_out| and |available_out|. + BrotliDecoderResult result = BrotliDecoderDecompressStream( + nti->brotli_decoder_state, &available_in, &data, &available_out, &next_out, nullptr); - // We encounter a fatal error if we fail to consume any input bytes. If this happens, abort the - // extraction. - if (consumed == 0) { - LOG(ERROR) << "Failed to process " << size << " input bytes."; + if (result == BROTLI_DECODER_RESULT_ERROR) { + LOG(ERROR) << "Decompression failed with " + << BrotliDecoderErrorString(BrotliDecoderGetErrorCode(nti->brotli_decoder_state)); return false; } - data += consumed; - size -= consumed; + + LOG(DEBUG) << "bytes to write: " << buffer_size - available_out << ", bytes consumed " + << size - available_in << ", decoder status " << result; + + size_t write_now = buffer_size - available_out; + if (nti->writer->Write(buffer, write_now) != write_now) { + LOG(ERROR) << "Failed to write " << write_now << " bytes."; + return false; + } + + // Update the remaining size. The input data ptr is already updated by brotli decoder function. + size = available_in; if (nti->writer->Finished()) { // We have written all the bytes desired by this writer. @@ -380,8 +370,11 @@ static bool receive_new_data(const uint8_t* data, size_t size, void* cookie) { static void* unzip_new_data(void* cookie) { NewThreadInfo* nti = static_cast(cookie); - ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti); - + if (nti->brotli_compressed) { + ProcessZipEntryContents(nti->za, &nti->entry, receive_brotli_new_data, nti); + } else { + ProcessZipEntryContents(nti->za, &nti->entry, receive_new_data, nti); + } pthread_mutex_lock(&nti->mu); nti->receiver_available = false; if (nti->writer != nullptr) { @@ -1240,12 +1233,7 @@ static int PerformCommandNew(CommandParameters& params) { LOG(INFO) << " writing " << tgt.blocks() << " blocks of new data"; pthread_mutex_lock(¶ms.nti.mu); - if (params.nti.brotli_compressed) { - params.nti.writer = - std::make_unique(params.fd, tgt, params.nti.brotli_decoder_state); - } else { - params.nti.writer = std::make_unique(params.fd, tgt); - } + params.nti.writer = std::make_unique(params.fd, tgt); pthread_cond_broadcast(¶ms.nti.cv); while (params.nti.writer != nullptr) { @@ -1485,7 +1473,6 @@ static Value* PerformBlockImageUpdate(const char* name, State* state, if (params.canwrite) { params.nti.za = za; params.nti.entry = new_entry; - // The entry is compressed by brotli if has a 'br' extension. params.nti.brotli_compressed = android::base::EndsWith(new_data_fn->data, ".br"); if (params.nti.brotli_compressed) { // Initialize brotli decoder state. -- cgit v1.2.3 From 329fe83509d916c2b7465f3a60ebb308add61898 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 21 Jul 2017 12:13:15 -0700 Subject: Avoid crashing recovery with unwritable /cache. When /cache is unwritable, recovery hits a crash loop. Because it passes nullptr to fileno(3) when writing back the locale file. This prevents user from recovering a device - it cannot boot far enough to recovery menu which allows wiping /cache. Bug: 63927337 Test: Corrupt /cache and boot into recovery on bullhead: 1. m -j recoveryimage 2. fastboot erase cache 3. fastboot boot $OUT/recovery.img 4. recovery menu shows up. Change-Id: I1407743f802049eb48add56a36298b665cb86139 (cherry picked from commit ec57903a7ec0bfe3c2f39dd6ee9cfc3de4ed20e6) --- recovery.cpp | 48 +++++++++++++++++++++++------------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 50115885f..11c12f6f1 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -478,40 +478,38 @@ static void copy_logs() { sync(); } -// clear the recovery command and prepare to boot a (hopefully working) system, +// Clear the recovery command and prepare to boot a (hopefully working) system, // copy our log file to cache as well (for the system to read). This function is // idempotent: call it as many times as you like. static void finish_recovery() { - // Save the locale to cache, so if recovery is next started up - // without a --locale argument (eg, directly from the bootloader) - // it will use the last-known locale. - if (!locale.empty() && has_cache) { - LOG(INFO) << "Saving locale \"" << locale << "\""; - - FILE* fp = fopen_path(LOCALE_FILE, "we"); - if (!android::base::WriteStringToFd(locale, fileno(fp))) { - PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE; - } - check_and_fclose(fp, LOCALE_FILE); + // Save the locale to cache, so if recovery is next started up without a '--locale' argument + // (e.g., directly from the bootloader) it will use the last-known locale. + if (!locale.empty() && has_cache) { + LOG(INFO) << "Saving locale \"" << locale << "\""; + if (ensure_path_mounted(LOCALE_FILE) != 0) { + LOG(ERROR) << "Failed to mount " << LOCALE_FILE; + } else if (!android::base::WriteStringToFile(locale, LOCALE_FILE)) { + PLOG(ERROR) << "Failed to save locale to " << LOCALE_FILE; } + } - copy_logs(); + copy_logs(); - // Reset to normal system boot so recovery won't cycle indefinitely. - std::string err; - if (!clear_bootloader_message(&err)) { - LOG(ERROR) << "Failed to clear BCB message: " << err; - } + // Reset to normal system boot so recovery won't cycle indefinitely. + std::string err; + if (!clear_bootloader_message(&err)) { + LOG(ERROR) << "Failed to clear BCB message: " << err; + } - // Remove the command file, so recovery won't repeat indefinitely. - if (has_cache) { - if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) { - LOG(WARNING) << "Can't unlink " << COMMAND_FILE; - } - ensure_path_unmounted(CACHE_ROOT); + // Remove the command file, so recovery won't repeat indefinitely. + if (has_cache) { + if (ensure_path_mounted(COMMAND_FILE) != 0 || (unlink(COMMAND_FILE) && errno != ENOENT)) { + LOG(WARNING) << "Can't unlink " << COMMAND_FILE; } + ensure_path_unmounted(CACHE_ROOT); + } - sync(); // For good measure. + sync(); // For good measure. } struct saved_log_file { -- cgit v1.2.3 From 5fb9f532f00ffc885c7415c6dc48c4deed20a219 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 21 Jul 2017 15:15:31 -0700 Subject: update_verifier: Handle legacy care_map.txt gracefully. update_verifier should be backward compatible to not reject legacy care_map.txt from old releases, which could otherwise fail to boot into the new release. For example, we've changed the care_map format between N and O. An O update_verifier would fail to work with an N care_map.txt - a) we have switched update_verifier to read from device mapper in O; b) the last few blocks that contain metadata can't be read via device mapper. This could be a result of sideloading an O OTA while the device having a pending N update. Bug: 63544345 Test: As follows on sailfish: 1. Flash the device with this CL; 2. Put a copy of N care_map.txt at /data/ota_package/. Restore the permissions properly ('cache' group); 3. `adb reboot bootloader`; 4. `fastboot set_active ` 5. Device boots up into home screen, with a warning in logcat that says it has skipped legacy care_map.txt. Change-Id: I6acc88c9e655a9245e6531f176fef7953953935f (cherry picked from commit 5a1dee01df3af346729b5791606b72d59b8e9815) --- update_verifier/update_verifier.cpp | 71 ++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index d3a5185b8..b49011a12 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -175,40 +175,53 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return true; } +// Returns true to indicate a passing verification (or the error should be ignored); Otherwise +// returns false on fatal errors, where we should reject the current boot and trigger a fallback. +// Note that update_verifier should be backward compatible to not reject care_map.txt from old +// releases, which could otherwise fail to boot into the new release. For example, we've changed +// the care_map format between N and O. An O update_verifier would fail to work with N +// care_map.txt. This could be a result of sideloading an O OTA while the device having a pending N +// update. bool verify_image(const std::string& care_map_name) { - android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY))); - // If the device is flashed before the current boot, it may not have care_map.txt - // in /data/ota_package. To allow the device to continue booting in this situation, - // we should print a warning and skip the block verification. - if (care_map_fd.get() == -1) { - PLOG(WARNING) << "Failed to open " << care_map_name; - return true; - } - // Care map file has four lines (two lines if vendor partition is not present): - // First line has the block partition name (system/vendor). - // Second line holds all ranges of blocks to verify. - // The next two lines have the same format but for vendor partition. - std::string file_content; - if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) { - LOG(ERROR) << "Error reading care map contents to string."; - return false; - } + android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY))); + // If the device is flashed before the current boot, it may not have care_map.txt + // in /data/ota_package. To allow the device to continue booting in this situation, + // we should print a warning and skip the block verification. + if (care_map_fd.get() == -1) { + PLOG(WARNING) << "Failed to open " << care_map_name; + return true; + } + // Care map file has four lines (two lines if vendor partition is not present): + // First line has the block partition name (system/vendor). + // Second line holds all ranges of blocks to verify. + // The next two lines have the same format but for vendor partition. + std::string file_content; + if (!android::base::ReadFdToString(care_map_fd.get(), &file_content)) { + LOG(ERROR) << "Error reading care map contents to string."; + return false; + } - std::vector lines; - lines = android::base::Split(android::base::Trim(file_content), "\n"); - if (lines.size() != 2 && lines.size() != 4) { - LOG(ERROR) << "Invalid lines in care_map: found " << lines.size() - << " lines, expecting 2 or 4 lines."; - return false; - } + std::vector lines; + lines = android::base::Split(android::base::Trim(file_content), "\n"); + if (lines.size() != 2 && lines.size() != 4) { + LOG(ERROR) << "Invalid lines in care_map: found " << lines.size() + << " lines, expecting 2 or 4 lines."; + return false; + } - for (size_t i = 0; i < lines.size(); i += 2) { - if (!read_blocks(lines[i], lines[i+1])) { - return false; - } + for (size_t i = 0; i < lines.size(); i += 2) { + // We're seeing an N care_map.txt. Skip the verification since it's not compatible with O + // update_verifier (the last few metadata blocks can't be read via device mapper). + if (android::base::StartsWith(lines[i], "/dev/block/")) { + LOG(WARNING) << "Found legacy care_map.txt; skipped."; + return true; + } + if (!read_blocks(lines[i], lines[i+1])) { + return false; } + } - return true; + return true; } static int reboot_device() { -- cgit v1.2.3 From 5cee24f4f1aa3f53c0b386abb2797c1e0f3088eb Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 24 Jul 2017 09:22:39 -0700 Subject: tests: Add a test to cover legacy care_map.txt handling. This is to cover the code added by commit 5a1dee01df3af346729b5791606b72d59b8e9815, where an O update_verifier should not reject N care_map.txt. Bug: 63544345 Test: recovery_component_test passes on marlin. Change-Id: Ia944e16cba3cc635098b3ffd92842d725b570fec (cherry picked from commit c319613e06f996aed32da3a1c3e93bf9b04ffa95) --- tests/component/update_verifier_test.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index 5fc7ef63f..b04e1185e 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -81,3 +81,16 @@ TEST_F(UpdateVerifierTest, verify_image_malformed_care_map) { ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); ASSERT_FALSE(verify_image(temp_file.path)); } + +TEST_F(UpdateVerifierTest, verify_image_legacy_care_map) { + // This test relies on dm-verity support. + if (!verity_supported) { + GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; + return; + } + + TemporaryFile temp_file; + std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0"; + ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file.path)); + ASSERT_TRUE(verify_image(temp_file.path)); +} -- cgit v1.2.3 From fda6ee7d6117f1ef563a590571af65c185169efe Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 25 Jul 2017 09:52:36 -0700 Subject: recovery: Fix the flickering when turning on text mode. When there's no command specified when booting into debuggable builds (such as using `adb reboot recovery`), we turn on the text mode (i.e. recovery menu) directly. This CL fixes the issue to avoid showing the background image in a flash while turning on the text mode. Bug: 63985334 Test: `fastboot boot $OUT/recovery.img` and it shows the recovery menu directly without the no command image in a flash. Change-Id: Id86bbe346ab76c8defc95e2b423e695a86774b09 (cherry picked from commit 7022f33ec8531c742f8f4701552d687233901495) --- recovery.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 11c12f6f1..8f08c53f4 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -1591,15 +1591,14 @@ int main(int argc, char **argv) { ui->Print("Rebooting automatically.\n"); } } else if (!just_exit) { - status = INSTALL_NONE; // No command specified - ui->SetBackground(RecoveryUI::NO_COMMAND); - - // http://b/17489952 - // If this is an eng or userdebug build, automatically turn on the - // text display if no command is specified. - if (is_ro_debuggable()) { - ui->ShowText(true); - } + // If this is an eng or userdebug build, automatically turn on the text display if no command + // is specified. Note that this should be called before setting the background to avoid + // flickering the background image. + if (is_ro_debuggable()) { + ui->ShowText(true); + } + status = INSTALL_NONE; // No command specified + ui->SetBackground(RecoveryUI::NO_COMMAND); } if (status == INSTALL_ERROR || status == INSTALL_CORRUPT) { -- cgit v1.2.3 From 5e47d5161aef9778c527b734baa203bf32c65a49 Mon Sep 17 00:00:00 2001 From: Jin Qian Date: Mon, 24 Jul 2017 10:34:35 -0700 Subject: recovery: handle security footer before passing size to mke2fs mke2fs doesn't take negative size as reserved size. If footer is specified, compute fs size to be (max partition size - reserved footer size) / block_size Bug: 23686092 Bug: 63968011 Merged-In: Iac4e143bd26a70cfc81eb52a399d687e19b1049c Change-Id: Iac4e143bd26a70cfc81eb52a399d687e19b1049c (cherry picked from commit f3ccad58ddb256150858df55a5e6fe5a906c5754) --- roots.cpp | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/roots.cpp b/roots.cpp index e98dfd448..49f002f97 100644 --- a/roots.cpp +++ b/roots.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -173,6 +174,23 @@ static int exec_cmd(const char* path, char* const argv[]) { return WEXITSTATUS(status); } +static ssize_t get_file_size(int fd, uint64_t reserve_len) { + struct stat buf; + int ret = fstat(fd, &buf); + if (ret) return 0; + + ssize_t computed_size; + if (S_ISREG(buf.st_mode)) { + computed_size = buf.st_size - reserve_len; + } else if (S_ISBLK(buf.st_mode)) { + computed_size = get_block_device_size(fd) - reserve_len; + } else { + computed_size = 0; + } + + return computed_size; +} + int format_volume(const char* volume, const char* directory) { Volume* v = volume_for_path(volume); if (v == NULL) { @@ -212,7 +230,16 @@ int format_volume(const char* volume, const char* directory) { if (v->length != 0) { length = v->length; } else if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0) { - length = -CRYPT_FOOTER_OFFSET; + android::base::unique_fd fd(open(v->blk_device, O_RDONLY)); + if (fd < 0) { + PLOG(ERROR) << "get_file_size: failed to open " << v->blk_device; + return -1; + } + length = get_file_size(fd.get(), CRYPT_FOOTER_OFFSET); + if (length <= 0) { + LOG(ERROR) << "get_file_size: invalid size " << length << " for " << v->blk_device; + return -1; + } } int result; if (strcmp(v->fs_type, "ext4") == 0) { @@ -272,16 +299,6 @@ int format_volume(const char* volume, const char* directory) { result = exec_cmd(e2fsdroid_argv[0], const_cast(e2fsdroid_argv)); } } else { /* Has to be f2fs because we checked earlier. */ - if (v->key_loc != NULL && strcmp(v->key_loc, "footer") == 0 && length < 0) { - LOG(ERROR) << "format_volume: crypt footer + negative length (" << length - << ") not supported on " << v->fs_type; - return -1; - } - if (length < 0) { - LOG(ERROR) << "format_volume: negative length (" << length - << ") not supported on " << v->fs_type; - return -1; - } char *num_sectors = nullptr; if (length >= 512 && asprintf(&num_sectors, "%zd", length / 512) <= 0) { LOG(ERROR) << "format_volume: failed to create " << v->fs_type -- cgit v1.2.3 From af9f8b4d97af56b5abcb622aaecebce59179bc0b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 28 Jul 2017 00:05:40 -0700 Subject: ui: Move the support for touch inputs into RecoveryUI. - Added detection for EV_ABS events in minui/events.cpp, if it's allowed; - Added listening and processing touch inputs in ui.cpp; - Fixed an issue in recognizing swipe with multi-touch protocol A; - Changed the logic in RecoveryUI::ProcessKey() to be swipe-aware. It now allows turning on text mode with + . The last change also fixed an issue on devices with protocol A: prior to this CL, user may accidentally toggle the text mode during an OTA. Because it was considered as a single-button device, a long tap that sent BTN_TOUCH event would turn on text mode. Test: Allow detecting touch inputs. Swiping (up, down, enter) works on angler, angelfish, dorado respectively. Bug: 36169090 Bug: 64307776 Change-Id: I4bc882b99114ce4ab414f8bdb8f4f7a525b8a8fd (cherry picked from commit 5f8dd9951d986b65d98d6a9ea38003427e9e46df) --- Android.mk | 12 ++++ minui/events.cpp | 47 +++++++++++---- minui/include/minui/minui.h | 3 +- ui.cpp | 139 ++++++++++++++++++++++++++++++++++++++++++-- ui.h | 24 ++++++++ 5 files changed, 209 insertions(+), 16 deletions(-) diff --git a/Android.mk b/Android.mk index 3eed7a696..967b9dfbe 100644 --- a/Android.mk +++ b/Android.mk @@ -108,6 +108,18 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_WIDTH=0 endif +ifneq ($(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD),) +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_LOW_THRESHOLD=$(TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD) +else +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_LOW_THRESHOLD=50 +endif + +ifneq ($(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD),) +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=$(TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD) +else +LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=90 +endif + ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) else diff --git a/minui/events.cpp b/minui/events.cpp index 0e1fd44a0..24c2a8277 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -53,36 +53,37 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0; } -int ev_init(ev_callback input_cb) { - bool epollctlfail = false; - +int ev_init(ev_callback input_cb, bool allow_touch_inputs) { g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS); if (g_epoll_fd == -1) { return -1; } + bool epollctlfail = false; DIR* dir = opendir("/dev/input"); - if (dir != NULL) { + if (dir != nullptr) { dirent* de; while ((de = readdir(dir))) { - // Use unsigned long to match ioctl's parameter type. - unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT - - // fprintf(stderr,"/dev/input/%s\n", de->d_name); if (strncmp(de->d_name, "event", 5)) continue; int fd = openat(dirfd(dir), de->d_name, O_RDONLY); if (fd == -1) continue; + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; // NOLINT + // Read the evbits of the input device. if (ioctl(fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { close(fd); continue; } - // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. + // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. EV_ABS is also + // allowed if allow_touch_inputs is set. if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { - close(fd); - continue; + if (!allow_touch_inputs || !test_bit(EV_ABS, ev_bits)) { + close(fd); + continue; + } } epoll_event ev; @@ -231,3 +232,27 @@ void ev_iterate_available_keys(const std::function& f) { } } } + +void ev_iterate_touch_inputs(const std::function& action) { + for (size_t i = 0; i < ev_dev_count; ++i) { + // Use unsigned long to match ioctl's parameter type. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)] = {}; // NOLINT + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + continue; + } + if (!test_bit(EV_ABS, ev_bits)) { + continue; + } + + unsigned long key_bits[BITS_TO_LONGS(KEY_MAX)] = {}; // NOLINT + if (ioctl(ev_fdinfo[i].fd, EVIOCGBIT(EV_ABS, KEY_MAX), key_bits) == -1) { + continue; + } + + for (int key_code = 0; key_code <= KEY_MAX; ++key_code) { + if (test_bit(key_code, key_bits)) { + action(key_code); + } + } + } +} diff --git a/minui/include/minui/minui.h b/minui/include/minui/minui.h index 78dd4cb98..017ddde75 100644 --- a/minui/include/minui/minui.h +++ b/minui/include/minui/minui.h @@ -74,10 +74,11 @@ struct input_event; using ev_callback = std::function; using ev_set_key_callback = std::function; -int ev_init(ev_callback input_cb); +int ev_init(ev_callback input_cb, bool allow_touch_inputs = false); void ev_exit(); int ev_add_fd(int fd, ev_callback cb); void ev_iterate_available_keys(const std::function& f); +void ev_iterate_touch_inputs(const std::function& action); int ev_sync_key_state(const ev_set_key_callback& set_key_cb); // 'timeout' has the same semantics as poll(2). diff --git a/ui.cpp b/ui.cpp index 30b42a19a..eadcdd433 100644 --- a/ui.cpp +++ b/ui.cpp @@ -54,6 +54,9 @@ RecoveryUI::RecoveryUI() rtl_locale_(false), brightness_normal_(50), brightness_dimmed_(25), + touch_screen_allowed_(false), + kTouchLowThreshold(RECOVERY_UI_TOUCH_LOW_THRESHOLD), + kTouchHighThreshold(RECOVERY_UI_TOUCH_HIGH_THRESHOLD), key_queue_len(0), key_last_down(-1), key_long_press(false), @@ -64,6 +67,8 @@ RecoveryUI::RecoveryUI() has_power_key(false), has_up_key(false), has_down_key(false), + has_touch_screen(false), + touch_slot_(0), screensaver_state_(ScreensaverState::DISABLED) { pthread_mutex_init(&key_queue_mutex, nullptr); pthread_cond_init(&key_queue_cond, nullptr); @@ -77,6 +82,8 @@ void RecoveryUI::OnKeyDetected(int key_code) { has_down_key = true; } else if (key_code == KEY_UP || key_code == KEY_VOLUMEUP) { has_up_key = true; + } else if (key_code == ABS_MT_POSITION_X || key_code == ABS_MT_POSITION_Y) { + has_touch_screen = true; } } @@ -128,10 +135,15 @@ bool RecoveryUI::Init(const std::string& locale) { // Set up the locale info. SetLocale(locale); - ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2)); + ev_init(std::bind(&RecoveryUI::OnInputEvent, this, std::placeholders::_1, std::placeholders::_2), + touch_screen_allowed_); ev_iterate_available_keys(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); + if (touch_screen_allowed_) { + ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); + } + if (!InitScreensaver()) { LOG(INFO) << "Screensaver disabled"; } @@ -140,15 +152,85 @@ bool RecoveryUI::Init(const std::string& locale) { return true; } +void RecoveryUI::OnTouchDetected(int dx, int dy) { + enum SwipeDirection { UP, DOWN, RIGHT, LEFT } direction; + + // We only consider a valid swipe if: + // - the delta along one axis is below kTouchLowThreshold; + // - and the delta along the other axis is beyond kTouchHighThreshold. + if (abs(dy) < kTouchLowThreshold && abs(dx) > kTouchHighThreshold) { + direction = dx < 0 ? SwipeDirection::LEFT : SwipeDirection::RIGHT; + } else if (abs(dx) < kTouchLowThreshold && abs(dy) > kTouchHighThreshold) { + direction = dy < 0 ? SwipeDirection::UP : SwipeDirection::DOWN; + } else { + LOG(DEBUG) << "Ignored " << dx << " " << dy << " (low: " << kTouchLowThreshold + << ", high: " << kTouchHighThreshold << ")"; + return; + } + + LOG(DEBUG) << "Swipe direction=" << direction; + switch (direction) { + case SwipeDirection::UP: + ProcessKey(KEY_UP, 1); // press up key + ProcessKey(KEY_UP, 0); // and release it + break; + + case SwipeDirection::DOWN: + ProcessKey(KEY_DOWN, 1); // press down key + ProcessKey(KEY_DOWN, 0); // and release it + break; + + case SwipeDirection::LEFT: + case SwipeDirection::RIGHT: + ProcessKey(KEY_POWER, 1); // press power key + ProcessKey(KEY_POWER, 0); // and release it + break; + }; +} + int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { struct input_event ev; if (ev_get_input(fd, epevents, &ev) == -1) { return -1; } + // Touch inputs handling. + // + // We handle the touch inputs by tracking the position changes between initial contacting and + // upon lifting. touch_start_X/Y record the initial positions, with touch_finger_down set. Upon + // detecting the lift, we unset touch_finger_down and detect a swipe based on position changes. + // + // Per the doc Multi-touch Protocol at below, there are two protocols. + // https://www.kernel.org/doc/Documentation/input/multi-touch-protocol.txt + // + // The main difference between the stateless type A protocol and the stateful type B slot protocol + // lies in the usage of identifiable contacts to reduce the amount of data sent to userspace. The + // slot protocol (i.e. type B) sends ABS_MT_TRACKING_ID with a unique id on initial contact, and + // sends ABS_MT_TRACKING_ID -1 upon lifting the contact. Protocol A doesn't send + // ABS_MT_TRACKING_ID -1 on lifting, but the driver may additionally report BTN_TOUCH event. + // + // For protocol A, we rely on BTN_TOUCH to recognize lifting, while for protocol B we look for + // ABS_MT_TRACKING_ID being -1. + // + // Touch input events will only be available if touch_screen_allowed_ is set. + if (ev.type == EV_SYN) { + if (touch_screen_allowed_ && ev.code == SYN_REPORT) { + // There might be multiple SYN_REPORT events. We should only detect a swipe after lifting the + // contact. + if (touch_finger_down_ && !touch_swiping_) { + touch_start_X_ = touch_X_; + touch_start_Y_ = touch_Y_; + touch_swiping_ = true; + } else if (!touch_finger_down_ && touch_swiping_) { + touch_swiping_ = false; + OnTouchDetected(touch_X_ - touch_start_X_, touch_Y_ - touch_start_Y_); + } + } return 0; - } else if (ev.type == EV_REL) { + } + + if (ev.type == EV_REL) { if (ev.code == REL_Y) { // accumulate the up or down motion reported by // the trackball. When it exceeds a threshold @@ -169,7 +251,48 @@ int RecoveryUI::OnInputEvent(int fd, uint32_t epevents) { rel_sum = 0; } + if (touch_screen_allowed_ && ev.type == EV_ABS) { + if (ev.code == ABS_MT_SLOT) { + touch_slot_ = ev.value; + } + // Ignore other fingers. + if (touch_slot_ > 0) return 0; + + switch (ev.code) { + case ABS_MT_POSITION_X: + touch_X_ = ev.value; + touch_finger_down_ = true; + break; + + case ABS_MT_POSITION_Y: + touch_Y_ = ev.value; + touch_finger_down_ = true; + break; + + case ABS_MT_TRACKING_ID: + // Protocol B: -1 marks lifting the contact. + if (ev.value < 0) touch_finger_down_ = false; + break; + } + return 0; + } + if (ev.type == EV_KEY && ev.code <= KEY_MAX) { + if (touch_screen_allowed_) { + if (ev.code == BTN_TOUCH) { + // A BTN_TOUCH with value 1 indicates the start of contact (protocol A), with 0 means + // lifting the contact. + touch_finger_down_ = (ev.value == 1); + } + + // Intentionally ignore BTN_TOUCH and BTN_TOOL_FINGER, which would otherwise trigger + // additional scrolling (because in ScreenRecoveryUI::ShowFile(), we consider keys other than + // KEY_POWER and KEY_UP as KEY_DOWN). + if (ev.code == BTN_TOUCH || ev.code == BTN_TOOL_FINGER) { + return 0; + } + } + ProcessKey(ev.code, ev.value); } @@ -365,6 +488,14 @@ bool RecoveryUI::HasThreeButtons() { return has_power_key && has_up_key && has_down_key; } +bool RecoveryUI::HasPowerKey() const { + return has_power_key; +} + +bool RecoveryUI::HasTouchScreen() const { + return has_touch_screen; +} + void RecoveryUI::FlushKeys() { pthread_mutex_lock(&key_queue_mutex); key_queue_len = 0; @@ -377,8 +508,8 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { pthread_mutex_unlock(&key_queue_mutex); // If we have power and volume up keys, that chord is the signal to toggle the text display. - if (HasThreeButtons()) { - if (key == KEY_VOLUMEUP && IsKeyPressed(KEY_POWER)) { + if (HasThreeButtons() || (HasPowerKey() && HasTouchScreen() && touch_screen_allowed_)) { + if ((key == KEY_VOLUMEUP || key == KEY_UP) && IsKeyPressed(KEY_POWER)) { return TOGGLE; } } else { diff --git a/ui.h b/ui.h index 7eb04aec8..5cda7af0f 100644 --- a/ui.h +++ b/ui.h @@ -82,6 +82,12 @@ class RecoveryUI { // otherwise. virtual bool HasThreeButtons(); + // Returns true if it has a power key. + virtual bool HasPowerKey() const; + + // Returns true if it supports touch inputs. + virtual bool HasTouchScreen() const; + // Erases any queued-up keys. virtual void FlushKeys(); @@ -129,7 +135,14 @@ class RecoveryUI { unsigned int brightness_normal_; unsigned int brightness_dimmed_; + // Whether we should listen for touch inputs (default: false). + bool touch_screen_allowed_; + private: + // The sensitivity when detecting a swipe. + const int kTouchLowThreshold; + const int kTouchHighThreshold; + // Key event input queue pthread_mutex_t key_queue_mutex; pthread_cond_t key_queue_cond; @@ -147,6 +160,16 @@ class RecoveryUI { bool has_power_key; bool has_up_key; bool has_down_key; + bool has_touch_screen; + + // Touch event related variables. See the comments in RecoveryUI::OnInputEvent(). + int touch_slot_; + int touch_X_; + int touch_Y_; + int touch_start_X_; + int touch_start_Y_; + bool touch_finger_down_; + bool touch_swiping_; struct key_timer_t { RecoveryUI* ui; @@ -157,6 +180,7 @@ class RecoveryUI { pthread_t input_thread_; void OnKeyDetected(int key_code); + void OnTouchDetected(int dx, int dy); int OnInputEvent(int fd, uint32_t epevents); void ProcessKey(int key_code, int updown); -- cgit v1.2.3 From 937e884ca113a5e775a3cb37a3f9dbe2174d3376 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 31 Jul 2017 23:15:09 -0700 Subject: ui: Check for bootreason=recovery_ui. Some wear bootloaders are passing bootreason=recovery_ui when booting into recovery from fastboot, or via 'adb reboot recovery'. Allow turning on text mode with a swipe for such a bootreason. Since we will turn on text mode automatically for debuggable builds, this bootreason mainly handles the case for user builds. Note this change only applies to devices that allow touch screen inputs. Bug: 36169090 Bug: 64307776 Test: Build and boot into user build recovery image. Toggle on text mode with a swipe. Change-Id: I55f19aed7b210352f8370de19935b4772cc12095 (cherry picked from commit 046aae29d9b0d2cdf24ad0567146991c3864c140) --- ui.cpp | 20 ++++++++++++++++++++ ui.h | 1 + 2 files changed, 21 insertions(+) diff --git a/ui.cpp b/ui.cpp index eadcdd433..e80d7ed04 100644 --- a/ui.cpp +++ b/ui.cpp @@ -69,6 +69,7 @@ RecoveryUI::RecoveryUI() has_down_key(false), has_touch_screen(false), touch_slot_(0), + is_bootreason_recovery_ui_(false), screensaver_state_(ScreensaverState::DISABLED) { pthread_mutex_init(&key_queue_mutex, nullptr); pthread_cond_init(&key_queue_cond, nullptr); @@ -142,6 +143,19 @@ bool RecoveryUI::Init(const std::string& locale) { if (touch_screen_allowed_) { ev_iterate_touch_inputs(std::bind(&RecoveryUI::OnKeyDetected, this, std::placeholders::_1)); + + // Parse /proc/cmdline to determine if it's booting into recovery with a bootreason of + // "recovery_ui". This specific reason is set by some (wear) bootloaders, to allow an easier way + // to turn on text mode. It will only be set if the recovery boot is triggered from fastboot, or + // with 'adb reboot recovery'. Note that this applies to all build variants. Otherwise the text + // mode will be turned on automatically on debuggable builds, even without a swipe. + std::string cmdline; + if (android::base::ReadFileToString("/proc/cmdline", &cmdline)) { + is_bootreason_recovery_ui_ = cmdline.find("bootreason=recovery_ui") != std::string::npos; + } else { + // Non-fatal, and won't affect Init() result. + PLOG(WARNING) << "Failed to read /proc/cmdline"; + } } if (!InitScreensaver()) { @@ -168,6 +182,12 @@ void RecoveryUI::OnTouchDetected(int dx, int dy) { return; } + // Allow turning on text mode with any swipe, if bootloader has set a bootreason of recovery_ui. + if (is_bootreason_recovery_ui_ && !IsTextVisible()) { + ShowText(true); + return; + } + LOG(DEBUG) << "Swipe direction=" << direction; switch (direction) { case SwipeDirection::UP: diff --git a/ui.h b/ui.h index 5cda7af0f..3d9afece0 100644 --- a/ui.h +++ b/ui.h @@ -170,6 +170,7 @@ class RecoveryUI { int touch_start_Y_; bool touch_finger_down_; bool touch_swiping_; + bool is_bootreason_recovery_ui_; struct key_timer_t { RecoveryUI* ui; -- cgit v1.2.3 From 5c2bc51bf26bbd88253c464340d9191ce247961b Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 5 Aug 2017 18:24:07 -0700 Subject: Import translations. DO NOT MERGE Change-Id: Ia215c30c6912ffa1e3270543f6ff4fc27f4260e4 Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import --- tools/recovery_l10n/res/values-en-rXC/strings.xml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tools/recovery_l10n/res/values-en-rXC/strings.xml diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml new file mode 100644 index 000000000..2d528b3fb --- /dev/null +++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml @@ -0,0 +1,9 @@ + + + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎Installing system update‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎Erasing‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎No command‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎Error!‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎Installing security update‎‏‎‎‏‎" + -- cgit v1.2.3 From 3222dc0d76ca0f19795df2aad417dec2293b7242 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Mon, 7 Aug 2017 18:47:27 -0400 Subject: update_verifier: Support androidboot.veritymode being empty or 'disabled'. Bootloaders using libavb will set androidboot.veritymode=disabled if the "disable dm-verity" flag has been set. Additionally if the "disable verification" flag is set androidboot.veritymode will not be set at all. Handle both cases. Without this fix we'll end up in a bootloop. Test: Manually tested on a device using AVB. (cherry-picked from commit 1a0929cc8aac532dba00b3c98cea22715719a421) Bug: 64404283 Change-Id: I3adf93f8dfd528fe9b869a63afa775f5730a3f69 --- update_verifier/update_verifier.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index d3a5185b8..48242a5d0 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -239,23 +239,36 @@ int update_verifier(int argc, char** argv) { // The current slot has not booted successfully. #if defined(PRODUCT_SUPPORTS_VERITY) || defined(BOARD_AVB_ENABLE) + bool skip_verification = false; std::string verity_mode = android::base::GetProperty("ro.boot.veritymode", ""); if (verity_mode.empty()) { + // With AVB it's possible to disable verification entirely and + // in this case ro.boot.veritymode is empty. +#if defined(BOARD_AVB_ENABLE) + LOG(WARNING) << "verification has been disabled; marking without verification."; + skip_verification = true; +#else LOG(ERROR) << "Failed to get dm-verity mode."; return reboot_device(); +#endif } else if (android::base::EqualsIgnoreCase(verity_mode, "eio")) { // We shouldn't see verity in EIO mode if the current slot hasn't booted successfully before. // Continue the verification until we fail to read some blocks. LOG(WARNING) << "Found dm-verity in EIO mode."; + } else if (android::base::EqualsIgnoreCase(verity_mode, "disabled")) { + LOG(WARNING) << "dm-verity in disabled mode; marking without verification."; + skip_verification = true; } else if (verity_mode != "enforcing") { LOG(ERROR) << "Unexpected dm-verity mode : " << verity_mode << ", expecting enforcing."; return reboot_device(); } - static constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt"; - if (!verify_image(CARE_MAP_FILE)) { - LOG(ERROR) << "Failed to verify all blocks in care map file."; - return reboot_device(); + if (!skip_verification) { + static constexpr auto CARE_MAP_FILE = "/data/ota_package/care_map.txt"; + if (!verify_image(CARE_MAP_FILE)) { + LOG(ERROR) << "Failed to verify all blocks in care map file."; + return reboot_device(); + } } #else LOG(WARNING) << "dm-verity not enabled; marking without verification."; -- cgit v1.2.3 From bd9664b5a01c8941949212973ca12be4df1b5d54 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Wed, 2 Aug 2017 10:27:31 -0700 Subject: update_verifier: verify blocks in parallel This CL is to change update_verifier to verify blocks in parallel to maximize storage bandwidth, it also preallocate the buffer to avoid vector allocation within reading loop. Test: care_map.txt: system 16,0,517,556,32770,33084,98306,98620,163842,164156,229378,229692,294914,295228,483544,524288,524296 vendor 8,0,119,135,32770,32831,96150,98304,98306 With CL: init: Service 'update_verifier_nonencrypted' (pid 711) exited with status 0 waiting took 2.978424 seconds Without CL: init: Service 'update_verifier_nonencrypted' (pid 695) exited with status 0 waiting took 4.466320 seconds Bug: 63686531 Test: reboot with manual insert care_map.txt Change-Id: Idf791865f15f6ff6cad89bf7ff230ee46c6adccc --- update_verifier/update_verifier.cpp | 82 +++++++++++++++++++++++-------------- 1 file changed, 51 insertions(+), 31 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index b49011a12..ceb3ec948 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -45,6 +45,7 @@ #include #include +#include #include #include @@ -123,11 +124,6 @@ static bool read_blocks(const std::string& partition, const std::string& range_s LOG(ERROR) << "Failed to find dm block device for " << partition; return false; } - android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY))); - if (fd.get() == -1) { - PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition; - return false; - } // For block range string, first integer 'count' equals 2 * total number of valid ranges, // followed by 'count' number comma separated integers. Every two integers reprensent a @@ -142,37 +138,61 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return false; } - size_t blk_count = 0; - for (size_t i = 1; i < ranges.size(); i += 2) { - unsigned int range_start, range_end; - bool parse_status = android::base::ParseUint(ranges[i], &range_start); - parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); - if (!parse_status || range_start >= range_end) { - LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1]; - return false; - } - - static constexpr size_t BLOCKSIZE = 4096; - if (lseek64(fd.get(), static_cast(range_start) * BLOCKSIZE, SEEK_SET) == -1) { - PLOG(ERROR) << "lseek to " << range_start << " failed"; - return false; - } + std::vector> threads; + size_t thread_num = std::thread::hardware_concurrency() ?: 4; + thread_num = std::min(thread_num, range_count / 2); + size_t group_range_count = range_count / thread_num; - size_t remain = (range_end - range_start) * BLOCKSIZE; - while (remain > 0) { - size_t to_read = std::min(remain, 1024 * BLOCKSIZE); - std::vector buf(to_read); - if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) { - PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end; + for (size_t t = 0; t < thread_num; t++) { + auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() { + size_t blk_count = 0; + static constexpr size_t kBlockSize = 4096; + std::vector buf(1024 * kBlockSize); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY))); + if (fd.get() == -1) { + PLOG(ERROR) << "Error reading " << dm_block_device << " for partition " << partition; return false; } - remain -= to_read; - } - blk_count += (range_end - range_start); + + for (size_t i = 1 + group_range_count * t; i < group_range_count * (t + 1) + 1; i += 2) { + unsigned int range_start, range_end; + bool parse_status = android::base::ParseUint(ranges[i], &range_start); + parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); + if (!parse_status || range_start >= range_end) { + LOG(ERROR) << "Invalid range pair " << ranges[i] << ", " << ranges[i + 1]; + return false; + } + + if (lseek64(fd.get(), static_cast(range_start) * kBlockSize, SEEK_SET) == -1) { + PLOG(ERROR) << "lseek to " << range_start << " failed"; + return false; + } + + size_t remain = (range_end - range_start) * kBlockSize; + while (remain > 0) { + size_t to_read = std::min(remain, 1024 * kBlockSize); + if (!android::base::ReadFully(fd.get(), buf.data(), to_read)) { + PLOG(ERROR) << "Failed to read blocks " << range_start << " to " << range_end; + return false; + } + remain -= to_read; + } + blk_count += (range_end - range_start); + } + LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device; + return true; + }; + + threads.emplace_back(std::async(std::launch::async, thread_func)); } - LOG(INFO) << "Finished reading " << blk_count << " blocks on " << dm_block_device; - return true; + bool ret = true; + for (auto& t : threads) { + ret = t.get() && ret; + } + LOG(INFO) << "Finished reading blocks on " << dm_block_device << " with " << thread_num + << " threads."; + return ret; } // Returns true to indicate a passing verification (or the error should be ignored); Otherwise -- cgit v1.2.3 From 102016ce1fe62190ace7016f2e7484b37f6391ea Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 9 Aug 2017 16:33:07 -0700 Subject: tests: Add the missing dependency on libhidlbase. It fails to build recovery_component_test with the following errors: out/soong/.intermediates/hardware/interfaces/boot/1.0/android.hardware.boot@1.0_genc++_headers/gen/android/hardware/boot/1.0/types.h:14: error: undefined reference to 'android::hardware::hidl_string::hidl_string(android::hardware::hidl_string const&)' out/soong/.intermediates/hardware/interfaces/boot/1.0/android.hardware.boot@1.0_genc++_headers/gen/android/hardware/boot/1.0/types.h:14: error: undefined reference to 'android::hardware::hidl_string::operator=(android::hardware::hidl_string const&)' out/soong/.intermediates/hardware/interfaces/boot/1.0/android.hardware.boot@1.0_genc++_headers/gen/android/hardware/boot/1.0/types.h:14: error: undefined reference to 'android::hardware::hidl_string::~hidl_string()' libupdate_verifier includes , which includes the 'types.h' above. In 'types.h', it defines struct CommandResult that's using android::hardware::hidl_string. Since libhidlbase doesn't have a static library target, remove 'LOCAL_FORCE_STATIC_EXECUTABLE := true', which isn't required for running tests. Test: mmma -j bootable/recovery Bug: 64538692 Change-Id: Iaa7c08adc241128d787274fcaea9b363e7ff93f4 --- tests/Android.mk | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Android.mk b/tests/Android.mk index 8b1dc1099..f2497b8b3 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -111,7 +111,8 @@ LOCAL_SRC_FILES := \ component/update_verifier_test.cpp \ component/verifier_test.cpp -LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_SHARED_LIBRARIES := \ + libhidlbase tune2fs_static_libraries := \ libext2_com_err \ -- cgit v1.2.3 From 016120f395795fb1be600c1e85f4dfd324797122 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 2 Aug 2017 17:11:04 -0700 Subject: Allow customizing WearRecoveryUI via Makefile variables. With the following Makefile variables, we can reduce the work of writing (copy/pasting) device-specific WearRecoveryUI classes. The list of Makefile variables (the ones useful for Wear devices): - TARGET_RECOVERY_UI_MARGIN_HEIGHT (default: 0) - TARGET_RECOVERY_UI_MARGIN_WIDTH (default: 0) Specify the margin space that we don't want to display texts. They replace the former outer_width and outer_height. - TARGET_RECOVERY_UI_TOUCH_LOW_THRESHOLD (default: 50) - TARGET_RECOVERY_UI_TOUCH_HIGH_THRESHOLD (default: 90) Specify the sensitivity of recognizing a swipe. Devices give absolute positions, so for some devices we need to adjust the thresholds. - TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE Specify the progress bar vertical position, which should be adjusted to the actual height of a device. It replaces the former progress_bar_y. - TARGET_RECOVERY_UI_ANIMATION_FPS (default: 30) Specify the animation FPS if using device-specific animation images. It replaces the former animation_fps. Devices can specify "TARGET_RECOVERY_UI_LIB := librecovery_ui_wear", with optionally defined Makefile vars above, in BoardConfig.mk to customize their WearRecoveryUI. Also remove the obsolete wear_touch.{cpp,h}, which has been merged into ui.cpp in commit 5f8dd9951d986b65d98d6a9ea38003427e9e46df. Bug: 64307776 Test: Change the device BoardConfig.mk and test recovery image. Change-Id: Id0fb2d4e3977ab5ddd31e71f9535470cab70e41b (cherry picked from commit 0470ceea381775b09eee931858c3320be88cc637) --- Android.mk | 23 +++++++- screen_ui.cpp | 4 +- screen_ui.h | 6 +- wear_device.cpp | 23 ++++++++ wear_touch.cpp | 177 -------------------------------------------------------- wear_touch.h | 58 ------------------- wear_ui.cpp | 21 ++++--- wear_ui.h | 5 +- 8 files changed, 64 insertions(+), 253 deletions(-) create mode 100644 wear_device.cpp delete mode 100644 wear_touch.cpp delete mode 100644 wear_touch.h diff --git a/Android.mk b/Android.mk index 967b9dfbe..b1ee2440b 100644 --- a/Android.mk +++ b/Android.mk @@ -79,7 +79,6 @@ LOCAL_SRC_FILES := \ ui.cpp \ vr_ui.cpp \ wear_ui.cpp \ - wear_touch.cpp \ LOCAL_MODULE := recovery @@ -120,6 +119,18 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_TOUCH_HIGH_THRESHOLD=90 endif +ifneq ($(TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE),) +LOCAL_CFLAGS += -DRECOVERY_UI_PROGRESS_BAR_BASELINE=$(TARGET_RECOVERY_UI_PROGRESS_BAR_BASELINE) +else +LOCAL_CFLAGS += -DRECOVERY_UI_PROGRESS_BAR_BASELINE=259 +endif + +ifneq ($(TARGET_RECOVERY_UI_ANIMATION_FPS),) +LOCAL_CFLAGS += -DRECOVERY_UI_ANIMATION_FPS=$(TARGET_RECOVERY_UI_ANIMATION_FPS) +else +LOCAL_CFLAGS += -DRECOVERY_UI_ANIMATION_FPS=30 +endif + ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) else @@ -216,6 +227,16 @@ LOCAL_STATIC_LIBRARIES := \ LOCAL_CFLAGS := -Werror include $(BUILD_STATIC_LIBRARY) +# Wear default device +# =============================== +include $(CLEAR_VARS) +LOCAL_SRC_FILES := wear_device.cpp + +# Should match TARGET_RECOVERY_UI_LIB in BoardConfig.mk. +LOCAL_MODULE := librecovery_ui_wear + +include $(BUILD_STATIC_LIBRARY) + # vr headset default device # =============================== include $(CLEAR_VARS) diff --git a/screen_ui.cpp b/screen_ui.cpp index 8f792f162..c83a7659e 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -53,6 +53,7 @@ static double now() { ScreenRecoveryUI::ScreenRecoveryUI() : kMarginWidth(RECOVERY_UI_MARGIN_WIDTH), kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT), + kAnimationFps(RECOVERY_UI_ANIMATION_FPS), density_(static_cast(android::base::GetIntProperty("ro.sf.lcd_density", 160)) / 160.f), currentIcon(NONE), progressBarType(EMPTY), @@ -77,7 +78,6 @@ ScreenRecoveryUI::ScreenRecoveryUI() loop_frames(0), current_frame(0), intro_done(false), - animation_fps(30), // TODO: there's currently no way to infer this. stage(-1), max_stage(-1), updateMutex(PTHREAD_MUTEX_INITIALIZER) {} @@ -375,7 +375,7 @@ void* ScreenRecoveryUI::ProgressThreadStartRoutine(void* data) { } void ScreenRecoveryUI::ProgressThreadLoop() { - double interval = 1.0 / animation_fps; + double interval = 1.0 / kAnimationFps; while (true) { double start = now(); pthread_mutex_lock(&updateMutex); diff --git a/screen_ui.h b/screen_ui.h index 8402fac00..1f40164af 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -84,6 +84,9 @@ class ScreenRecoveryUI : public RecoveryUI { const int kMarginWidth; const int kMarginHeight; + // Number of frames per sec (default: 30) for both parts of the animation. + const int kAnimationFps; + // The scale factor from dp to pixels. 1.0 for mdpi, 4.0 for xxxhdpi. const float density_; @@ -141,9 +144,6 @@ class ScreenRecoveryUI : public RecoveryUI { size_t current_frame; bool intro_done; - // Number of frames per sec (default: 30) for both parts of the animation. - int animation_fps; - int stage, max_stage; int char_width_; diff --git a/wear_device.cpp b/wear_device.cpp new file mode 100644 index 000000000..3268130b0 --- /dev/null +++ b/wear_device.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 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 "device.h" +#include "wear_ui.h" + +Device* make_device() { + return new Device(new WearRecoveryUI); +} + diff --git a/wear_touch.cpp b/wear_touch.cpp deleted file mode 100644 index e2ab44d2d..000000000 --- a/wear_touch.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * 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 -#include - -#include "wear_touch.h" - -#define DEVICE_PATH "/dev/input" - -WearSwipeDetector::WearSwipeDetector(int low, int high, OnSwipeCallback callback, void* cookie): - mLowThreshold(low), - mHighThreshold(high), - mCallback(callback), - mCookie(cookie), - mCurrentSlot(-1) { - pthread_create(&mThread, NULL, touch_thread, this); -} - -WearSwipeDetector::~WearSwipeDetector() { -} - -void WearSwipeDetector::detect(int dx, int dy) { - enum SwipeDirection direction; - - if (abs(dy) < mLowThreshold && abs(dx) > mHighThreshold) { - direction = dx < 0 ? LEFT : RIGHT; - } else if (abs(dx) < mLowThreshold && abs(dy) > mHighThreshold) { - direction = dy < 0 ? UP : DOWN; - } else { - LOG(DEBUG) << "Ignore " << dx << " " << dy; - return; - } - - LOG(DEBUG) << "Swipe direction=" << direction; - mCallback(mCookie, direction); -} - -void WearSwipeDetector::process(struct input_event *event) { - if (mCurrentSlot < 0) { - mCallback(mCookie, UP); - mCurrentSlot = 0; - } - - if (event->type == EV_ABS) { - if (event->code == ABS_MT_SLOT) - mCurrentSlot = event->value; - - // Ignore other fingers - if (mCurrentSlot > 0) { - return; - } - - switch (event->code) { - case ABS_MT_POSITION_X: - mX = event->value; - mFingerDown = true; - break; - - case ABS_MT_POSITION_Y: - mY = event->value; - mFingerDown = true; - break; - - case ABS_MT_TRACKING_ID: - if (event->value < 0) - mFingerDown = false; - break; - } - } else if (event->type == EV_SYN) { - if (event->code == SYN_REPORT) { - if (mFingerDown && !mSwiping) { - mStartX = mX; - mStartY = mY; - mSwiping = true; - } else if (!mFingerDown && mSwiping) { - mSwiping = false; - detect(mX - mStartX, mY - mStartY); - } - } - } -} - -void WearSwipeDetector::run() { - int fd = findDevice(DEVICE_PATH); - if (fd < 0) { - LOG(ERROR) << "no input devices found"; - return; - } - - struct input_event event; - while (read(fd, &event, sizeof(event)) == sizeof(event)) { - process(&event); - } - - close(fd); -} - -void* WearSwipeDetector::touch_thread(void* cookie) { - (static_cast(cookie))->run(); - return NULL; -} - -#define test_bit(bit, array) ((array)[(bit)/8] & (1<<((bit)%8))) - -int WearSwipeDetector::openDevice(const char *device) { - int fd = open(device, O_RDONLY); - if (fd < 0) { - PLOG(ERROR) << "could not open " << device; - return false; - } - - char name[80]; - name[sizeof(name) - 1] = '\0'; - if (ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) { - PLOG(ERROR) << "could not get device name for " << device; - name[0] = '\0'; - } - - uint8_t bits[512]; - memset(bits, 0, sizeof(bits)); - int ret = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits); - if (ret > 0) { - if (test_bit(ABS_MT_POSITION_X, bits) && test_bit(ABS_MT_POSITION_Y, bits)) { - LOG(DEBUG) << "Found " << device << " " << name; - return fd; - } - } - - close(fd); - return -1; -} - -int WearSwipeDetector::findDevice(const char* path) { - DIR* dir = opendir(path); - if (dir == NULL) { - PLOG(ERROR) << "Could not open directory " << path; - return false; - } - - struct dirent* entry; - int ret = -1; - while (ret < 0 && (entry = readdir(dir)) != NULL) { - if (entry->d_name[0] == '.') continue; - - char device[PATH_MAX]; - device[PATH_MAX-1] = '\0'; - snprintf(device, PATH_MAX-1, "%s/%s", path, entry->d_name); - - ret = openDevice(device); - } - - closedir(dir); - return ret; -} - diff --git a/wear_touch.h b/wear_touch.h deleted file mode 100644 index 9a1d3150c..000000000 --- a/wear_touch.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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. - */ - -#ifndef __WEAR_TOUCH_H -#define __WEAR_TOUCH_H - -#include - -class WearSwipeDetector { - -public: - enum SwipeDirection { UP, DOWN, RIGHT, LEFT }; - typedef void (*OnSwipeCallback)(void* cookie, enum SwipeDirection direction); - - WearSwipeDetector(int low, int high, OnSwipeCallback cb, void* cookie); - ~WearSwipeDetector(); - -private: - void run(); - void process(struct input_event *event); - void detect(int dx, int dy); - - pthread_t mThread; - static void* touch_thread(void* cookie); - - int findDevice(const char* path); - int openDevice(const char* device); - - int mLowThreshold; - int mHighThreshold; - - OnSwipeCallback mCallback; - void *mCookie; - - int mX; - int mY; - int mStartX; - int mStartY; - - int mCurrentSlot; - bool mFingerDown; - bool mSwiping; -}; - -#endif // __WEAR_TOUCH_H diff --git a/wear_ui.cpp b/wear_ui.cpp index 18c30d34a..b8801a0b3 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -51,10 +51,15 @@ static double now() { } WearRecoveryUI::WearRecoveryUI() - : progress_bar_y(259), outer_height(0), outer_width(0), menu_unusable_rows(0) { + : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE), menu_unusable_rows(9) { + // TODO: menu_unusable_rows should be computed based on the lines in draw_screen_locked(). + + // TODO: The following three variables are likely not needed. The first two are detected + // automatically in ScreenRecoveryUI::LoadAnimation(), based on the actual files seen on device. intro_frames = 22; loop_frames = 60; - animation_fps = 30; + + touch_screen_allowed_ = true; for (size_t i = 0; i < 5; i++) backgroundIcon[i] = NULL; @@ -62,7 +67,7 @@ WearRecoveryUI::WearRecoveryUI() } int WearRecoveryUI::GetProgressBaseline() const { - return progress_bar_y; + return kProgressBarBaseline; } // Draw background frame on the screen. Does not flip pages. @@ -113,8 +118,8 @@ void WearRecoveryUI::draw_screen_locked() { SetColor(TEXT_FILL); gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - int y = outer_height; - int x = outer_width; + int y = kMarginHeight; + int x = kMarginWidth; if (show_menu) { std::string recovery_fingerprint = android::base::GetProperty("ro.bootimage.build.fingerprint", ""); @@ -170,7 +175,7 @@ void WearRecoveryUI::draw_screen_locked() { int ty; int row = (text_top_ + text_rows_ - 1) % text_rows_; size_t count = 0; - for (int ty = gr_fb_height() - char_height_ - outer_height; ty > y + 2 && count < text_rows_; + for (int ty = gr_fb_height() - char_height_ - kMarginHeight; ty > y + 2 && count < text_rows_; ty -= char_height_, ++count) { gr_text(gr_sys_font(), x + 4, ty, text_[row], 0); --row; @@ -190,12 +195,12 @@ bool WearRecoveryUI::InitTextParams() { return false; } - text_cols_ = (gr_fb_width() - (outer_width * 2)) / char_width_; + text_cols_ = (gr_fb_width() - (kMarginWidth * 2)) / char_width_; if (text_rows_ > kMaxRows) text_rows_ = kMaxRows; if (text_cols_ > kMaxCols) text_cols_ = kMaxCols; - visible_text_rows = (gr_fb_height() - (outer_height * 2)) / char_height_; + visible_text_rows = (gr_fb_height() - (kMarginHeight * 2)) / char_height_; return true; } diff --git a/wear_ui.h b/wear_ui.h index a814118c7..c2bbdb66e 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -42,10 +42,7 @@ class WearRecoveryUI : public ScreenRecoveryUI { protected: // progress bar vertical position, it's centered horizontally - int progress_bar_y; - - // outer of window - int outer_height, outer_width; + const int kProgressBarBaseline; // Unusable rows when displaying the recovery menu, including the lines for headers (Android // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen. -- cgit v1.2.3 From f2be3bd74ac430a31ca4beb0db321d146283db2b Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 11 Aug 2017 13:50:24 -0700 Subject: wear_ui: Expose menu_unusable_rows via Makefile var. This variable is useful on small screens (e.g. on watches) to handle long menus. We should have better way to handle this value smartly. Prior to that, expose the value to be overridable by using the generic wearable UI module (librecovery_ui_wear). Bug: 64307776 Test: Define the variable, build and boot into recovery image and check the UI menu. Change-Id: I5d7a6baa8bb4cc852bfcc2a7b3cc9686c1c8817e (cherry picked from commit eea3af3f911d36ac1a82a9fb95d24912cc07e3b1) --- Android.mk | 6 ++++++ wear_ui.cpp | 7 ++++--- wear_ui.h | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Android.mk b/Android.mk index b1ee2440b..776e6ea19 100644 --- a/Android.mk +++ b/Android.mk @@ -131,6 +131,12 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_ANIMATION_FPS=30 endif +ifneq ($(TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS),) +LOCAL_CFLAGS += -DRECOVERY_UI_MENU_UNUSABLE_ROWS=$(TARGET_RECOVERY_UI_MENU_UNUSABLE_ROWS) +else +LOCAL_CFLAGS += -DRECOVERY_UI_MENU_UNUSABLE_ROWS=9 +endif + ifneq ($(TARGET_RECOVERY_UI_VR_STEREO_OFFSET),) LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=$(TARGET_RECOVERY_UI_VR_STEREO_OFFSET) else diff --git a/wear_ui.cpp b/wear_ui.cpp index b8801a0b3..169ef20e1 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -51,8 +51,9 @@ static double now() { } WearRecoveryUI::WearRecoveryUI() - : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE), menu_unusable_rows(9) { - // TODO: menu_unusable_rows should be computed based on the lines in draw_screen_locked(). + : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE), + kMenuUnusableRows(RECOVERY_UI_MENU_UNUSABLE_ROWS) { + // TODO: kMenuUnusableRows should be computed based on the lines in draw_screen_locked(). // TODO: The following three variables are likely not needed. The first two are detected // automatically in ScreenRecoveryUI::LoadAnimation(), based on the actual files seen on device. @@ -268,7 +269,7 @@ void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* it show_menu = true; menu_sel = initial_selection; menu_start = 0; - menu_end = visible_text_rows - 1 - menu_unusable_rows; + menu_end = visible_text_rows - 1 - kMenuUnusableRows; if (menu_items <= menu_end) menu_end = menu_items; update_screen_locked(); } diff --git a/wear_ui.h b/wear_ui.h index c2bbdb66e..3bd90b699 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -46,7 +46,7 @@ class WearRecoveryUI : public ScreenRecoveryUI { // Unusable rows when displaying the recovery menu, including the lines for headers (Android // Recovery, build id and etc) and the bottom lines that may otherwise go out of the screen. - int menu_unusable_rows; + const int kMenuUnusableRows; int GetProgressBaseline() const override; -- cgit v1.2.3 From 20fa1a92cc53950a030281453e46015ecaec29bb Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 15 Aug 2017 20:14:00 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import Bug: 64712476 Change-Id: I7e9049129d9a886cac53a1f6438d9c733fb3d3d8 --- tools/recovery_l10n/res/values-en-rCA/strings.xml | 9 +++++++++ tools/recovery_l10n/res/values-mr/strings.xml | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 tools/recovery_l10n/res/values-en-rCA/strings.xml diff --git a/tools/recovery_l10n/res/values-en-rCA/strings.xml b/tools/recovery_l10n/res/values-en-rCA/strings.xml new file mode 100644 index 000000000..dc75c2374 --- /dev/null +++ b/tools/recovery_l10n/res/values-en-rCA/strings.xml @@ -0,0 +1,9 @@ + + + "Installing system update" + "Erasing" + "No command" + "Error!" + "Installing security update" + diff --git a/tools/recovery_l10n/res/values-mr/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml index 8cf86f773..017a515c0 100644 --- a/tools/recovery_l10n/res/values-mr/strings.xml +++ b/tools/recovery_l10n/res/values-mr/strings.xml @@ -1,9 +1,9 @@ - "सिस्टम अद्यतन स्थापित करीत आहे" + "सिस्टम अपडेट इंस्टॉल करत आहे" "मिटवत आहे" "कोणताही आदेश नाही" "त्रुटी!" - "सुरक्षा अद्यतन स्थापित करीत आहे" + "सुरक्षा अपडेट इंस्टॉल करत आहे" -- cgit v1.2.3 From ee6fefd2f203a7df2164b3262fa54e62f61e6ad2 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Sun, 13 Aug 2017 23:48:55 -0700 Subject: screen_ui: Word-wrap menu headers. This CL adds ScreenRecoveryUI::DrawWrappedTextLines() to better handle long menu header texts. It does a word wrap at spaces, if available. This avoids fixed-length menu headers being truncated on small screens. Bug: 64293520 Test: On bullhead, boot into recovery with --prompt_and_wipe_data, and check the prompt texts. Change-Id: Ia22746583516dd230567a267584aca558429395e (cherry picked from commit 2bbc6d642d1fbfb007905d95b629fe5f833b2a1b) --- recovery.cpp | 9 +++++---- screen_ui.cpp | 30 +++++++++++++++++++++++++++++- screen_ui.h | 3 +++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/recovery.cpp b/recovery.cpp index 8f08c53f4..07bd7b9d4 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -758,12 +758,13 @@ static bool wipe_data(Device* device) { } static bool prompt_and_wipe_data(Device* device) { + // Use a single string and let ScreenRecoveryUI handles the wrapping. const char* const headers[] = { - "Can't load Android system. Your data may be corrupt.", - "If you continue to get this message, you may need to", - "perform a factory data reset and erase all user data", + "Can't load Android system. Your data may be corrupt. " + "If you continue to get this message, you may need to " + "perform a factory data reset and erase all user data " "stored on this device.", - NULL + nullptr }; const char* const items[] = { "Try again", diff --git a/screen_ui.cpp b/screen_ui.cpp index 8f792f162..e056512bd 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -278,6 +278,34 @@ int ScreenRecoveryUI::DrawTextLines(int x, int y, const char* const* lines) cons return offset; } +int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y, const char* const* lines) const { + int offset = 0; + for (size_t i = 0; lines != nullptr && lines[i] != nullptr; ++i) { + // The line will be wrapped if it exceeds text_cols_. + std::string line(lines[i]); + size_t next_start = 0; + while (next_start < line.size()) { + std::string sub = line.substr(next_start, text_cols_ + 1); + if (sub.size() <= text_cols_) { + next_start += sub.size(); + } else { + // Line too long and must be wrapped to text_cols_ columns. + size_t last_space = sub.find_last_of(" \t\n"); + if (last_space == std::string::npos) { + // No space found, just draw as much as we can + sub.resize(text_cols_); + next_start += text_cols_; + } else { + sub.resize(last_space); + next_start += last_space + 1; + } + } + offset += DrawTextLine(x, y + offset, sub.c_str(), false); + } + } + return offset; +} + static const char* REGULAR_HELP[] = { "Use volume up/down and power.", NULL @@ -316,7 +344,7 @@ void ScreenRecoveryUI::draw_screen_locked() { y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); SetColor(HEADER); - y += DrawTextLines(x, y, menu_headers_); + y += DrawWrappedTextLines(x, y, menu_headers_); SetColor(MENU); y += DrawHorizontalRule(y) + 4; diff --git a/screen_ui.h b/screen_ui.h index 8402fac00..df7cc25b3 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -187,6 +187,9 @@ class ScreenRecoveryUI : public RecoveryUI { virtual int DrawTextLine(int x, int y, const char* line, bool bold) const; // Draws multiple text lines. Returns the offset it should be moving along Y-axis. int DrawTextLines(int x, int y, const char* const* lines) const; + // Similar to DrawTextLines() to draw multiple text lines, but additionally wraps long lines. + // Returns the offset it should be moving along Y-axis. + int DrawWrappedTextLines(int x, int y, const char* const* lines) const; }; #endif // RECOVERY_UI_H -- cgit v1.2.3 From 2cf6fe2ced4c117d132afd2c6e40fe0d937d0fca Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Wed, 16 Aug 2017 13:25:55 -0700 Subject: screen_ui: Fix a case that may truncate the last char. ScreenRecoveryUI::DrawWrappedTextLines() should be called with kMarginWidth only. Because it's using a line limit of text_cols_, which is unaware of kMenuIdent. Bug: 64293520 Test: No missing char with long header text. Change-Id: Ib4d08de2c56473a483ff9964eb6cec31f8a74c9a (cherry picked from commit 13aa4a902ba2fa304fc9fe826f797a4e0e2182b7) --- screen_ui.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index e056512bd..a02550199 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -344,7 +344,8 @@ void ScreenRecoveryUI::draw_screen_locked() { y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); SetColor(HEADER); - y += DrawWrappedTextLines(x, y, menu_headers_); + // Ignore kMenuIndent, which is not taken into account by text_cols_. + y += DrawWrappedTextLines(kMarginWidth, y, menu_headers_); SetColor(MENU); y += DrawHorizontalRule(y) + 4; -- cgit v1.2.3 From c16d2225933f06f36391fda2997d5619407c230b Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 29 Aug 2017 13:28:09 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import Bug: 64712476 Change-Id: Ife1e0455a5433bc0cb0e15b0aabf2beef1ba8827 --- tools/recovery_l10n/res/values-hi/strings.xml | 2 +- tools/recovery_l10n/res/values-mr/strings.xml | 4 ++-- tools/recovery_l10n/res/values-te/strings.xml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/recovery_l10n/res/values-hi/strings.xml b/tools/recovery_l10n/res/values-hi/strings.xml index a8a876ee4..65d003352 100644 --- a/tools/recovery_l10n/res/values-hi/strings.xml +++ b/tools/recovery_l10n/res/values-hi/strings.xml @@ -3,7 +3,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "सिस्टम अपडेट इंस्टॉल किया जा रहा है" "मिटाया जा रहा है" - "कोई आदेश नहीं" + "कोई निर्देश नहीं मिला" "गड़बड़ी!" "सुरक्षा अपडेट इंस्टॉल किया जा रहा है" diff --git a/tools/recovery_l10n/res/values-mr/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml index 017a515c0..5f820336f 100644 --- a/tools/recovery_l10n/res/values-mr/strings.xml +++ b/tools/recovery_l10n/res/values-mr/strings.xml @@ -3,7 +3,7 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> "सिस्टम अपडेट इंस्टॉल करत आहे" "मिटवत आहे" - "कोणताही आदेश नाही" - "त्रुटी!" + "कोणतीही कमांड नाही" + "एरर!" "सुरक्षा अपडेट इंस्टॉल करत आहे" diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml index cfb02c915..e35c82bc4 100644 --- a/tools/recovery_l10n/res/values-te/strings.xml +++ b/tools/recovery_l10n/res/values-te/strings.xml @@ -4,6 +4,6 @@ "సిస్టమ్ నవీకరణను ఇన్‍స్టాల్ చేస్తోంది" "డేటాను తొలగిస్తోంది" "ఆదేశం లేదు" - "లోపం సంభవించింది!" + "ఎర్రర్ సంభవించింది!" "భద్రతా నవీకరణను ఇన్‌స్టాల్ చేస్తోంది" -- cgit v1.2.3 From 17fa5c7cab503015bde05fbe0455f8a6a3344396 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 7 Sep 2017 13:38:51 -0700 Subject: ui: Manage menu_ with std::vector. Prior to this CL, menu_ is allocated with a fixed length of text_rows_. However, because we support scrollable menu in wear_ui, there might be more menu entries than text_rows_, which would lead to out-of-bounds array access. This CL addresses the issue by switching to std::vector. Bug: 65416558 Test: Run 'View recovery logs' on angler. Test: Set large margin height that leaves text_rows less than 21. Then run 'View recovery logs' with 21 menu entries. Change-Id: I5d4e3a0a097039e1104eda7d494c6269053dc894 (cherry picked from commit e15d7a5104978cd8399501636aec0df9c1a4823c) --- screen_ui.cpp | 16 +++++++--------- screen_ui.h | 3 ++- wear_ui.cpp | 13 ++++++------- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/screen_ui.cpp b/screen_ui.cpp index 5c93b6672..b8f6ea28b 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -69,7 +69,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() text_top_(0), show_text(false), show_text_ever(false), - menu_(nullptr), + menu_headers_(nullptr), show_menu(false), menu_items(0), menu_sel(0), @@ -356,10 +356,10 @@ void ScreenRecoveryUI::draw_screen_locked() { DrawHighlightBar(0, y - 2, gr_fb_width(), char_height_ + 4); // Bold white text for the selected item. SetColor(MENU_SEL_FG); - y += DrawTextLine(x, y, menu_[i], true); + y += DrawTextLine(x, y, menu_[i].c_str(), true); SetColor(MENU); } else { - y += DrawTextLine(x, y, menu_[i], false); + y += DrawTextLine(x, y, menu_[i].c_str(), false); } } y += DrawHorizontalRule(y); @@ -508,7 +508,6 @@ bool ScreenRecoveryUI::Init(const std::string& locale) { text_ = Alloc2d(text_rows_, text_cols_ + 1); file_viewer_text_ = Alloc2d(text_rows_, text_cols_ + 1); - menu_ = Alloc2d(text_rows_, text_cols_ + 1); text_col_ = text_row_ = 0; text_top_ = 1; @@ -771,12 +770,11 @@ void ScreenRecoveryUI::StartMenu(const char* const* headers, const char* const* pthread_mutex_lock(&updateMutex); if (text_rows_ > 0 && text_cols_ > 0) { menu_headers_ = headers; - size_t i = 0; - for (; i < text_rows_ && items[i] != nullptr; ++i) { - strncpy(menu_[i], items[i], text_cols_ - 1); - menu_[i][text_cols_ - 1] = '\0'; + menu_.clear(); + for (size_t i = 0; i < text_rows_ && items[i] != nullptr; ++i) { + menu_.emplace_back(std::string(items[i], strnlen(items[i], text_cols_ - 1))); } - menu_items = i; + menu_items = static_cast(menu_.size()); show_menu = true; menu_sel = initial_selection; update_screen_locked(); diff --git a/screen_ui.h b/screen_ui.h index 62dda7558..8231a2ba0 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -21,6 +21,7 @@ #include #include +#include #include "ui.h" @@ -127,7 +128,7 @@ class ScreenRecoveryUI : public RecoveryUI { bool show_text; bool show_text_ever; // has show_text ever been true? - char** menu_; + std::vector menu_; const char* const* menu_headers_; bool show_menu; int menu_items, menu_sel; diff --git a/wear_ui.cpp b/wear_ui.cpp index 169ef20e1..624116c0c 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -154,11 +154,11 @@ void WearRecoveryUI::draw_screen_locked() { // white text of selected item SetColor(MENU_SEL_FG); if (menu_[i][0]) { - gr_text(gr_sys_font(), x + 4, y, menu_[i], 1); + gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 1); } SetColor(MENU); } else if (menu_[i][0]) { - gr_text(gr_sys_font(), x + 4, y, menu_[i], 0); + gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 0); } y += char_height_ + 4; } @@ -255,17 +255,16 @@ void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* it pthread_mutex_lock(&updateMutex); if (text_rows_ > 0 && text_cols_ > 0) { menu_headers_ = headers; - size_t i = 0; + menu_.clear(); // "i < text_rows_" is removed from the loop termination condition, // which is different from the one in ScreenRecoveryUI::StartMenu(). // Because WearRecoveryUI supports scrollable menu, it's fine to have // more entries than text_rows_. The menu may be truncated otherwise. // Bug: 23752519 - for (; items[i] != nullptr; i++) { - strncpy(menu_[i], items[i], text_cols_ - 1); - menu_[i][text_cols_ - 1] = '\0'; + for (size_t i = 0; items[i] != nullptr; i++) { + menu_.emplace_back(std::string(items[i], strnlen(items[i], text_cols_ - 1))); } - menu_items = i; + menu_items = static_cast(menu_.size()); show_menu = true; menu_sel = initial_selection; menu_start = 0; -- cgit v1.2.3 From c9b04168426f55d74a3de0bdb851307ea511a315 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 16 Sep 2017 05:39:19 -0700 Subject: Import translations. DO NOT MERGE Auto-generated-cl: translation import Exempt-From-Owner-Approval: translation import Bug: 64712476 Change-Id: Ia05d14ff5b7f6b283f46566c88a4edc4a89d5576 --- tools/recovery_l10n/res/values-pa/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/recovery_l10n/res/values-pa/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml index 8564c9c36..27972d117 100644 --- a/tools/recovery_l10n/res/values-pa/strings.xml +++ b/tools/recovery_l10n/res/values-pa/strings.xml @@ -1,9 +1,9 @@ - "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + "ਸਿਸਟਮ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" "ਮਿਟਾਈ ਜਾ ਰਹੀ ਹੈ" - "ਕੋਈ ਕਮਾਂਡ ਨਹੀਂ" + "ਕੋਈ ਆਦੇਸ਼ ਨਹੀਂ" "ਅਸ਼ੁੱਧੀ!" - "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" -- cgit v1.2.3 From ba30867b0174463bebefc92198ee24ec9e4bdfa3 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 27 Oct 2017 23:39:45 -0700 Subject: update_verifier: Fix the wrong computation with group_range_count. 'group_range_count' doesn't properly consider the pair-wise range structure. It may split the ranges into wrong pairs if it evaluates to an odd number. For example, for an input range string of "6,0,2,10,12,20,22" with 4 threads, group_range_count becomes 1. It would then try to verify (0,2), (2,10), (10,12) and (12,20). Note that (2,10) and (12,20) are not valid ranges to be verified, and with (20,22) uncovered. Bug: 68343761 Test: Trigger update_verifier verification. Check the number of verified blocks against the one in care_map.txt. Change-Id: I7c5769325d9866be06c45e7dbcc0c8ea266de714 (cherry picked from commit 62caeb5f48c9d7b1a8ed97c4a021195b8499b804) (cherry picked from commit 559a6d1d2ae2e5145641e1eb16e2c015d756d8c9) --- update_verifier/update_verifier.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index faebbede0..ba7b7aec4 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -137,11 +137,12 @@ static bool read_blocks(const std::string& partition, const std::string& range_s LOG(ERROR) << "Error in parsing range string."; return false; } + range_count /= 2; std::vector> threads; size_t thread_num = std::thread::hardware_concurrency() ?: 4; - thread_num = std::min(thread_num, range_count / 2); - size_t group_range_count = range_count / thread_num; + thread_num = std::min(thread_num, range_count); + size_t group_range_count = (range_count + thread_num - 1) / thread_num; for (size_t t = 0; t < thread_num; t++) { auto thread_func = [t, group_range_count, &dm_block_device, &ranges, &partition]() { @@ -154,7 +155,8 @@ static bool read_blocks(const std::string& partition, const std::string& range_s return false; } - for (size_t i = 1 + group_range_count * t; i < group_range_count * (t + 1) + 1; i += 2) { + for (size_t i = group_range_count * 2 * t + 1; + i < std::min(group_range_count * 2 * (t + 1) + 1, ranges.size()); i += 2) { unsigned int range_start, range_end; bool parse_status = android::base::ParseUint(ranges[i], &range_start); parse_status = parse_status && android::base::ParseUint(ranges[i + 1], &range_end); -- cgit v1.2.3