From 2b3f80068ece1040ba7c923afe1e70b705535ad5 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Tue, 20 Mar 2018 16:07:39 -0700 Subject: [PATCH 01/28] Factor out a menu class for screen ui Also consolidate the duplicate codes to draw the menu in ScreenRecoveryUI and WearRecoveryUI. This helps us to support text icons as menu in the future. Bug: 74397117 Test: Check the menu under recovery on bullhead and a wear device. Change-Id: Iba9b646c3828670f0e78a7e07d1a94a44e96bb0b (cherry picked from commit Iba9b646c3828670f0e78a7e07d1a94a44e96bb0b) --- Android.mk | 77 ++++++++----- screen_ui.cpp | 183 ++++++++++++++++++++++++------- screen_ui.h | 73 ++++++++++++- tests/Android.mk | 4 +- tests/unit/screen_ui_test.cpp | 198 ++++++++++++++++++++++++++++++++++ wear_ui.cpp | 116 ++------------------ wear_ui.h | 4 - 7 files changed, 471 insertions(+), 184 deletions(-) create mode 100644 tests/unit/screen_ui_test.cpp diff --git a/Android.mk b/Android.mk index 56d69c03..0499a6da 100644 --- a/Android.mk +++ b/Android.mk @@ -53,36 +53,21 @@ LOCAL_STATIC_LIBRARIES := \ include $(BUILD_STATIC_LIBRARY) -# recovery (static executable) +# librecovery_ui (static library) # =============================== include $(CLEAR_VARS) - LOCAL_SRC_FILES := \ - adb_install.cpp \ - device.cpp \ - fuse_sdcard_provider.cpp \ - recovery.cpp \ - roots.cpp \ - rotate_logs.cpp \ screen_ui.cpp \ ui.cpp \ vr_ui.cpp \ - wear_ui.cpp \ + wear_ui.cpp -LOCAL_MODULE := recovery +LOCAL_CFLAGS := -Wall -Werror -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 += sload.f2fs mkfs.f2fs -endif -endif - -LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) -LOCAL_CFLAGS += -Wall -Werror +LOCAL_MODULE := librecovery_ui +LOCAL_STATIC_LIBRARIES := \ + libminui \ + libbase ifneq ($(TARGET_RECOVERY_UI_MARGIN_HEIGHT),) LOCAL_CFLAGS += -DRECOVERY_UI_MARGIN_HEIGHT=$(TARGET_RECOVERY_UI_MARGIN_HEIGHT) @@ -132,6 +117,36 @@ else LOCAL_CFLAGS += -DRECOVERY_UI_VR_STEREO_OFFSET=0 endif +include $(BUILD_STATIC_LIBRARY) + +# recovery (static executable) +# =============================== +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + adb_install.cpp \ + device.cpp \ + fuse_sdcard_provider.cpp \ + recovery.cpp \ + roots.cpp \ + rotate_logs.cpp \ + + +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 += sload.f2fs mkfs.f2fs +endif +endif + +LOCAL_CFLAGS += -DRECOVERY_API_VERSION=$(RECOVERY_API_VERSION) +LOCAL_CFLAGS += -Wall -Werror + LOCAL_C_INCLUDES += \ system/vold \ @@ -148,8 +163,17 @@ LOCAL_STATIC_LIBRARIES := \ libvndksupport \ libbatterymonitor +LOCAL_STATIC_LIBRARIES += librecovery + +# If $(TARGET_RECOVERY_UI_LIB) is defined, the recovery calls make_device() from the +# $(TARGET_RECOVERY_UI_LIB), which depends on the librecovery_ui. +ifeq ($(TARGET_RECOVERY_UI_LIB),) + LOCAL_SRC_FILES += default_device.cpp +else + LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) +endif + LOCAL_STATIC_LIBRARIES += \ - librecovery \ libverifier \ libbootloader_message \ libfs_mgr \ @@ -161,6 +185,7 @@ LOCAL_STATIC_LIBRARIES += \ libminadbd \ libasyncio \ libfusesideload \ + librecovery_ui \ libminui \ libpng \ libcrypto_utils \ @@ -184,12 +209,6 @@ endif LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin -ifeq ($(TARGET_RECOVERY_UI_LIB),) - LOCAL_SRC_FILES += default_device.cpp -else - LOCAL_STATIC_LIBRARIES += $(TARGET_RECOVERY_UI_LIB) -endif - ifeq ($(BOARD_CACHEIMAGE_PARTITION_SIZE),) LOCAL_REQUIRED_MODULES += recovery-persist recovery-refresh endif diff --git a/screen_ui.cpp b/screen_ui.cpp index c8fb5aa7..317e5529 100644 --- a/screen_ui.cpp +++ b/screen_ui.cpp @@ -53,7 +53,98 @@ static double now() { return tv.tv_sec + tv.tv_usec / 1000000.0; } -ScreenRecoveryUI::ScreenRecoveryUI() +Menu::Menu(bool scrollable, size_t max_items, size_t max_length) + : scrollable_(scrollable), + max_display_items_(max_items), + max_item_length_(max_length), + text_headers_(nullptr), + menu_start_(0), + selection_(0) { + CHECK_LE(max_items, static_cast(std::numeric_limits::max())); +} + +const char* const* Menu::text_headers() const { + return text_headers_; +} + +std::string Menu::TextItem(size_t index) const { + CHECK_LT(index, text_items_.size()); + + return text_items_[index]; +} + +size_t Menu::MenuStart() const { + return menu_start_; +} + +size_t Menu::MenuEnd() const { + return std::min(ItemsCount(), menu_start_ + max_display_items_); +} + +size_t Menu::ItemsCount() const { + return text_items_.size(); +} + +bool Menu::ItemsOverflow(std::string* cur_selection_str) const { + if (!scrollable_ || static_cast(ItemsCount()) <= max_display_items_) { + return false; + } + + *cur_selection_str = + android::base::StringPrintf("Current item: %d/%zu", selection_ + 1, ItemsCount()); + return true; +} + +void Menu::Start(const char* const* headers, const char* const* items, int initial_selection) { + text_headers_ = headers; + + // It's fine to have more entries than text_rows_ if scrollable menu is supported. + size_t max_items_count = scrollable_ ? std::numeric_limits::max() : max_display_items_; + for (size_t i = 0; i < max_items_count && items[i] != nullptr; ++i) { + text_items_.emplace_back(items[i], strnlen(items[i], max_item_length_)); + } + + CHECK(!text_items_.empty()); + selection_ = initial_selection; +} + +// TODO(xunchang) modify the function parameters to button up & down. +int Menu::Select(int sel) { + CHECK_LE(ItemsCount(), static_cast(std::numeric_limits::max())); + int count = ItemsCount(); + + // Wraps the selection at boundary if the menu is not scrollable. + if (!scrollable_) { + if (sel < 0) { + selection_ = count - 1; + } else if (sel >= count) { + selection_ = 0; + } else { + selection_ = sel; + } + + return selection_; + } + + if (sel < 0) { + selection_ = 0; + } else if (sel >= count) { + selection_ = count - 1; + } else { + if (static_cast(sel) < menu_start_) { + menu_start_--; + } else if (static_cast(sel) >= MenuEnd()) { + menu_start_++; + } + selection_ = sel; + } + + return selection_; +} + +ScreenRecoveryUI::ScreenRecoveryUI() : ScreenRecoveryUI(false) {} + +ScreenRecoveryUI::ScreenRecoveryUI(bool scrollable_menu) : kMarginWidth(RECOVERY_UI_MARGIN_WIDTH), kMarginHeight(RECOVERY_UI_MARGIN_HEIGHT), kAnimationFps(RECOVERY_UI_ANIMATION_FPS), @@ -71,10 +162,7 @@ ScreenRecoveryUI::ScreenRecoveryUI() text_row_(0), show_text(false), show_text_ever(false), - menu_headers_(nullptr), - show_menu(false), - menu_items(0), - menu_sel(0), + scrollable_menu_(scrollable_menu), file_viewer_text_(nullptr), intro_frames(0), loop_frames(0), @@ -407,13 +495,13 @@ int ScreenRecoveryUI::DrawWrappedTextLines(int x, int y, const char* const* line static const char* REGULAR_HELP[] = { "Use volume up/down and power.", - NULL + nullptr, }; static const char* LONG_PRESS_HELP[] = { "Any button cycles highlight.", "Long-press activates.", - NULL + nullptr, }; // Redraws everything on the screen. Does not flip pages. Should only be called with updateMutex @@ -428,8 +516,13 @@ void ScreenRecoveryUI::draw_screen_locked() { gr_color(0, 0, 0, 255); gr_clear(); + draw_menu_and_text_buffer_locked(HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); +} + +// Draws the menu and text buffer on the screen. Should only be called with updateMutex locked. +void ScreenRecoveryUI::draw_menu_and_text_buffer_locked(const char* const* help_message) { int y = kMarginHeight; - if (show_menu) { + if (menu_) { static constexpr int kMenuIndent = 4; int x = kMarginWidth + kMenuIndent; @@ -440,26 +533,46 @@ void ScreenRecoveryUI::draw_screen_locked() { for (const auto& chunk : android::base::Split(recovery_fingerprint, ":")) { y += DrawTextLine(x, y, chunk.c_str(), false); } - y += DrawTextLines(x, y, HasThreeButtons() ? REGULAR_HELP : LONG_PRESS_HELP); + y += DrawTextLines(x, y, help_message); + + // Draw menu header. SetColor(HEADER); - // Ignore kMenuIndent, which is not taken into account by text_cols_. - y += DrawWrappedTextLines(kMarginWidth, y, menu_headers_); + if (!menu_->scrollable()) { + y += DrawWrappedTextLines(x, y, menu_->text_headers()); + } else { + y += DrawTextLines(x, y, menu_->text_headers()); + // Show the current menu item number in relation to total number if items don't fit on the + // screen. + std::string cur_selection_str; + if (menu_->ItemsOverflow(&cur_selection_str)) { + y += DrawTextLine(x, y, cur_selection_str.c_str(), true); + } + } + // Draw menu items. SetColor(MENU); - y += DrawHorizontalRule(y) + 4; - for (int i = 0; i < menu_items; ++i) { - if (i == menu_sel) { + // Do not draw the horizontal rule for wear devices. + if (!menu_->scrollable()) { + y += DrawHorizontalRule(y) + 4; + } + for (size_t i = menu_->MenuStart(); i < menu_->MenuEnd(); ++i) { + bool bold = false; + if (i == static_cast(menu_->selection())) { // Draw the highlight bar. SetColor(IsLongPress() ? MENU_SEL_BG_ACTIVE : MENU_SEL_BG); - DrawHighlightBar(0, y - 2, ScreenWidth(), char_height_ + 4); + + int bar_height = char_height_ + 4; + DrawHighlightBar(0, y - 2, ScreenWidth(), bar_height); + // Bold white text for the selected item. SetColor(MENU_SEL_FG); - y += DrawTextLine(x, y, menu_[i].c_str(), true); - SetColor(MENU); - } else { - y += DrawTextLine(x, y, menu_[i].c_str(), false); + bold = true; } + + y += DrawTextLine(x, y, menu_->TextItem(i).c_str(), bold); + + SetColor(MENU); } y += DrawHorizontalRule(y); } @@ -864,15 +977,10 @@ void ScreenRecoveryUI::ShowFile(const char* filename) { 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; - 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 = static_cast(menu_.size()); - show_menu = true; - menu_sel = initial_selection; + if (text_rows_ > 0 && text_cols_ > 1) { + menu_ = std::make_unique(scrollable_menu_, text_rows_, text_cols_ - 1); + menu_->Start(headers, items, initial_selection); + update_screen_locked(); } pthread_mutex_unlock(&updateMutex); @@ -880,16 +988,13 @@ void ScreenRecoveryUI::StartMenu(const char* const* headers, const char* const* int ScreenRecoveryUI::SelectMenu(int sel) { pthread_mutex_lock(&updateMutex); - if (show_menu) { - int old_sel = menu_sel; - menu_sel = sel; + if (menu_) { + int old_sel = menu_->selection(); + sel = menu_->Select(sel); - // 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(); + if (sel != old_sel) { + update_screen_locked(); + } } pthread_mutex_unlock(&updateMutex); return sel; @@ -897,8 +1002,8 @@ int ScreenRecoveryUI::SelectMenu(int sel) { void ScreenRecoveryUI::EndMenu() { pthread_mutex_lock(&updateMutex); - if (show_menu && text_rows_ > 0 && text_cols_ > 0) { - show_menu = false; + if (menu_) { + menu_.reset(); update_screen_locked(); } pthread_mutex_unlock(&updateMutex); diff --git a/screen_ui.h b/screen_ui.h index f05761c4..c1222a57 100644 --- a/screen_ui.h +++ b/screen_ui.h @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -28,6 +29,70 @@ // From minui/minui.h. struct GRSurface; +// This class maintains the menu selection and display of the screen ui. +class Menu { + public: + Menu(bool scrollable, size_t max_items, size_t max_length); + + bool scrollable() const { + return scrollable_; + } + + int selection() const { + return selection_; + } + + // Returns count of menu items. + size_t ItemsCount() const; + // Returns the index of the first menu item. + size_t MenuStart() const; + // Returns the index of the last menu item + 1. + size_t MenuEnd() const; + + // Menu example: + // info: Android Recovery + // .... + // help messages: Swipe up/down to move + // Swipe left/right to select + // empty line (horizontal rule): + // menu headers: Select file to view + // menu items: /cache/recovery/last_log + // /cache/recovery/last_log.1 + // /cache/recovery/last_log.2 + // ... + const char* const* text_headers() const; + std::string TextItem(size_t index) const; + + // Checks if the menu items fit vertically on the screen. Returns true and set the + // |cur_selection_str| if the items exceed the screen limit. + bool ItemsOverflow(std::string* cur_selection_str) const; + + // Starts the menu with |headers| and |items| in text. Sets the default selection to + // |initial_selection|. + void Start(const char* const* headers, const char* const* items, int initial_selection); + + // Sets the current selection to |sel|. Handle the overflow cases depending on if the menu is + // scrollable. + int Select(int sel); + + private: + // The menu is scrollable to display more items. Used on wear devices who have smaller screens. + const bool scrollable_; + // The max number of menu items to fit vertically on a screen. + const size_t max_display_items_; + // The length of each item to fit horizontally on a screen. + const size_t max_item_length_; + + // Internal storage for the menu headers and items in text. + const char* const* text_headers_; + std::vector text_items_; + + // The first item to display on the screen. + size_t menu_start_; + // Current menu selection. + int selection_; +}; + // Implementation of RecoveryUI appropriate for devices with a screen // (shows an icon + a progress bar, text logging, menu, etc.) class ScreenRecoveryUI : public RecoveryUI { @@ -44,6 +109,7 @@ class ScreenRecoveryUI : public RecoveryUI { }; ScreenRecoveryUI(); + explicit ScreenRecoveryUI(bool scrollable_menu); bool Init(const std::string& locale) override; @@ -101,6 +167,7 @@ class ScreenRecoveryUI : public RecoveryUI { virtual void draw_background_locked(); virtual void draw_foreground_locked(); virtual void draw_screen_locked(); + virtual void draw_menu_and_text_buffer_locked(const char* const* help_message); virtual void update_screen_locked(); virtual void update_progress_locked(); @@ -184,10 +251,8 @@ class ScreenRecoveryUI : public RecoveryUI { bool show_text; bool show_text_ever; // has show_text ever been true? - std::vector menu_; - const char* const* menu_headers_; - bool show_menu; - int menu_items, menu_sel; + bool scrollable_menu_; + std::unique_ptr menu_; // An alternate text screen, swapped with 'text_' when we're viewing a log file. char** file_viewer_text_; diff --git a/tests/Android.mk b/tests/Android.mk index b3584fe8..9a71371f 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -23,6 +23,7 @@ LOCAL_MODULE := recovery_unit_test LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_STATIC_LIBRARIES := \ libverifier \ + librecovery_ui \ libminui \ libotautil \ libupdater \ @@ -38,8 +39,9 @@ LOCAL_SRC_FILES := \ unit/dirutil_test.cpp \ unit/locale_test.cpp \ unit/rangeset_test.cpp \ + unit/screen_ui_test.cpp \ unit/sysutil_test.cpp \ - unit/zip_test.cpp \ + unit/zip_test.cpp LOCAL_C_INCLUDES := bootable/recovery LOCAL_SHARED_LIBRARIES := liblog diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp new file mode 100644 index 00000000..be6799f2 --- /dev/null +++ b/tests/unit/screen_ui_test.cpp @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2018 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 "screen_ui.h" + +#include + +#include + +constexpr const char* HEADER[] = { "header", nullptr }; +constexpr const char* ITEMS[] = { "items1", "items2", "items3", "items4", "1234567890", nullptr }; + +TEST(ScreenUITest, StartPhoneMenuSmoke) { + Menu menu(false, 10, 20); + ASSERT_FALSE(menu.scrollable()); + + menu.Start(HEADER, ITEMS, 0); + ASSERT_EQ(HEADER[0], menu.text_headers()[0]); + ASSERT_EQ(5u, menu.ItemsCount()); + + std::string message; + ASSERT_FALSE(menu.ItemsOverflow(&message)); + for (size_t i = 0; i < menu.ItemsCount(); i++) { + ASSERT_EQ(ITEMS[i], menu.TextItem(i)); + } + + ASSERT_EQ(0, menu.selection()); +} + +TEST(ScreenUITest, StartWearMenuSmoke) { + Menu menu(true, 10, 8); + ASSERT_TRUE(menu.scrollable()); + + menu.Start(HEADER, ITEMS, 1); + ASSERT_EQ(HEADER[0], menu.text_headers()[0]); + ASSERT_EQ(5u, menu.ItemsCount()); + + std::string message; + ASSERT_FALSE(menu.ItemsOverflow(&message)); + for (size_t i = 0; i < menu.ItemsCount() - 1; i++) { + ASSERT_EQ(ITEMS[i], menu.TextItem(i)); + } + // Test of the last item is truncated + ASSERT_EQ("12345678", menu.TextItem(4)); + ASSERT_EQ(1, menu.selection()); +} + +TEST(ScreenUITest, StartPhoneMenuItemsOverflow) { + Menu menu(false, 1, 20); + ASSERT_FALSE(menu.scrollable()); + + menu.Start(HEADER, ITEMS, 0); + ASSERT_EQ(1u, menu.ItemsCount()); + + std::string message; + ASSERT_FALSE(menu.ItemsOverflow(&message)); + for (size_t i = 0; i < menu.ItemsCount(); i++) { + ASSERT_EQ(ITEMS[i], menu.TextItem(i)); + } + + ASSERT_EQ(0u, menu.MenuStart()); + ASSERT_EQ(1u, menu.MenuEnd()); +} + +TEST(ScreenUITest, StartWearMenuItemsOverflow) { + Menu menu(true, 1, 20); + ASSERT_TRUE(menu.scrollable()); + + menu.Start(HEADER, ITEMS, 0); + ASSERT_EQ(5u, menu.ItemsCount()); + + std::string message; + ASSERT_TRUE(menu.ItemsOverflow(&message)); + ASSERT_EQ("Current item: 1/5", message); + + for (size_t i = 0; i < menu.ItemsCount(); i++) { + ASSERT_EQ(ITEMS[i], menu.TextItem(i)); + } + + ASSERT_EQ(0u, menu.MenuStart()); + ASSERT_EQ(1u, menu.MenuEnd()); +} + +TEST(ScreenUITest, PhoneMenuSelectSmoke) { + Menu menu(false, 10, 20); + + int sel = 0; + menu.Start(HEADER, ITEMS, sel); + // Mimic down button 10 times (2 * items size) + for (int i = 0; i < 10; i++) { + sel = menu.Select(++sel); + ASSERT_EQ(sel, menu.selection()); + + // Wraps the selection for unscrollable menu when it reaches the boundary. + int expected = (i + 1) % 5; + ASSERT_EQ(expected, menu.selection()); + + ASSERT_EQ(0u, menu.MenuStart()); + ASSERT_EQ(5u, menu.MenuEnd()); + } + + // Mimic up button 10 times + for (int i = 0; i < 10; i++) { + sel = menu.Select(--sel); + ASSERT_EQ(sel, menu.selection()); + + int expected = (9 - i) % 5; + ASSERT_EQ(expected, menu.selection()); + + ASSERT_EQ(0u, menu.MenuStart()); + ASSERT_EQ(5u, menu.MenuEnd()); + } +} + +TEST(ScreenUITest, WearMenuSelectSmoke) { + Menu menu(true, 10, 20); + + int sel = 0; + menu.Start(HEADER, ITEMS, sel); + // Mimic pressing down button 10 times (2 * items size) + for (int i = 0; i < 10; i++) { + sel = menu.Select(++sel); + ASSERT_EQ(sel, menu.selection()); + + // Stops the selection at the boundary if the menu is scrollable. + int expected = std::min(i + 1, 4); + ASSERT_EQ(expected, menu.selection()); + + ASSERT_EQ(0u, menu.MenuStart()); + ASSERT_EQ(5u, menu.MenuEnd()); + } + + // Mimic pressing up button 10 times + for (int i = 0; i < 10; i++) { + sel = menu.Select(--sel); + ASSERT_EQ(sel, menu.selection()); + + int expected = std::max(3 - i, 0); + ASSERT_EQ(expected, menu.selection()); + + ASSERT_EQ(0u, menu.MenuStart()); + ASSERT_EQ(5u, menu.MenuEnd()); + } +} + +TEST(ScreenUITest, WearMenuSelectItemsOverflow) { + Menu menu(true, 3, 20); + + int sel = 1; + menu.Start(HEADER, ITEMS, sel); + ASSERT_EQ(5u, menu.ItemsCount()); + + // Scroll the menu to the end, and check the start & end of menu. + for (int i = 0; i < 3; i++) { + sel = menu.Select(++sel); + ASSERT_EQ(i + 2, sel); + ASSERT_EQ(static_cast(i), menu.MenuStart()); + ASSERT_EQ(static_cast(i + 3), menu.MenuEnd()); + } + + // Press down button one more time won't change the MenuStart() and MenuEnd(). + sel = menu.Select(++sel); + ASSERT_EQ(4, sel); + ASSERT_EQ(2u, menu.MenuStart()); + ASSERT_EQ(5u, menu.MenuEnd()); + + // Scroll the menu to the top. + // The expected menu sel, start & ends are: + // sel 3, start 2, end 5 + // sel 2, start 2, end 5 + // sel 1, start 1, end 4 + // sel 0, start 0, end 3 + for (int i = 0; i < 4; i++) { + sel = menu.Select(--sel); + ASSERT_EQ(3 - i, sel); + ASSERT_EQ(static_cast(std::min(3 - i, 2)), menu.MenuStart()); + ASSERT_EQ(static_cast(std::min(6 - i, 5)), menu.MenuEnd()); + } + + // Press up button one more time won't change the MenuStart() and MenuEnd(). + sel = menu.Select(--sel); + ASSERT_EQ(0, sel); + ASSERT_EQ(0u, menu.MenuStart()); + ASSERT_EQ(3u, menu.MenuEnd()); +} diff --git a/wear_ui.cpp b/wear_ui.cpp index ca6b1b10..118e4350 100644 --- a/wear_ui.cpp +++ b/wear_ui.cpp @@ -17,7 +17,6 @@ #include "wear_ui.h" #include -#include // TODO: Remove after killing the call to sprintf(). #include #include @@ -27,7 +26,8 @@ #include WearRecoveryUI::WearRecoveryUI() - : kProgressBarBaseline(RECOVERY_UI_PROGRESS_BAR_BASELINE), + : ScreenRecoveryUI(true), + 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(). @@ -65,13 +65,10 @@ static const char* SWIPE_HELP[] = { "Swipe up/down to move.", "Swipe left/right to select.", "", - NULL + nullptr, }; -// TODO merge drawing routines with screen_ui void WearRecoveryUI::draw_screen_locked() { - char cur_selection_str[50]; - draw_background_locked(); if (!show_text) { draw_foreground_locked(); @@ -79,68 +76,7 @@ void WearRecoveryUI::draw_screen_locked() { SetColor(TEXT_FILL); gr_fill(0, 0, gr_fb_width(), gr_fb_height()); - int y = kMarginHeight; - int x = kMarginWidth; - if (show_menu) { - std::string recovery_fingerprint = - android::base::GetProperty("ro.bootimage.build.fingerprint", ""); - SetColor(HEADER); - y += DrawTextLine(x + 4, y, "Android Recovery", true); - for (auto& chunk : android::base::Split(recovery_fingerprint, ":")) { - y += DrawTextLine(x + 4, y, chunk.c_str(), false); - } - - // This is actually the help strings. - y += DrawTextLines(x + 4, y, SWIPE_HELP); - SetColor(HEADER); - 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. - 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].c_str(), 1); - } - SetColor(MENU); - } else if (menu_[i][0]) { - gr_text(gr_sys_font(), x + 4, y, menu_[i].c_str(), 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 row = text_row_; - size_t count = 0; - 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; - if (row < 0) row = text_rows_ - 1; - } + draw_menu_and_text_buffer_locked(SWIPE_HELP); } } @@ -156,45 +92,11 @@ void WearRecoveryUI::StartMenu(const char* const* headers, const char* const* it int initial_selection) { pthread_mutex_lock(&updateMutex); if (text_rows_ > 0 && text_cols_ > 0) { - menu_headers_ = headers; - 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 (size_t i = 0; items[i] != nullptr; i++) { - menu_.emplace_back(std::string(items[i], strnlen(items[i], text_cols_ - 1))); - } - menu_items = static_cast(menu_.size()); - show_menu = true; - menu_sel = initial_selection; - menu_start = 0; - menu_end = text_rows_ - 1 - kMenuUnusableRows; - if (menu_items <= menu_end) menu_end = menu_items; + menu_ = std::make_unique(scrollable_menu_, text_rows_ - kMenuUnusableRows - 1, + text_cols_ - 1); + menu_->Start(headers, items, initial_selection); + 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(); - } - pthread_mutex_unlock(&updateMutex); - return sel; -} +} \ No newline at end of file diff --git a/wear_ui.h b/wear_ui.h index 739b4cb1..8b24cb73 100644 --- a/wear_ui.h +++ b/wear_ui.h @@ -25,10 +25,8 @@ class WearRecoveryUI : public ScreenRecoveryUI { void SetStage(int current, int max) 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 @@ -45,8 +43,6 @@ class WearRecoveryUI : public ScreenRecoveryUI { private: void draw_background_locked() override; void draw_screen_locked() override; - - int menu_start, menu_end; }; #endif // RECOVERY_WEAR_UI_H From 6ec9ddddda7f280adebf0006e2546b3a54678aa2 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 26 Apr 2018 10:24:03 -0700 Subject: [PATCH 02/28] Mark ui_print with __printflike. And fix an issue as a result of the change. Test: mmma -j bootable/recovery Change-Id: I94e6384a1f39e9c37a8ed029d235142738d6e5d3 (cherry picked from commit 8af89c3a02c1bf358fc3c3b4e0a7cedc8f48631e) --- common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common.h b/common.h index a8513069..4228e71d 100644 --- a/common.h +++ b/common.h @@ -40,7 +40,7 @@ extern const char* reason; // fopen a file, mounting volumes and making parent dirs as necessary. FILE* fopen_path(const std::string& path, const char* mode); -void ui_print(const char* format, ...); +void ui_print(const char* format, ...) __printflike(1, 2); bool is_ro_debuggable(); From 3c3f211d1e5698da6eea9e83584acb2dee4ca46e Mon Sep 17 00:00:00 2001 From: Jerry Zhang Date: Wed, 2 May 2018 16:56:00 -0700 Subject: [PATCH 03/28] recovery: Refactor logging code into logging.cpp Move common logging related functions to rotate_logs.cpp, and rename that to logging.cpp. Test: Recovery works Bug: 78793464 Change-Id: I00f20a79a296680122b8437d54a87897c5cb2fc7 --- Android.bp | 12 +- Android.mk | 2 +- common.h | 9 +- logging.cpp | 243 +++++++++++++++++++++++++++++++++++++ rotate_logs.h => logging.h | 18 ++- recovery-persist.cpp | 2 +- recovery-refresh.cpp | 2 +- recovery.cpp | 134 +------------------- recovery_main.cpp | 2 +- rotate_logs.cpp | 107 ---------------- 10 files changed, 280 insertions(+), 251 deletions(-) create mode 100644 logging.cpp rename rotate_logs.h => logging.h (71%) delete mode 100644 rotate_logs.cpp diff --git a/Android.bp b/Android.bp index 9ad961d5..22c90bd3 100644 --- a/Android.bp +++ b/Android.bp @@ -89,8 +89,8 @@ cc_binary { ], srcs: [ + "logging.cpp", "recovery-persist.cpp", - "rotate_logs.cpp", ], shared_libs: [ @@ -98,6 +98,10 @@ cc_binary { "liblog", ], + static_libs: [ + "libotautil", + ], + init_rc: [ "recovery-persist.rc", ], @@ -112,8 +116,8 @@ cc_binary { ], srcs: [ + "logging.cpp", "recovery-refresh.cpp", - "rotate_logs.cpp", ], shared_libs: [ @@ -121,6 +125,10 @@ cc_binary { "liblog", ], + static_libs: [ + "libotautil", + ], + init_rc: [ "recovery-refresh.rc", ], diff --git a/Android.mk b/Android.mk index eff4b015..dbd2eb78 100644 --- a/Android.mk +++ b/Android.mk @@ -126,10 +126,10 @@ LOCAL_SRC_FILES := \ adb_install.cpp \ device.cpp \ fuse_sdcard_provider.cpp \ + logging.cpp \ recovery.cpp \ recovery_main.cpp \ roots.cpp \ - rotate_logs.cpp \ LOCAL_MODULE := recovery diff --git a/common.h b/common.h index de536fdb..3dc36a96 100644 --- a/common.h +++ b/common.h @@ -27,7 +27,9 @@ static constexpr int kRecoveryApiVersion = 3; class RecoveryUI; +struct selabel_handle; +extern struct selabel_handle* sehandle; extern RecoveryUI* ui; extern bool modified_flash; @@ -37,13 +39,6 @@ extern std::string stage; // The reason argument provided in "--reason=". extern const char* reason; -// fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the -// file pointer, or nullptr on error. -FILE* fopen_path(const std::string& path, const char* mode); - -// In turn fflush(3)'s, fsync(3)'s and fclose(3)'s the given stream. -void check_and_fclose(FILE* fp, const std::string& name); - void ui_print(const char* format, ...) __printflike(1, 2); bool is_ro_debuggable(); diff --git a/logging.cpp b/logging.cpp new file mode 100644 index 00000000..d5af72aa --- /dev/null +++ b/logging.cpp @@ -0,0 +1,243 @@ +/* + * 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 "logging.h" + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include /* for AID_SYSTEM */ +#include /* private pmsg functions */ + +#include "common.h" +#include "otautil/dirutil.h" +#include "otautil/paths.h" +#include "roots.h" + +static constexpr const char* LOG_FILE = "/cache/recovery/log"; +static constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install"; +static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; +static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log"; + +static const std::string LAST_KMSG_FILTER = "recovery/last_kmsg"; +static const std::string LAST_LOG_FILTER = "recovery/last_log"; + +// fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the +// file pointer, or nullptr on error. +static FILE* fopen_path(const std::string& path, const char* mode) { + if (ensure_path_mounted(path.c_str()) != 0) { + LOG(ERROR) << "Can't mount " << path; + return nullptr; + } + + // When writing, try to create the containing directory, if necessary. Use generous permissions, + // the system (init.rc) will reset them. + if (strchr("wa", mode[0])) { + mkdir_recursively(path, 0777, true, sehandle); + } + return fopen(path.c_str(), mode); +} + +void check_and_fclose(FILE* fp, const std::string& name) { + fflush(fp); + if (fsync(fileno(fp)) == -1) { + PLOG(ERROR) << "Failed to fsync " << name; + } + if (ferror(fp)) { + PLOG(ERROR) << "Error in " << name; + } + fclose(fp); +} + +// close a file, log an error if the error indicator is set +ssize_t logbasename(log_id_t /* id */, char /* prio */, const char* filename, const char* /* buf */, + size_t len, void* arg) { + bool* do_rotate = static_cast(arg); + if (LAST_KMSG_FILTER.find(filename) != std::string::npos || + LAST_LOG_FILTER.find(filename) != std::string::npos) { + *do_rotate = true; + } + return len; +} + +ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf, size_t len, + void* arg) { + bool* do_rotate = static_cast(arg); + if (!*do_rotate) { + return __android_log_pmsg_file_write(id, prio, filename, buf, len); + } + + std::string name(filename); + size_t dot = name.find_last_of('.'); + std::string sub = name.substr(0, dot); + + if (LAST_KMSG_FILTER.find(sub) == std::string::npos && + LAST_LOG_FILTER.find(sub) == std::string::npos) { + return __android_log_pmsg_file_write(id, prio, filename, buf, len); + } + + // filename rotation + if (dot == std::string::npos) { + name += ".1"; + } else { + std::string number = name.substr(dot + 1); + if (!isdigit(number[0])) { + name += ".1"; + } else { + size_t i; + if (!android::base::ParseUint(number, &i)) { + LOG(ERROR) << "failed to parse uint in " << number; + return -1; + } + name = sub + "." + std::to_string(i + 1); + } + } + + return __android_log_pmsg_file_write(id, prio, name.c_str(), buf, len); +} + +// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. +// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. +// Overwrite any existing last_log.$max and last_kmsg.$max. +void rotate_logs(const char* last_log_file, const char* last_kmsg_file) { + // Logs should only be rotated once. + static bool rotated = false; + if (rotated) { + return; + } + rotated = true; + + for (int i = KEEP_LOG_COUNT - 1; i >= 0; --i) { + std::string old_log = android::base::StringPrintf("%s", last_log_file); + if (i > 0) { + old_log += "." + std::to_string(i); + } + std::string new_log = android::base::StringPrintf("%s.%d", last_log_file, i + 1); + // Ignore errors if old_log doesn't exist. + rename(old_log.c_str(), new_log.c_str()); + + std::string old_kmsg = android::base::StringPrintf("%s", last_kmsg_file); + if (i > 0) { + old_kmsg += "." + std::to_string(i); + } + std::string new_kmsg = android::base::StringPrintf("%s.%d", last_kmsg_file, i + 1); + rename(old_kmsg.c_str(), new_kmsg.c_str()); + } +} + +// Writes content to the current pmsg session. +static ssize_t __pmsg_write(const std::string& filename, const std::string& buf) { + return __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filename.c_str(), + buf.data(), buf.size()); +} + +void copy_log_file_to_pmsg(const std::string& source, const std::string& destination) { + std::string content; + android::base::ReadFileToString(source, &content); + __pmsg_write(destination, content); +} + +// How much of the temp log we have copied to the copy in cache. +static off_t tmplog_offset = 0; + +void reset_tmplog_offset() { + tmplog_offset = 0; +} + +void copy_log_file(const std::string& source, const std::string& destination, bool append) { + 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.c_str(), "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); + } +} + +void copy_logs(bool modified_flash, bool has_cache) { + // We only rotate and record the log of the current session if there are actual attempts to modify + // the flash, such as wipes, installs from BCB or menu selections. This is to avoid unnecessary + // rotation (and possible deletion) of log files, if it does not do anything loggable. + if (!modified_flash) { + return; + } + + // Always write to pmsg, this allows the OTA logs to be caught in `logcat -L`. + copy_log_file_to_pmsg(Paths::Get().temporary_log_file(), LAST_LOG_FILE); + copy_log_file_to_pmsg(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE); + + // We can do nothing for now if there's no /cache partition. + if (!has_cache) { + return; + } + + ensure_path_mounted(LAST_LOG_FILE); + ensure_path_mounted(LAST_KMSG_FILE); + rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); + + // Copy logs to cache so the system can find out what happened. + copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true); + copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false); + copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false); + save_kernel_log(LAST_KMSG_FILE); + chmod(LOG_FILE, 0600); + chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM); + chmod(LAST_KMSG_FILE, 0600); + chown(LAST_KMSG_FILE, AID_SYSTEM, AID_SYSTEM); + chmod(LAST_LOG_FILE, 0640); + chmod(LAST_INSTALL_FILE, 0644); + sync(); +} + +// Read from kernel log into buffer and write out to file. +void save_kernel_log(const char* destination) { + int klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0); + if (klog_buf_len <= 0) { + PLOG(ERROR) << "Error getting klog size"; + return; + } + + std::string buffer(klog_buf_len, 0); + int n = klogctl(KLOG_READ_ALL, &buffer[0], klog_buf_len); + if (n == -1) { + PLOG(ERROR) << "Error in reading klog"; + return; + } + buffer.resize(n); + android::base::WriteStringToFile(buffer, destination); +} diff --git a/rotate_logs.h b/logging.h similarity index 71% rename from rotate_logs.h rename to logging.h index 007c33d4..3cfbc7af 100644 --- a/rotate_logs.h +++ b/logging.h @@ -14,12 +14,14 @@ * limitations under the License. */ -#ifndef _ROTATE_LOGS_H -#define _ROTATE_LOGS_H +#ifndef _LOGGING_H +#define _LOGGING_H #include #include +#include + #include static constexpr int KEEP_LOG_COUNT = 10; @@ -35,4 +37,14 @@ ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf, // Overwrite any existing last_log.$max and last_kmsg.$max. void rotate_logs(const char* last_log_file, const char* last_kmsg_file); -#endif //_ROTATE_LOG_H +// In turn fflush(3)'s, fsync(3)'s and fclose(3)'s the given stream. +void check_and_fclose(FILE* fp, const std::string& name); + +void copy_log_file_to_pmsg(const std::string& source, const std::string& destination); +void copy_log_file(const std::string& source, const std::string& destination, bool append); +void copy_logs(bool modified_flash, bool has_cache); +void reset_tmplog_offset(); + +void save_kernel_log(const char* destination); + +#endif //_LOGGING_H diff --git a/recovery-persist.cpp b/recovery-persist.cpp index dbce7ff7..d3ade626 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -41,7 +41,7 @@ #include #include /* private pmsg functions */ -#include "rotate_logs.h" +#include "logging.h" static const char *LAST_LOG_FILE = "/data/misc/recovery/last_log"; static const char *LAST_PMSG_FILE = "/sys/fs/pstore/pmsg-ramoops-0"; diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp index 14565d3f..aee1ca59 100644 --- a/recovery-refresh.cpp +++ b/recovery-refresh.cpp @@ -42,7 +42,7 @@ #include /* private pmsg functions */ -#include "rotate_logs.h" +#include "logging.h" int main(int argc, char **argv) { static const char filter[] = "recovery/"; diff --git a/recovery.cpp b/recovery.cpp index 00f38596..0ab34197 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -53,8 +52,6 @@ #include #include /* for property_list */ #include -#include /* for AID_SYSTEM */ -#include /* private pmsg functions */ #include #include #include @@ -66,21 +63,19 @@ #include "fuse_sdcard_provider.h" #include "fuse_sideload.h" #include "install.h" +#include "logging.h" #include "minui/minui.h" #include "otautil/dirutil.h" #include "otautil/error_code.h" #include "otautil/paths.h" #include "otautil/sysutil.h" #include "roots.h" -#include "rotate_logs.h" #include "screen_ui.h" #include "stub_ui.h" #include "ui.h" static constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; static constexpr const char* COMMAND_FILE = "/cache/recovery/command"; -static constexpr const char* LOG_FILE = "/cache/recovery/log"; -static constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install"; static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log"; static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale"; @@ -149,31 +144,6 @@ struct selabel_handle* sehandle; * 7b. the user reboots (pulling the battery, etc) into the main system */ -FILE* fopen_path(const std::string& path, const char* mode) { - if (ensure_path_mounted(path.c_str()) != 0) { - LOG(ERROR) << "Can't mount " << path; - return nullptr; - } - - // When writing, try to create the containing directory, if necessary. Use generous permissions, - // the system (init.rc) will reset them. - if (strchr("wa", mode[0])) { - mkdir_recursively(path, 0777, true, sehandle); - } - return fopen(path.c_str(), mode); -} - -void check_and_fclose(FILE* fp, const std::string& name) { - fflush(fp); - if (fsync(fileno(fp)) == -1) { - PLOG(ERROR) << "Failed to fsync " << name; - } - if (ferror(fp)) { - PLOG(ERROR) << "Error in " << name; - } - fclose(fp); -} - bool is_ro_debuggable() { return android::base::GetBoolProperty("ro.debuggable", false); } @@ -259,98 +229,6 @@ static void set_sdcard_update_bootloader_message() { } } -// Read from kernel log into buffer and write out to file. -static void save_kernel_log(const char* destination) { - int klog_buf_len = klogctl(KLOG_SIZE_BUFFER, 0, 0); - if (klog_buf_len <= 0) { - PLOG(ERROR) << "Error getting klog size"; - return; - } - - std::string buffer(klog_buf_len, 0); - int n = klogctl(KLOG_READ_ALL, &buffer[0], klog_buf_len); - if (n == -1) { - PLOG(ERROR) << "Error in reading klog"; - return; - } - buffer.resize(n); - android::base::WriteStringToFile(buffer, destination); -} - -// Writes content to the current pmsg session. -static ssize_t __pmsg_write(const std::string& filename, const std::string& buf) { - return __android_log_pmsg_file_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filename.c_str(), - buf.data(), buf.size()); -} - -static void copy_log_file_to_pmsg(const std::string& source, const std::string& destination) { - std::string content; - android::base::ReadFileToString(source, &content); - __pmsg_write(destination, content); -} - -// How much of the temp log we have copied to the copy in cache. -static off_t tmplog_offset = 0; - -static void copy_log_file(const std::string& source, const std::string& destination, bool append) { - 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.c_str(), "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() { - // We only rotate and record the log of the current session if there are actual attempts to modify - // the flash, such as wipes, installs from BCB or menu selections. This is to avoid unnecessary - // rotation (and possible deletion) of log files, if it does not do anything loggable. - if (!modified_flash) { - return; - } - - // Always write to pmsg, this allows the OTA logs to be caught in `logcat -L`. - copy_log_file_to_pmsg(Paths::Get().temporary_log_file(), LAST_LOG_FILE); - copy_log_file_to_pmsg(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE); - - // We can do nothing for now if there's no /cache partition. - if (!has_cache) { - return; - } - - ensure_path_mounted(LAST_LOG_FILE); - ensure_path_mounted(LAST_KMSG_FILE); - rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); - - // Copy logs to cache so the system can find out what happened. - copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true); - copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false); - copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false); - save_kernel_log(LAST_KMSG_FILE); - chmod(LOG_FILE, 0600); - chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM); - chmod(LAST_KMSG_FILE, 0600); - chown(LAST_KMSG_FILE, AID_SYSTEM, AID_SYSTEM); - chmod(LAST_LOG_FILE, 0640); - chmod(LAST_INSTALL_FILE, 0644); - sync(); -} - // 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. @@ -366,7 +244,7 @@ static void finish_recovery() { } } - copy_logs(); + copy_logs(modified_flash, has_cache); // Reset to normal system boot so recovery won't cycle indefinitely. std::string err; @@ -482,8 +360,8 @@ static bool erase_volume(const char* volume) { // Any part of the log we'd copied to cache is now gone. // Reset the pointer so we copy from the beginning of the temp // log. - tmplog_offset = 0; - copy_logs(); + reset_tmplog_offset(); + copy_logs(modified_flash, has_cache); } return (result == 0); @@ -1000,7 +878,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); - copy_logs(); + copy_logs(modified_flash, has_cache); } else if (!ui->IsTextVisible()) { return Device::NO_ACTION; // reboot if logs aren't visible } else { @@ -1394,7 +1272,7 @@ int start_recovery(int argc, char** argv) { // RETRY_LIMIT times before we abandon this OTA update. static constexpr int RETRY_LIMIT = 4; if (status == INSTALL_RETRY && retry_count < RETRY_LIMIT) { - copy_logs(); + copy_logs(modified_flash, has_cache); retry_count += 1; set_retry_bootloader_message(retry_count, args); // Print retry count on screen. diff --git a/recovery_main.cpp b/recovery_main.cpp index 9f579f7c..3147511e 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -24,10 +24,10 @@ #include /* private pmsg functions */ #include "common.h" +#include "logging.h" #include "minadbd/minadbd.h" #include "otautil/paths.h" #include "private/recovery.h" -#include "rotate_logs.h" #include "ui.h" static void UiLogger(android::base::LogId /* id */, android::base::LogSeverity severity, diff --git a/rotate_logs.cpp b/rotate_logs.cpp deleted file mode 100644 index da008792..00000000 --- a/rotate_logs.cpp +++ /dev/null @@ -1,107 +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 "rotate_logs.h" - -#include -#include -#include - -#include - -#include -#include -#include -#include -#include /* private pmsg functions */ - -static const std::string LAST_KMSG_FILTER = "recovery/last_kmsg"; -static const std::string LAST_LOG_FILTER = "recovery/last_log"; - -ssize_t logbasename(log_id_t /* id */, char /* prio */, const char* filename, const char* /* buf */, - size_t len, void* arg) { - bool* do_rotate = static_cast(arg); - if (LAST_KMSG_FILTER.find(filename) != std::string::npos || - LAST_LOG_FILTER.find(filename) != std::string::npos) { - *do_rotate = true; - } - return len; -} - -ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf, size_t len, - void* arg) { - bool* do_rotate = static_cast(arg); - if (!*do_rotate) { - return __android_log_pmsg_file_write(id, prio, filename, buf, len); - } - - std::string name(filename); - size_t dot = name.find_last_of('.'); - std::string sub = name.substr(0, dot); - - if (LAST_KMSG_FILTER.find(sub) == std::string::npos && - LAST_LOG_FILTER.find(sub) == std::string::npos) { - return __android_log_pmsg_file_write(id, prio, filename, buf, len); - } - - // filename rotation - if (dot == std::string::npos) { - name += ".1"; - } else { - std::string number = name.substr(dot + 1); - if (!isdigit(number[0])) { - name += ".1"; - } else { - size_t i; - if (!android::base::ParseUint(number, &i)) { - LOG(ERROR) << "failed to parse uint in " << number; - return -1; - } - name = sub + "." + std::to_string(i + 1); - } - } - - return __android_log_pmsg_file_write(id, prio, name.c_str(), buf, len); -} - -// Rename last_log -> last_log.1 -> last_log.2 -> ... -> last_log.$max. -// Similarly rename last_kmsg -> last_kmsg.1 -> ... -> last_kmsg.$max. -// Overwrite any existing last_log.$max and last_kmsg.$max. -void rotate_logs(const char* last_log_file, const char* last_kmsg_file) { - // Logs should only be rotated once. - static bool rotated = false; - if (rotated) { - return; - } - rotated = true; - - for (int i = KEEP_LOG_COUNT - 1; i >= 0; --i) { - std::string old_log = android::base::StringPrintf("%s", last_log_file); - if (i > 0) { - old_log += "." + std::to_string(i); - } - std::string new_log = android::base::StringPrintf("%s.%d", last_log_file, i + 1); - // Ignore errors if old_log doesn't exist. - rename(old_log.c_str(), new_log.c_str()); - - std::string old_kmsg = android::base::StringPrintf("%s", last_kmsg_file); - if (i > 0) { - old_kmsg += "." + std::to_string(i); - } - std::string new_kmsg = android::base::StringPrintf("%s.%d", last_kmsg_file, i + 1); - rename(old_kmsg.c_str(), new_kmsg.c_str()); - } -} From 92969c49dce519803ed0a1986781c474b1bbc82f Mon Sep 17 00:00:00 2001 From: Jerry Zhang Date: Tue, 17 Jul 2018 14:20:55 -0700 Subject: [PATCH 04/28] Make recovery libraries shared / recovery_available Test: compiles Bug: 78793464 Change-Id: Iff64bc1a597e70f749a9d825f7d386baa427be3d --- Android.mk | 7 +++++++ bootloader_message/Android.bp | 5 +++-- fuse_sideload/Android.bp | 3 ++- minadbd/Android.bp | 8 +++++--- otautil/Android.bp | 5 +++-- tests/Android.mk | 7 +++++++ 6 files changed, 27 insertions(+), 8 deletions(-) diff --git a/Android.mk b/Android.mk index 906fcd6a..0a9a33a7 100644 --- a/Android.mk +++ b/Android.mk @@ -120,11 +120,18 @@ librecovery_static_libraries := \ libverifier \ libotautil \ $(health_hal_static_libraries) \ + libadbd \ libasyncio \ + libavb_user \ + libdiagnose_usb \ libcrypto_utils \ libcrypto \ libext4_utils \ libfs_mgr \ + libfec \ + libfec_rs \ + libsquashfs_utils \ + liblogwrap \ libpng \ libsparse \ libvintf_recovery \ diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index ab23733c..6155daad 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -14,7 +14,7 @@ // limitations under the License. // -cc_library_static { +cc_library { name: "libbootloader_message", recovery_available: true, srcs: ["bootloader_message.cpp"], @@ -22,9 +22,10 @@ cc_library_static { "-Wall", "-Werror", ], - static_libs: [ + shared_libs: [ "libbase", "libfs_mgr", + "liblog", ], export_include_dirs: ["include"], } diff --git a/fuse_sideload/Android.bp b/fuse_sideload/Android.bp index 76bc16df..29404cea 100644 --- a/fuse_sideload/Android.bp +++ b/fuse_sideload/Android.bp @@ -14,6 +14,7 @@ cc_library_static { name: "libfusesideload", + recovery_available: true, cflags: [ "-D_XOPEN_SOURCE", @@ -30,7 +31,7 @@ cc_library_static { "include", ], - static_libs: [ + shared_libs: [ "libbase", "libcrypto", ], diff --git a/minadbd/Android.bp b/minadbd/Android.bp index 432b2f0f..0ef4af9b 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -28,6 +28,7 @@ cc_defaults { cc_library_static { name: "libminadbd", + recovery_available: true, defaults: [ "minadbd_defaults", @@ -41,12 +42,12 @@ cc_library_static { static_libs: [ "libfusesideload", - "libbase", - "libcrypto", ], - whole_static_libs: [ + shared_libs: [ "libadbd", + "libbase", + "libcrypto", ], } @@ -67,6 +68,7 @@ cc_test { ], shared_libs: [ + "libadbd", "libbase", "libcutils", "liblog", diff --git a/otautil/Android.bp b/otautil/Android.bp index b058f7b3..16af7e78 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -16,6 +16,7 @@ cc_library_static { name: "libotautil", host_supported: true, + recovery_available: true, // Minimal set of files to support host build. srcs: [ @@ -23,7 +24,7 @@ cc_library_static { "rangeset.cpp", ], - static_libs: [ + shared_libs: [ "libbase", ], @@ -46,7 +47,7 @@ cc_library_static { "thermalutil.cpp", ], - static_libs: [ + shared_libs: [ "libselinux", "libcutils", ], diff --git a/tests/Android.mk b/tests/Android.mk index daec11f1..de55587f 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -176,10 +176,17 @@ librecovery_static_libraries := \ libotautil \ $(health_hal_static_libraries) \ libasyncio \ + libadbd \ + libavb_user \ + libdiagnose_usb \ libcrypto_utils \ libcrypto \ libext4_utils \ libfs_mgr \ + libfec \ + libfec_rs \ + libsquashfs_utils \ + liblogwrap \ libpng \ libsparse \ libvintf_recovery \ From df115ad5371726f8c4c0bd92fcd652ef5e85992c Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 30 Oct 2018 19:01:50 -0700 Subject: [PATCH 05/28] Import translations. DO NOT MERGE Change-Id: Icfe0dc21567e74da70cc7b2f1229815bceeac958 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-af/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-am/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ar/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-as/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-az/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-b+sr+Latn/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-be/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-bg/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-bn/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-bs/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ca/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-cs/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-da/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-de/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-el/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-en-rAU/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-en-rCA/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-en-rGB/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-en-rIN/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-en-rXC/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-es-rUS/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-es/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-et/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-eu/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-fa/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-fi/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-fr-rCA/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-fr/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-gl/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-gu/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-hi/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-hr/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-hu/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-hy/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-in/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-is/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-it/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-iw/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ja/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ka/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-kk/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-km/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-kn/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ko/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ky/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-lo/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-lt/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-lv/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-mk/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ml/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-mn/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-mr/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ms/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-my/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-nb/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ne/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-nl/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-or/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-pa/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-pl/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-pt-rBR/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-pt-rPT/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-pt/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ro/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ru/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-si/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-sk/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-sl/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-sq/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-sr/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-sv/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-sw/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ta/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-te/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-th/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-tl/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-tr/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-uk/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-ur/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-uz/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-vi/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-zh-rCN/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-zh-rHK/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-zh-rTW/strings.xml | 10 ++++++++++ tools/recovery_l10n/res/values-zu/strings.xml | 10 ++++++++++ 85 files changed, 850 insertions(+) diff --git a/tools/recovery_l10n/res/values-af/strings.xml b/tools/recovery_l10n/res/values-af/strings.xml index b1974da2..5439d939 100644 --- a/tools/recovery_l10n/res/values-af/strings.xml +++ b/tools/recovery_l10n/res/values-af/strings.xml @@ -6,4 +6,14 @@ "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 75c17fba..8c4c1822 100644 --- a/tools/recovery_l10n/res/values-am/strings.xml +++ b/tools/recovery_l10n/res/values-am/strings.xml @@ -6,4 +6,14 @@ "ምንም ትዕዛዝ የለም" "ስህተት!" "የደህንነት ዝማኔ በመጫን ላይ" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml index 601b5832..b9b2eef7 100644 --- a/tools/recovery_l10n/res/values-ar/strings.xml +++ b/tools/recovery_l10n/res/values-ar/strings.xml @@ -6,4 +6,14 @@ "ليس هناك أي أمر" "خطأ!" "جارٍ تثبيت تحديث الأمان" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-as/strings.xml b/tools/recovery_l10n/res/values-as/strings.xml index 2624cebe..286d95f6 100644 --- a/tools/recovery_l10n/res/values-as/strings.xml +++ b/tools/recovery_l10n/res/values-as/strings.xml @@ -6,4 +6,14 @@ "কোনো আদেশ নাই" "ত্ৰুটি!" "সুৰক্ষা আপডেইট ইনষ্টল কৰি থকা হৈছে" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-az/strings.xml b/tools/recovery_l10n/res/values-az/strings.xml index c6765a9e..a3f8f57b 100644 --- a/tools/recovery_l10n/res/values-az/strings.xml +++ b/tools/recovery_l10n/res/values-az/strings.xml @@ -6,4 +6,14 @@ "Əmr yoxdur" "Xəta!" "Təhlükəsizlik güncəlləməsi yüklənir" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml b/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml index c2d8f223..db612b34 100644 --- a/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml +++ b/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml @@ -6,4 +6,14 @@ "Nema komande" "Greška!" "Instalira se bezbednosno ažuriranje" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-be/strings.xml b/tools/recovery_l10n/res/values-be/strings.xml index 7c0954d3..9514a1be 100644 --- a/tools/recovery_l10n/res/values-be/strings.xml +++ b/tools/recovery_l10n/res/values-be/strings.xml @@ -6,4 +6,14 @@ "Няма каманды" "Памылка" "Усталёўка абнаўлення сістэмы бяспекі" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-bg/strings.xml b/tools/recovery_l10n/res/values-bg/strings.xml index 9e628a2a..e7d790c5 100644 --- a/tools/recovery_l10n/res/values-bg/strings.xml +++ b/tools/recovery_l10n/res/values-bg/strings.xml @@ -6,4 +6,14 @@ "Без команда" "Грешка!" "Актуализацията на сигурносттa се инсталира" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-bn/strings.xml b/tools/recovery_l10n/res/values-bn/strings.xml index 0a481faf..5dba0f63 100644 --- a/tools/recovery_l10n/res/values-bn/strings.xml +++ b/tools/recovery_l10n/res/values-bn/strings.xml @@ -6,4 +6,14 @@ "কোনো আদেশ নেই" "ত্রুটি!" "নিরাপত্তার আপডেট ইনস্টল করা হচ্ছে" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-bs/strings.xml b/tools/recovery_l10n/res/values-bs/strings.xml index 412cf027..d9b397f3 100644 --- a/tools/recovery_l10n/res/values-bs/strings.xml +++ b/tools/recovery_l10n/res/values-bs/strings.xml @@ -6,4 +6,14 @@ "Nema komande" "Greška!" "Instaliranje sigurnosnog ažuriranja…" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ca/strings.xml b/tools/recovery_l10n/res/values-ca/strings.xml index 3f266d2d..43b08d93 100644 --- a/tools/recovery_l10n/res/values-ca/strings.xml +++ b/tools/recovery_l10n/res/values-ca/strings.xml @@ -6,4 +6,14 @@ "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 eb436a81..fcb10c23 100644 --- a/tools/recovery_l10n/res/values-cs/strings.xml +++ b/tools/recovery_l10n/res/values-cs/strings.xml @@ -6,4 +6,14 @@ "Žá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 c6e64a24..4811adea 100644 --- a/tools/recovery_l10n/res/values-da/strings.xml +++ b/tools/recovery_l10n/res/values-da/strings.xml @@ -6,4 +6,14 @@ "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 6b6726a2..c79b88a1 100644 --- a/tools/recovery_l10n/res/values-de/strings.xml +++ b/tools/recovery_l10n/res/values-de/strings.xml @@ -6,4 +6,14 @@ "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 4cb2da5f..c4fe60f3 100644 --- a/tools/recovery_l10n/res/values-el/strings.xml +++ b/tools/recovery_l10n/res/values-el/strings.xml @@ -6,4 +6,14 @@ "Καμία εντολή" "Σφάλμα!" "Εγκατάσταση ενημέρωσης ασφαλείας" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-en-rAU/strings.xml b/tools/recovery_l10n/res/values-en-rAU/strings.xml index dc75c237..25d5e303 100644 --- a/tools/recovery_l10n/res/values-en-rAU/strings.xml +++ b/tools/recovery_l10n/res/values-en-rAU/strings.xml @@ -6,4 +6,14 @@ "No command" "Error!" "Installing security update" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-en-rCA/strings.xml b/tools/recovery_l10n/res/values-en-rCA/strings.xml index dc75c237..25d5e303 100644 --- a/tools/recovery_l10n/res/values-en-rCA/strings.xml +++ b/tools/recovery_l10n/res/values-en-rCA/strings.xml @@ -6,4 +6,14 @@ "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 dc75c237..25d5e303 100644 --- a/tools/recovery_l10n/res/values-en-rGB/strings.xml +++ b/tools/recovery_l10n/res/values-en-rGB/strings.xml @@ -6,4 +6,14 @@ "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 dc75c237..25d5e303 100644 --- a/tools/recovery_l10n/res/values-en-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-en-rIN/strings.xml @@ -6,4 +6,14 @@ "No command" "Error!" "Installing security update" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml index 2d528b3f..9419a0a0 100644 --- a/tools/recovery_l10n/res/values-en-rXC/strings.xml +++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml @@ -6,4 +6,14 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎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 06b86069..5451372a 100644 --- a/tools/recovery_l10n/res/values-es-rUS/strings.xml +++ b/tools/recovery_l10n/res/values-es-rUS/strings.xml @@ -6,4 +6,14 @@ "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 d8618f2f..e0496b2f 100644 --- a/tools/recovery_l10n/res/values-es/strings.xml +++ b/tools/recovery_l10n/res/values-es/strings.xml @@ -6,4 +6,14 @@ "Sin comandos" "Error" "Instalando actualización de seguridad" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-et/strings.xml b/tools/recovery_l10n/res/values-et/strings.xml index 072a9ef8..6346e072 100644 --- a/tools/recovery_l10n/res/values-et/strings.xml +++ b/tools/recovery_l10n/res/values-et/strings.xml @@ -6,4 +6,14 @@ "Käsk puudub" "Viga!" "Turvavärskenduse installimine" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-eu/strings.xml b/tools/recovery_l10n/res/values-eu/strings.xml index 5540469d..033098b3 100644 --- a/tools/recovery_l10n/res/values-eu/strings.xml +++ b/tools/recovery_l10n/res/values-eu/strings.xml @@ -6,4 +6,14 @@ "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 cc390ae8..de9a2409 100644 --- a/tools/recovery_l10n/res/values-fa/strings.xml +++ b/tools/recovery_l10n/res/values-fa/strings.xml @@ -6,4 +6,14 @@ "فرمانی وجود ندارد" "خطا!" "در حال نصب به‌روزرسانی امنیتی" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-fi/strings.xml b/tools/recovery_l10n/res/values-fi/strings.xml index 5141642c..8d8cb914 100644 --- a/tools/recovery_l10n/res/values-fi/strings.xml +++ b/tools/recovery_l10n/res/values-fi/strings.xml @@ -6,4 +6,14 @@ "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 b2415290..19783084 100644 --- a/tools/recovery_l10n/res/values-fr-rCA/strings.xml +++ b/tools/recovery_l10n/res/values-fr-rCA/strings.xml @@ -6,4 +6,14 @@ "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 f0472b5a..599233d0 100644 --- a/tools/recovery_l10n/res/values-fr/strings.xml +++ b/tools/recovery_l10n/res/values-fr/strings.xml @@ -6,4 +6,14 @@ "Aucune commande" "Erreur !" "Installation de la mise à jour de sécurité…" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml index 42b2016c..8c652d62 100644 --- a/tools/recovery_l10n/res/values-gl/strings.xml +++ b/tools/recovery_l10n/res/values-gl/strings.xml @@ -6,4 +6,14 @@ "Non hai ningún comando" "Erro" "Instalando actualización de seguranza" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-gu/strings.xml b/tools/recovery_l10n/res/values-gu/strings.xml index 2355a0f4..84b4f451 100644 --- a/tools/recovery_l10n/res/values-gu/strings.xml +++ b/tools/recovery_l10n/res/values-gu/strings.xml @@ -6,4 +6,14 @@ "કોઈ આદેશ નથી" "ભૂલ!" "સુરક્ષા અપડેટ ઇન્સ્ટૉલ કરી રહ્યાં છે" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-hi/strings.xml b/tools/recovery_l10n/res/values-hi/strings.xml index 65d00335..50248b59 100644 --- a/tools/recovery_l10n/res/values-hi/strings.xml +++ b/tools/recovery_l10n/res/values-hi/strings.xml @@ -6,4 +6,14 @@ "कोई निर्देश नहीं मिला" "गड़बड़ी!" "सुरक्षा अपडेट इंस्टॉल किया जा रहा है" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-hr/strings.xml b/tools/recovery_l10n/res/values-hr/strings.xml index 3b75ff11..001e3349 100644 --- a/tools/recovery_l10n/res/values-hr/strings.xml +++ b/tools/recovery_l10n/res/values-hr/strings.xml @@ -6,4 +6,14 @@ "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 12d4d9fe..5a6d6018 100644 --- a/tools/recovery_l10n/res/values-hu/strings.xml +++ b/tools/recovery_l10n/res/values-hu/strings.xml @@ -6,4 +6,14 @@ "Nincs parancs" "Hiba!" "Biztonsági frissítés telepítése" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-hy/strings.xml b/tools/recovery_l10n/res/values-hy/strings.xml index 9d62bb76..a652e339 100644 --- a/tools/recovery_l10n/res/values-hy/strings.xml +++ b/tools/recovery_l10n/res/values-hy/strings.xml @@ -6,4 +6,14 @@ "Հրամանը տրված չէ" "Սխալ" "Անվտանգության թարմացման տեղադրում" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-in/strings.xml b/tools/recovery_l10n/res/values-in/strings.xml index 0e56e0dd..b34e3941 100644 --- a/tools/recovery_l10n/res/values-in/strings.xml +++ b/tools/recovery_l10n/res/values-in/strings.xml @@ -6,4 +6,14 @@ "Tidak ada perintah" "Error!" "Memasang pembaruan keamanan" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-is/strings.xml b/tools/recovery_l10n/res/values-is/strings.xml index 5065b652..d1e790a2 100644 --- a/tools/recovery_l10n/res/values-is/strings.xml +++ b/tools/recovery_l10n/res/values-is/strings.xml @@ -6,4 +6,14 @@ "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 2c0364e6..12c62752 100644 --- a/tools/recovery_l10n/res/values-it/strings.xml +++ b/tools/recovery_l10n/res/values-it/strings.xml @@ -6,4 +6,14 @@ "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 ea5e6f2c..42d931b5 100644 --- a/tools/recovery_l10n/res/values-iw/strings.xml +++ b/tools/recovery_l10n/res/values-iw/strings.xml @@ -6,4 +6,14 @@ "אין פקודה" "שגיאה!" "מתקין עדכון אבטחה" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml index 36e029b0..4e832117 100644 --- a/tools/recovery_l10n/res/values-ja/strings.xml +++ b/tools/recovery_l10n/res/values-ja/strings.xml @@ -6,4 +6,14 @@ "コマンドが指定されていません" "エラーが発生しました。" "セキュリティ アップデートをインストールしています" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ka/strings.xml b/tools/recovery_l10n/res/values-ka/strings.xml index 6a46b367..0c6f49a7 100644 --- a/tools/recovery_l10n/res/values-ka/strings.xml +++ b/tools/recovery_l10n/res/values-ka/strings.xml @@ -6,4 +6,14 @@ "ბრძანება არ არის" "წარმოიქმნა შეცდომა!" "მიმდინარეობს უსაფრთხოების განახლების ინსტალაცია" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-kk/strings.xml b/tools/recovery_l10n/res/values-kk/strings.xml index a4bd86e6..787bda07 100644 --- a/tools/recovery_l10n/res/values-kk/strings.xml +++ b/tools/recovery_l10n/res/values-kk/strings.xml @@ -6,4 +6,14 @@ "Пәрмен жоқ" "Қате!" "Қауіпсіздік жаңартуы орнатылуда" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-km/strings.xml b/tools/recovery_l10n/res/values-km/strings.xml index 313c0f45..355bffd2 100644 --- a/tools/recovery_l10n/res/values-km/strings.xml +++ b/tools/recovery_l10n/res/values-km/strings.xml @@ -6,4 +6,14 @@ "គ្មានពាក្យបញ្ជាទេ" "កំហុស!" "កំពុងដំឡើងការអាប់ដេតសុវត្ថិភាព" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-kn/strings.xml b/tools/recovery_l10n/res/values-kn/strings.xml index 5bf6260e..ef6b0801 100644 --- a/tools/recovery_l10n/res/values-kn/strings.xml +++ b/tools/recovery_l10n/res/values-kn/strings.xml @@ -6,4 +6,14 @@ "ಯಾವುದೇ ಆದೇಶವಿಲ್ಲ" "ದೋಷ!" "ಭದ್ರತೆಯ ಅಪ್‌ಡೇಟ್‌ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ko/strings.xml b/tools/recovery_l10n/res/values-ko/strings.xml index aca13bbe..3f83b205 100644 --- a/tools/recovery_l10n/res/values-ko/strings.xml +++ b/tools/recovery_l10n/res/values-ko/strings.xml @@ -6,4 +6,14 @@ "명령어 없음" "오류!" "보안 업데이트 설치 중" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ky/strings.xml b/tools/recovery_l10n/res/values-ky/strings.xml index 0a6bd783..3832f33e 100644 --- a/tools/recovery_l10n/res/values-ky/strings.xml +++ b/tools/recovery_l10n/res/values-ky/strings.xml @@ -6,4 +6,14 @@ "Буйрук берилген жок" "Ката!" "Коопсуздук жаңыртуусу орнотулууда" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-lo/strings.xml b/tools/recovery_l10n/res/values-lo/strings.xml index d3dbb397..f5224ff3 100644 --- a/tools/recovery_l10n/res/values-lo/strings.xml +++ b/tools/recovery_l10n/res/values-lo/strings.xml @@ -6,4 +6,14 @@ "ບໍ່ມີຄຳສັ່ງ" "ຜິດພາດ!" "ກຳລັງຕິດຕັ້ງອັບເດດຄວາມປອດໄພ" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-lt/strings.xml b/tools/recovery_l10n/res/values-lt/strings.xml index d5d5e88f..1ec3586b 100644 --- a/tools/recovery_l10n/res/values-lt/strings.xml +++ b/tools/recovery_l10n/res/values-lt/strings.xml @@ -6,4 +6,14 @@ "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 d877f6a6..83b05940 100644 --- a/tools/recovery_l10n/res/values-lv/strings.xml +++ b/tools/recovery_l10n/res/values-lv/strings.xml @@ -6,4 +6,14 @@ "Nav nevienas komandas" "Kļūda!" "Notiek drošības atjauninājuma instalēšana" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-mk/strings.xml b/tools/recovery_l10n/res/values-mk/strings.xml index 35145973..39b4fa42 100644 --- a/tools/recovery_l10n/res/values-mk/strings.xml +++ b/tools/recovery_l10n/res/values-mk/strings.xml @@ -6,4 +6,14 @@ "Нема наредба" "Грешка!" "Се инсталира безбедносно ажурирање" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ml/strings.xml b/tools/recovery_l10n/res/values-ml/strings.xml index b506e253..752a3ed1 100644 --- a/tools/recovery_l10n/res/values-ml/strings.xml +++ b/tools/recovery_l10n/res/values-ml/strings.xml @@ -6,4 +6,14 @@ "കമാൻഡ് ഒന്നുമില്ല" "പിശക്!" "സുരക്ഷാ അപ്ഡേറ്റ് ഇൻസ്റ്റാൾ ചെയ്യുന്നു" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-mn/strings.xml b/tools/recovery_l10n/res/values-mn/strings.xml index e3dd2e90..df61476c 100644 --- a/tools/recovery_l10n/res/values-mn/strings.xml +++ b/tools/recovery_l10n/res/values-mn/strings.xml @@ -6,4 +6,14 @@ "Тушаал байхгүй" "Алдаа!" "Аюулгүй байдлын шинэчлэлтийг суулгаж байна" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-mr/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml index 5f820336..f96ddea9 100644 --- a/tools/recovery_l10n/res/values-mr/strings.xml +++ b/tools/recovery_l10n/res/values-mr/strings.xml @@ -6,4 +6,14 @@ "कोणतीही कमांड नाही" "एरर!" "सुरक्षा अपडेट इंस्टॉल करत आहे" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ms/strings.xml b/tools/recovery_l10n/res/values-ms/strings.xml index 0e24ac4e..7a3480ee 100644 --- a/tools/recovery_l10n/res/values-ms/strings.xml +++ b/tools/recovery_l10n/res/values-ms/strings.xml @@ -6,4 +6,14 @@ "Tiada perintah" "Ralat!" "Memasang kemas kini keselamatan" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-my/strings.xml b/tools/recovery_l10n/res/values-my/strings.xml index f1375246..1dc11e00 100644 --- a/tools/recovery_l10n/res/values-my/strings.xml +++ b/tools/recovery_l10n/res/values-my/strings.xml @@ -6,4 +6,14 @@ "ညွှန်ကြားချက်မပေးထားပါ" "မှားနေပါသည်!" "လုံခြုံရေး အပ်ဒိတ်ကို ထည့်သွင်းနေသည်" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-nb/strings.xml b/tools/recovery_l10n/res/values-nb/strings.xml index ad6f20e4..f7d23a6e 100644 --- a/tools/recovery_l10n/res/values-nb/strings.xml +++ b/tools/recovery_l10n/res/values-nb/strings.xml @@ -6,4 +6,14 @@ "Ingen kommandoer" "Feil!" "Installerer sikkerhetsoppdateringen" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ne/strings.xml b/tools/recovery_l10n/res/values-ne/strings.xml index 1880e807..04477674 100644 --- a/tools/recovery_l10n/res/values-ne/strings.xml +++ b/tools/recovery_l10n/res/values-ne/strings.xml @@ -6,4 +6,14 @@ "कुनै आदेश छैन" "त्रुटि!" "सुरक्षा सम्बन्धी अद्यावधिकलाई स्थापना गर्दै" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-nl/strings.xml b/tools/recovery_l10n/res/values-nl/strings.xml index 0d6c15ab..3be53453 100644 --- a/tools/recovery_l10n/res/values-nl/strings.xml +++ b/tools/recovery_l10n/res/values-nl/strings.xml @@ -6,4 +6,14 @@ "Geen opdracht" "Fout!" "Beveiligingsupdate installeren" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-or/strings.xml b/tools/recovery_l10n/res/values-or/strings.xml index 2b0851cd..749ad82f 100644 --- a/tools/recovery_l10n/res/values-or/strings.xml +++ b/tools/recovery_l10n/res/values-or/strings.xml @@ -6,4 +6,14 @@ "କୌଣସି କମାଣ୍ଡ ନାହିଁ" "ତ୍ରୁଟି!" "ସୁରକ୍ଷା ଅପ୍‌ଡେଟ୍‌ ଇନ୍‌ଷ୍ଟଲ୍‌ କରୁଛି" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-pa/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml index 27972d11..0475a5e4 100644 --- a/tools/recovery_l10n/res/values-pa/strings.xml +++ b/tools/recovery_l10n/res/values-pa/strings.xml @@ -6,4 +6,14 @@ "ਕੋਈ ਆਦੇਸ਼ ਨਹੀਂ" "ਅਸ਼ੁੱਧੀ!" "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-pl/strings.xml b/tools/recovery_l10n/res/values-pl/strings.xml index 8d6db388..daea5bd4 100644 --- a/tools/recovery_l10n/res/values-pl/strings.xml +++ b/tools/recovery_l10n/res/values-pl/strings.xml @@ -6,4 +6,14 @@ "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 b7270438..3d3227cb 100644 --- a/tools/recovery_l10n/res/values-pt-rBR/strings.xml +++ b/tools/recovery_l10n/res/values-pt-rBR/strings.xml @@ -6,4 +6,14 @@ "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 98146373..ae393887 100644 --- a/tools/recovery_l10n/res/values-pt-rPT/strings.xml +++ b/tools/recovery_l10n/res/values-pt-rPT/strings.xml @@ -6,4 +6,14 @@ "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 b7270438..3d3227cb 100644 --- a/tools/recovery_l10n/res/values-pt/strings.xml +++ b/tools/recovery_l10n/res/values-pt/strings.xml @@ -6,4 +6,14 @@ "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 8032865b..4d90718e 100644 --- a/tools/recovery_l10n/res/values-ro/strings.xml +++ b/tools/recovery_l10n/res/values-ro/strings.xml @@ -6,4 +6,14 @@ "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 feebecf3..ab7520ae 100644 --- a/tools/recovery_l10n/res/values-ru/strings.xml +++ b/tools/recovery_l10n/res/values-ru/strings.xml @@ -6,4 +6,14 @@ "Команды нет" "Ошибка" "Установка обновления системы безопасности…" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-si/strings.xml b/tools/recovery_l10n/res/values-si/strings.xml index 456cdc56..cffb6e9b 100644 --- a/tools/recovery_l10n/res/values-si/strings.xml +++ b/tools/recovery_l10n/res/values-si/strings.xml @@ -6,4 +6,14 @@ "විධානයක් නොමැත" "දෝෂය!" "ආරක්ෂක යාවත්කාලීනය ස්ථාපනය කරමින්" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-sk/strings.xml b/tools/recovery_l10n/res/values-sk/strings.xml index b15f3802..59985c0e 100644 --- a/tools/recovery_l10n/res/values-sk/strings.xml +++ b/tools/recovery_l10n/res/values-sk/strings.xml @@ -6,4 +6,14 @@ "Ž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 d608b750..e36dfb45 100644 --- a/tools/recovery_l10n/res/values-sl/strings.xml +++ b/tools/recovery_l10n/res/values-sl/strings.xml @@ -6,4 +6,14 @@ "Ni ukaza" "Napaka" "Nameščanje varnostne posodobitve" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-sq/strings.xml b/tools/recovery_l10n/res/values-sq/strings.xml index 1156931f..58945c0e 100644 --- a/tools/recovery_l10n/res/values-sq/strings.xml +++ b/tools/recovery_l10n/res/values-sq/strings.xml @@ -6,4 +6,14 @@ "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 a593d8fa..275a3aaf 100644 --- a/tools/recovery_l10n/res/values-sr/strings.xml +++ b/tools/recovery_l10n/res/values-sr/strings.xml @@ -6,4 +6,14 @@ "Нема команде" "Грешка!" "Инсталира се безбедносно ажурирање" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-sv/strings.xml b/tools/recovery_l10n/res/values-sv/strings.xml index b33ce253..512b5d4b 100644 --- a/tools/recovery_l10n/res/values-sv/strings.xml +++ b/tools/recovery_l10n/res/values-sv/strings.xml @@ -6,4 +6,14 @@ "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 15676588..b0a072f9 100644 --- a/tools/recovery_l10n/res/values-sw/strings.xml +++ b/tools/recovery_l10n/res/values-sw/strings.xml @@ -6,4 +6,14 @@ "Hakuna amri" "Hitilafu fulani imetokea!" "Inasakinisha sasisho la usalama" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ta/strings.xml b/tools/recovery_l10n/res/values-ta/strings.xml index d49186d8..ff2e3e79 100644 --- a/tools/recovery_l10n/res/values-ta/strings.xml +++ b/tools/recovery_l10n/res/values-ta/strings.xml @@ -6,4 +6,14 @@ "கட்டளை இல்லை" "பிழை!" "பாதுகாப்புப் புதுப்பிப்பை நிறுவுகிறது" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml index e35c82bc..66039ac9 100644 --- a/tools/recovery_l10n/res/values-te/strings.xml +++ b/tools/recovery_l10n/res/values-te/strings.xml @@ -6,4 +6,14 @@ "ఆదేశం లేదు" "ఎర్రర్ సంభవించింది!" "భద్రతా నవీకరణను ఇన్‌స్టాల్ చేస్తోంది" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-th/strings.xml b/tools/recovery_l10n/res/values-th/strings.xml index 155affea..3b315816 100644 --- a/tools/recovery_l10n/res/values-th/strings.xml +++ b/tools/recovery_l10n/res/values-th/strings.xml @@ -6,4 +6,14 @@ "ไม่มีคำสั่ง" "ข้อผิดพลาด!" "กำลังติดตั้งการอัปเดตความปลอดภัย" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-tl/strings.xml b/tools/recovery_l10n/res/values-tl/strings.xml index 555b42b8..b414fd94 100644 --- a/tools/recovery_l10n/res/values-tl/strings.xml +++ b/tools/recovery_l10n/res/values-tl/strings.xml @@ -6,4 +6,14 @@ "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 5387cb2a..4e948b70 100644 --- a/tools/recovery_l10n/res/values-tr/strings.xml +++ b/tools/recovery_l10n/res/values-tr/strings.xml @@ -6,4 +6,14 @@ "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 0c2fa164..19656207 100644 --- a/tools/recovery_l10n/res/values-uk/strings.xml +++ b/tools/recovery_l10n/res/values-uk/strings.xml @@ -6,4 +6,14 @@ "Немає команди" "Помилка!" "Установлюється оновлення системи безпеки" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-ur/strings.xml b/tools/recovery_l10n/res/values-ur/strings.xml index 12e32fbc..9ace0f08 100644 --- a/tools/recovery_l10n/res/values-ur/strings.xml +++ b/tools/recovery_l10n/res/values-ur/strings.xml @@ -6,4 +6,14 @@ "کوئی کمانڈ نہیں ہے" "خرابی!" "سیکیورٹی اپ ڈیٹ انسٹال ہو رہی ہے" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-uz/strings.xml b/tools/recovery_l10n/res/values-uz/strings.xml index 2c309d64..676be7a6 100644 --- a/tools/recovery_l10n/res/values-uz/strings.xml +++ b/tools/recovery_l10n/res/values-uz/strings.xml @@ -6,4 +6,14 @@ "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 c77d0c8c..fce6cd90 100644 --- a/tools/recovery_l10n/res/values-vi/strings.xml +++ b/tools/recovery_l10n/res/values-vi/strings.xml @@ -6,4 +6,14 @@ "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 e0614979..66c876d7 100644 --- a/tools/recovery_l10n/res/values-zh-rCN/strings.xml +++ b/tools/recovery_l10n/res/values-zh-rCN/strings.xml @@ -6,4 +6,14 @@ "无命令" "出错了!" "正在安装安全更新" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-zh-rHK/strings.xml b/tools/recovery_l10n/res/values-zh-rHK/strings.xml index ec3315d3..5dc38d7a 100644 --- a/tools/recovery_l10n/res/values-zh-rHK/strings.xml +++ b/tools/recovery_l10n/res/values-zh-rHK/strings.xml @@ -6,4 +6,14 @@ "沒有指令" "錯誤!" "正在安裝安全性更新" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-zh-rTW/strings.xml b/tools/recovery_l10n/res/values-zh-rTW/strings.xml index 78eae242..e94038b2 100644 --- a/tools/recovery_l10n/res/values-zh-rTW/strings.xml +++ b/tools/recovery_l10n/res/values-zh-rTW/strings.xml @@ -6,4 +6,14 @@ "沒有指令" "錯誤!" "正在安裝安全性更新" + + + + + + + + + + diff --git a/tools/recovery_l10n/res/values-zu/strings.xml b/tools/recovery_l10n/res/values-zu/strings.xml index 6b815e1a..eef2293d 100644 --- a/tools/recovery_l10n/res/values-zu/strings.xml +++ b/tools/recovery_l10n/res/values-zu/strings.xml @@ -6,4 +6,14 @@ "Awukho umyalo" "Iphutha!" "Ifaka isibuyekezo sokuphepha" + + + + + + + + + + From f560a89470c83936f3187d96e4bbe8b21eecc8e3 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 13 Nov 2018 08:09:37 -0800 Subject: [PATCH 06/28] Import translations. DO NOT MERGE Change-Id: I1bd6d7f12c7bfffb7e022f09347a5be5a96766a3 Auto-generated-cl: translation import --- tools/recovery_l10n/res/values-af/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-am/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ar/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-as/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-az/strings.xml | 15 +++++---------- .../res/values-b+sr+Latn/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-be/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-bg/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-bn/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-bs/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ca/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-cs/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-da/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-de/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-el/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-en-rAU/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-en-rCA/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-en-rGB/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-en-rIN/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-en-rXC/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-es-rUS/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-es/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-et/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-eu/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-fa/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-fi/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-fr-rCA/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-fr/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-gl/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-gu/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-hi/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-hr/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-hu/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-hy/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-in/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-is/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-it/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-iw/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ja/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ka/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-kk/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-km/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-kn/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ko/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ky/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-lo/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-lt/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-lv/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-mk/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ml/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-mn/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-mr/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ms/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-my/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-nb/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ne/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-nl/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-or/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-pa/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-pl/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-pt-rBR/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-pt-rPT/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-pt/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ro/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ru/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-si/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-sk/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-sl/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-sq/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-sr/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-sv/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-sw/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ta/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-te/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-th/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-tl/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-tr/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-uk/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-ur/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-uz/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-vi/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-zh-rCN/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-zh-rHK/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-zh-rTW/strings.xml | 15 +++++---------- tools/recovery_l10n/res/values-zu/strings.xml | 15 +++++---------- 85 files changed, 425 insertions(+), 850 deletions(-) diff --git a/tools/recovery_l10n/res/values-af/strings.xml b/tools/recovery_l10n/res/values-af/strings.xml index 5439d939..85a3c903 100644 --- a/tools/recovery_l10n/res/values-af/strings.xml +++ b/tools/recovery_l10n/res/values-af/strings.xml @@ -6,14 +6,9 @@ "Geen opdrag nie" "Fout!" "Installeer tans sekuriteitopdatering" - - - - - - - - - - + "Kan nie Android-stelsel laai nie. Jou data is dalk korrup. As jy aanhou om hierdie boodskap te kry, sal jy dalk \'n fabrieksterugstelling moet doen en alle gebruikerdata moet uitvee wat op hierdie toestel geberg word." + "Probeer weer" + "Fabrieksterugstelling" + "Vee alle gebruikerdata uit?\n\n DIT KAN NIE ONTDOEN WORD NIE!" + "Kanselleer" diff --git a/tools/recovery_l10n/res/values-am/strings.xml b/tools/recovery_l10n/res/values-am/strings.xml index 8c4c1822..353f2233 100644 --- a/tools/recovery_l10n/res/values-am/strings.xml +++ b/tools/recovery_l10n/res/values-am/strings.xml @@ -6,14 +6,9 @@ "ምንም ትዕዛዝ የለም" "ስህተት!" "የደህንነት ዝማኔ በመጫን ላይ" - - - - - - - - - - + "የAndroid ስርዓትን መጫን አልተቻለም። የእርስዎ ውሂብ የተበላሸ ሊሆን ይችላል። ይህን መልዕክት ማግኘቱን ከቀጠሉ የፋብሪካ ውሂብ ዳግም ማስጀመር ማከናወንና በዚህ መሣሪያ ላይ የተከማቸ ሁሉንም የተጠቃሚ ውሂብ መሰረዝ ሊኖርብዎት ይችላል።" + "እንደገና ሞክር" + "የፋብሪካ ውሂብ ዳግም ማስጀመር" + "ሁሉም የተጠቃሚ ውሂብ ይሰረዝ?\n\n ይህ ሊቀለበስ አይችልም!" + "ይቅር" diff --git a/tools/recovery_l10n/res/values-ar/strings.xml b/tools/recovery_l10n/res/values-ar/strings.xml index b9b2eef7..2af36d64 100644 --- a/tools/recovery_l10n/res/values-ar/strings.xml +++ b/tools/recovery_l10n/res/values-ar/strings.xml @@ -6,14 +6,9 @@ "ليس هناك أي أمر" "خطأ!" "جارٍ تثبيت تحديث الأمان" - - - - - - - - - - + "‏يتعذَّر تحميل نظام Android، حيث قد تكون بياناتك تالفة. وإذا استمر ظهور هذه الرسالة، قد يتعيَّن عليك إجراء إعادة الضبط بحسب بيانات المصنع ومحو جميع بيانات المستخدم المُخزَّنة على هذا الجهاز." + "إعادة المحاولة" + "إعادة الضبط بحسب بيانات المصنع" + "هل تريد حجب كل بيانات المستخدم؟\n\n لا يمكن التراجع عن هذا الإجراء." + "إلغاء" diff --git a/tools/recovery_l10n/res/values-as/strings.xml b/tools/recovery_l10n/res/values-as/strings.xml index 286d95f6..33a204d0 100644 --- a/tools/recovery_l10n/res/values-as/strings.xml +++ b/tools/recovery_l10n/res/values-as/strings.xml @@ -6,14 +6,9 @@ "কোনো আদেশ নাই" "ত্ৰুটি!" "সুৰক্ষা আপডেইট ইনষ্টল কৰি থকা হৈছে" - - - - - - - - - - + "Android ছিষ্টেম ল\'ড কৰিব নোৱাৰি। আপোনাৰ ডেটাত কিবা আসোঁৱাহ থকা যেন লাগিছে। আপুনি যদি এই বাৰ্তাটো পায়েই থাকে, আপুনি নিজৰ ডিভাইচটো ফেক্টৰী ডেটা ৰিছেট কৰি সেইটোত থকা ব্যৱহাৰকাৰীৰ সকলো ডেটা মচিব লগা হ\'ব পাৰে।" + "আকৌ চেষ্টা কৰক" + "ফেক্টৰী ডেটা ৰিছেট" + "ব্যৱহাৰকাৰীৰ সকলো ডেটা মচিবনে?\n\n এইটো কৰাৰ পিছত আনডু কৰিব নোৱাৰি!" + "বাতিল কৰক" diff --git a/tools/recovery_l10n/res/values-az/strings.xml b/tools/recovery_l10n/res/values-az/strings.xml index a3f8f57b..35194c4b 100644 --- a/tools/recovery_l10n/res/values-az/strings.xml +++ b/tools/recovery_l10n/res/values-az/strings.xml @@ -6,14 +6,9 @@ "Əmr yoxdur" "Xəta!" "Təhlükəsizlik güncəlləməsi yüklənir" - - - - - - - - - - + "Android sistemi yüklənmir. Datanız zədələnə bilər. Bu mesajı yenə qəbul etsəniz, data zavod sıfırlamasını həyata keçirməli və bu cihazda saxlanmış istifadəçi datasının hamısını silməlisiniz." + "Yenidən cəhd edin" + "Data zavod sıfırlaması" + "Bütün istifadəçi datası silinsin?\n\n BU ƏMƏLİYYATI GERİ QAYTARMAQ MÜMKÜN DEYİL!" + "Ləğv edin" diff --git a/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml b/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml index db612b34..19c6f419 100644 --- a/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml +++ b/tools/recovery_l10n/res/values-b+sr+Latn/strings.xml @@ -6,14 +6,9 @@ "Nema komande" "Greška!" "Instalira se bezbednosno ažuriranje" - - - - - - - - - - + "Učitavanje Android sistema nije uspelo. Podaci su možda oštećeni. Ako nastavite da dobijate ovu poruku, možda ćete morati da resetujete uređaj na fabrička podešavanja i obrišete sve podatke korisnika koje čuvate na njemu." + "Probaj ponovo" + "Resetovanje na fabrička podešavanja" + "Želite li da izbrišete sve podatke korisnika?\n\n OVO NE MOŽE DA SE OPOZOVE!" + "Otkaži" diff --git a/tools/recovery_l10n/res/values-be/strings.xml b/tools/recovery_l10n/res/values-be/strings.xml index 9514a1be..ad14fbe2 100644 --- a/tools/recovery_l10n/res/values-be/strings.xml +++ b/tools/recovery_l10n/res/values-be/strings.xml @@ -6,14 +6,9 @@ "Няма каманды" "Памылка" "Усталёўка абнаўлення сістэмы бяспекі" - - - - - - - - - - + "Не ўдалося загрузіць сістэму Android. Магчыма, вашы даныя пашкоджаны. Калі вы зноў убачыце гэта паведамленне, скіньце налады прылады да заводскіх значэнняў і сатрыце ўсе карыстальніцкія даныя, якія на ёй захоўваюцца." + "Паўтарыць спробу" + "Скінуць да заводскіх налад" + "Ачысціць усе карыстальніцкія даныя?\n\n ГЭТА ДЗЕЯННЕ НЕЛЬГА АДРАБІЦЬ!" + "Скасаваць" diff --git a/tools/recovery_l10n/res/values-bg/strings.xml b/tools/recovery_l10n/res/values-bg/strings.xml index e7d790c5..e96ff446 100644 --- a/tools/recovery_l10n/res/values-bg/strings.xml +++ b/tools/recovery_l10n/res/values-bg/strings.xml @@ -6,14 +6,9 @@ "Без команда" "Грешка!" "Актуализацията на сигурносттa се инсталира" - - - - - - - - - - + "Системата Android не може да се зареди. Данните ви може да са повредени. Ако продължите да получавате това съобщение, може да е необходимо да възстановите фабричните настройки и да изтриете всички потребителски данни, съхранени на това устройство." + "Нов опит" + "Възстановяване на фабричните настройки" + "Да се изчистят ли всички потребителски данни?\n\n ТОВА ДЕЙСТВИЕ НЕ МОЖЕ ДА БЪДЕ ОТМЕНЕНО!" + "Отказ" diff --git a/tools/recovery_l10n/res/values-bn/strings.xml b/tools/recovery_l10n/res/values-bn/strings.xml index 5dba0f63..5967bc4b 100644 --- a/tools/recovery_l10n/res/values-bn/strings.xml +++ b/tools/recovery_l10n/res/values-bn/strings.xml @@ -6,14 +6,9 @@ "কোনো আদেশ নেই" "ত্রুটি!" "নিরাপত্তার আপডেট ইনস্টল করা হচ্ছে" - - - - - - - - - - + "Android সিস্টেম লোড করা যায়নি। আপনার ডেটা হয়ত নষ্ট হয়ে গেছে। যদি এই মেসেজটি আসতেই থাকে তাহলে হয়ত ফ্যাক্টরি ডেটা রিসেট করে এই ডিভাইসে থাকা ব্যবহারকারীর সব ডেটা মুছে ফেলতে হবে।" + "আবার চেষ্টা করুন" + "ফ্যাক্টরি ডেটা রিসেট করুন" + "ব্যবহারকারীর সব ডেটা মুছে দিতে চান?\n\n এই ডেটা আর ফিরে পাওয়া যাবে না!" + "বাতিল করুন" diff --git a/tools/recovery_l10n/res/values-bs/strings.xml b/tools/recovery_l10n/res/values-bs/strings.xml index d9b397f3..38f197f2 100644 --- a/tools/recovery_l10n/res/values-bs/strings.xml +++ b/tools/recovery_l10n/res/values-bs/strings.xml @@ -6,14 +6,9 @@ "Nema komande" "Greška!" "Instaliranje sigurnosnog ažuriranja…" - - - - - - - - - - + "Nije moguće učitati Android sistem. Podaci su možda oštećeni. Ako opet primite ovu poruku, možda ćete morati vratiti uređaj na fabričke postavke i izbrisati sve podatke korisnika pohranjene na ovom uređaju." + "Pokušaj ponovo" + "Vraćanje na fabričke postavke" + "Izbrisati sve podatke korisnika?\n\n TA RADNJA SE NE MOŽE PONIŠTITI!" + "Otkaži" diff --git a/tools/recovery_l10n/res/values-ca/strings.xml b/tools/recovery_l10n/res/values-ca/strings.xml index 43b08d93..6b7bec07 100644 --- a/tools/recovery_l10n/res/values-ca/strings.xml +++ b/tools/recovery_l10n/res/values-ca/strings.xml @@ -6,14 +6,9 @@ "No hi ha cap ordre" "S\'ha produït un error" "S\'està instal·lant una actualització de seguretat" - - - - - - - - - - + "No s\'ha pogut carregar el sistema Android. És possible que les teves dades estiguin malmeses. Si continues veient aquest missatge, pot ser que hagis de restablir les dades de fàbrica i esborrar totes les dades d\'usuari emmagatzemades en aquest dispositiu." + "Torna-ho a provar" + "Restableix les dades de fàbrica" + "Vols eliminar totes les dades d\'usuari?\n\n AQUESTA ACCIÓ NO ES POT DESFER." + "Cancel·la" diff --git a/tools/recovery_l10n/res/values-cs/strings.xml b/tools/recovery_l10n/res/values-cs/strings.xml index fcb10c23..c42dab2c 100644 --- a/tools/recovery_l10n/res/values-cs/strings.xml +++ b/tools/recovery_l10n/res/values-cs/strings.xml @@ -6,14 +6,9 @@ "Žádný příkaz" "Chyba!" "Instalace aktualizace zabezpečení" - - - - - - - - - - + "Systém Android se nepodařilo načíst. Vaše data jsou možná poškozena. Pokud se tato zpráva bude zobrazovat i nadále, bude nutné vymazat všechna uživatelská data v zařízení a obnovit tovární data." + "Zkusit znovu" + "Obnovení továrních dat" + "Vymazat všechna uživatelská data?\n\nTUTO AKCI NELZE VRÁTIT ZPĚT!" + "Zrušit" diff --git a/tools/recovery_l10n/res/values-da/strings.xml b/tools/recovery_l10n/res/values-da/strings.xml index 4811adea..814c0df0 100644 --- a/tools/recovery_l10n/res/values-da/strings.xml +++ b/tools/recovery_l10n/res/values-da/strings.xml @@ -6,14 +6,9 @@ "Ingen kommando" "Fejl!" "Installerer sikkerhedsopdateringen" - - - - - - - - - - + "Android-systemet kan ikke indlæses. Dine data er muligvis beskadigede. Hvis du bliver ved med at få denne meddelelse, er du måske nødt til at udføre en gendannelse af fabriksdata og slette alle brugerdata, der er gemt på denne enhed." + "Prøv igen" + "Gendannelse af fabriksdata" + "Vil du rydde alle brugerdata?\n\n DETTE KAN IKKE FORTRYDES!" + "Annuller" diff --git a/tools/recovery_l10n/res/values-de/strings.xml b/tools/recovery_l10n/res/values-de/strings.xml index c79b88a1..80fa9711 100644 --- a/tools/recovery_l10n/res/values-de/strings.xml +++ b/tools/recovery_l10n/res/values-de/strings.xml @@ -6,14 +6,9 @@ "Kein Befehl" "Fehler" "Sicherheitsupdate wird installiert" - - - - - - - - - - + "Android-System kann nicht geladen werden. Deine Daten sind eventuell beschädigt. Wenn du diese Nachricht weiterhin erhältst, musst du dein Gerät unter Umständen auf die Werkseinstellungen zurücksetzen und alle darauf gespeicherten Nutzerdaten löschen." + "Noch einmal versuchen" + "Zurücksetzen auf Werkseinstellungen" + "Alle Nutzerdaten löschen?\n\n DIESE AKTION KANN NICHT RÜCKGÄNGIG GEMACHT WERDEN." + "Abbrechen" diff --git a/tools/recovery_l10n/res/values-el/strings.xml b/tools/recovery_l10n/res/values-el/strings.xml index c4fe60f3..204ae409 100644 --- a/tools/recovery_l10n/res/values-el/strings.xml +++ b/tools/recovery_l10n/res/values-el/strings.xml @@ -6,14 +6,9 @@ "Καμία εντολή" "Σφάλμα!" "Εγκατάσταση ενημέρωσης ασφαλείας" - - - - - - - - - - + "Δεν είναι δυνατή η φόρτωση του συστήματος Android. Τα δεδομένα σας μπορεί να είναι κατεστραμμένα. Εάν εξακολουθήσετε να λαμβάνετε αυτό το μήνυμα, μπορεί να χρειαστεί να κάνετε επαναφορά εργοστασιακών ρυθμίσεων και να διαγράψετε όλα τα δεδομένα που έχουν αποθηκευτεί σε αυτήν τη συσκευή." + "Δοκιμάστε ξανά" + "Επαναφορά εργοστασιακών δεδομένων" + "Να διαγραφούν όλα τα δεδομένα χρήστη;\n\n ΔΕΝ ΕΙΝΑΙ ΔΥΝΑΤΗ Η ΑΝΑΙΡΕΣΗ ΑΥΤΗΣ ΤΗΣ ΕΝΕΡΓΕΙΑΣ!" + "Ακύρωση" diff --git a/tools/recovery_l10n/res/values-en-rAU/strings.xml b/tools/recovery_l10n/res/values-en-rAU/strings.xml index 25d5e303..6451e5b6 100644 --- a/tools/recovery_l10n/res/values-en-rAU/strings.xml +++ b/tools/recovery_l10n/res/values-en-rAU/strings.xml @@ -6,14 +6,9 @@ "No command" "Error!" "Installing security update" - - - - - - - - - - + "Cannot 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." + "Try again" + "Factory data reset" + "Wipe all user data?\n\n THIS CANNOT BE UNDONE!" + "Cancel" diff --git a/tools/recovery_l10n/res/values-en-rCA/strings.xml b/tools/recovery_l10n/res/values-en-rCA/strings.xml index 25d5e303..6451e5b6 100644 --- a/tools/recovery_l10n/res/values-en-rCA/strings.xml +++ b/tools/recovery_l10n/res/values-en-rCA/strings.xml @@ -6,14 +6,9 @@ "No command" "Error!" "Installing security update" - - - - - - - - - - + "Cannot 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." + "Try again" + "Factory data reset" + "Wipe all user data?\n\n THIS CANNOT BE UNDONE!" + "Cancel" diff --git a/tools/recovery_l10n/res/values-en-rGB/strings.xml b/tools/recovery_l10n/res/values-en-rGB/strings.xml index 25d5e303..6451e5b6 100644 --- a/tools/recovery_l10n/res/values-en-rGB/strings.xml +++ b/tools/recovery_l10n/res/values-en-rGB/strings.xml @@ -6,14 +6,9 @@ "No command" "Error!" "Installing security update" - - - - - - - - - - + "Cannot 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." + "Try again" + "Factory data reset" + "Wipe all user data?\n\n THIS CANNOT BE UNDONE!" + "Cancel" diff --git a/tools/recovery_l10n/res/values-en-rIN/strings.xml b/tools/recovery_l10n/res/values-en-rIN/strings.xml index 25d5e303..6451e5b6 100644 --- a/tools/recovery_l10n/res/values-en-rIN/strings.xml +++ b/tools/recovery_l10n/res/values-en-rIN/strings.xml @@ -6,14 +6,9 @@ "No command" "Error!" "Installing security update" - - - - - - - - - - + "Cannot 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." + "Try again" + "Factory data reset" + "Wipe all user data?\n\n THIS CANNOT BE UNDONE!" + "Cancel" diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml index 9419a0a0..61390f11 100644 --- a/tools/recovery_l10n/res/values-en-rXC/strings.xml +++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml @@ -6,14 +6,9 @@ "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎No command‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎Error!‎‏‎‎‏‎" "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎Installing security update‎‏‎‎‏‎" - - - - - - - - - - + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎Cannot 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.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎Try again‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎Factory data reset‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‎Wipe all user data?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ THIS CAN NOT BE UNDONE!‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎Cancel‎‏‎‎‏‎" diff --git a/tools/recovery_l10n/res/values-es-rUS/strings.xml b/tools/recovery_l10n/res/values-es-rUS/strings.xml index 5451372a..c0baa592 100644 --- a/tools/recovery_l10n/res/values-es-rUS/strings.xml +++ b/tools/recovery_l10n/res/values-es-rUS/strings.xml @@ -6,14 +6,9 @@ "Ningún comando" "Error" "Instalando actualización de seguridad" - - - - - - - - - - + "No se puede cargar el sistema Android. Es posible que los datos estén dañados. Si este mensaje no desaparece, es posible que debas restablecer la configuración de fábrica del dispositivo y borrar todos los datos del usuario almacenados en él." + "Reintentar" + "Restablecer configuración de fábrica" + "¿Quieres borrar todos los datos del usuario?\n\n ESTA ACCIÓN NO SE PUEDE DESHACER" + "Cancelar" diff --git a/tools/recovery_l10n/res/values-es/strings.xml b/tools/recovery_l10n/res/values-es/strings.xml index e0496b2f..de3b69bf 100644 --- a/tools/recovery_l10n/res/values-es/strings.xml +++ b/tools/recovery_l10n/res/values-es/strings.xml @@ -6,14 +6,9 @@ "Sin comandos" "Error" "Instalando actualización de seguridad" - - - - - - - - - - + "No se puede cargar el sistema Android. Es posible que tus datos estén dañados. Si sigue apareciendo este mensaje, es posible que tengas que restablecer el estado de fábrica y borrar todos los datos de usuario almacenados en este dispositivo." + "Reintentar" + "Restablecer estado de fábrica" + "¿Quieres borrar todos los datos de usuario?\n\n ESTA ACCIÓN NO SE PUEDE DESHACER." + "Cancelar" diff --git a/tools/recovery_l10n/res/values-et/strings.xml b/tools/recovery_l10n/res/values-et/strings.xml index 6346e072..cafb32ff 100644 --- a/tools/recovery_l10n/res/values-et/strings.xml +++ b/tools/recovery_l10n/res/values-et/strings.xml @@ -6,14 +6,9 @@ "Käsk puudub" "Viga!" "Turvavärskenduse installimine" - - - - - - - - - - + "Android-süsteemi ei saa laadida. Teie andmed on võib-olla rikutud. Kui jätkate selle sõnumi hankimist, peate võib-olla tegema tehaseandmetele lähtestamise ja kustutama kõik sellesse seadmesse salvestatud kasutajaandmed." + "Proovige uuesti" + "Tehaseandmetele lähtestamine" + "Kas kustutada kõik kasutajaandmed?\n\n SEDA TOIMINGUT EI SAA TAGASI VÕTTA!" + "Tühista" diff --git a/tools/recovery_l10n/res/values-eu/strings.xml b/tools/recovery_l10n/res/values-eu/strings.xml index 033098b3..005a0426 100644 --- a/tools/recovery_l10n/res/values-eu/strings.xml +++ b/tools/recovery_l10n/res/values-eu/strings.xml @@ -6,14 +6,9 @@ "Ez dago agindurik" "Errorea" "Segurtasun-eguneratzea instalatzen" - - - - - - - - - - + "Ezin da kargatu Android sistema. Zure datuak hondatuta egon daitezke. Mezu hau jasotzen jarraitzen baduzu, jatorrizko datuak berrezarri beharko dituzu eta gailuan gordetako erabiltzaile-datu guztiak ezabatu beharko dituzu." + "Saiatu berriro" + "Berrezarri jatorrizko datuak" + "Erabiltzailearen datu guztiak xahutu nahi dituzu?\n\n EKINTZA HORI EZIN DA DESEGIN!" + "Utzi" diff --git a/tools/recovery_l10n/res/values-fa/strings.xml b/tools/recovery_l10n/res/values-fa/strings.xml index de9a2409..1c1be9ae 100644 --- a/tools/recovery_l10n/res/values-fa/strings.xml +++ b/tools/recovery_l10n/res/values-fa/strings.xml @@ -6,14 +6,9 @@ "فرمانی وجود ندارد" "خطا!" "در حال نصب به‌روزرسانی امنیتی" - - - - - - - - - - + "‏نمی‌توان سیستم Android را بارگیری کرد. ممکن است داده‌های شما خراب باشند. اگر همچنان این پیام را دریافت می‌کنید، شاید لازم باشد بازنشانی داده‌های کارخانه‌ای انجام دهید و همه داده‌های کاربر را که در این دستگاه ذخیره شده است پاک کنید." + "تلاش مجدد" + "بازنشانی داده‌های کارخانه" + "همه داده‌های کاربر پاک شود؟\n\n این کار قابل‌واگرد نیست!" + "لغو" diff --git a/tools/recovery_l10n/res/values-fi/strings.xml b/tools/recovery_l10n/res/values-fi/strings.xml index 8d8cb914..fddaf145 100644 --- a/tools/recovery_l10n/res/values-fi/strings.xml +++ b/tools/recovery_l10n/res/values-fi/strings.xml @@ -6,14 +6,9 @@ "Ei komentoa" "Virhe!" "Asennetaan tietoturvapäivitystä" - - - - - - - - - - + "Android-järjestelmän lataaminen epäonnistui. Datasi voi olla vioittunut. Jos näet tämän viestin toistuvasti, sinun on ehkä palautettava tehdasasetukset ja poistettava kaikki laitteella olevat käyttäjätiedot." + "Yritä uudelleen" + "Tehdasasetuksien palauttaminen" + "Poistetaanko kaikki käyttäjätiedot?\n\nTÄTÄ EI VOI PERUA!" + "Peruuta" diff --git a/tools/recovery_l10n/res/values-fr-rCA/strings.xml b/tools/recovery_l10n/res/values-fr-rCA/strings.xml index 19783084..978e9ff9 100644 --- a/tools/recovery_l10n/res/values-fr-rCA/strings.xml +++ b/tools/recovery_l10n/res/values-fr-rCA/strings.xml @@ -6,14 +6,9 @@ "Aucune commande" "Erreur!" "Installation de la mise à jour de sécurité en cours..." - - - - - - - - - - + "Impossible de charger le système Android. Il se peut que vos données soient corrompues. Si vous continuez de recevoir ce message, vous devrez peut-être effectuer une réinitialisation de l\'appareil à ses paramètres d\'usine et effacer toutes les données d\'utilisateur qu\'il contient." + "Réessayer" + "Réinitialiser aux paramètres d\'usine" + "Effacer toutes les données de l\'utilisateur?\n\n CETTE ACTION NE PEUT PAS ÊTRE ANNULÉE!" + "Annuler" diff --git a/tools/recovery_l10n/res/values-fr/strings.xml b/tools/recovery_l10n/res/values-fr/strings.xml index 599233d0..693a5ddb 100644 --- a/tools/recovery_l10n/res/values-fr/strings.xml +++ b/tools/recovery_l10n/res/values-fr/strings.xml @@ -6,14 +6,9 @@ "Aucune commande" "Erreur !" "Installation de la mise à jour de sécurité…" - - - - - - - - - - + "Impossible de charger le système Android. Vos données sont peut-être corrompues. Si vous continuez à recevoir ce message, vous devrez peut-être rétablir la configuration d\'usine de votre appareil et effacer toutes les données utilisateur stockées sur cet appareil." + "Réessayer" + "Rétablir la configuration d\'usine" + "Effacer toutes les données utilisateur ?\n\n CETTE ACTION NE PEUT PAS ÊTRE ANNULÉE." + "Annuler" diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml index 8c652d62..e51b36df 100644 --- a/tools/recovery_l10n/res/values-gl/strings.xml +++ b/tools/recovery_l10n/res/values-gl/strings.xml @@ -6,14 +6,9 @@ "Non hai ningún comando" "Erro" "Instalando actualización de seguranza" - - - - - - - - - - + "Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos de usuario almacenados neste dispositivo." + "Tentar de novo" + "Restablecemento dos datos de fábrica" + "Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." + "Cancelar" diff --git a/tools/recovery_l10n/res/values-gu/strings.xml b/tools/recovery_l10n/res/values-gu/strings.xml index 84b4f451..bd83447d 100644 --- a/tools/recovery_l10n/res/values-gu/strings.xml +++ b/tools/recovery_l10n/res/values-gu/strings.xml @@ -6,14 +6,9 @@ "કોઈ આદેશ નથી" "ભૂલ!" "સુરક્ષા અપડેટ ઇન્સ્ટૉલ કરી રહ્યાં છે" - - - - - - - - - - + "Android સિસ્ટમ લોડ કરી શકાતી નથી. તમારો ડેટા કદાચ દૂષિત થયો હોઈ શકે છે. જો તમને આ સંદેશ મળવાનું ચાલુ રહે, તો કદાચ તમારે આ ડિવાઇસ માટે ફેક્ટરી ડેટા રીસેટ કરવાની પ્રક્રિયા કરવી અને આના પર સ્ટોર કરેલો વપરાશકર્તાનો બધો ડેટા કાઢી નાખવો જરૂરી રહેશે." + "ફરી પ્રયાસ કરો" + "ફેક્ટરી ડેટા રીસેટ કરો" + "શું વપરાશકર્તાનો બધો ડેટા વાઇપ કરીએ?\n\n આ ક્રિયામાં કરેલો ફેરફાર રદ કરી શકાતો નથી!" + "રદ કરો" diff --git a/tools/recovery_l10n/res/values-hi/strings.xml b/tools/recovery_l10n/res/values-hi/strings.xml index 50248b59..c1aa2e97 100644 --- a/tools/recovery_l10n/res/values-hi/strings.xml +++ b/tools/recovery_l10n/res/values-hi/strings.xml @@ -6,14 +6,9 @@ "कोई निर्देश नहीं मिला" "गड़बड़ी!" "सुरक्षा अपडेट इंस्टॉल किया जा रहा है" - - - - - - - - - - + "Android सिस्टम लोड नहीं किया जा सकता. शायद आपके डेटा में गड़बड़ी है. अगर आपको यह मैसेज मिलता रहता है, तो शायद आपको फ़ैक्ट्री डेटा रीसेट करना पड़े और इस डिवाइस की मेमोरी में मौजूद उपयोगकर्ता का सभी डेटा हमेशा के लिए मिटाना पड़े." + "फिर से कोशिश करें" + "फ़ैक्ट्री डेटा रीसेट" + "क्या उपयोगकर्ता का सभी डेटा मिटाएं?\n\n इसे वापस नहीं लाया जा सकता!" + "अभी नहीं" diff --git a/tools/recovery_l10n/res/values-hr/strings.xml b/tools/recovery_l10n/res/values-hr/strings.xml index 001e3349..0fa8fa9f 100644 --- a/tools/recovery_l10n/res/values-hr/strings.xml +++ b/tools/recovery_l10n/res/values-hr/strings.xml @@ -6,14 +6,9 @@ "Nema naredbe" "Pogreška!" "Instaliranje sigurnosnog ažuriranja" - - - - - - - - - - + "Sustav Android ne može se učitati. Podaci su možda oštećeni. Ako opet primite ovu poruku, možda ćete morati vratiti uređaj na tvorničko stanje i izbrisati sve podatke korisnika pohranjene na ovom uređaju." + "Pokušaj ponovo" + "Vraćanje na tvorničko stanje" + "Želite li izbrisati sve podatke korisnika?\n\n TO SE NE MOŽE PONIŠTITI!" + "Odustani" diff --git a/tools/recovery_l10n/res/values-hu/strings.xml b/tools/recovery_l10n/res/values-hu/strings.xml index 5a6d6018..b7998cea 100644 --- a/tools/recovery_l10n/res/values-hu/strings.xml +++ b/tools/recovery_l10n/res/values-hu/strings.xml @@ -6,14 +6,9 @@ "Nincs parancs" "Hiba!" "Biztonsági frissítés telepítése" - - - - - - - - - - + "Nem sikerült az Android rendszer betöltése. Az adatok sérültek lehetnek. Ha újra megjelenik ez az üzenet, előfordulhat, hogy vissza kell állítania az eszköz gyári adatait, és törölnie kell az eszközön tárolt összes felhasználói adatot." + "Újra" + "Gyári adatok visszaállítása" + "Törli az összes felhasználói adatot?\n\n A MŰVELET NEM VONHATÓ VISSZA." + "Mégse" diff --git a/tools/recovery_l10n/res/values-hy/strings.xml b/tools/recovery_l10n/res/values-hy/strings.xml index a652e339..35a0ab11 100644 --- a/tools/recovery_l10n/res/values-hy/strings.xml +++ b/tools/recovery_l10n/res/values-hy/strings.xml @@ -6,14 +6,9 @@ "Հրամանը տրված չէ" "Սխալ" "Անվտանգության թարմացման տեղադրում" - - - - - - - - - - + "Չհաջողվեց բեռնել Android համակարգը։ Հնարավոր է՝ ձեր տվյալները վնասված են։ Եթե նորից տեսնեք այս հաղորդագրությունը, փորձեք վերակայել սարքի կարգավորումները և ջնջել օգտատիրոջ բոլոր տվյալները։" + "Նորից փորձել" + "Վերակայել բոլոր տվյալները" + "Մաքրե՞լ օգտատիրոջ բոլոր տվյալները։\n\n ԱՅՍ ԳՈՐԾՈՂՈՒԹՅՈՒՆԸ ՀՆԱՐԱՎՈՐ ՉԻ ԼԻՆԻ ՀԵՏԱՐԿԵԼ" + "Չեղարկել" diff --git a/tools/recovery_l10n/res/values-in/strings.xml b/tools/recovery_l10n/res/values-in/strings.xml index b34e3941..15a78ec4 100644 --- a/tools/recovery_l10n/res/values-in/strings.xml +++ b/tools/recovery_l10n/res/values-in/strings.xml @@ -6,14 +6,9 @@ "Tidak ada perintah" "Error!" "Memasang pembaruan keamanan" - - - - - - - - - - + "Tidak dapat memuat sistem Android. Data Anda mungkin rusak. Jika terus mendapatkan pesan ini, Anda mungkin perlu melakukan reset ke setelan pabrik dan menghapus semua data pengguna yang disimpan di perangkat ini." + "Coba lagi" + "Reset ke setelan pabrik" + "Wipe semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!" + "Batal" diff --git a/tools/recovery_l10n/res/values-is/strings.xml b/tools/recovery_l10n/res/values-is/strings.xml index d1e790a2..4a6295af 100644 --- a/tools/recovery_l10n/res/values-is/strings.xml +++ b/tools/recovery_l10n/res/values-is/strings.xml @@ -6,14 +6,9 @@ "Engin skipun" "Villa!" "Setur upp öryggisuppfærslu" - - - - - - - - - - + "Ekki er hægt að hlaða Android kerfi. Gögnin þín kunna að vera skemmd. Ef þessi skilaboð halda áfram að birtast gætirðu þurft að núllstilla og eyða öllum notandagögnum sem eru vistuð í þessu tæki." + "Reyna aftur" + "Núllstilling" + "Viltu eyða öllum notandagögnum?\n\n EKKI ER HÆGT AÐ AFTURKALLA ÞETTA!" + "Hætta við" diff --git a/tools/recovery_l10n/res/values-it/strings.xml b/tools/recovery_l10n/res/values-it/strings.xml index 12c62752..8bc203c1 100644 --- a/tools/recovery_l10n/res/values-it/strings.xml +++ b/tools/recovery_l10n/res/values-it/strings.xml @@ -6,14 +6,9 @@ "Nessun comando" "Errore!" "Installazione aggiornamento sicurezza…" - - - - - - - - - - + "Impossibile caricare il sistema Android. I tuoi dati potrebbero essere danneggiati. Se continui a ricevere questo messaggio, potrebbe essere necessario eseguire un ripristino dei dati di fabbrica e cancellare tutti i dati utente memorizzati su questo dispositivo." + "Riprova" + "Ripristino dati di fabbrica" + "Vuoi cancellare tutti i dati utente?\n\n NON È POSSIBILE ANNULLARE L\'OPERAZIONE." + "Annulla" diff --git a/tools/recovery_l10n/res/values-iw/strings.xml b/tools/recovery_l10n/res/values-iw/strings.xml index 42d931b5..8ca3bdf0 100644 --- a/tools/recovery_l10n/res/values-iw/strings.xml +++ b/tools/recovery_l10n/res/values-iw/strings.xml @@ -6,14 +6,9 @@ "אין פקודה" "שגיאה!" "מתקין עדכון אבטחה" - - - - - - - - - - + "‏לא ניתן לטעון את מערכת Android. ייתכן שהנתונים שלך פגומים. אם הודעה זו תופיע שוב, ייתכן שיהיה עליך לבצע איפוס לנתוני היצרן ולמחוק את כל נתוני המשתמש ששמורים במכשיר זה." + "ניסיון נוסף" + "איפוס לנתוני היצרן" + "לאפס את כל נתוני המשתמש?\n\n לא ניתן לבטל פעולה זו!" + "ביטול" diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml index 4e832117..3d663727 100644 --- a/tools/recovery_l10n/res/values-ja/strings.xml +++ b/tools/recovery_l10n/res/values-ja/strings.xml @@ -6,14 +6,9 @@ "コマンドが指定されていません" "エラーが発生しました。" "セキュリティ アップデートをインストールしています" - - - - - - - - - - + "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。" + "再試行" + "データの初期化" + "すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。" + "キャンセル" diff --git a/tools/recovery_l10n/res/values-ka/strings.xml b/tools/recovery_l10n/res/values-ka/strings.xml index 0c6f49a7..04b8a417 100644 --- a/tools/recovery_l10n/res/values-ka/strings.xml +++ b/tools/recovery_l10n/res/values-ka/strings.xml @@ -6,14 +6,9 @@ "ბრძანება არ არის" "წარმოიქმნა შეცდომა!" "მიმდინარეობს უსაფრთხოების განახლების ინსტალაცია" - - - - - - - - - - + "Android სისტემის ჩატვირთვა ვერ მოხერხდა. შესაძლოა თქვენი მონაცემები დაზიანებულია. თუ ამ შეტყობინებას კვლავ მიიღებთ, შეიძლება საჭირო იყოს ქარხნული მონაცემების აღდგენა და ამ მოწყობილობაზე შენახული მომხმარებლის ყველა მონაცემის ამოშლა." + "ხელახლა ცდა" + "ქარხნული მონაცემების აღდგენა" + "გსურთ მომხმარებლის ყველა მონაცემის ამოშლა?\n\n ამ მოქმედების გაუქმება ვერ მოხერხდება!" + "გაუქმება" diff --git a/tools/recovery_l10n/res/values-kk/strings.xml b/tools/recovery_l10n/res/values-kk/strings.xml index 787bda07..3f6aa23d 100644 --- a/tools/recovery_l10n/res/values-kk/strings.xml +++ b/tools/recovery_l10n/res/values-kk/strings.xml @@ -6,14 +6,9 @@ "Пәрмен жоқ" "Қате!" "Қауіпсіздік жаңартуы орнатылуда" - - - - - - - - - - + "Android жүйесі жүктелмейді. Деректеріңіз бүлінген болуы мүмкін. Егер осы хабар қайта шықса, зауыттық деректерді қалпына келтіріп, пайдаланушы деректерін жойып көріңіз." + "Қайталау" + "Зауыттық деректерді қалпына келтіру" + "Пайдаланушының барлық деректері жойылсын ба?\n\n БҰЛ ӘРЕКЕТТІ ҚАЙТАРЫЛМАЙДЫ!" + "Бас тарту" diff --git a/tools/recovery_l10n/res/values-km/strings.xml b/tools/recovery_l10n/res/values-km/strings.xml index 355bffd2..0cedb6bb 100644 --- a/tools/recovery_l10n/res/values-km/strings.xml +++ b/tools/recovery_l10n/res/values-km/strings.xml @@ -6,14 +6,9 @@ "គ្មានពាក្យបញ្ជាទេ" "កំហុស!" "កំពុងដំឡើងការអាប់ដេតសុវត្ថិភាព" - - - - - - - - - - + "មិនអាច​ផ្ទុកប្រព័ន្ធ Android បានទេ។ ទិន្នន័យ​របស់​អ្នកអាច​នឹងខូច។ ប្រសិនបើ​អ្នក​បន្តទទួល​បានសារនេះ អ្នកអាចនឹងត្រូវកំណត់​ទិន្នន័យ​ដូច​ចេញ​ពី​រោងចក្រ និងលុបទិន្នន័យ​ទាំងអស់​របស់អ្នក​ប្រើប្រាស់​ដែលបានផ្ទុកនៅ​លើ​ឧបករណ៍​នេះ។" + "ព្យាយាម​ម្ដងទៀត" + "កំណត់​ទិន្នន័យ​ដូច​ចេញ​ពី​រោងចក្រ" + "ឈូស​ទិន្នន័យ​ទាំងអស់​របស់អ្នក​ប្រើប្រាស់?\n\nសកម្មភាព​នេះមិនអាចត្រឡប់វិញបានទេ!" + "បោះបង់" diff --git a/tools/recovery_l10n/res/values-kn/strings.xml b/tools/recovery_l10n/res/values-kn/strings.xml index ef6b0801..a98f4692 100644 --- a/tools/recovery_l10n/res/values-kn/strings.xml +++ b/tools/recovery_l10n/res/values-kn/strings.xml @@ -6,14 +6,9 @@ "ಯಾವುದೇ ಆದೇಶವಿಲ್ಲ" "ದೋಷ!" "ಭದ್ರತೆಯ ಅಪ್‌ಡೇಟ್‌ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ" - - - - - - - - - - + "Android ಸಿಸ್ಟಂ ಅನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ನಿಮ್ಮ ಡೇಟಾ ದೋಷಪೂರಿತವಾಗಿರಬಹುದು. ನೀವು ಈ ಸಂದೇಶ ಪಡೆಯುವುದು ಮುಂದುವರಿದರೆ, ನೀವು ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರಿಸೆಟ್ ಮಾಡುವ ಅಗತ್ಯವಿದೆ ಮತ್ತು ಈ ಸಾಧನದಲ್ಲಿ ಸಂಗ್ರಹಿಸಲಾದ ಎಲ್ಲಾ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸಬೇಕಾಗುತ್ತದೆ." + "ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ" + "ಫ್ಯಾಕ್ಟರಿ ಡೇಟಾ ರಿಸೆಟ್‌" + "ಎಲ್ಲಾ ಬಳಕೆದಾರರ ಡೇಟಾವನ್ನು ಅಳಿಸುವುದೇ?\n\n ಇದನ್ನು ರದ್ದುಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ!" + "ರದ್ದುಮಾಡಿ" diff --git a/tools/recovery_l10n/res/values-ko/strings.xml b/tools/recovery_l10n/res/values-ko/strings.xml index 3f83b205..9067f4c3 100644 --- a/tools/recovery_l10n/res/values-ko/strings.xml +++ b/tools/recovery_l10n/res/values-ko/strings.xml @@ -6,14 +6,9 @@ "명령어 없음" "오류!" "보안 업데이트 설치 중" - - - - - - - - - - + "Android 시스템을 로드할 수 없습니다. 데이터가 손상되었을 수 있습니다. 이 메시지가 계속 표시되면 초기화를 실행하여 기기에 저장된 사용자 데이터를 모두 삭제해야 할 수도 있습니다." + "다시 시도" + "초기화" + "사용자 데이터를 모두 삭제하시겠습니까?\n\n 이 작업은 실행취소할 수 없습니다." + "취소" diff --git a/tools/recovery_l10n/res/values-ky/strings.xml b/tools/recovery_l10n/res/values-ky/strings.xml index 3832f33e..1cd69ea8 100644 --- a/tools/recovery_l10n/res/values-ky/strings.xml +++ b/tools/recovery_l10n/res/values-ky/strings.xml @@ -6,14 +6,9 @@ "Буйрук берилген жок" "Ката!" "Коопсуздук жаңыртуусу орнотулууда" - - - - - - - - - - + "Android тутуму жүктөлбөй жатат. Дайындарыңыз бузук болушу мүмкүн. Бул билдирүү дагы деле келе берсе, түзмөктү кайра башынан жөндөп, анда сакталган бардык колдонуучу дайындарын тазалашыңыз керек." + "Кайталоо" + "Кайра башынан жөндөө" + "Колдонуучу дайындарынын баары жашырылсынбы?\n\n МУНУ АРТКА КАЙТАРУУ МҮМКҮН ЭМЕС!" + "Жок" diff --git a/tools/recovery_l10n/res/values-lo/strings.xml b/tools/recovery_l10n/res/values-lo/strings.xml index f5224ff3..4a814278 100644 --- a/tools/recovery_l10n/res/values-lo/strings.xml +++ b/tools/recovery_l10n/res/values-lo/strings.xml @@ -6,14 +6,9 @@ "ບໍ່ມີຄຳສັ່ງ" "ຜິດພາດ!" "ກຳລັງຕິດຕັ້ງອັບເດດຄວາມປອດໄພ" - - - - - - - - - - + "ບໍ່ສາມາດໂຫຼດລະບົບ Android ໄດ້. ຂໍ້ມູນຂອງທ່ານອາດເສຍຫາຍ. ຫາກທ່ານຍັງໄດ້ຮັບຂໍ້ຄວາມນີ້ຕໍ່ໄປ, ທ່ານອາດຕ້ອງຣີເຊັດເປັນຄ່າຈາກໂຮງງານ ແລະ ລຶບຂໍ້ມູນຜູ້ໃຊ້ທັງໝົດທີ່ຈັດເກັບໄວ້ຢູ່ອຸປະກອນນີ້ອອກ." + "ລອງໃໝ່" + "ຣີເຊັດຄ່າຈາກໂຮງງານ" + "ລຶບລ້າງຂໍ້ມູນຜູ້ໃຊ້ທັງໝົດບໍ?\n\n ຄຳສັ່ງນີ້ຈະບໍ່ສາມາດຍົກເລີກໄດ້!" + "ຍົກເລີກ" diff --git a/tools/recovery_l10n/res/values-lt/strings.xml b/tools/recovery_l10n/res/values-lt/strings.xml index 1ec3586b..f9b7d391 100644 --- a/tools/recovery_l10n/res/values-lt/strings.xml +++ b/tools/recovery_l10n/res/values-lt/strings.xml @@ -6,14 +6,9 @@ "Nėra jokių komandų" "Klaida!" "Diegiamas saugos naujinys" - - - - - - - - - - + "Negalima įkelti „Android“ sistemos. Duomenys gali būti pažeisti. Jei ir toliau gausite šį pranešimą, jums gali reikėti atkurti gamyklinius duomenis ir ištrinti visus naudotojo duomenis, saugomus šiame įrenginyje." + "Bandyti dar kartą" + "Gamyklinių duomenų atkūrimas" + "Išvalyti visus naudotojo duomenis?\n\n ŠIO VEIKSMO NEGALIMA ANULIUOTI!" + "Atšaukti" diff --git a/tools/recovery_l10n/res/values-lv/strings.xml b/tools/recovery_l10n/res/values-lv/strings.xml index 83b05940..6cf8ce30 100644 --- a/tools/recovery_l10n/res/values-lv/strings.xml +++ b/tools/recovery_l10n/res/values-lv/strings.xml @@ -6,14 +6,9 @@ "Nav nevienas komandas" "Kļūda!" "Notiek drošības atjauninājuma instalēšana" - - - - - - - - - - + "Nevar ielādēt Android sistēmu. Jūsu dati var būt bojāti. Ja šis ziņojums tiek rādīts atkārtoti, iespējams, jums ir jāveic rūpnīcas datu atiestatīšana un jādzēš visi šajā ierīcē saglabātie lietotāja dati." + "Mēģināt vēlreiz" + "Rūpnīcas datu atiestatīšana" + "Vai dzēst visus lietotāja datus?\n\n ŠO DARBĪBU NEVAR ATSAUKT!" + "Atcelt" diff --git a/tools/recovery_l10n/res/values-mk/strings.xml b/tools/recovery_l10n/res/values-mk/strings.xml index 39b4fa42..ff56131f 100644 --- a/tools/recovery_l10n/res/values-mk/strings.xml +++ b/tools/recovery_l10n/res/values-mk/strings.xml @@ -6,14 +6,9 @@ "Нема наредба" "Грешка!" "Се инсталира безбедносно ажурирање" - - - - - - - - - - + "Не може да се вчита системот Android. Можно е податоците да се оштетени. Ако и понатаму ја примате поракава, можеби ќе треба да извршите ресетирање на фабрички податоци и да ги избришете сите кориснички податоци меморирани на уредов." + "Обиди се пак" + "Ресетирање на фабрички податоци" + "Да се избришат ли сите кориснички податоци?\n\n ОВА НЕ МОЖЕ ДА СЕ ВРАТИ!" + "Откажи" diff --git a/tools/recovery_l10n/res/values-ml/strings.xml b/tools/recovery_l10n/res/values-ml/strings.xml index 752a3ed1..2b331ac7 100644 --- a/tools/recovery_l10n/res/values-ml/strings.xml +++ b/tools/recovery_l10n/res/values-ml/strings.xml @@ -6,14 +6,9 @@ "കമാൻഡ് ഒന്നുമില്ല" "പിശക്!" "സുരക്ഷാ അപ്ഡേറ്റ് ഇൻസ്റ്റാൾ ചെയ്യുന്നു" - - - - - - - - - - + "Android സിസ്‌റ്റം ലോഡ് ചെയ്യാനാവില്ല. നിങ്ങളുടെ ഡാറ്റ കേടായിരിക്കാം. ഈ സന്ദേശം തുടർന്നും ലഭിക്കുകയാണെങ്കിൽ, നിങ്ങൾ ഒരു ഫാക്‌ടറി ഡാറ്റ പുനഃക്രമീകരണം നടത്തേണ്ടതുണ്ട് ഒപ്പം ഈ ഉപകരണത്തിൽ സ്‌റ്റോർ ചെയ്‌തിട്ടുള്ള എല്ലാ ഉപയോക്തൃ ഡാറ്റകളും മായ്‌ക്കേണ്ടതുണ്ട്." + "വീണ്ടും ശ്രമിക്കുക" + "ഫാക്‌ടറി ഡാറ്റ പുനഃക്രമീകരണം" + "എല്ലാ ഉപയോക്തൃ ഡാറ്റകളും മായ്‌ക്കണോ?\n\n ഇത് പഴയപടിയാക്കാനാവില്ല!" + "റദ്ദാക്കുക" diff --git a/tools/recovery_l10n/res/values-mn/strings.xml b/tools/recovery_l10n/res/values-mn/strings.xml index df61476c..b0a57ed1 100644 --- a/tools/recovery_l10n/res/values-mn/strings.xml +++ b/tools/recovery_l10n/res/values-mn/strings.xml @@ -6,14 +6,9 @@ "Тушаал байхгүй" "Алдаа!" "Аюулгүй байдлын шинэчлэлтийг суулгаж байна" - - - - - - - - - - + "Андройд системийг ачаалах боломжгүй байна. Таны өгөгдөл эвдэрч болзошгүй. Хэрэв та энэ мессежийг үргэлжлүүлэн авах бол үйлдвэрээс гарсан төлөвийг ажиллуулж, энэ төхөөрөмжид хадгалсан хэрэглэгчийн бүх өгөгдлийг устгах шаардлагатай байж болзошгүй." + "Дахин оролдох" + "Үйлдвэрээс гарсан төлөвт" + "Хэрэглэгчийн бүх өгөгдлийг арчих уу?\n\n ҮҮНИЙГ БУЦААХ БОЛОМЖГҮЙ!" + "Цуцлах" diff --git a/tools/recovery_l10n/res/values-mr/strings.xml b/tools/recovery_l10n/res/values-mr/strings.xml index f96ddea9..9b137079 100644 --- a/tools/recovery_l10n/res/values-mr/strings.xml +++ b/tools/recovery_l10n/res/values-mr/strings.xml @@ -6,14 +6,9 @@ "कोणतीही कमांड नाही" "एरर!" "सुरक्षा अपडेट इंस्टॉल करत आहे" - - - - - - - - - - + "Android सिस्टम लोड करू शकत नाही. तुमचा डेटा धोक्यात असू शकतो.तुम्हाला हा मेसेज मिळत राहिल्यास, फॅक्टरी डेटा रीसेट करणे आणि या डिव्हाइसवर स्टोअर केलेला सर्व वापरकर्ता डेटा मिटवणे आवश्यक आहे." + "पुन्हा प्रयत्न करा" + "फॅक्‍टरी डेटा रीसेट" + "सर्व वापरकर्ता डेटा पुसून टाकायचा का?\n\n हे पहिल्‍यासारखे करू शकत नाही!" + "रद्द करा" diff --git a/tools/recovery_l10n/res/values-ms/strings.xml b/tools/recovery_l10n/res/values-ms/strings.xml index 7a3480ee..d094f547 100644 --- a/tools/recovery_l10n/res/values-ms/strings.xml +++ b/tools/recovery_l10n/res/values-ms/strings.xml @@ -6,14 +6,9 @@ "Tiada perintah" "Ralat!" "Memasang kemas kini keselamatan" - - - - - - - - - - + "Tidak dapat memuatkan sistem Android. Data anda mungkin rosak. Jika anda menerima mesej ini secara berterusan, anda mungkin perlu melaksanakan tetapan semula data kilang dan memadamkan semua data pengguna yang disimpan pada peranti ini." + "Cuba lagi" + "Tetapan semula data kilang" + "Lapkan semua data pengguna?\n\n TINDAKAN INI TIDAK BOLEH DIBUAT ASAL!" + "Batal" diff --git a/tools/recovery_l10n/res/values-my/strings.xml b/tools/recovery_l10n/res/values-my/strings.xml index 1dc11e00..09cd4ea5 100644 --- a/tools/recovery_l10n/res/values-my/strings.xml +++ b/tools/recovery_l10n/res/values-my/strings.xml @@ -6,14 +6,9 @@ "ညွှန်ကြားချက်မပေးထားပါ" "မှားနေပါသည်!" "လုံခြုံရေး အပ်ဒိတ်ကို ထည့်သွင်းနေသည်" - - - - - - - - - - + "Android စနစ် ဖွင့်၍မရပါ။ သင့်ဒေတာများ ပျက်နေခြင်း ဖြစ်နိုင်သည်။ ဤမက်ဆေ့ဂျ် ဆက်လက်ရရှိနေလျှင် စက်ရုံထုတ်အခြေအနေပြန်ယူပြီး ဤစက်ပေါ်တွင် သိမ်းထားသော အသုံးပြုသူဒေတာအားလုံး ဖျက်ရန် လိုအပ်နိုင်သည်။" + "ထပ်စမ်းကြည့်ပါ" + "စက်ရုံထုတ်အခြေအနေပြန်ယူခြင်း" + "အသုံးပြုသူဒေတာ အားလုံးကို ရှင်းလင်းမလား။\n\n ၎င်းကို ပြန်ပြင်၍မရပါ။" + "မလုပ်တော့" diff --git a/tools/recovery_l10n/res/values-nb/strings.xml b/tools/recovery_l10n/res/values-nb/strings.xml index f7d23a6e..e8cad136 100644 --- a/tools/recovery_l10n/res/values-nb/strings.xml +++ b/tools/recovery_l10n/res/values-nb/strings.xml @@ -6,14 +6,9 @@ "Ingen kommandoer" "Feil!" "Installerer sikkerhetsoppdateringen" - - - - - - - - - - + "Kan ikke laste inn Android-systemet. Dataene dine er muligens skadet. Hvis du fortsetter å se denne meldingen, må du muligens tilbakestille til fabrikkstandard og tømme alle brukerdataene som er lagret på denne enheten." + "Prøv igjen" + "Tilbakestill til fabrikkstandard" + "Vil du viske ut alle brukerdataene?\n\n DETTE KAN IKKE ANGRES!" + "Avbryt" diff --git a/tools/recovery_l10n/res/values-ne/strings.xml b/tools/recovery_l10n/res/values-ne/strings.xml index 04477674..fa53e9da 100644 --- a/tools/recovery_l10n/res/values-ne/strings.xml +++ b/tools/recovery_l10n/res/values-ne/strings.xml @@ -6,14 +6,9 @@ "कुनै आदेश छैन" "त्रुटि!" "सुरक्षा सम्बन्धी अद्यावधिकलाई स्थापना गर्दै" - - - - - - - - - - + "Android प्रणाली लोड गर्न सकिएन। तपाईंको डेटा बिग्रेको हुन सक्छ। तपाईं यो सन्देश प्राप्त गर्नुहुन्छ भने तपाईंले फ्याक्ट्री डेटा रिसेट गर्न आवश्यक छ र यो यन्त्रमा भण्डारण गरेका सबै प्रयोगकर्ताको डेटा मेट्न पर्छ।" + "फेरि प्रयास गर्नुहोस्" + "फ्याक्ट्री डेटा रिसेट" + "प्रयोगकर्ताको सबै डेटा मेट्ने हो?\n\n यो अन्डू गर्न सकिँदैन!" + "रद्द गर्नुहोस्" diff --git a/tools/recovery_l10n/res/values-nl/strings.xml b/tools/recovery_l10n/res/values-nl/strings.xml index 3be53453..b42bb658 100644 --- a/tools/recovery_l10n/res/values-nl/strings.xml +++ b/tools/recovery_l10n/res/values-nl/strings.xml @@ -6,14 +6,9 @@ "Geen opdracht" "Fout!" "Beveiligingsupdate installeren" - - - - - - - - - - + "Kan het Android-systeem niet laden. Je gegevens zijn mogelijk beschadigd. Als je dit bericht blijft ontvangen, moet je mogelijk de fabrieksinstellingen terugzetten en alle gebruikersgegevens wissen die op dit apparaat zijn opgeslagen." + "Opnieuw proberen" + "Terugzetten op fabrieksinstellingen" + "Alle gebruikersgegevens wissen?\n\n DIT KAN NIET ONGEDAAN WORDEN GEMAAKT." + "Annuleren" diff --git a/tools/recovery_l10n/res/values-or/strings.xml b/tools/recovery_l10n/res/values-or/strings.xml index 749ad82f..25b28e65 100644 --- a/tools/recovery_l10n/res/values-or/strings.xml +++ b/tools/recovery_l10n/res/values-or/strings.xml @@ -6,14 +6,9 @@ "କୌଣସି କମାଣ୍ଡ ନାହିଁ" "ତ୍ରୁଟି!" "ସୁରକ୍ଷା ଅପ୍‌ଡେଟ୍‌ ଇନ୍‌ଷ୍ଟଲ୍‌ କରୁଛି" - - - - - - - - - - + "Android ସିଷ୍ଟମ୍‍ ଲୋଡ୍‍ କରାଯାଇପାରିବ ନାହିଁ। ଆପଣଙ୍କ ଡାଟା ହୁଏତ ତ୍ରୁଟି ରହିଥାଇ ପାରେ। ଯଦି ଆପଣ ଏହି ମେସେଜ୍‍ ପାଇବା ଜାରି ରଖନ୍ତି, ତେବେ ଆପଣଙ୍କୁ ଫ୍ୟାକ୍ଟେରୀ ଡାଟା ରିସେଟ୍‍ କରିବାକୁ ହେବ ଏବଂ ଏହି ଡିଭାଇସ୍‍‍ରେ ଷ୍ଟୋର୍‍ ହୋଇଥିବା ସମସ୍ତ ଡାଟା ଇରେଜ୍‍ କରନ୍ତୁ।" + "ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ" + "ଫ୍ୟାକ୍ଟୋରୀ ଡାଟା ରିସେଟ୍‌" + "ସମସ୍ଯ ଉପଯୋଗକର୍ତ୍ତା ଡାଟା ୱାଇପ୍‍ କରିବେ?\n\n ଏହା ଫେରାଇ ନିଆଯାଇପାରିବ ନାହିଁ!" + "ବାତିଲ୍‌ କରନ୍ତୁ" diff --git a/tools/recovery_l10n/res/values-pa/strings.xml b/tools/recovery_l10n/res/values-pa/strings.xml index 0475a5e4..37430680 100644 --- a/tools/recovery_l10n/res/values-pa/strings.xml +++ b/tools/recovery_l10n/res/values-pa/strings.xml @@ -6,14 +6,9 @@ "ਕੋਈ ਆਦੇਸ਼ ਨਹੀਂ" "ਅਸ਼ੁੱਧੀ!" "ਸੁਰੱਖਿਆ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" - - - - - - - - - - + "Android ਸਿਸਟਮ ਨੂੰ ਲੋਡ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ। ਸ਼ਾਇਦ ਤੁਹਾਡਾ ਡਾਟਾ ਖਰਾਬ ਹੈ। ਜੇਕਰ ਤੁਹਾਨੂੰ ਇਹ ਸੁਨੇਹਾ ਪ੍ਰਾਪਤ ਹੋਣਾ ਜਾਰੀ ਰਹਿੰਦਾ ਹੈ, ਤਾਂ ਸ਼ਾਇਦ ਤੁਹਾਨੂੰ ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਨਾ ਪਵੇ ਅਤੇ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਸਟੋਰ ਕੀਤੇ ਸਾਰੇ ਵਰਤੋਂਕਾਰ ਡਾਟੇ ਨੂੰ ਮਿਟਾਉਣਾ ਪਵੇ।" + "ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ" + "ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰੋ" + "ਕੀ ਸਾਰਾ ਵਰਤੋਂਕਾਰ ਡਾਟਾ ਸਾਫ਼ ਕਰਨਾ ਹੈ?\n\n ਇਸਨੂੰ ਅਣਕੀਤਾ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ!" + "ਰੱਦ ਕਰੋ" diff --git a/tools/recovery_l10n/res/values-pl/strings.xml b/tools/recovery_l10n/res/values-pl/strings.xml index daea5bd4..48d3dbf6 100644 --- a/tools/recovery_l10n/res/values-pl/strings.xml +++ b/tools/recovery_l10n/res/values-pl/strings.xml @@ -6,14 +6,9 @@ "Brak polecenia" "Błąd" "Instaluję aktualizację zabezpieczeń" - - - - - - - - - - + "Nie można załadować systemu Android. Dane mogą być uszkodzone. Jeśli ten komunikat nadal będzie się pojawiać, może być konieczne przywrócenie danych fabrycznych urządzenia i usunięcie wszystkich zapisanych na nim danych użytkownika." + "Ponów próbę" + "Przywracanie danych fabrycznych" + "Wyczyścić wszystkie dane użytkownika?\n\n TEJ CZYNNOŚCI NIE MOŻNA COFNĄĆ." + "Anuluj" diff --git a/tools/recovery_l10n/res/values-pt-rBR/strings.xml b/tools/recovery_l10n/res/values-pt-rBR/strings.xml index 3d3227cb..0df3edcc 100644 --- a/tools/recovery_l10n/res/values-pt-rBR/strings.xml +++ b/tools/recovery_l10n/res/values-pt-rBR/strings.xml @@ -6,14 +6,9 @@ "Nenhum comando" "Erro!" "Instalando atualização de segurança" - - - - - - - - - - + "Não é possível carregar o sistema Android. Seus dados podem estar corrompidos. Se você continuar recebendo esta mensagem, talvez seja necessário realizar uma redefinição para a configuração original e limpar todos os dados do usuário armazenados neste dispositivo." + "Tentar novamente" + "Redefinição para configuração original" + "Limpar todos os dados do usuário?\n\n NÃO É POSSÍVEL DESFAZER ESSA AÇÃO." + "Cancelar" diff --git a/tools/recovery_l10n/res/values-pt-rPT/strings.xml b/tools/recovery_l10n/res/values-pt-rPT/strings.xml index ae393887..08eb3c95 100644 --- a/tools/recovery_l10n/res/values-pt-rPT/strings.xml +++ b/tools/recovery_l10n/res/values-pt-rPT/strings.xml @@ -6,14 +6,9 @@ "Nenhum comando" "Erro!" "A instalar atualização de segurança" - - - - - - - - - - + "Não é possível carregar o sistema Android. Os seus dados podem estar danificados. Se continuar a receber esta mensagem, pode ter de efetuar uma reposição de dados de fábrica e apagar todos os dados do utilizador armazenados neste dispositivo." + "Tentar novamente" + "Reposição de dados de fábrica" + "Pretende limpar todos os dados do utilizador?\n\n NÃO É POSSÍVEL ANULAR ESTA AÇÃO." + "Cancelar" diff --git a/tools/recovery_l10n/res/values-pt/strings.xml b/tools/recovery_l10n/res/values-pt/strings.xml index 3d3227cb..0df3edcc 100644 --- a/tools/recovery_l10n/res/values-pt/strings.xml +++ b/tools/recovery_l10n/res/values-pt/strings.xml @@ -6,14 +6,9 @@ "Nenhum comando" "Erro!" "Instalando atualização de segurança" - - - - - - - - - - + "Não é possível carregar o sistema Android. Seus dados podem estar corrompidos. Se você continuar recebendo esta mensagem, talvez seja necessário realizar uma redefinição para a configuração original e limpar todos os dados do usuário armazenados neste dispositivo." + "Tentar novamente" + "Redefinição para configuração original" + "Limpar todos os dados do usuário?\n\n NÃO É POSSÍVEL DESFAZER ESSA AÇÃO." + "Cancelar" diff --git a/tools/recovery_l10n/res/values-ro/strings.xml b/tools/recovery_l10n/res/values-ro/strings.xml index 4d90718e..585db835 100644 --- a/tools/recovery_l10n/res/values-ro/strings.xml +++ b/tools/recovery_l10n/res/values-ro/strings.xml @@ -6,14 +6,9 @@ "Nicio comandă" "Eroare!" "Se instalează actualizarea de securitate" - - - - - - - - - - + "Nu se poate încărca sistemul Android. Datele dvs. pot fi corupte. Dacă primiți în continuare acest mesaj, poate fi necesar să reveniți la setările din fabrică și să ștergeți toate datele utilizatorului stocate pe acest dispozitiv." + "Reîncercați" + "Revenire la setările din fabrică" + "Ștergeți toate datele utilizatorului?\n\n ACEST LUCRU NU POATE FI ANULAT!" + "Anulați" diff --git a/tools/recovery_l10n/res/values-ru/strings.xml b/tools/recovery_l10n/res/values-ru/strings.xml index ab7520ae..db8b7611 100644 --- a/tools/recovery_l10n/res/values-ru/strings.xml +++ b/tools/recovery_l10n/res/values-ru/strings.xml @@ -6,14 +6,9 @@ "Команды нет" "Ошибка" "Установка обновления системы безопасности…" - - - - - - - - - - + "Не удалось загрузить систему Android. Возможно, данные повреждены. Если вы снова увидите это сообщение, попробуйте сбросить настройки устройства и удалить все пользовательские данные." + "Повторить попытку" + "Сбросить настройки" + "Стереть все пользовательские данные?\n\nЭТО ДЕЙСТВИЕ НЕЛЬЗЯ ОТМЕНИТЬ." + "Отмена" diff --git a/tools/recovery_l10n/res/values-si/strings.xml b/tools/recovery_l10n/res/values-si/strings.xml index cffb6e9b..67aca72f 100644 --- a/tools/recovery_l10n/res/values-si/strings.xml +++ b/tools/recovery_l10n/res/values-si/strings.xml @@ -6,14 +6,9 @@ "විධානයක් නොමැත" "දෝෂය!" "ආරක්ෂක යාවත්කාලීනය ස්ථාපනය කරමින්" - - - - - - - - - - + "Android පද්ධතිය පූරණය කළ නොහැකිය. ඔබේ දත්ත දූෂිත විය හැකිය. ඔබට මෙම පණිවිඩය දිගටම ලැබෙන්නේ නම්, කර්මාන්ත ශාලා දත්ත යළි සැකසීමක් සිදු කර මෙම උපාංගයේ ගබඩා කළ සියලු පරිශීලක දත්ත මකා දැමීමට ඔබට අවශ්‍ය විය හැකිය." + "නැවත උත්සාහ කරන්න" + "කර්මාන්ත ශාලා දත්ත යළි සැකසීම" + "සියලු පරිශීලක දත්ත මකා දමන්නද?\n\n මෙය පසුගමනය කළ නොහැකිය!" + "අවලංගු කරන්න" diff --git a/tools/recovery_l10n/res/values-sk/strings.xml b/tools/recovery_l10n/res/values-sk/strings.xml index 59985c0e..8a2d2e0c 100644 --- a/tools/recovery_l10n/res/values-sk/strings.xml +++ b/tools/recovery_l10n/res/values-sk/strings.xml @@ -6,14 +6,9 @@ "Žiadny príkaz" "Chyba!" "Inštaluje sa bezpečnostná aktualizácia" - - - - - - - - - - + "Systém Android sa nedá načítať. Vaše údaje môžu byť poškodené. Ak chcete získať túto správu a budete pokračovať, zrejme budete musieť obnoviť výrobné nastavenia a vymazať tak všetky údaje používateľa uložené v tomto zariadení." + "Skúsiť znova" + "Obnovenie výrobných nastavení" + "Chcete vymazať všetky údaje používateľa?\n\n TÁTO AKCIA SA NEDÁ VRÁTIŤ SPÄŤ!" + "Zrušiť" diff --git a/tools/recovery_l10n/res/values-sl/strings.xml b/tools/recovery_l10n/res/values-sl/strings.xml index e36dfb45..653c4274 100644 --- a/tools/recovery_l10n/res/values-sl/strings.xml +++ b/tools/recovery_l10n/res/values-sl/strings.xml @@ -6,14 +6,9 @@ "Ni ukaza" "Napaka" "Nameščanje varnostne posodobitve" - - - - - - - - - - + "Sistema Android ni mogoče naložiti. Podatki so morda poškodovani. Če se bo to sporočilo še naprej prikazovalo, boste morda morali izvesti ponastavitev na tovarniške nastavitve in izbrisati vse uporabniške podatke, ki so shranjeni v tej napravi." + "Poskusi znova" + "Ponastavitev na tovarniške nastavitve" + "Želite izbrisati vse uporabniške podatke?\n\n TEGA NI MOGOČE RAZVELJAVITI!" + "Prekliči" diff --git a/tools/recovery_l10n/res/values-sq/strings.xml b/tools/recovery_l10n/res/values-sq/strings.xml index 58945c0e..5c824e68 100644 --- a/tools/recovery_l10n/res/values-sq/strings.xml +++ b/tools/recovery_l10n/res/values-sq/strings.xml @@ -6,14 +6,9 @@ "Nuk ka komanda" "Gabim!" "Po instalon përditësimin e sigurisë" - - - - - - - - - - + "Sistemi Android nuk mund të ngarkohet. Të dhënat e tua mund të jenë të dëmtuara. Nëse vazhdon të marrësh këtë mesazh, mund të jetë e nevojshme të kryesh një rivendosje të të dhënave të fabrikës dhe të spastrosh të gjitha të dhënat e përdoruesit të ruajtura në këtë pajisje." + "Provo përsëri" + "Rivendosja e të dhënave të fabrikës" + "Të pastrohen të gjitha të dhënat e përdoruesit?\n\n KJO NUK MUND TË ZHBËHET!" + "Anulo" diff --git a/tools/recovery_l10n/res/values-sr/strings.xml b/tools/recovery_l10n/res/values-sr/strings.xml index 275a3aaf..1583beaa 100644 --- a/tools/recovery_l10n/res/values-sr/strings.xml +++ b/tools/recovery_l10n/res/values-sr/strings.xml @@ -6,14 +6,9 @@ "Нема команде" "Грешка!" "Инсталира се безбедносно ажурирање" - - - - - - - - - - + "Учитавање Android система није успело. Подаци су можда оштећени. Ако наставите да добијате ову поруку, можда ћете морати да ресетујете уређај на фабричка подешавања и обришете све податке корисника које чувате на њему." + "Пробај поново" + "Ресетовање на фабричка подешавања" + "Желите ли да избришете све податке корисника?\n\n ОВО НЕ МОЖЕ ДА СЕ ОПОЗОВЕ!" + "Откажи" diff --git a/tools/recovery_l10n/res/values-sv/strings.xml b/tools/recovery_l10n/res/values-sv/strings.xml index 512b5d4b..cf43b251 100644 --- a/tools/recovery_l10n/res/values-sv/strings.xml +++ b/tools/recovery_l10n/res/values-sv/strings.xml @@ -6,14 +6,9 @@ "Inget kommando" "Fel!" "Säkerhetsuppdatering installeras" - - - - - - - - - - + "Det gick inte att läsa in Android-systemet. Data kan ha skadats. Om det här meddelandet visas igen kan du behöva återställa standardinställningarna så att all användardata som sparats på enheten raderas." + "Försök igen" + "Återställ standardinställningarna" + "Vill du rensa bort all användardata?\n\n DET GÅR INTE ATT ÅNGRA DENNA ÅTGÄRD." + "Avbryt" diff --git a/tools/recovery_l10n/res/values-sw/strings.xml b/tools/recovery_l10n/res/values-sw/strings.xml index b0a072f9..6fa72825 100644 --- a/tools/recovery_l10n/res/values-sw/strings.xml +++ b/tools/recovery_l10n/res/values-sw/strings.xml @@ -6,14 +6,9 @@ "Hakuna amri" "Hitilafu fulani imetokea!" "Inasakinisha sasisho la usalama" - - - - - - - - - - + "Imeshindwa kupakia mfumo wa Android. Huenda data yako imeharibika. Kama utandelea kupata ujumbe huu, huenda ukahitaji kurejesha data iliyotoka nayo kiwandani na ufute data yote ya mtumiaji iliyohifadhiwa kwenye kifaa hiki." + "Jaribu tena" + "Kurejesha data iliyotoka nayo kiwandani" + "Ungependa kufuta data yote ya mtumiaji?\n\n KITENDO HIKI HAKIWEZI KUTENDULIWA!" + "Ghairi" diff --git a/tools/recovery_l10n/res/values-ta/strings.xml b/tools/recovery_l10n/res/values-ta/strings.xml index ff2e3e79..bc370f7b 100644 --- a/tools/recovery_l10n/res/values-ta/strings.xml +++ b/tools/recovery_l10n/res/values-ta/strings.xml @@ -6,14 +6,9 @@ "கட்டளை இல்லை" "பிழை!" "பாதுகாப்புப் புதுப்பிப்பை நிறுவுகிறது" - - - - - - - - - - + "Android சிஸ்டத்தைக் காண்பிக்க இயலவில்லை. உங்களின் தரவு சிதைந்திருக்கலாம். இந்த மெசேஜ் உங்களுக்குத் தொடர்ந்து வந்தால், தரவின் ஆரம்பநிலைக்கு மீட்டமைத்தல் மற்றும் இந்தச் சாதனத்தில் சேமிக்கப்பட்டுள்ள அனைத்துப் பயனர் தரவையும் அழித்தல் ஆகியவற்றைச் செய்ய வேண்டியிருக்கலாம்." + "மீண்டும் முயல்க" + "தரவின் ஆரம்பநிலை மீட்டமைப்பு" + "பயனரின் அனைத்துத் தரவையும் நீக்கவா?\n\n இதைச் செயல்தவிர்க்க இயலாது!" + "இல்லை" diff --git a/tools/recovery_l10n/res/values-te/strings.xml b/tools/recovery_l10n/res/values-te/strings.xml index 66039ac9..4d521143 100644 --- a/tools/recovery_l10n/res/values-te/strings.xml +++ b/tools/recovery_l10n/res/values-te/strings.xml @@ -6,14 +6,9 @@ "ఆదేశం లేదు" "ఎర్రర్ సంభవించింది!" "భద్రతా నవీకరణను ఇన్‌స్టాల్ చేస్తోంది" - - - - - - - - - - + "Android సిస్టమ్‌ని లోడ్ చేయడం సాధ్యం కాదు. మీ డేటా పాడై ఉండవచ్చు. మీకు ఈ సందేశం వస్తూనే ఉంటే, మీరు ఫ్యాక్టరీ డేటా రీసెట్ చేసి, పరికరంలో నిల్వ అయిన వినియోగదారు డేటా మొత్తాన్ని తొలగించాల్సి రావచ్చు." + "మళ్లీ ప్రయత్నించు" + "ఫ్యాక్టరీ డేటా రీసెట్" + "వినియోగదారు డేటా మొత్తాన్ని తొలగించాలా?\n\n ఈ చర్యను రద్దు చేయలేరు!" + "రద్దు చేయి" diff --git a/tools/recovery_l10n/res/values-th/strings.xml b/tools/recovery_l10n/res/values-th/strings.xml index 3b315816..83d445df 100644 --- a/tools/recovery_l10n/res/values-th/strings.xml +++ b/tools/recovery_l10n/res/values-th/strings.xml @@ -6,14 +6,9 @@ "ไม่มีคำสั่ง" "ข้อผิดพลาด!" "กำลังติดตั้งการอัปเดตความปลอดภัย" - - - - - - - - - - + "โหลดระบบ Android ไม่ได้ ข้อมูลของคุณอาจเสียหาย หากคุณยังคงได้รับข้อความนี้อยู่ คุณอาจต้องรีเซ็ตข้อมูลเป็นค่าเริ่มต้นและลบข้อมูลผู้ใช้ทั้งหมดที่เก็บอยู่ในอุปกรณ์นี้" + "ลองอีกครั้ง" + "รีเซ็ตข้อมูลเป็นค่าเริ่มต้น" + "ต้องการล้างข้อมูลผู้ใช้ทั้งหมดใช่ไหม\n\n การกระทำนี้จะยกเลิกไม่ได้" + "ยกเลิก" diff --git a/tools/recovery_l10n/res/values-tl/strings.xml b/tools/recovery_l10n/res/values-tl/strings.xml index b414fd94..6621473f 100644 --- a/tools/recovery_l10n/res/values-tl/strings.xml +++ b/tools/recovery_l10n/res/values-tl/strings.xml @@ -6,14 +6,9 @@ "Walang command" "Error!" "Nag-i-install ng update sa seguridad" - - - - - - - - - - + "Hindi ma-load ang Android system. Maaaring sira ang iyong data. Kung patuloy mong matatanggap ang mensaheng ito, maaaring kailanganin mong magsagawa ng pag-reset ng factory data at burahin ang lahat ng data ng user na naka-store sa device na ito." + "Subukang muli" + "Pag-reset ng factory data" + "I-wipe ang lahat ng data ng user?\n\n HINDI ITO MAA-UNDO!" + "Kanselahin" diff --git a/tools/recovery_l10n/res/values-tr/strings.xml b/tools/recovery_l10n/res/values-tr/strings.xml index 4e948b70..e4eca52d 100644 --- a/tools/recovery_l10n/res/values-tr/strings.xml +++ b/tools/recovery_l10n/res/values-tr/strings.xml @@ -6,14 +6,9 @@ "Komut yok" "Hata!" "Güvenlik güncellemesi yükleniyor" - - - - - - - - - - + "Android sisteminiz yüklenemedi. Verileriniz bozulmuş olabilir. Bu mesajı almaya devam ederseniz fabrika verilerine sıfırlama işlemi yapmanız ve bu cihazda depolanan tüm kullanıcı verilerini silmeniz gerekebilir." + "Tekrar dene" + "Fabrika verilerine sıfırla" + "Tüm kullanıcı verileri silinsin mi?\n\n BU İŞLEM GERİ ALINAMAZ!" + "İptal" diff --git a/tools/recovery_l10n/res/values-uk/strings.xml b/tools/recovery_l10n/res/values-uk/strings.xml index 19656207..7bd6fecf 100644 --- a/tools/recovery_l10n/res/values-uk/strings.xml +++ b/tools/recovery_l10n/res/values-uk/strings.xml @@ -6,14 +6,9 @@ "Немає команди" "Помилка!" "Установлюється оновлення системи безпеки" - - - - - - - - - - + "Не вдається завантажити систему Android. Можливо, ваші дані пошкоджено. Якщо ви далі отримуватимете це повідомлення, можливо, доведеться відновити заводські налаштування й видалити всі дані користувача з цього пристрою." + "Повторити" + "Відновити заводські налаштування" + "Видалити всі дані користувача?\n\n ЦЮ ДІЮ НЕ МОЖНА ВІДМІНИТИ." + "Скасувати" diff --git a/tools/recovery_l10n/res/values-ur/strings.xml b/tools/recovery_l10n/res/values-ur/strings.xml index 9ace0f08..da03f197 100644 --- a/tools/recovery_l10n/res/values-ur/strings.xml +++ b/tools/recovery_l10n/res/values-ur/strings.xml @@ -6,14 +6,9 @@ "کوئی کمانڈ نہیں ہے" "خرابی!" "سیکیورٹی اپ ڈیٹ انسٹال ہو رہی ہے" - - - - - - - - - - + "‏Android سسٹم لوڈ نہیں کیا جا سکتا۔ آپ کا ڈیٹا خراب ہو سکتا ہے۔ اگر آپ کو مستقل یہ پیغام موصول ہوتا ہے تو آپ کو فیکٹری ڈیٹا کی دوبارہ ترتیب انجام دینے اور اس آلہ پر اسٹور کردہ سبھی صارف ڈیٹا کو مٹانے کی ضرورت پڑ سکتی ہے۔" + "دوبارہ کوشش کریں" + "فیکٹری ڈیٹا کی دوبارہ ترتیب" + "سبھی صارف ڈیٹا صاف کریں؟\n\n اسے کالعدم نہیں کیا جا سکتا!" + "منسوخ کریں" diff --git a/tools/recovery_l10n/res/values-uz/strings.xml b/tools/recovery_l10n/res/values-uz/strings.xml index 676be7a6..9bde4c6b 100644 --- a/tools/recovery_l10n/res/values-uz/strings.xml +++ b/tools/recovery_l10n/res/values-uz/strings.xml @@ -6,14 +6,9 @@ "Buyruq yo‘q" "Xato!" "Xavfsizlik yangilanishi o‘rnatilmoqda" - - - - - - - - - - + "Android tizimi yuklanmadi. Maʼlumotlaringiz buzuq shekilli. Yana shu xabarni olsangiz, zavod sozlamalarini tiklashingiz va bu qurilmadagi barcha maʼlumotlarni tozalab tashlashingiz lozim." + "Qayta urinish" + "Zavod sozlamalarini tiklash" + "Barcha maʼlumotlar tozalab tashlansinmi?\n\n ULARNI TIKLASH IMKONSIZ!" + "Bekor qilish" diff --git a/tools/recovery_l10n/res/values-vi/strings.xml b/tools/recovery_l10n/res/values-vi/strings.xml index fce6cd90..3753394e 100644 --- a/tools/recovery_l10n/res/values-vi/strings.xml +++ b/tools/recovery_l10n/res/values-vi/strings.xml @@ -6,14 +6,9 @@ "Không có lệnh nào" "Lỗi!" "Đang cài đặt bản cập nhật bảo mật" - - - - - - - - - - + "Không thể tải hệ thống Android. Dữ liệu của bạn có thể bị hỏng. Nếu tiếp tục thấy thông báo này, bạn có thể cần phải thiết lập lại dữ liệu ban đầu và xóa tất cả dữ liệu người dùng lưu trữ trên thiết bị này." + "Thử lại" + "Thiết lập lại dữ liệu ban đầu" + "Xóa sạch tất cả dữ liệu người dùng?\n\n KHÔNG THỂ HOÀN TÁC THAO TÁC NÀY!" + "Hủy" diff --git a/tools/recovery_l10n/res/values-zh-rCN/strings.xml b/tools/recovery_l10n/res/values-zh-rCN/strings.xml index 66c876d7..ab1fdbbc 100644 --- a/tools/recovery_l10n/res/values-zh-rCN/strings.xml +++ b/tools/recovery_l10n/res/values-zh-rCN/strings.xml @@ -6,14 +6,9 @@ "无命令" "出错了!" "正在安装安全更新" - - - - - - - - - - + "无法加载 Android 系统。您的数据可能已损坏。如果系统仍然显示这条消息,您可能需要恢复出厂设置,并清空存储在此设备上的所有用户数据。" + "重试" + "恢复出厂设置" + "是否清除所有用户数据?\n\n此操作无法撤消!" + "取消" diff --git a/tools/recovery_l10n/res/values-zh-rHK/strings.xml b/tools/recovery_l10n/res/values-zh-rHK/strings.xml index 5dc38d7a..55ce31e9 100644 --- a/tools/recovery_l10n/res/values-zh-rHK/strings.xml +++ b/tools/recovery_l10n/res/values-zh-rHK/strings.xml @@ -6,14 +6,9 @@ "沒有指令" "錯誤!" "正在安裝安全性更新" - - - - - - - - - - + "無法載入 Android 系統。您的資料可能已損壞。如您繼續收到此訊息,則可能需要將裝置回復原廠設定,並清除儲存在裝置上的所有使用者資料。" + "再試一次" + "回復原廠設定" + "要清除所有使用者資料嗎?\n\n這項操作無法復原!" + "取消" diff --git a/tools/recovery_l10n/res/values-zh-rTW/strings.xml b/tools/recovery_l10n/res/values-zh-rTW/strings.xml index e94038b2..0a777a6e 100644 --- a/tools/recovery_l10n/res/values-zh-rTW/strings.xml +++ b/tools/recovery_l10n/res/values-zh-rTW/strings.xml @@ -6,14 +6,9 @@ "沒有指令" "錯誤!" "正在安裝安全性更新" - - - - - - - - - - + "無法載入 Android 系統。你的資料可能已經損毀。如果系統持續顯示這則訊息,你可能必須恢復原廠設定,並清除裝置上儲存的所有使用者資料。" + "再試一次" + "恢復原廠設定" + "要抹除所有使用者資料嗎?\n\n請注意,一旦抹除就無法復原!" + "取消" diff --git a/tools/recovery_l10n/res/values-zu/strings.xml b/tools/recovery_l10n/res/values-zu/strings.xml index eef2293d..4667dac4 100644 --- a/tools/recovery_l10n/res/values-zu/strings.xml +++ b/tools/recovery_l10n/res/values-zu/strings.xml @@ -6,14 +6,9 @@ "Awukho umyalo" "Iphutha!" "Ifaka isibuyekezo sokuphepha" - - - - - - - - - - + "Ayikwazi ukulayisha isistimu ye-Android. Idatha yakho kungenzeka yonakele. Uma uqhubeka ukuthola lo mlayezo, kungenzeka kumele wenze ukusethwa kabusha kwasekuqaleni kwedatha uphinde usule yonke idatha yomsebenzisi egcinwe kule divayisi." + "Zama futhi" + "Ukuhlela kabusha idatha yasembonini" + "Sula yonke idatha yomsebenzisi?\n\n LOKHU AKUKWAZI UKUHLEHLISWA!" + "Khansela" From d5c7fb5e6efb98a4b8072be3c1e7e505b3344466 Mon Sep 17 00:00:00 2001 From: koushik panuganti Date: Wed, 12 Dec 2018 10:39:44 -0800 Subject: [PATCH 07/28] Migrate bootable/recovery to androidx.test See go/jetpack-test-android-migration Test: make checkbuild Change-Id: I0740a2205e6b3893ba292cd841592ea85071eefb --- updater_sample/tests/Android.bp | 2 +- updater_sample/tests/AndroidManifest.xml | 2 +- .../android/systemupdatersample/UpdateConfigTest.java | 7 ++++--- .../android/systemupdatersample/UpdateManagerTest.java | 7 ++++--- .../systemupdatersample/util/FileDownloaderTest.java | 7 ++++--- .../android/systemupdatersample/util/PayloadSpecsTest.java | 7 ++++--- .../systemupdatersample/util/UpdateConfigsTest.java | 4 ++-- 7 files changed, 20 insertions(+), 16 deletions(-) diff --git a/updater_sample/tests/Android.bp b/updater_sample/tests/Android.bp index 7867770a..e4344051 100644 --- a/updater_sample/tests/Android.bp +++ b/updater_sample/tests/Android.bp @@ -23,7 +23,7 @@ android_test { ], static_libs: [ - "android-support-test", + "androidx.test.runner", "mockito-target-minus-junit4", "guava", ], diff --git a/updater_sample/tests/AndroidManifest.xml b/updater_sample/tests/AndroidManifest.xml index 76af5f1a..a2da3f73 100644 --- a/updater_sample/tests/AndroidManifest.xml +++ b/updater_sample/tests/AndroidManifest.xml @@ -26,7 +26,7 @@ - diff --git a/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateConfigTest.java b/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateConfigTest.java index 48d0e424..ae666830 100644 --- a/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateConfigTest.java +++ b/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateConfigTest.java @@ -21,9 +21,10 @@ import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.example.android.systemupdatersample.tests.R; import com.google.common.io.CharStreams; diff --git a/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateManagerTest.java b/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateManagerTest.java index e05ad290..6bef6de0 100644 --- a/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateManagerTest.java +++ b/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateManagerTest.java @@ -26,9 +26,10 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.os.UpdateEngine; import android.os.UpdateEngineCallback; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.example.android.systemupdatersample.tests.R; import com.example.android.systemupdatersample.util.PayloadSpecs; diff --git a/updater_sample/tests/src/com/example/android/systemupdatersample/util/FileDownloaderTest.java b/updater_sample/tests/src/com/example/android/systemupdatersample/util/FileDownloaderTest.java index a136ff0e..ede24577 100644 --- a/updater_sample/tests/src/com/example/android/systemupdatersample/util/FileDownloaderTest.java +++ b/updater_sample/tests/src/com/example/android/systemupdatersample/util/FileDownloaderTest.java @@ -19,9 +19,10 @@ package com.example.android.systemupdatersample.util; import static org.junit.Assert.assertEquals; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.example.android.systemupdatersample.tests.R; diff --git a/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java b/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java index 03086930..9931e7bf 100644 --- a/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java +++ b/updater_sample/tests/src/com/example/android/systemupdatersample/util/PayloadSpecsTest.java @@ -22,9 +22,10 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.example.android.systemupdatersample.PayloadSpec; import com.example.android.systemupdatersample.tests.R; diff --git a/updater_sample/tests/src/com/example/android/systemupdatersample/util/UpdateConfigsTest.java b/updater_sample/tests/src/com/example/android/systemupdatersample/util/UpdateConfigsTest.java index 4ccae938..4a967a95 100644 --- a/updater_sample/tests/src/com/example/android/systemupdatersample/util/UpdateConfigsTest.java +++ b/updater_sample/tests/src/com/example/android/systemupdatersample/util/UpdateConfigsTest.java @@ -18,8 +18,8 @@ package com.example.android.systemupdatersample.util; import static org.junit.Assert.assertArrayEquals; -import android.support.test.filters.SmallTest; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.example.android.systemupdatersample.UpdateConfig; From 75f4073baf4b480f3d27da6b181e4aec6e30856d Mon Sep 17 00:00:00 2001 From: Zhomart Mukhamejanov Date: Fri, 14 Dec 2018 09:36:32 -0800 Subject: [PATCH 08/28] Add PrepareUpdateService. It's moved from PrepareStreamingService intent service. Now PrepareUpdateService takes an UpdateConfig and builds PayloadSpec for UpdateEngine for both streaming and non-streaming update. It allows us to do all preparations in intent service's thread, without blocking UI. We will also add checksum verification to PrepareUpdateService. Test: device, junit Bug: 77150191 Change-Id: Iea69acd9aa41e17538c26aff60f7598093ca7744 --- updater_sample/AndroidManifest.xml | 2 +- updater_sample/README.md | 6 +- .../systemupdatersample/UpdateManager.java | 79 +++++++------------ ...Service.java => PrepareUpdateService.java} | 55 +++++++------ .../systemupdatersample/ui/MainActivity.java | 4 +- .../util/FileDownloader.java | 2 +- updater_sample/tests/Android.bp | 1 + .../UpdateManagerTest.java | 75 ++++++++++-------- 8 files changed, 111 insertions(+), 113 deletions(-) rename updater_sample/src/com/example/android/systemupdatersample/services/{PrepareStreamingService.java => PrepareUpdateService.java} (85%) diff --git a/updater_sample/AndroidManifest.xml b/updater_sample/AndroidManifest.xml index 18d8425e..0a251161 100644 --- a/updater_sample/AndroidManifest.xml +++ b/updater_sample/AndroidManifest.xml @@ -33,7 +33,7 @@ - + diff --git a/updater_sample/README.md b/updater_sample/README.md index f9c3fb8e..5894cf8c 100644 --- a/updater_sample/README.md +++ b/updater_sample/README.md @@ -220,7 +220,7 @@ privileged system app, so it's granted the required permissions to access - [x] Add Sample app update state (separate from update_engine status) - [x] Add smart update completion detection using onStatusUpdate - [x] Add pause/resume demo -- [x] Verify system partition checksum for package +- [-] Verify system partition checksum for package ## Running tests @@ -235,8 +235,8 @@ privileged system app, so it's granted the required permissions to access 5. Run a test file ``` adb shell am instrument \ - -w com.example.android.systemupdatersample.tests/android.support.test.runner.AndroidJUnitRunner \ - -c com.example.android.systemupdatersample.util.PayloadSpecsTest + -w -e class com.example.android.systemupdatersample.UpdateManagerTest#applyUpdate_appliesPayloadToUpdateEngine \ + com.example.android.systemupdatersample.tests/android.support.test.runner.AndroidJUnitRunner ``` diff --git a/updater_sample/src/com/example/android/systemupdatersample/UpdateManager.java b/updater_sample/src/com/example/android/systemupdatersample/UpdateManager.java index 12a8f3f5..c02e6084 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/UpdateManager.java +++ b/updater_sample/src/com/example/android/systemupdatersample/UpdateManager.java @@ -17,19 +17,18 @@ package com.example.android.systemupdatersample; import android.content.Context; +import android.os.Handler; import android.os.UpdateEngine; import android.os.UpdateEngineCallback; import android.util.Log; -import com.example.android.systemupdatersample.services.PrepareStreamingService; -import com.example.android.systemupdatersample.util.PayloadSpecs; +import com.example.android.systemupdatersample.services.PrepareUpdateService; import com.example.android.systemupdatersample.util.UpdateEngineErrorCodes; import com.example.android.systemupdatersample.util.UpdateEngineProperties; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.util.concurrent.AtomicDouble; -import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -50,11 +49,10 @@ public class UpdateManager { private static final String TAG = "UpdateManager"; /** HTTP Header: User-Agent; it will be sent to the server when streaming the payload. */ - private static final String HTTP_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + static final String HTTP_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36"; private final UpdateEngine mUpdateEngine; - private final PayloadSpecs mPayloadSpecs; private AtomicInteger mUpdateEngineStatus = new AtomicInteger(UpdateEngine.UpdateStatusConstants.IDLE); @@ -84,9 +82,15 @@ public class UpdateManager { private final UpdateManager.UpdateEngineCallbackImpl mUpdateEngineCallback = new UpdateManager.UpdateEngineCallbackImpl(); - public UpdateManager(UpdateEngine updateEngine, PayloadSpecs payloadSpecs) { + private final Handler mHandler; + + /** + * @param updateEngine UpdateEngine instance. + * @param handler Handler for {@link PrepareUpdateService} intent service. + */ + public UpdateManager(UpdateEngine updateEngine, Handler handler) { this.mUpdateEngine = updateEngine; - this.mPayloadSpecs = payloadSpecs; + this.mHandler = handler; } /** @@ -293,45 +297,17 @@ public class UpdateManager { mManualSwitchSlotRequired.set(false); } - if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) { - applyAbNonStreamingUpdate(config); - } else { - applyAbStreamingUpdate(context, config); - } - } - - private void applyAbNonStreamingUpdate(UpdateConfig config) - throws UpdaterState.InvalidTransitionException { - UpdateData.Builder builder = UpdateData.builder() - .setExtraProperties(prepareExtraProperties(config)); - - try { - builder.setPayload(mPayloadSpecs.forNonStreaming(config.getUpdatePackageFile())); - } catch (IOException e) { - Log.e(TAG, "Error creating payload spec", e); - setUpdaterState(UpdaterState.ERROR); - return; - } - updateEngineApplyPayload(builder.build()); - } - - private void applyAbStreamingUpdate(Context context, UpdateConfig config) { - UpdateData.Builder builder = UpdateData.builder() - .setExtraProperties(prepareExtraProperties(config)); - - Log.d(TAG, "Starting PrepareStreamingService"); - PrepareStreamingService.startService(context, config, (code, payloadSpec) -> { - if (code == PrepareStreamingService.RESULT_CODE_SUCCESS) { - builder.setPayload(payloadSpec); - builder.addExtraProperty("USER_AGENT=" + HTTP_USER_AGENT); - config.getAbConfig() - .getAuthorization() - .ifPresent(s -> builder.addExtraProperty("AUTHORIZATION=" + s)); - updateEngineApplyPayload(builder.build()); - } else { - Log.e(TAG, "PrepareStreamingService failed, result code is " + code); + Log.d(TAG, "Starting PrepareUpdateService"); + PrepareUpdateService.startService(context, config, mHandler, (code, payloadSpec) -> { + if (code != PrepareUpdateService.RESULT_CODE_SUCCESS) { + Log.e(TAG, "PrepareUpdateService failed, result code is " + code); setUpdaterStateSilent(UpdaterState.ERROR); + return; } + updateEngineApplyPayload(UpdateData.builder() + .setExtraProperties(prepareExtraProperties(config)) + .setPayload(payloadSpec) + .build()); }); } @@ -343,6 +319,12 @@ public class UpdateManager { // User will enable it manually by clicking "Switch Slot" button on the screen. extraProperties.add(UpdateEngineProperties.PROPERTY_DISABLE_SWITCH_SLOT_ON_REBOOT); } + if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_STREAMING) { + extraProperties.add("USER_AGENT=" + HTTP_USER_AGENT); + config.getAbConfig() + .getAuthorization() + .ifPresent(s -> extraProperties.add("AUTHORIZATION=" + s)); + } return extraProperties; } @@ -497,14 +479,14 @@ public class UpdateManager { * system/update_engine/binder_service_android.cc in * function BinderUpdateEngineAndroidService::bind). * - * @param status one of {@link UpdateEngine.UpdateStatusConstants}. + * @param status one of {@link UpdateEngine.UpdateStatusConstants}. * @param progress a number from 0.0 to 1.0. */ private void onStatusUpdate(int status, float progress) { Log.d(TAG, String.format( - "onStatusUpdate invoked, status=%s, progress=%.2f", - status, - progress)); + "onStatusUpdate invoked, status=%s, progress=%.2f", + status, + progress)); int previousStatus = mUpdateEngineStatus.get(); mUpdateEngineStatus.set(status); @@ -555,7 +537,6 @@ public class UpdateManager { } /** - * * Contains update data - PayloadSpec and extra properties list. * *

{@code mPayload} contains url, offset and size to {@code PAYLOAD_BINARY_FILE_NAME}. diff --git a/updater_sample/src/com/example/android/systemupdatersample/services/PrepareStreamingService.java b/updater_sample/src/com/example/android/systemupdatersample/services/PrepareUpdateService.java similarity index 85% rename from updater_sample/src/com/example/android/systemupdatersample/services/PrepareStreamingService.java rename to updater_sample/src/com/example/android/systemupdatersample/services/PrepareUpdateService.java index 93140485..06581bee 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/services/PrepareStreamingService.java +++ b/updater_sample/src/com/example/android/systemupdatersample/services/PrepareUpdateService.java @@ -28,6 +28,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.RecoverySystem; import android.os.ResultReceiver; +import android.os.UpdateEngine; import android.util.Log; import com.example.android.systemupdatersample.PayloadSpec; @@ -49,10 +50,10 @@ import java.util.Optional; * without downloading the whole package. And it constructs {@link PayloadSpec}. * All this work required to install streaming A/B updates. * - * PrepareStreamingService runs on it's own thread. It will notify activity + * PrepareUpdateService runs on it's own thread. It will notify activity * using interface {@link UpdateResultCallback} when update is ready to install. */ -public class PrepareStreamingService extends IntentService { +public class PrepareUpdateService extends IntentService { /** * UpdateResultCallback result codes. @@ -61,62 +62,63 @@ public class PrepareStreamingService extends IntentService { public static final int RESULT_CODE_ERROR = 1; /** - * This interface is used to send results from {@link PrepareStreamingService} to + * Extra params that will be sent to IntentService. + */ + public static final String EXTRA_PARAM_CONFIG = "config"; + public static final String EXTRA_PARAM_RESULT_RECEIVER = "result-receiver"; + + /** + * This interface is used to send results from {@link PrepareUpdateService} to * {@code MainActivity}. */ public interface UpdateResultCallback { - /** * Invoked when files are downloaded and payload spec is constructed. * - * @param resultCode result code, values are defined in {@link PrepareStreamingService} + * @param resultCode result code, values are defined in {@link PrepareUpdateService} * @param payloadSpec prepared payload spec for streaming update */ void onReceiveResult(int resultCode, PayloadSpec payloadSpec); } /** - * Starts PrepareStreamingService. + * Starts PrepareUpdateService. * - * @param context application context - * @param config update config + * @param context application context + * @param config update config * @param resultCallback callback that will be called when the update is ready to be installed */ public static void startService(Context context, UpdateConfig config, + Handler handler, UpdateResultCallback resultCallback) { - Log.d(TAG, "Starting PrepareStreamingService"); - ResultReceiver receiver = new CallbackResultReceiver(new Handler(), resultCallback); - Intent intent = new Intent(context, PrepareStreamingService.class); + Log.d(TAG, "Starting PrepareUpdateService"); + ResultReceiver receiver = new CallbackResultReceiver(handler, resultCallback); + Intent intent = new Intent(context, PrepareUpdateService.class); intent.putExtra(EXTRA_PARAM_CONFIG, config); intent.putExtra(EXTRA_PARAM_RESULT_RECEIVER, receiver); context.startService(intent); } - public PrepareStreamingService() { + public PrepareUpdateService() { super(TAG); } - private static final String TAG = "PrepareStreamingService"; - - /** - * Extra params that will be sent from Activity to IntentService. - */ - private static final String EXTRA_PARAM_CONFIG = "config"; - private static final String EXTRA_PARAM_RESULT_RECEIVER = "result-receiver"; + private static final String TAG = "PrepareUpdateService"; /** * The files that should be downloaded before streaming. */ private static final ImmutableSet PRE_STREAMING_FILES_SET = ImmutableSet.of( - PackageFiles.CARE_MAP_FILE_NAME, - PackageFiles.COMPATIBILITY_ZIP_FILE_NAME, - PackageFiles.METADATA_FILE_NAME, - PackageFiles.PAYLOAD_PROPERTIES_FILE_NAME + PackageFiles.CARE_MAP_FILE_NAME, + PackageFiles.COMPATIBILITY_ZIP_FILE_NAME, + PackageFiles.METADATA_FILE_NAME, + PackageFiles.PAYLOAD_PROPERTIES_FILE_NAME ); private final PayloadSpecs mPayloadSpecs = new PayloadSpecs(); + private final UpdateEngine mUpdateEngine = new UpdateEngine(); @Override protected void onHandleIntent(Intent intent) { @@ -142,6 +144,10 @@ public class PrepareStreamingService extends IntentService { private PayloadSpec execute(UpdateConfig config) throws IOException, PreparationFailedException { + if (config.getInstallType() == UpdateConfig.AB_INSTALL_TYPE_NON_STREAMING) { + return mPayloadSpecs.forNonStreaming(config.getUpdatePackageFile()); + } + downloadPreStreamingFiles(config, OTA_PACKAGE_DIR); Optional payloadBinary = @@ -176,6 +182,7 @@ public class PrepareStreamingService extends IntentService { * Downloads files defined in {@link UpdateConfig#getAbConfig()} * and exists in {@code PRE_STREAMING_FILES_SET}, and put them * in directory {@code dir}. + * * @throws IOException when can't download a file */ private void downloadPreStreamingFiles(UpdateConfig config, String dir) @@ -212,7 +219,7 @@ public class PrepareStreamingService extends IntentService { } /** - * Used by {@link PrepareStreamingService} to pass {@link PayloadSpec} + * Used by {@link PrepareUpdateService} to pass {@link PayloadSpec} * to {@link UpdateResultCallback#onReceiveResult}. */ private static class CallbackResultReceiver extends ResultReceiver { diff --git a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java index fc9fddd7..6d1e4c35 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java +++ b/updater_sample/src/com/example/android/systemupdatersample/ui/MainActivity.java @@ -21,6 +21,7 @@ import android.app.AlertDialog; import android.graphics.Color; import android.os.Build; import android.os.Bundle; +import android.os.Handler; import android.os.UpdateEngine; import android.util.Log; import android.view.View; @@ -34,7 +35,6 @@ import com.example.android.systemupdatersample.R; import com.example.android.systemupdatersample.UpdateConfig; import com.example.android.systemupdatersample.UpdateManager; import com.example.android.systemupdatersample.UpdaterState; -import com.example.android.systemupdatersample.util.PayloadSpecs; import com.example.android.systemupdatersample.util.UpdateConfigs; import com.example.android.systemupdatersample.util.UpdateEngineErrorCodes; import com.example.android.systemupdatersample.util.UpdateEngineStatuses; @@ -67,7 +67,7 @@ public class MainActivity extends Activity { private List mConfigs; private final UpdateManager mUpdateManager = - new UpdateManager(new UpdateEngine(), new PayloadSpecs()); + new UpdateManager(new UpdateEngine(), new Handler()); @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java b/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java index ddd0919b..0f9083d2 100644 --- a/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java +++ b/updater_sample/src/com/example/android/systemupdatersample/util/FileDownloader.java @@ -30,7 +30,7 @@ import java.net.URLConnection; * Downloads chunk of a file from given url using {@code offset} and {@code size}, * and saves to a given location. * - * In real-life application this helper class should download from HTTP Server, + * In a real-life application this helper class should download from HTTP Server, * but in this sample app it will only download from a local file. */ public final class FileDownloader { diff --git a/updater_sample/tests/Android.bp b/updater_sample/tests/Android.bp index e4344051..806babd9 100644 --- a/updater_sample/tests/Android.bp +++ b/updater_sample/tests/Android.bp @@ -24,6 +24,7 @@ android_test { static_libs: [ "androidx.test.runner", + "androidx.test.rules", "mockito-target-minus-junit4", "guava", ], diff --git a/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateManagerTest.java b/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateManagerTest.java index 6bef6de0..af891f00 100644 --- a/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateManagerTest.java +++ b/updater_sample/tests/src/com/example/android/systemupdatersample/UpdateManagerTest.java @@ -18,21 +18,26 @@ package com.example.android.systemupdatersample; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.ResultReceiver; import android.os.UpdateEngine; import android.os.UpdateEngineCallback; import androidx.test.InstrumentationRegistry; +import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.example.android.systemupdatersample.services.PrepareUpdateService; import com.example.android.systemupdatersample.tests.R; -import com.example.android.systemupdatersample.util.PayloadSpecs; import com.google.common.collect.ImmutableList; import com.google.common.io.CharStreams; @@ -44,7 +49,6 @@ import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; -import java.io.File; import java.io.IOException; import java.io.InputStreamReader; @@ -61,49 +65,39 @@ public class UpdateManagerTest { @Mock private UpdateEngine mUpdateEngine; @Mock - private PayloadSpecs mPayloadSpecs; + private Context mMockContext; private UpdateManager mSubject; - private Context mContext; - private UpdateConfig mNonStreamingUpdate003; + private Context mTestContext; + private UpdateConfig mStreamingUpdate002; @Before public void setUp() throws Exception { - mContext = InstrumentationRegistry.getContext(); - mSubject = new UpdateManager(mUpdateEngine, mPayloadSpecs); - mNonStreamingUpdate003 = - UpdateConfig.fromJson(readResource(R.raw.update_config_003_nonstream)); + mTestContext = InstrumentationRegistry.getContext(); + mSubject = new UpdateManager(mUpdateEngine, null); + mStreamingUpdate002 = + UpdateConfig.fromJson(readResource(R.raw.update_config_002_stream)); } @Test public void applyUpdate_appliesPayloadToUpdateEngine() throws Exception { - PayloadSpec payload = buildMockPayloadSpec(); - when(mPayloadSpecs.forNonStreaming(any(File.class))).thenReturn(payload); - when(mUpdateEngine.bind(any(UpdateEngineCallback.class))).thenAnswer(answer -> { - // When UpdateManager is bound to update_engine, it passes - // UpdateEngineCallback as a callback to update_engine. - UpdateEngineCallback callback = answer.getArgument(0); - callback.onStatusUpdate( - UpdateEngine.UpdateStatusConstants.IDLE, - /*engineProgress*/ 0.0f); - return null; - }); - - mSubject.bind(); - mSubject.applyUpdate(null, mNonStreamingUpdate003); + mockContextStartServiceAnswer(buildMockPayloadSpec()); + mSubject.applyUpdate(mMockContext, mStreamingUpdate002); verify(mUpdateEngine).applyPayload( "file://blah", 120, 340, - new String[] { - "SWITCH_SLOT_ON_REBOOT=0" // ab_config.force_switch_slot = false + new String[]{ + "SWITCH_SLOT_ON_REBOOT=0", // ab_config.force_switch_slot = false + "USER_AGENT=" + UpdateManager.HTTP_USER_AGENT }); } @Test - public void stateIsRunningAndEngineStatusIsIdle_reApplyLastUpdate() throws Exception { - PayloadSpec payload = buildMockPayloadSpec(); - when(mPayloadSpecs.forNonStreaming(any(File.class))).thenReturn(payload); + @UiThreadTest + public void stateIsRunningAndEngineStatusIsIdle_reApplyLastUpdate() throws Throwable { + mockContextStartServiceAnswer(buildMockPayloadSpec()); + // UpdateEngine always returns IDLE status. when(mUpdateEngine.bind(any(UpdateEngineCallback.class))).thenAnswer(answer -> { // When UpdateManager is bound to update_engine, it passes // UpdateEngineCallback as a callback to update_engine. @@ -115,21 +109,36 @@ public class UpdateManagerTest { }); mSubject.bind(); - mSubject.applyUpdate(null, mNonStreamingUpdate003); + mSubject.applyUpdate(mMockContext, mStreamingUpdate002); mSubject.unbind(); mSubject.bind(); // re-bind - now it should re-apply last update assertEquals(mSubject.getUpdaterState(), UpdaterState.RUNNING); - // it should be called 2 times verify(mUpdateEngine, times(2)).applyPayload( "file://blah", 120, 340, - new String[] { - "SWITCH_SLOT_ON_REBOOT=0" // ab_config.force_switch_slot = false + new String[]{ + "SWITCH_SLOT_ON_REBOOT=0", // ab_config.force_switch_slot = false + "USER_AGENT=" + UpdateManager.HTTP_USER_AGENT }); } + private void mockContextStartServiceAnswer(PayloadSpec payloadSpec) { + doAnswer(args -> { + Intent intent = args.getArgument(0); + ResultReceiver resultReceiver = intent.getParcelableExtra( + PrepareUpdateService.EXTRA_PARAM_RESULT_RECEIVER); + Bundle b = new Bundle(); + b.putSerializable( + /* PrepareUpdateService.CallbackResultReceiver.BUNDLE_PARAM_PAYLOAD_SPEC */ + "payload-spec", + payloadSpec); + resultReceiver.send(PrepareUpdateService.RESULT_CODE_SUCCESS, b); + return null; + }).when(mMockContext).startService(any(Intent.class)); + } + private PayloadSpec buildMockPayloadSpec() { PayloadSpec payload = mock(PayloadSpec.class); when(payload.getUrl()).thenReturn("file://blah"); @@ -141,7 +150,7 @@ public class UpdateManagerTest { private String readResource(int id) throws IOException { return CharStreams.toString(new InputStreamReader( - mContext.getResources().openRawResource(id))); + mTestContext.getResources().openRawResource(id))); } } From 79f4680e4d706e43e58b4258ea47cab4fd81c526 Mon Sep 17 00:00:00 2001 From: Suren Baghdasaryan Date: Sun, 13 Jan 2019 12:48:24 -0800 Subject: [PATCH 09/28] Add libprocessgroup dependency Because set_sched_policy is moved into libprocessgroup an additional dependency is requred for recovery_component_test to build. Exempt-From-Owner-Approval: janitorial Bug: 111307099 Test: builds, boots Change-Id: I7cf75e473ee1e2837940606c71d15be26db0c3f2 Signed-off-by: Suren Baghdasaryan --- tests/Android.bp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Android.bp b/tests/Android.bp index 1d6a056f..898ed7d6 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -29,6 +29,7 @@ cc_defaults { "libcutils", "liblog", "libpng", + "libprocessgroup", "libselinux", "libz", "libziparchive", From 25bfb1bea9214ffe6f6969036322a45785cb6f7f Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Fri, 1 Feb 2019 21:22:54 -0800 Subject: [PATCH 10/28] Import translations. DO NOT MERGE Change-Id: Id46b7b2d7bf8f9fe1edd9bb90cc0e3d279f3472c Auto-generated-cl: translation import --- .../res/values-en-rXC/strings.xml | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/recovery_l10n/res/values-en-rXC/strings.xml b/tools/recovery_l10n/res/values-en-rXC/strings.xml index 61390f11..18b077f2 100644 --- a/tools/recovery_l10n/res/values-en-rXC/strings.xml +++ b/tools/recovery_l10n/res/values-en-rXC/strings.xml @@ -1,14 +1,14 @@ - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎Installing system update‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎Erasing‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎No command‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎Error!‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎Installing security update‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎Cannot 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.‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎Try again‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎Factory data reset‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‎Wipe all user data?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ THIS CAN NOT BE UNDONE!‎‏‎‎‏‎" - "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎Cancel‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‏‎‎‏‏‏‏‎Installing system update‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‎‎‎‎Erasing‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‏‎‎‎‏‏‎‎‎‏‏‏‎No command‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‎‎‎‏‎‎‏‎‏‎‏‎‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‎‎‎‎‎‎‎Error!‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‎‎Installing security update‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‎‏‏‏‎‎Cannot 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.‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‎‏‎‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‏‎‎‎‎Try again‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎Factory data reset‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‎‎‎‏‎‏‏‎‎‏‎‎Wipe all user data?‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎ THIS CAN NOT BE UNDONE!‎‏‎‎‏‎" + "‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‏‎‎‎‎Cancel‎‏‎‎‏‎" From aab4b829f46ad6aaeaf86a4886b95a10516dc394 Mon Sep 17 00:00:00 2001 From: xunchang Date: Wed, 13 Mar 2019 14:21:48 -0700 Subject: [PATCH 11/28] Update_verifier: Remove the support for legacy text format CareMap We have already switched to the protobuf format for new builds, and the downgrade packages will require a data wipe. So it should be safe to drop the support for text format. This also helps to save the issue when users sideload a package with a pending OTA, because the new CareMap contains the fingerprint of the intended build. Bug: 128536706 Test: unit tests pass, run update_verifier with legacy CareMap Change-Id: I1c4d0e54ec591f16cc0a65dac76767725ff9e7c4 (cherry picked from commit aaa6103ae72985d061432745e668df9ca29d6ac2) --- tests/component/update_verifier_test.cpp | 44 ++--------------- .../include/update_verifier/update_verifier.h | 3 -- update_verifier/update_verifier.cpp | 47 ++----------------- 3 files changed, 6 insertions(+), 88 deletions(-) diff --git a/tests/component/update_verifier_test.cpp b/tests/component/update_verifier_test.cpp index 0a594037..e27e58c2 100644 --- a/tests/component/update_verifier_test.cpp +++ b/tests/component/update_verifier_test.cpp @@ -98,7 +98,7 @@ TEST_F(UpdateVerifierTest, verify_image_no_care_map) { ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_smoke) { +TEST_F(UpdateVerifierTest, verify_image_text_format) { // This test relies on dm-verity support. if (!verity_supported) { GTEST_LOG_(INFO) << "Test skipped on devices without dm-verity support."; @@ -107,52 +107,14 @@ TEST_F(UpdateVerifierTest, verify_image_smoke) { std::string content = "system\n2,0,1"; ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_)); - ASSERT_TRUE(verifier_.ParseCareMap()); - ASSERT_TRUE(verifier_.VerifyPartitions()); - - // Leading and trailing newlines should be accepted. - ASSERT_TRUE(android::base::WriteStringToFile("\n" + content + "\n\n", care_map_txt_)); - ASSERT_TRUE(verifier_.ParseCareMap()); - ASSERT_TRUE(verifier_.VerifyPartitions()); + // CareMap in text format is no longer supported. + ASSERT_FALSE(verifier_.ParseCareMap()); } TEST_F(UpdateVerifierTest, verify_image_empty_care_map) { ASSERT_FALSE(verifier_.ParseCareMap()); } -TEST_F(UpdateVerifierTest, verify_image_wrong_lines) { - // The care map file can have only 2 / 4 / 6 lines. - ASSERT_TRUE(android::base::WriteStringToFile("line1", care_map_txt_)); - ASSERT_FALSE(verifier_.ParseCareMap()); - - ASSERT_TRUE(android::base::WriteStringToFile("line1\nline2\nline3", care_map_txt_)); - ASSERT_FALSE(verifier_.ParseCareMap()); -} - -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; - } - - std::string content = "system\n2,1,0"; - ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_)); - ASSERT_FALSE(verifier_.ParseCareMap()); -} - -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; - } - - std::string content = "/dev/block/bootdevice/by-name/system\n2,1,0"; - ASSERT_TRUE(android::base::WriteStringToFile(content, care_map_txt_)); - ASSERT_FALSE(verifier_.ParseCareMap()); -} - TEST_F(UpdateVerifierTest, verify_image_protobuf_care_map_smoke) { // This test relies on dm-verity support. if (!verity_supported) { diff --git a/update_verifier/include/update_verifier/update_verifier.h b/update_verifier/include/update_verifier/update_verifier.h index b00890e8..4c64b1ea 100644 --- a/update_verifier/include/update_verifier/update_verifier.h +++ b/update_verifier/include/update_verifier/update_verifier.h @@ -50,9 +50,6 @@ class UpdateVerifier { private: friend class UpdateVerifierTest; - // Parses the legacy care_map.txt in plain text format. - bool ParseCareMapPlainText(const std::string& content); - // Finds all the dm-enabled partitions, and returns a map of . std::map FindDmPartitions(); diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp index 5e5eac7a..28c3fe78 100644 --- a/update_verifier/update_verifier.cpp +++ b/update_verifier/update_verifier.cpp @@ -68,6 +68,7 @@ using android::hardware::boot::V1_0::IBootControl; using android::hardware::boot::V1_0::BoolResult; using android::hardware::boot::V1_0::CommandResult; +// TODO(xunchang) remove the prefix and use a default path instead. constexpr const char* kDefaultCareMapPrefix = "/data/ota_package/care_map"; // Find directories in format of "/sys/block/dm-X". @@ -196,51 +197,13 @@ bool UpdateVerifier::VerifyPartitions() { return true; } -bool UpdateVerifier::ParseCareMapPlainText(const std::string& content) { - // care_map file has up to six lines, where every two lines make a pair. Within each pair, the - // first line has the partition name (e.g. "system"), while the second line holds the ranges of - // all the blocks to verify. - auto lines = android::base::Split(android::base::Trim(content), "\n"); - if (lines.size() != 2 && lines.size() != 4 && lines.size() != 6) { - LOG(WARNING) << "Invalid lines in care_map: found " << lines.size() - << " lines, expecting 2 or 4 or 6 lines."; - return false; - } - - for (size_t i = 0; i < lines.size(); i += 2) { - const std::string& partition_name = lines[i]; - const std::string& range_str = lines[i + 1]; - // 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(partition_name, "/dev/block/")) { - LOG(WARNING) << "Found legacy care_map.txt; skipped."; - 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 - // block range with the first number included in range but second number not included. - // For example '4,64536,65343,74149,74150' represents: [64536,65343) and [74149,74150). - RangeSet ranges = RangeSet::Parse(range_str); - if (!ranges) { - LOG(WARNING) << "Error parsing RangeSet string " << range_str; - return false; - } - - partition_map_.emplace(partition_name, ranges); - } - - return true; -} - bool UpdateVerifier::ParseCareMap() { partition_map_.clear(); std::string care_map_name = care_map_prefix_ + ".pb"; if (access(care_map_name.c_str(), R_OK) == -1) { - LOG(WARNING) << care_map_name - << " doesn't exist, falling back to read the care_map in plain text format."; - care_map_name = care_map_prefix_ + ".txt"; + LOG(ERROR) << care_map_name << " doesn't exist"; + return false; } android::base::unique_fd care_map_fd(TEMP_FAILURE_RETRY(open(care_map_name.c_str(), O_RDONLY))); @@ -263,10 +226,6 @@ bool UpdateVerifier::ParseCareMap() { return false; } - if (android::base::EndsWith(care_map_name, ".txt")) { - return ParseCareMapPlainText(file_content); - } - recovery_update_verifier::CareMap care_map; if (!care_map.ParseFromString(file_content)) { LOG(WARNING) << "Failed to parse " << care_map_name << " in protobuf format."; From b5108c372c8b92671ea5ebb4eeff00757fcee187 Mon Sep 17 00:00:00 2001 From: Tianjie Xu Date: Mon, 20 Aug 2018 13:40:47 -0700 Subject: [PATCH 12/28] Move librecovery_ui to a sub-directory This helps to expose librecovery_ui for device specific RecoveryUi. Bug: 76436783 Test: mma, unit tests pass Change-Id: Ic6c3d301d5833e4a592e6ea9d9d059bc4e4919be --- Android.bp | 76 +--------------- adb_install.cpp | 2 +- fastboot/fastboot.cpp | 3 +- fastboot/fastboot.h | 2 +- fuse_sdcard_install.h | 4 +- install.cpp | 2 +- recovery.cpp | 5 +- recovery.h | 2 +- recovery_main.cpp | 6 +- recovery_ui/Android.bp | 91 +++++++++++++++++++ .../default_device.cpp | 4 +- device.cpp => recovery_ui/device.cpp | 4 +- .../include/recovery_ui/device.h | 2 +- .../include/recovery_ui/screen_ui.h | 0 .../include/recovery_ui/stub_ui.h | 0 ui.h => recovery_ui/include/recovery_ui/ui.h | 12 +-- .../include/recovery_ui/vr_ui.h | 0 .../include/recovery_ui/wear_ui.h | 0 screen_ui.cpp => recovery_ui/screen_ui.cpp | 16 ++-- ui.cpp => recovery_ui/ui.cpp | 6 +- vr_device.cpp => recovery_ui/vr_device.cpp | 7 +- vr_ui.cpp => recovery_ui/vr_ui.cpp | 2 +- .../wear_device.cpp | 5 +- wear_ui.cpp => recovery_ui/wear_ui.cpp | 2 +- tests/unit/screen_ui_test.cpp | 4 +- 25 files changed, 138 insertions(+), 119 deletions(-) create mode 100644 recovery_ui/Android.bp rename default_device.cpp => recovery_ui/default_device.cpp (91%) rename device.cpp => recovery_ui/device.cpp (97%) rename device.h => recovery_ui/include/recovery_ui/device.h (99%) rename screen_ui.h => recovery_ui/include/recovery_ui/screen_ui.h (100%) rename stub_ui.h => recovery_ui/include/recovery_ui/stub_ui.h (100%) rename ui.h => recovery_ui/include/recovery_ui/ui.h (98%) rename vr_ui.h => recovery_ui/include/recovery_ui/vr_ui.h (100%) rename wear_ui.h => recovery_ui/include/recovery_ui/wear_ui.h (100%) rename screen_ui.cpp => recovery_ui/screen_ui.cpp (99%) rename ui.cpp => recovery_ui/ui.cpp (99%) rename vr_device.cpp => recovery_ui/vr_device.cpp (86%) rename vr_ui.cpp => recovery_ui/vr_ui.cpp (98%) rename wear_device.cpp => recovery_ui/wear_device.cpp (91%) rename wear_ui.cpp => recovery_ui/wear_ui.cpp (99%) diff --git a/Android.bp b/Android.bp index a6986bb2..7b67f407 100644 --- a/Android.bp +++ b/Android.bp @@ -26,77 +26,6 @@ cc_defaults { ], } -cc_library { - name: "librecovery_ui", - recovery_available: true, - - defaults: [ - "recovery_defaults", - ], - - srcs: [ - "device.cpp", - "screen_ui.cpp", - "ui.cpp", - "vr_ui.cpp", - "wear_ui.cpp" - ], - - static_libs: [ - "libminui", - "libotautil", - "libfstab", - ], - - shared_libs: [ - "libbase", - "libpng", - "libz", - ], -} - -// Generic device that uses ScreenRecoveryUI. -cc_library_static { - name: "librecovery_ui_default", - recovery_available: true, - - defaults: [ - "recovery_defaults", - ], - - srcs: [ - "default_device.cpp", - ], -} - -// The default wear device that uses WearRecoveryUI. -cc_library_static { - name: "librecovery_ui_wear", - recovery_available: true, - - defaults: [ - "recovery_defaults", - ], - - srcs: [ - "wear_device.cpp", - ], -} - -// The default VR device that uses VrRecoveryUI. -cc_library_static { - name: "librecovery_ui_vr", - recovery_available: true, - - defaults: [ - "recovery_defaults", - ], - - srcs: [ - "vr_device.cpp", - ], -} - cc_library_static { name: "librecovery_fastboot", recovery_available: true, @@ -113,6 +42,7 @@ cc_library_static { "libbootloader_message", "libcutils", "liblog", + "librecovery_ui", ], static_libs: [ @@ -180,6 +110,10 @@ cc_library_static { "roots.cpp", ], + shared_libs: [ + "librecovery_ui", + ], + include_dirs: [ "system/vold", ], diff --git a/adb_install.cpp b/adb_install.cpp index 3f2843fe..1d19fd3a 100644 --- a/adb_install.cpp +++ b/adb_install.cpp @@ -32,7 +32,7 @@ #include "common.h" #include "fuse_sideload.h" #include "install.h" -#include "ui.h" +#include "recovery_ui/ui.h" int apply_from_adb(bool* wipe_cache) { // Save the usb state to restore after the sideload operation. diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 8458c99d..14f5e4bd 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -27,8 +27,7 @@ #include #include -#include "device.h" -#include "ui.h" +#include "recovery_ui/ui.h" static const std::vector> kFastbootMenuActions{ { "Reboot system now", Device::REBOOT }, diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 53a2adca..1aa7de66 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -19,6 +19,6 @@ #include #include -#include "device.h" +#include "recovery_ui/device.h" Device::BuiltinAction StartFastboot(Device* device, const std::vector& args); diff --git a/fuse_sdcard_install.h b/fuse_sdcard_install.h index 5f0d64ac..345aea45 100644 --- a/fuse_sdcard_install.h +++ b/fuse_sdcard_install.h @@ -16,7 +16,7 @@ #pragma once -#include "device.h" -#include "ui.h" +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui); diff --git a/install.cpp b/install.cpp index dbc815d4..b7fb7887 100644 --- a/install.cpp +++ b/install.cpp @@ -53,8 +53,8 @@ #include "otautil/thermalutil.h" #include "package.h" #include "private/install.h" +#include "recovery_ui/ui.h" #include "roots.h" -#include "ui.h" #include "verifier.h" using namespace std::chrono_literals; diff --git a/recovery.cpp b/recovery.cpp index 90ca3f0e..d9c1f22f 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -52,7 +52,6 @@ #include "adb_install.h" #include "common.h" -#include "device.h" #include "fsck_unshare_blocks.h" #include "fuse_sdcard_install.h" #include "install.h" @@ -62,9 +61,9 @@ #include "otautil/paths.h" #include "otautil/sysutil.h" #include "package.h" +#include "recovery_ui/screen_ui.h" +#include "recovery_ui/ui.h" #include "roots.h" -#include "screen_ui.h" -#include "ui.h" static constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; static constexpr const char* COMMAND_FILE = "/cache/recovery/command"; diff --git a/recovery.h b/recovery.h index 00e22daa..f050549c 100644 --- a/recovery.h +++ b/recovery.h @@ -19,6 +19,6 @@ #include #include -#include "device.h" +#include "recovery_ui/device.h" Device::BuiltinAction start_recovery(Device* device, const std::vector& args); diff --git a/recovery_main.cpp b/recovery_main.cpp index 935d6981..2f5a1845 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -49,16 +49,16 @@ #include #include "common.h" -#include "device.h" #include "fastboot/fastboot.h" #include "logging.h" #include "minadbd/minadbd.h" #include "otautil/paths.h" #include "otautil/sysutil.h" #include "recovery.h" +#include "recovery_ui/device.h" +#include "recovery_ui/stub_ui.h" +#include "recovery_ui/ui.h" #include "roots.h" -#include "stub_ui.h" -#include "ui.h" static constexpr const char* COMMAND_FILE = "/cache/recovery/command"; static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale"; diff --git a/recovery_ui/Android.bp b/recovery_ui/Android.bp new file mode 100644 index 00000000..ee3149d5 --- /dev/null +++ b/recovery_ui/Android.bp @@ -0,0 +1,91 @@ +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +cc_library { + name: "librecovery_ui", + recovery_available: true, + + defaults: [ + "recovery_defaults", + ], + + srcs: [ + "device.cpp", + "screen_ui.cpp", + "ui.cpp", + "vr_ui.cpp", + "wear_ui.cpp", + ], + + export_include_dirs: ["include"], + + static_libs: [ + "libminui", + "libotautil", + ], + + shared_libs: [ + "libbase", + "libpng", + "libz", + ], +} + +// Generic device that uses ScreenRecoveryUI. +cc_library_static { + name: "librecovery_ui_default", + recovery_available: true, + + defaults: [ + "recovery_defaults", + ], + + srcs: [ + "default_device.cpp", + ], + + export_include_dirs: ["include"], +} + +// The default wear device that uses WearRecoveryUI. +cc_library_static { + name: "librecovery_ui_wear", + recovery_available: true, + + defaults: [ + "recovery_defaults", + ], + + srcs: [ + "wear_device.cpp", + ], + + export_include_dirs: ["include"], +} + +// The default VR device that uses VrRecoveryUI. +cc_library_static { + name: "librecovery_ui_vr", + recovery_available: true, + + defaults: [ + "recovery_defaults", + ], + + srcs: [ + "vr_device.cpp", + ], + + export_include_dirs: ["include"], +} diff --git a/default_device.cpp b/recovery_ui/default_device.cpp similarity index 91% rename from default_device.cpp rename to recovery_ui/default_device.cpp index a9718668..4db461af 100644 --- a/default_device.cpp +++ b/recovery_ui/default_device.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "device.h" -#include "screen_ui.h" +#include "recovery_ui/device.h" +#include "recovery_ui/screen_ui.h" Device* make_device() { return new Device(new ScreenRecoveryUI); diff --git a/device.cpp b/recovery_ui/device.cpp similarity index 97% rename from device.cpp rename to recovery_ui/device.cpp index eec1932c..ddb0118d 100644 --- a/device.cpp +++ b/recovery_ui/device.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "device.h" +#include "recovery_ui/device.h" #include #include @@ -23,7 +23,7 @@ #include -#include "ui.h" +#include "recovery_ui/ui.h" static std::vector> g_menu_actions{ { "Reboot system now", Device::REBOOT }, diff --git a/device.h b/recovery_ui/include/recovery_ui/device.h similarity index 99% rename from device.h rename to recovery_ui/include/recovery_ui/device.h index 6a8daf83..cfa914e7 100644 --- a/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -68,7 +68,7 @@ class Device { // Called when recovery starts up (after the UI has been obtained and initialized and after the // arguments have been parsed, but before anything else). - virtual void StartRecovery() {}; + virtual void StartRecovery() {} // Called from the main thread when recovery is at the main menu and waiting for input, and a key // is pressed. (Note that "at" the main menu does not necessarily mean the menu is visible; diff --git a/screen_ui.h b/recovery_ui/include/recovery_ui/screen_ui.h similarity index 100% rename from screen_ui.h rename to recovery_ui/include/recovery_ui/screen_ui.h diff --git a/stub_ui.h b/recovery_ui/include/recovery_ui/stub_ui.h similarity index 100% rename from stub_ui.h rename to recovery_ui/include/recovery_ui/stub_ui.h diff --git a/ui.h b/recovery_ui/include/recovery_ui/ui.h similarity index 98% rename from ui.h rename to recovery_ui/include/recovery_ui/ui.h index b387ae3c..d55322cf 100644 --- a/ui.h +++ b/recovery_ui/include/recovery_ui/ui.h @@ -35,20 +35,20 @@ class RecoveryUI { INSTALLING_UPDATE, ERASING, NO_COMMAND, - ERROR + ERROR, }; enum ProgressType { EMPTY, INDETERMINATE, - DETERMINATE + DETERMINATE, }; enum KeyAction { ENQUEUE, TOGGLE, REBOOT, - IGNORE + IGNORE, }; enum class KeyError : int { @@ -60,8 +60,8 @@ class RecoveryUI { virtual ~RecoveryUI(); - // Initializes 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); virtual std::string GetLocale() const = 0; @@ -211,7 +211,7 @@ class RecoveryUI { DISABLED, NORMAL, DIMMED, - OFF + OFF, }; // The sensitivity when detecting a swipe. diff --git a/vr_ui.h b/recovery_ui/include/recovery_ui/vr_ui.h similarity index 100% rename from vr_ui.h rename to recovery_ui/include/recovery_ui/vr_ui.h diff --git a/wear_ui.h b/recovery_ui/include/recovery_ui/wear_ui.h similarity index 100% rename from wear_ui.h rename to recovery_ui/include/recovery_ui/wear_ui.h diff --git a/screen_ui.cpp b/recovery_ui/screen_ui.cpp similarity index 99% rename from screen_ui.cpp rename to recovery_ui/screen_ui.cpp index 6f2b68b4..870db621 100644 --- a/screen_ui.cpp +++ b/recovery_ui/screen_ui.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "screen_ui.h" +#include "recovery_ui/screen_ui.h" #include #include @@ -42,10 +42,10 @@ #include #include -#include "device.h" #include "minui/minui.h" #include "otautil/paths.h" -#include "ui.h" +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" // Return the current time as a double (including fractions of a second). static double now() { @@ -388,10 +388,10 @@ int ScreenRecoveryUI::PixelsFromDp(int dp) const { enum Layout { PORTRAIT = 0, PORTRAIT_LARGE = 1, LANDSCAPE = 2, LANDSCAPE_LARGE = 3, LAYOUT_MAX }; enum Dimension { TEXT = 0, ICON = 1, DIMENSION_MAX }; static constexpr int kLayouts[LAYOUT_MAX][DIMENSION_MAX] = { - { 32, 68, }, // PORTRAIT - { 32, 68, }, // PORTRAIT_LARGE - { 26, 56, }, // LANDSCAPE - { 52, 112, }, // LANDSCAPE_LARGE + { 32, 68 }, // PORTRAIT + { 32, 68 }, // PORTRAIT_LARGE + { 26, 56 }, // LANDSCAPE + { 52, 112 }, // LANDSCAPE_LARGE }; int ScreenRecoveryUI::GetAnimationBaseline() const { @@ -1042,7 +1042,7 @@ void ScreenRecoveryUI::Print(const char* fmt, ...) { va_end(ap); } -void ScreenRecoveryUI::PrintOnScreenOnly(const char *fmt, ...) { +void ScreenRecoveryUI::PrintOnScreenOnly(const char* fmt, ...) { va_list ap; va_start(ap, fmt); PrintV(fmt, false, ap); diff --git a/ui.cpp b/recovery_ui/ui.cpp similarity index 99% rename from ui.cpp rename to recovery_ui/ui.cpp index c12a11b3..b7107ff2 100644 --- a/ui.cpp +++ b/recovery_ui/ui.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "ui.h" +#include "recovery_ui/ui.h" #include #include @@ -39,7 +39,6 @@ #include "minui/minui.h" #include "otautil/sysutil.h" -#include "roots.h" using namespace std::chrono_literals; @@ -590,8 +589,7 @@ RecoveryUI::KeyAction RecoveryUI::CheckKey(int key, bool is_long_press) { return (IsTextVisible() || screensaver_state_ == ScreensaverState::OFF) ? ENQUEUE : IGNORE; } -void RecoveryUI::KeyLongPress(int) { -} +void RecoveryUI::KeyLongPress(int) {} void RecoveryUI::SetEnableReboot(bool enabled) { std::lock_guard lg(key_queue_mutex); diff --git a/vr_device.cpp b/recovery_ui/vr_device.cpp similarity index 86% rename from vr_device.cpp rename to recovery_ui/vr_device.cpp index 61e15cbb..fd761330 100644 --- a/vr_device.cpp +++ b/recovery_ui/vr_device.cpp @@ -14,10 +14,9 @@ * limitations under the License. */ -#include "device.h" -#include "vr_ui.h" +#include "recovery_ui/device.h" +#include "recovery_ui/vr_ui.h" Device* make_device() { - return new Device(new VrRecoveryUI); + return new Device(new VrRecoveryUI); } - diff --git a/vr_ui.cpp b/recovery_ui/vr_ui.cpp similarity index 98% rename from vr_ui.cpp rename to recovery_ui/vr_ui.cpp index 1f0292c3..5b9b1b4e 100644 --- a/vr_ui.cpp +++ b/recovery_ui/vr_ui.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "vr_ui.h" +#include "recovery_ui/vr_ui.h" #include diff --git a/wear_device.cpp b/recovery_ui/wear_device.cpp similarity index 91% rename from wear_device.cpp rename to recovery_ui/wear_device.cpp index 3268130b..bf21bc96 100644 --- a/wear_device.cpp +++ b/recovery_ui/wear_device.cpp @@ -14,10 +14,9 @@ * limitations under the License. */ -#include "device.h" -#include "wear_ui.h" +#include "recovery_ui/device.h" +#include "recovery_ui/wear_ui.h" Device* make_device() { return new Device(new WearRecoveryUI); } - diff --git a/wear_ui.cpp b/recovery_ui/wear_ui.cpp similarity index 99% rename from wear_ui.cpp rename to recovery_ui/wear_ui.cpp index 6da84c92..8d8108f1 100644 --- a/wear_ui.cpp +++ b/recovery_ui/wear_ui.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "wear_ui.h" +#include "recovery_ui/wear_ui.h" #include diff --git a/tests/unit/screen_ui_test.cpp b/tests/unit/screen_ui_test.cpp index 647c7b2d..883dfbde 100644 --- a/tests/unit/screen_ui_test.cpp +++ b/tests/unit/screen_ui_test.cpp @@ -30,11 +30,11 @@ #include #include "common/test_constants.h" -#include "device.h" #include "minui/minui.h" #include "otautil/paths.h" #include "private/resources.h" -#include "screen_ui.h" +#include "recovery_ui/device.h" +#include "recovery_ui/screen_ui.h" static const std::vector HEADERS{ "header" }; static const std::vector ITEMS{ "item1", "item2", "item3", "item4", "1234567890" }; From 2f8afba7073e6339287ba48b9af5a8868613e409 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 8 Apr 2019 11:26:11 -0700 Subject: [PATCH 13/28] DO NOT MERGE: Build libinstall as a static library. It was once considered to be shared between recovery and minadbd, so that the latter can start an install on its own. The plan has been changed, since package install -- including device wipe operations -- could be device-specific, which should be done by recovery only. This CL moves libinstall back to a static library, which also saves the overall size (reducing from 140256 + 660576 to 555880 bytes on aosp_taimen-userdebug). Bug: 130166585 Test: Run recovery_component_test. Test: `adb sideload` on taimen. Change-Id: Ib1f5f79f235df4682c0bd104425c9c122f6091ba --- Android.bp | 3 +-- CleanSpec.mk | 10 +++++++--- install/Android.bp | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Android.bp b/Android.bp index a44a2c62..f2230ae0 100644 --- a/Android.bp +++ b/Android.bp @@ -69,6 +69,7 @@ cc_defaults { ], static_libs: [ + "libinstall", "librecovery_fastboot", "libminui", "libotautil", @@ -93,7 +94,6 @@ cc_library_static { ], shared_libs: [ - "libinstall", "librecovery_ui", ], } @@ -113,7 +113,6 @@ cc_binary { ], shared_libs: [ - "libinstall", "libminadbd_services", "librecovery_ui", ], diff --git a/CleanSpec.mk b/CleanSpec.mk index fec823e7..a7ab0d9b 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -44,9 +44,13 @@ #$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) -# ************************************************ -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST -# ************************************************ $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/recovery_intermediates) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libminui_intermediates/import_includes) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/sbin) + +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libinstall.recovery_intermediates) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/recovery/root/system/lib64/libinstall.so) + +# ************************************************ +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST +# ************************************************ diff --git a/install/Android.bp b/install/Android.bp index aa47990a..221ce72a 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -47,7 +47,7 @@ cc_defaults { ], } -cc_library { +cc_library_static { name: "libinstall", recovery_available: true, From 95d67323a423db036a43a8c2de1072d514f3717f Mon Sep 17 00:00:00 2001 From: xunchang Date: Fri, 5 Apr 2019 16:16:07 -0700 Subject: [PATCH 14/28] DO NOT MERGE: Add socket communication between recovery and minadbd This cl adds a socket pair to support the communication between recovery and minadbd. Therefore, minadbd will be able to issue multiple commands to recovery and get back the status of each command. This cl also switches the adb sideload from the recovery menu to use this protocol; and moves minadbd to a separate binary. Bug: 130166585 Test: sideload a package Change-Id: I80d36d5c4e6fe1ae3ea23640907bc50c0dc0d482 (cherry picked from commit 34690ced91e22f5d9b5dd19c33b11c8e0b4bafa0) --- Android.bp | 2 +- install/Android.bp | 4 + install/adb_install.cpp | 309 +++++++++++++++++----- minadbd/Android.bp | 31 ++- minadbd/minadbd.cpp | 48 +++- minadbd/minadbd_services.cpp | 73 ++++- minadbd/{minadbd.h => minadbd_services.h} | 7 +- minadbd/minadbd_types.h | 53 ++++ recovery_main.cpp | 11 - 9 files changed, 441 insertions(+), 97 deletions(-) rename minadbd/{minadbd.h => minadbd_services.h} (90%) create mode 100644 minadbd/minadbd_types.h diff --git a/Android.bp b/Android.bp index f2230ae0..dd18a89a 100644 --- a/Android.bp +++ b/Android.bp @@ -113,7 +113,6 @@ cc_binary { ], shared_libs: [ - "libminadbd_services", "librecovery_ui", ], @@ -125,6 +124,7 @@ cc_binary { required: [ "e2fsdroid.recovery", "librecovery_ui_ext", + "minadbd", "mke2fs.conf.recovery", "mke2fs.recovery", "recovery_deps", diff --git a/install/Android.bp b/install/Android.bp index 221ce72a..ce4244ca 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -19,6 +19,10 @@ cc_defaults { "recovery_defaults", ], + header_libs: [ + "libminadbd_headers", + ], + shared_libs: [ "libbase", "libbootloader_message", diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 5296ff60..dc7ee0b3 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -21,23 +21,265 @@ #include #include #include +#include +#include #include #include #include #include +#include +#include +#include + +#include #include +#include #include +#include +#include #include "fuse_sideload.h" #include "install/install.h" +#include "minadbd_types.h" #include "recovery_ui/ui.h" +using CommandFunction = std::function; + static bool SetUsbConfig(const std::string& state) { android::base::SetProperty("sys.usb.config", state); return android::base::WaitForProperty("sys.usb.state", state); } +// Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors. +static MinadbdCommands ParseMinadbdCommands(const std::string& message) { + if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) { + LOG(ERROR) << "Failed to parse command in message " << message; + return MinadbdCommands::kError; + } + + auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix)); + auto cmd_code = android::base::get_unaligned(cmd_code_string.c_str()); + if (cmd_code >= static_cast(MinadbdCommands::kError)) { + LOG(ERROR) << "Unsupported command code: " << cmd_code; + return MinadbdCommands::kError; + } + + return static_cast(cmd_code); +} + +static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { + char message[kMinadbdMessageSize]; + memcpy(message, kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix)); + android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), status); + + if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) { + PLOG(ERROR) << "Failed to write message " << message; + return false; + } + return true; +} + +// Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise. +static bool AdbInstallPackageHandler(bool* wipe_cache, RecoveryUI* ui, int* result) { + // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long + // because the minadbd service has already issued an install command. 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.) + constexpr int ADB_INSTALL_TIMEOUT = 15; + *result = INSTALL_ERROR; + for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { + struct stat st; + 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 fuse to be ready.\n\n"); + break; + } + } + *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui); + break; + } + + // Calling stat() on this magic filename signals the FUSE to exit. + struct stat st; + stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); + return *result == INSTALL_SUCCESS; +} + +// Parses and executes the command from minadbd. Returns false if we enter an invalid state so that +// the caller can kill the minadbd service properly. +static bool HandleMessageFromMinadbd( + int socket_fd, const std::map& command_map) { + char buffer[kMinadbdMessageSize]; + if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) { + PLOG(ERROR) << "Failed to read message from minadbd"; + return false; + } + + std::string message(buffer, buffer + kMinadbdMessageSize); + auto command_type = ParseMinadbdCommands(message); + if (command_type == MinadbdCommands::kError) { + return false; + } + if (command_map.find(command_type) == command_map.end()) { + LOG(ERROR) << "Unsupported command: " + << android::base::get_unaligned( + message.substr(strlen(kMinadbdCommandPrefix)).c_str()); + return false; + } + + // We have received a valid command, execute the corresponding function. + const auto& command_func = command_map.at(command_type); + if (!command_func()) { + LOG(ERROR) << "Failed to execute command " << static_cast(command_type); + return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd); + } + return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd); +} + +// TODO(xunchang) add a wrapper function and kill the minadbd service there. +static void ListenAndExecuteMinadbdCommands( + pid_t minadbd_pid, android::base::unique_fd&& socket_fd, + const std::map& command_map) { + android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); + if (epoll_fd == -1) { + PLOG(ERROR) << "Failed to create epoll"; + kill(minadbd_pid, SIGKILL); + return; + } + + constexpr int EPOLL_MAX_EVENTS = 10; + struct epoll_event ev = {}; + ev.events = EPOLLIN | EPOLLHUP; + ev.data.fd = socket_fd.get(); + struct epoll_event events[EPOLL_MAX_EVENTS]; + if (epoll_ctl(epoll_fd.get(), EPOLL_CTL_ADD, socket_fd.get(), &ev) == -1) { + PLOG(ERROR) << "Failed to add socket fd to epoll"; + kill(minadbd_pid, SIGKILL); + return; + } + + // Set the timeout to be 300s when waiting for minadbd commands. + constexpr int TIMEOUT_MILLIS = 300 * 1000; + while (true) { + // Poll for the status change of the socket_fd, and handle the message if the fd is ready to + // read. + int event_count = + TEMP_FAILURE_RETRY(epoll_wait(epoll_fd.get(), events, EPOLL_MAX_EVENTS, TIMEOUT_MILLIS)); + if (event_count == -1) { + PLOG(ERROR) << "Failed to wait for epoll events"; + kill(minadbd_pid, SIGKILL); + return; + } + if (event_count == 0) { + LOG(ERROR) << "Timeout waiting for messages from minadbd"; + kill(minadbd_pid, SIGKILL); + return; + } + + for (int n = 0; n < event_count; n++) { + if (events[n].events & EPOLLHUP) { + LOG(INFO) << "Socket has been closed"; + kill(minadbd_pid, SIGKILL); + return; + } + if (!HandleMessageFromMinadbd(socket_fd.get(), command_map)) { + kill(minadbd_pid, SIGKILL); + return; + } + } + } +} + +// Recovery starts minadbd service as a child process, and spawns another thread to listen for the +// message from minadbd through a socket pair. Here is an example to execute one command from adb +// host. +// a. recovery b. listener thread c. minadbd service +// +// a1. create socket pair +// a2. fork minadbd service +// c3. wait for the adb commands +// from host +// c4. after receiving host commands: +// 1) set up pre-condition (i.e. +// start fuse for adb sideload) +// 2) issue command through +// socket. +// 3) wait for result +// a5. start listener thread +// b6. listen for message from +// minadbd in a loop. +// b7. After receiving a minadbd +// command from socket +// 1) execute the command function +// 2) send the result back to +// minadbd +// ...... +// c8. exit upon receiving the +// result +// a9. wait for listener thread +// to exit. +// +// a10. wait for minadbd to +// exit +// b11. exit the listening loop +// +static void CreateMinadbdServiceAndExecuteCommands( + const std::map& command_map) { + signal(SIGPIPE, SIG_IGN); + + android::base::unique_fd recovery_socket; + android::base::unique_fd minadbd_socket; + if (!android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &recovery_socket, &minadbd_socket)) { + PLOG(ERROR) << "Failed to create socket"; + return; + } + + pid_t child = fork(); + if (child == -1) { + PLOG(ERROR) << "Failed to fork child process"; + return; + } + if (child == 0) { + recovery_socket.reset(); + execl("/system/bin/minadbd", "minadbd", "--socket_fd", + std::to_string(minadbd_socket.release()).c_str(), nullptr); + + _exit(EXIT_FAILURE); + } + + minadbd_socket.reset(); + + // We need to call SetUsbConfig() after forking minadbd service. Because the function waits for + // the usb state to be updated, which depends on sys.usb.ffs.ready=1 set in the adb daemon. + if (!SetUsbConfig("sideload")) { + LOG(ERROR) << "Failed to set usb config to sideload"; + return; + } + + std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket), + std::ref(command_map)); + + if (listener_thread.joinable()) { + listener_thread.join(); + } + + int status; + waitpid(child, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + if (WEXITSTATUS(status) == MinadbdErrorCode::kMinadbdAdbVersionError) { + LOG(ERROR) << "\nYou need adb 1.0.32 or newer to sideload\nto this device.\n"; + } else if (!WIFSIGNALED(status)) { + LOG(ERROR) << "\n(adbd status " << WEXITSTATUS(status) << ")"; + } + } + + signal(SIGPIPE, SIG_DFL); +} + int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); @@ -51,66 +293,13 @@ int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { "\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("/system/bin/recovery", "recovery", "--adbd", nullptr); - _exit(EXIT_FAILURE); - } + int install_result = INSTALL_ERROR; + std::map command_map{ + { MinadbdCommands::kInstall, + std::bind(&AdbInstallPackageHandler, wipe_cache, ui, &install_result) }, + }; - if (!SetUsbConfig("sideload")) { - LOG(ERROR) << "Failed to set usb config to sideload"; - return INSTALL_ERROR; - } - - // 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; - } - - struct stat st; - 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, false, 0, ui); - break; - } - - 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)); - } - } + CreateMinadbdServiceAndExecuteCommands(command_map); // Clean up before switching to the older state, for example setting the state // to none sets sys/class/android_usb/android0/enable to 0. @@ -124,5 +313,5 @@ int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { } } - return result; + return install_result; } diff --git a/minadbd/Android.bp b/minadbd/Android.bp index 9b889f4a..e4f7712e 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -40,7 +40,6 @@ cc_library { srcs: [ "fuse_adb_provider.cpp", - "minadbd.cpp", "minadbd_services.cpp", ], @@ -52,6 +51,36 @@ cc_library { ], } +cc_library_headers { + name: "libminadbd_headers", + recovery_available: true, + // TODO create a include dir + export_include_dirs: [ + ".", + ], +} + +cc_binary { + name: "minadbd", + recovery: true, + + defaults: [ + "minadbd_defaults", + ], + + srcs: [ + "minadbd.cpp", + ], + + shared_libs: [ + "libadbd", + "libbase", + "libcrypto", + "libfusesideload", + "libminadbd_services", + ], +} + cc_test { name: "minadbd_test", isolated: true, diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp index 349189cc..57158ad5 100644 --- a/minadbd/minadbd.cpp +++ b/minadbd/minadbd.cpp @@ -14,30 +14,54 @@ * limitations under the License. */ -#include "minadbd.h" - #include +#include #include #include #include +#include + +#include +#include #include "adb.h" #include "adb_auth.h" #include "transport.h" -int minadbd_main() { - adb_device_banner = "sideload"; +#include "minadbd_services.h" +#include "minadbd_types.h" - signal(SIGPIPE, SIG_IGN); +int main(int argc, char** argv) { + android::base::InitLogging(argv, &android::base::StderrLogger); + // TODO(xunchang) implement a command parser + if (argc != 3 || strcmp("--socket_fd", argv[1]) != 0) { + LOG(ERROR) << "minadbd has invalid arguments, argc: " << argc; + exit(kMinadbdArgumentsParsingError); + } - // We can't require authentication for sideloading. http://b/22025550. - auth_required = false; + int socket_fd; + if (!android::base::ParseInt(argv[2], &socket_fd)) { + LOG(ERROR) << "Failed to parse int in " << argv[2]; + exit(kMinadbdArgumentsParsingError); + } + if (fcntl(socket_fd, F_GETFD, 0) == -1) { + PLOG(ERROR) << "Failed to get minadbd socket"; + exit(kMinadbdSocketIOError); + } + SetMinadbdSocketFd(socket_fd); - init_transport_registration(); - usb_init(); + adb_device_banner = "sideload"; - VLOG(ADB) << "Event loop starting"; - fdevent_loop(); + signal(SIGPIPE, SIG_IGN); - return 0; + // We can't require authentication for sideloading. http://b/22025550. + auth_required = false; + + init_transport_registration(); + usb_init(); + + VLOG(ADB) << "Event loop starting"; + fdevent_loop(); + + return 0; } diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 6fe5c79b..79e6fc4e 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "minadbd_services.h" + #include #include #include @@ -27,38 +29,95 @@ #include #include +#include +#include +#include +#include +#include + #include "adb.h" #include "adb_unique_fd.h" #include "fdevent.h" #include "fuse_adb_provider.h" #include "fuse_sideload.h" +#include "minadbd_types.h" #include "services.h" #include "sysdeps.h" +static int minadbd_socket = -1; +void SetMinadbdSocketFd(int socket_fd) { + minadbd_socket = socket_fd; +} + +static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { + char message[kMinadbdMessageSize]; + memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); + android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd); + + if (!android::base::WriteFully(fd, message, kMinadbdMessageSize)) { + PLOG(ERROR) << "Failed to write message " << message; + return false; + } + return true; +} + +// Blocks and reads the command status from |fd|. Returns false if the received message has a +// format error. +static bool WaitForCommandStatus(int fd, MinadbdCommandStatus* status) { + char buffer[kMinadbdMessageSize]; + if (!android::base::ReadFully(fd, buffer, kMinadbdMessageSize)) { + PLOG(ERROR) << "Failed to response status from socket"; + exit(kMinadbdSocketIOError); + } + + std::string message(buffer, buffer + kMinadbdMessageSize); + if (!android::base::StartsWith(message, kMinadbdStatusPrefix)) { + LOG(ERROR) << "Failed to parse status in " << message; + return false; + } + + *status = android::base::get_unaligned( + message.substr(strlen(kMinadbdStatusPrefix)).c_str()); + return true; +} + static void sideload_host_service(unique_fd sfd, const std::string& args) { int64_t file_size; int block_size; if ((sscanf(args.c_str(), "%" SCNd64 ":%d", &file_size, &block_size) != 2) || file_size <= 0 || block_size <= 0) { - printf("bad sideload-host arguments: %s\n", args.c_str()); - exit(1); + LOG(ERROR) << "bad sideload-host arguments: " << args; + exit(kMinadbdPackageSizeError); } - printf("sideload-host file size %" PRId64 " block size %d\n", file_size, block_size); + LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; + + if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) { + exit(kMinadbdSocketIOError); + } auto adb_data_reader = std::make_unique(std::move(sfd), file_size, block_size); - int result = run_fuse_sideload(std::move(adb_data_reader)); + if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) { + LOG(ERROR) << "Failed to start fuse"; + exit(kMinadbdFuseStartError); + } - printf("sideload_host finished\n"); - exit(result == 0 ? 0 : 1); + MinadbdCommandStatus status; + if (!WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } + LOG(INFO) << "Got command status: " << static_cast(status); + + LOG(INFO) << "sideload_host finished"; + exit(kMinadbdSuccess); } unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { if (name.starts_with("sideload:")) { // This exit status causes recovery to print a special error message saying to use a newer adb // (that supports sideload-host). - exit(3); + exit(kMinadbdAdbVersionError); } else if (name.starts_with("sideload-host:")) { std::string arg(name.substr(strlen("sideload-host:"))); return create_service_thread("sideload-host", diff --git a/minadbd/minadbd.h b/minadbd/minadbd_services.h similarity index 90% rename from minadbd/minadbd.h rename to minadbd/minadbd_services.h index 3570a5da..6835bd77 100644 --- a/minadbd/minadbd.h +++ b/minadbd/minadbd_services.h @@ -14,9 +14,6 @@ * limitations under the License. */ -#ifndef MINADBD_H__ -#define MINADBD_H__ +#pragma once -int minadbd_main(); - -#endif +void SetMinadbdSocketFd(int socket_fd); diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h new file mode 100644 index 00000000..7bd69096 --- /dev/null +++ b/minadbd/minadbd_types.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +// The message between recovery and minadbd is 8 bytes in size unless the length is explicitly +// specified. Both the command and status has the format |prefix(4 bytes) + encoded enum(4 bytes)|. +constexpr size_t kMinadbdMessageSize = 8; +constexpr char const kMinadbdCommandPrefix[] = "COMD"; +constexpr char const kMinadbdStatusPrefix[] = "STAT"; + +enum MinadbdErrorCode : int { + kMinadbdSuccess = 0, + kMinadbdArgumentsParsingError = 1, + kMinadbdSocketIOError = 2, + kMinadbdMessageFormatError = 3, + kMinadbdAdbVersionError = 4, + kMinadbdPackageSizeError = 5, + kMinadbdFuseStartError = 6, + kMinadbdUnsupportedCommandError = 7, + kMinadbdCommandExecutionError = 8, + kMinadbdErrorUnknown = 9, +}; + +enum class MinadbdCommandStatus : uint32_t { + kSuccess = 0, + kFailure = 1, +}; + +enum class MinadbdCommands : uint32_t { + kInstall = 0, + kUiPrint = 1, + kError = 2, +}; + +static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommands)); +static_assert(kMinadbdMessageSize == + sizeof(kMinadbdStatusPrefix) - 1 + sizeof(MinadbdCommandStatus)); diff --git a/recovery_main.cpp b/recovery_main.cpp index b41368d7..8b4ad5a3 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -51,7 +51,6 @@ #include "common.h" #include "fastboot/fastboot.h" #include "logging.h" -#include "minadbd/minadbd.h" #include "otautil/paths.h" #include "otautil/roots.h" #include "otautil/sysutil.h" @@ -322,16 +321,6 @@ int main(int argc, char** argv) { // Take action to refresh pmsg contents __android_log_pmsg_file_read(LOG_ID_SYSTEM, ANDROID_LOG_INFO, filter, logrotate, &do_rotate); - // If this binary is started with the single argument "--adbd", instead of being the normal - // recovery binary, it turns into kind of a stripped-down version of adbd that only supports the - // 'sideload' command. Note this must be a real argument, not anything in the command file or - // bootloader control block; the only way recovery should be run with this argument is when it - // starts a copy of itself from the apply_from_adb() function. - if (argc == 2 && strcmp(argv[1], "--adbd") == 0) { - minadbd_main(); - return 0; - } - time_t start = time(nullptr); // redirect_stdio should be called only in non-sideload mode. Otherwise we may have two logger From 388d253b9cf4d12ca7879d29dc6711ad44541ccd Mon Sep 17 00:00:00 2001 From: xunchang Date: Fri, 12 Apr 2019 16:22:15 -0700 Subject: [PATCH 15/28] DO NOT MERGE: Move wipe cache|data to libinstall Therefore, libinstall becomes the sole owner to handle the request from minadbd service. The change also includes 1. move logging.cpp out of librecovery 2. drop the dependency on common.h 3. now it's more sensible to move the wipe_cache as part of install_package. move the wipe_cache to the end of the function. Bug: 130166585 Test: wipe data and cache from menu Change-Id: I6f356dccdb38015c50acf756bac246f87c30fc1f (cherry picked from commit 316e9717461890dd319dc370970069fe4532a561) --- Android.bp | 3 - install/Android.bp | 1 + install/adb_install.cpp | 9 +- install/fuse_sdcard_install.cpp | 4 +- install/include/install/adb_install.h | 2 +- install/include/install/fuse_sdcard_install.h | 2 +- install/include/install/install.h | 9 +- install/include/install/wipe_data.h | 32 +++ install/install.cpp | 18 +- install/wipe_data.cpp | 190 +++++++++++++++ otautil/Android.bp | 1 + .../include/otautil/logging.h | 5 +- logging.cpp => otautil/logging.cpp | 25 +- recovery-persist.cpp | 2 +- recovery-refresh.cpp | 2 +- recovery.cpp | 217 +++--------------- recovery_main.cpp | 5 +- 17 files changed, 303 insertions(+), 224 deletions(-) create mode 100644 install/include/install/wipe_data.h create mode 100644 install/wipe_data.cpp rename logging.h => otautil/include/otautil/logging.h (91%) rename logging.cpp => otautil/logging.cpp (92%) diff --git a/Android.bp b/Android.bp index dd18a89a..f9207825 100644 --- a/Android.bp +++ b/Android.bp @@ -108,7 +108,6 @@ cc_binary { ], srcs: [ - "logging.cpp", "recovery_main.cpp", ], @@ -140,7 +139,6 @@ cc_binary { ], srcs: [ - "logging.cpp", "recovery-persist.cpp", ], @@ -169,7 +167,6 @@ cc_binary { ], srcs: [ - "logging.cpp", "recovery-refresh.cpp", ], diff --git a/install/Android.bp b/install/Android.bp index ce4244ca..ea893a07 100644 --- a/install/Android.bp +++ b/install/Android.bp @@ -66,6 +66,7 @@ cc_library_static { "install.cpp", "package.cpp", "verifier.cpp", + "wipe_data.cpp", ], shared_libs: [ diff --git a/install/adb_install.cpp b/install/adb_install.cpp index dc7ee0b3..548b6e5b 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -81,7 +81,7 @@ static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { } // Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise. -static bool AdbInstallPackageHandler(bool* wipe_cache, RecoveryUI* ui, int* result) { +static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long // because the minadbd service has already issued an install command. FUSE_SIDELOAD_HOST_PATHNAME // will start to exist once the host connects and starts serving a package. Poll for its @@ -99,7 +99,7 @@ static bool AdbInstallPackageHandler(bool* wipe_cache, RecoveryUI* ui, int* resu break; } } - *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0, ui); + *result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0, ui); break; } @@ -280,7 +280,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { +int apply_from_adb(RecoveryUI* ui) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); // Clean up state and stop adbd. @@ -295,8 +295,7 @@ int apply_from_adb(bool* wipe_cache, RecoveryUI* ui) { int install_result = INSTALL_ERROR; std::map command_map{ - { MinadbdCommands::kInstall, - std::bind(&AdbInstallPackageHandler, wipe_cache, ui, &install_result) }, + { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, }; CreateMinadbdServiceAndExecuteCommands(command_map); diff --git a/install/fuse_sdcard_install.cpp b/install/fuse_sdcard_install.cpp index dde289f8..1aa8768e 100644 --- a/install/fuse_sdcard_install.cpp +++ b/install/fuse_sdcard_install.cpp @@ -133,7 +133,7 @@ static bool StartSdcardFuse(const std::string& path) { return run_fuse_sideload(std::move(file_data_reader)) == 0; } -int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui) { +int ApplyFromSdcard(Device* device, RecoveryUI* ui) { if (ensure_path_mounted(SDCARD_ROOT) != 0) { LOG(ERROR) << "\n-- Couldn't mount " << SDCARD_ROOT << ".\n"; return INSTALL_ERROR; @@ -184,7 +184,7 @@ int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui) { } } - result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, wipe_cache, false, 0 /*retry_count*/, ui); + result = install_package(FUSE_SIDELOAD_HOST_PATHNAME, false, false, 0 /*retry_count*/, ui); break; } diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index dbc82450..f7b065b6 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -18,4 +18,4 @@ #include -int apply_from_adb(bool* wipe_cache, RecoveryUI* ui); +int apply_from_adb(RecoveryUI* ui); diff --git a/install/include/install/fuse_sdcard_install.h b/install/include/install/fuse_sdcard_install.h index 345aea45..d9214ca3 100644 --- a/install/include/install/fuse_sdcard_install.h +++ b/install/include/install/fuse_sdcard_install.h @@ -19,4 +19,4 @@ #include "recovery_ui/device.h" #include "recovery_ui/ui.h" -int ApplyFromSdcard(Device* device, bool* wipe_cache, RecoveryUI* ui); +int ApplyFromSdcard(Device* device, RecoveryUI* ui); diff --git a/install/include/install/install.h b/install/include/install/install.h index 74fb3d17..1e41b484 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -43,10 +43,11 @@ enum class OtaType { BRICK, }; -// 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, bool needs_mount, int retry_count, - RecoveryUI* ui); +// Installs the given update package. This function should also wipe the cache partition after a +// successful installation if |should_wipe_cache| is true or an updater command asks to wipe the +// cache. +int install_package(const std::string& package, bool should_wipe_cache, bool needs_mount, + int retry_count, RecoveryUI* ui); // Verifies the package by ota keys. Returns true if the package is verified successfully, // otherwise returns false. diff --git a/install/include/install/wipe_data.h b/install/include/install/wipe_data.h new file mode 100644 index 00000000..06b1b95d --- /dev/null +++ b/install/include/install/wipe_data.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "recovery_ui/device.h" +#include "recovery_ui/ui.h" + +struct selabel_handle; + +void SetWipeDataSehandle(selabel_handle* handle); + +// Returns true on success. +bool WipeCache(RecoveryUI* ui, const std::function& confirm); + +// Returns true on success. +bool WipeData(Device* device, bool convert_fbe); diff --git a/install/install.cpp b/install/install.cpp index a1124c36..e2d47009 100644 --- a/install/install.cpp +++ b/install/install.cpp @@ -48,6 +48,7 @@ #include "install/package.h" #include "install/verifier.h" +#include "install/wipe_data.h" #include "otautil/error_code.h" #include "otautil/paths.h" #include "otautil/roots.h" @@ -631,10 +632,9 @@ static int really_install_package(const std::string& path, bool* wipe_cache, boo return result; } -int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, int retry_count, - RecoveryUI* ui) { +int install_package(const std::string& path, bool should_wipe_cache, bool needs_mount, + int retry_count, RecoveryUI* ui) { CHECK(!path.empty()); - CHECK(wipe_cache != nullptr); auto start = std::chrono::system_clock::now(); @@ -647,8 +647,10 @@ int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, 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, ui); + bool updater_wipe_cache = false; + result = really_install_package(path, &updater_wipe_cache, needs_mount, &log_buffer, + retry_count, &max_temperature, ui); + should_wipe_cache = should_wipe_cache || updater_wipe_cache; } // Measure the time spent to apply OTA update in seconds. @@ -703,6 +705,12 @@ int install_package(const std::string& path, bool* wipe_cache, bool needs_mount, // Write a copy into last_log. LOG(INFO) << log_content; + if (result == INSTALL_SUCCESS && should_wipe_cache) { + if (!WipeCache(ui, nullptr)) { + result = INSTALL_ERROR; + } + } + return result; } diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp new file mode 100644 index 00000000..7db79048 --- /dev/null +++ b/install/wipe_data.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "install/wipe_data.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "otautil/dirutil.h" +#include "otautil/logging.h" +#include "otautil/roots.h" +#include "recovery_ui/ui.h" + +constexpr const char* CACHE_ROOT = "/cache"; +constexpr const char* DATA_ROOT = "/data"; +constexpr const char* METADATA_ROOT = "/metadata"; + +constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; + +static struct selabel_handle* sehandle; + +void SetWipeDataSehandle(selabel_handle* handle) { + sehandle = handle; +} + +struct saved_log_file { + std::string name; + struct stat sb; + std::string data; +}; + +static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) { + bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); + bool is_data = (strcmp(volume, DATA_ROOT) == 0); + + ui->SetBackground(RecoveryUI::ERASING); + ui->SetProgressType(RecoveryUI::INDETERMINATE); + + std::vector log_files; + + if (is_cache) { + // If we're reformatting /cache, we load any past logs + // (i.e. "/cache/recovery/last_*") and the current log + // ("/cache/recovery/log") into memory, so we can restore them after + // the reformat. + + ensure_path_mounted(volume); + + struct dirent* de; + std::unique_ptr d(opendir(CACHE_LOG_DIR), closedir); + if (d) { + while ((de = readdir(d.get())) != nullptr) { + if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) { + std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name); + + struct stat sb; + if (stat(path.c_str(), &sb) == 0) { + // truncate files to 512kb + if (sb.st_size > (1 << 19)) { + sb.st_size = 1 << 19; + } + + std::string data(sb.st_size, '\0'); + FILE* f = fopen(path.c_str(), "rbe"); + fread(&data[0], 1, data.size(), f); + fclose(f); + + log_files.emplace_back(saved_log_file{ path, sb, data }); + } + } + } + } else { + if (errno != ENOENT) { + PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR; + } + } + } + + ui->Print("Formatting %s...\n", volume); + + ensure_path_unmounted(volume); + + int result; + if (is_data && convert_fbe) { + constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe"; + constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; + // Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not + // full disk encryption. + if (mkdir(CONVERT_FBE_DIR, 0700) != 0) { + PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR; + return false; + } + FILE* f = fopen(CONVERT_FBE_FILE, "wbe"); + if (!f) { + PLOG(ERROR) << "Failed to convert to file encryption"; + return false; + } + fclose(f); + result = format_volume(volume, CONVERT_FBE_DIR); + remove(CONVERT_FBE_FILE); + rmdir(CONVERT_FBE_DIR); + } else { + result = format_volume(volume); + } + + if (is_cache) { + // Re-create the log dir and write back the log entries. + if (ensure_path_mounted(CACHE_LOG_DIR) == 0 && + mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle) == 0) { + for (const auto& log : log_files) { + if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid, + log.sb.st_gid)) { + PLOG(ERROR) << "Failed to write to " << log.name; + } + } + } else { + PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR; + } + + // Any part of the log we'd copied to cache is now gone. + // Reset the pointer so we copy from the beginning of the temp + // log. + reset_tmplog_offset(); + copy_logs(true /* save_current_log */, true /* has_cache */, sehandle); + } + + return (result == 0); +} + +bool WipeCache(RecoveryUI* ui, const std::function& confirm_func) { + bool has_cache = volume_for_mount_point("/cache") != nullptr; + if (!has_cache) { + ui->Print("No /cache partition found.\n"); + return false; + } + + if (confirm_func && !confirm_func()) { + return false; + } + + ui->Print("\n-- Wiping cache...\n"); + bool success = EraseVolume("/cache", ui, false); + ui->Print("Cache wipe %s.\n", success ? "complete" : "failed"); + return success; +} + +bool WipeData(Device* device, bool convert_fbe) { + RecoveryUI* ui = device->GetUI(); + ui->Print("\n-- Wiping data...\n"); + bool success = device->PreWipeData(); + if (success) { + success &= EraseVolume(DATA_ROOT, ui, convert_fbe); + bool has_cache = volume_for_mount_point("/cache") != nullptr; + if (has_cache) { + success &= EraseVolume(CACHE_ROOT, ui, false); + } + if (volume_for_mount_point(METADATA_ROOT) != nullptr) { + success &= EraseVolume(METADATA_ROOT, ui, false); + } + } + if (success) { + success &= device->PostWipeData(); + } + ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); + return success; +} \ No newline at end of file diff --git a/otautil/Android.bp b/otautil/Android.bp index b4936c08..0a21731e 100644 --- a/otautil/Android.bp +++ b/otautil/Android.bp @@ -40,6 +40,7 @@ cc_library_static { android: { srcs: [ "dirutil.cpp", + "logging.cpp", "mounts.cpp", "parse_install_logs.cpp", "roots.cpp", diff --git a/logging.h b/otautil/include/otautil/logging.h similarity index 91% rename from logging.h rename to otautil/include/otautil/logging.h index 3cfbc7af..c4f13292 100644 --- a/logging.h +++ b/otautil/include/otautil/logging.h @@ -26,6 +26,8 @@ static constexpr int KEEP_LOG_COUNT = 10; +struct selabel_handle; + ssize_t logbasename(log_id_t id, char prio, const char* filename, const char* buf, size_t len, void* arg); @@ -41,8 +43,7 @@ void rotate_logs(const char* last_log_file, const char* last_kmsg_file); void check_and_fclose(FILE* fp, const std::string& name); void copy_log_file_to_pmsg(const std::string& source, const std::string& destination); -void copy_log_file(const std::string& source, const std::string& destination, bool append); -void copy_logs(bool modified_flash, bool has_cache); +void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle); void reset_tmplog_offset(); void save_kernel_log(const char* destination); diff --git a/logging.cpp b/otautil/logging.cpp similarity index 92% rename from logging.cpp rename to otautil/logging.cpp index 48f9ec31..7c330ac6 100644 --- a/logging.cpp +++ b/otautil/logging.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "logging.h" +#include "otautil/logging.h" #include #include @@ -29,8 +29,8 @@ #include #include /* for AID_SYSTEM */ #include /* private pmsg functions */ +#include -#include "common.h" #include "otautil/dirutil.h" #include "otautil/paths.h" #include "otautil/roots.h" @@ -45,7 +45,7 @@ static const std::string LAST_LOG_FILTER = "recovery/last_log"; // fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the // file pointer, or nullptr on error. -static FILE* fopen_path(const std::string& path, const char* mode) { +static FILE* fopen_path(const std::string& path, const char* mode, const selabel_handle* sehandle) { if (ensure_path_mounted(path) != 0) { LOG(ERROR) << "Can't mount " << path; return nullptr; @@ -165,8 +165,9 @@ void reset_tmplog_offset() { tmplog_offset = 0; } -void copy_log_file(const std::string& source, const std::string& destination, bool append) { - FILE* dest_fp = fopen_path(destination, append ? "ae" : "we"); +static void copy_log_file(const std::string& source, const std::string& destination, bool append, + const selabel_handle* sehandle) { + FILE* dest_fp = fopen_path(destination, append ? "ae" : "we", sehandle); if (dest_fp == nullptr) { PLOG(ERROR) << "Can't open " << destination; } else { @@ -189,11 +190,11 @@ void copy_log_file(const std::string& source, const std::string& destination, bo } } -void copy_logs(bool modified_flash, bool has_cache) { - // We only rotate and record the log of the current session if there are actual attempts to modify - // the flash, such as wipes, installs from BCB or menu selections. This is to avoid unnecessary +void copy_logs(bool save_current_log, bool has_cache, const selabel_handle* sehandle) { + // We only rotate and record the log of the current session if explicitly requested. This usually + // happens after wipes, installation from BCB or menu selections. This is to avoid unnecessary // rotation (and possible deletion) of log files, if it does not do anything loggable. - if (!modified_flash) { + if (!save_current_log) { return; } @@ -211,9 +212,9 @@ void copy_logs(bool modified_flash, bool has_cache) { rotate_logs(LAST_LOG_FILE, LAST_KMSG_FILE); // Copy logs to cache so the system can find out what happened. - copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true); - copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false); - copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false); + copy_log_file(Paths::Get().temporary_log_file(), LOG_FILE, true, sehandle); + copy_log_file(Paths::Get().temporary_log_file(), LAST_LOG_FILE, false, sehandle); + copy_log_file(Paths::Get().temporary_install_file(), LAST_INSTALL_FILE, false, sehandle); save_kernel_log(LAST_KMSG_FILE); chmod(LOG_FILE, 0600); chown(LOG_FILE, AID_SYSTEM, AID_SYSTEM); diff --git a/recovery-persist.cpp b/recovery-persist.cpp index e2a6699f..294017a1 100644 --- a/recovery-persist.cpp +++ b/recovery-persist.cpp @@ -43,7 +43,7 @@ #include #include /* private pmsg functions */ -#include "logging.h" +#include "otautil/logging.h" #include "otautil/parse_install_logs.h" constexpr const char* LAST_LOG_FILE = "/data/misc/recovery/last_log"; diff --git a/recovery-refresh.cpp b/recovery-refresh.cpp index aee1ca59..d41755d0 100644 --- a/recovery-refresh.cpp +++ b/recovery-refresh.cpp @@ -42,7 +42,7 @@ #include /* private pmsg functions */ -#include "logging.h" +#include "otautil/logging.h" int main(int argc, char **argv) { static const char filter[] = "recovery/"; diff --git a/recovery.cpp b/recovery.cpp index 03491849..0e6e4976 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include @@ -55,30 +54,27 @@ #include "install/fuse_sdcard_install.h" #include "install/install.h" #include "install/package.h" -#include "logging.h" -#include "otautil/dirutil.h" +#include "install/wipe_data.h" #include "otautil/error_code.h" +#include "otautil/logging.h" #include "otautil/paths.h" #include "otautil/roots.h" #include "otautil/sysutil.h" #include "recovery_ui/screen_ui.h" #include "recovery_ui/ui.h" -static constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; static constexpr const char* COMMAND_FILE = "/cache/recovery/command"; static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log"; static constexpr const char* LOCALE_FILE = "/cache/recovery/last_locale"; static constexpr const char* CACHE_ROOT = "/cache"; -static constexpr const char* DATA_ROOT = "/data"; -static constexpr const char* METADATA_ROOT = "/metadata"; // We define RECOVERY_API_VERSION in Android.mk, which will be picked up by build system and packed // into target_files.zip. Assert the version defined in code and in Android.mk are consistent. static_assert(kRecoveryApiVersion == RECOVERY_API_VERSION, "Mismatching recovery API versions."); -static bool modified_flash = false; +static bool save_current_log = false; std::string stage; const char* reason = nullptr; @@ -148,7 +144,7 @@ static void finish_recovery() { } } - copy_logs(modified_flash, has_cache); + copy_logs(save_current_log, has_cache, sehandle); // Reset to normal system boot so recovery won't cycle indefinitely. std::string err; @@ -167,110 +163,6 @@ static void finish_recovery() { sync(); // For good measure. } -struct saved_log_file { - std::string name; - struct stat sb; - std::string data; -}; - -static bool erase_volume(const char* volume) { - bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); - bool is_data = (strcmp(volume, DATA_ROOT) == 0); - - ui->SetBackground(RecoveryUI::ERASING); - ui->SetProgressType(RecoveryUI::INDETERMINATE); - - std::vector log_files; - - if (is_cache) { - // If we're reformatting /cache, we load any past logs - // (i.e. "/cache/recovery/last_*") and the current log - // ("/cache/recovery/log") into memory, so we can restore them after - // the reformat. - - ensure_path_mounted(volume); - - struct dirent* de; - std::unique_ptr d(opendir(CACHE_LOG_DIR), closedir); - if (d) { - while ((de = readdir(d.get())) != nullptr) { - if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) { - std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name); - - struct stat sb; - if (stat(path.c_str(), &sb) == 0) { - // truncate files to 512kb - if (sb.st_size > (1 << 19)) { - sb.st_size = 1 << 19; - } - - std::string data(sb.st_size, '\0'); - FILE* f = fopen(path.c_str(), "rbe"); - fread(&data[0], 1, data.size(), f); - fclose(f); - - log_files.emplace_back(saved_log_file{ path, sb, data }); - } - } - } - } else { - if (errno != ENOENT) { - PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR; - } - } - } - - ui->Print("Formatting %s...\n", volume); - - ensure_path_unmounted(volume); - - int result; - if (is_data && reason && strcmp(reason, "convert_fbe") == 0) { - static constexpr const char* CONVERT_FBE_DIR = "/tmp/convert_fbe"; - static constexpr const char* CONVERT_FBE_FILE = "/tmp/convert_fbe/convert_fbe"; - // Create convert_fbe breadcrumb file to signal init to convert to file based encryption, not - // full disk encryption. - if (mkdir(CONVERT_FBE_DIR, 0700) != 0) { - PLOG(ERROR) << "Failed to mkdir " << CONVERT_FBE_DIR; - return false; - } - FILE* f = fopen(CONVERT_FBE_FILE, "wbe"); - if (!f) { - PLOG(ERROR) << "Failed to convert to file encryption"; - return false; - } - fclose(f); - result = format_volume(volume, CONVERT_FBE_DIR); - remove(CONVERT_FBE_FILE); - rmdir(CONVERT_FBE_DIR); - } else { - result = format_volume(volume); - } - - if (is_cache) { - // Re-create the log dir and write back the log entries. - if (ensure_path_mounted(CACHE_LOG_DIR) == 0 && - mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle) == 0) { - for (const auto& log : log_files) { - if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid, - log.sb.st_gid)) { - PLOG(ERROR) << "Failed to write to " << log.name; - } - } - } else { - PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR; - } - - // Any part of the log we'd copied to cache is now gone. - // Reset the pointer so we copy from the beginning of the temp - // log. - reset_tmplog_offset(); - copy_logs(modified_flash, has_cache); - } - - return (result == 0); -} - static bool yes_no(Device* device, const char* question1, const char* question2) { std::vector headers{ question1, question2 }; std::vector items{ " No", " Yes" }; @@ -292,28 +184,6 @@ static bool ask_to_wipe_data(Device* device) { return (chosen_item == 1); } -// Return true on success. -static bool wipe_data(Device* device) { - modified_flash = true; - - ui->Print("\n-- Wiping data...\n"); - bool success = device->PreWipeData(); - if (success) { - success &= erase_volume(DATA_ROOT); - if (has_cache) { - success &= erase_volume(CACHE_ROOT); - } - if (volume_for_mount_point(METADATA_ROOT) != nullptr) { - success &= erase_volume(METADATA_ROOT); - } - } - if (success) { - success &= device->PostWipeData(); - } - ui->Print("Data wipe %s.\n", success ? "complete" : "failed"); - return success; -} - static InstallResult prompt_and_wipe_data(Device* device) { // Use a single string and let ScreenRecoveryUI handles the wrapping. std::vector wipe_data_menu_headers{ @@ -341,7 +211,8 @@ static InstallResult prompt_and_wipe_data(Device* device) { } if (ask_to_wipe_data(device)) { - if (wipe_data(device)) { + bool convert_fbe = reason && strcmp(reason, "convert_fbe") == 0; + if (WipeData(device, convert_fbe)) { return INSTALL_SUCCESS; } else { return INSTALL_ERROR; @@ -350,25 +221,6 @@ static InstallResult prompt_and_wipe_data(Device* device) { } } -// Return true on success. -static bool wipe_cache(bool should_confirm, Device* device) { - if (!has_cache) { - ui->Print("No /cache partition found.\n"); - return false; - } - - if (should_confirm && !yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!")) { - return false; - } - - modified_flash = true; - - ui->Print("\n-- Wiping cache...\n"); - bool success = erase_volume("/cache"); - ui->Print("Cache wipe %s.\n", success ? "complete" : "failed"); - return success; -} - // Secure-wipe a given partition. It uses BLKSECDISCARD, if supported. Otherwise, it goes with // BLKDISCARD (if device supports BLKDISCARDZEROES) or BLKZEROOUT. static bool secure_wipe_partition(const std::string& partition) { @@ -653,7 +505,6 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { ? Device::REBOOT : device->InvokeMenuItem(chosen_item); - bool should_wipe_cache = false; switch (chosen_action) { case Device::NO_ACTION: break; @@ -666,41 +517,40 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { return chosen_action; case Device::WIPE_DATA: + save_current_log = true; if (ui->IsTextVisible()) { if (ask_to_wipe_data(device)) { - wipe_data(device); + WipeData(device, false); } } else { - wipe_data(device); + WipeData(device, false); return Device::NO_ACTION; } break; - case Device::WIPE_CACHE: - wipe_cache(ui->IsTextVisible(), device); + case Device::WIPE_CACHE: { + save_current_log = true; + std::function confirm_func = [&device]() { + return yes_no(device, "Wipe cache?", " THIS CAN NOT BE UNDONE!"); + }; + WipeCache(ui, ui->IsTextVisible() ? confirm_func : nullptr); if (!ui->IsTextVisible()) return Device::NO_ACTION; break; - + } case Device::APPLY_ADB_SIDELOAD: case Device::APPLY_SDCARD: { - modified_flash = true; + save_current_log = true; bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD); if (adb) { - status = apply_from_adb(&should_wipe_cache, ui); + status = apply_from_adb(ui); } else { - status = ApplyFromSdcard(device, &should_wipe_cache, ui); - } - - if (status == INSTALL_SUCCESS && should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; - } + status = ApplyFromSdcard(device, ui); } if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); - copy_logs(modified_flash, has_cache); + copy_logs(save_current_log, has_cache, sehandle); } else if (!ui->IsTextVisible()) { return Device::NO_ACTION; // reboot if logs aren't visible } else { @@ -987,7 +837,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Installation aborted.\n"); @@ -1021,7 +867,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); ui->SetBackground(RecoveryUI::ERROR); @@ -1059,7 +907,8 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(false); } } else if (should_wipe_cache) { - if (!wipe_cache(false, device)) { + save_current_log = true; + if (!WipeCache(ui, nullptr)) { status = INSTALL_ERROR; } } else if (should_wipe_ab) { @@ -1073,15 +922,11 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } - status = apply_from_adb(&should_wipe_cache, ui); - if (status == INSTALL_SUCCESS && should_wipe_cache) { - if (!wipe_cache(false, device)) { - status = INSTALL_ERROR; - } - } + status = apply_from_adb(ui); ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { ui->Print("Rebooting automatically.\n"); diff --git a/recovery_main.cpp b/recovery_main.cpp index 8b4ad5a3..8f3f2a78 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -50,7 +50,8 @@ #include "common.h" #include "fastboot/fastboot.h" -#include "logging.h" +#include "install/wipe_data.h" +#include "otautil/logging.h" #include "otautil/paths.h" #include "otautil/roots.h" #include "otautil/sysutil.h" @@ -434,6 +435,8 @@ int main(int argc, char** argv) { ui->Print("Warning: No file_contexts\n"); } + SetWipeDataSehandle(sehandle); + std::atomic action; std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action)); listener_thread.detach(); From cd780b456f2fcb108ccbe42d83cb8b03fb097513 Mon Sep 17 00:00:00 2001 From: xunchang Date: Mon, 15 Apr 2019 15:24:24 -0700 Subject: [PATCH 16/28] DO NOT MERGE: Move load & restore logs to logging.cpp We perform these steps to perserve the recovery logs when wiping /cache partition. Move them to logging.cpp to keep the actually EraseVolume function concise. Bug: 130166585 Test: unit tests pass, mount cache and check last log after cache Change-Id: Idc52833817a446f3a0148a3dd2112f911c9ef48d (cherry picked from commit 2239b9e4dd08e307ad74dc44b597fd53d2d17de8) --- install/include/install/wipe_data.h | 2 - install/wipe_data.cpp | 75 ++------------------- otautil/include/otautil/logging.h | 14 ++++ otautil/logging.cpp | 100 +++++++++++++++++++++++++--- recovery_main.cpp | 2 +- 5 files changed, 109 insertions(+), 84 deletions(-) diff --git a/install/include/install/wipe_data.h b/install/include/install/wipe_data.h index 06b1b95d..b34891f3 100644 --- a/install/include/install/wipe_data.h +++ b/install/include/install/wipe_data.h @@ -23,8 +23,6 @@ struct selabel_handle; -void SetWipeDataSehandle(selabel_handle* handle); - // Returns true on success. bool WipeCache(RecoveryUI* ui, const std::function& confirm); diff --git a/install/wipe_data.cpp b/install/wipe_data.cpp index 7db79048..765a8152 100644 --- a/install/wipe_data.cpp +++ b/install/wipe_data.cpp @@ -16,14 +16,11 @@ #include "install/wipe_data.h" -#include -#include #include #include #include #include -#include #include #include @@ -39,20 +36,6 @@ constexpr const char* CACHE_ROOT = "/cache"; constexpr const char* DATA_ROOT = "/data"; constexpr const char* METADATA_ROOT = "/metadata"; -constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; - -static struct selabel_handle* sehandle; - -void SetWipeDataSehandle(selabel_handle* handle) { - sehandle = handle; -} - -struct saved_log_file { - std::string name; - struct stat sb; - std::string data; -}; - static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) { bool is_cache = (strcmp(volume, CACHE_ROOT) == 0); bool is_data = (strcmp(volume, DATA_ROOT) == 0); @@ -61,43 +44,10 @@ static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) { ui->SetProgressType(RecoveryUI::INDETERMINATE); std::vector log_files; - if (is_cache) { - // If we're reformatting /cache, we load any past logs - // (i.e. "/cache/recovery/last_*") and the current log - // ("/cache/recovery/log") into memory, so we can restore them after - // the reformat. - - ensure_path_mounted(volume); - - struct dirent* de; - std::unique_ptr d(opendir(CACHE_LOG_DIR), closedir); - if (d) { - while ((de = readdir(d.get())) != nullptr) { - if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) { - std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name); - - struct stat sb; - if (stat(path.c_str(), &sb) == 0) { - // truncate files to 512kb - if (sb.st_size > (1 << 19)) { - sb.st_size = 1 << 19; - } - - std::string data(sb.st_size, '\0'); - FILE* f = fopen(path.c_str(), "rbe"); - fread(&data[0], 1, data.size(), f); - fclose(f); - - log_files.emplace_back(saved_log_file{ path, sb, data }); - } - } - } - } else { - if (errno != ENOENT) { - PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR; - } - } + // If we're reformatting /cache, we load any past logs (i.e. "/cache/recovery/last_*") and the + // current log ("/cache/recovery/log") into memory, so we can restore them after the reformat. + log_files = ReadLogFilesToMemory(); } ui->Print("Formatting %s...\n", volume); @@ -128,24 +78,7 @@ static bool EraseVolume(const char* volume, RecoveryUI* ui, bool convert_fbe) { } if (is_cache) { - // Re-create the log dir and write back the log entries. - if (ensure_path_mounted(CACHE_LOG_DIR) == 0 && - mkdir_recursively(CACHE_LOG_DIR, 0777, false, sehandle) == 0) { - for (const auto& log : log_files) { - if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid, - log.sb.st_gid)) { - PLOG(ERROR) << "Failed to write to " << log.name; - } - } - } else { - PLOG(ERROR) << "Failed to mount / create " << CACHE_LOG_DIR; - } - - // Any part of the log we'd copied to cache is now gone. - // Reset the pointer so we copy from the beginning of the temp - // log. - reset_tmplog_offset(); - copy_logs(true /* save_current_log */, true /* has_cache */, sehandle); + RestoreLogFilesAfterFormat(log_files); } return (result == 0); diff --git a/otautil/include/otautil/logging.h b/otautil/include/otautil/logging.h index c4f13292..60834978 100644 --- a/otautil/include/otautil/logging.h +++ b/otautil/include/otautil/logging.h @@ -18,9 +18,11 @@ #define _LOGGING_H #include +#include #include #include +#include #include @@ -28,6 +30,14 @@ static constexpr int KEEP_LOG_COUNT = 10; struct selabel_handle; +struct saved_log_file { + std::string name; + struct stat sb; + std::string data; +}; + +void SetLoggingSehandle(selabel_handle* handle); + ssize_t logbasename(log_id_t id, char prio, const char* filename, const char* buf, size_t len, void* arg); @@ -48,4 +58,8 @@ void reset_tmplog_offset(); void save_kernel_log(const char* destination); +std::vector ReadLogFilesToMemory(); + +bool RestoreLogFilesAfterFormat(const std::vector& log_files); + #endif //_LOGGING_H diff --git a/otautil/logging.cpp b/otautil/logging.cpp index 7c330ac6..484f1150 100644 --- a/otautil/logging.cpp +++ b/otautil/logging.cpp @@ -16,17 +16,22 @@ #include "otautil/logging.h" +#include +#include #include #include #include #include +#include +#include #include #include #include #include #include +#include #include /* for AID_SYSTEM */ #include /* private pmsg functions */ #include @@ -35,13 +40,21 @@ #include "otautil/paths.h" #include "otautil/roots.h" -static constexpr const char* LOG_FILE = "/cache/recovery/log"; -static constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install"; -static constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; -static constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log"; +constexpr const char* LOG_FILE = "/cache/recovery/log"; +constexpr const char* LAST_INSTALL_FILE = "/cache/recovery/last_install"; +constexpr const char* LAST_KMSG_FILE = "/cache/recovery/last_kmsg"; +constexpr const char* LAST_LOG_FILE = "/cache/recovery/last_log"; -static const std::string LAST_KMSG_FILTER = "recovery/last_kmsg"; -static const std::string LAST_LOG_FILTER = "recovery/last_log"; +constexpr const char* LAST_KMSG_FILTER = "recovery/last_kmsg"; +constexpr const char* LAST_LOG_FILTER = "recovery/last_log"; + +constexpr const char* CACHE_LOG_DIR = "/cache/recovery"; + +static struct selabel_handle* logging_sehandle; + +void SetLoggingSehandle(selabel_handle* handle) { + logging_sehandle = handle; +} // fopen(3)'s the given file, by mounting volumes and making parent dirs as necessary. Returns the // file pointer, or nullptr on error. @@ -74,8 +87,8 @@ void check_and_fclose(FILE* fp, const std::string& name) { ssize_t logbasename(log_id_t /* id */, char /* prio */, const char* filename, const char* /* buf */, size_t len, void* arg) { bool* do_rotate = static_cast(arg); - if (LAST_KMSG_FILTER.find(filename) != std::string::npos || - LAST_LOG_FILTER.find(filename) != std::string::npos) { + if (std::string(LAST_KMSG_FILTER).find(filename) != std::string::npos || + std::string(LAST_LOG_FILTER).find(filename) != std::string::npos) { *do_rotate = true; } return len; @@ -92,8 +105,8 @@ ssize_t logrotate(log_id_t id, char prio, const char* filename, const char* buf, size_t dot = name.find_last_of('.'); std::string sub = name.substr(0, dot); - if (LAST_KMSG_FILTER.find(sub) == std::string::npos && - LAST_LOG_FILTER.find(sub) == std::string::npos) { + if (std::string(LAST_KMSG_FILTER).find(sub) == std::string::npos && + std::string(LAST_LOG_FILTER).find(sub) == std::string::npos) { return __android_log_pmsg_file_write(id, prio, filename, buf, len); } @@ -243,3 +256,70 @@ void save_kernel_log(const char* destination) { buffer.resize(n); android::base::WriteStringToFile(buffer, destination); } + +std::vector ReadLogFilesToMemory() { + ensure_path_mounted("/cache"); + + struct dirent* de; + std::unique_ptr d(opendir(CACHE_LOG_DIR), closedir); + if (!d) { + if (errno != ENOENT) { + PLOG(ERROR) << "Failed to opendir " << CACHE_LOG_DIR; + } + return {}; + } + + std::vector log_files; + while ((de = readdir(d.get())) != nullptr) { + if (strncmp(de->d_name, "last_", 5) == 0 || strcmp(de->d_name, "log") == 0) { + std::string path = android::base::StringPrintf("%s/%s", CACHE_LOG_DIR, de->d_name); + + struct stat sb; + if (stat(path.c_str(), &sb) != 0) { + PLOG(ERROR) << "Failed to stat " << path; + continue; + } + // Truncate files to 512kb + size_t read_size = std::min(sb.st_size, 1 << 19); + std::string data(read_size, '\0'); + + android::base::unique_fd log_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY))); + if (log_fd == -1 || !android::base::ReadFully(log_fd, data.data(), read_size)) { + PLOG(ERROR) << "Failed to read log file " << path; + continue; + } + + log_files.emplace_back(saved_log_file{ path, sb, data }); + } + } + + return log_files; +} + +bool RestoreLogFilesAfterFormat(const std::vector& log_files) { + // Re-create the log dir and write back the log entries. + if (ensure_path_mounted(CACHE_LOG_DIR) != 0) { + PLOG(ERROR) << "Failed to mount " << CACHE_LOG_DIR; + return false; + } + + if (mkdir_recursively(CACHE_LOG_DIR, 0777, false, logging_sehandle) != 0) { + PLOG(ERROR) << "Failed to create " << CACHE_LOG_DIR; + return false; + } + + for (const auto& log : log_files) { + if (!android::base::WriteStringToFile(log.data, log.name, log.sb.st_mode, log.sb.st_uid, + log.sb.st_gid)) { + PLOG(ERROR) << "Failed to write to " << log.name; + } + } + + // Any part of the log we'd copied to cache is now gone. + // Reset the pointer so we copy from the beginning of the temp + // log. + reset_tmplog_offset(); + copy_logs(true /* save_current_log */, true /* has_cache */, logging_sehandle); + + return true; +} diff --git a/recovery_main.cpp b/recovery_main.cpp index 8f3f2a78..5f3ab76d 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -435,7 +435,7 @@ int main(int argc, char** argv) { ui->Print("Warning: No file_contexts\n"); } - SetWipeDataSehandle(sehandle); + SetLoggingSehandle(sehandle); std::atomic action; std::thread listener_thread(ListenRecoverySocket, ui, std::ref(action)); From d0052c30cef1202fff6bb0f87d64eddb98cfbc99 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Tue, 16 Apr 2019 19:59:20 -0700 Subject: [PATCH 17/28] Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: Ie58e59d8d2731350f58bc9733e4599f75a3aca61 --- tools/recovery_l10n/res/values-gl/strings.xml | 4 ++-- tools/recovery_l10n/res/values-ja/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/recovery_l10n/res/values-gl/strings.xml b/tools/recovery_l10n/res/values-gl/strings.xml index e51b36df..e6f2ffd8 100644 --- a/tools/recovery_l10n/res/values-gl/strings.xml +++ b/tools/recovery_l10n/res/values-gl/strings.xml @@ -6,9 +6,9 @@ "Non hai ningún comando" "Erro" "Instalando actualización de seguranza" - "Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos de usuario almacenados neste dispositivo." + "Non se puido cargar o sistema Android. Os teus datos poden estar danados. Se segue aparecendo esta mensaxe, pode ser necesario restablecer os datos de fábrica e borrar todos os datos do usuario almacenados neste dispositivo." "Tentar de novo" "Restablecemento dos datos de fábrica" - "Queres borrar todos os datos de usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." + "Queres borrar todos os datos do usuario?\n\n ESTA ACCIÓN NON SE PODE DESFACER." "Cancelar" diff --git a/tools/recovery_l10n/res/values-ja/strings.xml b/tools/recovery_l10n/res/values-ja/strings.xml index 3d663727..2d6c0abc 100644 --- a/tools/recovery_l10n/res/values-ja/strings.xml +++ b/tools/recovery_l10n/res/values-ja/strings.xml @@ -6,7 +6,7 @@ "コマンドが指定されていません" "エラーが発生しました。" "セキュリティ アップデートをインストールしています" - "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、この端末に保存されているすべてのユーザー データを消去することが必要な場合があります。" + "Android システムを読み込めません。データが破損している可能性があります。このメッセージが引き続き表示される場合は、データの初期化を行い、このデバイスに保存されているすべてのユーザー データを消去することが必要な場合があります。" "再試行" "データの初期化" "すべてのユーザー データをワイプしますか?\n\nこの操作は元に戻せません。" From 378bfbfc5c6f268c16b7f1a254de0ed36da52012 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 16 Apr 2019 14:22:25 -0700 Subject: [PATCH 18/28] Allow entering rescue mode via recovery UI. Only enabled on debuggable builds. Bug: 128415917 Test: Sideload package on taimen. Test: Choose "Enter rescue" from recovery UI. Change-Id: I913dbdbcffd3179e6fa72ca862f74ca8f1364b02 Merged-In: I913dbdbcffd3179e6fa72ca862f74ca8f1364b02 (cherry picked from commit c6dc325e88a25201aa3856e6532c3ed14203a376) --- install/adb_install.cpp | 31 +++++++++++++++++------- install/include/install/adb_install.h | 2 +- minadbd/minadbd.cpp | 12 +++++++-- minadbd/minadbd_services.cpp | 6 +++++ minadbd/minadbd_services.h | 2 ++ recovery.cpp | 18 ++++++++++---- recovery_main.cpp | 4 +++ recovery_ui/device.cpp | 1 + recovery_ui/include/recovery_ui/device.h | 1 + 9 files changed, 60 insertions(+), 17 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 548b6e5b..f430920a 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -42,6 +43,7 @@ #include "fuse_sideload.h" #include "install/install.h" #include "minadbd_types.h" +#include "otautil/sysutil.h" #include "recovery_ui/ui.h" using CommandFunction = std::function; @@ -228,7 +230,7 @@ static void ListenAndExecuteMinadbdCommands( // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( - const std::map& command_map) { + const std::map& command_map, bool rescue_mode) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; @@ -245,9 +247,16 @@ static void CreateMinadbdServiceAndExecuteCommands( } if (child == 0) { recovery_socket.reset(); - execl("/system/bin/minadbd", "minadbd", "--socket_fd", - std::to_string(minadbd_socket.release()).c_str(), nullptr); - + std::vector minadbd_commands = { + "/system/bin/minadbd", + "--socket_fd", + std::to_string(minadbd_socket.release()), + }; + if (rescue_mode) { + minadbd_commands.push_back("--rescue"); + } + auto exec_args = StringVectorToNullTerminatedArray(minadbd_commands); + execv(exec_args[0], exec_args.data()); _exit(EXIT_FAILURE); } @@ -280,7 +289,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int apply_from_adb(RecoveryUI* ui) { +int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); // Clean up state and stop adbd. @@ -289,16 +298,20 @@ int apply_from_adb(RecoveryUI* ui) { return INSTALL_ERROR; } - ui->Print( - "\n\nNow send the package you want to apply\n" - "to the device with \"adb sideload \"...\n"); + if (!rescue_mode) { + ui->Print( + "\n\nNow send the package you want to apply\n" + "to the device with \"adb sideload \"...\n"); + } else { + ui->Print("\n\nWaiting for rescue commands...\n"); + } int install_result = INSTALL_ERROR; std::map command_map{ { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, }; - CreateMinadbdServiceAndExecuteCommands(command_map); + CreateMinadbdServiceAndExecuteCommands(command_map, rescue_mode); // Clean up before switching to the older state, for example setting the state // to none sets sys/class/android_usb/android0/enable to 0. diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index f7b065b6..208d0c78 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -18,4 +18,4 @@ #include -int apply_from_adb(RecoveryUI* ui); +int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode); diff --git a/minadbd/minadbd.cpp b/minadbd/minadbd.cpp index 57158ad5..c80d5490 100644 --- a/minadbd/minadbd.cpp +++ b/minadbd/minadbd.cpp @@ -31,10 +31,13 @@ #include "minadbd_services.h" #include "minadbd_types.h" +using namespace std::string_literals; + int main(int argc, char** argv) { android::base::InitLogging(argv, &android::base::StderrLogger); // TODO(xunchang) implement a command parser - if (argc != 3 || strcmp("--socket_fd", argv[1]) != 0) { + if ((argc != 3 && argc != 4) || argv[1] != "--socket_fd"s || + (argc == 4 && argv[3] != "--rescue"s)) { LOG(ERROR) << "minadbd has invalid arguments, argc: " << argc; exit(kMinadbdArgumentsParsingError); } @@ -50,7 +53,12 @@ int main(int argc, char** argv) { } SetMinadbdSocketFd(socket_fd); - adb_device_banner = "sideload"; + if (argc == 4) { + SetMinadbdRescueMode(true); + adb_device_banner = "rescue"; + } else { + adb_device_banner = "sideload"; + } signal(SIGPIPE, SIG_IGN); diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 79e6fc4e..f0bb70af 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -45,10 +45,16 @@ #include "sysdeps.h" static int minadbd_socket = -1; +static bool rescue_mode = false; + void SetMinadbdSocketFd(int socket_fd) { minadbd_socket = socket_fd; } +void SetMinadbdRescueMode(bool rescue) { + rescue_mode = rescue; +} + static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { char message[kMinadbdMessageSize]; memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); diff --git a/minadbd/minadbd_services.h b/minadbd/minadbd_services.h index 6835bd77..20e3410c 100644 --- a/minadbd/minadbd_services.h +++ b/minadbd/minadbd_services.h @@ -17,3 +17,5 @@ #pragma once void SetMinadbdSocketFd(int socket_fd); + +void SetMinadbdRescueMode(bool); diff --git a/recovery.cpp b/recovery.cpp index 0e6e4976..ce29cb27 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -538,12 +538,20 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { break; } case Device::APPLY_ADB_SIDELOAD: - case Device::APPLY_SDCARD: { + case Device::APPLY_SDCARD: + case Device::ENTER_RESCUE: { save_current_log = true; - bool adb = (chosen_action == Device::APPLY_ADB_SIDELOAD); - if (adb) { - status = apply_from_adb(ui); + + bool adb = true; + if (chosen_action == Device::ENTER_RESCUE) { + // Switch to graphics screen. + ui->ShowText(false); + status = ApplyFromAdb(ui, true /* rescue_mode */); + ui->ShowText(true); + } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) { + status = ApplyFromAdb(ui, false /* rescue_mode */); } else { + adb = false; status = ApplyFromSdcard(device, ui); } @@ -926,7 +934,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } - status = apply_from_adb(ui); + status = ApplyFromAdb(ui, false /* rescue_mode */); ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { ui->Print("Rebooting automatically.\n"); diff --git a/recovery_main.cpp b/recovery_main.cpp index 5f3ab76d..38e1db73 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -423,6 +423,10 @@ int main(int argc, char** argv) { device->RemoveMenuItemForAction(Device::ENTER_FASTBOOT); } + if (!is_ro_debuggable()) { + device->RemoveMenuItemForAction(Device::ENTER_RESCUE); + } + ui->SetBackground(RecoveryUI::NONE); if (show_text) ui->ShowText(true); diff --git a/recovery_ui/device.cpp b/recovery_ui/device.cpp index ddb0118d..e7ae1a3e 100644 --- a/recovery_ui/device.cpp +++ b/recovery_ui/device.cpp @@ -37,6 +37,7 @@ static std::vector> g_menu_actions { "View recovery logs", Device::VIEW_RECOVERY_LOGS }, { "Run graphics test", Device::RUN_GRAPHICS_TEST }, { "Run locale test", Device::RUN_LOCALE_TEST }, + { "Enter rescue", Device::ENTER_RESCUE }, { "Power off", Device::SHUTDOWN }, }; diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 3c44510c..8f17639d 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -50,6 +50,7 @@ class Device { KEY_INTERRUPTED = 13, ENTER_FASTBOOT = 14, ENTER_RECOVERY = 15, + ENTER_RESCUE = 16, }; explicit Device(RecoveryUI* ui); From 178cdd4f5c6ae59d5aaae2614f22cd783eba60d8 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Mon, 15 Apr 2019 12:45:50 -0700 Subject: [PATCH 19/28] Remove the FD parameter from FuseDataProvider ctor. This leaves the FD implementation details to subclasses. In particular, it allows minadbd to do additional works with the FD after sideloading. Bug: 128415917 Test: atest recovery_component_test Test: atest minadbd_test Test: Sideload package on taimen. Change-Id: I106bbaad05201227bbc5fe28890bbbb06fdcb67e Merged-In: I106bbaad05201227bbc5fe28890bbbb06fdcb67e (cherry picked from commit 2be9737cf449dd0650c85ee5168d09b12d386077) --- fuse_sideload/include/fuse_provider.h | 23 +++++++++++------------ minadbd/fuse_adb_provider.h | 15 +++++++-------- minadbd/minadbd_services.cpp | 3 +-- tests/component/sideload_test.cpp | 22 +++++++++++++++------- 4 files changed, 34 insertions(+), 29 deletions(-) diff --git a/fuse_sideload/include/fuse_provider.h b/fuse_sideload/include/fuse_provider.h index 499d57aa..59059cf9 100644 --- a/fuse_sideload/include/fuse_provider.h +++ b/fuse_sideload/include/fuse_provider.h @@ -25,8 +25,8 @@ // This is the base class to read data from source and provide the data to FUSE. class FuseDataProvider { public: - FuseDataProvider(android::base::unique_fd&& fd, uint64_t file_size, uint32_t block_size) - : fd_(std::move(fd)), file_size_(file_size), fuse_block_size_(block_size) {} + FuseDataProvider(uint64_t file_size, uint32_t block_size) + : file_size_(file_size), fuse_block_size_(block_size) {} virtual ~FuseDataProvider() = default; @@ -37,21 +37,15 @@ class FuseDataProvider { return fuse_block_size_; } - bool Valid() const { - return fd_ != -1; - } - // Reads |fetch_size| bytes data starting from |start_block|. Puts the result in |buffer|. virtual bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, uint32_t start_block) const = 0; - virtual void Close() = 0; + virtual void Close() {} protected: FuseDataProvider() = default; - // The underlying source to read data from. - android::base::unique_fd fd_; // Size in bytes of the file to read. uint64_t file_size_ = 0; // Block size passed to the fuse, this is different from the block size of the block device. @@ -61,13 +55,18 @@ class FuseDataProvider { // This class reads data from a file. class FuseFileDataProvider : public FuseDataProvider { public: - FuseFileDataProvider(android::base::unique_fd&& fd, uint64_t file_size, uint32_t block_size) - : FuseDataProvider(std::move(fd), file_size, block_size) {} - FuseFileDataProvider(const std::string& path, uint32_t block_size); bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, uint32_t start_block) const override; + bool Valid() const { + return fd_ != -1; + } + void Close() override; + + private: + // The underlying source to read data from. + android::base::unique_fd fd_; }; diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h index 3fb689bd..24a463d9 100644 --- a/minadbd/fuse_adb_provider.h +++ b/minadbd/fuse_adb_provider.h @@ -14,25 +14,24 @@ * limitations under the License. */ -#ifndef __FUSE_ADB_PROVIDER_H -#define __FUSE_ADB_PROVIDER_H +#pragma once #include -#include "android-base/unique_fd.h" - #include "fuse_provider.h" // This class reads data from adb server. class FuseAdbDataProvider : public FuseDataProvider { public: - FuseAdbDataProvider(android::base::unique_fd&& fd, uint64_t file_size, uint32_t block_size) - : FuseDataProvider(std::move(fd), file_size, block_size) {} + FuseAdbDataProvider(int fd, uint64_t file_size, uint32_t block_size) + : FuseDataProvider(file_size, block_size), fd_(fd) {} bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, uint32_t start_block) const override; void Close() override; -}; -#endif + private: + // The underlying source to read data from (i.e. the one that talks to the host). + int fd_; +}; diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index f0bb70af..eaf88ecc 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -102,8 +102,7 @@ static void sideload_host_service(unique_fd sfd, const std::string& args) { exit(kMinadbdSocketIOError); } - auto adb_data_reader = - std::make_unique(std::move(sfd), file_size, block_size); + auto adb_data_reader = std::make_unique(sfd, file_size, block_size); if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) { LOG(ERROR) << "Failed to start fuse"; exit(kMinadbdFuseStartError); diff --git a/tests/component/sideload_test.cpp b/tests/component/sideload_test.cpp index f5981acb..6add99f4 100644 --- a/tests/component/sideload_test.cpp +++ b/tests/component/sideload_test.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include "fuse_provider.h" @@ -32,17 +31,26 @@ TEST(SideloadTest, fuse_device) { ASSERT_EQ(0, access("/dev/fuse", R_OK | W_OK)); } +class FuseTestDataProvider : public FuseDataProvider { + public: + FuseTestDataProvider(uint64_t file_size, uint32_t block_size) + : FuseDataProvider(file_size, block_size) {} + + private: + bool ReadBlockAlignedData(uint8_t*, uint32_t, uint32_t) const override { + return true; + } +}; + TEST(SideloadTest, run_fuse_sideload_wrong_parameters) { - auto provider_small_block = - std::make_unique(android::base::unique_fd(), 4096, 4095); + auto provider_small_block = std::make_unique(4096, 4095); ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_small_block))); - auto provider_large_block = - std::make_unique(android::base::unique_fd(), 4096, (1 << 22) + 1); + auto provider_large_block = std::make_unique(4096, (1 << 22) + 1); ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_large_block))); - auto provider_too_many_blocks = std::make_unique( - android::base::unique_fd(), ((1 << 18) + 1) * 4096, 4096); + auto provider_too_many_blocks = + std::make_unique(((1 << 18) + 1) * 4096, 4096); ASSERT_EQ(-1, run_fuse_sideload(std::move(provider_too_many_blocks))); } From e5c6446a10291eaca258fcae3ce2654d6224dcb9 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 4 Apr 2019 18:37:58 -0700 Subject: [PATCH 20/28] minadbd: Support rescue install and getprop commands. Bug: 128415917 Test: Enter rescue mode on taimen. Send the following commands: `adb rescue getprop ro.build.fingerprint` `adb rescue getprop ro.build.date.utc` `adb rescue install /path/to/package.zip` Test: Sideload on taimen w/ `adb sideload /path/to/package.zip`. Change-Id: Ibc25daf9fd13f7002e54789f67aaf85d06976bb8 Merged-In: Ibc25daf9fd13f7002e54789f67aaf85d06976bb8 (cherry picked from commit ed717ca17d0b1a35f2d2e57802e2381a6004fdd1) --- minadbd/Android.bp | 1 - minadbd/fuse_adb_provider.cpp | 4 -- minadbd/fuse_adb_provider.h | 2 - minadbd/minadbd_services.cpp | 97 +++++++++++++++++++++++++++++------ minadbd/minadbd_types.h | 1 + 5 files changed, 83 insertions(+), 22 deletions(-) diff --git a/minadbd/Android.bp b/minadbd/Android.bp index e4f7712e..b1c68ca9 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -76,7 +76,6 @@ cc_binary { "libadbd", "libbase", "libcrypto", - "libfusesideload", "libminadbd_services", ], } diff --git a/minadbd/fuse_adb_provider.cpp b/minadbd/fuse_adb_provider.cpp index 9d19a1ec..47719b07 100644 --- a/minadbd/fuse_adb_provider.cpp +++ b/minadbd/fuse_adb_provider.cpp @@ -37,7 +37,3 @@ bool FuseAdbDataProvider::ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_s return true; } - -void FuseAdbDataProvider::Close() { - WriteFdExactly(fd_, "DONEDONE"); -} diff --git a/minadbd/fuse_adb_provider.h b/minadbd/fuse_adb_provider.h index 24a463d9..c5561e57 100644 --- a/minadbd/fuse_adb_provider.h +++ b/minadbd/fuse_adb_provider.h @@ -29,8 +29,6 @@ class FuseAdbDataProvider : public FuseDataProvider { bool ReadBlockAlignedData(uint8_t* buffer, uint32_t fetch_size, uint32_t start_block) const override; - void Close() override; - private: // The underlying source to read data from (i.e. the one that talks to the host). int fd_; diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index eaf88ecc..136392a6 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -28,15 +28,19 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include "adb.h" #include "adb_unique_fd.h" +#include "adb_utils.h" #include "fdevent.h" #include "fuse_adb_provider.h" #include "fuse_sideload.h" @@ -87,46 +91,109 @@ static bool WaitForCommandStatus(int fd, MinadbdCommandStatus* status) { return true; } -static void sideload_host_service(unique_fd sfd, const std::string& args) { +static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, + MinadbdCommandStatus* status) { + auto pieces = android::base::Split(args, ":"); int64_t file_size; int block_size; - if ((sscanf(args.c_str(), "%" SCNd64 ":%d", &file_size, &block_size) != 2) || file_size <= 0 || - block_size <= 0) { + if (pieces.size() != 2 || !android::base::ParseInt(pieces[0], &file_size) || file_size <= 0 || + !android::base::ParseInt(pieces[1], &block_size) || block_size <= 0) { LOG(ERROR) << "bad sideload-host arguments: " << args; - exit(kMinadbdPackageSizeError); + return kMinadbdPackageSizeError; } LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) { - exit(kMinadbdSocketIOError); + return kMinadbdSocketIOError; } auto adb_data_reader = std::make_unique(sfd, file_size, block_size); if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) { LOG(ERROR) << "Failed to start fuse"; - exit(kMinadbdFuseStartError); + return kMinadbdFuseStartError; } + if (!WaitForCommandStatus(minadbd_socket, status)) { + return kMinadbdMessageFormatError; + } + + // Signal host-side adb to stop. For sideload mode, we always send kSideloadServiceExitSuccess + // (i.e. "DONEDONE") regardless of the install result. For rescue mode, we send failure message on + // install error. + if (!rescue_mode || *status == MinadbdCommandStatus::kSuccess) { + if (!android::base::WriteFully(sfd, kSideloadServiceExitSuccess, + strlen(kSideloadServiceExitSuccess))) { + return kMinadbdHostSocketIOError; + } + } else { + if (!android::base::WriteFully(sfd, kSideloadServiceExitFailure, + strlen(kSideloadServiceExitFailure))) { + return kMinadbdHostSocketIOError; + } + } + + return kMinadbdSuccess; +} + +// Sideload service always exits after serving an install command. +static void SideloadHostService(unique_fd sfd, const std::string& args) { MinadbdCommandStatus status; - if (!WaitForCommandStatus(minadbd_socket, &status)) { - exit(kMinadbdMessageFormatError); - } - LOG(INFO) << "Got command status: " << static_cast(status); + exit(RunAdbFuseSideload(sfd.get(), args, &status)); +} - LOG(INFO) << "sideload_host finished"; - exit(kMinadbdSuccess); +// Rescue service waits for the next command after an install command. +static void RescueInstallHostService(unique_fd sfd, const std::string& args) { + MinadbdCommandStatus status; + if (auto result = RunAdbFuseSideload(sfd.get(), args, &status); result != kMinadbdSuccess) { + exit(result); + } +} + +static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { + static const std::unordered_set kGetpropAllowedProps = { + "ro.build.fingerprint", + "ro.build.date.utc", + }; + auto allowed = kGetpropAllowedProps.find(prop) != kGetpropAllowedProps.end(); + if (!allowed) { + return; + } + + auto result = android::base::GetProperty(prop, ""); + if (result.empty()) { + return; + } + if (!android::base::WriteFully(sfd, result.data(), result.size())) { + exit(kMinadbdHostSocketIOError); + } } unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { + if (rescue_mode) { + if (ConsumePrefix(&name, "rescue-install:")) { + // rescue-install:: + std::string args(name); + return create_service_thread( + "rescue-install", std::bind(RescueInstallHostService, std::placeholders::_1, args)); + } else if (ConsumePrefix(&name, "rescue-getprop:")) { + // rescue-getprop: + std::string args(name); + return create_service_thread( + "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args)); + } + return unique_fd{}; + } + if (name.starts_with("sideload:")) { // This exit status causes recovery to print a special error message saying to use a newer adb // (that supports sideload-host). exit(kMinadbdAdbVersionError); - } else if (name.starts_with("sideload-host:")) { - std::string arg(name.substr(strlen("sideload-host:"))); + } else if (ConsumePrefix(&name, "sideload-host:")) { + // sideload-host:: + std::string args(name); return create_service_thread("sideload-host", - std::bind(sideload_host_service, std::placeholders::_1, arg)); + std::bind(SideloadHostService, std::placeholders::_1, args)); } return unique_fd{}; } diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index 7bd69096..5fb7803e 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -35,6 +35,7 @@ enum MinadbdErrorCode : int { kMinadbdUnsupportedCommandError = 7, kMinadbdCommandExecutionError = 8, kMinadbdErrorUnknown = 9, + kMinadbdHostSocketIOError = 10, }; enum class MinadbdCommandStatus : uint32_t { From 23f15fcfafe3aeab13d4deeaca48917fa0dc415c Mon Sep 17 00:00:00 2001 From: xunchang Date: Wed, 17 Apr 2019 14:43:58 -0700 Subject: [PATCH 21/28] Add test for minadbd Ass some unit tests to check if the minadbd service exit correctly in the failure case. Also start the fuse and verify the socket communication between minadbd with adb host, and minadbd with recovery. Bug: 131037235 Test: run unit tests repeatedly, injects some errors and test fails without dangling process. Change-Id: I2f073b701b25d7f1aafc59868a7a91a8cbefaf49 Merged-In: I2f073b701b25d7f1aafc59868a7a91a8cbefaf49 (cherry picked from commit 9c04eb46b7492033e4675bd303a8bb20548081b5) --- minadbd/Android.bp | 2 + minadbd/minadbd_services.cpp | 8 +- minadbd/minadbd_services.h | 4 + minadbd/minadbd_services_test.cpp | 213 ++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+), 1 deletion(-) create mode 100644 minadbd/minadbd_services_test.cpp diff --git a/minadbd/Android.bp b/minadbd/Android.bp index b1c68ca9..007e5057 100644 --- a/minadbd/Android.bp +++ b/minadbd/Android.bp @@ -90,12 +90,14 @@ cc_test { srcs: [ "fuse_adb_provider_test.cpp", + "minadbd_services_test.cpp", ], static_libs: [ "libminadbd_services", "libfusesideload", "libadbd", + "libcrypto", ], shared_libs: [ diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 136392a6..f6aff71f 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -50,6 +50,7 @@ static int minadbd_socket = -1; static bool rescue_mode = false; +static std::string sideload_mount_point = FUSE_SIDELOAD_HOST_MOUNTPOINT; void SetMinadbdSocketFd(int socket_fd) { minadbd_socket = socket_fd; @@ -59,6 +60,10 @@ void SetMinadbdRescueMode(bool rescue) { rescue_mode = rescue; } +void SetSideloadMountPoint(const std::string& path) { + sideload_mount_point = path; +} + static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { char message[kMinadbdMessageSize]; memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); @@ -109,7 +114,8 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, } auto adb_data_reader = std::make_unique(sfd, file_size, block_size); - if (int result = run_fuse_sideload(std::move(adb_data_reader)); result != 0) { + if (int result = run_fuse_sideload(std::move(adb_data_reader), sideload_mount_point.c_str()); + result != 0) { LOG(ERROR) << "Failed to start fuse"; return kMinadbdFuseStartError; } diff --git a/minadbd/minadbd_services.h b/minadbd/minadbd_services.h index 20e3410c..5575c6b8 100644 --- a/minadbd/minadbd_services.h +++ b/minadbd/minadbd_services.h @@ -16,6 +16,10 @@ #pragma once +#include + void SetMinadbdSocketFd(int socket_fd); void SetMinadbdRescueMode(bool); + +void SetSideloadMountPoint(const std::string& path); diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp new file mode 100644 index 00000000..413ba0df --- /dev/null +++ b/minadbd/minadbd_services_test.cpp @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "adb.h" +#include "adb_io.h" +#include "fuse_adb_provider.h" +#include "fuse_sideload.h" +#include "minadbd_services.h" +#include "minadbd_types.h" +#include "socket.h" + +class MinadbdServicesTest : public ::testing::Test { + protected: + static constexpr int EXIT_TIME_OUT = 10; + + void SetUp() override { + ASSERT_TRUE( + android::base::Socketpair(AF_UNIX, SOCK_STREAM, 0, &minadbd_socket_, &recovery_socket_)); + SetMinadbdSocketFd(minadbd_socket_); + SetSideloadMountPoint(mount_point_.path); + + package_path_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_FILENAME; + exit_flag_ = std::string(mount_point_.path) + "/" + FUSE_SIDELOAD_HOST_EXIT_FLAG; + + signal(SIGPIPE, SIG_IGN); + } + + void TearDown() override { + // Umount in case the test fails. Ignore the result. + umount(mount_point_.path); + + signal(SIGPIPE, SIG_DFL); + } + + void ReadAndCheckCommandMessage(int fd, MinadbdCommands expected_command) { + std::vector received(kMinadbdMessageSize, '\0'); + ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize)); + + std::vector expected(kMinadbdMessageSize, '\0'); + memcpy(expected.data(), kMinadbdCommandPrefix, strlen(kMinadbdCommandPrefix)); + memcpy(expected.data() + strlen(kMinadbdCommandPrefix), &expected_command, + sizeof(expected_command)); + ASSERT_EQ(expected, received); + } + + void WaitForFusePath() { + constexpr int TIME_OUT = 10; + for (int i = 0; i < TIME_OUT; ++i) { + struct stat sb; + if (stat(package_path_.c_str(), &sb) == 0) { + return; + } + + if (errno == ENOENT) { + sleep(1); + continue; + } + FAIL() << "Timed out waiting for the fuse-provided package " << strerror(errno); + } + } + + void StatExitFlagAndExitProcess(int exit_code) { + struct stat sb; + if (stat(exit_flag_.c_str(), &sb) != 0) { + PLOG(ERROR) << "Failed to stat " << exit_flag_; + } + + exit(exit_code); + } + + void WriteMinadbdCommandStatus(MinadbdCommandStatus status) { + std::string status_message(kMinadbdMessageSize, '\0'); + memcpy(status_message.data(), kMinadbdStatusPrefix, strlen(kMinadbdStatusPrefix)); + memcpy(status_message.data() + strlen(kMinadbdStatusPrefix), &status, sizeof(status)); + ASSERT_TRUE( + android::base::WriteFully(recovery_socket_, status_message.data(), kMinadbdMessageSize)); + } + + void ExecuteCommandAndWaitForExit(const std::string& command) { + unique_fd fd = daemon_service_to_fd(command, nullptr); + ASSERT_NE(-1, fd); + sleep(EXIT_TIME_OUT); + } + + android::base::unique_fd minadbd_socket_; + android::base::unique_fd recovery_socket_; + + TemporaryDir mount_point_; + std::string package_path_; + std::string exit_flag_; +}; + +TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) { + ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"), + ::testing::ExitedWithCode(kMinadbdPackageSizeError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) { + ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:10:20"), + ::testing::ExitedWithCode(kMinadbdFuseStartError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_broken_minadbd_socket) { + SetMinadbdSocketFd(-1); + ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"), + ::testing::ExitedWithCode(kMinadbdSocketIOError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_broken_recovery_socket) { + recovery_socket_.reset(); + ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:4096:4096"), + ::testing::ExitedWithCode(kMinadbdSocketIOError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_wrong_command_format) { + auto test_body = [&](const std::string& command) { + unique_fd fd = daemon_service_to_fd(command, nullptr); + ASSERT_NE(-1, fd); + WaitForFusePath(); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + + struct stat sb; + ASSERT_EQ(0, stat(exit_flag_.c_str(), &sb)); + ASSERT_TRUE(android::base::WriteStringToFd("12345678", recovery_socket_)); + sleep(EXIT_TIME_OUT); + }; + + ASSERT_EXIT(test_body("sideload-host:4096:4096"), + ::testing::ExitedWithCode(kMinadbdMessageFormatError), ""); +} + +TEST_F(MinadbdServicesTest, SideloadHostService_read_data_from_fuse) { + auto test_body = [&]() { + std::vector content(4096, 'a'); + // Start a new process instead of a thread to read from the package mounted by FUSE. Because + // the test may not exit and report failures correctly when the thread blocks by a syscall. + pid_t pid = fork(); + if (pid == 0) { + WaitForFusePath(); + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(package_path_.c_str(), O_RDONLY))); + // Do not use assertion here because we want to stat the exit flag and exit the process. + // Otherwise the test will wait for the time out instead of failing immediately. + if (fd == -1) { + PLOG(ERROR) << "Failed to open " << package_path_; + StatExitFlagAndExitProcess(1); + } + std::vector content_from_fuse(4096); + if (!android::base::ReadFully(fd, content_from_fuse.data(), 4096)) { + PLOG(ERROR) << "Failed to read from " << package_path_; + StatExitFlagAndExitProcess(1); + } + if (content_from_fuse != content) { + LOG(ERROR) << "Content read from fuse doesn't match with the expected value"; + StatExitFlagAndExitProcess(1); + } + StatExitFlagAndExitProcess(0); + } + + unique_fd fd = daemon_service_to_fd("sideload-host:4096:4096", nullptr); + ASSERT_NE(-1, fd); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + + // Mimic the response from adb host. + std::string adb_message(8, '\0'); + ASSERT_TRUE(android::base::ReadFully(fd, adb_message.data(), 8)); + ASSERT_EQ(android::base::StringPrintf("%08u", 0), adb_message); + ASSERT_TRUE(android::base::WriteFully(fd, content.data(), 4096)); + + // Check that we read the correct data from fuse. + int child_status; + waitpid(pid, &child_status, 0); + ASSERT_TRUE(WIFEXITED(child_status)); + ASSERT_EQ(0, WEXITSTATUS(child_status)); + + WriteMinadbdCommandStatus(MinadbdCommandStatus::kSuccess); + + // TODO(xunchang) check if adb host-side receives "DONEDONE", there's a race condition between + // receiving the message and exit of test body (by detached thread in minadbd service). + exit(kMinadbdSuccess); + }; + + ASSERT_EXIT(test_body(), ::testing::ExitedWithCode(kMinadbdSuccess), ""); +} From 7b9b7db877f082a9adaa8b8a8572940cb5720a11 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Fri, 19 Apr 2019 15:22:15 -0700 Subject: [PATCH 22/28] minadbd: Support `adb reboot` under sideload/rescue modes. Bug: 128415917 Test: Run the following commands under sideload and rescue modes respectively. $ adb reboot $ adb reboot bootloader $ adb reboot recovery $ adb reboot rescue $ adb reboot invalid Change-Id: I84daf63e3360b7b4a0af5e055149a4f54e10ba90 Merged-In: I84daf63e3360b7b4a0af5e055149a4f54e10ba90 (cherry picked from commit 10f441a9dbb91be3124f455439631abcf8e96cde) --- install/adb_install.cpp | 98 +++++++++++++++++------- install/include/install/adb_install.h | 6 +- install/include/install/install.h | 3 +- minadbd/minadbd_services.cpp | 43 ++++++++++- minadbd/minadbd_services_test.cpp | 6 +- minadbd/minadbd_types.h | 13 +++- recovery.cpp | 58 ++++++++------ recovery_main.cpp | 5 ++ recovery_ui/include/recovery_ui/device.h | 4 + 9 files changed, 176 insertions(+), 60 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index f430920a..d79f6f4b 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -44,30 +45,34 @@ #include "install/install.h" #include "minadbd_types.h" #include "otautil/sysutil.h" +#include "recovery_ui/device.h" #include "recovery_ui/ui.h" -using CommandFunction = std::function; +// A CommandFunction returns a pair of (result, should_continue), which indicates the command +// execution result and whether it should proceed to the next iteration. The execution result will +// always be sent to the minadbd side. +using CommandFunction = std::function()>; static bool SetUsbConfig(const std::string& state) { android::base::SetProperty("sys.usb.config", state); return android::base::WaitForProperty("sys.usb.state", state); } -// Parses the minadbd command in |message|; returns MinadbdCommands::kError upon errors. -static MinadbdCommands ParseMinadbdCommands(const std::string& message) { +// Parses the minadbd command in |message|; returns MinadbdCommand::kError upon errors. +static MinadbdCommand ParseMinadbdCommand(const std::string& message) { if (!android::base::StartsWith(message, kMinadbdCommandPrefix)) { LOG(ERROR) << "Failed to parse command in message " << message; - return MinadbdCommands::kError; + return MinadbdCommand::kError; } auto cmd_code_string = message.substr(strlen(kMinadbdCommandPrefix)); auto cmd_code = android::base::get_unaligned(cmd_code_string.c_str()); - if (cmd_code >= static_cast(MinadbdCommands::kError)) { + if (cmd_code >= static_cast(MinadbdCommand::kError)) { LOG(ERROR) << "Unsupported command code: " << cmd_code; - return MinadbdCommands::kError; + return MinadbdCommand::kError; } - return static_cast(cmd_code); + return static_cast(cmd_code); } static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { @@ -82,13 +87,15 @@ static bool WriteStatusToFd(MinadbdCommandStatus status, int fd) { return true; } -// Installs the package from FUSE. Returns true if the installation succeeds, and false otherwise. -static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { +// Installs the package from FUSE. Returns the installation result and whether it should continue +// waiting for new commands. +static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { // How long (in seconds) we wait for the package path to be ready. It doesn't need to be too long // because the minadbd service has already issued an install command. 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.) constexpr int ADB_INSTALL_TIMEOUT = 15; + bool should_continue = true; *result = INSTALL_ERROR; for (int i = 0; i < ADB_INSTALL_TIMEOUT; ++i) { struct stat st; @@ -97,6 +104,7 @@ static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { sleep(1); continue; } else { + should_continue = false; ui->Print("\nTimed out waiting for fuse to be ready.\n\n"); break; } @@ -108,13 +116,39 @@ static bool AdbInstallPackageHandler(RecoveryUI* ui, int* result) { // Calling stat() on this magic filename signals the FUSE to exit. struct stat st; stat(FUSE_SIDELOAD_HOST_EXIT_PATHNAME, &st); - return *result == INSTALL_SUCCESS; + return std::make_pair(*result == INSTALL_SUCCESS, should_continue); } -// Parses and executes the command from minadbd. Returns false if we enter an invalid state so that -// the caller can kill the minadbd service properly. -static bool HandleMessageFromMinadbd( - int socket_fd, const std::map& command_map) { +static auto AdbRebootHandler(MinadbdCommand command, int* result, + Device::BuiltinAction* reboot_action) { + switch (command) { + case MinadbdCommand::kRebootBootloader: + *reboot_action = Device::REBOOT_BOOTLOADER; + break; + case MinadbdCommand::kRebootFastboot: + *reboot_action = Device::ENTER_FASTBOOT; + break; + case MinadbdCommand::kRebootRecovery: + *reboot_action = Device::ENTER_RECOVERY; + break; + case MinadbdCommand::kRebootRescue: + // Use Device::REBOOT_RESCUE instead of Device::ENTER_RESCUE. This allows rebooting back into + // rescue mode (potentially using a newly installed recovery image). + *reboot_action = Device::REBOOT_RESCUE; + break; + case MinadbdCommand::kRebootAndroid: + default: + *reboot_action = Device::REBOOT; + break; + } + *result = INSTALL_REBOOT; + return std::make_pair(true, false); +} + +// Parses and executes the command from minadbd. Returns whether the caller should keep waiting for +// next command. +static bool HandleMessageFromMinadbd(int socket_fd, + const std::map& command_map) { char buffer[kMinadbdMessageSize]; if (!android::base::ReadFully(socket_fd, buffer, kMinadbdMessageSize)) { PLOG(ERROR) << "Failed to read message from minadbd"; @@ -122,8 +156,8 @@ static bool HandleMessageFromMinadbd( } std::string message(buffer, buffer + kMinadbdMessageSize); - auto command_type = ParseMinadbdCommands(message); - if (command_type == MinadbdCommands::kError) { + auto command_type = ParseMinadbdCommand(message); + if (command_type == MinadbdCommand::kError) { return false; } if (command_map.find(command_type) == command_map.end()) { @@ -135,17 +169,19 @@ static bool HandleMessageFromMinadbd( // We have received a valid command, execute the corresponding function. const auto& command_func = command_map.at(command_type); - if (!command_func()) { - LOG(ERROR) << "Failed to execute command " << static_cast(command_type); - return WriteStatusToFd(MinadbdCommandStatus::kFailure, socket_fd); + const auto [result, should_continue] = command_func(); + LOG(INFO) << "Command " << static_cast(command_type) << " finished with " << result; + if (!WriteStatusToFd(result ? MinadbdCommandStatus::kSuccess : MinadbdCommandStatus::kFailure, + socket_fd)) { + return false; } - return WriteStatusToFd(MinadbdCommandStatus::kSuccess, socket_fd); + return should_continue; } // TODO(xunchang) add a wrapper function and kill the minadbd service there. static void ListenAndExecuteMinadbdCommands( pid_t minadbd_pid, android::base::unique_fd&& socket_fd, - const std::map& command_map) { + const std::map& command_map) { android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); if (epoll_fd == -1) { PLOG(ERROR) << "Failed to create epoll"; @@ -230,7 +266,7 @@ static void ListenAndExecuteMinadbdCommands( // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( - const std::map& command_map, bool rescue_mode) { + const std::map& command_map, bool rescue_mode) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; @@ -271,7 +307,6 @@ static void CreateMinadbdServiceAndExecuteCommands( std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket), std::ref(command_map)); - if (listener_thread.joinable()) { listener_thread.join(); } @@ -289,7 +324,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode) { +int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); // Clean up state and stop adbd. @@ -307,8 +342,19 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode) { } int install_result = INSTALL_ERROR; - std::map command_map{ - { MinadbdCommands::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, + std::map command_map{ + { MinadbdCommand::kInstall, std::bind(&AdbInstallPackageHandler, ui, &install_result) }, + { MinadbdCommand::kRebootAndroid, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootAndroid, + &install_result, reboot_action) }, + { MinadbdCommand::kRebootBootloader, + std::bind(&AdbRebootHandler, MinadbdCommand::kRebootBootloader, &install_result, + reboot_action) }, + { MinadbdCommand::kRebootFastboot, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootFastboot, + &install_result, reboot_action) }, + { MinadbdCommand::kRebootRecovery, std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRecovery, + &install_result, reboot_action) }, + { MinadbdCommand::kRebootRescue, + std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) }, }; CreateMinadbdServiceAndExecuteCommands(command_map, rescue_mode); diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index 208d0c78..49b32b54 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -16,6 +16,10 @@ #pragma once +#include #include -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode); +// Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum +// InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with +// the reboot target set in reboot_action. +int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action); diff --git a/install/include/install/install.h b/install/include/install/install.h index 1e41b484..c0a8f1f4 100644 --- a/install/include/install/install.h +++ b/install/include/install/install.h @@ -34,7 +34,8 @@ enum InstallResult { INSTALL_NONE, INSTALL_SKIPPED, INSTALL_RETRY, - INSTALL_KEY_INTERRUPTED + INSTALL_KEY_INTERRUPTED, + INSTALL_REBOOT, }; enum class OtaType { diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index f6aff71f..9b1999d9 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -64,7 +64,7 @@ void SetSideloadMountPoint(const std::string& path) { sideload_mount_point = path; } -static bool WriteCommandToFd(MinadbdCommands cmd, int fd) { +static bool WriteCommandToFd(MinadbdCommand cmd, int fd) { char message[kMinadbdMessageSize]; memcpy(message, kMinadbdCommandPrefix, strlen(kMinadbdStatusPrefix)); android::base::put_unaligned(message + strlen(kMinadbdStatusPrefix), cmd); @@ -109,7 +109,7 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; - if (!WriteCommandToFd(MinadbdCommands::kInstall, minadbd_socket)) { + if (!WriteCommandToFd(MinadbdCommand::kInstall, minadbd_socket)) { return kMinadbdSocketIOError; } @@ -175,7 +175,45 @@ static void RescueGetpropHostService(unique_fd sfd, const std::string& prop) { } } +// Reboots into the given target. We don't reboot directly from minadbd, but going through recovery +// instead. This allows recovery to finish all the pending works (clear BCB, save logs etc) before +// the reboot. +static void RebootHostService(unique_fd /* sfd */, const std::string& target) { + MinadbdCommand command; + if (target == "bootloader") { + command = MinadbdCommand::kRebootBootloader; + } else if (target == "rescue") { + command = MinadbdCommand::kRebootRescue; + } else if (target == "recovery") { + command = MinadbdCommand::kRebootRecovery; + } else if (target == "fastboot") { + command = MinadbdCommand::kRebootFastboot; + } else { + command = MinadbdCommand::kRebootAndroid; + } + if (!WriteCommandToFd(command, minadbd_socket)) { + exit(kMinadbdSocketIOError); + } + MinadbdCommandStatus status; + if (!WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } +} + unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { + // Common services that are supported both in sideload and rescue modes. + if (ConsumePrefix(&name, "reboot:")) { + // "reboot:", where target must be one of the following. + std::string args(name); + if (args.empty() || args == "bootloader" || args == "rescue" || args == "recovery" || + args == "fastboot") { + return create_service_thread("reboot", + std::bind(RebootHostService, std::placeholders::_1, args)); + } + return unique_fd{}; + } + + // Rescue-specific services. if (rescue_mode) { if (ConsumePrefix(&name, "rescue-install:")) { // rescue-install:: @@ -191,6 +229,7 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport * return unique_fd{}; } + // Sideload-specific services. if (name.starts_with("sideload:")) { // This exit status causes recovery to print a special error message saying to use a newer adb // (that supports sideload-host). diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp index 413ba0df..593180bb 100644 --- a/minadbd/minadbd_services_test.cpp +++ b/minadbd/minadbd_services_test.cpp @@ -62,7 +62,7 @@ class MinadbdServicesTest : public ::testing::Test { signal(SIGPIPE, SIG_DFL); } - void ReadAndCheckCommandMessage(int fd, MinadbdCommands expected_command) { + void ReadAndCheckCommandMessage(int fd, MinadbdCommand expected_command) { std::vector received(kMinadbdMessageSize, '\0'); ASSERT_TRUE(android::base::ReadFully(fd, received.data(), kMinadbdMessageSize)); @@ -147,7 +147,7 @@ TEST_F(MinadbdServicesTest, SideloadHostService_wrong_command_format) { unique_fd fd = daemon_service_to_fd(command, nullptr); ASSERT_NE(-1, fd); WaitForFusePath(); - ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall); struct stat sb; ASSERT_EQ(0, stat(exit_flag_.c_str(), &sb)); @@ -188,7 +188,7 @@ TEST_F(MinadbdServicesTest, SideloadHostService_read_data_from_fuse) { unique_fd fd = daemon_service_to_fd("sideload-host:4096:4096", nullptr); ASSERT_NE(-1, fd); - ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommands::kInstall); + ReadAndCheckCommandMessage(recovery_socket_, MinadbdCommand::kInstall); // Mimic the response from adb host. std::string adb_message(8, '\0'); diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index 5fb7803e..b370b795 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -43,12 +43,19 @@ enum class MinadbdCommandStatus : uint32_t { kFailure = 1, }; -enum class MinadbdCommands : uint32_t { +enum class MinadbdCommand : uint32_t { kInstall = 0, kUiPrint = 1, - kError = 2, + kRebootAndroid = 2, + kRebootBootloader = 3, + kRebootFastboot = 4, + kRebootRecovery = 5, + kRebootRescue = 6, + + // Last but invalid command. + kError, }; -static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommands)); +static_assert(kMinadbdMessageSize == sizeof(kMinadbdCommandPrefix) - 1 + sizeof(MinadbdCommand)); static_assert(kMinadbdMessageSize == sizeof(kMinadbdStatusPrefix) - 1 + sizeof(MinadbdCommandStatus)); diff --git a/recovery.cpp b/recovery.cpp index ce29cb27..5bd9b172 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -512,6 +512,7 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { case Device::REBOOT: case Device::SHUTDOWN: case Device::REBOOT_BOOTLOADER: + case Device::REBOOT_RESCUE: case Device::ENTER_FASTBOOT: case Device::ENTER_RECOVERY: return chosen_action; @@ -537,32 +538,36 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { if (!ui->IsTextVisible()) return Device::NO_ACTION; break; } + case Device::APPLY_ADB_SIDELOAD: case Device::APPLY_SDCARD: case Device::ENTER_RESCUE: { save_current_log = true; bool adb = true; + Device::BuiltinAction reboot_action; if (chosen_action == Device::ENTER_RESCUE) { // Switch to graphics screen. ui->ShowText(false); - status = ApplyFromAdb(ui, true /* rescue_mode */); - ui->ShowText(true); + status = ApplyFromAdb(ui, true /* rescue_mode */, &reboot_action); } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) { - status = ApplyFromAdb(ui, false /* rescue_mode */); + status = ApplyFromAdb(ui, false /* rescue_mode */, &reboot_action); } else { adb = false; status = ApplyFromSdcard(device, ui); } + ui->Print("\nInstall from %s completed with status %d.\n", adb ? "ADB" : "SD card", status); + if (status == INSTALL_REBOOT) { + return reboot_action; + } + if (status != INSTALL_SUCCESS) { ui->SetBackground(RecoveryUI::ERROR); ui->Print("Installation aborted.\n"); copy_logs(save_current_log, has_cache, sehandle); } else if (!ui->IsTextVisible()) { return Device::NO_ACTION; // reboot if logs aren't visible - } else { - ui->Print("\nInstall from %s complete.\n", adb ? "ADB" : "SD card"); } break; } @@ -841,6 +846,9 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Supported API: %d\n", kRecoveryApiVersion); int status = INSTALL_SUCCESS; + // next_action indicates the next target to reboot into upon finishing the install. It could be + // overridden to a different reboot target per user request. + Device::BuiltinAction next_action = shutdown_after ? Device::SHUTDOWN : Device::REBOOT; if (update_package != nullptr) { // It's not entirely true that we will modify the flash. But we want @@ -924,19 +932,18 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } - status = ApplyFromAdb(ui, false /* rescue_mode */); + status = ApplyFromAdb(ui, false /* rescue_mode */, &next_action); ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { + status = INSTALL_REBOOT; ui->Print("Rebooting automatically.\n"); } } else if (fsck_unshare_blocks) { @@ -961,23 +968,26 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorIsTextVisible()) { - Device::BuiltinAction temp = prompt_and_wait(device, status); - if (temp != Device::NO_ACTION) { - after = temp; + // Determine the next action. + // - If the state is INSTALL_REBOOT, device will reboot into the target as specified in + // `next_action`. + // - If the recovery menu is visible, prompt and wait for commands. + // - If the state is INSTALL_NONE, wait for commands (e.g. in user build, one manually boots + // into recovery to sideload a package or to wipe the device). + // - In all other cases, reboot the device. Therefore, normal users will observe the device + // rebooting a) immediately upon successful finish (INSTALL_SUCCESS); or b) an "error" screen + // for 5s followed by an automatic reboot. + if (status != INSTALL_REBOOT) { + if (status == INSTALL_NONE || ui->IsTextVisible()) { + Device::BuiltinAction temp = prompt_and_wait(device, status); + if (temp != Device::NO_ACTION) { + next_action = temp; + } } } // Save logs and clean up before rebooting or shutting down. finish_recovery(); - return after; + return next_action; } diff --git a/recovery_main.cpp b/recovery_main.cpp index 38e1db73..18abff76 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -478,6 +478,11 @@ int main(int argc, char** argv) { android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); break; + case Device::REBOOT_RESCUE: + ui->Print("Rebooting to rescue...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,rescue"); + break; + case Device::ENTER_FASTBOOT: if (logical_partitions_mapped()) { ui->Print("Partitions may be mounted - rebooting to enter fastboot."); diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 8f17639d..09b5d1f4 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -50,7 +50,11 @@ class Device { KEY_INTERRUPTED = 13, ENTER_FASTBOOT = 14, ENTER_RECOVERY = 15, + // ENTER vs REBOOT: The latter will trigger a reboot that uses `rescue` as the reboot target. + // So it goes from rescue -> bootloader -> rescue, whereas ENTER_RESCUE switches from recovery + // -> rescue directly. ENTER_RESCUE = 16, + REBOOT_RESCUE = 17, }; explicit Device(RecoveryUI* ui); From 75321ade8733317bfe7bf0b1850c94b055c8a1c1 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Tue, 23 Apr 2019 11:46:25 -0700 Subject: [PATCH 23/28] Parse BCB command to enter rescue mode. bootloader will set `boot-rescue` in BCB command field to indicate booting into rescue mode. This CL adds the matching parsing code. This CL changes the on-screen UI to display the default image while waiting for each sideload / rescue command. It also changes the minadbd reboot handlers to use REBOOT_ instead of the previous ENTER_ actions. This ensures a reboot going through bootloader, which may load a newly installed bootloader/recovery. Bug: 128505466 Bug: 128415917 Test: Boot into rescue mode. Run `adb rescue getprop` and `adb rescue install`. Check the UI. Then run `adb reboot rescue`. Change-Id: I5b7de9dfd898ed8e14bea0d4ad7385a9bae26e94 Merged-In: I5b7de9dfd898ed8e14bea0d4ad7385a9bae26e94 (cherry picked from commit d9cb014d431fee946308bdb8979c8e8fa74b582f) --- install/adb_install.cpp | 24 +++++++++++------- recovery.cpp | 18 ++++++++++--- recovery_main.cpp | 32 +++++++++++++++++++++--- recovery_ui/include/recovery_ui/device.h | 11 +++++--- 4 files changed, 64 insertions(+), 21 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index d79f6f4b..9dfe0407 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -121,19 +121,20 @@ static auto AdbInstallPackageHandler(RecoveryUI* ui, int* result) { static auto AdbRebootHandler(MinadbdCommand command, int* result, Device::BuiltinAction* reboot_action) { + // Use Device::REBOOT_{FASTBOOT,RECOVERY,RESCUE}, instead of the ones with ENTER_. This allows + // rebooting back into fastboot/recovery/rescue mode through bootloader, which may use a newly + // installed bootloader/recovery image. switch (command) { case MinadbdCommand::kRebootBootloader: *reboot_action = Device::REBOOT_BOOTLOADER; break; case MinadbdCommand::kRebootFastboot: - *reboot_action = Device::ENTER_FASTBOOT; + *reboot_action = Device::REBOOT_FASTBOOT; break; case MinadbdCommand::kRebootRecovery: - *reboot_action = Device::ENTER_RECOVERY; + *reboot_action = Device::REBOOT_RECOVERY; break; case MinadbdCommand::kRebootRescue: - // Use Device::REBOOT_RESCUE instead of Device::ENTER_RESCUE. This allows rebooting back into - // rescue mode (potentially using a newly installed recovery image). *reboot_action = Device::REBOOT_RESCUE; break; case MinadbdCommand::kRebootAndroid: @@ -180,7 +181,7 @@ static bool HandleMessageFromMinadbd(int socket_fd, // TODO(xunchang) add a wrapper function and kill the minadbd service there. static void ListenAndExecuteMinadbdCommands( - pid_t minadbd_pid, android::base::unique_fd&& socket_fd, + RecoveryUI* ui, pid_t minadbd_pid, android::base::unique_fd&& socket_fd, const std::map& command_map) { android::base::unique_fd epoll_fd(epoll_create1(O_CLOEXEC)); if (epoll_fd == -1) { @@ -203,6 +204,10 @@ static void ListenAndExecuteMinadbdCommands( // Set the timeout to be 300s when waiting for minadbd commands. constexpr int TIMEOUT_MILLIS = 300 * 1000; while (true) { + // Reset the progress bar and the background image before each command. + ui->SetProgressType(RecoveryUI::EMPTY); + ui->SetBackground(RecoveryUI::NO_COMMAND); + // Poll for the status change of the socket_fd, and handle the message if the fd is ready to // read. int event_count = @@ -266,7 +271,8 @@ static void ListenAndExecuteMinadbdCommands( // b11. exit the listening loop // static void CreateMinadbdServiceAndExecuteCommands( - const std::map& command_map, bool rescue_mode) { + RecoveryUI* ui, const std::map& command_map, + bool rescue_mode) { signal(SIGPIPE, SIG_IGN); android::base::unique_fd recovery_socket; @@ -305,8 +311,8 @@ static void CreateMinadbdServiceAndExecuteCommands( return; } - std::thread listener_thread(ListenAndExecuteMinadbdCommands, child, std::move(recovery_socket), - std::ref(command_map)); + std::thread listener_thread(ListenAndExecuteMinadbdCommands, ui, child, + std::move(recovery_socket), std::ref(command_map)); if (listener_thread.joinable()) { listener_thread.join(); } @@ -357,7 +363,7 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) }, }; - CreateMinadbdServiceAndExecuteCommands(command_map, rescue_mode); + CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode); // Clean up before switching to the older state, for example setting the state // to none sets sys/class/android_usb/android0/enable to 0. diff --git a/recovery.cpp b/recovery.cpp index 5bd9b172..f9b3bfc0 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -509,12 +509,14 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { case Device::NO_ACTION: break; - case Device::REBOOT: - case Device::SHUTDOWN: - case Device::REBOOT_BOOTLOADER: - case Device::REBOOT_RESCUE: case Device::ENTER_FASTBOOT: case Device::ENTER_RECOVERY: + case Device::REBOOT: + case Device::REBOOT_BOOTLOADER: + case Device::REBOOT_FASTBOOT: + case Device::REBOOT_RECOVERY: + case Device::REBOOT_RESCUE: + case Device::SHUTDOWN: return chosen_action; case Device::WIPE_DATA: @@ -728,6 +730,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("Rebooting automatically.\n"); } + } else if (rescue) { + save_current_log = true; + status = ApplyFromAdb(ui, true /* rescue_mode */, &next_action); + ui->Print("\nInstall from ADB complete (status: %d).\n", status); } else if (fsck_unshare_blocks) { if (!do_fsck_unshare_blocks()) { status = INSTALL_ERROR; diff --git a/recovery_main.cpp b/recovery_main.cpp index 18abff76..de8ac1f4 100644 --- a/recovery_main.cpp +++ b/recovery_main.cpp @@ -155,9 +155,11 @@ static std::vector get_args(const int argc, char** const argv) { } // Finally, if no arguments were specified, check whether we should boot - // into fastboot. + // into fastboot or rescue mode. if (args.size() == 1 && boot_command == "boot-fastboot") { args.emplace_back("--fastboot"); + } else if (args.size() == 1 && boot_command == "boot-rescue") { + args.emplace_back("--rescue"); } return args; @@ -470,6 +472,7 @@ int main(int argc, char** argv) { switch (ret) { case Device::SHUTDOWN: ui->Print("Shutting down...\n"); + // TODO: Move all the reboots to reboot(), which should conditionally set quiescent flag. android::base::SetProperty(ANDROID_RB_PROPERTY, "shutdown,"); break; @@ -478,11 +481,32 @@ int main(int argc, char** argv) { android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,bootloader"); break; - case Device::REBOOT_RESCUE: - ui->Print("Rebooting to rescue...\n"); - android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,rescue"); + case Device::REBOOT_FASTBOOT: + ui->Print("Rebooting to recovery/fastboot...\n"); + android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,fastboot"); break; + case Device::REBOOT_RECOVERY: + ui->Print("Rebooting to recovery...\n"); + reboot("reboot,recovery"); + break; + + case Device::REBOOT_RESCUE: { + // Not using `reboot("reboot,rescue")`, as it requires matching support in kernel and/or + // bootloader. + bootloader_message boot = {}; + strlcpy(boot.command, "boot-rescue", sizeof(boot.command)); + std::string err; + if (!write_bootloader_message(boot, &err)) { + LOG(ERROR) << "Failed to write bootloader message: " << err; + // Stay under recovery on failure. + continue; + } + ui->Print("Rebooting to recovery/rescue...\n"); + reboot("reboot,recovery"); + break; + } + case Device::ENTER_FASTBOOT: if (logical_partitions_mapped()) { ui->Print("Partitions may be mounted - rebooting to enter fastboot."); diff --git a/recovery_ui/include/recovery_ui/device.h b/recovery_ui/include/recovery_ui/device.h index 09b5d1f4..7c76cdb0 100644 --- a/recovery_ui/include/recovery_ui/device.h +++ b/recovery_ui/include/recovery_ui/device.h @@ -33,6 +33,10 @@ class Device { static constexpr const int kHighlightDown = -3; static constexpr const int kInvokeItem = -4; + // ENTER vs REBOOT: The latter will trigger a reboot that goes through bootloader, which allows + // using a new bootloader / recovery image if applicable. For example, REBOOT_RESCUE goes from + // rescue -> bootloader -> rescue, whereas ENTER_RESCUE switches from recovery -> rescue + // directly. enum BuiltinAction { NO_ACTION = 0, REBOOT = 1, @@ -50,11 +54,10 @@ class Device { KEY_INTERRUPTED = 13, ENTER_FASTBOOT = 14, ENTER_RECOVERY = 15, - // ENTER vs REBOOT: The latter will trigger a reboot that uses `rescue` as the reboot target. - // So it goes from rescue -> bootloader -> rescue, whereas ENTER_RESCUE switches from recovery - // -> rescue directly. ENTER_RESCUE = 16, - REBOOT_RESCUE = 17, + REBOOT_FASTBOOT = 17, + REBOOT_RECOVERY = 18, + REBOOT_RESCUE = 19, }; explicit Device(RecoveryUI* ui); From 5a1916b9bedce04fb79f64b61def1bc53fffb11d Mon Sep 17 00:00:00 2001 From: xunchang Date: Mon, 22 Apr 2019 12:18:14 -0700 Subject: [PATCH 24/28] Support wipe command in rescue mode Bug: 131037235 Test: unit tests pass, run `adb rescue wipe` Change-Id: I22668f2c98fe2d9195d2561f961c28a7c08e712c (cherry picked from commit fedeef6f6d1f7b8f1e5a8b9e77f8dc21ef6b3c95) --- install/adb_install.cpp | 23 +++++++++----- install/include/install/adb_install.h | 3 +- minadbd/minadbd_services.cpp | 46 +++++++++++++++++++++++---- minadbd/minadbd_services_test.cpp | 2 +- minadbd/minadbd_types.h | 4 ++- recovery.cpp | 8 ++--- 6 files changed, 64 insertions(+), 22 deletions(-) diff --git a/install/adb_install.cpp b/install/adb_install.cpp index 9dfe0407..4dd1f1b0 100644 --- a/install/adb_install.cpp +++ b/install/adb_install.cpp @@ -43,6 +43,7 @@ #include "fuse_sideload.h" #include "install/install.h" +#include "install/wipe_data.h" #include "minadbd_types.h" #include "otautil/sysutil.h" #include "recovery_ui/device.h" @@ -330,7 +331,7 @@ static void CreateMinadbdServiceAndExecuteCommands( signal(SIGPIPE, SIG_DFL); } -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action) { +int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action) { // Save the usb state to restore after the sideload operation. std::string usb_state = android::base::GetProperty("sys.usb.state", "none"); // Clean up state and stop adbd. @@ -339,13 +340,7 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot return INSTALL_ERROR; } - if (!rescue_mode) { - ui->Print( - "\n\nNow send the package you want to apply\n" - "to the device with \"adb sideload \"...\n"); - } else { - ui->Print("\n\nWaiting for rescue commands...\n"); - } + RecoveryUI* ui = device->GetUI(); int install_result = INSTALL_ERROR; std::map command_map{ @@ -363,6 +358,18 @@ int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot std::bind(&AdbRebootHandler, MinadbdCommand::kRebootRescue, &install_result, reboot_action) }, }; + if (!rescue_mode) { + ui->Print( + "\n\nNow send the package you want to apply\n" + "to the device with \"adb sideload \"...\n"); + } else { + ui->Print("\n\nWaiting for rescue commands...\n"); + command_map.emplace(MinadbdCommand::kWipeData, [&device]() { + bool result = WipeData(device, false); + return std::make_pair(result, true); + }); + } + CreateMinadbdServiceAndExecuteCommands(ui, command_map, rescue_mode); // Clean up before switching to the older state, for example setting the state diff --git a/install/include/install/adb_install.h b/install/include/install/adb_install.h index 49b32b54..3a0a8174 100644 --- a/install/include/install/adb_install.h +++ b/install/include/install/adb_install.h @@ -17,9 +17,8 @@ #pragma once #include -#include // Applies a package via `adb sideload` or `adb rescue`. Returns the install result (in `enum // InstallResult`). When a reboot has been requested, INSTALL_REBOOT will be the return value, with // the reboot target set in reboot_action. -int ApplyFromAdb(RecoveryUI* ui, bool rescue_mode, Device::BuiltinAction* reboot_action); +int ApplyFromAdb(Device* device, bool rescue_mode, Device::BuiltinAction* reboot_action); diff --git a/minadbd/minadbd_services.cpp b/minadbd/minadbd_services.cpp index 9b1999d9..1c4c0f49 100644 --- a/minadbd/minadbd_services.cpp +++ b/minadbd/minadbd_services.cpp @@ -104,7 +104,7 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, if (pieces.size() != 2 || !android::base::ParseInt(pieces[0], &file_size) || file_size <= 0 || !android::base::ParseInt(pieces[1], &block_size) || block_size <= 0) { LOG(ERROR) << "bad sideload-host arguments: " << args; - return kMinadbdPackageSizeError; + return kMinadbdHostCommandArgumentError; } LOG(INFO) << "sideload-host file size " << file_size << ", block size " << block_size; @@ -124,17 +124,17 @@ static MinadbdErrorCode RunAdbFuseSideload(int sfd, const std::string& args, return kMinadbdMessageFormatError; } - // Signal host-side adb to stop. For sideload mode, we always send kSideloadServiceExitSuccess + // Signal host-side adb to stop. For sideload mode, we always send kMinadbdServicesExitSuccess // (i.e. "DONEDONE") regardless of the install result. For rescue mode, we send failure message on // install error. if (!rescue_mode || *status == MinadbdCommandStatus::kSuccess) { - if (!android::base::WriteFully(sfd, kSideloadServiceExitSuccess, - strlen(kSideloadServiceExitSuccess))) { + if (!android::base::WriteFully(sfd, kMinadbdServicesExitSuccess, + strlen(kMinadbdServicesExitSuccess))) { return kMinadbdHostSocketIOError; } } else { - if (!android::base::WriteFully(sfd, kSideloadServiceExitFailure, - strlen(kSideloadServiceExitFailure))) { + if (!android::base::WriteFully(sfd, kMinadbdServicesExitFailure, + strlen(kMinadbdServicesExitFailure))) { return kMinadbdHostSocketIOError; } } @@ -200,6 +200,34 @@ static void RebootHostService(unique_fd /* sfd */, const std::string& target) { } } +static void WipeDeviceService(unique_fd fd, const std::string& args) { + auto pieces = android::base::Split(args, ":"); + if (pieces.size() != 2 || pieces[0] != "userdata") { + LOG(ERROR) << "Failed to parse wipe device command arguments " << args; + exit(kMinadbdHostCommandArgumentError); + } + + size_t message_size; + if (!android::base::ParseUint(pieces[1], &message_size) || + message_size < strlen(kMinadbdServicesExitSuccess)) { + LOG(ERROR) << "Failed to parse wipe device message size in " << args; + exit(kMinadbdHostCommandArgumentError); + } + + WriteCommandToFd(MinadbdCommand::kWipeData, minadbd_socket); + MinadbdCommandStatus status; + if (!WaitForCommandStatus(minadbd_socket, &status)) { + exit(kMinadbdMessageFormatError); + } + + std::string response = (status == MinadbdCommandStatus::kSuccess) ? kMinadbdServicesExitSuccess + : kMinadbdServicesExitFailure; + response += std::string(message_size - response.size(), '\0'); + if (!android::base::WriteFully(fd, response.c_str(), response.size())) { + exit(kMinadbdHostSocketIOError); + } +} + unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport */) { // Common services that are supported both in sideload and rescue modes. if (ConsumePrefix(&name, "reboot:")) { @@ -225,7 +253,13 @@ unique_fd daemon_service_to_fd(std::string_view name, atransport* /* transport * std::string args(name); return create_service_thread( "rescue-getprop", std::bind(RescueGetpropHostService, std::placeholders::_1, args)); + } else if (ConsumePrefix(&name, "rescue-wipe:")) { + // rescue-wipe:target: + std::string args(name); + return create_service_thread("rescue-wipe", + std::bind(WipeDeviceService, std::placeholders::_1, args)); } + return unique_fd{}; } diff --git a/minadbd/minadbd_services_test.cpp b/minadbd/minadbd_services_test.cpp index 593180bb..f8787379 100644 --- a/minadbd/minadbd_services_test.cpp +++ b/minadbd/minadbd_services_test.cpp @@ -122,7 +122,7 @@ class MinadbdServicesTest : public ::testing::Test { TEST_F(MinadbdServicesTest, SideloadHostService_wrong_size_argument) { ASSERT_EXIT(ExecuteCommandAndWaitForExit("sideload-host:abc:4096"), - ::testing::ExitedWithCode(kMinadbdPackageSizeError), ""); + ::testing::ExitedWithCode(kMinadbdHostCommandArgumentError), ""); } TEST_F(MinadbdServicesTest, SideloadHostService_wrong_block_size) { diff --git a/minadbd/minadbd_types.h b/minadbd/minadbd_types.h index b370b795..99fd45e8 100644 --- a/minadbd/minadbd_types.h +++ b/minadbd/minadbd_types.h @@ -30,7 +30,7 @@ enum MinadbdErrorCode : int { kMinadbdSocketIOError = 2, kMinadbdMessageFormatError = 3, kMinadbdAdbVersionError = 4, - kMinadbdPackageSizeError = 5, + kMinadbdHostCommandArgumentError = 5, kMinadbdFuseStartError = 6, kMinadbdUnsupportedCommandError = 7, kMinadbdCommandExecutionError = 8, @@ -51,6 +51,8 @@ enum class MinadbdCommand : uint32_t { kRebootFastboot = 4, kRebootRecovery = 5, kRebootRescue = 6, + kWipeCache = 7, + kWipeData = 8, // Last but invalid command. kError, diff --git a/recovery.cpp b/recovery.cpp index f9b3bfc0..5fc673ec 100644 --- a/recovery.cpp +++ b/recovery.cpp @@ -551,9 +551,9 @@ static Device::BuiltinAction prompt_and_wait(Device* device, int status) { if (chosen_action == Device::ENTER_RESCUE) { // Switch to graphics screen. ui->ShowText(false); - status = ApplyFromAdb(ui, true /* rescue_mode */, &reboot_action); + status = ApplyFromAdb(device, true /* rescue_mode */, &reboot_action); } else if (chosen_action == Device::APPLY_ADB_SIDELOAD) { - status = ApplyFromAdb(ui, false /* rescue_mode */, &reboot_action); + status = ApplyFromAdb(device, false /* rescue_mode */, &reboot_action); } else { adb = false; status = ApplyFromSdcard(device, ui); @@ -946,7 +946,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorShowText(true); } - status = ApplyFromAdb(ui, false /* rescue_mode */, &next_action); + status = ApplyFromAdb(device, false /* rescue_mode */, &next_action); ui->Print("\nInstall from ADB complete (status: %d).\n", status); if (sideload_auto_reboot) { status = INSTALL_REBOOT; @@ -954,7 +954,7 @@ Device::BuiltinAction start_recovery(Device* device, const std::vectorPrint("\nInstall from ADB complete (status: %d).\n", status); } else if (fsck_unshare_blocks) { if (!do_fsck_unshare_blocks()) { From 9681eef5ff179980ae0d1c5437894f34ea7cfe87 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Sat, 4 May 2019 18:39:11 -0700 Subject: [PATCH 25/28] Import translations. DO NOT MERGE Auto-generated-cl: translation import Bug: 64712476 Change-Id: I4933222253f1bde2daab4726be971c6d7008a0e4 --- tools/recovery_l10n/res/values-in/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/recovery_l10n/res/values-in/strings.xml b/tools/recovery_l10n/res/values-in/strings.xml index 15a78ec4..43c9deb9 100644 --- a/tools/recovery_l10n/res/values-in/strings.xml +++ b/tools/recovery_l10n/res/values-in/strings.xml @@ -9,6 +9,6 @@ "Tidak dapat memuat sistem Android. Data Anda mungkin rusak. Jika terus mendapatkan pesan ini, Anda mungkin perlu melakukan reset ke setelan pabrik dan menghapus semua data pengguna yang disimpan di perangkat ini." "Coba lagi" "Reset ke setelan pabrik" - "Wipe semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!" + "Hapus total semua data pengguna?\n\n TINDAKAN INI TIDAK DAPAT DIURUNGKAN!" "Batal" From 35e0f6d290ea7c12164bcfaf1b857d965e943938 Mon Sep 17 00:00:00 2001 From: Tao Bao Date: Thu, 16 May 2019 14:42:42 -0700 Subject: [PATCH 26/28] Add misc_writer. bootloader_message.h currently divides /misc into four segments. The space between 2K and 16K is reserved for vendor use (e.g. bootloader persists flags). This CL adds a vendor tool "misc_writer", to allow writing data to the vendor space in /misc, before getting a dedicated HAL for accessing /misc partition (b/131775112). Targets need to explicitly include the module, then invoke the executable to write data. For example, the following command will write 3-byte data ("0xABCDEF") to offset 4 in vendor space (i.e. 2048 + 4 in /misc). $ /vendor/bin/misc_writer --vendor-space-offset 4 --hex-string 0xABCDEF Bug: 132906936 Test: Run recovery_unit_test on crosshatch. Test: Call the command via init.hardware.rc on crosshatch. Check that the call finishes successfully. Then check the contents written to /misc (`dd bs=1 skip=2048 if=/dev/block/sda2 count=32 | xxd`). Change-Id: I79548fc63fc79b705a0320868690569c3106949f Merged-In: I79548fc63fc79b705a0320868690569c3106949f (cherry picked from commit 7ae01698424cc3adf635c324961b1405594f5156) --- bootloader_message/Android.bp | 25 ++++- bootloader_message/bootloader_message.cpp | 42 +++++++ .../bootloader_message/bootloader_message.h | 13 ++- misc_writer/Android.bp | 40 +++++++ misc_writer/misc_writer.cpp | 106 ++++++++++++++++++ tests/component/bootloader_message_test.cpp | 38 +++++++ 6 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 misc_writer/Android.bp create mode 100644 misc_writer/misc_writer.cpp diff --git a/bootloader_message/Android.bp b/bootloader_message/Android.bp index 5cd21323..450dad08 100644 --- a/bootloader_message/Android.bp +++ b/bootloader_message/Android.bp @@ -14,9 +14,8 @@ // limitations under the License. // -cc_library { - name: "libbootloader_message", - recovery_available: true, +cc_defaults { + name: "libbootloader_message_defaults", srcs: ["bootloader_message.cpp"], cflags: [ "-Wall", @@ -24,7 +23,25 @@ cc_library { ], shared_libs: [ "libbase", - "libfs_mgr", + ], + static_libs: [ + "libfstab", ], export_include_dirs: ["include"], } + +cc_library { + name: "libbootloader_message", + defaults: [ + "libbootloader_message_defaults", + ], + recovery_available: true, +} + +cc_library_static { + name: "libbootloader_message_vendor", + defaults: [ + "libbootloader_message_defaults", + ], + vendor: true, +} diff --git a/bootloader_message/bootloader_message.cpp b/bootloader_message/bootloader_message.cpp index 8c1d63bd..c1ebeaa8 100644 --- a/bootloader_message/bootloader_message.cpp +++ b/bootloader_message/bootloader_message.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -32,7 +33,17 @@ using android::fs_mgr::Fstab; using android::fs_mgr::ReadDefaultFstab; +static std::string g_misc_device_for_test; + +// Exposed for test purpose. +void SetMiscBlockDeviceForTest(std::string_view misc_device) { + g_misc_device_for_test = misc_device; +} + static std::string get_misc_blk_device(std::string* err) { + if (!g_misc_device_for_test.empty()) { + return g_misc_device_for_test; + } Fstab fstab; if (!ReadDefaultFstab(&fstab)) { *err = "failed to read default fstab"; @@ -228,6 +239,37 @@ bool write_wipe_package(const std::string& package_data, std::string* err) { WIPE_PACKAGE_OFFSET_IN_MISC, err); } +static bool OffsetAndSizeInVendorSpace(size_t offset, size_t size) { + auto total_size = WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC; + return size <= total_size && offset <= total_size - size; +} + +bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound read (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return read_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); +} + +bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err) { + if (!OffsetAndSizeInVendorSpace(offset, size)) { + *err = android::base::StringPrintf("Out of bound write (offset %zu size %zu)", offset, size); + return false; + } + auto misc_blk_device = get_misc_blk_device(err); + if (misc_blk_device.empty()) { + return false; + } + return write_misc_partition(data, size, misc_blk_device, VENDOR_SPACE_OFFSET_IN_MISC + offset, + err); +} + extern "C" bool write_reboot_bootloader(void) { std::string err; return write_reboot_bootloader(&err); diff --git a/bootloader_message/include/bootloader_message/bootloader_message.h b/bootloader_message/include/bootloader_message/bootloader_message.h index 95c19ae5..95dd8f4c 100644 --- a/bootloader_message/include/bootloader_message/bootloader_message.h +++ b/bootloader_message/include/bootloader_message/bootloader_message.h @@ -28,8 +28,9 @@ // 16K - 64K Used by uncrypt and recovery to store wipe_package for A/B devices // Note that these offsets are admitted by bootloader,recovery and uncrypt, so they // are not configurable without changing all of them. -static const size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; -static const size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; +constexpr size_t BOOTLOADER_MESSAGE_OFFSET_IN_MISC = 0; +constexpr size_t VENDOR_SPACE_OFFSET_IN_MISC = 2 * 1024; +constexpr size_t WIPE_PACKAGE_OFFSET_IN_MISC = 16 * 1024; /* Bootloader Message (2-KiB) * @@ -228,6 +229,14 @@ bool read_wipe_package(std::string* package_data, size_t size, std::string* err) // Write the wipe package into BCB (to offset WIPE_PACKAGE_OFFSET_IN_MISC). bool write_wipe_package(const std::string& package_data, std::string* err); +// Reads data from the vendor space in /misc partition, with the given offset and size. Note that +// offset is in relative to the start of vendor space. +bool ReadMiscPartitionVendorSpace(void* data, size_t size, size_t offset, std::string* err); + +// Writes the given data to the vendor space in /misc partition, at the given offset. Note that +// offset is in relative to the start of the vendor space. +bool WriteMiscPartitionVendorSpace(const void* data, size_t size, size_t offset, std::string* err); + #else #include diff --git a/misc_writer/Android.bp b/misc_writer/Android.bp new file mode 100644 index 00000000..567143c7 --- /dev/null +++ b/misc_writer/Android.bp @@ -0,0 +1,40 @@ +// +// Copyright (C) 2019 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_binary { + name: "misc_writer", + vendor: true, + + srcs: [ + "misc_writer.cpp", + ], + + cpp_std: "experimental", + + cflags: [ + "-Wall", + "-Werror", + ], + + shared_libs: [ + "libbase", + ], + + static_libs: [ + "libbootloader_message_vendor", + "libfstab", + ], +} diff --git a/misc_writer/misc_writer.cpp b/misc_writer/misc_writer.cpp new file mode 100644 index 00000000..1d9702eb --- /dev/null +++ b/misc_writer/misc_writer.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +using namespace std::string_literals; + +static std::vector ParseHexString(std::string_view hex_string) { + auto length = hex_string.size(); + if (length % 2 != 0 || length == 0) { + return {}; + } + + std::vector result(length / 2); + for (size_t i = 0; i < length / 2; i++) { + auto sub = "0x" + std::string(hex_string.substr(i * 2, 2)); + if (!android::base::ParseUint(sub, &result[i])) { + return {}; + } + } + return result; +} + +static int Usage(std::string_view name) { + std::cerr << name << " usage:\n"; + std::cerr << name << " [--vendor-space-offset ] --hex-string 0xABCDEF\n"; + std::cerr << "Writes the given hex string to the specified offset in vendor space in /misc " + "partition. Offset defaults to 0 if unspecified.\n"; + return EXIT_FAILURE; +} + +// misc_writer is a vendor tool that writes data to the vendor space in /misc. +int main(int argc, char** argv) { + constexpr struct option OPTIONS[] = { + { "vendor-space-offset", required_argument, nullptr, 0 }, + { "hex-string", required_argument, nullptr, 0 }, + { nullptr, 0, nullptr, 0 }, + }; + + // Offset defaults to 0 if unspecified. + size_t offset = 0; + std::string_view hex_string; + + int arg; + int option_index; + while ((arg = getopt_long(argc, argv, "", OPTIONS, &option_index)) != -1) { + if (arg != 0) { + LOG(ERROR) << "Invalid command argument"; + return Usage(argv[0]); + } + auto option_name = OPTIONS[option_index].name; + if (option_name == "vendor-space-offset"s) { + if (!android::base::ParseUint(optarg, &offset)) { + LOG(ERROR) << "Failed to parse the offset: " << optarg; + return Usage(argv[0]); + } + } else if (option_name == "hex-string"s) { + hex_string = optarg; + } + } + + if (hex_string.starts_with("0x") || hex_string.starts_with("0X")) { + hex_string = hex_string.substr(2); + } + if (hex_string.empty()) { + LOG(ERROR) << "Invalid input hex string: " << hex_string; + return Usage(argv[0]); + } + + auto data = ParseHexString(hex_string); + if (data.empty()) { + LOG(ERROR) << "Failed to parse the input hex string: " << hex_string; + return EXIT_FAILURE; + } + if (std::string err; !WriteMiscPartitionVendorSpace(data.data(), data.size(), offset, &err)) { + LOG(ERROR) << "Failed to write to misc partition: " << err; + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/tests/component/bootloader_message_test.cpp b/tests/component/bootloader_message_test.cpp index b005d199..95d875e6 100644 --- a/tests/component/bootloader_message_test.cpp +++ b/tests/component/bootloader_message_test.cpp @@ -15,6 +15,7 @@ */ #include +#include #include #include @@ -22,6 +23,10 @@ #include #include +using namespace std::string_literals; + +extern void SetMiscBlockDeviceForTest(std::string_view misc_device); + TEST(BootloaderMessageTest, read_and_write_bootloader_message) { TemporaryFile temp_misc; @@ -114,3 +119,36 @@ TEST(BootloaderMessageTest, update_bootloader_message_recovery_options_long) { std::string(boot.reserved, sizeof(boot.reserved))); } +TEST(BootloaderMessageTest, WriteMiscPartitionVendorSpace) { + TemporaryFile temp_misc; + ASSERT_TRUE(android::base::WriteStringToFile(std::string(4096, '\x00'), temp_misc.path)); + SetMiscBlockDeviceForTest(temp_misc.path); + + constexpr std::string_view kTestMessage = "kTestMessage"; + std::string err; + ASSERT_TRUE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), 0, &err)); + + std::string message; + message.resize(kTestMessage.size()); + ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err)); + ASSERT_EQ(kTestMessage, message); + + // Write with an offset. + ASSERT_TRUE(WriteMiscPartitionVendorSpace("\x00\x00", 2, 5, &err)); + ASSERT_TRUE(ReadMiscPartitionVendorSpace(message.data(), message.size(), 0, &err)); + ASSERT_EQ("kTest\x00\x00ssage"s, message); + + // Write with the right size. + auto start_offset = + WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC - kTestMessage.size(); + ASSERT_TRUE( + WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), start_offset, &err)); + + // Out-of-bound write. + ASSERT_FALSE(WriteMiscPartitionVendorSpace(kTestMessage.data(), kTestMessage.size(), + start_offset + 1, &err)); + + // Message won't fit. + std::string long_message(WIPE_PACKAGE_OFFSET_IN_MISC - VENDOR_SPACE_OFFSET_IN_MISC + 1, 'a'); + ASSERT_FALSE(WriteMiscPartitionVendorSpace(long_message.data(), long_message.size(), 0, &err)); +} From 46ec20b69298ff267fcc9f42a742015fb541b4a4 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Fri, 14 Jun 2019 09:43:24 -0700 Subject: [PATCH 27/28] libprocessgroup users use libcutils libprocessgroup symbols are being moved into libcutils in order to optimize linking/memory usage. libprocessgroup will no longer be required in the future (however removing references to it will come separately). Since libcutils is used statically here, the dependencies of libprocessgroup need to be explicitly listed. Bug: 135145426 Test: boot Change-Id: I91c082f0fa2f5f5c52751065cd5f50f5cb965b23 --- updater/Android.bp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/updater/Android.bp b/updater/Android.bp index b80cdb3a..4e87db2d 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -45,6 +45,8 @@ cc_defaults { "libcrypto", "libcrypto_utils", "libcutils", + "libcgrouprc", + "libcgrouprc_format", "libutils", "libtune2fs", From cdbd84de26bb213fee6d8560976d9b99eea75f77 Mon Sep 17 00:00:00 2001 From: Zhijun He Date: Wed, 19 Jun 2019 04:44:04 +0000 Subject: [PATCH 28/28] Revert "libprocessgroup users use libcutils" This reverts commit 46ec20b69298ff267fcc9f42a742015fb541b4a4. Reason for revert: breaks all camera use cases Bug: 135568875 Change-Id: I86747c0df5489f80d1966dd07669637597fb2b00 --- updater/Android.bp | 2 -- 1 file changed, 2 deletions(-) diff --git a/updater/Android.bp b/updater/Android.bp index 4e87db2d..b80cdb3a 100644 --- a/updater/Android.bp +++ b/updater/Android.bp @@ -45,8 +45,6 @@ cc_defaults { "libcrypto", "libcrypto_utils", "libcutils", - "libcgrouprc", - "libcgrouprc_format", "libutils", "libtune2fs",